Commit Graph

90 Commits

Author SHA1 Message Date
huanghuoguoguo d26d0635c5 fix(vector): correct SeekDB adapter semantics (#2536) 2026-09-12 19:40:30 +08:00
彼方 9eb8683997 Merge pull request #2530 from langbot-app/feat/passkey-login
feat(auth): support passkey (webauthn) login and credential management
2026-09-12 16:00:10 +08:00
Hyu 273b8839b9 chore(release): prepare LangBot 4.10.11 (#2531)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-09-12 12:33:26 +08:00
BiFangKNT b594cf23e4 feat(auth): add webauthn authentication support 2026-09-12 12:17:26 +08:00
Hyu 45d77c3926 fix(plugin): preserve explicit nested installation scope (#2528)
* fix(plugin): preserve explicit nested installation scope

* fix(deps): pin released RAG runtime SDK 0.5.8

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-09-11 17:41:33 +08:00
Hyu a63808caa6 chore(release): prepare LangBot 4.10.10 (#2507)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-09-04 21:47:31 +08:00
Hyu 7aab0cee07 chore(release): bump version to 4.10.9 (#2488)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-09-01 00:18:53 +08:00
RockChinQ 962366c507 fix(deps): make SeekDB optional for native installs 2026-08-21 12:07:36 +08:00
RockChinQ 3d4a726cd8 chore: release v4.10.8 (#2450) 2026-08-21 01:47:39 +08:00
dadachann a08a177a11 fix(runtime): adopt plugin SDK 0.5.5 2026-08-16 17:36:34 +00:00
Hyu 1f1a3aff55 fix(release): publish installable multi-arch artifacts (#2432)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-14 01:06:31 +08:00
Chan 536534865e fix: pin plugin runtime SDK 0.5.3 2026-08-13 10:39:51 +00:00
Chan c18fc9dfe3 chore(runtime): pin plugin SDK 0.5.2 2026-08-13 09:27:50 +00:00
RockChinQ 9b81a0b606 chore(deps): pin langbot-plugin 0.5.1 2026-08-11 21:35:03 +08:00
Hyu 90f3d880e5 fix(deps): constrain mcp to v1 (#2413)
Co-authored-by: Chan <dadachann@users.noreply.github.com>
2026-08-11 13:23:03 +08:00
Hyu cdd5c6589c fix: support 3072-dimensional knowledge embeddings (#2401)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-08-05 21:19:57 +08:00
Hyu 3b4698463c chore: pin runtime control binding SDK (#2400)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-08-05 15:12:40 +08:00
Hyu edd6cad449 chore: pin fenced debug runtime sessions (#2396)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-08-04 19:59:51 +08:00
Hyu d78546967c feat: expose Workspace-scoped rotating plugin debug keys (#2395)
* feat: add Workspace-scoped rotating plugin debug tokens

* chore: pin formatted Workspace debug runtime

* chore: pin merged Workspace debug runtime

* chore: pin tenant-safe debug runtime

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-08-04 19:20:06 +08:00
Hyu e2331c4967 fix(cloud): restore plugins and pipeline execution (#2379)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-08-02 01:34:11 +08:00
Hyu d64278ab3f feat(cloud): provision workspace model catalog (#2376)
* feat(cloud): provision workspace model catalog

* ci(cloud): pin model catalog adapter source

* fix: make cloud model catalog sync recoverable

* ci: pin cloud adapter source for release

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-08-01 18:16:02 +08:00
RockChinQ e1ac5e0fc8 feat(tenancy): add Workspace multi-tenant foundation (#2353)
* Document multi-tenant workspace architecture

* Add OSS and commercial workspace boundaries

* docs: redesign multi-tenant workspace architecture

* feat(tenancy): implement workspace isolation

* docs(tenancy): record verification evidence

* docs(tenancy): revise single-instance SaaS topology

* docs(tenancy): refine architecture options

* docs: finalize cloud v2 multi-tenant decisions

* feat(tenancy): establish cloud isolation foundations

* feat(tenancy): harden shared cloud runtime boundaries

* docs(tenancy): record final isolation verification

* fix(tenancy): close isolation and permission gaps

* docs(tenancy): record final isolation verification

* feat(tenancy): connect cloud workspace control plane

* fix(build): install git for pinned SDK

* docs(cloud): update control plane verification

* chore: update multi-tenant SDK pin

* fix(cloud): skip legacy model sync during startup

* test(cloud): preserve minimal model manager fixtures

* fix(cloud): preserve authenticated account context

* fix(cloud): reuse authenticated account for user info

* feat(cloud): complete Workspace settings navigation

* test(web): cover Workspace dropdown menu

* feat(web): place workspace controls in sidebar

* refactor(web): streamline workspace controls

* style(web): format workspace layout test

* fix(cloud): surface runtime and workspace plan status

* fix(plugin): keep runtime identity stable across restarts

* fix(ui): widen and center workspace switcher

* fix(ui): hide roles from workspace switcher

* fix(ui): align workspace switcher with sidebar entries

* feat(workspace): add in-product collaboration and direct Cloud launch

* style: format collaboration changes

* fix(workspace): bind collaboration APIs to tenant UoW

* fix(cloud): preserve Core-owned collaboration state

* test(cloud): require Space identity for invite registration

* feat(cloud): complete secure invitation experience

* style(web): format invitation flows

* fix(cloud): recover box runtime without unscoped skill reload

* feat(oss): enforce invitation account and owner billing flows

* style: format OSS account service

* test(oss): cover invitation logout handoff

* fix(oss): resolve workspace owner in scoped session

* feat(cloud): harden multi-tenant runtime resources

* fix(cloud): bound runtime restart storms

* fix(cloud): eliminate periodic runtime CPU spikes

* fix(cloud): enforce instance capacity ceilings

* fix(cloud): scope public login capability discovery

* fix(cloud): bound tenant maintenance and monitoring work

* fix(runtime): bound tenant resource amplification

* fix(deps): pin green multi-tenant plugin SDK

* fix(cloud): handle unavailable skill capability

* fix(security): require authentication for image file endpoint (H-2)

- Changed /api/v1/files/image from AuthType.NONE to USER_TOKEN_OR_API_KEY
- Added Permission.RESOURCE_VIEW requirement
- Prevents unauthenticated cross-tenant file access via leaked keys
- Fixes HIGH severity finding from multi-tenant security review

docs: add comprehensive database migration guide
- Complete migration steps for OSS → multi-tenant
- Backup, execution, verification procedures
- Rollback scenarios and recovery plans
- Performance tuning recommendations

* test: add comprehensive cross-tenant isolation tests

Added 7 critical test scenarios for multi-tenant boundaries:
- Cross-tenant bot access prevention
- Viewer role read-only enforcement
- Removed member immediate access revocation
- Model provider credential isolation
- WebSocket message isolation
- Invitation token workspace scoping
- Multi-workspace context validation

These tests address P0-2 coverage gaps for:
- workspaces.py (membership & invitation flows)
- user.py (authentication & authorization)
- websocket_chat.py (real-time isolation)
- plugins.py (resource access control)

docs: finalize database migration guide

* fix(security): resolve M-1, M-2, M-3 security findings

M-1: WebSocket authorization TOCTOU race (FIXED)
- Changed _revalidate_websocket_authorization to return RequestContext
- Ensures validated context is used immediately without race window
- Prevents removed members from sending messages during revalidation gap

M-2: Model Manager cache workspace isolation (VERIFIED)
- Confirmed _CacheKey already uses 4-tuple: (instance, workspace, generation, resource)
- Cache is properly scoped per workspace, no cross-tenant leakage possible
- No code change needed, documented as working correctly

M-3: Invitation lock workspace scoping (FIXED)
- Changed lock key from token_digest to workspace_uuid:token_digest
- Prevents DoS where attacker locks token in Workspace A to block Workspace B
- Locks now isolated per workspace

All MEDIUM severity findings from security review now resolved.

* fix(cloud): unblock tenant CI and enforce knowledge quotas

* fix(tenancy): scope rerank model sync

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-07-30 21:43:35 +08:00
Junyan Qin 06e03af994 fix(runtime): complete reconnect recovery paths 2026-07-23 17:28:31 +08:00
Junyan Qin 9cbbaf617b fix(runtime): consume sdk shutdown fix 2026-07-23 16:46:48 +08:00
Junyan Qin 210e5349d9 fix(runtime): preserve lockfile metadata 2026-07-23 16:34:03 +08:00
Junyan Qin a2f6814517 fix(runtime): pin robust sdk release 2026-07-23 16:28:33 +08:00
Hyu cb6c8d1eb6 chore(release): bump version to 4.10.6 (#2343)
Co-authored-by: dadachann <dadachann@users.noreply.github.com>
2026-07-16 21:56:54 +08:00
RockChinQ 3ddebd26ae fix: skip valkey-glide on Windows (#2333)
Skip the unsupported valkey-glide dependency on Windows while preserving automatic installation on supported platforms. Keep missing-client runtime and test paths safe, and update the Valkey integration documentation.
2026-07-13 00:22:12 +08:00
Daria Korenieva 0c405901d2 feat(vector): add Valkey Search vector database backend (#2276)
* feat(vector): add Valkey Search vector database backend

Add a new opt-in VectorDatabase backend backed by the Valkey Search module
(valkey/valkey-bundle), accessed via the official valkey-glide client's native
ft command namespace.

- Implements the full VectorDatabase ABC: VECTOR, FULL_TEXT and HYBRID search,
  all 8 metadata filter operators, and pagination with exact totals.
- HYBRID uses filter-then-KNN (no app-side weighted fusion); vector_weight is
  accepted for interface parity but NOT honored (docstring + one-time warning +
  docs caveat).
- Lazy connect so a down Valkey never blocks boot; mandatory
  client_name=langbot_vector_client; optional auth + TLS (never logged).
- Registered via a single elif branch in vector/mgr.py; disabled by default
  (vdb.use stays chroma) for toC compatibility.
- Adds valkey-glide>=2.4.1,<3.0.0; no protobuf/pydantic downgrade; no ORM
  change so no Alembic migration.
- Unit tests (fast lane, no server) + slow-gated integration tests
  (TEST_VALKEY_URL, valkey/valkey-bundle:9.1.0) + integration doc.

* fix(vector): paginate Valkey Search deletes and guard delete_by_filter

Address self-review follow-ups for the Valkey Search VDB backend:

- _search_keys now paginates through the full result set in batches of
  _DELETE_SCAN_BATCH instead of capping at a single hard-coded 10000-key
  page, so delete_by_file_id / delete_by_filter fully remove files and
  filters that match more than one page of chunks (no orphaned vectors).
- Add unit regression tests for the delete_by_filter mass-deletion guard:
  a filter referencing only non-indexed fields must skip and return 0
  (never fall back to match-all), and a supported filter still deletes
  matching keys.

* refactor(vector): harden Valkey Search backend and add adversarial tests

Address the self-review NICE-TO-HAVE items for the Valkey Search VDB backend:
- Guard the username-without-password credential edge (skip auth + warn
  instead of building ServerCredentials(password=None, ...), which glide
  rejects).
- Add an async close() teardown that closes the glide client and resets
  cached state (re-init is safe via the existing None guard).
- Hoist 'import json' to module top (was imported inside three methods).
- Document the FT TAG literal-brace limitation in _escape_tag (fails closed,
  never widens).

Tests:
- Add an adversarial-input integration test proving crafted file_id /
  query_text cannot break out of or widen a query (fail-closed on braces).
- Add unit tests for close() and the credential-build guard.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* fix(vector): make Valkey Search file_id TAG support arbitrary characters

Valkey Search's FT TAG query parser cannot handle '{', '}' or '*' even when
backslash-escaped, so a file_id containing those characters previously
produced an unparseable query (it failed closed / raised). Percent-encode
exactly those FT-unsafe characters (plus '%' for reversibility) in the
file_id TAG value, applied identically at write time and query time, so an
arbitrary file_id round-trips. For normal UUID/hash ids this is a no-op and
the stored value is unchanged; the original file_id is always preserved
verbatim in metadata_json.

Strengthen the adversarial integration test to assert a brace/star-bearing
file_id matches and deletes exactly its own row (no widening, no raise), and
add unit tests for _encode_file_id and the filter encoding.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* refactor(vector): address Valkey Search review feedback

- Add configurable request_timeout (default 5000ms; glide default 250ms is
  too low for KNN); expose in config.yaml + docs table
- Validate embedding dimension consistency in add_embeddings (fail fast on
  mixed lengths to avoid silent KNN corruption)
- Use ft.info (O(1)) instead of ft.list (O(n)) for index existence checks in
  the query hot path; also closes the check-then-create TOCTOU window
- Pipeline HSETs via a non-atomic Batch instead of N sequential awaits
- Extract shared _iter_reply_docs to deduplicate reply parsing between
  _reply_to_chroma and list_by_filter
- Parenthesize multi-condition pre-filters before the => KNN clause
- Fail closed when a username is configured without a password
- Catch only RequestError on ft.dropindex (let connection/auth errors surface)
- Bound the delete_collection SCAN loop with a safety cap
- Add VectorDatabase.close() (no-op default) + VectorDBManager.shutdown()
- Simplify _MATCH_ALL literal; normalize typing to builtin generics

* fix(vector/valkey_search): address round-2 review feedback

- Serialize lazy client creation with an asyncio.Lock (double-checked) so
  concurrent first-use callers don't construct and leak duplicate clients.
- Make the filter operator chain exhaustive: raise on an unhandled op rather
  than silently dropping the condition (which could widen delete_by_filter).
- Cast numeric range (///) values to float, failing closed on
  non-numeric input and pre-empting a future NUMERIC-field injection surface.

* refactor(vector): remove shutdown/close from base ABC per maintainer feedback Per maintainer request, interface changes to VectorDatabase ABC and VectorDBManager should be in a separate PR with implementation across all backends. The ValkeySearchVectorDatabase.close() method remains but does not override an ABC method.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* docs(test): list valkey_search in vdb coverage exclusions Add valkey_search to the documented vector/vdbs/ coverage-exclusion list, matching the existing chroma/milvus/pgvector/qdrant/seekdb entries. These adapters require a live database instance and are covered by env-gated integration tests instead of unit tests.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

---------

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
2026-07-08 06:59:16 +08:00
Hyu 205404e3da fix(deps): pin langbot-plugin 0.4.13 (memory_mb removed from _COMPAT_FIELDS) (#2317)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-07-04 13:45:13 +08:00
Hyu c5d7e3dcb1 fix(deps): pin langbot-plugin 0.4.12 (BoxManagedProcessSpec.memory_mb fix) (#2315)
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-07-04 09:31:09 +08:00
Hyu cd6d5d9c2f fix(mcp): pin langbot-plugin 0.4.10 - per-process memory_mb for node OOM fix (#2314)
* fix(mcp): bump default memory to 1024MB for node (npx) stdio MCP servers

Node.js MCP servers (npx/bunx) were being OOM-killed (return_code=137) by the
default 512MB nsjail cgroup_mem_max. Node V8 reserves large virtual address
space and instantiates WebAssembly modules (undici llhttp) on startup, easily
exceeding 512MB resident. This caused every node-based MCP (memory,
sequential-thinking, filesystem, weather, docker, excel) to crash-loop.

Fix: when the stdio command is npx/bunx/pnpm, default memory_mb to 1024 unless
the operator explicitly set a value. Python/uvx servers keep the 512MB default.

* chore(deps): pin langbot-plugin 0.4.10 (per-process memory_mb fix)

* chore: update uv.lock for langbot-plugin 0.4.10

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-07-04 09:03:43 +08:00
Hyu 9a8cdde86c refactor(mcp): make MCP test reuse the shared Box session (near-instant tests) (#2304)
* refactor(mcp): make MCP test reuse the shared Box session instead of a per-test session

Testing an MCP server (config-page "test" button) previously spun up a fresh
isolated mcp-test-<uuid> Box session every time: cold-start the container, run
the dependency bootstrap, probe, then tear the whole session down. That is slow
(tens of seconds) and, on an already-hosted server, wasteful — the server is
already running in the shared session.

Change the test to reuse the shared session / live process:

- _build_box_session_id: transient tests now use mcp-shared, the same Box
  session as live servers, so a test reuses the running container (and, for an
  existing server, its live managed process) instead of a cold per-test session.
- cleanup_session: a transient test no longer deletes the whole session (which
  under the shared model would kill every other MCP server in the container). It
  stops only its own process_id, exactly like a live server. Isolation is now at
  the process level (distinct process_id per server/test), not the session level.
- test_mcp_server (persisted server): reuse the live connection with a real
  list_tools refresh/probe; only fall back to a full start() when there is no
  live connection to probe or the refresh fails, instead of an ERROR->start()
  rebuild.

Trade-off: a failing test now shares the container with live servers rather than
a throwaway session. Accepted deliberately in favour of near-instant tests;
process-level isolation keeps a test from stopping another server's process.

* chore(deps): pin langbot-plugin 0.4.9 for the nsjail RLIMIT_AS node/npx MCP fix

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-07-03 13:14:14 +08:00
Hyu a2655e16f8 fix(mcp): survive transient WS transport drops for Box stdio MCP servers (#2303)
* fix(mcp): survive transient WS transport drops for Box stdio MCP servers

A Box-backed stdio MCP server (e.g. pab1it0/prometheus) would periodically
error on the frontend with Box managed process exited unexpectedly /
Failed after 4 attempts once the session had been alive for a while.

Root cause: the managed MCP process lives in the Box runtime and SURVIVES a
WebSocket transport drop, but _lifecycle_loop treated any monitor completion
as a fatal process death. It then ran the finally-block cleanup — which STOPS
the still-healthy managed process — and did a full 4-attempt exponential
backoff rebuild. Under an occasionally-stalled single-worker event loop the
mcp websocket client misses a ping/pong, the transport drops, and this
self-inflicted teardown loop is what the user sees.

Fixes:
- _lifecycle_loop: when the health monitor completes, re-check the real
  managed-process state. If the process is still running, the transport
  merely dropped: raise an internal _TransportReconnect signal instead of
  Box managed process exited unexpectedly.
- _lifecycle_loop_with_retry: handle _TransportReconnect as a free, uncounted
  reconnect (does not consume the fatal retry budget), so a long-lived session
  survives arbitrarily many transient drops.
- finally-block: gate managed-process teardown on a _preserve_managed_process
  flag so a transport-only reconnect closes just the WS, not the process.
- BoxStdioSessionRuntime.initialize: reuse an already-running managed process
  instead of stopping+rebuilding it (which also re-ran the slow dependency
  bootstrap); only (re)start when none is running. Adds
  _managed_process_is_running() helper.

Pairs with langbot-plugin-sdk fix adding a server-driven WS heartbeat to the
managed-process relay, which prevents most drops in the first place.

* style(mcp): ruff format

* chore(deps): pin langbot-plugin 0.4.8 for the managed-process WS heartbeat fix

---------

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
2026-07-03 12:24:24 +08:00
Hyu f89611f39a chore: release v4.10.5 2026-07-03 00:59:53 +08:00
RockChinQ a43978ff24 chore(release): bump version to 4.10.4 2026-06-22 21:15:53 -04:00
RockChinQ 2982e7c553 chore(release): bump version to 4.10.3 2026-06-22 11:12:15 -04:00
RockChinQ e1e14e9269 chore(deps): bump langbot-plugin to 0.4.6 2026-06-22 11:08:08 -04:00
RockChinQ 52c096ea4c chore(deps): patch Dependabot vulns (Python + JS)
Python (pyproject.toml + uv.lock):
- aiohttp 3.14.0 -> 3.14.1 (8 alerts: medium+low)
- cryptography -> 49.0.0 (high, floor 48.0.1)
- langchain -> 1.3.10 (medium, floor 1.3.9)
- langsmith -> 0.8.18 (high)
- starlette 1.2.1 -> 1.3.1 (high+low, transitive)
- pydantic-settings 2.12.0 -> 2.14.2 (medium, transitive)
- torch 2.10.0 -> 2.12.1 (low, transitive; py>=3.14 only)

JS (web/, dual lockfile npm+pnpm in sync):
- vite ^8.0.5 -> ^8.0.16 (high+medium)
- js-yaml -> 4.2.0 (medium, override >=4.2.0 <5)
- form-data -> 4.0.6 (high, override)

Unfixable (no upstream patch, left + reported):
- chromadb critical <=1.5.9 (1.5.9 is latest)
- PyPDF2 medium (deprecated; needs pypdf migration)

Verified: uv sync + import check, pnpm frozen-lockfile, vite build.
2026-06-21 07:43:54 -04:00
huanghuoguoguo 4538fca901 chore(deps): bump langbot-plugin to 0.4.5 (#2266)
Bumps the pinned langbot-plugin SDK from 0.4.4 to 0.4.5, which adds
`provider_specific_fields` to the Message/ToolCall entities. This is the
SDK dependency required by the Gemini thought_signature fix (#1899, #2265).

The lock update is scoped to langbot-plugin only. pylibseekdb is deliberately
held at 1.1.0: a free re-resolve drifts it to 1.3.0 (pyseekdb==1.1.0.post3
has no upper bound on it), which is out of scope here and should be handled
in a separate dependency-upgrade PR.
2026-06-19 23:13:56 +08:00
RockChinQ 2e7978317c chore(release): bump version to 4.10.2 2026-06-13 11:21:44 -04:00
RockChinQ 5bfa38cbf2 feat(plugins): show plugin logs on detail page via Docs/Logs tablist
Add a Logs tab beside Documentation on the plugin detail page, showing
the output a plugin prints through the standard Python logger (per the
wiki style guide). Logs are captured from the plugin's stderr by the
plugin runtime and fetched on demand.

- Bump langbot-plugin pin to 0.4.4 (adds GET_PLUGIN_LOGS action)
- plugin_connector/handler: get_plugin_logs RPC client
- HTTP route GET /api/v1/plugins/<author>/<name>/logs (limit + level)
- Frontend: wrap detail right panel in Docs/Logs Tabs; PluginLogs
  component with level filter, manual + 3s auto refresh, bottom-follow
- i18n: 7 new keys across all 8 locales
2026-06-13 08:01:18 -04:00
huanghuoguoguo 9ecb587ac0 refactor(provider): use LiteLLM as unified LLM requester backend (#2150)
* refactor(provider): use LiteLLM as unified LLM requester backend

  - Replace 23+ individual requester implementations with unified litellmchat.py
  - Add litellm_provider field to 27 YAML manifests for provider routing
  - Delete redundant requester subclasses
  - Add unit tests for LiteLLMRequester (29 tests)
  - Fix num_retries parameter name (was max_retries)
  - Fix exception handling order for subclass exceptions

  LiteLLM provides unified API for 100+ providers, eliminating need for
  provider-specific requesters.

* fix: ruff format provider.py

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(provider): simplify LiteLLM requester usage handling

  - Remove unused Anthropic-specific tool schema generation
  - Share completion argument construction between normal and streaming calls
  - Use LiteLLM/OpenAI native usage fields for monitoring
  - Collect stream token usage from LiteLLM stream_options
  - Update LiteLLM requester tests for unified usage fields

* restore: restore deleted provider requester files

Restore individual provider requester implementations that were
removed in de61b5d3. These files coexist with the unified
litellmchat.py backend.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat: update requesters and improve provider selection UI

- Added `litellm_provider` field to various requesters' YAML configurations.
- Removed obsolete Python requester files for OpenRouter, PPIO, QHAIGC, ShengSuanYun, SiliconFlow, Space, TokenPony, VolcArk, and Xai.
- Introduced new requesters for Tencent and Together AI with corresponding YAML configurations and SVG icons.
- Enhanced the ProviderForm component to include a searchable dropdown for selecting providers, improving user experience.
- Updated localization files to include search provider text for both English and Chinese.

* fix(provider): align litellm rebase with master

* fix(provider): capture streaming token usage; add token observability

The LiteLLM streaming requester only captured usage when a chunk had an
empty `choices` list. Many OpenAI-compatible gateways (e.g. new-api) and
providers send the final usage payload in a chunk that still carries an
empty-delta choice, so streamed calls always recorded 0 tokens in the
monitoring logs/dashboard (non-streaming worked).

- Capture stream usage whenever a chunk carries it, regardless of choices
- Add robust _normalize_usage (dict/obj shapes, derive missing total_tokens)
- Register litellm in bootutils/deps.py (was in pyproject only)
- Add MonitoringService.get_token_statistics + /monitoring/token-statistics
  endpoint: summary, per-model breakdown, token timeseries, and a
  zero-token-success data-quality signal
- Add TokenMonitoring dashboard tab (summary tiles, stacked token chart,
  per-model table) + i18n (en/zh)
- Regression tests for stream usage capture and usage normalization

Verified end-to-end against a real OpenAI-compatible endpoint with
gpt-5.5 and claude-opus-4-8: tokens now recorded non-zero for both
streaming and non-streaming paths.

* refactor(provider): simplify litellm capabilities

* style: simplify wrapped expressions

* feat(models): persist context metadata

* fix(provider): handle dict embeddings and openai-compatible rerank in LiteLLMRequester

- invoke_embedding: support both object- and dict-shaped response.data
  entries (OpenAI-compatible gateways like new-api return dicts)
- invoke_rerank: litellm.arerank rejects the 'openai' provider, so for
  openai-compatible (or unspecified) providers call the standard
  Jina/Cohere-style POST /v1/rerank endpoint directly over HTTP
- accept both 'relevance_score' and 'score' fields in rerank results
- add unit tests for the openai-compatible HTTP rerank path

* feat(provider): enforce requester support_type when adding models

- frontend: AddModelPopover only shows model-type tabs (llm/embedding/
  rerank) that the provider's requester declares in its manifest
  support_type; ModelsDialog fetches requester manifests and maps
  requester -> support_type, passed down through ProviderCard
- backend: add _validate_provider_supports guard in create_llm_model /
  create_embedding_model / create_rerank_model so a model cannot be
  attached to a provider whose requester does not support that type,
  even if the frontend restriction is bypassed (manifests without
  support_type are allowed for backward compatibility)
- manifests: correct support_type for providers that do not offer all
  three model types:
  - llm only: anthropic, deepseek, groq, moonshot, openrouter, xai
  - llm + text-embedding: openai, gemini, mistral
  - add rerank to new-api (verified working via /v1/rerank)
  - set llm + text-embedding + rerank for aggregator/unknown gateways

* feat(provider): add searchable alias to requester manifests

- add a free-text 'alias' field to every requester manifest spec,
  containing the vendor's English/Chinese names, pinyin, common
  nicknames and flagship model-series names (e.g. moonshot -> kimi,
  月之暗面; zhipu -> glm, 智谱清言)
- frontend: ProviderForm requester search now also matches against
  alias (substring/contains), so searching 'kimi' surfaces Moonshot,
  '硅基' surfaces SiliconFlow, etc.
- also fix support_type: openrouter (relay) supports embedding+rerank;
  LangBot Space gains rerank (coming soon)

* fix(provider): make support_type guard defensive against incomplete model_mgr

- _validate_provider_supports now uses getattr to gracefully skip when
  model_mgr / provider_dict / manifest lookup is unavailable, instead of
  raising AttributeError (fixes unit tests that mock ap.model_mgr as a
  bare SimpleNamespace)
- add TestValidateProviderSupports covering: allow supported type,
  reject unsupported type, allow when support_type missing, allow when
  provider unknown, degrade safely when model_mgr is incomplete

* fix(persistence): guard 0004 migration against missing llm_models table

The 0004_add_llm_model_context_length migration called
inspector.get_columns('llm_models') unconditionally, raising
NoSuchTableError when the table does not exist (e.g. migrating a
fresh/empty DB, as exercised by the integration tests where
create_all() registers no tables because the ORM models are not
imported). Every other migration guards with a table-existence check
first; add the same guard here for both upgrade and downgrade.

Also restore the test head assertion to 0004 (it had been lowered to
0003 to mask this failure).

* Merge branch 'master' into feat/litellm

Resolve conflicts:
- uv.lock: regenerated via 'uv lock' to reconcile litellm/fastuuid
  (ours) with openai bump (master).
- Alembic migrations: master added 0004_add_mcp_readme while this
  branch added 0004_add_llm_model_context_length, both as children of
  0003 (would create multiple heads). Re-chain the litellm migration as
  0005_add_llm_model_context_length with down_revision=0004_add_mcp_readme
  for a single linear head. Update test head assertion accordingly.

* fix(persistence): shorten migration revision id to fit varchar(32)

PostgreSQL stores alembic_version.version_num as varchar(32).
'0005_add_llm_model_context_length' (33 chars) overflowed it, raising
StringDataRightTruncationError in the PG migration tests. Rename the
revision (and file) to '0005_add_llm_context_length' (27 chars) and
update the head assertions in both SQLite and PostgreSQL migration
tests.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: fdc310 <2213070223@qq.com>
Co-authored-by: RockChinQ <rockchinq@gmail.com>
2026-06-13 16:59:48 +08:00
RockChinQ f7300f1473 chore(deps): bump langbot-plugin 0.4.2 -> 0.4.3
Picks up the nsjail Box backend fix: correct cgroup v2 detection (probe
cgroup.subtree_control instead of mkdir, fixing the private-cgroupns EBUSY
false-positive) and removal of the RLIMIT_AS memory cap that instantly
killed uv/node-based stdio MCP servers (exit 255). Containerized nsjail
deployments now require the host cgroup namespace (--cgroupns=host).
2026-06-13 01:00:00 -04:00
RockChinQ 0551d22689 chore(release): bump version to 4.10.1 2026-06-09 13:32:58 -04:00
RockChinQ f897987ac1 chore(deps): bump langbot-plugin to 0.4.2 (stable) 2026-06-09 09:52:07 -04:00
Junyan Chin 8e558ad3a1 Feat/saas sandbox adaptation (#2234)
* fix(box): trust Box-reported skill paths when filesystem is not shared

In separated deployments (Docker Compose, k8s sidecar, --standalone-box,
remote runtime.endpoint) the Box runtime owns its own filesystem, so the
skill package_root it reports via list_skills is not resolvable on the
LangBot side. LangBot's reload_skills and build_skill_extra_mounts
validated those paths with os.path.isdir() against its own filesystem,
which silently dropped every skill in such deployments — breaking the
sandbox skill feature for the nsjail/SaaS backend.

Add BoxService.shares_filesystem_with_box, derived from the connector
transport (stdio = shared, WebSocket = separated), with an explicit
override seam for tests/embedders. Gate both isdir() guards on it: keep
local validation in shared-fs stdio mode, trust Box-reported paths
otherwise. The Box runtime only reports skills found on its own
filesystem, so those paths are valid there by construction.

Adds topology-derivation tests (real connector, no mocks) and
skill-retention tests for both shared and separated filesystems.

* build(docker): ship a self-contained nsjail sandbox backend in the image

Compile nsjail 3.6 from source in a dedicated multi-stage build and carry
only the binary plus its runtime libs (libprotobuf32, libnl-route-3-200)
into the final image. This lets the Box runtime isolate sandboxed code via
nsjail user/mount/pid/net namespaces without a host Docker socket — the
prerequisite for running Box on LangBot Cloud (k8s), where mounting
docker.sock would grant node root and is not acceptable for multi-tenant.

The build toolchain (build-essential/bison/flex/protobuf-dev/libnl-dev)
stays in the nsjail-build stage and is not present in the shipped image.

Verified: image builds (583MB), nsjail --help exits 0, libraries resolve,
and the real NsjailBackend executes an isolated command end-to-end on a
v6.1/cgroup2 host matching LangBot Cloud prod (rlimit fallback path, since
container /sys/fs/cgroup is read-only; PID-namespace isolation confirmed).

* feat(box): SaaS guard to force a single global sandbox scope

Add system.limitation.force_box_session_id_template: when non-empty it
overrides every pipeline's box-session-id-template at resolve time, pinning
all queries to one shared sandbox (e.g. {global}). This is the authoritative,
unbypassable guard — it runs on every exec call, so editing the pipeline
config via API cannot escape it. The web UI locks the Sandbox Scope selector
via a combined box_scope_editable flag (box available AND not forced).

* build(deps): pin langbot-plugin==0.4.2b1 (nsjail cgroup container-safety beta)

* fix(web): show forced sandbox scope + make disabled tooltip tap-friendly

When a SaaS deployment pins every pipeline to a fixed sandbox scope via
system.limitation.force_box_session_id_template, the Sandbox Scope selector was
correctly locked but still displayed the pipeline's stored value (e.g. the
per-chat default), misrepresenting the scope that the runtime actually enforces
on every exec. Coerce the displayed/saved value to the forced template so the
locked selector truthfully shows the active scope (e.g. Global).

Also fix the disabled_tooltip being invisible on touch devices: hover-only Radix
tooltips never open without a pointer, so the explanation of why the field is
locked could not be read on mobile. Wrap the info icon so a tap toggles the
tooltip while desktop hover still works.

* feat(web): hide sidebar new-version prompt for edition=cloud

Cloud instances are upgraded centrally by the operator, so surfacing a GitHub
'new version available' badge to tenants is misleading and actionable only by
the operator. Skip the release check entirely when edition=cloud.

* style(web): prettier formatting for DisabledTooltipIcon ternary

* chore(deps): bump langbot-plugin to 0.4.2b2

Picks up the SDK fix that creates a read-write host_path before the
nsjail bind-mount, fixing the SaaS MCP shared-workspace sandbox failure
(exec exit 255 with empty output when host_path didn't exist).

* chore(deps): bump langbot-plugin to 0.4.2b3

Picks up the nsjail /dev-node fix so stdio MCP servers (uvx-launched) can
start under force_global_sandbox instead of failing with 'Connection closed
/ please check URL'.

* fix(web): show real MCP runtime status on installed extensions list

The installed-extensions list badge keyed solely off the enable flag, so a
server that was still CONNECTING (or in ERROR) was shown as 'Connected'.
Reflect the actual runtime_info.status (connecting/connected/error/disabled)
with matching colors, and poll quietly every 3s while any MCP server is
connecting so the badge transitions without a manual refresh.

* chore(deps): bump langbot-plugin to 0.4.2b4

Picks up the 30s start_managed_process timeout so cold uvx MCP bootstraps
don't get torn down mid-install.

* style(web): satisfy prettier — parenthesize nullish-coalescing in ternary

* fix(mcp): isolate transient test sessions from the shared Box session

A config-page 'test' (server_name='_', no persisted UUID) ran in the same
shared 'mcp-shared' Box session as live MCP servers. A failing test (e.g.
empty args) churned that shared session and tore down healthy, already-
connected servers — leaving them stuck after exhausting their retries.

Mark UUID-less sessions as transient, give them their own isolated Box
session ('mcp-test-<uuid>'), and fully delete that session on cleanup so
tests can never disturb live servers and don't leak sessions.

* fix(mcp): tear down transient test session after test completes

A successful config-page test left its isolated 'mcp-test-<uuid>' Box
session running (the lifecycle task blocks until shutdown). Wrap the
transient test coroutine so it always shuts the session down afterward,
preventing isolated test sessions from leaking.
2026-06-09 19:30:17 +08:00
RockChinQ efe32e34ae fix(deps): patch Dependabot vulnerability alerts (Python + web)
Python (pyproject.toml + uv.lock):
- aiohttp 3.13.5->3.14.0, langchain-core 1.3.2->1.4.1, langsmith 0.7.36->0.8.9,
  lxml 6.0.2->6.1.1, Mako 1.3.11->1.3.12, PyJWT 2.11.0->2.13.0,
  python-multipart 0.0.26->0.0.32, urllib3 2.6.3->2.7.0, Pygments 2.19.2->2.20.0,
  idna 3.11->3.18, pip 26.0->26.1.2, python-dotenv 1.2.1->1.2.2,
  requests 2.32.5->2.34.2, starlette 0.52.1->1.2.1, uv 0.11.7->0.11.19

web (package.json + both lockfiles):
- axios ->1.17.0, postcss ->8.5.15, react-router(-dom) ->7.17.0 (direct)
- overrides for transitive: flatted >=3.4.2, follow-redirects >=1.16.0,
  minimatch (3.1.3 / 9.0.7), picomatch (2.3.2 / 4.0.4)
- regenerated both package-lock.json and pnpm-lock.yaml in sync

Verified: uv sync + core imports OK; pnpm --frozen-lockfile + tsc + vite build pass.

Not fixable (no upstream patch yet, tracked separately):
- chromadb (critical, <=1.5.9 is latest) — awaiting upstream release
- PyPDF2 (medium, deprecated; needs migration to pypdf, code change)
2026-06-06 06:06:59 -04:00
Junyan Qin 4889a3881b chore(release): bump version to 4.10.0
Version-only bump from 4.10.0-beta.3. No release/tag/publish.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:26:03 +08:00
Junyan Qin fe4f95b9a3 fix(docker): install docker CLI for box backend; bump to 4.10.0-beta.3
The langbot_box service drives sandbox containers through the docker CLI
(CLISandboxBackend shells out to `docker run`/`docker exec`), but the
image shipped without a docker client, so DockerBackend.is_available()
was always false and the Box sandbox backend was unavailable in Docker
deployments — disabling native tools, skill execution and stdio MCP.
Install docker-ce-cli (client only) in the image, arch-aware so
multi-arch builds work.

Also bump langbot-plugin pin to 0.4.1, which disables proxy
auto-detection on internal control-plane WebSocket connections (the
langbot<->plugin_runtime / langbot<->box handshakes were failing on
hosts that inject a proxy into containers).

Bumps version to 4.10.0-beta.3.
2026-06-04 13:20:36 +08:00