Legacy knowledge bases (`datasets`) and KnowledgeFS spaces
(`knowledge_fs_control_spaces`) live in different tables, so a dataset API
key binding could only ever name a legacy dataset and the KnowledgeFS service
API never consulted bindings at all: a key scoped to specific knowledge bases
still reached every KnowledgeFS space in the workspace.
- Bindings carry a `resource_type` (`dataset` | `knowledge_fs_space`) and a
nullable `control_space_id` (migration 9a4e7d1c2b60, existing rows stay
`dataset`), with a CHECK that exactly one id column matches the type.
- `dataset_api_key_service` exposes the key scope by kind, validates and
binds KnowledgeFS spaces (tenant-owned, not deleting/deleted), and cleans up
keys scoped only to a space when its deletion is requested. The orphan-key
cleanup is now NULL-safe so a space binding keeps a key alive.
- Legacy dataset routes only honour `dataset` bindings; the KnowledgeFS
authorization service only honours `knowledge_fs_space` bindings and raises
a scope error that the service API maps to 403 (unknown keys stay 401).
- Console key creation accepts `knowledge_space_ids`; list/create responses
return them next to `dataset_ids`.
- The web scope picker lists KnowledgeFS spaces alongside legacy datasets
when KnowledgeFS is enabled and submits the ids by kind; the scope column
counts both.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zw5G5SX3HmVfnZof6YWAc
The KnowledgeFS retrieval node treated the query text as mandatory even though
a query image is a complete input on its own. Text and image are now
alternatives: the node needs at least one of them, never both.
- web: drop the required mark from the query text field; the checklist fails
only when neither variable is bound (new i18n message in every locale).
- api: make `query_variable_selector` optional with a model-level rule that
one of the two selectors must be set, and only map the query variable when
it is bound. At runtime an empty text without images is a configuration
error; an image-only query retrieves with an empty text, merges spaces on
their own scores instead of a text reranker, skips automatic metadata
extraction, and skips the text-based failed-query capture.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zw5G5SX3HmVfnZof6YWAc
Bring the LLM-driven "automatic" metadata filtering mode from the legacy
Knowledge Retrieval node to the KnowledgeFS retrieval node
(knowledge-retrieval-v2).
Backend:
- Accept `automatic` in `metadata_filtering_mode` and add
`metadata_model_config` to the node entity.
- Add `automatic_metadata_filter.py`, which reuses the legacy metadata
filter prompt templates, invokes the configured model, parses the JSON
answer and normalises operators/values against the KnowledgeFS
metadata catalog before building custom metadata conditions.
- Resolve the shared metadata catalog across the selected spaces through
the new `list_metadata_fields` app execution capability (paginated),
fail closed on missing/unavailable models and catalog admission errors,
fail open on extraction errors, and expose the outcome via
`metrics.metadata_filtering` and `llm_usage`.
- Inject the default extractor from the node factory.
- Fix an unescaped brace in the legacy completion prompt template that
broke `.format()`.
Frontend:
- Offer disabled/automatic/manual modes, normalise unknown persisted
modes to disabled, store the automatic model and completion params,
render the model selector through the shared MetadataFilter, and
require a model when saving in automatic mode.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnUM1CdV5LpcxZEQ2vu9Xc