cadrumo.application.ledger.invoice_draft_extraction module¶
On-host routing of one stored evidence reference to a typed invoice draft.
Holds the reading layer that decides which reader a given document is handed
to, and the three readers’ wiring. It never persists an
Invoice and never guesses a value no reader could
ground in the document: every field a reader could not recover is left None
rather than fabricated (no-silent-under-declaration in spirit: an unconfident
field is absent, not invented).
Three reading paths, chosen on the document’s own shape rather than on its stored MIME type:
A structured e-invoice (Facturae, CII, UBL) is read exactly through the application-owned
StructuredInvoiceReader. It reaches no model at all, so prompt injection is categorically impossible for that document rather than merely mitigated, and it is the only path that can recover the document’s own line decomposition and per-rate breakdown.A text-native PDF is transcribed by
transcribe_text_layer()and read first by the fixed label rules ofread_invoice_fields_by_labels(). When those rules read every required field no model runs; otherwise the fields they could not read come fromextract_invoice_fields_from_text(), and an unreachable model leaves the rule reading standing. Either way the draft is grounded against that same transcription byground_draft_against_transcription(). The transcription is produced by a DIFFERENT reader than the one that proposes values, which is what makes the anchor check an external check rather than a model confirming itself.A scan-only PDF or image has nothing to transcribe, so it falls back to the on-host LOCAL vision reader (
evidence_draft_vision) – the same rasterise-then-read-with-Ollama transportLocalVisionLLMClassifieralready uses for classification, gated byLLM_VISIONand never a cloud call.
The escalation is one-directional and the asymmetry is deliberate. A document
with no text layer genuinely needs a reader that works on pixels, so it escalates.
A readable document whose semantic reader is absent or unreachable is an
ENVIRONMENT failure, so it REFUSES in the operator’s face rather than silently
running a heavier engine they did not ask for – see
_refuse_a_text_read_with_no_reader(). A profile that has opted out of vision
reading likewise gets a typed, instructive refusal, never a silent empty draft.
Everything here runs on-host and in-memory only. The evidence bytes, the transcription and the draft never touch disk and are never sent to a cloud provider. This module performs no filesystem write.
extract_invoice_draft_from_evidence() is the CLI-facing
wiring layer: it resolves an already-stored purchase_invoice_evidence record
or a linked attachment_id to its in-memory bytes (through the private
evidence-input resolvers
resolve_purchase_invoice_evidence_input()
and
resolve_attachment_evidence_input()) and
routes them to one of the three readers above, so
aeat app ledger evidence extract needs only a bucket id plus one of the two
reference ids.
Confirming a read draft into a catalogue record is a separate responsibility and
lives in invoice_confirmation; the draft’s own
direction-resolved projections live in
evidence_draft.
See also
InvoiceDraftPublic draft record returned before an invoice is persisted.
transcribe_text_layer()Acquisition-stage primitive that turns a text-native PDF into the reading-order transcription the semantic reader is handed.
confirm_invoice_draft_from_evidence()Non-interactive confirm step that re-extracts, applies overrides, and delegates the catalogue write.
evidence_draft_visionOn-host vision fallback for scan-only PDFs and image attachments.
- extract_invoice_draft_from_evidence(*, bucket_id, evidence_id=None, attachment_id=None, settings=None, off_host_provider=None, consent_token=None, ports, operation, legends)[source]¶
Resolve one stored evidence reference to bytes and extract its
InvoiceDraft.The CLI-facing wiring layer over the three readers: given either a
purchase_invoice_evidenceid (looked up throughPurchaseInvoiceEvidenceService) or a linkedattachment_id, reads the evidence’s bytes from secure storage into memory (the private evidence-input resolversresolve_purchase_invoice_evidence_input()andresolve_attachment_evidence_input()) and routes them to the reader the document’s shape selects. Exactly one of evidence_id / attachment_id must be supplied.Nothing is written to disk and nothing leaves the host: the resolved bytes and the extracted text stay in process memory for the duration of this call (
sensitive-financial-data-secure-storage-only).- Parameters:
bucket_id (
str) – Active ledger bucket the evidence or attachment belongs to.evidence_id (
str|None) – Apurchase_invoice_evidencerecord id, orNone.attachment_id (
str|None) – A linked attachment id, orNone.settings (
Settings|None) – ResolvedSettings. WhenNone,load_settings()is used so test overrides viaoverride_settings()are honoured.off_host_provider (
LLMProvider|None) – Provider to route the MODEL-BEARING stages at, orNonefor the on-host default. Widening this signature does not widen the boundary: the refusal lives below this function, at the client’s single dispatch point, so a caller naming a cloud provider without a token is refused there rather than trusted here.consent_token (
EvidenceConsentProof|None) – Per-invocation off-host consent proof, minted throughmint_evidence_consent_token().Noneis correct for every on-host read.operation (
PinnedAuthorityOperation) – Caller-owned pinned authority operation used to resolve the invoice extraction authority values. The lease must remain open for the complete extraction operation.legends (
tuple[RegimeLegend,...]) – The dated regime declarations resolved fromoperationby the enclosing composition boundary. The same tuple is used by the structured and grounded deterministic checks.ports (
InvoiceDraftExtractionPorts) – Application-owned evidence and reader capabilities supplied by the enclosing composition root.
- Returns:
The best-effort extracted fields, for operator review. Never itself persisted as an
Invoice.- Return type:
- Raises:
PurchaseInvoiceEvidenceInputError – When neither or both of evidence_id / attachment_id are supplied, when evidence_id resolves outside the bytes-bearing evidence-record id space (a catalogue-invoice id carries fiscal totals, not a document), when the resolved evidence’s media type is unsupported, or when a scan-only PDF / image falls back to the on-host vision reader and that reader is disabled for the profile or the local Ollama runtime is unreachable.