mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-03 04:24:36 +00:00
96b041846dd7f2bb7a9be8a396db6ec271dfe18f
78 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
96b041846d |
Feat/sandbox (#2072)
* feat: add mcp and skills
* feat: add filter
* feat: modify frontend
* feat(box): add sandbox_exec tool loop for local-agent calculations
* feat(box): add host workspace mounting and sandbox_exec guidance
* feat(box): add BoxProfile with resource limits and improved output truncation
- Implement head+tail output truncation (60/40 split) so LLM sees both
beginning and final results; add streaming byte-limited reads in backend
to prevent unbounded memory usage (_MAX_RAW_OUTPUT_BYTES = 1MB)
- Define BoxProfile model with locked fields and max_timeout_sec clamping
- Add four built-in profiles: default, offline_readonly, network_basic,
network_extended with differentiated resource and security constraints
- Add resource limit fields to BoxSpec (cpus, memory_mb, pids_limit,
read_only_rootfs) and pass corresponding container CLI flags
(--cpus, --memory, --pids-limit, --read-only, --tmpfs)
- Profile loaded from config (box.profile), applied in service layer
before BoxSpec validation; locked fields cannot be overridden by
tool-call parameters
* feat(box): add obs
* refactor(box): unify box service lifecycle and local runtime
management
* refactor(box): remove legacy in-process runtime code and clean up smells
After the architecture settled on always using an independent Box Runtime
service, several pieces of compatibility code and design shortcuts were
left behind. This commit cleans them up:
- Remove `LocalBoxRuntimeClient` and `create_box_runtime_client` from
production code (moved to test-only helper).
- Remove unused `_clip_bytes` method from backend.
- Remove `__langbot_session_placeholder__` hack by making `BoxSpec.cmd`
default to empty and validating non-empty only in `runtime.execute()`.
- Extract `get_box_config()` helper to eliminate 5× duplicated config
access boilerplate.
- Remove `session_id`/`host_path`/`host_path_mode` from the LLM-facing
tool schema to enforce request-scoped session isolation.
- Fix dual shutdown path: `NativeToolLoader.shutdown()` no longer calls
`box_service.shutdown()` (handled by `Application.dispose()`).
- Simplify `_assert_session_compatible` with a loop.
- Inline client creation in `BoxRuntimeConnector`.
- Remove redundant `BOX__RUNTIME_URL` env var from docker-compose
(auto-detected by code).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add test
* fix: fix box intergration test
* feat(box/mcp): integrate MCP stdio with Box sandbox — auto-isolation, dep install, security
## Summary
When Podman/Docker is available, all stdio-mode MCP servers now automatically
run inside Box containers with dependency installation, path rewriting, and
lifecycle management. When no container runtime exists, LangBot starts normally
and stdio MCP falls back to host-direct execution.
## What changed
### MCP stdio → Box integration (mcp.py)
- Add `MCPServerBoxConfig` pydantic model for structured box configuration
with validation and defaults (network, host_path_mode, timeouts, resources)
- Auto-infer `host_path` from command/args with venv detection: recognizes
`.venv/bin/python` patterns and walks up to the project root
- Rewrite host paths to container `/workspace` paths transparently
- Replace venv python commands with container-native `python`
- Auto-detect `pyproject.toml`/`setup.py`/`requirements.txt` and run
`pip install` inside the container before starting the MCP server
- Copy project to `/tmp` before install to handle read-only mounts
- Add retry with exponential backoff (3 retries, 2s/4s/8s delays)
- Add Box managed process health monitoring (poll every 5s)
- Fix session leak: `_cleanup_box_stdio_session()` now runs in `finally`
block of `_lifecycle_loop`, covering all exit paths
- Fix retry logic: `_ready_event` is only set after all retries exhaust
or on success, not on first failure
- Enhance `get_runtime_info_dict()` with `box_session_id` and `box_enabled`
### Box security (security.py — new)
- `validate_sandbox_security()` blocks dangerous host paths:
`/etc`, `/proc`, `/sys`, `/dev`, `/root`, `/boot`, `/run`,
docker.sock, podman socket
- Called at the start of `CLISandboxBackend.start_session()`
### Box models (models.py)
- Add `BoxHostMountMode.NONE` — skips volume mount entirely
- Adjust `validate_host_mount_consistency` to allow arbitrary workdir
when `host_path_mode=NONE`
### Box backend (backend.py)
- Add `validate_sandbox_security()` call in `start_session()`
- Add `langbot.box.config_hash` label on containers for drift detection
- Handle `BoxHostMountMode.NONE` — skip `-v` mount arg
- Add `cleanup_orphaned_containers()` to base class (no-op default) and
CLI implementation (single batched `rm -f` command)
### Box runtime (runtime.py)
- Call `cleanup_orphaned_containers()` during `initialize()` to remove
lingering containers from previous runs
### Box service (service.py)
- Graceful degradation: `initialize()` catches runtime errors and sets
`available=False` instead of crashing LangBot startup
- Add `available` property and guard on `execute_sandbox_tool()`
- Add `skip_host_mount_validation` parameter to `build_spec()` and
`create_session()` — MCP paths are admin-configured and trusted,
bypassing `allowed_host_mount_roots` restrictions meant for
LLM-generated sandbox_exec commands
### Default behavior
- stdio MCP servers automatically use Box when `box_service.available`
is True (Podman/Docker detected); no explicit `box` config needed
- When no container runtime exists, falls back to host-direct stdio
- MCP Box defaults: `network=on` (for pip install), `read_only_rootfs=false`
(for site-packages), `host_path_mode=ro`, `startup_timeout=120s`
### Tests
- `test_box_security.py`: blocked paths, safe paths, subpath rejection
- `test_mcp_box_integration.py`: config model, path rewriting, venv
unwrap, host_path inference, payload building, runtime info, box
availability check
- `test_box_service.py`: `BoxHostMountMode.NONE` validation tests
* feat(box/mcp): instance-based orphan cleanup, error classification, session API, and integration tests
## Changes
### Precise orphan container cleanup
- Runtime generates a unique instance_id on startup
- Every container gets a `langbot.box.instance_id` label
- `cleanup_orphaned_containers()` only removes containers from
previous instances, preserving containers owned by the current one
- Containers from older versions (no label) are also cleaned up
- `cleanup_orphaned_containers` added to `BaseSandboxBackend` as
a no-op default method, removing hasattr duck-typing
### Fine-grained MCP error classification
- New `MCPSessionErrorPhase` enum with 7 phases: session_create,
dep_install, process_start, relay_connect, mcp_init, runtime,
tool_call
- Each phase in `_init_box_stdio_server()` sets the error phase
before re-raising, enabling precise failure diagnosis
- `retry_count` tracked across retry attempts
- `get_runtime_info_dict()` exposes `error_phase` and `retry_count`
### GET /v1/sessions/{id} API
- `BoxRuntime.get_session()` returns session details including
managed process info when present
- `handle_get_session` HTTP handler + route in server.py
- `BoxRuntimeClient.get_session()` abstract method + remote impl
### stdio defaults to Box when runtime is available
- `_uses_box_stdio()` checks `box_service.available` instead of
requiring explicit `box` key in server_config
- `BoxService.initialize()` catches runtime errors gracefully,
sets `available=False` instead of crashing LangBot startup
- When no container runtime exists, stdio MCP falls back to
host-direct execution
### Code quality (from /simplify review)
- Extracted `_VENV_DIRS` / `_VENV_BIN_DIRS` module-level constants
- Removed dead `_box_network_mode()` method and unused `bc` variable
- Fixed broken import `from ....box.models` → `from ...box.models`
- Cached `_resolve_host_path()` result — computed once, passed through
- Config hash now includes `host_path` field
- Batched orphan cleanup into single `rm -f` command
### Session leak fix
- `_cleanup_box_stdio_session()` now runs in `_lifecycle_loop`'s
finally block, covering all exit paths (normal shutdown, error,
retry, final failure)
### Integration tests
- 6 end-to-end tests covering managed process lifecycle, WebSocket
stdio bidirectional IO, session cleanup verification, single
session query, process exit detection, and orphan cleanup safety
* refactor: use rpc
* fix: import
* refactor(box): clean up sandbox subsystem code quality and efficiency
- Fix O(n²) stderr trimming in runtime.py with running length tracker
- Remove dead code: RESERVED_CONTAINER_PATHS, _subprocess_wait_task,
unused config_hash computation, unused imports
- Deduplicate connection callback in BoxRuntimeConnector, parse URL once
- Use enum comparison instead of stringly-typed spec.network.value check
- Replace manual _result_to_dict/_session_to_dict with model_dump()
- Cache NativeToolLoader tool definition and sandbox system guidance
- Extract _is_path_under() helper to eliminate duplicated path checks
- Import SANDBOX_EXEC_TOOL_NAME from native.py instead of redefining
- Add JSON startswith guard in logging_utils to skip futile json.loads
- Fix ruff lint errors (F401 unused imports, F841 unused variables)
* fix: ruff
* refactor(sandbox): keep box logic out of pipeline and localagent
- Move sandbox system-prompt guidance from LocalAgentRunner into
BoxService.get_system_guidance() so all box domain knowledge stays
in the box module.
- Remove standalone logging_utils.py; merge format_result_log() into
MessageHandler base class alongside cut_str().
- Strip sandbox-specific JSON parsing from log formatting; tool
results now use generic truncation.
- Revert TYPE_CHECKING changes in stage.py and runner.py that were
unrelated to this feature.
- Skip two test files affected by a pre-existing circular import
(runner ↔ app) until the import cycle is resolved in a separate PR.
* fix: ruff
* refactor(box): move box runtime to langbot-plugin-sdk
Extract self-contained box runtime modules (actions, backend, client,
errors, models, runtime, security, server) to langbot-plugin-sdk and
update all imports to use `langbot_plugin.box.*`. Keep only service
and
connector in LangBot core as they depend on the Application context.
- Update docker-compose to use `langbot_plugin.box.server` entry
point
- Update pyproject.toml to use local SDK via `tool.uv.sources`
- Remove migrated source files and their unit/integration tests
- Update remaining test imports to match new module paths
* fix: ruff
* feat: enhance sandbox api
* refactor(box): derive paths from shared host root
* fix(box): tighten sandbox exposure and restore box integration coverage
* refactor(types): remove quoted annotations under postponed evaluation
* feat(box): unify native agent tools around exec/read/write/edit
* chore(sandbox): move MCP loader changes to follow-up branch
* feat(box): add session workspace quota enforcement and SDK quota metadata
* feat(skills): add Agent Skills management system (#1917)
* feat(skills): add Agent Skills management system
Implement comprehensive skills management feature inspired by agentskills spec:
Backend:
- Add Skill and SkillPipelineBinding database entities
- Add database migration (dbm018) for skills tables
- Implement SkillManager for skill loading, matching, and resolution
- Implement SkillService for CRUD operations
- Add skills API endpoints for skill and pipeline binding management
- Integrate skill index injection into pipeline preprocessor
- Add skill activation detection in LocalAgentRunner
Frontend:
- Add Skills page with listing, search, and type filter
- Add SkillDetailDialog for create/edit with preview
- Add SkillCard and SkillForm components
- Add skills API methods to BackendClient
- Add skills entry to sidebar navigation
- Add i18n translations (en-US, zh-Hans)
Features:
- Support skill and workflow types
- Sub-skill composition via {{INVOKE_SKILL: name}} syntax
- Progressive disclosure (index in prompt, full instructions on activation)
- Pipeline-specific skill bindings with priority
* fix: resolve cherry-pick conflicts for agentskills onto sandbox
- Remove non-existent external_kb service import
- Add skill_mgr mock to localagent sandbox_exec tests
- Keep database version at 24 (sandbox branch's latest)
* feat(skills): upgrade to package-backed skills with sandbox execution
Evolve the skills system from pure prompt-based to package-backed with
sandbox tool execution support:
- Add source_type/package_root/entry_file/skill_tools fields to Skill entity
- SkillManager loads SKILL.md from local package directories
- SkillToolLoader as 4th dispatch layer in ToolManager (query-scoped)
- LocalAgent injects skill tools into use_funcs on skill activation
- BoxService.execute_skill_tool() runs scripts in sandbox (ro mount, env params)
- Skill tool names auto-namespaced as skill__{skill}__{tool}
- API validation for package_root allowlist and entry path traversal
- Frontend source_type toggle, package_root input, skill_tools editor
- Migration renumbered to 025 with ALTER TABLE fallback for existing DBs
- Fix unclosed limitation section in i18n files
- Fix skills API methods misplaced outside BackendClient class
* fix: test info
* feat(skills): switch skills to package-backed storage and add import tooling
- skills 从 inline/package 双轨收敛成 package-first
- instructions 改为写入并读取 SKILL.md
- 新增本地目录扫描和 GitHub 安装 skill
- 前端把 skills 整合进 plugins 页,新增 SkillsComponent 和 GitHub 导入弹窗
- skill form 去掉 source_type / type 筛选,改成目录扫描驱动
- Box skill tool 挂载模式从 ro 改成 rw
- 测试和中英文文案同步更新
* feat: simplify langbot skill create and import
* refactor(skills): clean up legacy skill API and harden activation flow
* refactor(skills): remove skill dependency expansion and add skill_get
* fix: lint
* fix: delete
* fix(skills): align tool manager loader initialization
* refactor: remove sandbox execute skill
* fix(skills): hide activation markers and isolate skill activation flow
* refactor(skills): switch skill model to filesystem-backed packages
* refactor(skills): switch skill model to filesystem-backed packages
* refactor(skills): unify runtime skill access around filesystem paths
* refactor(skills): unify runtime skill access around filesystem paths
* feat(skills): align rw package design and fix skill activation, visibility, and lint issues
* refactor(skills): replace rich authoring API with import/reload flow and update
Box design doc
* feat(box): add sandbox_exec tool loop for local-agent calculations
* feat(box): add host workspace mounting and sandbox_exec guidance
* feat(box): add BoxProfile with resource limits and improved output truncation
- Implement head+tail output truncation (60/40 split) so LLM sees both
beginning and final results; add streaming byte-limited reads in backend
to prevent unbounded memory usage (_MAX_RAW_OUTPUT_BYTES = 1MB)
- Define BoxProfile model with locked fields and max_timeout_sec clamping
- Add four built-in profiles: default, offline_readonly, network_basic,
network_extended with differentiated resource and security constraints
- Add resource limit fields to BoxSpec (cpus, memory_mb, pids_limit,
read_only_rootfs) and pass corresponding container CLI flags
(--cpus, --memory, --pids-limit, --read-only, --tmpfs)
- Profile loaded from config (box.profile), applied in service layer
before BoxSpec validation; locked fields cannot be overridden by
tool-call parameters
* feat(box): add obs
* refactor(box): unify box service lifecycle and local runtime
management
* refactor(box): remove legacy in-process runtime code and clean up smells
After the architecture settled on always using an independent Box Runtime
service, several pieces of compatibility code and design shortcuts were
left behind. This commit cleans them up:
- Remove `LocalBoxRuntimeClient` and `create_box_runtime_client` from
production code (moved to test-only helper).
- Remove unused `_clip_bytes` method from backend.
- Remove `__langbot_session_placeholder__` hack by making `BoxSpec.cmd`
default to empty and validating non-empty only in `runtime.execute()`.
- Extract `get_box_config()` helper to eliminate 5× duplicated config
access boilerplate.
- Remove `session_id`/`host_path`/`host_path_mode` from the LLM-facing
tool schema to enforce request-scoped session isolation.
- Fix dual shutdown path: `NativeToolLoader.shutdown()` no longer calls
`box_service.shutdown()` (handled by `Application.dispose()`).
- Simplify `_assert_session_compatible` with a loop.
- Inline client creation in `BoxRuntimeConnector`.
- Remove redundant `BOX__RUNTIME_URL` env var from docker-compose
(auto-detected by code).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(box/mcp): integrate MCP stdio with Box sandbox — auto-isolation, dep install, security
## Summary
When Podman/Docker is available, all stdio-mode MCP servers now automatically
run inside Box containers with dependency installation, path rewriting, and
lifecycle management. When no container runtime exists, LangBot starts normally
and stdio MCP falls back to host-direct execution.
## What changed
### MCP stdio → Box integration (mcp.py)
- Add `MCPServerBoxConfig` pydantic model for structured box configuration
with validation and defaults (network, host_path_mode, timeouts, resources)
- Auto-infer `host_path` from command/args with venv detection: recognizes
`.venv/bin/python` patterns and walks up to the project root
- Rewrite host paths to container `/workspace` paths transparently
- Replace venv python commands with container-native `python`
- Auto-detect `pyproject.toml`/`setup.py`/`requirements.txt` and run
`pip install` inside the container before starting the MCP server
- Copy project to `/tmp` before install to handle read-only mounts
- Add retry with exponential backoff (3 retries, 2s/4s/8s delays)
- Add Box managed process health monitoring (poll every 5s)
- Fix session leak: `_cleanup_box_stdio_session()` now runs in `finally`
block of `_lifecycle_loop`, covering all exit paths
- Fix retry logic: `_ready_event` is only set after all retries exhaust
or on success, not on first failure
- Enhance `get_runtime_info_dict()` with `box_session_id` and `box_enabled`
### Box security (security.py — new)
- `validate_sandbox_security()` blocks dangerous host paths:
`/etc`, `/proc`, `/sys`, `/dev`, `/root`, `/boot`, `/run`,
docker.sock, podman socket
- Called at the start of `CLISandboxBackend.start_session()`
### Box models (models.py)
- Add `BoxHostMountMode.NONE` — skips volume mount entirely
- Adjust `validate_host_mount_consistency` to allow arbitrary workdir
when `host_path_mode=NONE`
### Box backend (backend.py)
- Add `validate_sandbox_security()` call in `start_session()`
- Add `langbot.box.config_hash` label on containers for drift detection
- Handle `BoxHostMountMode.NONE` — skip `-v` mount arg
- Add `cleanup_orphaned_containers()` to base class (no-op default) and
CLI implementation (single batched `rm -f` command)
### Box runtime (runtime.py)
- Call `cleanup_orphaned_containers()` during `initialize()` to remove
lingering containers from previous runs
### Box service (service.py)
- Graceful degradation: `initialize()` catches runtime errors and sets
`available=False` instead of crashing LangBot startup
- Add `available` property and guard on `execute_sandbox_tool()`
- Add `skip_host_mount_validation` parameter to `build_spec()` and
`create_session()` — MCP paths are admin-configured and trusted,
bypassing `allowed_host_mount_roots` restrictions meant for
LLM-generated sandbox_exec commands
### Default behavior
- stdio MCP servers automatically use Box when `box_service.available`
is True (Podman/Docker detected); no explicit `box` config needed
- When no container runtime exists, falls back to host-direct stdio
- MCP Box defaults: `network=on` (for pip install), `read_only_rootfs=false`
(for site-packages), `host_path_mode=ro`, `startup_timeout=120s`
### Tests
- `test_box_security.py`: blocked paths, safe paths, subpath rejection
- `test_mcp_box_integration.py`: config model, path rewriting, venv
unwrap, host_path inference, payload building, runtime info, box
availability check
- `test_box_service.py`: `BoxHostMountMode.NONE` validation tests
* feat(box/mcp): instance-based orphan cleanup, error classification, session API, and integration tests
## Changes
### Precise orphan container cleanup
- Runtime generates a unique instance_id on startup
- Every container gets a `langbot.box.instance_id` label
- `cleanup_orphaned_containers()` only removes containers from
previous instances, preserving containers owned by the current one
- Containers from older versions (no label) are also cleaned up
- `cleanup_orphaned_containers` added to `BaseSandboxBackend` as
a no-op default method, removing hasattr duck-typing
### Fine-grained MCP error classification
- New `MCPSessionErrorPhase` enum with 7 phases: session_create,
dep_install, process_start, relay_connect, mcp_init, runtime,
tool_call
- Each phase in `_init_box_stdio_server()` sets the error phase
before re-raising, enabling precise failure diagnosis
- `retry_count` tracked across retry attempts
- `get_runtime_info_dict()` exposes `error_phase` and `retry_count`
### GET /v1/sessions/{id} API
- `BoxRuntime.get_session()` returns session details including
managed process info when present
- `handle_get_session` HTTP handler + route in server.py
- `BoxRuntimeClient.get_session()` abstract method + remote impl
### stdio defaults to Box when runtime is available
- `_uses_box_stdio()` checks `box_service.available` instead of
requiring explicit `box` key in server_config
- `BoxService.initialize()` catches runtime errors gracefully,
sets `available=False` instead of crashing LangBot startup
- When no container runtime exists, stdio MCP falls back to
host-direct execution
### Code quality (from /simplify review)
- Extracted `_VENV_DIRS` / `_VENV_BIN_DIRS` module-level constants
- Removed dead `_box_network_mode()` method and unused `bc` variable
- Fixed broken import `from ....box.models` → `from ...box.models`
- Cached `_resolve_host_path()` result — computed once, passed through
- Config hash now includes `host_path` field
- Batched orphan cleanup into single `rm -f` command
### Session leak fix
- `_cleanup_box_stdio_session()` now runs in `_lifecycle_loop`'s
finally block, covering all exit paths (normal shutdown, error,
retry, final failure)
### Integration tests
- 6 end-to-end tests covering managed process lifecycle, WebSocket
stdio bidirectional IO, session cleanup verification, single
session query, process exit detection, and orphan cleanup safety
* refactor: use rpc
* fix: import
* refactor(box): clean up sandbox subsystem code quality and efficiency
- Fix O(n²) stderr trimming in runtime.py with running length tracker
- Remove dead code: RESERVED_CONTAINER_PATHS, _subprocess_wait_task,
unused config_hash computation, unused imports
- Deduplicate connection callback in BoxRuntimeConnector, parse URL once
- Use enum comparison instead of stringly-typed spec.network.value check
- Replace manual _result_to_dict/_session_to_dict with model_dump()
- Cache NativeToolLoader tool definition and sandbox system guidance
- Extract _is_path_under() helper to eliminate duplicated path checks
- Import SANDBOX_EXEC_TOOL_NAME from native.py instead of redefining
- Add JSON startswith guard in logging_utils to skip futile json.loads
- Fix ruff lint errors (F401 unused imports, F841 unused variables)
* fix: ruff
* refactor(sandbox): keep box logic out of pipeline and localagent
- Move sandbox system-prompt guidance from LocalAgentRunner into
BoxService.get_system_guidance() so all box domain knowledge stays
in the box module.
- Remove standalone logging_utils.py; merge format_result_log() into
MessageHandler base class alongside cut_str().
- Strip sandbox-specific JSON parsing from log formatting; tool
results now use generic truncation.
- Revert TYPE_CHECKING changes in stage.py and runner.py that were
unrelated to this feature.
- Skip two test files affected by a pre-existing circular import
(runner ↔ app) until the import cycle is resolved in a separate PR.
* refactor(box): move box runtime to langbot-plugin-sdk
Extract self-contained box runtime modules (actions, backend, client,
errors, models, runtime, security, server) to langbot-plugin-sdk and
update all imports to use `langbot_plugin.box.*`. Keep only service
and
connector in LangBot core as they depend on the Application context.
- Update docker-compose to use `langbot_plugin.box.server` entry
point
- Update pyproject.toml to use local SDK via `tool.uv.sources`
- Remove migrated source files and their unit/integration tests
- Update remaining test imports to match new module paths
* fix: ruff
* fix(box): tighten sandbox exposure and restore box integration coverage
* refactor(types): remove quoted annotations under postponed evaluation
* chore(sandbox): move MCP loader changes to follow-up branch
* refactor(plugins): simplify GitHub install flow to default master archive
* revert(api): restore plugin GitHub import flow in plugins controller
* Improve data-root handling and skill install previews
* Add managed skill authoring tools for local agents
* Refactor the skills UI around sidebar detail pages
* Document why managed skill authoring tools bypass box
* fix: lint
* feat(web): refactor plugin/skill install flows and fix skills page
- Fix sidebar skill icon
- Add skills route and error page component
- Refactor plugin GitHub install from dialog modal to inline card
- Add skill install dropdown menu (create/upload/github) in sidebar
- Wire sidebar → skills page communication via pendingSkillInstallAction context
- Add i18n keys for error page and skill install actions
* fix(web): persist sidebar collapsible section open state on navigation
Sections opened via sub-item navigation now retain their expanded state
when the user switches to a different section, instead of collapsing
because the isActive fallback becomes false.
---------
Co-authored-by: youhuanghe <1051233107@qq.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Junyan Qin <rockchinq@gmail.com>
* feat(sandbox): add MCP box integration on top of sandbox base (#2083)
* refactor(mcp): extract box stdio runtime helper
* refactor(box): introduce reusable workspace session helper
* refactor(box): run Box Runtime as subprocess inside LangBot container
Remove the separate langbot_box_runtime Docker service. Box Runtime
now always launches as a local stdio subprocess, regardless of whether
LangBot runs in Docker or not. The WebSocket transport path is kept
only for explicit runtime_url configuration (remote deployment).
This simplifies deployment by eliminating cross-container path mapping
and network hops. Box Runtime is a pure scheduling process (talks to
Docker socket / nsjail), it does not execute user code or touch the
filesystem, so container isolation is unnecessary — unlike Plugin
Runtime.
* fix(web): prevent first-emission snapshot from swallowing unsaved changes in pipeline editor
When switching runner (e.g. local-agent → n8n), the newly mounted stage's
first emit would re-capture the saved snapshot, erasing the dirty state
caused by the runner change. The save button would incorrectly go dim.
- Skip snapshot re-capture in handleDynamicFormEmit when form is already dirty
- Add mount-time emit to N8nAuthFormComponent (matching DynamicFormComponent)
- Use stable onSubmitRef to prevent useEffect subscription churn
- Add previousInitialValues guard to prevent initialValues echo loops
* style(web): align plugin list header button heights
* docs(review): update Box architecture review documents
Replace old review docs with 5 focused documents:
- box-architecture.md: deep architecture analysis (LangBot + SDK)
- box-issues.md: 22 issues rated P0/P1/P2
- box-test-coverage.md: test coverage analysis
- box-tob-analysis.md: toB commercialization analysis
- box-vs-plugin-runtime.md: Box vs Plugin runtime comparison
* feat(web): improve login error layout and add Terms of Service link
- Improve backend connection error display with bordered container,
inline icon, and better visual hierarchy
- Extract actual error message from axios response object
- Add Terms of Service link (https://langbot.app/terms) to login footer
- Add termsOfService i18n key for all 7 locales
* refactor(web): replace all hardcoded SVG icons with lucide-react
Unify icon usage across the entire frontend by replacing 67 hardcoded
SVG icons with lucide-react components across ~25 files. This improves
consistency, maintainability, and reduces bundle duplication.
Key replacements:
- Sidebar nav: Zap, LayoutDashboard, Bot, Workflow, BookMarked, etc.
- MCP forms: Loader2, XCircle, Trash2
- Monitoring: Sparkles, MessageSquare, CheckCircle2, RefreshCw, etc.
- Cards: Clock, Star, Workflow, Hexagon, Puzzle, Github, etc.
- Misc: Paperclip, AudioLines, CloudUpload, Layers, Heart, Smile
Zero hardcoded <svg> tags remain in .tsx files.
* fix(web): stop polling plugin tasks when no active installs
The PluginInstallTaskProvider was unconditionally polling
getAsyncTasks every 3s on all /home/* routes. Now it only
syncs once on mount and starts periodic polling only when
there are active (non-terminal) install tasks.
* fix(deps): update langbot-plugin version and add new dependencies
* refactor: use Space API for release checks and stop idle polling
- version.py: switch release list API from GitHub to space.langbot.app,
remove unused in-place update logic (update_all, compare_version_str),
translate all comments/logs to English
- PluginInstallTaskContext: only poll when active install tasks exist
* feat(box): add --standalone-box flag and 3-way transport decision for Box runtime
Align Box runtime connection logic with Plugin runtime's pattern:
- Docker: WebSocket to langbot_box container (ws://langbot_box:5411)
- --standalone-box: WebSocket to external Box process (ws://localhost:5411)
- Windows: subprocess + WebSocket (workaround for async stdio limitation)
- Unix/macOS: subprocess + stdio pipe (unchanged)
BoxRuntimeConnector now inherits ManagedRuntimeConnector for subprocess
lifecycle reuse. Add langbot_box service to docker-compose.yaml.
* refactor(box): use single port with path-based routing for Box WS
Update connector to use ws://host:5410/rpc/ws instead of ws://host:5411.
Update review docs to reflect the single-port architecture.
* feat(web): show Box runtime status in plugin debug info popover
Add Box status section to the debug info popover on the plugin list page,
displaying connection status, backend info, profile, active sessions,
and recent error count. Fetched from GET /api/v1/box/status in parallel
with plugin debug info. Includes i18n for all 8 supported languages.
* fix(web): remove ephemeral sandbox count from Box status display
The active_sessions count reflects transient sandbox containers that
expire after 5 minutes of inactivity, making it misleading in the UI.
Keep only connection status, backend, profile, and error count.
* feat(box): configurable sandbox scope and unified skill containers
Replace the per-message session_id with a template-based system
configurable per pipeline via 'Sandbox Scope' in the local-agent panel.
Default scope is per-chat ({launcher_type}_{launcher_id}).
Unify skill exec into the same container as default exec — skills are
mounted at /workspace/.skills/{name}/ via extra_mounts instead of
getting separate containers. All pipeline-bound skills are injected
at container creation time.
- Add box-session-id-template to pipeline metadata (select, 4 options, 8 languages)
- Add resolve_box_session_id() and build_skill_extra_mounts() to BoxService
- Rewrite native.py skill exec path to use execute_tool with shared session
- Update tests for new session_id format
- Add design doc: docs/review/box-session-scope.md
* feat(web): show active sandbox details in Box status popover
Display sandbox count and a detailed list of active sessions including
session ID, image, backend, resources (CPU/memory), network mode, and
last used time. Fetched from GET /api/v1/box/sessions in parallel.
Includes i18n for all 8 supported languages.
* feat(box): add startup and availability logging for sandbox tools
Log Box runtime initialization result (success with profile info, or
failure warning). Log native tool availability status at ToolManager
startup so it's immediately clear whether exec/read/write/edit tools
are registered for the LLM.
* feat(box): support custom sandbox container image via config.yaml
Add 'image' field to box config section. When set, it overrides the
profile default image (python:3.11-slim) for all sandbox containers.
Priority: caller-specified > config.yaml image > profile default.
* feat(box): add heartbeat and reconnection for Box runtime connector
Add 20-second heartbeat ping loop to detect silent Box runtime
disconnections. On disconnect, set available=false and attempt
reconnection after 3 seconds via the disconnect callback chain.
- BoxRuntimeConnector: heartbeat loop, disconnect callback parameter,
disconnect detection in connection callback and WS failure handler
- BoxService: wire disconnect callback to toggle available state and
re-initialize the connector on reconnection
* feat(web): move runtime status to dashboard, clean up plugin debug popover
Add SystemStatusCards component to the monitoring dashboard showing
Plugin Runtime and Box Runtime connection status with details (backend,
profile, sandbox count). Remove all Box/session status from the plugin
page debug popover — it now only shows debug URL and key.
Includes i18n for all 8 supported languages.
* refactor(web): compact system status into a single card alongside metrics
Replace the separate two-card row with a single compact 'System Status'
card placed as the 5th column in the metrics grid. Shows green/red dots
for Plugin Runtime and Box Runtime. Click to expand a popover with
connection details (backend, profile, sandbox count).
* feat: show connector error details for Plugin and Box runtime status
Record Box connector error in BoxService and expose it as
'connector_error' in GET /api/v1/box/status when unavailable.
Display error messages in the dashboard System Status popover
for both Plugin Runtime (plugin_connector_error) and Box Runtime
(connector_error) when they are disconnected.
* fix(web): auto-refresh system status and show disconnect errors in real time
Poll Plugin Runtime and Box Runtime status every 30 seconds so the
dashboard reflects disconnections without a manual page refresh.
Also re-fetch when the popover is opened for immediate feedback.
* fix(box): handle RPC failure in get_status/get_sessions gracefully
When the Box runtime disconnects, there is a race between the heartbeat
flipping _available=false and the frontend polling get_status(). If the
poll arrives first, client.get_status() throws a ConnectionClosedError
which propagated as a 500, causing the frontend to show a grey dot
(null status) instead of a red dot with error details.
Now get_status() catches RPC errors and returns available=false with
the exception message as connector_error. get_sessions() returns an
empty list when unavailable or on RPC failure.
* fix(box): add persistent reconnection loop with exponential backoff
The previous disconnect handler only retried once and then gave up.
Now spawns a background task that retries with exponential backoff
(3s, 6s, 12s, ... up to 60s) until the Box runtime is reachable again.
Uses a _reconnecting guard to prevent duplicate loops. Calls
connector.dispose() before each retry to clean up stale tasks.
* fix(box): detect disconnect when handler.run() returns normally
The generic Handler.run() catches ConnectionClosedError and breaks out
of its loop (normal return) instead of raising, because it has no
disconnect_callback. The old code only triggered reconnection in the
except branch, so a clean WebSocket close was never detected.
Now treat handler.run() returning normally (after successful handshake)
as a disconnect event, triggering the reconnection callback.
* fix(web): refresh system status card when clicking Refresh Data button
Pass a refreshKey prop through OverviewCards to SystemStatusCard that
increments on each Refresh Data click, triggering a re-fetch of Plugin
and Box runtime status alongside the monitoring data refresh.
* fix(web): fix system status card stuck in loading state
fetchStatus(showLoading=false) never called setLoading(false), so the
initial loading=true was never cleared. Simplify to always setLoading
in the finally block — the spinner only shows on the very first load
since subsequent fetches complete near-instantly.
* feat(web): show active sandbox details in dashboard Box status popover
Fetch box sessions alongside status and display each active sandbox
in the popover with session ID, image, resources (CPU/memory), and
last used time.
* feat(box): add global sandbox scope option
Add a 'Global (shared by all)' option to the sandbox scope selector.
Uses a constant '{global}' template variable that always resolves to
'global', so all users and chats share one sandbox container.
* refactor(web): replace popover with dialog for system status details
Replace the dropdown popover with a proper Dialog for runtime status
details. Add a small info button on the System Status card that opens
the dialog. Session details now show in a spacious 2-column grid layout
with full image name, backend, CPU/memory, network, mount path, and
created/last-used timestamps.
* fix(web): widen system status dialog and fix scroll border issue
Use max-w-2xl (matching other dialogs) instead of max-w-lg. Move
overflow-y-auto to an inner container with overflow-hidden on
DialogContent to prevent padding bleed at scroll edges.
* feat(web): add tooltips for truncated fields in system status dialog
Wrap session_id, image, and mount path fields with Tooltip components
so hovering over truncated text shows the full value.
* feat: add download button
* feat: successfully install
* feat: delete old filter
* feat: youhua frontend
* fix: align box runtime launch args
* feat: translate
* feat: refactor market
* feat: youhua qianduan
* chore: rename extension zh translation
* feat(extensions): unify extensions endpoint and refresh extensions page UX
- Rename /home/plugins route to /home/extensions and update all sidebar links.
- Add unified GET /api/v1/extensions returning plugins, MCP servers and skills,
sorted by name; replace the three separate frontend fetches with this single call.
- Migrate the extensions page to shadcn primitives (Tabs/Card/Alert/Badge/Skeleton/
Switch/Label) and clean up hardcoded color tokens on the extension card.
- Add a localStorage-persisted "Group by type" switch that, when enabled in the
All Types tab, renders extensions grouped by type with a compact section header.
- Show a spinner while loading and rename the empty-state copy from
"No plugins installed" to "No extensions installed".
- Rename the "格式 / Formats" filter label to "类型 / Types" across all 8 locales.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(extensions): fallback lucide icon when extension icon is missing
Render a tinted lucide icon (Puzzle / Server / Sparkles) on the extension
card when the icon URL is empty or the image fails to load. Picked icons
distinct from EventListener (AudioWaveform) and KnowledgeEngine (Book) to
avoid visual collision with plugin component badges.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(sidebar): unify installed-extensions list with plugins, MCP and skills
- Render plugins, MCP servers and skills together under the "Installed
Extensions" sidebar entry, alphabetically sorted to match the list page.
- Resolve per-item routes by extension type (plugin -> /home/extensions,
mcp -> /home/mcp, skill -> /home/skills) and gate the plugin-only hover
context menu on extensionType === 'plugin'.
- Lift the "group by type" toggle into SidebarDataContext (still persisted
in localStorage) so the sidebar groups items with section headers
whenever the list page has the toggle enabled.
- Show lucide fallback icons (Server / Sparkles / Puzzle) tinted in the
LangBot blue for MCP, skill, and missing-icon plugin items, overriding
the SidebarMenuSubButton svg color rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(extensions): mobile-friendly layout for extensions and add-extension pages
- Stack the extensions page header vertically on small screens, let the
filter Tabs scroll horizontally if they overflow, hide the debug
button label below sm and let the install/debug controls wrap.
- Constrain the debug popover and its inputs to the viewport width so
they no longer overflow on phone-sized screens.
- Drop the card grid from a fixed 30rem column to a min(100%, 22rem)
column at base / 28rem at sm, and reduce the gap, so cards render
cleanly at 360px+ widths in both flat and grouped views.
- Make the add-extension header actions wrap on lg- viewports and the
install dialog responsive instead of a hard 500px box.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: change ui
* feat: delete version for mcp and skills
* fix: constrain home page content width
* fix: preserve monitoring card borders under sticky filters
* fix(box): restore sandbox config and shared mcp runtime
* fix(box): harden sandbox session isolation
* fix(skill): remove auto activation setting
* feat(skill): align skill system with Claude Code's Tool Call design
- Replace text marker activation with `activate` tool (Tool Call mechanism)
- Replace 7 authoring tools with 2: `activate` + `register_skill`
- Add builtin skills loading from templates/skills/
- Add create-skill as first builtin skill
- Remove SKILL_ACTIVATION_MARKER and text detection methods
- Tool Result returns SKILL.md content (protects KV Cache)
This aligns with Claude Code's progressive disclosure pattern:
- Metadata (name+description) always visible in tool description
- SKILL.md body loaded on activate via Tool Call
- Bundled resources accessible through virtual path mapping
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat(tools): add glob and grep native sandbox tools
Add file discovery and content search capabilities to the sandbox:
- glob: Find files by pattern (supports ** recursive matching)
- grep: Search file contents with regex patterns
Both tools respect skill package paths and include safety limits
(max 100 files for glob, max 200 matches for grep).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(skill): add skill file browsing capability
- Add API endpoints for listing/reading/writing skill files
- Add FileTree component in SkillForm for directory browsing
- Users can now view scripts/, references/, assets/ directories
- Files can be selected and edited in the instructions textarea
- Add translations for new file browsing features
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(skill): copy builtin skills to data/skills on startup
- Builtin skills (templates/skills/) are now copied to data/skills/
- Users can view and manage builtin skills in the UI
- Rename SkillAuthoringToolLoader to SkillToolLoader
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(skill): improve file browsing and fix path handling
- Fix nested directory display in skill file tree (preserve root entries)
- Fix file content display when clicking files in skill browser
- Add skill manager and tool manager as proper package modules
- Separate fileContent state to allow editing non-SKILL.md files
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(toolmgr): correct skill_tool_loader attribute name
Rename skill_authoring_tool_loader to skill_tool_loader in execute_func_call
and shutdown methods to match the attribute defined in initialize().
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(native): update tool descriptions to use register_skill
Replace references to removed import_skill_from_directory with
register_skill in exec/write/edit tool descriptions.
* feat(toolmgr): enhance tool initialization with backend availability checks
* refactor: remove unused imports and clean up code in various files
* feat: polish extension detail pages
* feat: persist sidebar list expansion
* fix: refine extension ui and backend errors
* fix: align add extension marketplace ui
* feat: manage skills through box runtime
* feat: support github skill installation
* fix: import github skill directories
* feat: install market extensions from card click
* feat(web): improve skill import flow
* feat: polish extension import flow
* fix(mcp): stabilize shared box managed processes
* fix(web): improve backend retry and sidebar scrolling
* docs(review): refresh box architecture review for feat/sandbox
Sync the docs/review/ suite to the current state of the feat/sandbox branch
(both LangBot and langbot-plugin-sdk), ~30 commits ahead of the prior review.
- box-architecture.md: rewrite for the new box.{backend,runtime,local,e2b}
config schema, add E2B backend, 6 native tools (incl. glob/grep), Skill
Tool Call activation, shared multi-process MCP container, SkillManager,
BoxSkillStore (SDK), 25 actions, 9 error types, heartbeat/reconnect
- box-issues.md: move resolved items (reconnect, heartbeat, Windows, nsjail
image conflict, frontend monitoring card) into a Resolved section; add
new P0 (INIT/backend ordering), P1 (extra_mounts immutability after
container creation), P2 (skill_store test gap, integration tests not in CI)
- box-session-scope.md: add §0 Implementation Status — Phase 1 shipped,
MCP unification landed earlier than originally scoped
- box-test-coverage.md: realign file inventory (4,400 -> 6,500 LOC),
add 7 new test files including SDK backend_selection/e2b/skill_store
- box-tob-analysis.md: connection recovery now满足基本要求; add E2B and
backend self-heal to capabilities; tick off Phase 1 reconnect/heartbeat
- box-vs-plugin-runtime.md: heartbeat/reconnect/Windows support now aligned
with Plugin Runtime; revise remaining gaps (WS auth, shared base class)
* refactor(box): use unified env-override mechanism for box.local config
The box module hand-rolled its own LANGBOT_BOX_LOCAL_* env parsing in two
places (connector._get_box_config and service._local_config), duplicating
logic that LoadConfigStage._apply_env_overrides_to_config already provides
generically via the SECTION__SUBSECTION__KEY convention.
- Drop the bespoke LANGBOT_BOX_LOCAL_* parsing; read box.local straight
from instance_config (the unified BOX__LOCAL__* overrides are already
applied before BoxService initializes)
- Harden _load_allowed_mount_roots to accept a comma-separated string,
since the generic mechanism stores a freshly-created key as a raw
string when config.yaml has no box.local.allowed_mount_roots entry
- docker-compose: rename the langbot container env vars to
BOX__LOCAL__* (the canonical convention); remove them entirely from
the langbot_box container — the Box runtime never reads box.local from
env/config.yaml, it is configured via the INIT RPC action
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: repair stale skill/sandbox tests for feat/sandbox
The skill subsystem moved to Tool-Call activation and a Box-managed
skill store; several tests still asserted removed APIs and a sys.modules
stub leaked across the suite. Full unit suite now green (was 23 failing).
- test_skill_tools: drop TestSkillManagerActivation (text-marker API
removed); rewrite TestSkillActivationHelper around the current
skill.activation.register_activated_skill; replace the CRUD
TestSkillAuthoringToolLoader with TestSkillToolLoader covering the
current activate/register_skill tools and sandbox-availability gating
- test_tool_manager_native: ToolManager attr is skill_tool_loader (not
skill_authoring_tool_loader); native loader now exposes 6 tools
(exec/read/write/edit/glob/grep) and requires initialize() with a
backend-available get_status()
- test_localagent_sandbox_exec: remove obsolete activation-marker
leakage tests and their helper providers
- test_model_service / pipeline conftest: give the mocks skill_mgr=None
so PreProcessor's local-agent skill-binding guard short-circuits
- test_n8nsvapi: stop permanently overwriting sys.modules
('langbot.pkg.provider.runner' etc.); save and restore around the
import so other modules get the real LocalAgentRunner base class
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(tests): run unit tests on every push to feat/** branches
- Add feat/** to push branches so long-lived feature branches are
tested on every push (they accumulate large changes before a PR)
- Drop the push path filter entirely: every push to master/develop/
feat/** now runs the full unit suite (the old 'pkg/**' filter never
matched the real source path 'src/langbot/pkg/**', so backend-only
pushes silently skipped tests)
- Fix the same broken path glob on the pull_request trigger
('pkg/**' -> 'src/langbot/pkg/**')
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(skill): harden mount/reload paths and HTTP errors against stale skill cache
The Box backends behave inconsistently when extra_mounts reference a
missing host directory (nsjail aborts the entire sandbox start, Docker
silently creates a root-owned empty dir on the host, E2B silently skips
the upload). The cache in skill_mgr.skills is only refreshed on
in-process mutations, so out-of-band changes — container rebuilds,
manual rm in the box volume, anything the LangBot API didn't drive —
leave a stale skill that later produces one of those bad mount paths.
- box/service.py: build_skill_extra_mounts now filters skills whose
package_root is not isdir on the LangBot-visible filesystem and logs
a warning, instead of passing the bad mount through to the backend
- skill/manager.py: reload_skills (Box path) drops skills whose
package_root is missing on the LangBot-side filesystem before they
reach the in-memory cache, with a summary warning
- api/http/controller/groups/skills.py: file/CRUD handlers now also
catch BoxError (RuntimeError subclass, previously slipping past
``except ValueError`` and surfacing as 500); list/get handlers gain
a try/except so a transient Box RPC failure becomes a clean 400
instead of a stack trace
Tests added for build_skill_extra_mounts (skip missing, skip empty,
no skill manager) and SkillManager.reload_skills (drop missing on Box
path). Full unit suite: 279 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(box): add box.enabled toggle and gate consumers on availability
Make the Box sandbox runtime optional. When ``box.enabled`` is false in
config (or when an enabled Box fails to connect), every dependent feature
degrades to the same disabled-state UX rather than crashing or silently
falling back to less safe code paths.
Backend:
- config.yaml: new top-level ``box.enabled: true`` flag (default true)
- BoxService:
- Read box.enabled on construction
- initialize() short-circuits when disabled — no remote WS connect, no
stdio subprocess fork
- _on_runtime_disconnect is a no-op when disabled (no reconnect loop
on a deliberately-off service)
- get_status() now exposes ``enabled`` so the frontend can tell
"disabled in config" from "configured but failed"
- MCP stdio loader (mcp_stdio.uses_box_stdio): requires box_service to
be available, not just installed
- MCP _init_stdio_python_server: when ap.box_service exists but is
unavailable, refuse the stdio server with an actionable error instead
of silently falling through to host-stdio (which bypasses the sandbox
the operator asked for). Setups without ap.box_service installed at
all keep the legacy host-stdio fallback for pre-Box dev mode
- SkillService._require_box_for_write: refuses create/update/install/
write_skill_file when ap.box_service is installed but unavailable.
Distinguishes disabled vs failed in the error message so the UI can
surface the right hint. Legacy setups (no ap.box_service) keep the
local fallback path — that distinction is what keeps the existing
local-skills tests valid
Tests:
- Box disabled-state behavior (4 cases)
- Skill write refusal in disabled & failed states (7 cases)
- MCP stdio runtime info policy updated to match new refuse-when-down
behavior
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(web): surface Box disabled/unavailable state across consumers
When Box is disabled in config (``box.enabled = false``) or fails to
connect, every dependent UI surface now degrades visibly:
- ``useBoxStatus`` hook: shared, polled 30s, exposes ``available``,
``disabled`` (config-off) and a single ``hint`` key so callers don't
have to re-derive the three states
- ``BoxUnavailableNotice`` reusable Alert banner driven by that hint
- Dashboard SystemStatusCards: three-state dot + label
(connected / disabled-gray / disconnected-red); disabled state shows
the ``boxDisabled`` hint, failed state continues to show the connector
error. Plugin block kept untouched
- Skills page (create view) and SkillDetailContent (edit view):
Save button disabled and banner inserted above the form when Box is
unavailable — matches the backend gate added in the previous commit
- PipelineExtension skill section: ``enable_all_skills`` switch, Add
Skill button and Remove buttons all gate on Box availability;
banner inline under the section header
- PipelineFormComponent: banner above the ``local-agent`` stage card
when Box is unavailable, since that stage carries the sandbox-bound
``box-session-id-template`` field
- Box status payload type (``ApiRespBoxStatus.enabled``) and 8 locale
files updated with ``boxDisabled`` / ``boxUnavailable`` /
``boxRequiredHint`` strings
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(box): document the box.enabled toggle and gate behavior matrix
- docker-compose: move ``langbot_box`` under compose profiles
(``box`` and ``all``) so ``docker compose up`` no longer requires
the sandbox container. Inline comment explains how to pair the
profile choice with ``box.enabled`` so the langbot service does not
thrash trying to reach a runtime that was never started
- docs/review/box-architecture.md:
- Annotate ``box.enabled`` in the config.yaml example, listing the
exact side effects (no remote/stdio connect; tools/skills/MCP
stdio off; reads still work)
- Replace the bare compose snippet with the actual profile-driven
invocation and the BOX__ENABLED pairing
- New "关闭/连接失败时的行为矩阵" section: a single table mapping
every consumer (native tools, activate/register_skill, stdio MCP,
skill list/CRUD, pipeline AI config, extensions page, dashboard)
to its disabled-state behavior, plus the legacy ``ap.box_service``
distinguisher note
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(pipeline-form): swap Box banner for field-level disable_if + tooltip
The previous commit hard-coded a BoxUnavailableNotice banner above the
``local-agent`` stage card. That works, but it shouts at the user about
every field in that stage when in reality only one field —
``box-session-id-template`` — depends on the sandbox.
Use the dynamic-form schema's existing variable-injection mechanism
(``__system.*`` references via ``systemContext``) and add a sibling to
``show_if``: ``disable_if`` + ``disabled_tooltip``. The field stays
visible, becomes inert, and an info icon next to its label exposes the
reason on hover. The rest of the AI tab is left untouched.
- entities/form/dynamic.ts: extend IDynamicFormItemSchema with
``disable_if: IShowIfCondition`` and ``disabled_tooltip: I18nObject``
- DynamicFormComponent: evaluate disable_if with the same resolver as
show_if; OR the result into isFieldDisabled; render an Info tooltip
trigger next to the label when the condition matches
- ai.yaml metadata: attach disable_if (__system.box_available eq false)
and a localized disabled_tooltip to box-session-id-template
- PipelineFormComponent: drop the BoxUnavailableNotice import and the
per-stage banner; pass ``systemContext={ box_available: boxAvailable }``
only for the local-agent stage so other stages aren't paying the
re-render cost
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(mcp): friendly UI message when stdio MCP refused by Box state
Previously the MCP detail dialog dumped the raw RuntimeError text from
``_init_stdio_python_server`` — English-only, prefixed with "Failed
after 4 attempts", and exposing internal config names. The retry
wrapper also kept retrying a refusal that is deterministically going
to fail again, polluting logs.
Replace the raw text with a structured signal:
- New ``MCPSessionErrorPhase.BOX_UNAVAILABLE`` enum value. The stdio
refusal path sets it before raising and uses a short opaque
discriminator (``box_disabled_in_config`` / ``box_unavailable``) as
the message body — never user-facing
- ``_lifecycle_loop_with_retry`` short-circuits on
``BOX_UNAVAILABLE``: surfaces the error immediately, no retries,
no "Failed after N attempts" prefix. Silences the warning storm
seen during smoke-testing
- ``MCPServerRuntimeInfo`` (TS type) now declares ``error_phase``,
``retry_count``, ``box_session_id``, ``box_enabled`` to match what
the backend already returns in get_runtime_info_dict()
- Both MCP detail forms (``mcp/components/mcp-form/MCPForm.tsx`` and
``plugins/mcp-server/mcp-form/MCPFormDialog.tsx``) detect
``error_phase === 'box_unavailable'`` and render a two-line
localized notice: state line ("Box disabled / unreachable") plus
remediation line ("enable Box or switch to http/sse")
- 8 locale files (en/zh-Hans/zh-Hant/ja/ru/vi/th/es) get
``mcp.boxDisabledStdioRefused``, ``mcp.boxUnavailableStdioRefused``,
``mcp.boxStdioRefusedSuggestion``
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(mcp-web): block stdio MCP creation at the form when Box is unavailable
When Box is disabled in config (``box.enabled = false``) or unreachable,
saving a new MCP server in stdio mode produced one that could never
start — the user would only learn that from the runtime error on the
detail page. Stop the user before they save instead.
Both MCP forms (the page-level ``MCPForm.tsx`` and the older dialog
``MCPFormDialog.tsx``) now:
- Disable the ``stdio`` option in the mode select when Box is
unavailable, with a small "(requires Box)" suffix so the reason is
obvious. Existing stdio configs still display their current value
- Show ``BoxUnavailableNotice`` inline under the mode select when the
currently-selected mode is stdio and Box is unavailable, so editing
a stale stdio config makes the cause visible
- Disable the Save / Submit button while stdio is selected under that
condition. ``MCPForm`` exposes a new ``onSaveBlockedChange`` prop
so the parent ``MCPDetailContent`` can disable both its Submit and
Save buttons. ``MCPFormDialog`` disables its Save button locally
- Refuse the submit handler too (Enter-key path) with a toast carrying
the same i18n message
i18n: ``mcp.boxRequired`` (short tag in the disabled option) and
``mcp.stdioBlockedByBoxToast`` added to all 8 locales.
Backend runtime gate (``_init_stdio_python_server`` refusal +
``BOX_UNAVAILABLE`` error_phase + retry short-circuit) stays in place
as the last line of defence for API bypass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(web): prevent plugin config form overflow
* refactor(skill): remove all local-filesystem fallbacks; Box is the sole source
Skills now flow exclusively through the Box runtime. Every read and write
method funnels through ``_box_service()``; when Box is unavailable
(disabled in config, connection failed, or simply not installed) the
operation either returns an empty surface (``list_skills`` → []) or
raises with a clear ``Box runtime ... not initialised / disabled /
unavailable: ...`` message via the new ``_require_box(action)`` helper.
Why: the legacy local-fallback path scanned ``data/skills/``, but Box
manages its own ``box.local.skills_root`` (default ``data/box/skills/``).
The two diverging directories caused stale / phantom skill lists when
Box flapped, and the local-fallback writes silently bypassed all the
sandboxing the operator had configured.
SkillService (``api/http/service/skill.py``):
- New ``_require_box(action)`` returns the box service or raises a
structured ValueError. ``_require_box_for_write`` kept as alias
- ``list_skills`` → returns [] when Box is down so the UI can render
the disabled banner cleanly
- ``get_skill`` / ``get_skill_by_name`` → return None
- All read-file / write-file / scan-dir / create / update / delete /
install / preview methods → ``_require_box`` then box delegate.
Local fallback bodies (shutil.copytree, tempfile.mkdtemp, preview
pipelines) removed entirely
SkillManager (``pkg/skill/manager.py``):
- ``reload_skills`` returns early with empty cache when Box is down.
data/skills/ discovery loop removed
- ``refresh_skill_from_disk`` now just reports cache presence; the
on-disk re-parse is gone since Box is the only writer
Tests:
- Drop 11 obsolete test_skill_service.py tests that exercised the
removed local-fallback paths (create/install/file/delete/update)
- Add list-empty + read-refused tests; flip the legacy-allow test to
legacy-refuses-too
- Rewrite refresh_skill_from_disk test to match the new behaviour
Several helper methods (_managed_skill_path, _resolve_skill_path,
_preview_skill_candidates, _install_preview_candidates, etc.) are now
unreachable; a follow-up commit will prune them so this diff stays
reviewable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(skill): prune dead local-filesystem helpers left over from Box migration
Follow-up to the Box-only refactor. The previous commit removed the
local-fallback BRANCHES from every public method; this one removes the
HELPERS those branches called, which are now unreachable.
SkillService (service/skill.py): 787 → 449 lines
Removed: scan_directory (sync), _read_skill_package, _write_skill_md,
_resolve_create_field, _managed_skill_path,
_managed_install_root_for_package, _normalize_package_root,
_resolve_skill_path, _find_skill_entry, _discover_skill_directories,
_safe_extract_zip, _extract_uploaded_skill_to_temp,
_download_github_skill_to_temp, _resolve_github_source_root,
_build_preview_target_dir, _preview_skill_candidates,
_select_preview_candidates, _install_preview_candidates,
_preview_source_root, _resolve_installed_skills, plus the
module-level _FRONTMATTER_FIELDS and _build_skill_md.
Kept (still needed by the surviving GitHub-import path):
_download_github_asset, _download_github_skill_directory_as_zip,
_find_github_skill_archive_entry, _copy_github_skill_directory_to_zip,
_is_github_skill_md_url, _parse_github_skill_md_url,
_resolve_github_skill_md_package_name, _validate_github_asset_url,
_uploaded_skill_target_stem, _validate_skill_name.
Imports dropped: shutil, tempfile, yaml, ....utils.paths.
SkillManager (skill/manager.py): 187 → 88 lines
Removed: get_managed_skills_root, _discover_skill_directories,
_find_skill_entry, _load_skill_file, _normalize_package_root.
Imports dropped: datetime, parse_frontmatter, paths.
Tests:
- test_skill_service.py: drop the 3 sync scan_directory tests +
skill_service fixture + _create_skill_file helper
- test_skill_tools.py: drop test_load_skill_file_success; rename
TestSkillManagerPackageLoading → TestSkillManagerCache
Full unit suite: 277 passed, 1 skipped. ``ruff check`` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(skill): re-inject skill index into local-agent system prompt
The contributor's original PR (#1917) appended an ``Available Skills``
index to the system prompt before the LLM saw the user message, so the
LLM could decide whether to activate a skill. ``7145447b`` removed the
text-marker activation flow and, together with it, the entire system
prompt injection — but the Tool Call replacement only put the available
skills inside the ``activate`` tool's description. In practice the LLM
ignores tool descriptions for selection and goes straight to native
tools, so user-visible skill activation silently broke.
Restore the injection, adapted for the Tool Call era:
- SkillManager regains ``get_skill_index(bound_skills)`` and
``build_skill_aware_prompt_addition(bound_skills)``. The addendum
carries only ``name (display_name): description`` for each
pipeline-visible skill plus one instruction line pointing at the
``activate`` tool. No SKILL.md contents — KV cache stays clean
- PreProcessor appends the addendum to the first system message (or
inserts a new one) of ``query.prompt.messages`` for the local-agent
runner. Handles plain-string and ContentElement[] bodies. Skips
cleanly when no skills are visible
- 3 new test_preproc cases: injection happens, bound-skills subset
honoured, empty addendum touches nothing. 280 passed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(box): downgrade get_status.available when backend probed unavailable
Until now ``BoxService.get_status`` returned ``available: true`` whenever
the runtime connector was healthy, even if the runtime itself reported
``backend: { available: false }`` (operator selected nsjail without the
binary, Docker daemon crashed mid-session, E2B credentials wrong, ...).
The dashboard / ``useBoxStatus`` hook / skill_service gate consumed the
top-level flag and showed "connected" while every actual call to native
exec or skill management would fail.
The native-tool loader already polled ``status.backend.available``
independently and hid its tools correctly, but every other consumer
(dashboard banner, the disabled-state hint, the LLM-facing message)
disagreed with it.
Combine the two in the payload: ``available = self._available AND
status.backend.available``. When ``backend.available`` is false we now
also surface a ``connector_error`` that names the backend
("Configured sandbox backend \"nsjail\" is unavailable") so the dialog
shows the actionable reason instead of an empty error pane. The
detailed ``backend`` object is preserved unchanged for the dialog.
Internal ``box_service.available`` (used by ``skill_service`` writes,
``mcp_stdio.uses_box_stdio``, the reconnect callback) is intentionally
NOT changed — it still tracks connector health only, so a backend blip
does not trigger spurious reconnect loops.
Tests:
- ``test_get_status_downgrades_available_when_backend_dead`` — exercise
the new branch (connector OK, backend.available=false → top-level
available=false, connector_error mentions the backend name)
- ``test_get_status_keeps_available_true_when_backend_ok`` — guard
against regressing the happy path
Live-verified with ``box.backend: nsjail`` on macOS (no nsjail binary):
``GET /api/v1/box/status`` now returns ``available: false`` with the
named connector_error, instead of the previous misleading
``available: true``.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(web): surface the specific Box failure reason in unavailable banner
When Box is configured but the runtime reports its backend is dead
(e.g. ``box.backend = nsjail`` but the binary is missing, or Docker
daemon crashed), the backend now returns a structured
``connector_error`` like ``Configured sandbox backend "nsjail" is
unavailable``. The previous notice only said "Box sandbox is
unavailable" + a generic "enable Box" hint, hiding the actionable
detail.
- ``useBoxStatus``: derive ``reason`` from ``status.connector_error``.
Only exposed for the failed-state (``hint === 'boxUnavailable'``),
since the disabled-by-config message already carries its reason
- ``BoxUnavailableNotice``: insert the reason as a small monospaced
line between the state message and the action hint. The disabled
variant is unchanged (operator chose the state)
- Wire ``reason`` through every existing call site (Skills page +
detail, PipelineExtension, both MCP forms). Old unused ``context``
prop dropped
Net layout (3 lines, still compact):
⚠ Box sandbox is unavailable — sandbox tools, skill add/edit, ...
Configured sandbox backend "nsjail" is unavailable
This feature requires the Box runtime. Enable it in config ...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: reconcile master's unit tests with feat/sandbox refactors
The merge from master brought in new unit tests that target pre-refactor
APIs on feat/sandbox. Reconcile each:
- factories/app.py: FakeApp now exposes a Mock skill_mgr (with empty .skills
dict + inert prompt-addition builder) and a Mock pipeline_service so the
PreProcessor skill-index injection branch can run end-to-end in tests.
- pipeline/conftest.py: eagerly import langbot.pkg.pipeline.pipelinemgr so
pipeline.stage is fully initialised before any individual stage test
(preproc, longtext, ...) tries to lazy-load it. Without this preload,
running test_preproc.py in isolation hit a circular-import error via the
stage -> app -> pipelinemgr -> stage chain.
- provider/test_tool_manager.py: ToolManager now probes four loaders
(native -> plugin -> mcp -> skill). Inject inert native + skill mocks in
the execute_func_call fixture and assert all four shutdowns fire.
- utils/test_paths.py: drop the three cwd-dependent _check_if_source_install
cases. The refactor walks Path(__file__).resolve().parents looking for
pyproject.toml + main.py, so cwd no longer factors in and there's no
file read to mock-fail. The positive case and caching test still apply.
- utils/test_version.py: delete entirely. is_newer and compare_version_str
were removed when VersionManager was refactored to use the Space API for
release checks (
|
||
|
|
894709d577 |
feat(qrcode-login): enhance WeChat login flow with expiration handlin… (#2212)
* feat(qrcode-login): enhance WeChat login flow with expiration handling and improved session management * feat(qrcode-login): replace RefreshCw icon with RotateCw for loading state * feat(qrcode-login): adjust session expiration handling and improve error status management |
||
|
|
f0061817ea |
fix: remove /debug/exec endpoint that allows authenticated RCE via exec() (#2178)
The /api/v1/system/debug/exec endpoint passes user-supplied HTTP body directly to Python exec(), enabling arbitrary code execution for any authenticated user when debug_mode is enabled. This is a critical security risk (CWE-94): a single misconfiguration or compromised JWT grants full server-side code execution. Remove the endpoint entirely. The /debug/plugin/action endpoint (which does not use exec()) is left intact as it serves a different, scoped purpose. Co-authored-by: Junyan Chin <rockchinq@gmail.com> |
||
|
|
1bd9c334aa |
fix: load persisted plugin config (#2208)
Co-authored-by: RockChinQ <rockchinq@gmail.com> |
||
|
|
484643c0ee | fix(api): validate api key prefix (#2195) | ||
|
|
ec61459619 | fix(api): avoid mutating bot update payload (#2194) | ||
|
|
10d3a9cc92 | fix(api): avoid mutating pipeline update payload (#2192) | ||
|
|
b251fc4b89 | fix(plugin): resolve plugin page asset origin | ||
|
|
6713b57d01 | feat: enhance API key normalization and improve Space OAuth callback handling | ||
|
|
ea13ef87f2 | feat(provider): add API key normalization and update OpenAI requester initialization | ||
|
|
f412127fb0 |
feat: add one-click app creation for Feishu/dingding/wexin/wecombot with QR code support (#2165)
* feat: add one-click app creation for Feishu with QR code support * feat: implement WeChat QR code login functionality and update related configurations * feat: add qrcode dependency for QR code generation support * feat: enhance QR code login UI and add internationalization support for new labels * feat: new ui back * feat: add DingTalk one-click app creation and QR code login support * feat: add WeComBot one-click creation support and enhance QR code login functionality * feat: Update the robot creation function and bind the most recently updated pipeline |
||
|
|
0ceab3f6a5 |
feat(knowledge): validate required fields based on plugin schema
Add business-agnostic validation for knowledge base creation: - Backend: dynamically validate required fields from plugin's creation_schema and retrieval_schema, with support for show_if conditional fields - Frontend: expose validation function from DynamicFormComponent and validate before KBForm submission - Add i18n translations for validation error messages Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
1fcdbd472f |
fix model runtime uuid after updates (#2160)
* fix model runtime uuid after updates * test: avoid local agent constructor coupling |
||
|
|
0154ea6cd3 |
Fix/storage retention cleanup (#2159)
* fix: add storage retention cleanup * fix: prune completed tasks on completion * fix: complete storage analysis i18n |
||
|
|
12df9d6ee9 |
feat: add plugin extension pages (iframe rendering, Page SDK, security hardening, i18n)
Co-Authored-By: Typer_Body <mcjiekejiemi@163.com> |
||
|
|
564d829e25 |
Feat/webpage adapter (#2135)
* feat: add web_page_bot adapter and embed widget - Implemented a new `web_page_bot` adapter for embedding chat widgets on websites. - Created a new YAML configuration file for `web_page_bot` with necessary metadata and execution details. - Developed the `WebPageBotAdapter` class to handle message events and manage listeners. - Added a JavaScript widget for embedding the chat interface, including styles and functionality for user interaction. - Updated WebSocket handling to support the new bot adapter and manage connections. - Enhanced the bot form to include pipeline UUID and adapter configuration in the system context. - Introduced a new dynamic form item type for embed code in the form entity. * feat(embed): add feedback submission and image upload functionality to embed widget * feat(embed): add reset session endpoint for embed widget and improve WebSocket image handling * feat(widget): remove typing indicator display logic from message handling * fix(embed): security hardening for embed widget - Add UUID format validation for pipeline_uuid parameters - Add Cloudflare Turnstile integration for bot protection (optional) - Add HMAC-signed session tokens for /messages, /reset, /feedback endpoints - Sanitize error responses (remove internal exception details) - Sanitize base_url before JS injection - Fix XSS in markdown link rendering (only allow http/https protocols) - Fix XSS in image URL extraction (only allow http/https/data protocols) - Escape widget title in embed code snippet (HTML entity encoding) - Remove class-level mutable default in WebPageBotAdapter - Remove duplicate config line and console.log in widget.js - Add turnstile_site_key and turnstile_secret_key config fields * style: fix prettier formatting for chained replace calls * fix(embed): declare listeners as Pydantic field in WebPageBotAdapter The base class is a Pydantic BaseModel, so listeners must be declared as a field (with default_factory) rather than assigned in __init__. Also keep the __init__ to convert positional args to keyword args for Pydantic compatibility with botmgr's calling convention. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(embed): use bot_uuid instead of pipeline_uuid in all embed URLs Replace pipeline_uuid with bot_uuid in all user-facing embed widget URLs so internal pipeline identifiers are never exposed. The server resolves bot_uuid to the owning web_page_bot, validates it is enabled and has a pipeline bound, then routes internally using pipeline_uuid. Add a dedicated WebSocket endpoint at /api/v1/embed/<bot_uuid>/ws/connect instead of reusing the pipeline debug path. Wire WebPageBotAdapter to proxy reply_message calls through the WebSocket adapter so dashboard shows the correct adapter name while replies are still delivered. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(embed): improve Turnstile config field descriptions Add guidance on where to obtain the keys (Cloudflare dashboard) and clarify that leaving them empty disables the feature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(embed): add multi-language support for embed widget Add a language selector to the web_page_bot config with 8 locales (en, zh-Hans, zh-Hant, ja, es, ru, th, vi). The backend injects the locale into widget.js which uses a built-in i18n dictionary for all user-facing strings (welcome message, placeholder, aria labels, error messages, powered-by footer). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(embed): use correct select option format for language selector Options must use name/label (i18n object) format, not value/label (plain string), to match the dynamic form renderer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(embed): adjust footer padding and link to langbot.app Increase footer padding for more breathing room from the bottom edge. Change powered-by link from GitHub repo to langbot.app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(embed): ignore Enter key during IME composition Check e.isComposing before treating Enter as send, so confirming an IME candidate (e.g. Chinese/Japanese input) does not also fire the message. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(embed): center bubble icon and fill entire circle Make .lb-chat-icon span fill the full bubble area so the logo image covers the circle completely without exposing the blue background. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(embed): add bubble icon presets selector Add 6 bubble icon options (LangBot logo, chat bubble, robot, headset, sparkle, message) configurable in the bot settings. Icons are inline SVGs in widget.js, selected via a config field injected by the backend. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: RockChinQ <rockchinq@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
58c1916712 |
fix(space): add page_size param to models sync request to fetch all models
The Space API defaults to page_size=20, but the model catalog has grown beyond 20 entries (currently 26), causing models to be silently dropped during sync. |
||
|
|
323481d69b |
Feat/rerank model (#2137)
* feat(provider): add rerank model management as a core model type * feat(provider): add rerank support to existing requesters and new rerank providers * feat(web): add rerank model management UI and pipeline config * fix(provider): correct rerank support_type after verification - Add rerank to OpenRouter (confirmed /api/v1/rerank endpoint) - Remove rerank from Ollama (no native support, PR #7219 unmerged) - Remove rerank from JiekouAI (no rerank docs found, URL path mismatch) * fix(provider): remove alru_cache from model getters and add rerank param hints * fix: resolve lint errors - Remove unused alru_cache import from modelmgr.py - Remove unused error_message variable in invoke_rerank - Fix prettier formatting in frontend files Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: remove unused exception variable - Change `except Exception as e:` to `except Exception:` since e is not used - Fix prettier formatting in ProviderCard.tsx Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: apply ruff format Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(template): add rerank config fields to default pipeline config Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: remove PR.md Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ui): remove duplicate rerank model form in AddModelPopover The form was being rendered twice: once in TabsContent manual mode and again in a separate conditional block for rerank tab. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4e5a6ee79a |
feat(models): add provider model scanning (#2106)
* feat(models): add provider model scanning * fix: double close button * feat: update plugin module * fix(monitoring): WeChat Work feedback recording bugs (#2108) * fix(monitoring): fix WeChat Work feedback recording bugs - Fix feedback events silently dropped when stream session expires: dispatch feedback handlers regardless of session availability - Fix IntegrityError on repeated feedback (like→dislike) for same message: implement UPSERT logic in record_feedback() - Fix cancel feedback (type=3) not removing records: add delete logic - Fix inaccurate_reasons validation error: convert int reason codes to strings before creating FeedbackEvent (Pydantic expects List[str]) - Fix feedback timestamps 8 hours off in frontend: use parseUTCTimestamp instead of new Date() for UTC timestamp parsing - Fix StreamSessionManager.cleanup missing _feedback_index cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(monitoring): apply ruff format to wecom feedback files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: 6mvp6 <13727783693@163.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat: add feat for receive files in wecombot * fix: ruff error * fix: always show sidebar plus buttons on touch/mobile devices (#2115) Agent-Logs-Url: https://github.com/langbot-app/LangBot/sessions/e27a4886-fbad-4a7a-8558-67a387852753 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * fix: SPA fallback for all frontend routes, not just /home/* After migrating from Next.js to Vite SPA, routes like /auth/space/callback returned 404 because the static file server only had SPA fallback for /home/*. Now all non-API routes fall back to index.html for React Router to handle. * style: ruff format main.py * feat: add marketplace link when no parser available for file upload Links to /home/market?category=Parser, same pattern as knowledge engine selector. * fix: lint error * fix(user): allow password login and password change for Space accounts with local password set Previously, Space accounts were unconditionally blocked from password login and password change based on account_type. Now the check verifies whether the user actually has a local password set, allowing Space users who have set a local password to authenticate and change it normally. * feat: add edition field to telemetry payload Sends constants.edition (community/saas) with each telemetry event so Space can distinguish between community and SaaS instances. * style: ruff format telemetry.py * fix(dingtalk): use voice recognition text instead of raw audio binary When DingTalk sends a voice message to the bot, the callback JSON contains a 'recognition' field with the speech-to-text result (powered by Qwen). Previously, LangBot only extracted the 'downloadCode' to download the raw audio binary and passed it as 'file_base64' to LLM APIs, which caused 400 errors since most models don't support this content type. This patch: - Extracts the 'recognition' field from DingTalk audio message content - Uses it as plain text input to the LLM instead of raw audio - Falls back to audio binary only when no recognition text is available - Fixes duplicate text issue for audio messages with recognition Fixes voice messages returning 'Request failed' on all LLM models. * feat: integrate Alembic for database migrations Replace manual if-sqlite/if-postgres branching with Alembic: - Add alembic dependency - Create programmatic alembic env (no CLI/alembic.ini needed) - Support async engines via run_sync passthrough - render_as_batch=True for SQLite ALTER TABLE compatibility - Auto-stamp baseline on first run (existing DB at version 25) - Run alembic upgrade head after legacy migrations - Include sample migration showing schema + data migration patterns - Add alembic dir to package-data for distribution * ci: add migration test workflow for SQLite and PostgreSQL Tests alembic upgrade on both databases: - Stamp baseline on existing schema - Upgrade to head - Idempotent re-upgrade - Fresh DB upgrade from scratch * feat: add autogenerate support and CLI entrypoint for alembic - autogenerate: compare ORM models vs DB schema to generate migrations - CLI: python -m langbot.pkg.persistence.alembic_runner <command> - autogenerate, upgrade, stamp, current - Reads data/config.yaml for DB connection * fix: add filereader for dingtalk,lark (#2122) * fix: add filereader for dingtalk * feat: add lark * feat: update uv.lock * chore: update version to 4.9.6 in pyproject.toml, __init__.py, and uv.lock * fix: update langbot-plugin version to 0.3.8 * fix: update langbot-plugin version to 0.3.8 * docs: update database migration instructions in AGENTS.md * fix(dashscopeapi): fix null value check in reasoning content processing logic (#2128) * fix(n8n-runner): fix output_key not applied when n8n returns plain JSON (#2119) * fix: bump dependencies to resolve Dependabot security alerts (#2130) * fix: bump dependencies to resolve Dependabot security alerts Python: - aiohttp: >=3.11.18 → >=3.13.4 (duplicate Host headers, header injection, redirect leak, multipart DoS) - cryptography: >=44.0.3 → >=46.0.7 (buffer overflow with non-contiguous buffers) - pillow: >=11.2.1 → >=12.2.0 (FITS GZIP decompression bomb, HIGH) - langchain-text-splitters: >=0.0.1 → >=1.1.2 (SSRF redirect bypass) - langchain-core: add >=1.2.28 (incomplete f-string validation) - langsmith: add >=0.7.31 (streaming token redaction bypass) - python-multipart: add >=0.0.26 (multipart DoS) - Mako: add >=1.3.11 (path traversal) - pytest: >=8.4.1 → >=9.0.3 (tmpdir handling) - uv: >=0.7.11 → >=0.11.6 (arbitrary file deletion) JavaScript (web/): - vite: ^8.0.3 → ^8.0.5 (fs.deny bypass, WebSocket file read, path traversal, HIGH) - axios: ^1.13.5 → ^1.15.0 (cloud metadata exfiltration) - lodash: ^4.17.23 → ^4.18.0 (code injection via _.template, prototype pollution, HIGH) * fix: update pnpm-lock.yaml for bumped dependencies * feat(ci): add i18n key consistency check for frontend locales (#2133) * feat(ci): add i18n key consistency check workflow Agent-Logs-Url: https://github.com/langbot-app/LangBot/sessions/c7bf50da-189b-49a5-9671-dbe8e70ff9d0 Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * feat(ci): replace eval with line-by-line parser, add permissions block Agent-Logs-Url: https://github.com/langbot-app/LangBot/sessions/c7bf50da-189b-49a5-9671-dbe8e70ff9d0 Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * feat(models): add provider model scanning * feat(models): add 'select all' functionality and enrich model abilities * fix:ruff * fix:ruff --------- Co-authored-by: WangCham <651122857@qq.com> Co-authored-by: 6mvp6 <119733319+6mvp6@users.noreply.github.com> Co-authored-by: 6mvp6 <13727783693@163.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Guanchao Wang <wangcham233@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> Co-authored-by: RockChinQ <rockchinq@gmail.com> Co-authored-by: haiyangbg <zhouhaiyangaa@gmail.com> Co-authored-by: Rock Chin <1010553892@qq.com> Co-authored-by: Amadeus <115918672+AmadeusKurisu1@users.noreply.github.com> Co-authored-by: hzhhong <hung.z.h916@gmail.com> Co-authored-by: fdc310 <2213070223@qq.com> |
||
|
|
b894842736 |
fix(user): allow password login and password change for Space accounts with local password set
Previously, Space accounts were unconditionally blocked from password login and password change based on account_type. Now the check verifies whether the user actually has a local password set, allowing Space users who have set a local password to authenticate and change it normally. |
||
|
|
1cdd428bcc | style: ruff format main.py | ||
|
|
71ac719aee |
fix: SPA fallback for all frontend routes, not just /home/*
After migrating from Next.js to Vite SPA, routes like /auth/space/callback returned 404 because the static file server only had SPA fallback for /home/*. Now all non-API routes fall back to index.html for React Router to handle. |
||
|
|
83ccb33fd3 |
fix(monitoring): WeChat Work feedback recording bugs (#2108)
* fix(monitoring): fix WeChat Work feedback recording bugs - Fix feedback events silently dropped when stream session expires: dispatch feedback handlers regardless of session availability - Fix IntegrityError on repeated feedback (like→dislike) for same message: implement UPSERT logic in record_feedback() - Fix cancel feedback (type=3) not removing records: add delete logic - Fix inaccurate_reasons validation error: convert int reason codes to strings before creating FeedbackEvent (Pydantic expects List[str]) - Fix feedback timestamps 8 hours off in frontend: use parseUTCTimestamp instead of new Date() for UTC timestamp parsing - Fix StreamSessionManager.cleanup missing _feedback_index cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(monitoring): apply ruff format to wecom feedback files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: 6mvp6 <13727783693@163.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
875227a2fe |
feat: add tools API endpoint and tools-selector form type (#2103)
* feat: add tools API endpoint and tools-selector form type Backend: - Add GET /api/v1/tools — list all available tools (plugin + MCP) - Add GET /api/v1/tools/<tool_name> — get specific tool details Frontend: - Add TOOLS_SELECTOR form type for plugin config forms - Multi-select dialog with tool name and description - Add PluginTool entity type and API client methods * fix: remove unused quart import, fix prettier formatting * style: ruff format tools.py * chore: bump langbot-plugin to 0.3.7 |
||
|
|
6e37aae636 |
feat(wecom): add user feedback support for WeChat Work AI Bot (#2078)
* feat(wecom): add user feedback support for WeChat Work AI Bot This commit implements user feedback functionality (like/dislike) for WeChat Work AI Bot conversations, including: Backend changes: - Add feedback_id and stream_id fields to WecomBotEvent - Implement feedback event handling in WecomBotClient (api.py) - Add StreamSessionManager._feedback_index for feedback_id lookup - Add on_feedback decorator for custom feedback handlers - Create MonitoringFeedback entity for database persistence - Add dbm025 migration for monitoring_feedback table - Implement FeedbackMonitor helper class - Update all platform adapters with ap parameter support - Update botmgr to pass bot_info for monitoring context Frontend changes: - Add FeedbackCard and FeedbackList components - Add useFeedbackData hook for feedback data fetching - Add feedback tab to monitoring page - Add feedback types and interfaces - Add i18n translations (zh-Hans, en-US) Other changes: - Update Dockerfile with Chinese mirror for faster builds - Update docker-compose.yaml with network configuration - Update .gitignore for docker data and backup files Note: Known issues that need future improvement: - feedback_type=3 (cancel) is recorded but not properly handled - Duplicate feedback records are not deduplicated * chore: remove unnecessary migration for new table will be created automatically * chore: ruff format * chore: prettier * feat: add feedback handling support across multiple platform adapters * fix(web): remove unused imports and variables in monitoring module --------- Co-authored-by: 6mvp6 <13727783693@163.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> |
||
|
|
1c419e3591 |
Optimize the plugin system (#2090)
* Optimize the plugin system * feat: enhance plugin installation process and improve task management * fix: linter err --------- Co-authored-by: Junyan Qin <rockchinq@gmail.com> |
||
|
|
71e44f0e54 |
Feat/space cta optimization (#2089)
* feat(wizard): persist wizard progress to backend for session resumption Store wizard step, selected adapter, created bot UUID, and runner selection in the metadata table. On revisit, the wizard restores progress and verifies the bot still exists. Progress is cleared automatically when the wizard is completed or skipped. * feat(dynamic-form): optimize LLM model selection with space login CTA and improve localization strings * feat(web): add LangBot Cloud CTA for webhook URL fields in community edition Show a subtle hint below webhook URL fields prompting users about LangBot Cloud's public endpoint, only visible in community edition. Covers all 8 webhook-based adapters with i18n support (4 locales). |
||
|
|
4c904c2375 |
Fix/frontend optimizations (#2088)
* fix(web): auto-redirect to wizard on first visit and change sidebar icons to blue * refactor(wizard): use backend metadata table instead of localStorage for wizard completion state - Add wizard_completed field to system info API (read from metadata table) - Add POST /api/v1/system/wizard/completed endpoint to mark wizard done - Frontend home layout checks systemInfo.wizard_completed for auto-redirect - Wizard calls markWizardCompleted API on skip/finish - Ensures consistent behavior across all browsers on the same instance * fix(wizard): update systemInfo in memory before navigation to prevent redirect loop * fix(monitoring): prevent horizontal overflow and unify empty state styles * fix(wizard): use Object.assign for systemInfo and await wizard completion API - Replace systemInfo reassignment with Object.assign in all 3 locations to preserve object identity across module imports - Await markWizardCompleted() POST in wizard skip/finish handlers instead of fire-and-forget to ensure backend persistence - Always re-fetch systemInfo in home layout to get latest wizard_completed state from backend * fix(wizard): prevent redirect loop by blocking navigation on failed status save - Refactor wizard_completed (boolean) to wizard_status (string: none/skipped/completed) - Remove ALL localStorage usage from wizard page (form state persistence) - Replace AlertDialogAction with Button so skip dialog stays open during POST - Add loading spinners for skip and complete actions - If POST fails, show error toast and keep dialog/button active for retry - If POST succeeds, update in-memory state and navigate * fix(wizard): fix row[0].value bug causing GET /info to always return wizard_status=none conn.execute(select(Entity)) returns Row with raw column values, not ORM entities. row[0] is the key column (a string), so row[0].value raises AttributeError which was silently swallowed by except-pass, making the GET endpoint always return wizard_status=none regardless of DB state. * fix(wizard): replace AlertDialog with Dialog for skip confirmation to remove slide animation * chore: optimize toast in wizard * fix(wizard): set default token value for Telegram adapter and initialize adapter config in wizard * feat(web): move webhook URL to dynamic form system, add market category filter, fix layout overflow - Add 'webhook-url' dynamic form field type rendered as read-only input with copy button, defined in adapter YAML specs instead of hardcoded in BotForm. Supports show_if conditions for optional-webhook adapters. - Remove hardcoded webhook display logic from BotForm.tsx, pass webhook URLs via systemContext to DynamicFormComponent. - Fetch webhook URLs after bot creation in wizard and pass to Step 1. - Support ?category= query param on /home/market page for filtering by component type (mirrors langbot-space behavior). - Link 'install knowledge engine' hint to /home/market?category=KnowledgeEngine. - Fix SidebarInset missing min-w-0 causing content overflow when sidebar is expanded. - Add vertical divider between plugin detail config and readme panels. - Fix infinite re-render loop in DynamicFormComponent by memoizing editableItems array. * fix: lint * fix(web): change systemInfo to const to satisfy prefer-const lint rule * fix: update adapter descriptions for clarity and usage requirements |
||
|
|
e8dc6fde53 | feat: autoclean monitoring events | ||
|
|
4a97895dea |
Feat/shadcn sidebar and page views (#2084)
* feat(web): migrate sidebar to shadcn and convert entity editors to page views * feat(web): enhance sidebar with sections, collapsible persistence, sub-item sorting/limiting, and UI polish - Reorganize sidebar into Home and Extensions sections with collapsible groups - Split plugins page into plugins, market, and mcp as separate routes - Add sidebar sub-items sorted by updatedAt with max 5 visible and expand/collapse toggle - Persist collapsible section state and sidebar open state in localStorage - Fix page refresh stripping query params by splitting handleChildClick/selectChild - Swap plugin detail layout (config left, readme right) - Add fixed headers with internal scroll for all detail and list pages - Remove entity form borders and sidebar rail - Improve dark mode sidebar/content contrast - Rename monitoring to Dashboard, move to first position - Update breadcrumb to show Home or Extensions based on current route - Add i18n translations for more/less toggle in all 4 locales * fix(web): fix scroll behavior - constrain layout to viewport, fix fixed headers and independent scroll areas - Change SidebarProvider wrapper from min-h-svh to h-svh overflow-hidden to constrain layout to viewport height (root cause of all scroll issues) - Fix create mode pages (bot, pipeline, knowledge): extract title bar out of scroll container so only form content scrolls - Fix plugin detail: add overflow-x-hidden on both config and readme panels to prevent horizontal overflow - Add min-h-0 to all TabsContent in edit mode for cross-browser flex shrink safety - Change nested <main> to <div> in layout to avoid invalid nested <main> tags (SidebarInset already renders as <main>) * style(web): polish UI - dashboard i18n, sidebar create text, cursor-pointer tabs, remove cancel buttons * feat(web): add plugin context menu to sidebar sub-items - Add hover-reveal dropdown menu (Ellipsis icon) on plugin sidebar items - Menu items: Update (marketplace only), View Source (marketplace/github), Delete - Add confirmation dialog with async task polling for delete/update operations - Extend SidebarEntityItem with installSource and installInfo fields - Fix PipelineFormComponent optional onCancel invocation * fix(web): prevent plugin sidebar text from overlapping menu button Add right padding on plugin sub-items and explicit truncate on text span so long plugin names never overlap the hover menu button. * feat(web): show update indicator on sidebar plugin menu - Fetch marketplace plugin versions in SidebarDataContext.refreshPlugins - Compare with installed version using isNewerVersion to set hasUpdate - Show red dot on menu trigger when update available (always visible) - Show 'New' badge on Update menu item when update available - Marketplace fetch failure is silently caught to avoid blocking sidebar * refactor(web): remove entity list pages, back buttons, and make sidebar toggle collapse - Remove card grid list views from bots, pipelines, knowledge pages - Show empty state placeholder when no entity is selected - Preserve KB migration dialog at page level - Remove back (ArrowLeft) buttons from all detail pages (bots, pipelines, knowledge, plugins) - Sidebar parent click for bots/pipelines/knowledge now toggles collapse instead of navigating - Breadcrumb second level is now non-clickable (always BreadcrumbPage) - Add selectFromSidebar i18n keys in all 4 locales * feat(web): enhance bot session monitor with refresh functionality and improve log card UI * refactor(web): optimize pipeline detail page with vertical config nav and debug chat polish - Convert pipeline config tab's horizontal sub-tabs to vertical left-side navigation with icons - Replace hardcoded colors in PipelineFormComponent and DebugDialog with theme-aware Tailwind classes - Replace custom SVG icons with lucide-react (User, Users, ImageIcon, Send, Reply, etc.) - Replace hardcoded Chinese strings with i18n keys (allMembers, file, voice, uploadImage, uploading) - Modernize chat bubbles to use bg-primary/10 and bg-muted instead of hardcoded blue/gray - Translate all Chinese comments to English in both components - Delete unused pipelineFormStyle.module.css - Remove max-w-2xl constraint from config tab container * fix(web): improve dark mode contrast and relocate WebSocket status indicator Bump dark mode --muted, --accent, --secondary from oklch(0.18) to oklch(0.24) to fix invisible TabsList, message bubbles, and selected items against the oklch(0.17) background. Move WebSocket connection dot from pipeline title into the Debug Chat tab trigger so it is always visible. Replace hardcoded Quote border colors with theme-aware border-muted-foreground/50. * fix(web): increase dark mode contrast for muted/accent/secondary to oklch(0.27) Previous value of oklch(0.24) was still not distinguishable enough against the oklch(0.17) background. Bump to oklch(0.27) for a 0.10 lightness gap, matching the contrast ratio of the default shadcn zinc dark theme. * style(web): replace hardcoded colors with theme tokens in monitoring dashboard Convert all monitoring page components from hardcoded gray/white colors to theme-aware CSS variable tokens (bg-card, text-foreground, text-muted-foreground, bg-muted, bg-background, bg-accent, border). Semantic colors (red/green/blue/purple for status badges and error styling) are intentionally preserved. * feat(web): show debug indicator for debugging plugins in sidebar Add orange Bug icon next to plugin name in sidebar sub-items when the plugin is connected via WebSocket debug mode. Hide context menu for debug plugins since delete/update operations are not supported. * feat(web): show install source and debug badge on plugin detail page Display a badge next to the plugin title indicating the install source (GitHub blue, Local green, Marketplace purple) or debugging status (orange with Bug icon), matching the existing plugin card convention. * fix(web): resolve eslint errors for CI - remove unused imports and variables * fix(web): remove stale setSubtitle call and fix prettier formatting * Refactor code formatting and improve readability - Updated HomeSidebar.tsx to enhance clarity in conditional assignment. - Adjusted CSS formatting in github-markdown.css for better alignment. - Cleaned up tsconfig.json by consolidating array formatting for consistency. * fix(ci): use local prettier instead of mirrors-prettier to avoid version mismatch (3.1.0 vs 3.8.1) |
||
|
|
01852b81d4 |
Feat/openclaw weixin adapter (#2074)
* feat: add wexin openclaw adapter * feat: The new feature will store the token and other configurations after login. * fix: wexin qc to base64 and in log image print * feat: add image to base64 * feat: add update file and image and voice |
||
|
|
52eb991a70 | feat: add extra webhook prefix config | ||
|
|
10c716be0c | fix: bad model field ref | ||
|
|
89064a9d5b |
feat: add support for username (#2047)
* feat: add support for username * fix: lint * fix: migerations * fix: change to version 21 * fix: remove duplicate dbm021 migration and rename dbm022 * feat: add user_id and user_name display with copy functionality in BotSessionMonitor --------- Co-authored-by: wangcham <wangcham@gmail.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> |
||
|
|
2a74a8d6ae |
Feat/dbm20 rag (#2037)
* feat(rag): add knowledge base migration from v4.9.0 to plugin architecture Rewrite dbm020 to backup old knowledge_bases data and preserve external_knowledge_bases table. Add migration API endpoints and frontend dialog so users can opt-in to auto-install LangRAG plugin and restore their knowledge bases with original UUIDs preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): query marketplace for actual plugin version instead of 'latest' The marketplace API does not support 'latest' as a version string. Fetch the plugin info first to get latest_version, then use that concrete version for installation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(rag): add data-only migration option and fix dialog width Add option to migrate knowledge base data without auto-installing the LangRAG plugin (for offline/intranet environments). Also narrow the migration dialog to match other confirmation dialogs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: to red and no more * fix lint * fix ruff lint * feat: add external migration * fix: show * feat: add external plugin auto download * feat: update migration messages for knowledge base in multiple languages --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> |
||
|
|
cadcf10047 |
Feat/rag plugin (#1995)
* [issue:1933] RAG engine plugin architecture (#1967) * refactor: migrate RAG knowledge services to a plugin-oriented host service architecture. * feat(rag): phase 2 core refactor with RPC Action handlers * feat: 为 RAG 插件添加知识库创建和删除事件通知,并优化了 RAG 动作的参数传递和枚举使用。 * feat: 统一知识库管理为RAG引擎,支持动态配置并移除旧的外部知识库组件。 * refactor(rag): remove plugin_adapter, inline logic into RuntimeKnowledgeBase BREAKING CHANGE: RAGPluginAdapter has been removed. All plugin communication is now handled directly by RuntimeKnowledgeBase. Architecture change: - Before: RuntimeKnowledgeBase → RAGPluginAdapter → plugin_connector - After: RuntimeKnowledgeBase → plugin_connector (direct) Changes to kbmgr.py (RuntimeKnowledgeBase): - Remove RAGPluginAdapter import and usage - Inline plugin communication methods: - _on_kb_create(): Notify plugin when KB is created - _on_kb_delete(): Notify plugin when KB is deleted - _ingest_document(): Call plugin for document ingestion - _retrieve(): Call plugin for retrieval - _delete_document(): Call plugin to delete document - Simplify dispose(): Only notify plugin, no built-in VDB assumption Changes to base.py (KnowledgeBaseInterface): - Remove get_type() abstract method (outdated internal/external concept) - Add get_rag_engine_plugin_id() abstract method Changes to localagent.py: - Remove get_type() call - Simplify top_k retrieval from KB entity Deleted files: - pkg/rag/knowledge/plugin_adapter.py Benefits: - Reduced abstraction layer, simpler code - Plugin communication logic centralized in RuntimeKnowledgeBase - Easier to understand and maintain 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(api): remove ExternalKnowledgeBase infrastructure BREAKING CHANGE: ExternalKnowledgeBase has been completely removed. All knowledge bases are now unified under the single KnowledgeBase model, differentiated by their rag_engine_plugin_id. Deleted files: - pkg/api/http/controller/groups/knowledge/external.py (ExternalKBController with /external-bases routes) - pkg/api/http/service/external_kb.py (ExternalKnowledgeBaseService) - pkg/rag/knowledge/external.py (ExternalKnowledgeBase implementation) Modified files: - pkg/entity/persistence/rag.py: Remove ExternalKnowledgeBase SQLAlchemy table definition - pkg/core/app.py: Remove external_kb_service attribute from LangBotApplication - pkg/core/stages/build_app.py: Remove external_kb_service initialization Migration notes: - Existing external knowledge base data should be migrated manually - API consumers should use /api/v1/knowledge/bases for all KB operations - Use /api/v1/knowledge/engines to discover available RAG engines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(plugin): remove list_knowledge_retrievers from connector Remove deprecated list_knowledge_retrievers functionality from the plugin communication layer. This aligns with the SDK change that removed the LIST_KNOWLEDGE_RETRIEVERS action. Changes: - connector.py: Remove list_knowledge_retrievers() method - handler.py: Remove list_knowledge_retrievers() handler The functionality is replaced by the new /api/v1/knowledge/engines endpoint which lists available RAGEngine components with their capabilities and configuration schemas. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(service): update knowledge service with capability-based checks Replace type-based checks with capability-based checks for file operations, aligning with the unified knowledge base architecture. Changes to knowledge.py: - store_file(): Replace get_type() check with doc_ingestion capability check - delete_file(): Replace get_type() check with doc_ingestion capability check - list_rag_engines(): Remove list_knowledge_retrievers call, simplify to only list RAGEngine components (KnowledgeRetriever type removed) Changes to pipelines.py: - Minor cleanup related to knowledge base references The capability-based approach allows RAG engines to declare their supported features (doc_ingestion, chunking_config, rerank, hybrid_search) and the system responds accordingly, rather than hardcoding behavior based on internal/external type distinction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(web): unify knowledge base UI, remove external KB components BREAKING CHANGE: The internal/external knowledge base distinction has been removed from the frontend. All knowledge bases are now displayed in a unified list, differentiated by their RAG engine. Changes to page.tsx: - Remove Tab component (内置/外置 tabs) - Remove selectedKbType state - Unified knowledge base list display - Single "Create Knowledge Base" button for all types Changes to KBDetailDialog.tsx: - Remove kbType prop - Simplify dialog logic for unified KB handling - Documents menu item conditionally shown based on doc_ingestion capability Changes to KBForm.tsx: - Remove retriever type handling code - Simplify form for unified KB creation - Dynamic form rendering based on RAG engine's creation_schema Changes to KBCardVO.ts: - Remove 'type' field from KBCardVO interface Changes to BackendClient.ts: - Remove all external KB related methods: - getExternalKnowledgeBases() - getExternalKnowledgeBase() - createExternalKnowledgeBase() - updateExternalKnowledgeBase() - deleteExternalKnowledgeBase() - retrieveFromExternalKnowledgeBase() Changes to api/index.ts: - Remove ExternalKnowledgeBase interface definition UI/UX improvements: - Users no longer need to understand internal vs external distinction - RAG engine selection is now the primary differentiator - Documents panel visibility is capability-driven (doc_ingestion) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(plugin): code review improvements for RAG handlers - Unify embed_model field naming to embedding_model_uuid only - Add structured error responses with error_type for RAG actions - Fix file_size and mime_type detection in _store_file_task - Improve error handling with detailed error context (error_type, original_error) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(rag): refactor KB dynamic form and vector manager - Frontend: Refactor Knowledge Base form using DynamicForm components. - Frontend: Remove obsolete jsonSchemaConverter utility. - Backend: Update VectorManager and PluginHandler to support new RAG architecture. - Chore: Update dependencies in pyproject.toml. * fix: code review fixes for RAG refactor - Remove DEBUG stderr outputs in handler.py - Move repeated `import json` to file top - Add warning log for unimplemented delete_by_filter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(rag): consolidate valid_fields into entity constants Define MUTABLE_FIELDS, CREATE_FIELDS, ALL_DB_FIELDS as class constants in KnowledgeBase entity to eliminate duplication. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: 将知识库获取和RAG引擎信息丰富逻辑移至知识库管理器。 * refactor(rag): introduce RAGRuntimeService and clean up plugin handler - Create RAGRuntimeService to encapsulate RAG capability implementation (Embedding, VectorOps). - Refactor PluginHandler to delegate RAG actions to RAGRuntimeService. - Move KnowledgeService enrichment and creation logic to RAGManager. - Register RAGRuntimeService in Application and BuildAppStage. - Clean up legacy code in KnowledgeService. * refactor(rag): standardize logger and fix type hints - Use self.ap.logger consistently in kbmgr.py and runtime.py, removing module-level loggers. - Fix type hints for retrieve_knowledge in handler.py and connector.py to match implementation returning dict. * refactor: 将引擎徽章的样式从 Tailwind CSS 类迁移到 CSS 模块。 * fix(web): resolve React rendering errors in plugins page - Fix missing key prop in PluginComponentList by using ternary instead of Fragment - Fix RAGEngine.name type to I18nObject and use extractI18nObject() for rendering - Preserves multi-language support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(rag): update runtime service and web components * refactor: 优化知识库设置结构并增强前端距离显示健壮性。 * fix: 处理前端距离显示中的空值。 * fix(rag): document retrieve ui and kbmgr top_k validation * 更新 uv.lock 中的 PyPI 镜像源为官方地址。 * fix: address code review issues for RAG engine plugin architecture P0 fixes: - Fix ALL_DB_FIELDS missing collection_id and emoji fields - Move rag_engine_plugin_id to CREATE_FIELDS (immutable after creation) - Fix creation_settings mutable default value (dict -> None) - Rename vector delete method to delete_by_file_id for correct semantics - Fix delete_by_filter to raise NotImplementedError instead of silent no-op - Add database migration script (dbm019) for new columns and table cleanup P1 fixes: - Clean up design-hesitation comments in connector.py - Add _parse_plugin_id() with format validation for all RAG methods - Make _retrieve() raise exceptions instead of silently returning empty results - Extract _make_rag_error_response() helper for clean error formatting - Remove unused imports from handler.py P2 fixes: - Fix runtime.py indentation inconsistencies - Simplify get_file_stream to use storage abstraction uniformly - Reduce redundant DB queries in knowledge service (extract _check_doc_capability) - Fix engines.py URL encoding: use <path:plugin_id> instead of __ replacement - Add read-only mode for engine settings in KBForm edit mode - Simplify page.tsx handleKBCardClick to pass only kbId string Co-authored-by: Cursor <cursoragent@cursor.com> * fix: address code review findings for RAG plugin architecture - Frontend: add retrieval_settings param to retrieveKnowledgeBase API call - Backend: return {uuid} from PUT knowledge base to match frontend expectation - Backend: validate query is non-empty in retrieve endpoint (400 on empty) - Backend: rename vector_delete ids→file_ids for semantic clarity, keep backward compat by accepting both 'file_ids' and 'ids' in RPC handler - Backend: ensure rag_engine.name fallback is always I18nObject-compatible dict, preventing frontend extractI18nObject from receiving plain strings - Migration: fix misleading docstring about external_kb data migration Co-authored-by: Cursor <cursoragent@cursor.com> * Update langbot-plugin version to 0.2.6 * chore: update required database version from 18 to 19 * refactor: remove unused polymorphic component framework * chore: fix lint and format issues for python and frontend * fix(plugin): remove legacy `ids` fallback in rag_vector_delete handler SDK now sends `file_ids` directly, the `ids` backward-compat fallback is no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): deep review fixes for critical bugs, security and quality Critical: - Fix StorageMgr.load() -> storage_provider.load() (C1, AttributeError) - Update required_database_version 18 -> 19 (C2, migration never runs) Security: - Add path traversal validation in get_file_stream (C11) - Add vectors/ids/metadata length validation in rag_vector_upsert (C12) Logic fixes: - Legacy KBs: set capabilities to [] instead of ['doc_ingestion'] (C4) - Fix store_file return type int -> str (C5) - Fix retrieve_knowledge return [] -> {'results': []} when disabled (C6) - Re-raise exception in _on_kb_create instead of silently swallowing (C7) - Log warning when KB not found in memory during delete (C8) API fixes: - Catch ValueError as 400 in create_knowledge_base endpoint (C15) - Validate plugin_id format in engines endpoints (C16) Quality: - Remove dead if/else in migration with identical branches (C17) - Fix variable shadowing: rag_context -> rag_context_text (C18) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove unused os import to fix ruff lint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(plugin): remove PolymorphicComponent sync from LangBot side Remove sync_polymorphic_component_instances() from connector and handler, and the post-connection sync call in initialize(). This dead code synced an always-empty list of polymorphic instances that were never created. Companion change to langbot-plugin-sdk PolymorphicComponent removal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): fix vector_delete count bug and remove vestigial instance_id parameter 1. vector_delete: assign return value from delete_by_filter to count instead of silently returning 0 for filter-based deletion. 2. Remove instance_id parameter from the entire retrieve_knowledge call chain (kbmgr → connector → handler → runtime). This parameter was a remnant of the PolymorphicComponent mechanism and is no longer used — RAGEngine operates as a stateless singleton. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(web): 支持 creation_schema 字段级别的 editable 属性控制编辑模式可修改性 - IDynamicFormItemSchema 添加 editable 可选属性 - DynamicFormItemConfig 透传 editable 属性 - DynamicFormComponent 接收 isEditing prop,按字段 editable 值控制禁用 - KBForm 解析 editable 并传递 isEditing 给动态表单组件 - editable 未指定时默认可编辑,editable: false 时编辑模式下禁用该字段 * feat(storage): 添加 size() 抽象方法及 LocalStorage/S3 实现 支持获取存储对象大小,S3 使用 head_object 避免下载整个文件 * fix(migration): 删除 external_knowledge_bases 表前记录日志警告 - 迁移时如果表中存在数据,先 warning 日志记录避免无感数据丢失 - 添加 chunk 清理注释说明:仅对旧版非插件架构 KB 有效 * fix(web): 修复检索结果长文本撑大容器导致查询按钮不可见 KBDetailDialog 的 main 容器添加 min-w-0 overflow-x-hidden, 限制 flex-1 子容器宽度,防止 Dify RAG 长文本撑出 Dialog 边界 * fix(rag): address code review issues for plugin architecture PR - Fix SQL injection in migration helpers by using bind parameters - Move numpy import to module level in vector/mgr.py - Improve path traversal validation using posixpath.normpath - Add call_rag_retrieve to connector, eliminating duplicate plugin_id parsing in kbmgr.py _retrieve - Normalize typing style to modern dict/list/None syntax Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(web): fix prettier formatting errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(rag): update embedding handling in RuntimeConnectionHandler - Renamed RAG_EMBED_DOCUMENTS and RAG_EMBED_QUERY actions to INVOKE_EMBEDDING for clarity. - Removed embed_documents and embed_query methods from RuntimeEmbeddingModel and RAGRuntimeService. - Integrated embedding model retrieval directly in the invoke_embedding method, improving error handling for missing models. - Updated the embedding invocation logic to streamline the process and enhance error reporting. * refactor(web): replace KnowledgeRetriever with RAGEngine across frontend and tests KnowledgeRetriever component type has been removed in favor of the new RAGEngine architecture. Update all remaining references in i18n locales, plugin component icon mappings, marketplace filter, and unit tests. Addresses reviewer notes from RockChinQ on PR #1967. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): address critical bugs found in deep review - Fix path traversal bypass in runtime.py (check all path components for '..') - Use normalized path for file loading instead of raw user input - Change knowledge_bases from list to dict for O(1) lookup and race safety - Add rollback on KB creation failure (clean up DB + runtime on plugin error) - Add null check after KB update in knowledge service - Fix file extension parsing to use os.path.splitext instead of split('.') (handles multi-dot filenames like 'report.v2.pdf' correctly) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): address remaining review issues across frontend and backend Frontend: - Fix KB delete: use async/await with error handling instead of fire-and-forget - Fix capabilities null check: add optional chaining to prevent crash - Add toast.error on KB info load failure instead of silent console.error - Replace hard-coded Chinese validation message with i18n key - Replace hard-coded English error messages in DynamicFormItemComponent with i18n - Optimize document polling: stop when all documents reach terminal state - Add i18n keys (fieldRequired, loadKnowledgeBaseFailed, deleteKnowledgeBaseFailed, getKnowledgeBaseListError) to all 4 locales Backend: - Fix KB delete atomicity: delete from DB first, then notify plugin - Add RAG engine plugin existence validation before creating KB Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(rag): fix ruff formatting in kbmgr.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> * chore: bump langbot-plugin to 0.3.0 (#1992) * chore: correct sdk version to 0.3.0a1 * feat: normalize rag related actions' names * refactor(rag): align IngestionContext fields with SDK changes Remove redundant `chunking_strategy` field and rename `custom_settings` to `creation_settings` to match the updated SDK entity definitions (langbot-plugin-sdk#36). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: fix ruff formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): enforce immutability of embedding_model_uuid and non-editable creation_settings fields Remove embedding_model_uuid from MUTABLE_FIELDS to prevent post-creation modification via API. Add backend validation for creation_settings to preserve fields marked editable:false in the plugin's creation schema. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(rag): fix ruff formatting in knowledge service Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(rag): split settings into immutable creation_settings and mutable retrieval_settings - Remove standalone embedding_model_uuid and top_k columns from KB entity - Add retrieval_settings column; update MUTABLE_FIELDS/CREATE_FIELDS accordingly - Merge migration logic into dbm019 (add retrieval_settings, migrate top_k and embedding_model_uuid into JSON settings, drop old columns on PostgreSQL) - Remove _filter_creation_settings and per-field editable concept - Frontend: creation_settings fields are all disabled when editing, retrieval_settings fields are always editable via a second DynamicFormComponent - Remove editable from IDynamicFormItemSchema, DynamicFormItemConfig - Clean up KBCardVO, KnowledgeBase API type, and localagent runner Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * bugfix: if ingest_document failed,not raise exep * fix: ruff lint * refactor(rag): remove unused _get_kb_entity method from RAGRuntimeService * feat(vector): implement metadata filters for vector_search and vector_delete (#1997) Add functional metadata filter support across all 5 VDB backends using Chroma-style where syntax as the canonical format. Previously the filters parameter existed throughout the stack but was entirely ignored. - Add filter_utils.py with normalize_filter() and strip_unsupported_fields() - Implement filter in search() and add delete_by_filter() for all backends: Chroma/SeekDB (native passthrough), Qdrant (translated to models.Filter), Milvus (translated to expr string), pgvector (translated to SQLAlchemy conditions) - Milvus/pgvector limited to {text, file_id, chunk_uuid}; other fields logged and ignored - Replace delete_by_filter() NotImplementedError with backend delegation in mgr.py - Populate retrieval_context['filters'] from settings in kbmgr._retrieve() - Pass search_type/query_text/documents through handler and runtime service Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(vector): fix ruff formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(vector): remove numpy dependency and fix SeekDB search modes - Remove numpy array conversion for query vectors; all VDB backends accept list[float] directly - Remove redundant get_or_create_collection call from upsert; backends handle collection creation internally in add_embeddings - Fix SeekDB to raise ValueError when vector dimension is unknown instead of defaulting to 384 - Use hybrid_search() for full-text and hybrid search modes in SeekDB, since pyseekdb's query() always requires embeddings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(vector): escape single quotes in SeekDB documents and metadata Document text containing apostrophes (e.g. "don't", "it's") causes SQL syntax errors in OceanBase because single quotes were not in the escape table. Add single-quote escaping and apply the escape table to the documents parameter in add_embeddings(), not just metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(vector): use standard SQL escaping for single quotes in SeekDB Change single quote escaping from MySQL-style \' to standard SQL '' (doubled quote). The backslash escape is not recognized by OceanBase in NO_BACKSLASH_ESCAPES mode, causing SQL syntax errors when metadata text contains apostrophes (e.g. O'Shea in academic citations). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): persist retrieval_settings on knowledge base creation retrieval_settings was not being passed from the service layer to RAGManager.create_knowledge_base(), causing retrieval schema fields (e.g. query_rewrite) to be lost on initial KB creation. They only took effect after a subsequent edit/update. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(web): add show_if conditional rendering for dynamic forms Support conditional field visibility in plugin-defined forms via show_if rules (eq, neq, in operators). Fields can depend on values from the same form or cross-reference between creation and retrieval settings via externalDependentValues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(rag): replace base64 with chunked file transfer for get_rag_file_stream Use send_file() instead of base64 encoding for returning file content in the GET_RAG_FILE_STREAM handler, avoiding memory issues with large files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(parser): add parser plugin integration and capability-aware upload UI (#2000) * feat(parser): add parser plugin integration and capability-aware upload UI Backend: add parser plugin API endpoints (list/invoke), connector and handler support for parser actions, and KB manager passthrough. Frontend: thread ragEngineCapabilities prop to FileUploadZone and use doc_parsing capability to conditionally show the RAG engine option in the parser selector. When no parser is available, show a warning prompting users to install a parser plugin. Update i18n: rename builtInParser to "Provided by RAG engine" and add noParserAvailable warning message in all 4 locales. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(parser): replace base64 with chunked file transfer and remove stale cache - Remove @alru_cache from list_parsers() and list_rag_engines() - Replace inline base64 file content with send_file/read_local_file chunked transfer pattern in parse_document and invoke_parser flows - Remove unused base64 import from kbmgr.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * feat(web): add Parser component kind to plugin market UI and i18n Add Parser to kindIconMap, market filter toggle, and all 4 locale files so parser plugins are properly displayed and filterable in the plugin market, matching the existing RAGEngine treatment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(web): fix prettier formatting from merge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: rename RAGEngine to KnowledgeEngine across frontend and backend * fix(web): fix I18nObject import path in FileUploadZone and KBDoc * chore: format files involved in RAGEngine to KnowledgeEngine refactor * refactor: change rag engine to knowledge engine * fix: update langbot-plugin version to 0.3.0rc1 * chore: disable migration 20 for now --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com> |
||
|
|
88132dff8a |
perf: reduce memory usage by ~200MB+ at startup (#2013)
* perf: reduce memory usage by ~200MB+ at startup
Two key optimizations:
1. Use importlib.util.find_spec() instead of __import__() in dependency
checking. find_spec() only locates modules without executing them,
avoiding loading all 36 dependencies (~222MB) into memory at startup.
2. Introduce shared aiohttp.ClientSession via httpclient module.
Previously, every HTTP request created a new ClientSession, which
creates a new TCPConnector and SSL context, loading system root
certificates each time (~270MB total allocations observed via memray).
Now all HTTP client code reuses shared sessions.
- satori.py and coze_server_api/client.py are left unchanged as they
create one session per adapter lifecycle (not per-request).
Profiling data (memray):
- Peak memory: 403MB
- SSL context creation: 270MB / 6.7M allocations (67% of total)
- Dependency import: 222MB (55% of peak)
- Expected reduction: 150-350MB at startup
* fix: remove unused aiohttp imports (ruff F401)
* style: ruff format
|
||
|
|
2d837c9cb4 |
feat: add in-product survey system (#2008)
* feat: add in-product survey system
- SurveyManager: event-based trigger, Space API communication
- Trigger on first successful non-WebSocket response
- Backend API: /api/v1/survey/{pending,respond,dismiss}
- Frontend: floating survey widget with progressive questions
- Flat radio/checkbox style (not dropdown Select)
* fix: persist triggered survey events to disk across restarts
Store triggered events in data/survey_triggered_events.json so that
restarting the process doesn't re-query Space for already-triggered events.
* fix: use metadata table for survey event persistence instead of file
Store triggered events in the existing metadata KV table
(key='survey_triggered_events') instead of a standalone JSON file.
* fix: ruff format and prettier fixes
|
||
|
|
d9a630b8c1 |
feat: add session message monitoring tab to bot detail dialog (#2005)
* feat: add session message monitoring tab to bot detail dialog Add a new "Sessions" tab in the bot detail dialog that displays sent & received messages grouped by sessions. Users can select any session to view its messages in a chat-bubble style layout. Backend changes: - Add sessionId filter to monitoring messages endpoint - Add role column to MonitoringMessage (user/assistant) - Record bot responses in monitoring via record_query_response() - Add DB migration (dbm019) for the new role column Frontend changes: - New BotSessionMonitor component with session list + message viewer - Add Sessions sidebar tab to BotDetailDialog - Add getBotSessions/getSessionMessages API methods to BackendClient - Add i18n translations (en-US, zh-Hans, zh-Hant, ja-JP) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * refactor: remove outdated version comment from PipelineManager class * fix: bump required_database_version to 19 to trigger monitoring_messages.role migration * fix: prevent session message auto-scroll from pushing dialog content out of view Replace scrollIntoView (which scrolls all ancestor containers) with direct scrollTop manipulation on the ScrollArea viewport. This keeps the scroll contained within the messages panel only. * ui: redesign BotSessionMonitor with polished chat UI - Wider session list (w-72) with avatar circles and cleaner layout - Richer chat header with avatar, platform info, and active indicator - User messages now use blue-500 (solid) instead of blue-100 for clear visual distinction - Metadata (time, runner) shown on hover below bubbles, not inside - Proper empty state illustrations for both panels - Better spacing, rounded corners, and shadow treatment - Consistent dark mode styling * fix: infinite re-render loop in DynamicFormComponent The useEffect depended on onSubmit which was a new closure every parent render. Calling onSubmit inside the effect triggered parent state update → re-render → new onSubmit ref → effect re-runs → loop. Fix: use useRef to hold a stable reference to onSubmit, removing it from the useEffect dependency array. Also add DialogDescription to BotDetailDialog to suppress Radix aria-describedby warning. * fix: remove .html suffix from docs.langbot.app links (Mintlify migration) * style: fix prettier and ruff formatting --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Happy <yesreply@happy.engineering> |
||
|
|
42caae1bcf |
feat: Implement extension and bot limitations across services and UI (#1991)
- Added checks for maximum allowed extensions, bots, and pipelines in the backend services (PluginsRouterGroup, BotService, MCPService, PipelineService). - Updated system configuration to include limitation settings for max_bots, max_pipelines, and max_extensions. - Enhanced frontend components to handle limitations, providing user feedback when limits are reached. - Added internationalization support for limitation messages in English, Japanese, Simplified Chinese, and Traditional Chinese. |
||
|
|
b7e1e43fbd | fix: some errors | ||
|
|
c2574bdd3a | fix: lint error | ||
|
|
6d858475d7 | feat: support export message history | ||
|
|
50dff55217 |
feat: enhance LLM model creation with optional default pipeline setting
- Updated create_llm_model method to include auto_set_to_default_pipeline parameter. - Adjusted ModelManager to set auto_set_to_default_pipeline to False when creating models. - Improved logic for setting the default pipeline model based on the new parameter. |
||
|
|
5d9f6ec763 |
Feat/monitor (#1928)
* feat: add monitor * feat: fix tab * feat: work * feat: not reliable monitor * feat: enhance monitoring page layout with integrated filters and refresh button * feat: add support for runner recording * feat: add jump button & alignment * feat: new * fix: not show query variables in local agent * fix: pnpm lint and python ruff check * fix: ruff fromat * chore: remove unnecessary migration * style: optimize monitoring page layout and fix sticky filter issues - Enhanced metric cards with gradient backgrounds and hover effects - Increased traffic chart height from 200px to 300px - Adjusted grid layout and spacing for better visual appeal - Fixed sticky filter area to properly cover parent padding without transparent gaps - Used negative margins and positioning to eliminate scrolling artifacts - Matched padding/margins with other pages (pipelines, bots) for consistency - Removed duplicate title/subtitle from page content - Added cursor-pointer styling to tab triggers - Removed border between tab list and tab content Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: apply prettier formatting to monitoring components - Fixed indentation and spacing in MetricCard.tsx - Fixed formatting in TrafficChart.tsx - Applied prettier formatting to page.tsx Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: update HomeSidebar to trigger action on child selection and localize monitoring titles * refactor: streamline LLM and embedding invocation methods * feat: add embedding model monitor * fix: database version * chore: simplify pnpm-lock.yaml formatting --------- Co-authored-by: Junyan Qin <rockchinq@gmail.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> |
||
|
|
bbf77bac4c | feat(user): update Space model provider API keys in UserService (#1932) | ||
|
|
fc6e414be4 |
feat: add GitHub Actions workflow for linting with Ruff (#1929)
* feat: add GitHub Actions workflow for linting with Ruff * refactor: rename lint job and add formatting step to Ruff workflow * chore: run ruff format * chore: rename Ruff lint job to 'Lint' and add frontend linting workflow |
||
|
|
02e12cc1e4 | feat: implement account email mismatch error handling and improve user feedback in authentication flows | ||
|
|
61f08f3218 | feat: add disable_models_service configuration to manage model service availability and update related components | ||
|
|
b99c5561fc | fix: update cloud service URL retrieval and enhance model synchronization error handling |