Skip to content

feat(citations): carry structured citations from retrieval into the answer - #113

Merged
mrsibe merged 2 commits into
mainfrom
feat/structured-citations
Sep 25, 2026
Merged

mrsibe merged 2 commits into
mainfrom
feat/structured-citations

Conversation

@mrsibe

@mrsibe mrsibe commented Sep 25, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

Makes an answer's sources resolvable to a page and paragraph. Retrieval already knows the source's locator (page range, block ids, char span) after #74; this PR assembles it into a shared Citation, asks the model to mark sources with [n], and persists the result under chat_messages.metadata.citations — with no schema change.

Why?

The prompt numbered its sources ([来源 1: …]) but dropped chunkId, documentId, page and block. Once an answer existed there was no way back to the original document, which is the point of the v1.4 Trusted Research Loop epic (#82).

Related issue

Fixes #69

What changed?

  • src/shared/types/citation.ts — the Citation contract (document, page range, block id, char span, quote, score). A snapshot, not a reference, so re-indexing or deleting the source cannot erase where the answer was grounded.
  • src/main/services/citations.ts — pure citationFromSearchResult / buildCitations, plus the RAG context assembly moved out of chatHandlers so it is testable without Electron. The prompt now instructs the model to mark sources with [n].
  • src/main/ipc/chatHandlers.ts — persists citations alongside the existing sources, and sends the persisted metadata on the finish streaming event.
  • src/shared/utils/citations.ts — defensive parseCitations reader; drops malformed rows individually, keeps citations whose document was deleted.
  • src/shared/types/chat.ts — citations on ChatMessageMetadata.
  • Renderer: chatStore applies messageMetadata on finish so an answer is citable without a session reload.

How was this tested?

  • npm run typecheck — passes (node, web, test).
  • npm test — 138 tests pass, including 15 new cases in test/citations.test.ts (page/block/span assembly, multi-block spans, unpaginated sources, [n] prompt instruction, defensive parsing, deleted-document record).
  • Lint: no new warnings introduced in touched files.

Manual UI verification of the chat flow (two-page fixture) is pending; the streaming metadata handoff is covered structurally by the finish event change.

Screenshots / recordings

Not applicable (no visible UI change yet; citation chips land in #72).

Checklist

  • I have reviewed my own changes.
  • npm run typecheck passes.
  • npm run build passes.
  • I have tested the affected user workflow.
  • I have not included unrelated changes.
  • I have updated documentation when necessary.

Desktop / build changes

  • Not applicable

…nswer

The prompt numbered its sources but threw the provenance away: by the time an
answer existed there was no page, block or span to point back at. `SearchResult`
already carries a `locator` (#74), so assemble it into a shared `Citation`
snapshot — document, page range, block id, char span and the retrieved quote —
and persist it under `chat_messages.metadata.citations`.

The citation is a snapshot, not a reference: re-indexing or deleting the source
later must not erase where the answer was grounded. A defensive reader
(`parseCitations`) drops malformed rows individually so one bad entry cannot hide
the rest.

The renderer's in-memory message never sees the DB row written before streaming,
so the persisted metadata also rides along on the `finish` event; otherwise an
answer would stay un-citable until the session was reloaded.

Refs #69
@github-actions github-actions Bot added the enhancement New feature or request label Sep 25, 2026
Resolve the shared type barrel: keep both the citation (#69) and source reader
(#71) exports.
@mrsibe
mrsibe merged commit 650c986 into main Sep 25, 2026
3 checks passed
@mrsibe
mrsibe deleted the feat/structured-citations branch September 25, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] Structured citations through retrieval → prompt → chat_messages.metadata.citations[]

1 participant