* feat(knowledge): add optional platform connections and hierarchical spaces * fix(knowledge): return actionable errors for unavailable policy storage * perf(knowledge): resolve space policy once per snapshot
Optional knowledge management extension
This package implements the first management slice of #6269's second stage: document ingestion and provider-owned processing. It installs no frontend, model tools, database, parser, embedding model or index. Existing retrieval works without this package.
The supplied adapter targets WeKnora v0.8.3, commit 3e22822c1d2397be730c458c7539d0eb8c113a24. API mappings were checked against its handlers and superbuzzy/deer-flow's community/weknora/documents.py. This is a new independent extension using the current host contracts, not a merge of that branch.
Installation and authorization
From the repository root:
make extension-install SOURCE=./examples/deerflow-extension-knowledge-management
Installation alone does not expose management routes. Configure the installed plugin's config in operator-owned config.yaml, then restart Gateway:
plugins:
- use: deerflow_extension_knowledge_management:install
config:
enabled: true
provider_config:
connections:
- id: team-reference
base_url: https://weknora.example
tenant_id: "1"
api_key_env: WEKNORA_MANAGEMENT_KEY
grants:
- user_id: exact-deerflow-admin-user-id
knowledge_base_ids: [native-weknora-base-id]
operations: [capabilities, status, upload, url, manual, reprocess, cancel]
Set the credential in the Gateway environment. Use a restricted upstream key with retrieval and ingestion rights only for the intended bases. A retrieval-only key will fail writes; the extension never substitutes a more privileged key. Disable by setting config.enabled: false or removing the extension, then restarting Gateway. There is no runtime settings override or separate page switch.
Every route requires all of:
- Gateway authentication and its normal CSRF protection for writes.
- Host
plugin_managementread/write authorization forknowledge-management. - A host-resolved administrator principal, excluding internal callers.
- An explicit operator grant matching the exact host user, connection, native knowledge base and operation.
- Upstream resource authorization using that connection's key. Existing documents must also belong to the selected base before processing mutations.
This initial authorization mode is explicit administrator resource grants with service credentials. It does not claim upstream user impersonation, general organization RBAC, Agent-delegated writes, or platform-space authorization. Existing WeKnora retrieval user_ids confer no management rights. The management grants do not grant retrieval access. Client-provided user, Agent, tenant and space fields are rejected. Future multi-user/space management must supply a separately reviewed authorization implementation; do not remove the administrator floor to imply those capabilities exist.
To retain the registered knowledge-management namespace for space-routed authorization while disabling these direct routes, set enabled: true and routes_enabled: false. In that mode no Provider factory is instantiated; the platform extension supplies its own operator-configured Provider. The default routes_enabled: true preserves existing installations.
HTTP contract, version 1
All routes have this prefix:
/api/extensions/knowledge-management/connections/{connection}/bases/{base}
connection is the operator-defined connection ID; base is the native Provider base ID. Browser clients use DeerFlow's authenticated API and normal CSRF mechanism.
| Method and suffix | Request | Result |
|---|---|---|
GET /capabilities |
None | Allowed operations, processing options, provider-managed stages |
POST /documents |
URL or manual JSON below | 202 with document processing snapshot |
POST /files?filename=guide.txt |
Raw application/octet-stream, at most 16 MiB |
202 with snapshot |
GET /documents/{document} |
None | Current upstream document processing snapshot |
POST /documents/{document}/reprocess |
Processing options object, or {} |
202 with upstream snapshot |
POST /documents/{document}/cancel |
{} |
202 with upstream snapshot |
Manual ingestion explicitly publishes the source, so it enters the upstream processing pipeline instead of remaining a draft:
{"kind":"manual","title":"Guide","content":"Cedar evidence","processing":{"chunk_size":512,"chunk_overlap":64}}
URL ingestion delegates fetching and SSRF enforcement to WeKnora's validated URL-ingestion endpoint; DeerFlow does not fetch the URL:
{"kind":"url","url":"https://example.com/guide","title":"Guide"}
File ingestion optionally accepts X-Processing-Options containing the same JSON processing options. JSON requests are limited to 2 MiB; manual text is limited to 1,000,000 characters. Filenames cannot contain path separators or header control characters. No client filesystem path is accepted.
The snapshot contains version, provider, connection_id, dataset_id, document_id, state, provider_state, and searchable. The WeKnora dataset_id uses exactly the existing retrieval adapter's endpoint/tenant/base identity. A completed document can therefore be selected in the existing chat scope selector when the separately configured retrieval connection authorizes it.
States are queued, running, completed, failed, cancelled, or unknown. New upstream statuses stay unknown; they are never interpreted as successful. finalizing is running and may already be searchable, matching the existing retrieval contract. Cancellation is not deletion or rollback of already written chunks. 202 confirms a provider response, not completed processing. A document's terminal parse status does not establish that every Wiki/graph enrichment succeeded.
Error semantics: 403 denied, 404 missing resource, 409 conflict, 413 size limit, 415 upload media type, 422 invalid/unsupported input, 502 upstream failure, 503 missing/invalid local management credential (no upstream request made). Raw upstream bodies and credentials are never returned. No writes are automatically retried, including on timeout, cancellation, malformed response or conflict. If delivery is uncertain, reconcile using the upstream catalog before resubmitting; this version does not promise exactly-once ingestion or persist admission records.
Processing scope
| Pipeline part | Adapter behavior |
|---|---|
| Parse and chunk | Performed by upstream ingestion/reparse; bounded chunk size/overlap overrides |
| Vector indexing | Uses the upstream base's model and indexing configuration; capability describes availability |
| Summary | Optional summary_enabled override |
| Knowledge entry generation/update | Upstream Wiki pipeline of a Wiki-enabled base during ingestion/reparse; no standalone Wiki editing endpoint |
| Entity extraction, relations and graph indexing | Upstream graph pipeline; graph_enabled: true requires that the base already supports graph extraction |
| Retry/cancel | Explicit reprocess/cancel operations; no automatic retry or local worker |
Missing optional stages are reported as unsupported. Processing options are base-aware: graph controls require graph support, and chunk overrides are advertised only for a valid non-parent-child chunking configuration. The extension does not simulate them or accept arbitrary provider options. Parser engine credentials, model selection, Wiki settings and graph extraction schema remain operator-controlled in WeKnora; this API cannot change the base-wide policy. Chunk overrides preserve the upstream base’s other chunking settings. Bases using parent-child mode reject chunk size/overlap overrides with 422 before mutation because upstream derives their effective sizes separately; requests without chunk overrides retain that mode. WeKnora v0.8.3 cannot override a nonzero overlap with zero; this request is rejected instead of silently retaining the old value. Empty reprocess options preserve the stored upstream processing policy. The API does not claim independent execution or progress for each stage.
Platform connections/spaces, resource-sharing UI, a knowledge center, Trace persistence, Wiki page lifecycle editing and multi-hop graph algorithms remain separate work. Processing snapshots contain no raw Trace, source content or model prompts.
Provider integration and tests
The router only consumes contracts.ManagementProvider. An operator can set provider_factory to another trusted module:factory implementing the optional management contract. This executable setting is not API-writable. Providers must enforce their own resource grants and upstream authorization in every operation and explicitly reject unsupported operations. See CONTRACT.md for the handoff boundary.
Run offline tests from the repository root:
uv run --no-sync --project backend pytest backend/tests/test_knowledge_management_extension.py -q
uv run --no-sync --project backend pytest backend/tests/blocking_io/test_knowledge_management_extension.py -q
Tests use synthetic httpx.MockTransport responses. They cover ingestion → status → existing scope/retrieval → citation replay, denied identities/resources, upstream errors without credential fallback, request size limits, strict options, optional installation and a second test Provider. Blocking-I/O coverage exercises actual HTTPX/SSL initialization off-loop. These tests do not establish real upstream extraction/indexing quality or browser acceptance.
For live acceptance, use a dedicated test base and explicitly granted administrator: upload a unique test document, poll its document ID, confirm searchable state, select it in the existing chat scope selector, ask a question whose answer is unique to that source, and verify the citation after refreshing. Repeat with URL/manual ingestion and with Wiki/graph-enabled bases, checking upstream processing outcomes rather than inferring enrichment success from parse completion. Record upstream version and configuration; never commit credentials or document content. The offline suite never performs live writes. Real OrbStack/GLM-5.3/BGE acceptance, environment repairs and observed limitations are recorded in VALIDATION.md.
Opt-in live regression
Use a dedicated synthetic test base and an authenticated, explicitly granted administrator. Export these variables without committing their values:
DEER_FLOW_RUN_LIVE_TESTS=1andKNOWLEDGE_MANAGEMENT_ALLOW_WRITES=1KNOWLEDGE_GATEWAY_URL: browser/Gateway origin, including scheme and portKNOWLEDGE_SESSION_COOKIE: value of the authenticatedaccess_tokencookieKNOWLEDGE_CSRF_TOKEN: matching CSRF cookie/header valueKNOWLEDGE_CONNECTION_ID: configured management connectionWEKNORA_BASE_URL,WEKNORA_TENANT_ID,WEKNORA_KNOWLEDGE_BASE_IDWEKNORA_API_KEY: separately scoped retrieval credential for that test base
uv run --no-sync --project backend pytest backend/tests/test_knowledge_management_extension_live.py -m live -q -s --override-ini=addopts=
Each run creates one uniquely marked synthetic manual document, prints its native ID for reconciliation, polls for completion, and verifies document-scoped retrieval and serialized citation evidence. It neither retries writes nor deletes fixtures. If submission is uncertain, inspect the upstream catalog before rerunning. Without both opt-ins the test skips; missing credentials after opt-in fail explicitly. This API regression does not replace browser or upstream Wiki/graph inspection.