ingest() in lib/pipeline/ingest.ts accepts a PNG image as a Uint8Array and calls GPT-5.6 Sol via the OpenAI Responses API to extract structured remittance data. The image is base64-encoded and sent as an input_image content block with detail: "original". The model response is parsed and validated against IngestOutputSchema before the function returns. To avoid redundant API calls during development and testing, results are cached to disk by a SHA-256 content hash combined with the active schema version.
Function signature
imageBytes is the raw binary content of the PNG file — the same bytes produced by file.arrayBuffer() in the route handler. dependencies is an optional bag of injectable collaborators; when omitted, the function uses the real filesystem cache and the live OpenAI vision client.
Caching
Before making a model call,ingest() computes a cache key by SHA-256 hashing the image bytes and prepending the schema version constant "v2":
samples/parsed/. On a cache hit the file is parsed through IngestOutputSchema and returned immediately. On a cache miss the model is called, the result is written to disk, and the validated output is returned. The v2 prefix ensures that a schema change automatically invalidates all existing cache entries without touching the files.
Output schema
IngestOutput is z.array(ClaimSchema).min(1) — an array with at least one Claim object per claim shown on the remittance. Each Claim has three top-level fields:
remittance — header fields shared across every claim on the same remittance:
claim — claim-level identifiers:
lines — one entry per service line (ServiceLine[], minimum one):
Dependency injection
cache with an in-memory implementation lets tests skip filesystem I/O entirely. Replacing visionClient with a fixture that returns a pre-built response lets tests run without an OpenAI API key. Both substitutions are independent — you can inject only the one you need.
Prompt design
The ingest prompt atprompts/ingest.md gives the model a strictly bounded transcription role. The key constraints are:
- Transcribe only — copy values visible on the page; never infer, normalize, or complete a code from context or model knowledge.
- Exact code strings — CARC and RARC are strings, not numbers; preserve leading zeroes and alphanumeric codes exactly as printed.
- Verbatim legend definitions — transcribe every
code_legendentry verbatim from the printed legend; never use the model’s internal knowledge of what a code means. - Always emit
rarcandnotes— usenullwhen the page provides no value for either field. - ISO 8601 dates — convert page dates only when all date components are visible.
- Nonnegative amounts — no currency symbols or thousand separators.
The reasoning effort level is
"high", set in the responseWithImageSchema() call inside ingest.ts. This gives the model maximum deliberation time to transcribe complex multi-claim remittance layouts accurately.