cadrumo.adapters.outbound.llm.evidence_draft_vision module¶
On-host VISION TRANSCRIPTION of a scan-only PDF or image: stage one, and only stage one.
This module reads pixels into text. It does not read pixels into fields, and the difference is the whole reason it was refitted.
What it used to do, and why that could not stand. One call handed rasterised
pages to a vision model and took back typed invoice fields with their anchors.
Two questions were answered together – what does this page say and what does
that mean – so a wrong result could not be attributed to either: a
transcription error and a reasoning error arrived in the same shape. Worse, the
anchors came back from the same call that produced the values, so there was
nothing independent to check them against. The provenance record had to say so
(anchor_self_reported), and a
self-reported anchor can never read as verified, because a fabricating model is
self-consistent too. The vision lane was therefore structurally incapable of
earning the grounding the text lane earned for free.
What it does now. It emits a
DocumentTranscription – reading-order text with
printed forms preserved verbatim – and stops. The semantic stage
(extract_invoice_fields_from_text()) then reads that text in a
SEPARATE call, and the anchor check runs against the transcription this module
produced. Two readers, two calls, one external check: exactly the shape the text
lane already had, now reachable for a document that has no text layer at all.
Faithfulness is the only job, and an explicit unknown is a legitimate result. A vision model reading a creased receipt genuinely cannot always resolve a glyph. The prompt therefore asks it to transcribe what it can see and to mark what it cannot, rather than to produce a clean-looking page by filling the gap. An illegible character reported as illegible costs one field; an invented one that happens to parse costs a filing, silently, because a plausible figure passes every downstream check that is not the anchor check – and it would pass that one too, since the fabrication would be in the transcription the check runs against.
On-host by default, and the default is stated rather than inherited. The shipped provider default is a cloud vendor, so a reader that took whatever the environment resolved would send a taxpayer’s document off-host by configuration alone. Naming another provider requires naming its model too, which makes an off-host read a deliberate act at the call site. That is a floor, not the gate: the gate is the client’s per-invocation evidence-consent check, and every request built here is marked evidence-derived unless the caller names the public, synthetic measurement corpus.
See also
DocumentTranscriptionThe typed stage-one artefact this module produces.
transcribe_text_layer()The deterministic sibling for a document that HAS a text layer.
extract_invoice_fields_from_text()The stage-two semantic reader that consumes what this produces.
- VISION_TRANSCRIPTION_PROMPT_VERSION: Final[int]¶
Version of the transcription prompt.
Part of the transcriber’s recorded revision because two prompts can produce different readings of the same pixels.
- VISION_TRANSCRIPTION_PROMPT: Final[str]¶
read the page, do not interpret it.
Every rule here exists to protect a downstream check. Printed forms survive verbatim because the anchor check searches this text for the form a value claims to have been read from, so normalising a separator here deletes the evidence that check runs against. Headings and labels survive because they are what assigns an identifier to a party, and an identity with no role evidence does not resolve. The illegible marker exists because an invented character is indistinguishable from a read one once it is in the transcription – it would anchor perfectly against itself.
The prompt names no invoice field, and that absence is load-bearing rather than incidental: a model told what to look for finds it, and this stage must not decide what the document is before the stage that decides has run.
- Type:
The transcription instruction
- vision_transcription_prompt_registry()[source]¶
Return the registry holding the transcription prompt.
Registered rather than reachable only as a module constant, so the id and version travel with the artefact the way the extraction template’s do.
- Returns:
A registry carrying the transcription prompt at
VISION_TRANSCRIPTION_PROMPT_VERSION.- Return type:
- class LocalVisionDocumentTranscriber(*, model=None, provider=LLMProvider.LOCAL, client=None, settings=None, consent_token=None, public_corpus=False)[source]¶
Bases:
objectTranscribe rendered document pages on-host with a local vision model.
Stage one of the reading pipeline for evidence that carries no text layer. It produces text and nothing else; every judgement about what that text MEANS belongs to the semantic stage, which runs separately over what this returns.
- Parameters:
model (
str|None) – Vision model identifier;Noneuses the configured LOCAL vision model. Required whenprovideris not LOCAL, because the only default that exists names a local model no vendor serves.provider (
LLMProvider) – Which provider carries the read. Defaults to LOCAL so a taxpayer’s document does not leave the host by default.client (
LLMClient|None) – InjectedLLMClient(dependency injection for tests); default-constructed against the resolved settings otherwise.settings (
Settings|None) – Injected settings; defaults toload_settings().consent_token (
EvidenceConsentToken|None) – Per-invocation off-host consent proof, minted throughmint_evidence_consent_token(). Required only for an off-host read of real evidence;Noneis correct for every on-host read.public_corpus (
bool) – Whether the pages handed to this reader come from the public, synthetic measurement corpus rather than from a taxpayer’s document. Defaults toFalse– the fail-closed direction, so a caller that says nothing gets the gate.
- property transcriber_identity: TranscriberIdentity¶
Return the provenance stamp for this reader.
The revision folds the prompt version because the prompt is half of what produced the text: the same model under different instructions returns a different reading of the same pixels. A revision naming only the model would erase which instructions produced the recorded text.
The TRANSPORT is recorded for a different reason, and it is not tidiness. A transcription is a durable artefact derived from the document, so if it was produced off-host it is one of the artefacts a consent withdrawal must enumerate – and a model identifier reveals the vendor only to a reader who already knows the catalogue. Recorded this way, the artefact that most needs re-deriving is not the one the survey cannot see.
It rides its own field rather than the name, which is where it began.
nameis contracted to say which reader produced the text and explicitly not to carry a coarse label, so folding the transport through it broke that contract and made a third provenance grammar – one no parser knew, on top of the two the stamp constructor had just collapsed into one. The reason for recording it was right; only the place was wrong.
- transcribe(*, evidence_images, source_content_sha256)[source]¶
Transcribe
evidence_imagesinto the acquisition-stage record.- Parameters:
evidence_images (
tuple[MultimodalImageInput,...]) – In-memory page/image renders of the evidence, each carrying its declared media type (built by the caller fromrasterise_pdf_pages_to_base64_png()for a scan-only PDF, or from the raw bytes of an image attachment).source_content_sha256 (
str) – Content address of the SOURCE bytes. Supplied by the caller rather than computed here, because the address must identify the document – the same document rasterised at a different resolution is one document, and hashing the renders would make it two.
- Returns:
The reading-order text with printed forms intact.
- Return type:
- Raises:
PurchaseInvoiceEvidenceInputError – When no pages were supplied, or the model returned no text. An empty transcription is a failed read, never an empty success: passed on it would tell the semantic stage the document is blank, which is a fact about the reader being reported as a fact about the document.
- transcribe_document_images(evidence_images, *, source_content_sha256, model=None, provider=LLMProvider.LOCAL, settings=None)[source]¶
Convenience wrapper: build a
LocalVisionDocumentTranscriberand transcribe.- Parameters:
evidence_images (
tuple[MultimodalImageInput,...]) – In-memory page/image renders of the evidence.source_content_sha256 (
str) – Content address of the source bytes.provider (
LLMProvider) – Transport serving the read. Defaults toLOCAL, so the production route stays on-host; naming another provider is the caller’s explicit, per-invocation decision to read off-host.settings (
Settings|None) – Optional resolved settings override.
- Returns:
The transcription.
- Return type: