Commit Graph
11 Commits
Author SHA1 Message Date
lyingbug 6afb6ca9d5 feat(knowledge): flag documents that stop making progress (#3557)
* feat(knowledge): flag documents that stop making progress

A document stuck in processing looked the same as one still working: a
spinner that polled forever, no hint of where it stopped, and, hours
later, a generic housekeeping failure.

- The batch and spans endpoints return last_activity_at for in-flight
  rows: the later of updated_at and the newest span write. updated_at
  only moves at stage transitions; span writes keep moving while a long
  stage still works.
- After 20 minutes with no progress, the list and card views show
  "may be stuck" with a tooltip. The timeline shows the stage it
  stopped at and a stop-parsing button, plus a "last progress" stat.
  Both pollers back off to 15s while everything in flight is stalled.
- When housekeeping fails a row, the error names the stalled stage and
  the time of the last progress. It closes the row's open spans with
  TASK_STALLED (the running stage fails, the rest are cancelled), so
  the timeline shows where the run stopped instead of a spinner.

* fix(knowledge): locate stalls in finalizing and tell backlog from stuck

- Housekeeping reads the stall site from the latest attempt. It takes
  the running stages, or, when none runs (post-process closes its stage
  once enrichment is fanned out), the innermost running spans, mapped
  back to their stage. Those spans fail with TASK_STALLED and are named
  in the message; every stalled stage is listed in pipeline order. The
  other open spans are cancelled. Every closed span gets a duration, and
  the message leaves out the time when the heartbeat query failed.
- The spans tree prefers a TASK_STALLED failure for last_error over a
  later subtask failure. current_stage falls back to the stage owning a
  running subspan.
- Batch and spans responses flag rows quiet past the stall hint whose
  work is still queued (waiting_in_queue), using the sweep's own probes.
  At most 20 rows are probed per request, cached for a minute. The UI
  shows these as queued, with no stop button in the banner.

* fix(knowledge): answer stall verdicts from one shared queue scan

- Probing a quiet document for a backlog rescanned the whole queue once
  per document. When the document was absent, which is exactly the
  stuck case, the scan ran to the end, and it read a Redis error as
  "nothing queued". QueuedKnowledgeIDs now collects every referenced
  knowledge ID in one pass and returns list errors. HousekeepingService
  keeps that snapshot for a minute, shared across requests and
  single-flight. A failed scan is not cached.
- With the per-request cap of 20 gone, every quiet row on a page gets a
  verdict. The response carries it as stall_state (queued or stalled),
  replacing waiting_in_queue. The UI marks a row as stuck only on a
  "stalled" verdict. A row with no verdict, because the probe failed,
  shows as ordinary processing instead of inviting a stop.
2026-09-22 21:34:08 +08:00
wizardchen 4c0d42c4cb feat(agent): keep long sandbox turns from truncating, overflowing, or rewriting files
Refuse truncated tool calls, compact-and-retry once on overflow, and let
sandbox files be appended, paged, and batch-edited instead of rewritten.
Models can set context_window (default 200K) so compaction sizes itself
from the real window; agent max_iterations of -1 means unlimited.
2026-09-02 22:09:09 +08:00
988cbb0330 fix(models): show usage details when deletion is blocked (#2975)
* fix(models): show usage details when deletion is blocked

* fix(models): open the matching agent section and cap usage lists

Agent "open configuration" now lands on the tab that owns the binding
(VLM/ASR vs follow-ups), and delete failures keep the server message when
structured details cannot be parsed. Listed KB/agent rows are capped at 50
while totals still drive the deletion guard and dialog headings.

---------

Co-authored-by: Leon <leon_hfut@foxmail.com>
Co-authored-by: wizardchen <wizardchen@tencent.com>
2026-09-02 17:20:37 +08:00
wizardchen 41975abcc5 refactor(terminology): rename user-facing tenant labels to workspace
Align product copy, API docs, Swagger descriptions, and i18n strings
with workspace terminology while keeping internal tenant identifiers
and headers unchanged for API compatibility.
2026-07-14 16:14:43 +08:00
wizardchen ecf2c1fb9b feat(auth): support tenantless provisioning and gated self-service workspace creation
Allow deployments to register users without auto-creating a personal tenant, expose creation policy via /auth/me, and guide tenantless accounts through a workspace onboarding flow.
2026-07-13 18:13:51 +08:00
wizardchen 04f56f9cda feat(knowledge): track per-stage parsing progress with /stages API
Adds a five-segment progress model for the document parsing pipeline so
the UI (PR③) can render a timeline showing where each document is
(DocReader → Chunking → Embedding → Multimodal → PostProcess) and
which stage failed with what error code.

- New table `knowledge_processing_stages` (migration 000052) with one
  row per (knowledge_id, stage). UPSERT on Begin/Done/Fail bumps an
  attempt counter so re-parses don't lose history.

- StageTracker service exposes Begin/Done/Fail/Skip; all calls are
  best-effort and never break the pipeline if persistence fails.

- Stable error codes (DOCREADER_TIMEOUT / EMBEDDING_RATE_LIMIT /
  VECTORSTORE_WRITE_FAILED / ...) the UI can map to localized
  remediation hints.

- Tracker call sites added at the four meaningful failure points:
  convert (DocReader), CreateChunks (Chunking), BatchIndex (Embedding),
  enqueueImageMultimodalTasks (Multimodal start),
  KnowledgePostProcess.Handle (Multimodal close + PostProcess).

- New endpoint `GET /api/v1/knowledge/:id/stages` returns the five
  canonical stages — missing rows are synthesized as "pending" so
  the timeline always renders five segments. Includes current_stage
  and last_error block.
2026-05-28 15:14:45 +08:00
wizardchen 7d030a6f6a feat(tenant): implement tenant creation limit and error handling
Added a new configuration option to limit the number of tenants a non-superuser can create via self-service. Introduced a new error type for handling cases where users exceed this limit, returning a 429 status code. Updated the tenant creation and update handlers to enforce this limit and provide appropriate feedback to users. Additionally, refactored the tenant update request structure to ensure only mutable fields are allowed.

Refs: #1303
2026-05-18 17:28:58 +08:00
wizardchen 8efe1b4187 refactor(rbac): address kb-access guard review findings
Follow-up to the route-level KB-access guard introduced earlier in
this PR. Five reviewer findings, all fixed in middleware + tests
without changing handler call sites:

  - Honour `?agent_id` in the agent-share fallback. The in-handler
    resolution in knowledgebase.go branches on a specific agent's
    KBSelectionMode (all / selected / none); the guard was only ever
    calling TenantCanAccessKBViaSomeSharedAgent, which let some
    requests pass at the guard layer that the handler would then
    reject — and rewrote the request's tenant context to the source
    tenant before the rejection happened. The guard now mirrors the
    handler: a specific agent_id pins the resolution to that agent
    (no fallback to any-agent), an empty agent_id keeps the existing
    any-agent behaviour. The knowledge.go / knowledgebase.go handler
    helpers stay in place as defence in depth until they're folded
    into the guard in a follow-up.

  - Distinguish not-found vs. transient on the knowledge / chunk
    resolvers. A DB hiccup used to surface as a 404, both confusing
    clients and hiding the underlying failure from monitoring. The
    resolvers now return apperror 404 only for the known not-found
    sentinels (apprepo.ErrKnowledgeBaseNotFound, ErrResourceNotFound)
    and let other errors propagate; the guard maps those to a fresh
    NewServiceUnavailableError (503).

  - Stop 500-ing on legacy chunks with empty knowledge_base_id. The
    chunk is effectively unresolvable to a KB, so the client gets the
    same 404 they'd get for a missing chunk instead of a 500 that
    pollutes alerting. The warn log preserves the operator signal.

  - Honour cfg.Tenant.EnableRBAC. The guard now mirrors RequireRole /
    RequireOwnershipOrRole: with enforcement off, 401/403 paths log
    the would-be rejection and pass through. 404 still fires either
    way (a missing resource is not an authorisation event). Keeps the
    rollout window safe — operators can flip enforcement on globally
    without code changes elsewhere.

  - Document the c.Keys vs. c.Request.Context() split. The guard
    intentionally rewrites only the request context (handlers that
    still run their own share resolution, currently knowledge.go /
    knowledgebase.go, would otherwise see kb.TenantID == c.Keys
    tenant and misclassify shared-Viewer access as Admin). The
    package-level comment now spells this out so the migration of the
    remaining handlers in a follow-up doesn't accidentally regress
    `my_permission` rendering.

Tests:

  - Existing four cases keep working; runGuard signature gained a
    guardOpts knob.
  - Eight new cases cover the agent-share resolution paths (any-agent
    viewer, Editor required → must reject, specific agent in modes
    all / selected-match / selected-miss / none, cross-tenant
    mismatch) plus the EnableRBAC=false fail-open + 404-still-fires
    pair.
2026-05-18 17:28:58 +08:00
ochan.kwon 0e8de6192c feat(knowledge-base): validate vector store bindings on create, copy, and delete
Wires KnowledgeBase.VectorStoreID and the ownership-aware retrieve factory
into the user-facing knowledge-base lifecycle:

- POST /knowledge-bases validates the requested vector_store_id against
  the caller's tenant scope and the engine registry. New error codes
  ErrVectorStoreBindingInvalid (2200) and ErrVectorStoreUnavailable (2201)
  distinguish the typed branches without echoing UUIDs to the client.
- GET / POST / PUT / PUT-pin responses embed the bound store's display
  metadata (name, source, engine_type, status) without exposing any
  connection credentials. Cross-tenant shared KBs receive a suppressed
  payload (vector_store_id stripped, source="shared") so operator-chosen
  store names cannot be enumerated across tenants.
- POST /knowledge-bases/copy synchronously rejects clones whose target
  has a different embedding model or vector store, before the async
  clone task is enqueued. The async clone worker re-applies the same
  checks for defense in depth.
- DELETE /vector-stores/:id refuses to remove a store with bound KBs,
  inside a transaction that row-locks the store on PostgreSQL and
  serializes via WAL on SQLite. unregister-from-registry is wrapped in
  defer/recover so a panic surfaces as a structured warning instead of
  silently leaking a stale engine.
- vector_store_id is immutable after creation. The GORM <-:create tag
  blocks every ORM update path; the service-layer DTO omits the field
  entirely; a reflection-based regression test catches any future
  maintainer who adds it back to either layer.
- Empty-string vector_store_id is normalized to nil at both the create
  path and inside SharesStoreWith, so rows persisted by callers that
  did not run Normalize first cannot trip false same-store comparisons.

Part of #993. Depends on #994 and #1310.
2026-05-18 15:58:46 +08:00
wizardchen 4fa3adbf3b feat: Add agent configuration and cleanup scripts for database migrations 2025-11-05 23:18:44 +08:00
wizardchen 56eb2bce33 init commit 2025-08-05 15:08:07 +08:00