mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
Run fact extraction in the background, re-checking under a row lock
The extract route accepts `Prefer: respond-async` for all three inputs (text, stored, vision) and queues a DOCUMENT_EXTRACT run (added to migration 0370). The run's result is the count of staged facts and the document status; the facts themselves are read from the document. Without the header the route answers with the document detail as before; both paths run one shared body. A background read can take minutes, and a review the person finishes in the meantime must not have its approved facts replaced. Staging now locks the document row and asks again whether it may stage, refusing with the route's own alreadyConfirmed / alreadyPartlyConfirmed codes. The synchronous path gets the same re-check. The vault's stored-text extract uses the background path and shows the shared waiting line in place of its hint while the read runs.
This commit is contained in:
1 parent
eda5776886
commit
bb58b40b2a
16 files changed
+1003
-352
No files matched your search
+66
-29
@@ -16701,13 +16701,32 @@ paths:
|
||||
structures the document's own stored extracted text (its content index) — the manual recovery for a
|
||||
skipped/failed automatic staging run, 422 `documents.inbound.notIndexed` when no stored text exists. Extraction
|
||||
reproduces what the document states — it never interprets. Nothing reaches the structured stores here; the
|
||||
confirm route is the only write path."
|
||||
confirm route is the only write path. With `Prefer: respond-async` (v1.40) the extraction runs in the
|
||||
background: after the same quick refusals the route answers 202 with a run id, a second request while an
|
||||
extraction of this document from the same input is queued or running answers with that run, and `GET
|
||||
/api/ai-runs/{id}` serves `DocumentExtractRunResult` (`{ documentId, factsStaged, status }`, not the document
|
||||
detail) as the run's `result`. Staging re-checks the document under a row lock, so a review finished while the
|
||||
read ran fails the run with `documents.inbound.alreadyPartlyConfirmed` (409) or
|
||||
`documents.inbound.alreadyConfirmed` (422) and leaves the review untouched; the synchronous form answers the
|
||||
same in that race."
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- &a37
|
||||
name: Prefer
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: respond-async
|
||||
description: "Send `respond-async` (RFC 7240) to run the read in the background: the route answers 202 with a run id
|
||||
once every quick refusal has passed, and `GET /api/ai-runs/{id}` serves the result. Without it the route
|
||||
answers synchronously with the result, as it always has; that form is retained for clients that have not
|
||||
adopted runs, and its retirement will be announced one minor release ahead. A preference the route cannot
|
||||
apply is ignored and the response then carries no `Preference-Applied`."
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
@@ -16721,8 +16740,9 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/InboundExtractEnvelope"
|
||||
"202": *a36
|
||||
"401": *a1
|
||||
"403": &a37
|
||||
"403": &a38
|
||||
description: "AI refusal: the `documentAi` capability is unavailable for this record. The envelope carries
|
||||
`meta.errorCode`, `meta.capability` and `meta.reason` (one of `AiUnavailableReason`).
|
||||
`assistant.disabled.documentAi` — the operator turned off reading documents, or every AI feature (the master
|
||||
@@ -16746,7 +16766,7 @@ paths:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
"503": &a38
|
||||
"503": &a39
|
||||
description: "`ai.unavailable` — the inputs behind the AI capability could not be read, and the answer failed closed.
|
||||
Nothing was sent. Retry later."
|
||||
content:
|
||||
@@ -16776,18 +16796,7 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- &a39
|
||||
name: Prefer
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: respond-async
|
||||
description: "Send `respond-async` (RFC 7240) to run the read in the background: the route answers 202 with a run id
|
||||
once every quick refusal has passed, and `GET /api/ai-runs/{id}` serves the result. Without it the route
|
||||
answers synchronously with the result, as it always has; that form is retained for clients that have not
|
||||
adopted runs, and its retirement will be announced one minor release ahead. A preference the route cannot
|
||||
apply is ignored and the response then carries no `Preference-Applied`."
|
||||
- *a37
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
@@ -16824,10 +16833,10 @@ paths:
|
||||
$ref: "#/components/schemas/DocumentSuggestEnvelope"
|
||||
"202": *a36
|
||||
"401": *a1
|
||||
"403": *a37
|
||||
"403": *a38
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
"503": *a38
|
||||
"503": *a39
|
||||
/api/documents/inbound/{id}/summary:
|
||||
post:
|
||||
tags:
|
||||
@@ -16877,7 +16886,7 @@ paths:
|
||||
type: boolean
|
||||
default: false
|
||||
description: With `persist=true`, explicitly replace an existing stored summary.
|
||||
- *a39
|
||||
- *a37
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
@@ -16914,10 +16923,10 @@ paths:
|
||||
$ref: "#/components/schemas/DocumentSummaryEnvelope"
|
||||
"202": *a36
|
||||
"401": *a1
|
||||
"403": *a37
|
||||
"403": *a38
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
"503": *a38
|
||||
"503": *a39
|
||||
/api/documents/inbound/{id}/index:
|
||||
post:
|
||||
tags:
|
||||
@@ -16943,7 +16952,7 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- *a39
|
||||
- *a37
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
@@ -16980,10 +16989,10 @@ paths:
|
||||
$ref: "#/components/schemas/DocumentIndexEnvelope"
|
||||
"202": *a36
|
||||
"401": *a1
|
||||
"403": *a37
|
||||
"403": *a38
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
"503": *a38
|
||||
"503": *a39
|
||||
/api/documents/inbound/{id}/chat:
|
||||
post:
|
||||
tags:
|
||||
@@ -17023,10 +17032,10 @@ paths:
|
||||
type: string
|
||||
description: "SSE frames: `data: <json>\\n\\n`. See the operation description for the per-`type` frame shapes."
|
||||
"401": *a1
|
||||
"403": *a37
|
||||
"403": *a38
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
"503": *a38
|
||||
"503": *a39
|
||||
get:
|
||||
tags:
|
||||
- Documents
|
||||
@@ -17089,10 +17098,10 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DocumentReindexEnvelope"
|
||||
"401": *a1
|
||||
"403": *a37
|
||||
"403": *a38
|
||||
"422": *a4
|
||||
"429": *a5
|
||||
"503": *a38
|
||||
"503": *a39
|
||||
/api/documents/inbound/{id}/facts/{factId}:
|
||||
patch:
|
||||
tags:
|
||||
@@ -63669,6 +63678,7 @@ components:
|
||||
- LABS_OCR_EXTRACT
|
||||
- DOCUMENT_SUMMARY
|
||||
- DOCUMENT_SUGGEST
|
||||
- DOCUMENT_EXTRACT
|
||||
documentId:
|
||||
anyOf:
|
||||
- type: string
|
||||
@@ -63688,11 +63698,13 @@ components:
|
||||
- $ref: "#/components/schemas/OcrExtractResponse"
|
||||
- $ref: "#/components/schemas/DocumentSummaryResponse"
|
||||
- $ref: "#/components/schemas/DocumentSuggestResponse"
|
||||
- $ref: "#/components/schemas/DocumentExtractRunResult"
|
||||
- type: "null"
|
||||
description: "Present once `status` is `SUCCEEDED`: the body the queuing route answers with synchronously.
|
||||
`DOCUMENT_INDEX` carries `DocumentIndexResponse`, `LABS_OCR_EXTRACT` carries `OcrExtractResponse`,
|
||||
`DOCUMENT_SUMMARY` carries `DocumentSummaryResponse`, `DOCUMENT_SUGGEST` carries `DocumentSuggestResponse`.
|
||||
Decode by `kind`; treat an unknown `kind` as a run this client did not start."
|
||||
`DOCUMENT_SUMMARY` carries `DocumentSummaryResponse`, `DOCUMENT_SUGGEST` carries `DocumentSuggestResponse`,
|
||||
`DOCUMENT_EXTRACT` carries `DocumentExtractRunResult` (not the extract route's detail body). Decode by
|
||||
`kind`; treat an unknown `kind` as a run this client did not start."
|
||||
error:
|
||||
anyOf:
|
||||
- type: object
|
||||
@@ -63951,6 +63963,31 @@ components:
|
||||
required:
|
||||
- suggestions
|
||||
additionalProperties: false
|
||||
DocumentExtractRunResult:
|
||||
type: object
|
||||
properties:
|
||||
documentId:
|
||||
type: string
|
||||
factsStaged:
|
||||
type: integer
|
||||
minimum: -9007199254740991
|
||||
maximum: 9007199254740991
|
||||
description: How many facts were staged PENDING for review. Read them from `GET /api/documents/inbound/{id}`; the
|
||||
confirm route stays the only write into the record.
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- STORED
|
||||
- EXTRACTING
|
||||
- EXTRACTED
|
||||
- FAILED
|
||||
- CONFIRMED
|
||||
- DISCARDED
|
||||
required:
|
||||
- documentId
|
||||
- factsStaged
|
||||
- status
|
||||
additionalProperties: false
|
||||
ListBiomarkersEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in new issue
Block a user