feat(documents): persist an auto summary for uploaded documents

When the auto-AI-read opt-in is on, a freshly uploaded document is summarised
once in the background and the encrypted summary is stored and shown on the
document detail view.

- A background step runs after upload alongside the existing content-index and
  thumbnail steps. It generates a short (3-4 sentence) plain-language summary
  only when the user's documentsAutoAiRead opt-in is on (that flag is also the
  standing AI-egress consent), skips a document that already has one, and
  no-ops when no vision provider is configured. It reuses the same provider
  resolution, egress-consent re-assertion, and AI-budget reserve/reconcile
  helpers as the on-demand route; a provider error refunds the reservation and
  never fails the upload.
- The summary is stored encrypted (summaryEncrypted Bytes) with a
  summaryGeneratedAt timestamp on inbound_documents, registered for key
  rotation, and returned decrypted in the document detail response.
- The on-demand transient summary route is unchanged.

Gated by the same consent, budget, and module checks.
This commit is contained in:
Marc-André Bombeck committed 2026-07-17 17:46:28 +02:00
1 parent 43eddb63b0
commit 5cff7473c9
19 files changed
+674 -22

No files matched your search

+12
View File
@@ -540,6 +540,18 @@ async function main() {
prisma.extractedFact,
),
);
// The short background document summary. Small `encrypt()`-string-as-UTF-8
// Bytes payload (never the multi-megabyte original blob — the select is
// scoped to id + this column), so the generic Bytes walk covers it. NULL on
// rows uploaded with auto-read OFF / no provider — `rotateBytesColumn` skips
// those.
results.push(
await rotateBytesColumn(
"InboundDocument",
"summaryEncrypted",
prisma.inboundDocument,
),
);
// ───── v1.27.22 document content-search index (Bytes text + re-tokenise) ─────
// The blind content index carries TWO coupled artefacts under the index key