ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
@@ -0,0 +1,233 @@
// What the extraction does with a page by its class (ocr-service OCR3.15): a drawing and an empty page
// are never asked about and are reported; a scan and a table are sent as image and text; a text page as
// text; the lines that repeat on most pages are not in the prompt; and a verified quote names the
// reader's block. The model is a recording gateway: what it was sent is what is asserted.
import type { ChatMessage, GenerateObjectOptions, ModelGateway } from "@conusai-kit/platform"
import { describe, expect, it } from "vitest"
import { LOW_TRUST_NOTE, modelLineExtractor, PAGE_TEXT_CLOSE, PAGE_TEXT_OPEN } from "../adapters/model-extractor.js"
import type { PageClass } from "../contract/index.js"
import { planPage, promptTextOf } from "../core/page-plan.js"
import type { ExtractionInput, PageImage, ReadDocument, ReadPage } from "../core/ports.js"
import { noCache } from "../core/stage-cache.js"
/** A row long enough that a page of it is not weak for want of text (MIN_PAGE_TEXT_CHARS). */
const SHEET_ROW = "Reference: QR-2026-0417 (rev 2)"
const png: PageImage = { bytes: new Uint8Array([0x89, 0x50, 0x4e, 0x47, 1, 2, 3]), mediaType: "image/png" }
function page(index: number, pageClass: PageClass, rows: readonly string[], over: Partial<ReadPage> = {}): ReadPage {
return {
index,
width: 1000,
height: 1400,
class: pageClass,
source: pageClass === "text" ? "text_layer" : "ocr",
text: rows.join("\n"),
lines: rows.map((text, order) => ({
text,
bbox: [100, 100 + order * 40, 600, 130 + order * 40] as const,
confidence: 0.97,
order,
repeated: false,
})),
blocks: rows.map((_, order) => ({
id: `p${index + 1}-b${order + 1}`,
bbox: [90, 90 + order * 40, 700, 140 + order * 40] as const,
order,
lines: [order],
})),
...over,
}
}
const documentOf = (...pages: ReadPage[]): ReadDocument => ({
engine: { name: "fixture", version: "1" },
page_count: pages.length,
pages,
})
interface Sent {
messages: ChatMessage[]
options: GenerateObjectOptions
}
/** A gateway that records every request and answers with the lines it is given for the page it sees. */
function recording(answer: (text: string) => Array<{ label: string; value: string; quote: string }>) {
const sent: Sent[] = []
const gateway: ModelGateway = {
async generateObject(_schema, messages, options) {
sent.push({ messages: [...messages], options: options ?? {} })
const user = messages.find((message) => message.role === "user")
const parts = Array.isArray(user?.content) ? user.content : []
const text = parts.map((part) => ("text" in part ? part.text : "")).join("\n")
const from = text.indexOf(PAGE_TEXT_OPEN) + PAGE_TEXT_OPEN.length + 1
const to = text.lastIndexOf(PAGE_TEXT_CLOSE) - 1
return { lines: answer(text.slice(from, to)) } as never
},
}
return { gateway, sent }
}
const partsOf = (sent: Sent): Array<{ type: string; text?: string }> => {
const user = sent.messages.find((message) => message.role === "user")
return (Array.isArray(user?.content) ? user.content : []) as Array<{ type: string; text?: string }>
}
const input = (document: ReadDocument, pageImage: ExtractionInput["pageImage"]): ExtractionInput => ({
document,
pageImage,
cache: noCache,
})
const reference = (text: string) =>
text
.split("\n")
.filter((row) => row.includes(":"))
.map((row) => ({ label: row.split(":")[0] as string, value: (row.split(":")[1] as string).trim(), quote: row }))
describe("the plan for a page", () => {
const by = (pageClass: PageClass, over: Partial<ReadPage> = {}) =>
planPage(page(0, pageClass, [SHEET_ROW], over), ["drawing", "empty"])
it("leaves out the classes it is told to, whatever else is true of the page", () => {
expect(by("drawing")).toEqual({ kind: "skip" })
expect(by("empty")).toEqual({ kind: "skip" })
expect(planPage(page(0, "scan", [SHEET_ROW]), ["scan"])).toEqual({ kind: "skip" })
})
it("shows a scan its image and says its text is a recognition; a table gets image and text", () => {
expect(by("scan")).toEqual({ kind: "read", image: true, lowTrustText: true })
expect(by("table")).toMatchObject({ kind: "read", image: true })
})
it("reads a text page as text, with the image only when the reader was unsure of it", () => {
expect(by("text")).toEqual({ kind: "read", image: false, lowTrustText: false })
const unsure = page(0, "text", [SHEET_ROW])
const weak = { ...unsure, lines: unsure.lines.map((line) => ({ ...line, confidence: 0.4 })) }
expect(planPage(weak, ["drawing", "empty"])).toMatchObject({ kind: "read", image: true })
})
it("reads a drawing when the deployment lets it, with its image", () => {
expect(planPage(page(0, "drawing", ["A-101"]), [])).toMatchObject({ kind: "read", image: true })
})
it("drops the lines flagged as repeating from the text the model is shown", () => {
const rows = ["ACME Specification", "Reference: QR-1", "Page footer"]
const shown = page(0, "text", rows)
const lines = shown.lines.map((line, at) => ({ ...line, repeated: at !== 1 }))
expect(promptTextOf({ ...shown, lines })).toBe("Reference: QR-1")
expect(promptTextOf({ ...shown, lines: [] })).toBe(rows.join("\n"))
})
})
describe("the extractor and the page classes", () => {
it("asks nothing about a drawing or an empty page and reports them as skipped, in page order", async () => {
const { gateway, sent } = recording(reference)
const document = documentOf(
page(0, "text", ["Reference: QR-1"]),
page(
1,
"drawing",
Array.from({ length: 400 }, (_, at) => `A-${at} 2400`),
),
page(2, "empty", []),
)
const result = await modelLineExtractor({ gateway, model: "m" }).extract(input(document, async () => png))
expect(sent).toHaveLength(1)
expect(result.lines.map((line) => [line.page, line.value])).toEqual([[1, "QR-1"]])
expect(result.skipped).toEqual([
{ page: 2, pageClass: "drawing" },
{ page: 3, pageClass: "empty" },
])
})
it("lets a deployment read drawings: the list of excluded classes is the option's", async () => {
const { gateway, sent } = recording(reference)
const document = documentOf(page(0, "drawing", ["Scale: 1:50"]))
const result = await modelLineExtractor({ gateway, model: "m", excludedPageClasses: [] }).extract(
input(document, async () => png),
)
expect(sent).toHaveLength(1)
expect(result.skipped).toEqual([])
})
it("sends a scan as text and image, the text marked low trust; a text page as text alone", async () => {
const { gateway, sent } = recording(reference)
const asked: number[] = []
const document = documentOf(page(0, "scan", ["Reference: QR-1"]), page(1, "text", [SHEET_ROW]))
await modelLineExtractor({ gateway, model: "m" }).extract(
input(document, async (index) => {
asked.push(index)
return png
}),
)
expect(asked).toEqual([0])
const kinds = sent.map((one) => partsOf(one).map((part) => part.type))
expect(kinds).toContainEqual(["text", "image_url"])
expect(kinds).toContainEqual(["text"])
const scanText = partsOf(sent.find((one) => partsOf(one).length === 2) as Sent)[0]?.text
expect(scanText).toContain(LOW_TRUST_NOTE)
const textText = partsOf(sent.find((one) => partsOf(one).length === 1) as Sent)[0]?.text
expect(textText).not.toContain(LOW_TRUST_NOTE)
})
it("leaves a header that repeats on most pages out of the prompt, and still verifies against the page", async () => {
const { gateway, sent } = recording(reference)
const rows = ["ACME: Specification", "Reference: QR-1"]
const withHeader = page(0, "text", rows)
const document = documentOf({
...withHeader,
lines: withHeader.lines.map((line, at) => ({ ...line, repeated: at === 0 })),
})
const result = await modelLineExtractor({ gateway, model: "m" }).extract(input(document, async () => undefined))
const text = partsOf(sent[0] as Sent)[0]?.text ?? ""
expect(text).toContain("Reference: QR-1")
expect(text).not.toContain("ACME")
expect(result.lines.map((line) => [line.label, line.verification])).toEqual([["Reference", "verified"]])
})
it("gives a verified line the block it sits in, and that block's box in place of the lines'", async () => {
const { gateway } = recording(reference)
const document = documentOf(page(0, "text", ["Reference: QR-1", "Issued: 2026-04-01"]))
const { lines } = await modelLineExtractor({ gateway, model: "m" }).extract(input(document, async () => undefined))
const second = lines.find((line) => line.label === "Issued")
expect(second?.blockId).toBe("p1-b2")
// block p1-b2 is [90, 130, 700, 180] on a 1000 x 1400 page, not the line's [100, 140, 600, 170]
expect(second?.box).toEqual({ x: 0.09, y: 130 / 1400, width: 0.61, height: 50 / 1400 })
})
it("marks a quote the model read off the image, and the text does not hold, as a scanned quote to check", async () => {
const { gateway } = recording(() => [{ label: "Qty", value: "12", quote: "Qty 12 pcs" }])
const document = documentOf(page(0, "scan", ["Delivery note"]), page(1, "text", ["Delivery note"]))
const { lines } = await modelLineExtractor({ gateway, model: "m" }).extract(
input(document, async (index) => (index === 0 ? png : undefined)),
)
const [scan, text] = lines
expect(scan).toMatchObject({ page: 1, verification: "needs_review", note: "scanned_quote", confidence: 0 })
// the same unfound quote on a page the model saw no image of carries no such note
expect(text).toMatchObject({ page: 2, verification: "needs_review" })
expect(text?.note).toBeUndefined()
})
it("keys the cache by the image: another image of the same page is asked again", async () => {
const stored = new Map<string, unknown>()
const cache = {
async get(stage: string, key: string) {
return stored.get(`${stage}/${key}`) as never
},
async put(stage: string, key: string, value: unknown) {
stored.set(`${stage}/${key}`, value)
},
}
const { gateway, sent } = recording(reference)
const document = documentOf(page(0, "scan", ["Reference: QR-1"]))
const extractor = modelLineExtractor({ gateway, model: "m" })
const run = (image: PageImage) => extractor.extract({ document, pageImage: async () => image, cache })
await run(png)
await run(png)
expect(sent).toHaveLength(1)
await run({ ...png, bytes: new Uint8Array([0x89, 0x50, 0x4e, 0x47, 9, 9, 9]) })
expect(sent).toHaveLength(2)
})
})