fix(kb): keep the filename and mark a source whose document was deleted - #784
Merged
Conversation
A KB source's display name was resolved live from SmartDocument, so deleting the document from Files left the row showing a bare UUID with no explanation — while the KB kept answering from the chunks it had already indexed. The user saw a confident answer citing a file that no longer exists, and a source list that named nothing. Capture the document's title on the source at ingest, so the name survives the document, and report document_exists on the source response. The KB panel then shows "Award Letter.pdf · source deleted" with a tooltip saying answers still come from the indexed text — the treatment extraction test cases already get. The inspector says the same and drops its File tab, which could only ever render a 404 for a deleted document. Adds scripts/backfill_kb_source_titles.py for rows ingested before this: it fills the title from the live document where there still is one, and from the source_name on the source's own chunks where there isn't — the chunks outlive the document and carry the name it had when indexed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017f65pmoEKbpBGownseoHf7
# Conflicts: # frontend/src/components/workspace/KnowledgePanel.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket
Deleting a document from Files leaves its knowledge base source in place: the KB stays Ready, still answers from the deleted document's content, and cites it by name and page. The only sign anything changed is that the source is now listed as
12423A16EF3F476BA92B57F5301C2EFFinstead of its filename. Extraction test cases already handle this — filename kept, "source deleted" label, an explanation that validation still runs from the saved text.Cause
KnowledgeBaseSourcestores no name for a document source;resolve_document_titleslooks the title up live fromSmartDocumenton every request. Delete the document and the lookup returns nothing, soKnowledgePanelfalls through tosource.document_title || source.document_uuid— the UUID. Nothing anywhere reported that the document was gone.The content survives because it lives in ChromaDB, written at ingest and never keyed to the document's continued existence.
Scope decision
Following the ticket's Expected — parity with extraction — the chunks stay and the KB keeps answering. That is the same contract extraction states ("validation still works from the saved text"), and silently emptying knowledge bases when a file is tidied out of Files would be a much larger behavioural change than this report asks for. What changes is that the KB now says the document is gone. Happy to take the other direction (purge chunks on delete) as a follow-up if that's what you want — it's a different conversation about data lifecycle, not a bug fix.
Change
The name survives the document —
KnowledgeBaseSource.document_title, written by both ingest paths (kb_ingest_document,kb_reingest) alongside chunk count and status.resolve_document_titlesprefers the live title and falls back to it.The deleted state is reported — new
resolve_existing_documentsanddocument_existsonKBSourceResponse(KB detail, source detail, source PATCH). A lookup failure returns "exists" for everything rather than painting every source with a red label.The UI says so —
KnowledgePanelrendersAward Letter.pdf · source deletedin amber, tooltip: "The source document was deleted from Files. This knowledge base still answers from the text it indexed."KBSourceInspectorModalsays the same in its subtitle and drops the Text/File toggle, whose File view could only ever render a 404 for a document that no longer exists.Two paths already behaved correctly and are untouched: chat citations keep the filename (it comes from chunk metadata, written at ingest), and
resolve_openable_documentsalready withholds the "open the document" affordance when the document is gone, so no citation offers a dead click.Existing data —
backend/scripts/backfill_kb_source_titles.py(--dry-runsupported). Fillsdocument_titlefrom the live document where there still is one; where there isn't — the rows already broken on prod, including the one in this report — it recovers the name fromsource_nameon the source's own chunks in ChromaDB, which outlive the document. Idempotent, read-only against Chroma, and it handles the implicit-KB chunk-id convention (document_uuidvs sourceuuid).Tests
backend/tests/test_kb_deleted_document_source.py(new, 7) — title falls back to the stored one, live title wins, nothing invented when neither exists; existence lookup reports only surviving documents and fails safe; the detail endpoint reportsdocument_existsboth ways while keeping the filename.frontend/.../KnowledgePanel.test.tsx— the deleted source renders its filename and "source deleted" and never the UUID; a live source says nothing about deletion.test_knowledge_routes.py's two source mocks gaineddocument_title— without it a MagicMock attribute reached astrfield on the response model.Backend: 7 new +
test_knowledge_routes.py(92) + fourtest_kb_source_*suites andtest_kb_retrieval.py(113) pass; ruff clean. Frontend:KnowledgePanel.test.tsx(12) passes;tsc --noEmitclean.🤖 Generated with Claude Code
https://claude.ai/code/session_017f65pmoEKbpBGownseoHf7