This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// The OCR port over the platform's document reader: the one file of this module that knows the SDK's
|
||||
// error type.
|
||||
//
|
||||
// `DocumentReader` (kit-platform) is `@conusai-sdk/ocr` wired to the deployment contract: `submit`
|
||||
// returns when the platform has accepted the document and `wait` polls the job to its document. What
|
||||
// this adapter adds is the one distinction core needs and the SDK only encodes in a string: "the
|
||||
// platform says this job failed" (permanent, the document is marked failed) against everything else
|
||||
// (a network error, a timeout, a 5xx: the job throws and the queue retries it). A page image the platform
|
||||
// has none of is not a failure either: the page is read from its text.
|
||||
|
||||
import type { DocumentReader } from "@conusai-kit/platform"
|
||||
import { OcrError } from "@conusai-sdk/ocr"
|
||||
import { type DocumentOcr, OcrJobFailed } from "../core/ports.js"
|
||||
|
||||
export interface SdkOcrOptions {
|
||||
/** Poll interval of `wait`, in milliseconds. */
|
||||
readonly intervalMs?: number
|
||||
/** Give up on one job after this long; the document then retries from its stored job id. */
|
||||
readonly timeoutMs?: number
|
||||
}
|
||||
|
||||
export function sdkDocumentOcr(
|
||||
reader: Pick<DocumentReader, "submit" | "wait" | "page">,
|
||||
options: SdkOcrOptions = {},
|
||||
): DocumentOcr {
|
||||
return {
|
||||
async submit(bytes, { filename, idempotencyKey }) {
|
||||
const job = await reader.submit(bytes, { filename, idempotencyKey })
|
||||
return { jobId: job.id }
|
||||
},
|
||||
async wait(jobId, { signal } = {}) {
|
||||
try {
|
||||
return await reader.wait(jobId, {
|
||||
...(options.intervalMs === undefined ? {} : { intervalMs: options.intervalMs }),
|
||||
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
||||
...(signal ? { signal } : {}),
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof OcrError && error.type === "job-failed") throw new OcrJobFailed()
|
||||
throw error
|
||||
}
|
||||
},
|
||||
async page(jobId, page, { signal } = {}) {
|
||||
try {
|
||||
const image = await reader.page(jobId, page, signal ? { signal } : {})
|
||||
return { bytes: image.bytes, mediaType: image.contentType }
|
||||
} catch (error) {
|
||||
// The platform keeps a job's input for a day and draws a page on demand: a job past that, or a
|
||||
// page it has none of, is a page read from its text. Anything else is transient and thrown.
|
||||
if (error instanceof OcrError && (error.status === 404 || error.status === 422)) return undefined
|
||||
throw error
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user