From 3c3a658583220f04f4e810073e9590f4c7729eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Wed, 14 Jan 2026 12:32:35 +0100 Subject: [PATCH 1/5] initial idea for utilities in client --- docs/code_samples/default_v2.txt | 2 +- src/http/apiCore.ts | 2 +- src/index.ts | 2 +- src/input/localInputSource.ts | 2 +- src/v2/cli.ts | 69 ++++++-- src/v2/client.ts | 160 ++++++++++++++---- src/v2/client/baseParameters.ts | 106 ++++++++++++ src/v2/client/index.ts | 1 + src/v2/client/inferenceParameters.ts | 77 +-------- src/v2/client/utilityParameters.ts | 24 +++ src/v2/http/mindeeApiV2.ts | 116 +++++++++++-- src/v2/index.ts | 2 +- src/v2/parsing/index.ts | 26 +-- src/v2/parsing/inference.ts | 46 ----- src/v2/parsing/inference/baseInference.ts | 24 +++ .../inference/baseInferenceResponse.ts | 18 ++ .../classification/classifyResponse.ts | 9 + .../parsing/inference/crop/cropInference.ts | 25 +++ src/v2/parsing/inference/crop/cropItem.ts | 16 ++ src/v2/parsing/inference/crop/cropResponse.ts | 15 ++ src/v2/parsing/inference/crop/cropResult.ts | 17 ++ .../extraction}/dataSchemaActiveOption.ts | 0 .../extraction/extractionActiveOptions.ts} | 2 +- .../extraction/extractionInference.ts | 32 ++++ .../extraction/extractionResponse.ts | 15 ++ .../extraction/extractionResult.ts} | 8 +- .../{ => inference}/field/baseField.ts | 0 .../{ => inference}/field/fieldConfidence.ts | 0 .../{ => inference}/field/fieldFactory.ts | 0 .../{ => inference}/field/fieldLocation.ts | 0 src/v2/parsing/{ => inference}/field/index.ts | 2 + .../{ => inference}/field/inferenceFields.ts | 0 .../{ => inference}/field/listField.ts | 0 .../{ => inference}/field/objectField.ts | 0 .../{ => inference/field}/ragMetadata.ts | 0 .../parsing/{ => inference/field}/rawText.ts | 0 .../{ => inference/field}/rawTextPage.ts | 0 .../{ => inference}/field/simpleField.ts | 0 src/v2/parsing/inference/index.ts | 30 ++++ .../parsing/{ => inference}/inferenceFile.ts | 0 .../parsing/{ => inference}/inferenceModel.ts | 0 src/v2/parsing/inference/ocr/ocrResponse.ts | 9 + .../parsing/inference/split/splitResponse.ts | 9 + src/v2/parsing/inferenceResponse.ts | 15 -- src/v2/parsing/localResponse.ts | 2 +- tests/v2/client.integration.ts | 41 ++--- tests/v2/client.spec.ts | 85 +++++----- tests/v2/parsing/inference.spec.ts | 18 +- tests/v2/parsing/job.spec.ts | 6 +- tests/v2/parsing/localResponse.spec.ts | 24 ++- 50 files changed, 764 insertions(+), 293 deletions(-) create mode 100644 src/v2/client/baseParameters.ts create mode 100644 src/v2/client/utilityParameters.ts delete mode 100644 src/v2/parsing/inference.ts create mode 100644 src/v2/parsing/inference/baseInference.ts create mode 100644 src/v2/parsing/inference/baseInferenceResponse.ts create mode 100644 src/v2/parsing/inference/classification/classifyResponse.ts create mode 100644 src/v2/parsing/inference/crop/cropInference.ts create mode 100644 src/v2/parsing/inference/crop/cropItem.ts create mode 100644 src/v2/parsing/inference/crop/cropResponse.ts create mode 100644 src/v2/parsing/inference/crop/cropResult.ts rename src/v2/parsing/{ => inference/extraction}/dataSchemaActiveOption.ts (100%) rename src/v2/parsing/{inferenceActiveOptions.ts => inference/extraction/extractionActiveOptions.ts} (97%) create mode 100644 src/v2/parsing/inference/extraction/extractionInference.ts create mode 100644 src/v2/parsing/inference/extraction/extractionResponse.ts rename src/v2/parsing/{inferenceResult.ts => inference/extraction/extractionResult.ts} (76%) rename src/v2/parsing/{ => inference}/field/baseField.ts (100%) rename src/v2/parsing/{ => inference}/field/fieldConfidence.ts (100%) rename src/v2/parsing/{ => inference}/field/fieldFactory.ts (100%) rename src/v2/parsing/{ => inference}/field/fieldLocation.ts (100%) rename src/v2/parsing/{ => inference}/field/index.ts (77%) rename src/v2/parsing/{ => inference}/field/inferenceFields.ts (100%) rename src/v2/parsing/{ => inference}/field/listField.ts (100%) rename src/v2/parsing/{ => inference}/field/objectField.ts (100%) rename src/v2/parsing/{ => inference/field}/ragMetadata.ts (100%) rename src/v2/parsing/{ => inference/field}/rawText.ts (100%) rename src/v2/parsing/{ => inference/field}/rawTextPage.ts (100%) rename src/v2/parsing/{ => inference}/field/simpleField.ts (100%) create mode 100644 src/v2/parsing/inference/index.ts rename src/v2/parsing/{ => inference}/inferenceFile.ts (100%) rename src/v2/parsing/{ => inference}/inferenceModel.ts (100%) create mode 100644 src/v2/parsing/inference/ocr/ocrResponse.ts create mode 100644 src/v2/parsing/inference/split/splitResponse.ts delete mode 100644 src/v2/parsing/inferenceResponse.ts diff --git a/docs/code_samples/default_v2.txt b/docs/code_samples/default_v2.txt index bf95a030..8560c88c 100644 --- a/docs/code_samples/default_v2.txt +++ b/docs/code_samples/default_v2.txt @@ -32,7 +32,7 @@ const inferenceParams = { const inputSource = new mindee.PathInput({ inputPath: filePath }); // Send for processing -const response = mindeeClient.enqueueAndGetInference( +const response = mindeeClient.enqueueAndGetExtraction( inputSource, inferenceParams ); diff --git a/src/http/apiCore.ts b/src/http/apiCore.ts index 995e1f1d..98819997 100644 --- a/src/http/apiCore.ts +++ b/src/http/apiCore.ts @@ -62,7 +62,7 @@ export async function sendRequestAndReadResponse( } try { const parsedResponse = JSON.parse(responseBody); - logger.debug("JSON parsed successfully, returning object."); + logger.debug("JSON parsed successfully, returning plain object."); return { messageObj: response, data: parsedResponse, diff --git a/src/index.ts b/src/index.ts index 06aef230..3c8958af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ export * as v2 from "./v2/index.js"; export { Client, InferenceFile, - InferenceResponse, + ExtractionResponse, JobResponse, RawText, RagMetadata, diff --git a/src/input/localInputSource.ts b/src/input/localInputSource.ts index ec27955a..cf18216a 100644 --- a/src/input/localInputSource.ts +++ b/src/input/localInputSource.ts @@ -54,7 +54,7 @@ export abstract class LocalInputSource extends InputSource { ); } this.inputType = inputType; - logger.debug(`Loading file from: ${inputType}`); + logger.debug(`New local input source of type: ${inputType}`); } protected async checkMimetype(): Promise { diff --git a/src/v2/cli.ts b/src/v2/cli.ts index bedb1413..c9a6eb4c 100644 --- a/src/v2/cli.ts +++ b/src/v2/cli.ts @@ -2,7 +2,7 @@ import { Command, OptionValues } from "commander"; import { Client } from "./client.js"; import { PathInput } from "../input/index.js"; import * as console from "console"; -import { Inference } from "@/v2/parsing/index.js"; +import { BaseInference } from "@/v2/parsing/inference/index.js"; const program = new Command(); @@ -18,13 +18,33 @@ function initClient(options: OptionValues): Client { }); } -async function callEnqueueAndGetInference( +async function enqueueAndGetInference( inputPath: string, - options: any + options: OptionValues ): Promise { const mindeeClient = initClient(options); const inputSource = new PathInput({ inputPath: inputPath }); - const response = await mindeeClient.enqueueAndGetInference(inputSource, { + const response = await mindeeClient.enqueueAndGetExtraction(inputSource, { + modelId: options.model, + pollingOptions: { + initialDelaySec: 2, + delaySec: 1.5, + maxRetries: 80, + } + }); + if (!response.inference) { + throw Error("Inference could not be retrieved"); + } + printResponse(response.inference); +} + +async function enqueueAndGetUtility( + inputPath: string, + options: OptionValues +): Promise { + const mindeeClient = initClient(options); + const inputSource = new PathInput({ inputPath: inputPath }); + const response = await mindeeClient.enqueueAndGetUtility(inputSource, { modelId: options.model, pollingOptions: { initialDelaySec: 2, @@ -39,7 +59,7 @@ async function callEnqueueAndGetInference( } function printResponse( - document: Inference, + document: BaseInference, ): void { if (document) { console.log(`\n${document}`); @@ -55,25 +75,50 @@ function addMainOptions(prog: Command) { "-m, --model ", "Model ID (required)" ); - prog.option("-k, --api-key ", "API key for document endpoint"); prog.argument("", "full path to the file"); } export function cli() { program.name("mindee") - .description("Command line interface for Mindee products.") - .option("-d, --debug", "high verbosity mode"); - - const inferenceCmd: Command = program.command("parse") - .description("Send a file and retrieve the inference results."); + .description("Command line interface for Mindee V2 products.") + .option("-d, --debug", "high verbosity mode") + .option("-k, --api-key ", "your Mindee API key"); + const inferenceCmd: Command = program.command("extract") + .description("Send a file and extract results."); addMainOptions(inferenceCmd); + const cropCmd: Command = program.command("crop") + .description("Send a file and crop it."); + addMainOptions(cropCmd); + + const splitCmd: Command = program.command("split") + .description("Send a file and split it."); + addMainOptions(splitCmd); + + const ocrCmd: Command = program.command("ocr") + .description("Send a file and read its text content."); + addMainOptions(ocrCmd); + + const classifyCmd: Command = program.command("classify") + .description("Send a file and classify its content."); + addMainOptions(classifyCmd); + + inferenceCmd.action(function ( inputPath: string, options: OptionValues, ) { - return callEnqueueAndGetInference(inputPath, options); + const allOptions = { ...program.opts(), ...options }; + return enqueueAndGetInference(inputPath, allOptions); + }); + + cropCmd.action(function ( + inputPath: string, + options: OptionValues, + ) { + const allOptions = { ...program.opts(), ...options }; + return enqueueAndGetUtility(inputPath, allOptions); }); program.parse(process.argv); diff --git a/src/v2/client.ts b/src/v2/client.ts index 698b9ac6..ebc6b1da 100644 --- a/src/v2/client.ts +++ b/src/v2/client.ts @@ -1,12 +1,19 @@ import { setTimeout } from "node:timers/promises"; import { Dispatcher } from "undici"; import { InputSource } from "@/input/index.js"; +import { MindeeError } from "@/errors/index.js"; import { errorHandler } from "@/errors/handler.js"; import { LOG_LEVELS, logger } from "@/logger.js"; -import { ErrorResponse, InferenceResponse, JobResponse } from "./parsing/index.js"; +import { + ErrorResponse, + ExtractionResponse, + JobResponse, + ResponseConstructor, +} from "./parsing/index.js"; import { MindeeApiV2 } from "./http/mindeeApiV2.js"; import { MindeeHttpErrorV2 } from "./http/errors.js"; -import { InferenceParameters } from "./client/index.js"; +import { InferenceParameters, UtilityParameters, ValidatedPollingOptions } from "./client/index.js"; +import { CropResponse, BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; /** * Options for the V2 Mindee Client. @@ -66,20 +73,75 @@ export class Client { * @category Asynchronous * @returns a `Promise` containing the job (queue) corresponding to a document. */ - async enqueueInference( + async enqueueExtraction( inputSource: InputSource, params: InferenceParameters| ConstructorParameters[0] ): Promise { if (inputSource === undefined) { - throw new Error("The 'enqueue' function requires an input document."); + throw new MindeeError("An input document is required."); } - const inferenceParams = params instanceof InferenceParameters + const paramsInstance = params instanceof InferenceParameters ? params : new InferenceParameters(params); await inputSource.init(); + const jobResponse = await this.mindeeApi.reqPostInferenceEnqueue( + inputSource, paramsInstance + ); + if (jobResponse.job.id === undefined || jobResponse.job.id.length === 0) { + logger.error(`Failed enqueueing:\n${jobResponse.getRawHttp()}`); + throw new MindeeError("Enqueueing of the document failed."); + } + logger.debug( + `Successfully enqueued document with job ID: ${jobResponse.job.id}.` + ); + return jobResponse; + } - return await this.mindeeApi.reqPostInferenceEnqueue(inputSource, inferenceParams); + async enqueueUtility( + inputSource: InputSource, + params: UtilityParameters | ConstructorParameters[0] + ): Promise { + if (inputSource === undefined) { + throw new MindeeError("An input document is required."); + } + const paramsInstance = params instanceof UtilityParameters + ? params + : new UtilityParameters(params); + + await inputSource.init(); + const jobResponse = await this.mindeeApi.reqPostUtilityEnqueue(inputSource, paramsInstance); + if (jobResponse.job.id === undefined || jobResponse.job.id.length === 0) { + logger.error(`Failed enqueueing:\n${jobResponse.getRawHttp()}`); + throw new MindeeError("Enqueueing of the document failed."); + } + logger.debug( + `Successfully enqueued document with job ID: ${jobResponse.job.id}.` + ); + return jobResponse; + } + + async getInference( + responseType: ResponseConstructor, + inferenceId: string + ): Promise { + logger.debug( + `Attempting to get inference with ID: ${inferenceId} using response type: ${responseType.name}` + ); + return await this.mindeeApi.reqGetInference(responseType, inferenceId); + } + + /** + * Retrieves an inference. + * + * @param inferenceId id of the queue to poll. + * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. + * @category Asynchronous + * @returns a `Promise` containing a `Job`, which also contains a `Document` if the + * parsing is complete. + */ + async getExtraction(inferenceId: string): Promise { + return await this.getInference(ExtractionResponse, inferenceId); } /** @@ -91,8 +153,8 @@ export class Client { * @returns a `Promise` containing a `Job`, which also contains a `Document` if the * parsing is complete. */ - async getInference(inferenceId: string): Promise { - return await this.mindeeApi.reqGetInference(inferenceId); + async getUtility(inferenceId: string): Promise { + return await this.getInference(CropResponse, inferenceId); } /** @@ -120,58 +182,86 @@ export class Client { * @category Synchronous * @returns a `Promise` containing parsing results. */ - async enqueueAndGetInference( + async enqueueAndGetExtraction( inputSource: InputSource, - params: InferenceParameters| ConstructorParameters[0] - ): Promise { - const inferenceParams = params instanceof InferenceParameters + params: InferenceParameters | ConstructorParameters[0] + ): Promise { + const paramsInstance = params instanceof InferenceParameters ? params : new InferenceParameters(params); - const pollingOptions = inferenceParams.getValidatedPollingOptions(); + const pollingOptions = paramsInstance.getValidatedPollingOptions(); - const enqueueResponse: JobResponse = await this.enqueueInference(inputSource, params); - if (enqueueResponse.job.id === undefined || enqueueResponse.job.id.length === 0) { - logger.error(`Failed enqueueing:\n${enqueueResponse.getRawHttp()}`); - throw Error("Enqueueing of the document failed."); - } - const queueId: string = enqueueResponse.job.id; - logger.debug( - `Successfully enqueued document with job id: ${queueId}.` + const jobResponse: JobResponse = await this.enqueueExtraction(inputSource, params); + return await this.pollForInference( + ExtractionResponse, pollingOptions, jobResponse.job.id ); + } + + async enqueueAndGetUtility( + inputSource: InputSource, + params: UtilityParameters | ConstructorParameters[0] + ): Promise { + const paramsInstance = params instanceof UtilityParameters + ? params + : new UtilityParameters(params); + const pollingOptions = paramsInstance.getValidatedPollingOptions(); + + const jobResponse: JobResponse = await this.enqueueUtility(inputSource, params); + return await this.pollForInference( + CropResponse, pollingOptions, jobResponse.job.id + ); + } + + /** + * Send a document to an endpoint and poll the server until the result is sent or + * until the maximum number of tries is reached. + * @protected + */ + protected async pollForInference( + responseType: ResponseConstructor, + pollingOptions: ValidatedPollingOptions, + queueId: string, + ): Promise { + logger.debug( + `Waiting ${pollingOptions.initialDelaySec} seconds before polling.` + ); await setTimeout( pollingOptions.initialDelaySec * 1000, undefined, pollingOptions.initialTimerOptions ); + logger.debug( + `Start polling for inference using job ID: ${queueId}.` + ); let retryCounter: number = 1; - let pollResults: JobResponse = await this.getJob(queueId); - while (retryCounter < pollingOptions.maxRetries) { + let pollResults: JobResponse; + while (retryCounter < pollingOptions.maxRetries + 1) { + logger.debug( + `Attempt ${retryCounter} of ${pollingOptions.maxRetries}` + ); + pollResults = await this.getJob(queueId); + const error: ErrorResponse | undefined = pollResults.job.error; + if (error) { + throw new MindeeHttpErrorV2(error); + } + logger.debug(`Job status: ${pollResults.job.status}.`); if (pollResults.job.status === "Failed") { break; } if (pollResults.job.status === "Processed") { - return this.getInference(pollResults.job.id); + return this.getInference(responseType, pollResults.job.id); } - logger.debug( - `Polling server for parsing result with queueId: ${queueId}. -Attempt no. ${retryCounter} of ${pollingOptions.maxRetries}. -Job status: ${pollResults.job.status}.` - ); await setTimeout( pollingOptions.delaySec * 1000, undefined, pollingOptions.recurringTimerOptions ); - pollResults = await this.getJob(queueId); retryCounter++; } - const error: ErrorResponse | undefined = pollResults.job.error; - if (error) { - throw new MindeeHttpErrorV2(error); - } - throw Error( + + throw new MindeeError( "Asynchronous parsing request timed out after " + pollingOptions.delaySec * retryCounter + " seconds" diff --git a/src/v2/client/baseParameters.ts b/src/v2/client/baseParameters.ts new file mode 100644 index 00000000..57ca770c --- /dev/null +++ b/src/v2/client/baseParameters.ts @@ -0,0 +1,106 @@ +import { ValidatedPollingOptions } from "@/v2/client/pollingOptions.js"; +import { PollingOptions } from "@/v2/index.js"; +import { MindeeConfigurationError } from "@/errors/index.js"; + +/** + * Constructor parameters for BaseParameters and its subclasses. + */ +export interface BaseParametersConstructor { + modelId: string; + alias?: string; + webhookIds?: string[]; + pollingOptions?: PollingOptions; + closeFile?: boolean; +} + +/** + * Parameters accepted by the asynchronous **inference** v2 endpoint. + * + * All fields are optional except `modelId`. + * + * @category ClientV2 + * @example + * const params = { + * modelId: "YOUR_MODEL_ID", + * rag: true, + * alias: "YOUR_ALIAS", + * webhookIds: ["YOUR_WEBHOOK_ID_1", "YOUR_WEBHOOK_ID_2"], + * pollingOptions: { + * initialDelaySec: 2, + * delaySec: 1.5, + * } + * }; + */ +export abstract class BaseParameters { + /** + * Model ID to use for the inference. **Required.** + */ + modelId: string; + /** + * Use an alias to link the file to your own DB. + * If empty, no alias will be used. + */ + alias?: string; + /** + * Webhook IDs to call after all processing is finished. + * If empty, no webhooks will be used. + */ + webhookIds?: string[]; + /** + * Client-side polling configuration (see {@link PollingOptions}). + */ + pollingOptions?: PollingOptions; + /** + * By default, the file is closed once the upload is finished. + * Set to `false` to keep it open. + */ + closeFile?: boolean; + + protected constructor(params: BaseParametersConstructor) { + if (params.modelId === undefined || params.modelId === null || params.modelId === "") { + throw new MindeeConfigurationError("Model ID must be provided"); + } + this.modelId = params.modelId; + this.alias = params.alias; + this.webhookIds = params.webhookIds; + this.closeFile = params.closeFile; + this.pollingOptions = params.pollingOptions; + } + + /** + * Checks the values for asynchronous parsing. Returns their corrected value if they are undefined. + * @returns A valid `AsyncOptions`. + */ + getValidatedPollingOptions(): ValidatedPollingOptions { + const minDelaySec = 1; + const minInitialDelay = 1; + const minRetries = 2; + let newAsyncParams: PollingOptions; + if (this.pollingOptions === undefined) { + newAsyncParams = { + delaySec: 1.5, + initialDelaySec: 2, + maxRetries: 80 + }; + } else { + newAsyncParams = { ...this.pollingOptions }; + if ( + !newAsyncParams.delaySec || + !newAsyncParams.initialDelaySec || + !newAsyncParams.maxRetries + ) { + throw Error("Invalid polling options."); + } + if (newAsyncParams.delaySec < minDelaySec) { + throw Error(`Cannot set auto-parsing delay to less than ${minDelaySec} second(s).`); + } + if (newAsyncParams.initialDelaySec < minInitialDelay) { + throw Error(`Cannot set initial parsing delay to less than ${minInitialDelay} second(s).`); + } + if (newAsyncParams.maxRetries < minRetries) { + throw Error(`Cannot set retry to less than ${minRetries}.`); + } + } + return newAsyncParams as ValidatedPollingOptions; + } +} diff --git a/src/v2/client/index.ts b/src/v2/client/index.ts index a871d3bf..132168be 100644 --- a/src/v2/client/index.ts +++ b/src/v2/client/index.ts @@ -1,3 +1,4 @@ export { DataSchema } from "./dataSchema.js"; export type { PollingOptions, ValidatedPollingOptions } from "./pollingOptions.js"; export { InferenceParameters } from "./inferenceParameters.js"; +export { UtilityParameters } from "./utilityParameters.js"; diff --git a/src/v2/client/inferenceParameters.ts b/src/v2/client/inferenceParameters.ts index 13687a30..14f6332d 100644 --- a/src/v2/client/inferenceParameters.ts +++ b/src/v2/client/inferenceParameters.ts @@ -1,6 +1,6 @@ import { StringDict } from "@/parsing/stringDict.js"; -import { PollingOptions, ValidatedPollingOptions } from "./pollingOptions.js"; import { DataSchema } from "./dataSchema.js"; +import { BaseParameters, BaseParametersConstructor } from "@/v2/client/baseParameters.js"; /** * Parameters accepted by the asynchronous **inference** v2 endpoint. @@ -20,11 +20,7 @@ import { DataSchema } from "./dataSchema.js"; * } * }; */ -export class InferenceParameters { - /** - * Model ID to use for the inference. **Required.** - */ - modelId: string; +export class InferenceParameters extends BaseParameters { /** * Use Retrieval-Augmented Generation during inference. */ @@ -42,59 +38,31 @@ export class InferenceParameters { * Useful for automation. */ confidence?: boolean; - /** - * Use an alias to link the file to your own DB. - * If empty, no alias will be used. - */ - alias?: string; /** * Additional text context used by the model during inference. * *Not recommended*, for specific use only. */ textContext?: string; - /** - * Webhook IDs to call after all processing is finished. - * If empty, no webhooks will be used. - */ - webhookIds?: string[]; - /** - * Client-side polling configuration (see {@link PollingOptions}). - */ - pollingOptions?: PollingOptions; - /** - * By default, the file is closed once the upload is finished. - * Set to `false` to keep it open. - */ - closeFile?: boolean; /** * Dynamic changes to the data schema of the model for this inference. * Not recommended, for specific use only. */ dataSchema?: DataSchema | StringDict | string; - constructor(params: { - modelId: string; + constructor(params: BaseParametersConstructor & { rag?: boolean; rawText?: boolean; polygon?: boolean; confidence?: boolean; - alias?: string; textContext?: string; - webhookIds?: string[]; - pollingOptions?: PollingOptions; - closeFile?: boolean; dataSchema?: DataSchema | StringDict | string; }) { - this.modelId = params.modelId; + super({ ...params }); this.rag = params.rag; this.rawText = params.rawText; this.polygon = params.polygon; this.confidence = params.confidence; - this.alias = params.alias; this.textContext = params.textContext; - this.webhookIds = params.webhookIds; - this.closeFile = params.closeFile; - this.pollingOptions = params.pollingOptions; if (params.dataSchema !== undefined && params.dataSchema !== null) { if (!(params.dataSchema instanceof DataSchema)){ @@ -104,41 +72,4 @@ export class InferenceParameters { } } } - - /** - * Checks the values for asynchronous parsing. Returns their corrected value if they are undefined. - * @returns A valid `AsyncOptions`. - */ - getValidatedPollingOptions(): ValidatedPollingOptions { - const minDelaySec = 1; - const minInitialDelay = 1; - const minRetries = 2; - let newAsyncParams: PollingOptions; - if (this.pollingOptions === undefined) { - newAsyncParams = { - delaySec: 1.5, - initialDelaySec: 2, - maxRetries: 80 - }; - } else { - newAsyncParams = { ...this.pollingOptions }; - if ( - !newAsyncParams.delaySec || - !newAsyncParams.initialDelaySec || - !newAsyncParams.maxRetries - ) { - throw Error("Invalid polling options."); - } - if (newAsyncParams.delaySec < minDelaySec) { - throw Error(`Cannot set auto-parsing delay to less than ${minDelaySec} second(s).`); - } - if (newAsyncParams.initialDelaySec < minInitialDelay) { - throw Error(`Cannot set initial parsing delay to less than ${minInitialDelay} second(s).`); - } - if (newAsyncParams.maxRetries < minRetries) { - throw Error(`Cannot set retry to less than ${minRetries}.`); - } - } - return newAsyncParams as ValidatedPollingOptions; - } } diff --git a/src/v2/client/utilityParameters.ts b/src/v2/client/utilityParameters.ts new file mode 100644 index 00000000..eef114b6 --- /dev/null +++ b/src/v2/client/utilityParameters.ts @@ -0,0 +1,24 @@ +import { BaseParameters, BaseParametersConstructor } from "@/v2/client/baseParameters.js"; + +/** + * Parameters accepted by the asynchronous **inference** v2 endpoint. + * + * All fields are optional except `modelId`. + * + * @category ClientV2 + * @example + * const params = { + * modelId: "YOUR_MODEL_ID", + * alias: "YOUR_ALIAS", + * webhookIds: ["YOUR_WEBHOOK_ID_1", "YOUR_WEBHOOK_ID_2"], + * pollingOptions: { + * initialDelaySec: 2, + * delaySec: 1.5, + * } + * }; + */ +export class UtilityParameters extends BaseParameters { + constructor(params: BaseParametersConstructor & {}) { + super({ ...params }); + } +} diff --git a/src/v2/http/mindeeApiV2.ts b/src/v2/http/mindeeApiV2.ts index 3f89533e..bb210b8c 100644 --- a/src/v2/http/mindeeApiV2.ts +++ b/src/v2/http/mindeeApiV2.ts @@ -1,10 +1,18 @@ import { ApiSettingsV2 } from "./apiSettingsV2.js"; import { Dispatcher } from "undici"; -import { InferenceParameters } from "@/v2/client/index.js"; -import { ErrorResponse, InferenceResponse, JobResponse } from "@/v2/parsing/index.js"; +import { InferenceParameters, UtilityParameters } from "@/v2/client/index.js"; +import { + BaseResponse, + ErrorResponse, + ResponseConstructor, + JobResponse, + CropResponse, + OcrResponse, + SplitResponse, ExtractionResponse, BaseInferenceResponse, +} from "@/v2/parsing/index.js"; import { sendRequestAndReadResponse, BaseHttpResponse } from "@/http/apiCore.js"; import { InputSource, LocalInputSource, UrlInput } from "@/input/index.js"; -import { MindeeConfigurationError, MindeeDeserializationError } from "@/errors/index.js"; +import { MindeeDeserializationError } from "@/errors/index.js"; import { MindeeHttpErrorV2 } from "./errors.js"; import { logger } from "@/logger.js"; @@ -16,36 +24,76 @@ export class MindeeApiV2 { } /** - * Sends a file to the inference queue. + * Sends a file to the extraction inference queue. * @param inputSource Local file loaded as an input. * @param params {InferenceParameters} parameters relating to the enqueueing options. * @category V2 * @throws Error if the server's response contains one. * @returns a `Promise` containing a job response. */ - async reqPostInferenceEnqueue(inputSource: InputSource, params: InferenceParameters): Promise { + async reqPostInferenceEnqueue( + inputSource: InputSource, params: InferenceParameters + ): Promise { await inputSource.init(); - if (params.modelId === undefined || params.modelId === null || params.modelId === "") { - throw new MindeeConfigurationError("Model ID must be provided"); - } - const result: BaseHttpResponse = await this.#documentEnqueuePost(inputSource, params); + const result: BaseHttpResponse = await this.#inferenceEnqueuePost(inputSource, params); if (result.data.error !== undefined) { throw new MindeeHttpErrorV2(result.data.error); } return this.#processResponse(result, JobResponse); } + /** + * Sends a file to the utility inference queue. + * @param inputSource Local file loaded as an input. + * @param params {UtilityParameters} parameters relating to the enqueueing options. + * @category V2 + * @throws Error if the server's response contains one. + * @returns a `Promise` containing a job response. + */ + async reqPostUtilityEnqueue( + inputSource: InputSource, params: UtilityParameters + ): Promise { + await inputSource.init(); + const result: BaseHttpResponse = await this.#utilityEnqueuePost(inputSource, "crop", params); + if (result.data.error !== undefined) { + throw new MindeeHttpErrorV2(result.data.error); + } + return this.#processResponse(result, JobResponse); + } /** * Requests the job of a queued document from the API. * Throws an error if the server's response contains one. + * @param responseType * @param inferenceId The document's ID in the queue. * @category Asynchronous * @returns a `Promise` containing either the parsed result, or information on the queue. */ - async reqGetInference(inferenceId: string): Promise { - const queueResponse: BaseHttpResponse = await this.#inferenceResultReqGet(inferenceId, "inferences"); - return this.#processResponse(queueResponse, InferenceResponse); + async reqGetInference( + responseType: ResponseConstructor, + inferenceId: string, + ): Promise { + let slug: string; + // this is disgusting, look into a more elegant way of linking the response type to the slug + switch (responseType as any) { + case CropResponse: + slug = "utilities/crop"; + break; + case OcrResponse: + slug = "utilities/ocr"; + break; + case SplitResponse: + slug = "utilities/split"; + break; + case ExtractionResponse: + slug = "inferences"; + break; + default: + slug = "inferences"; + break; + } + const queueResponse: BaseHttpResponse = await this.#inferenceResultReqGet(inferenceId, slug); + return this.#processResponse(queueResponse, responseType); } /** @@ -60,8 +108,10 @@ export class MindeeApiV2 { return this.#processResponse(queueResponse, JobResponse); } - #processResponse - (result: BaseHttpResponse, responseType: new (data: { [key: string]: any; }) => T): T { + #processResponse( + result: BaseHttpResponse, + responseType: ResponseConstructor, + ): T { if (result.messageObj?.statusCode && (result.messageObj?.statusCode > 399 || result.messageObj?.statusCode < 200)) { if (result.data?.status !== null) { throw new MindeeHttpErrorV2(new ErrorResponse(result.data)); @@ -85,13 +135,49 @@ export class MindeeApiV2 { } } + /** + * Sends a document to the inference queue. + * + * @param inputSource Local or remote file as an input. + * @param slug Slug of the utility to enqueue. + * @param params {InferenceParameters} parameters relating to the enqueueing options. + */ + async #utilityEnqueuePost( + inputSource: InputSource, + slug: string, + params: UtilityParameters + ): Promise { + const form = new FormData(); + + form.set("model_id", params.modelId); + if (params.webhookIds && params.webhookIds.length > 0) { + form.set("webhook_ids", params.webhookIds.join(",")); + } + if (inputSource instanceof LocalInputSource) { + form.set("file", new Blob([inputSource.fileObject]), inputSource.filename); + } else { + form.set("url", (inputSource as UrlInput).url); + } + const path = `/v2/utilities/${slug}/enqueue`; + const options = { + method: "POST", + headers: this.settings.baseHeaders, + hostname: this.settings.hostname, + path: path, + body: form, + timeout: this.settings.timeout, + }; + return await sendRequestAndReadResponse(this.settings.dispatcher, options); + } + + /** * Sends a document to the inference queue. * * @param inputSource Local or remote file as an input. * @param params {InferenceParameters} parameters relating to the enqueueing options. */ - async #documentEnqueuePost( + async #inferenceEnqueuePost( inputSource: InputSource, params: InferenceParameters ): Promise { diff --git a/src/v2/index.ts b/src/v2/index.ts index b9f968f6..6157c1fe 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -4,7 +4,7 @@ export { LocalResponse } from "./parsing/localResponse.js"; export { Client } from "./client.js"; export { InferenceFile, - InferenceResponse, + ExtractionResponse, JobResponse, RawText, RagMetadata, diff --git a/src/v2/parsing/index.ts b/src/v2/parsing/index.ts index 60652919..d440caa2 100644 --- a/src/v2/parsing/index.ts +++ b/src/v2/parsing/index.ts @@ -6,14 +6,20 @@ export type { ErrorDetails } from "./error/index.js"; export { Job, JobResponse, - JobWebhook, + JobWebhook } from "./job/index.js"; -export { InferenceFile } from "./inferenceFile.js"; -export { BaseResponse } from "./baseResponse.js"; -export { Inference } from "./inference.js"; -export { InferenceActiveOptions } from "./inferenceActiveOptions.js"; -export { InferenceModel } from "./inferenceModel.js"; -export { InferenceResponse } from "./inferenceResponse.js"; -export { InferenceResult } from "./inferenceResult.js"; -export { RawText } from "./rawText.js"; -export { RagMetadata } from "./ragMetadata.js"; +export { + InferenceFile, + InferenceModel, + ExtractionInference, + ExtractionActiveOptions, + ExtractionResponse, + ExtractionResult, + ClassifyResponse, + CropResponse, + OcrResponse, + SplitResponse, +} from "./inference/index.js"; +export { RawText, RagMetadata } from "./inference/field/index.js"; +export type { ResponseConstructor, BaseResponse } from "./baseResponse.js"; +export type { BaseInferenceResponse } from "./inference/index.js"; diff --git a/src/v2/parsing/inference.ts b/src/v2/parsing/inference.ts deleted file mode 100644 index 37a91c7c..00000000 --- a/src/v2/parsing/inference.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { StringDict } from "@/parsing/stringDict.js"; -import { InferenceModel } from "./inferenceModel.js"; -import { InferenceResult } from "./inferenceResult.js"; -import { InferenceFile } from "./inferenceFile.js"; -import { InferenceActiveOptions } from "./inferenceActiveOptions.js"; - -export class Inference { - /** - * Model info for the inference. - */ - public model: InferenceModel; - /** - * File info for the inference. - */ - public file: InferenceFile; - /** - * Result of the inference. - */ - public result: InferenceResult; - /** - * ID of the inference. - */ - public id?: string; - /** - * Active options for the inference. - */ - public activeOptions: InferenceActiveOptions; - - constructor(serverResponse: StringDict) { - this.model = new InferenceModel(serverResponse["model"]); - this.file = new InferenceFile(serverResponse["file"]); - this.result = new InferenceResult(serverResponse["result"]); - this.activeOptions = new InferenceActiveOptions(serverResponse["active_options"]); - } - - toString(): string { - return ( - "Inference\n" + - "#########\n" + - this.model.toString() + "\n" + - this.file.toString() + "\n" + - this.activeOptions.toString() + "\n" + - this.result + "\n" - ); - } -} diff --git a/src/v2/parsing/inference/baseInference.ts b/src/v2/parsing/inference/baseInference.ts new file mode 100644 index 00000000..ab44a6ca --- /dev/null +++ b/src/v2/parsing/inference/baseInference.ts @@ -0,0 +1,24 @@ +import { InferenceModel } from "@/v2/parsing/inference/inferenceModel.js"; +import { InferenceFile } from "@/v2/index.js"; +import { StringDict } from "@/parsing/index.js"; + +export abstract class BaseInference { + /** + * Model info for the inference. + */ + public model: InferenceModel; + /** + * File info for the inference. + */ + public file: InferenceFile; + /** + * ID of the inference. + */ + public id: string; + + protected constructor(serverResponse: StringDict) { + this.id = serverResponse["id"]; + this.model = new InferenceModel(serverResponse["model"]); + this.file = new InferenceFile(serverResponse["file"]); + } +} diff --git a/src/v2/parsing/inference/baseInferenceResponse.ts b/src/v2/parsing/inference/baseInferenceResponse.ts new file mode 100644 index 00000000..c87dcc10 --- /dev/null +++ b/src/v2/parsing/inference/baseInferenceResponse.ts @@ -0,0 +1,18 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { BaseResponse } from "@/v2/parsing/baseResponse.js"; +import { BaseInference } from "./baseInference.js"; + +export abstract class BaseInferenceResponse extends BaseResponse { + /** + * The inference result for a crop utility request. + */ + public inference: BaseInference; + + /** + * @param serverResponse JSON response from the server. + */ + protected constructor(serverResponse: StringDict) { + super(serverResponse); + this.inference = serverResponse["inference"]; + } +} diff --git a/src/v2/parsing/inference/classification/classifyResponse.ts b/src/v2/parsing/inference/classification/classifyResponse.ts new file mode 100644 index 00000000..a3112f46 --- /dev/null +++ b/src/v2/parsing/inference/classification/classifyResponse.ts @@ -0,0 +1,9 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; + +export class ClassifyResponse extends BaseInferenceResponse { + + constructor(serverResponse: StringDict) { + super(serverResponse); + } +} diff --git a/src/v2/parsing/inference/crop/cropInference.ts b/src/v2/parsing/inference/crop/cropInference.ts new file mode 100644 index 00000000..19e68c98 --- /dev/null +++ b/src/v2/parsing/inference/crop/cropInference.ts @@ -0,0 +1,25 @@ +import { StringDict } from "@/parsing/index.js"; +import { BaseInference } from "@/v2/parsing/inference/baseInference.js"; +import { CropResult } from "@/v2/parsing/inference/crop/cropResult.js"; + +export class CropInference extends BaseInference { + /** + * Result of a crop utility inference. + */ + result: CropResult; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.result = new CropResult(serverResponse["result"]); + } + + toString(): string { + return ( + "Inference\n" + + "#########\n" + + this.model.toString() + "\n" + + this.file.toString() + "\n" + + this.result.toString() + "\n" + ); + } +} diff --git a/src/v2/parsing/inference/crop/cropItem.ts b/src/v2/parsing/inference/crop/cropItem.ts new file mode 100644 index 00000000..c1481a07 --- /dev/null +++ b/src/v2/parsing/inference/crop/cropItem.ts @@ -0,0 +1,16 @@ +import { FieldLocation } from "@/v2/parsing/inference/field/index.js"; +import { StringDict } from "@/parsing/index.js"; + +export class CropItem { + objectType: string; + location: FieldLocation; + + constructor(serverResponse: StringDict) { + this.objectType = serverResponse["objectType"]; + this.location = new FieldLocation(serverResponse["location"]); + } + + toString(): string { + return `${this.objectType} :: ${this.location}`; + } +} diff --git a/src/v2/parsing/inference/crop/cropResponse.ts b/src/v2/parsing/inference/crop/cropResponse.ts new file mode 100644 index 00000000..482ffe8d --- /dev/null +++ b/src/v2/parsing/inference/crop/cropResponse.ts @@ -0,0 +1,15 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { CropInference } from "./cropInference.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; + +export class CropResponse extends BaseInferenceResponse { + /** + * The inference result for a crop utility request. + */ + public inference: CropInference; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.inference = new CropInference(serverResponse["inference"]); + } +} diff --git a/src/v2/parsing/inference/crop/cropResult.ts b/src/v2/parsing/inference/crop/cropResult.ts new file mode 100644 index 00000000..b2bcb51a --- /dev/null +++ b/src/v2/parsing/inference/crop/cropResult.ts @@ -0,0 +1,17 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { CropItem } from "@/v2/parsing/inference/crop/cropItem.js"; + +export class CropResult { + /** + * Fields contained in the inference. + */ + public crop: CropItem[] = []; + + constructor(serverResponse: StringDict) { + this.crop = serverResponse["crop"].map((cropItem: StringDict) => new CropItem(cropItem)); + } + + toString(): string { + return `Crop\n====\n${this.crop}`; + } +} diff --git a/src/v2/parsing/dataSchemaActiveOption.ts b/src/v2/parsing/inference/extraction/dataSchemaActiveOption.ts similarity index 100% rename from src/v2/parsing/dataSchemaActiveOption.ts rename to src/v2/parsing/inference/extraction/dataSchemaActiveOption.ts diff --git a/src/v2/parsing/inferenceActiveOptions.ts b/src/v2/parsing/inference/extraction/extractionActiveOptions.ts similarity index 97% rename from src/v2/parsing/inferenceActiveOptions.ts rename to src/v2/parsing/inference/extraction/extractionActiveOptions.ts index 1f5febea..7846205f 100644 --- a/src/v2/parsing/inferenceActiveOptions.ts +++ b/src/v2/parsing/inference/extraction/extractionActiveOptions.ts @@ -1,7 +1,7 @@ import { StringDict } from "@/parsing/stringDict.js"; import { DataSchemaActiveOption } from "./dataSchemaActiveOption.js"; -export class InferenceActiveOptions { +export class ExtractionActiveOptions { /** * Whether the RAG feature was activated. */ diff --git a/src/v2/parsing/inference/extraction/extractionInference.ts b/src/v2/parsing/inference/extraction/extractionInference.ts new file mode 100644 index 00000000..7db6d240 --- /dev/null +++ b/src/v2/parsing/inference/extraction/extractionInference.ts @@ -0,0 +1,32 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { ExtractionResult } from "./extractionResult.js"; +import { ExtractionActiveOptions } from "./extractionActiveOptions.js"; +import { BaseInference } from "@/v2/parsing/inference/baseInference.js"; + +export class ExtractionInference extends BaseInference { + /** + * Result of the inference. + */ + public result: ExtractionResult; + /** + * Active options for the inference. + */ + public activeOptions: ExtractionActiveOptions; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.result = new ExtractionResult(serverResponse["result"]); + this.activeOptions = new ExtractionActiveOptions(serverResponse["active_options"]); + } + + toString(): string { + return ( + "Inference\n" + + "#########\n" + + this.model.toString() + "\n" + + this.file.toString() + "\n" + + this.activeOptions.toString() + "\n" + + this.result.toString() + "\n" + ); + } +} diff --git a/src/v2/parsing/inference/extraction/extractionResponse.ts b/src/v2/parsing/inference/extraction/extractionResponse.ts new file mode 100644 index 00000000..a70f2caa --- /dev/null +++ b/src/v2/parsing/inference/extraction/extractionResponse.ts @@ -0,0 +1,15 @@ +import { ExtractionInference } from "./extractionInference.js"; +import { StringDict } from "@/parsing/stringDict.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; + +export class ExtractionResponse extends BaseInferenceResponse { + /** + * Inference result. + */ + public inference: ExtractionInference; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.inference = new ExtractionInference(serverResponse["inference"]); + } +} diff --git a/src/v2/parsing/inferenceResult.ts b/src/v2/parsing/inference/extraction/extractionResult.ts similarity index 76% rename from src/v2/parsing/inferenceResult.ts rename to src/v2/parsing/inference/extraction/extractionResult.ts index 5acd4284..e1a89d50 100644 --- a/src/v2/parsing/inferenceResult.ts +++ b/src/v2/parsing/inference/extraction/extractionResult.ts @@ -1,9 +1,9 @@ -import { InferenceFields } from "./field/index.js"; +import { InferenceFields } from "@/v2/parsing/inference/field/index.js"; import { StringDict } from "@/parsing/stringDict.js"; -import { RawText } from "./rawText.js"; -import { RagMetadata } from "./ragMetadata.js"; +import { RawText } from "../field/rawText.js"; +import { RagMetadata } from "../field/ragMetadata.js"; -export class InferenceResult { +export class ExtractionResult { /** * Fields contained in the inference. */ diff --git a/src/v2/parsing/field/baseField.ts b/src/v2/parsing/inference/field/baseField.ts similarity index 100% rename from src/v2/parsing/field/baseField.ts rename to src/v2/parsing/inference/field/baseField.ts diff --git a/src/v2/parsing/field/fieldConfidence.ts b/src/v2/parsing/inference/field/fieldConfidence.ts similarity index 100% rename from src/v2/parsing/field/fieldConfidence.ts rename to src/v2/parsing/inference/field/fieldConfidence.ts diff --git a/src/v2/parsing/field/fieldFactory.ts b/src/v2/parsing/inference/field/fieldFactory.ts similarity index 100% rename from src/v2/parsing/field/fieldFactory.ts rename to src/v2/parsing/inference/field/fieldFactory.ts diff --git a/src/v2/parsing/field/fieldLocation.ts b/src/v2/parsing/inference/field/fieldLocation.ts similarity index 100% rename from src/v2/parsing/field/fieldLocation.ts rename to src/v2/parsing/inference/field/fieldLocation.ts diff --git a/src/v2/parsing/field/index.ts b/src/v2/parsing/inference/field/index.ts similarity index 77% rename from src/v2/parsing/field/index.ts rename to src/v2/parsing/inference/field/index.ts index 7b4a651e..da7d5b53 100644 --- a/src/v2/parsing/field/index.ts +++ b/src/v2/parsing/inference/field/index.ts @@ -4,3 +4,5 @@ export { FieldLocation } from "./fieldLocation.js"; export { ListField } from "./listField.js"; export { ObjectField } from "./objectField.js"; export { SimpleField } from "./simpleField.js"; +export { RawText } from "./rawText.js"; +export { RagMetadata } from "./ragMetadata.js"; diff --git a/src/v2/parsing/field/inferenceFields.ts b/src/v2/parsing/inference/field/inferenceFields.ts similarity index 100% rename from src/v2/parsing/field/inferenceFields.ts rename to src/v2/parsing/inference/field/inferenceFields.ts diff --git a/src/v2/parsing/field/listField.ts b/src/v2/parsing/inference/field/listField.ts similarity index 100% rename from src/v2/parsing/field/listField.ts rename to src/v2/parsing/inference/field/listField.ts diff --git a/src/v2/parsing/field/objectField.ts b/src/v2/parsing/inference/field/objectField.ts similarity index 100% rename from src/v2/parsing/field/objectField.ts rename to src/v2/parsing/inference/field/objectField.ts diff --git a/src/v2/parsing/ragMetadata.ts b/src/v2/parsing/inference/field/ragMetadata.ts similarity index 100% rename from src/v2/parsing/ragMetadata.ts rename to src/v2/parsing/inference/field/ragMetadata.ts diff --git a/src/v2/parsing/rawText.ts b/src/v2/parsing/inference/field/rawText.ts similarity index 100% rename from src/v2/parsing/rawText.ts rename to src/v2/parsing/inference/field/rawText.ts diff --git a/src/v2/parsing/rawTextPage.ts b/src/v2/parsing/inference/field/rawTextPage.ts similarity index 100% rename from src/v2/parsing/rawTextPage.ts rename to src/v2/parsing/inference/field/rawTextPage.ts diff --git a/src/v2/parsing/field/simpleField.ts b/src/v2/parsing/inference/field/simpleField.ts similarity index 100% rename from src/v2/parsing/field/simpleField.ts rename to src/v2/parsing/inference/field/simpleField.ts diff --git a/src/v2/parsing/inference/index.ts b/src/v2/parsing/inference/index.ts new file mode 100644 index 00000000..6070f10f --- /dev/null +++ b/src/v2/parsing/inference/index.ts @@ -0,0 +1,30 @@ +// Inference +export { InferenceFile } from "./inferenceFile.js"; +export { InferenceModel } from "./inferenceModel.js"; +export { BaseInference } from "./baseInference.js"; +export { BaseInferenceResponse } from "./baseInferenceResponse.js"; + +// Fields +export * as field from "./field/index.js"; + +// Extraction +export { ExtractionInference } from "./extraction/extractionInference.js"; +export { ExtractionActiveOptions } from "./extraction/extractionActiveOptions.js"; +export { ExtractionResponse } from "./extraction/extractionResponse.js"; +export { ExtractionResult } from "./extraction/extractionResult.js"; + +// Classification +export { ClassifyResponse } from "./classification/classifyResponse.js"; + +// Crop +export { CropInference } from "./crop/cropInference.js"; +export { CropItem } from "./crop/cropItem.js"; +export { CropResponse } from "./crop/cropResponse.js"; +export { CropResult } from "./crop/cropResult.js"; + +// OCR +export { OcrResponse } from "./ocr/ocrResponse.js"; + +// Split +export { SplitResponse } from "./split/splitResponse.js"; + diff --git a/src/v2/parsing/inferenceFile.ts b/src/v2/parsing/inference/inferenceFile.ts similarity index 100% rename from src/v2/parsing/inferenceFile.ts rename to src/v2/parsing/inference/inferenceFile.ts diff --git a/src/v2/parsing/inferenceModel.ts b/src/v2/parsing/inference/inferenceModel.ts similarity index 100% rename from src/v2/parsing/inferenceModel.ts rename to src/v2/parsing/inference/inferenceModel.ts diff --git a/src/v2/parsing/inference/ocr/ocrResponse.ts b/src/v2/parsing/inference/ocr/ocrResponse.ts new file mode 100644 index 00000000..5e37cf5c --- /dev/null +++ b/src/v2/parsing/inference/ocr/ocrResponse.ts @@ -0,0 +1,9 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; + +export class OcrResponse extends BaseInferenceResponse { + + constructor(serverResponse: StringDict) { + super(serverResponse); + } +} diff --git a/src/v2/parsing/inference/split/splitResponse.ts b/src/v2/parsing/inference/split/splitResponse.ts new file mode 100644 index 00000000..a0227d50 --- /dev/null +++ b/src/v2/parsing/inference/split/splitResponse.ts @@ -0,0 +1,9 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; + +export class SplitResponse extends BaseInferenceResponse { + + constructor(serverResponse: StringDict) { + super(serverResponse); + } +} diff --git a/src/v2/parsing/inferenceResponse.ts b/src/v2/parsing/inferenceResponse.ts deleted file mode 100644 index 95090df2..00000000 --- a/src/v2/parsing/inferenceResponse.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { BaseResponse } from "./baseResponse.js"; -import { Inference } from "./inference.js"; -import { StringDict } from "@/parsing/stringDict.js"; - -export class InferenceResponse extends BaseResponse { - /** - * Inference result. - */ - public inference: Inference; - - constructor(serverResponse: StringDict) { - super(serverResponse); - this.inference = new Inference(serverResponse["inference"]); - } -} diff --git a/src/v2/parsing/localResponse.ts b/src/v2/parsing/localResponse.ts index 4f0399ab..657e7b7b 100644 --- a/src/v2/parsing/localResponse.ts +++ b/src/v2/parsing/localResponse.ts @@ -1,7 +1,7 @@ import { StringDict } from "@/parsing/stringDict.js"; import { MindeeError } from "@/errors/index.js"; -import { BaseResponse } from "./baseResponse.js"; import { LocalResponseBase } from "@/parsing/localResponseBase.js"; +import { BaseResponse } from "./baseResponse.js"; /** * Local response loaded from a file. diff --git a/tests/v2/client.integration.ts b/tests/v2/client.integration.ts index 1dcb4e44..aec63533 100644 --- a/tests/v2/client.integration.ts +++ b/tests/v2/client.integration.ts @@ -7,10 +7,10 @@ import { PathInput, UrlInput, Base64Input, - InferenceResponse, + ExtractionResponse, } from "@/index.js"; -import { Inference } from "@/v2/parsing/index.js"; -import { SimpleField } from "@/v2/parsing/field/index.js"; +import { ExtractionInference } from "@/v2/parsing/index.js"; +import { SimpleField } from "@/v2/parsing/inference/field/index.js"; import { MindeeHttpErrorV2 } from "@/v2/http/index.js"; import * as fs from "node:fs"; import { RESOURCE_PATH, V2_PRODUCT_PATH, V2_RESOURCE_PATH } from "../index.js"; @@ -25,7 +25,7 @@ function check422(err: unknown) { expect(errObj.errors).to.be.instanceOf(Array); } -function checkEmptyActiveOptions(inference: Inference) { +function checkEmptyActiveOptions(inference: ExtractionInference) { expect(inference.activeOptions).to.not.be.null; expect(inference.activeOptions?.rag).to.be.false; expect(inference.activeOptions?.rawText).to.be.false; @@ -80,11 +80,11 @@ describe("MindeeV2 – Client Integration Tests", () => { webhookIds: [], alias: "ts_integration_empty_multiple" }; - const response = await client.enqueueAndGetInference(source, params); + const response = await client.enqueueAndGetExtraction(source, params); expect(response).to.exist; - expect(response.inference).to.be.instanceOf(Inference); - const inference: Inference = response.inference; + expect(response.inference).to.be.instanceOf(ExtractionInference); + const inference: ExtractionInference = response.inference; expect(inference.file?.name).to.equal("multipage_cut-2.pdf"); expect(inference.file.pageCount).to.equal(2); @@ -108,10 +108,10 @@ describe("MindeeV2 – Client Integration Tests", () => { alias: "ts_integration_binary_filled_single" }; - const response = await client.enqueueAndGetInference(source, params); + const response = await client.enqueueAndGetExtraction(source, params); - expect(response.inference).to.be.instanceOf(Inference); - const inference: Inference = response.inference; + expect(response.inference).to.be.instanceOf(ExtractionInference); + const inference: ExtractionInference = response.inference; expect(inference.file?.name).to.equal("default_sample.jpg"); expect(inference.model?.id).to.equal(modelId); @@ -146,10 +146,10 @@ describe("MindeeV2 – Client Integration Tests", () => { alias: "ts_integration_base64_filled_single" }); - const response = await client.enqueueAndGetInference(source, params); + const response = await client.enqueueAndGetExtraction(source, params); - expect(response.inference).to.be.instanceOf(Inference); - const inference: Inference = response.inference; + expect(response.inference).to.be.instanceOf(ExtractionInference); + const inference: ExtractionInference = response.inference; expect(inference.file?.name).to.equal("receipt.jpg"); expect(inference.model?.id).to.equal(modelId); @@ -168,7 +168,7 @@ describe("MindeeV2 – Client Integration Tests", () => { const badParams = { modelId: "00000000-0000-0000-0000-000000000000" }; try { - await client.enqueueInference(source, badParams); + await client.enqueueExtraction(source, badParams); expect.fail("Expected the call to throw, but it succeeded."); } catch (err) { check422(err); @@ -177,7 +177,10 @@ describe("MindeeV2 – Client Integration Tests", () => { it("Invalid job ID – getInference must raise 422", async () => { try { - await client.getInference("00000000-0000-0000-0000-000000000000"); + await client.getInference( + ExtractionResponse, + "00000000-0000-0000-0000-000000000000" + ); expect.fail("Expected the call to throw, but it succeeded."); } catch (err) { check422(err); @@ -196,10 +199,10 @@ describe("MindeeV2 – Client Integration Tests", () => { webhookIds: [], alias: "ts_integration_url_source" }); - const response: InferenceResponse = await client.enqueueAndGetInference(source, params); + const response: ExtractionResponse = await client.enqueueAndGetExtraction(source, params); expect(response).to.exist; - expect(response.inference).to.be.instanceOf(Inference); + expect(response.inference).to.be.instanceOf(ExtractionInference); }).timeout(60000); it("Data Schema Override - Overrides the data schema successfully", async () => { @@ -214,10 +217,10 @@ describe("MindeeV2 – Client Integration Tests", () => { dataSchema: dataSchemaReplace, alias: "ts_integration_data_schema_replace" }); - const response = await client.enqueueAndGetInference(source, params); + const response = await client.enqueueAndGetExtraction(source, params); expect(response).to.exist; - expect(response.inference).to.be.instanceOf(Inference); + expect(response.inference).to.be.instanceOf(ExtractionInference); expect(response.inference.result.fields.get("test_replace")).to.exist; expect((response.inference.result.fields.get("test_replace") as SimpleField).value).to.be.equals("a test value"); diff --git a/tests/v2/client.spec.ts b/tests/v2/client.spec.ts index 03c130fc..7b1f869c 100644 --- a/tests/v2/client.spec.ts +++ b/tests/v2/client.spec.ts @@ -1,11 +1,11 @@ import { expect } from "chai"; import { MockAgent, setGlobalDispatcher } from "undici"; import path from "node:path"; -import { Client, PathInput, InferenceResponse } from "@/index.js"; +import { Client, PathInput } from "@/index.js"; import { MindeeHttpErrorV2 } from "@/v2/http/index.js"; import assert from "node:assert/strict"; import { RESOURCE_PATH, V2_RESOURCE_PATH } from "../index.js"; -import { LocalResponse } from "@/v2/index.js"; +import fs from "node:fs/promises"; const mockAgent = new MockAgent(); setGlobalDispatcher(mockAgent); @@ -20,36 +20,29 @@ function dummyEnvvars(): void { process.env.MINDEE_V2_API_HOST = "v2-client-host"; } -function setNockInterceptors(): void { +async function setInterceptor(statusCode: number, filePath: string): Promise { + const fileObj = await fs.readFile(filePath, { encoding: "utf-8" }); + mockPool + .intercept({ path: /.*/, method: "GET" }) + .reply(statusCode, fileObj); +} + +async function setAllInterceptors(): Promise { mockPool .intercept({ path: /.*/, method: "POST" }) .reply( 400, { status: 400, detail: "forced failure from test", title: "Bad Request", code: "400-001" } ); - - mockPool - .intercept({ path: /.*/, method: "GET" }) - .reply(200, { - job: { - id: "12345678-1234-1234-1234-123456789ABC", - model_id: "87654321-4321-4321-4321-CBA987654321", - filename: "default_sample.jpg", - alias: "dummy-alias.jpg", - created_at: "2025-07-03T14:27:58.974451", - status: "Processing", - polling_url: - "https://api-v2.mindee.net/v2/jobs/12345678-1234-1234-1234-123456789ABC", - result_url: null, - webhooks: [], - error: null, - }, - }); + await setInterceptor( + 200, + path.join(V2_RESOURCE_PATH, "job/ok_processing.json") + ); } -const fileTypesDir = path.join(RESOURCE_PATH, "file_types"); - describe("MindeeV2 - ClientV2", () => { + const fileTypesDir = path.join(RESOURCE_PATH, "file_types"); + before(() => { dummyEnvvars(); }); @@ -62,8 +55,8 @@ describe("MindeeV2 - ClientV2", () => { describe("Client configured via environment variables", () => { let client: Client; - beforeEach(() => { - setNockInterceptors(); + beforeEach(async () => { + await setAllInterceptors(); client = new Client({ apiKey: "dummy", debug: true, dispatcher: mockAgent }); }); @@ -75,12 +68,12 @@ describe("MindeeV2 - ClientV2", () => { expect(api.settings.baseHeaders["User-Agent"]).to.match(/mindee/i); }); - it("enqueue(path) rejects with MindeeHttpErrorV2 on 400", async () => { + it("enqueueInference(path) rejects with MindeeHttpErrorV2 on 400", async () => { const filePath = path.join(fileTypesDir, "receipt.jpg"); const inputDoc = new PathInput({ inputPath: filePath }); await assert.rejects( - client.enqueueInference(inputDoc, { modelId: "dummy-model", textContext: "hello" }), + client.enqueueExtraction(inputDoc, { modelId: "dummy-model", textContext: "hello" }), (error: any) => { assert.strictEqual(error instanceof MindeeHttpErrorV2, true); assert.strictEqual(error.status, 400); @@ -89,14 +82,12 @@ describe("MindeeV2 - ClientV2", () => { ); }); - it("enqueueAndParse(path) rejects with MindeeHttpErrorV2 on 400", async () => { + it("enqueueUtility(path) rejects with MindeeHttpErrorV2 on 400", async () => { const filePath = path.join(fileTypesDir, "receipt.jpg"); const inputDoc = new PathInput({ inputPath: filePath }); + await assert.rejects( - client.enqueueAndGetInference( - inputDoc, - { modelId: "dummy-model", rag: false } - ), + client.enqueueUtility(inputDoc, { modelId: "dummy-model" }), (error: any) => { assert.strictEqual(error instanceof MindeeHttpErrorV2, true); assert.strictEqual(error.status, 400); @@ -105,19 +96,19 @@ describe("MindeeV2 - ClientV2", () => { ); }); - it("loading an inference works on stored JSON fixtures", async () => { - const jsonPath = path.join( - V2_RESOURCE_PATH, - "products", - "financial_document", - "complete.json" - ); - - const localResponse = new LocalResponse(jsonPath); - const response: InferenceResponse = await localResponse.deserializeResponse(InferenceResponse); - - expect(response.inference.model.id).to.equal( - "12345678-1234-1234-1234-123456789abc" + it("enqueueAndGetInference(path) rejects with MindeeHttpErrorV2 on 400", async () => { + const filePath = path.join(fileTypesDir, "receipt.jpg"); + const inputDoc = new PathInput({ inputPath: filePath }); + await assert.rejects( + client.enqueueAndGetExtraction( + inputDoc, + { modelId: "dummy-model", rag: false } + ), + (error: any) => { + assert.strictEqual(error instanceof MindeeHttpErrorV2, true); + assert.strictEqual(error.status, 400); + return true; + } ); }); @@ -131,7 +122,7 @@ describe("MindeeV2 - ClientV2", () => { ), }); await assert.rejects( - client.enqueueInference(input, { modelId: "dummy-model" }), + client.enqueueExtraction(input, { modelId: "dummy-model" }), (error: any) => { expect(error).to.be.instanceOf(MindeeHttpErrorV2); expect(error.status).to.equal(400); @@ -141,7 +132,7 @@ describe("MindeeV2 - ClientV2", () => { ); }); - it("parseQueued(jobId) returns a fully-formed JobResponse", async () => { + it("getJob(jobId) returns a fully-formed JobResponse", async () => { const resp = await client.getJob( "12345678-1234-1234-1234-123456789ABC" ); diff --git a/tests/v2/parsing/inference.spec.ts b/tests/v2/parsing/inference.spec.ts index 13a81749..357b151c 100644 --- a/tests/v2/parsing/inference.spec.ts +++ b/tests/v2/parsing/inference.spec.ts @@ -1,7 +1,17 @@ import { expect } from "chai"; import path from "node:path"; -import { LocalResponse, InferenceResponse, RawText, RagMetadata } from "@/v2/index.js"; -import { FieldConfidence, ListField, ObjectField, SimpleField } from "@/v2/parsing/field/index.js"; +import { + LocalResponse, + ExtractionResponse, + RawText, + RagMetadata, +} from "@/v2/index.js"; +import { + FieldConfidence, + ListField, + ObjectField, + SimpleField, +} from "@/v2/parsing/inference/field/index.js"; import { promises as fs } from "node:fs"; import { Polygon } from "@/geometry/index.js"; import { V2_RESOURCE_PATH } from "../../index.js"; @@ -13,10 +23,10 @@ const standardFieldPath = path.join(inferencePath, "standard_field_types.json"); const standardFieldRstPath = path.join(inferencePath, "standard_field_types.rst"); const locationFieldPath = path.join(findocPath, "complete_with_coordinates.json"); -async function loadV2Inference(resourcePath: string): Promise { +async function loadV2Inference(resourcePath: string): Promise { const localResponse = new LocalResponse(resourcePath); await localResponse.init(); - return localResponse.deserializeResponse(InferenceResponse); + return localResponse.deserializeResponse(ExtractionResponse); } describe("MindeeV2 - Inference Response", async () => { diff --git a/tests/v2/parsing/job.spec.ts b/tests/v2/parsing/job.spec.ts index 1c6a291b..81d4f41a 100644 --- a/tests/v2/parsing/job.spec.ts +++ b/tests/v2/parsing/job.spec.ts @@ -1,4 +1,8 @@ -import { JobResponse, LocalResponse, ErrorResponse } from "@/v2/index.js"; +import { + JobResponse, + LocalResponse, + ErrorResponse, +} from "@/v2/index.js"; import path from "node:path"; import { V2_RESOURCE_PATH } from "../../index.js"; import { expect } from "chai"; diff --git a/tests/v2/parsing/localResponse.spec.ts b/tests/v2/parsing/localResponse.spec.ts index 9e423704..a91f6089 100644 --- a/tests/v2/parsing/localResponse.spec.ts +++ b/tests/v2/parsing/localResponse.spec.ts @@ -1,6 +1,6 @@ import * as fs from "node:fs/promises"; import { expect } from "chai"; -import { InferenceResponse, LocalResponse } from "@/v2/index.js"; +import { ExtractionResponse, LocalResponse } from "@/v2/index.js"; import path from "path"; import { V2_RESOURCE_PATH } from "../../index.js"; @@ -16,8 +16,8 @@ async function assertLocalResponse(localResponse: LocalResponse) { expect(localResponse.isValidHmacSignature(dummySecretKey, "invalid signature")).to.be.false; expect(localResponse.getHmacSignature(dummySecretKey)).to.eq(signature); expect(localResponse.isValidHmacSignature(dummySecretKey, signature)).to.be.true; - const inferenceResponse = await localResponse.deserializeResponse(InferenceResponse); - expect(inferenceResponse).to.be.an.instanceof(InferenceResponse); + const inferenceResponse = await localResponse.deserializeResponse(ExtractionResponse); + expect(inferenceResponse).to.be.an.instanceof(ExtractionResponse); expect(inferenceResponse.inference).to.not.be.null; } @@ -40,9 +40,23 @@ describe("MindeeV2 - Load Local Response", () => { it("should deserialize a prediction.", async () => { const fileObj = await fs.readFile(filePath, { encoding: "utf-8" }); const localResponse = new LocalResponse(fileObj); - const response = await localResponse.deserializeResponse(InferenceResponse); - expect(response).to.be.an.instanceof(InferenceResponse); + const response = await localResponse.deserializeResponse(ExtractionResponse); + expect(response).to.be.an.instanceof(ExtractionResponse); expect(JSON.stringify(response.getRawHttp())).to.eq(JSON.stringify(JSON.parse(fileObj))); }); + + it("loading an inference works on catalog model", async () => { + const jsonPath = path.join( + V2_RESOURCE_PATH, + "products", + "financial_document", + "complete.json" + ); + const localResponse = new LocalResponse(jsonPath); + const response: ExtractionResponse = await localResponse.deserializeResponse(ExtractionResponse); + expect(response.inference.model.id).to.equal( + "12345678-1234-1234-1234-123456789abc" + ); + }); }); From 30af52b7a4fc0949487405f64c9952dc2713256c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ianar=C3=A9=20s=C3=A9vi?= Date: Wed, 21 Jan 2026 17:48:00 +0100 Subject: [PATCH 2/5] simplify --- src/v2/client.ts | 42 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/v2/client.ts b/src/v2/client.ts index ebc6b1da..002e693f 100644 --- a/src/v2/client.ts +++ b/src/v2/client.ts @@ -29,8 +29,6 @@ import { CropResponse, BaseInferenceResponse } from "@/v2/parsing/inference/inde export interface ClientOptions { /** Your API key for all endpoints. */ apiKey?: string; - /** Raise an `Error` on errors. */ - throwOnError?: boolean; /** Log debug messages. */ debug?: boolean; /** Custom Dispatcher instance for the HTTP requests. */ @@ -50,15 +48,14 @@ export class Client { * @param {ClientOptions} options options for the initialization of a client. */ constructor( - { apiKey, throwOnError, debug, dispatcher }: ClientOptions = { + { apiKey, debug, dispatcher }: ClientOptions = { apiKey: undefined, - throwOnError: true, debug: false, dispatcher: undefined, } ) { this.mindeeApi = new MindeeApiV2(dispatcher, apiKey); - errorHandler.throwOnError = throwOnError ?? true; + errorHandler.throwOnError = true; logger.level = debug ?? process.env.MINDEE_DEBUG ? LOG_LEVELS["debug"] @@ -121,6 +118,15 @@ export class Client { return jobResponse; } + /** + * Retrieves an inference. + * + * @param responseType class of the inference to retrieve. + * @param inferenceId id of the queue to poll. + * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. + * @category Asynchronous + * @returns a `Promise` containing the inference. + */ async getInference( responseType: ResponseConstructor, inferenceId: string @@ -131,32 +137,6 @@ export class Client { return await this.mindeeApi.reqGetInference(responseType, inferenceId); } - /** - * Retrieves an inference. - * - * @param inferenceId id of the queue to poll. - * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. - * @category Asynchronous - * @returns a `Promise` containing a `Job`, which also contains a `Document` if the - * parsing is complete. - */ - async getExtraction(inferenceId: string): Promise { - return await this.getInference(ExtractionResponse, inferenceId); - } - - /** - * Retrieves an inference. - * - * @param inferenceId id of the queue to poll. - * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. - * @category Asynchronous - * @returns a `Promise` containing a `Job`, which also contains a `Document` if the - * parsing is complete. - */ - async getUtility(inferenceId: string): Promise { - return await this.getInference(CropResponse, inferenceId); - } - /** * Get the status of an inference that was previously enqueued. * Can be used for polling. From e60ebb874ce896de25bad59fd0b30b15e698586f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ianar=C3=A9=20s=C3=A9vi?= Date: Wed, 21 Jan 2026 18:35:54 +0100 Subject: [PATCH 3/5] params turn themselves into forms --- src/index.ts | 2 +- src/v2/client.ts | 20 +-- src/v2/client/baseParameters.ts | 18 +++ ...eParameters.ts => extractionParameters.ts} | 31 +++- src/v2/client/index.ts | 2 +- src/v2/client/utilityParameters.ts | 1 + src/v2/http/mindeeApiV2.ts | 147 +++++------------- src/v2/index.ts | 2 +- tests/v2/client.integration.ts | 8 +- tests/v2/client/inferenceParameter.spec.ts | 12 +- 10 files changed, 111 insertions(+), 132 deletions(-) rename src/v2/client/{inferenceParameters.ts => extractionParameters.ts} (66%) diff --git a/src/index.ts b/src/index.ts index 3c8958af..a1f4b588 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,7 +23,7 @@ export { JobResponse, RawText, RagMetadata, - InferenceParameters, + ExtractionParameters, DataSchema, } from "./v2/index.js"; export type { PollingOptions } from "./v2/index.js"; diff --git a/src/v2/client.ts b/src/v2/client.ts index 002e693f..9c5b0b46 100644 --- a/src/v2/client.ts +++ b/src/v2/client.ts @@ -12,7 +12,7 @@ import { } from "./parsing/index.js"; import { MindeeApiV2 } from "./http/mindeeApiV2.js"; import { MindeeHttpErrorV2 } from "./http/errors.js"; -import { InferenceParameters, UtilityParameters, ValidatedPollingOptions } from "./client/index.js"; +import { ExtractionParameters, UtilityParameters, ValidatedPollingOptions } from "./client/index.js"; import { CropResponse, BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; /** @@ -72,18 +72,18 @@ export class Client { */ async enqueueExtraction( inputSource: InputSource, - params: InferenceParameters| ConstructorParameters[0] + params: ExtractionParameters| ConstructorParameters[0] ): Promise { if (inputSource === undefined) { throw new MindeeError("An input document is required."); } - const paramsInstance = params instanceof InferenceParameters + const paramsInstance = params instanceof ExtractionParameters ? params - : new InferenceParameters(params); + : new ExtractionParameters(params); await inputSource.init(); const jobResponse = await this.mindeeApi.reqPostInferenceEnqueue( - inputSource, paramsInstance + ExtractionResponse, inputSource, paramsInstance ); if (jobResponse.job.id === undefined || jobResponse.job.id.length === 0) { logger.error(`Failed enqueueing:\n${jobResponse.getRawHttp()}`); @@ -107,7 +107,9 @@ export class Client { : new UtilityParameters(params); await inputSource.init(); - const jobResponse = await this.mindeeApi.reqPostUtilityEnqueue(inputSource, paramsInstance); + const jobResponse = await this.mindeeApi.reqPostInferenceEnqueue( + CropResponse, inputSource, paramsInstance + ); if (jobResponse.job.id === undefined || jobResponse.job.id.length === 0) { logger.error(`Failed enqueueing:\n${jobResponse.getRawHttp()}`); throw new MindeeError("Enqueueing of the document failed."); @@ -164,11 +166,11 @@ export class Client { */ async enqueueAndGetExtraction( inputSource: InputSource, - params: InferenceParameters | ConstructorParameters[0] + params: ExtractionParameters | ConstructorParameters[0] ): Promise { - const paramsInstance = params instanceof InferenceParameters + const paramsInstance = params instanceof ExtractionParameters ? params - : new InferenceParameters(params); + : new ExtractionParameters(params); const pollingOptions = paramsInstance.getValidatedPollingOptions(); diff --git a/src/v2/client/baseParameters.ts b/src/v2/client/baseParameters.ts index 57ca770c..e2f3263c 100644 --- a/src/v2/client/baseParameters.ts +++ b/src/v2/client/baseParameters.ts @@ -103,4 +103,22 @@ export abstract class BaseParameters { } return newAsyncParams as ValidatedPollingOptions; } + + /** + * Returns the form data to send to the API. + * @returns A `FormData` object. + */ + getFormData(): FormData { + const form = new FormData(); + + form.set("model_id", this.modelId); + + if (this.alias !== undefined && this.alias !== null) { + form.set("alias", this.alias); + } + if (this.webhookIds && this.webhookIds.length > 0) { + form.set("webhook_ids", this.webhookIds.join(",")); + } + return form; + } } diff --git a/src/v2/client/inferenceParameters.ts b/src/v2/client/extractionParameters.ts similarity index 66% rename from src/v2/client/inferenceParameters.ts rename to src/v2/client/extractionParameters.ts index 14f6332d..b63f83be 100644 --- a/src/v2/client/inferenceParameters.ts +++ b/src/v2/client/extractionParameters.ts @@ -20,7 +20,7 @@ import { BaseParameters, BaseParametersConstructor } from "@/v2/client/baseParam * } * }; */ -export class InferenceParameters extends BaseParameters { +export class ExtractionParameters extends BaseParameters { /** * Use Retrieval-Augmented Generation during inference. */ @@ -72,4 +72,33 @@ export class InferenceParameters extends BaseParameters { } } } + + getFormData(): FormData { + const form = new FormData(); + + form.set("model_id", this.modelId); + + if (this.rag !== undefined && this.rag !== null) { + form.set("rag", this.rag.toString()); + } + if (this.polygon !== undefined && this.polygon !== null) { + form.set("polygon", this.polygon.toString().toLowerCase()); + } + if (this.confidence !== undefined && this.confidence !== null) { + form.set("confidence", this.confidence.toString().toLowerCase()); + } + if (this.rawText !== undefined && this.rawText !== null) { + form.set("raw_text", this.rawText.toString().toLowerCase()); + } + if (this.textContext !== undefined && this.textContext !== null) { + form.set("text_context", this.textContext); + } + if (this.dataSchema !== undefined && this.dataSchema !== null) { + form.set("data_schema", this.dataSchema.toString()); + } + if (this.webhookIds && this.webhookIds.length > 0) { + form.set("webhook_ids", this.webhookIds.join(",")); + } + return form; + } } diff --git a/src/v2/client/index.ts b/src/v2/client/index.ts index 132168be..d64ca2b3 100644 --- a/src/v2/client/index.ts +++ b/src/v2/client/index.ts @@ -1,4 +1,4 @@ export { DataSchema } from "./dataSchema.js"; export type { PollingOptions, ValidatedPollingOptions } from "./pollingOptions.js"; -export { InferenceParameters } from "./inferenceParameters.js"; +export { ExtractionParameters } from "./extractionParameters.js"; export { UtilityParameters } from "./utilityParameters.js"; diff --git a/src/v2/client/utilityParameters.ts b/src/v2/client/utilityParameters.ts index eef114b6..b46676f0 100644 --- a/src/v2/client/utilityParameters.ts +++ b/src/v2/client/utilityParameters.ts @@ -18,6 +18,7 @@ import { BaseParameters, BaseParametersConstructor } from "@/v2/client/baseParam * }; */ export class UtilityParameters extends BaseParameters { + constructor(params: BaseParametersConstructor & {}) { super({ ...params }); } diff --git a/src/v2/http/mindeeApiV2.ts b/src/v2/http/mindeeApiV2.ts index bb210b8c..229777e7 100644 --- a/src/v2/http/mindeeApiV2.ts +++ b/src/v2/http/mindeeApiV2.ts @@ -1,6 +1,6 @@ import { ApiSettingsV2 } from "./apiSettingsV2.js"; import { Dispatcher } from "undici"; -import { InferenceParameters, UtilityParameters } from "@/v2/client/index.js"; +import { ExtractionParameters, UtilityParameters } from "@/v2/client/index.js"; import { BaseResponse, ErrorResponse, @@ -23,38 +23,42 @@ export class MindeeApiV2 { this.settings = new ApiSettingsV2({ dispatcher: dispatcher, apiKey: apiKey }); } - /** - * Sends a file to the extraction inference queue. - * @param inputSource Local file loaded as an input. - * @param params {InferenceParameters} parameters relating to the enqueueing options. - * @category V2 - * @throws Error if the server's response contains one. - * @returns a `Promise` containing a job response. - */ - async reqPostInferenceEnqueue( - inputSource: InputSource, params: InferenceParameters - ): Promise { - await inputSource.init(); - const result: BaseHttpResponse = await this.#inferenceEnqueuePost(inputSource, params); - if (result.data.error !== undefined) { - throw new MindeeHttpErrorV2(result.data.error); + #getSlugFromResponse( + responseClass: ResponseConstructor + ): string { + switch (responseClass as any) { + case CropResponse: + return "utilities/crop"; + case OcrResponse: + return "utilities/ocr"; + case SplitResponse: + return "utilities/split"; + case ExtractionResponse: + return "inferences"; + default: + throw new Error("Unsupported response class."); } - return this.#processResponse(result, JobResponse); } /** - * Sends a file to the utility inference queue. + * Sends a file to the extraction inference queue. + * @param responseClass Class of the inference to enqueue. * @param inputSource Local file loaded as an input. - * @param params {UtilityParameters} parameters relating to the enqueueing options. + * @param params {ExtractionParameters} parameters relating to the enqueueing options. * @category V2 * @throws Error if the server's response contains one. * @returns a `Promise` containing a job response. */ - async reqPostUtilityEnqueue( - inputSource: InputSource, params: UtilityParameters + async reqPostInferenceEnqueue( + responseClass: ResponseConstructor, + inputSource: InputSource, + params: ExtractionParameters | UtilityParameters ): Promise { await inputSource.init(); - const result: BaseHttpResponse = await this.#utilityEnqueuePost(inputSource, "crop", params); + const slug = this.#getSlugFromResponse(responseClass); + const result: BaseHttpResponse = await this.#inferenceEnqueuePost( + inputSource, slug, params + ); if (result.data.error !== undefined) { throw new MindeeHttpErrorV2(result.data.error); } @@ -64,36 +68,18 @@ export class MindeeApiV2 { /** * Requests the job of a queued document from the API. * Throws an error if the server's response contains one. - * @param responseType + * @param responseClass * @param inferenceId The document's ID in the queue. * @category Asynchronous * @returns a `Promise` containing either the parsed result, or information on the queue. */ async reqGetInference( - responseType: ResponseConstructor, + responseClass: ResponseConstructor, inferenceId: string, ): Promise { - let slug: string; - // this is disgusting, look into a more elegant way of linking the response type to the slug - switch (responseType as any) { - case CropResponse: - slug = "utilities/crop"; - break; - case OcrResponse: - slug = "utilities/ocr"; - break; - case SplitResponse: - slug = "utilities/split"; - break; - case ExtractionResponse: - slug = "inferences"; - break; - default: - slug = "inferences"; - break; - } + const slug = this.#getSlugFromResponse(responseClass); const queueResponse: BaseHttpResponse = await this.#inferenceResultReqGet(inferenceId, slug); - return this.#processResponse(queueResponse, responseType); + return this.#processResponse(queueResponse, responseClass); } /** @@ -110,7 +96,7 @@ export class MindeeApiV2 { #processResponse( result: BaseHttpResponse, - responseType: ResponseConstructor, + responseClass: ResponseConstructor, ): T { if (result.messageObj?.statusCode && (result.messageObj?.statusCode > 399 || result.messageObj?.statusCode < 200)) { if (result.data?.status !== null) { @@ -128,7 +114,7 @@ export class MindeeApiV2 { ); } try { - return new responseType(result.data); + return new responseClass(result.data); } catch (e) { logger.error(`Raised '${e}' Couldn't deserialize response object:\n${JSON.stringify(result.data)}`); throw new MindeeDeserializationError("Couldn't deserialize response object."); @@ -139,78 +125,21 @@ export class MindeeApiV2 { * Sends a document to the inference queue. * * @param inputSource Local or remote file as an input. - * @param slug Slug of the utility to enqueue. - * @param params {InferenceParameters} parameters relating to the enqueueing options. - */ - async #utilityEnqueuePost( - inputSource: InputSource, - slug: string, - params: UtilityParameters - ): Promise { - const form = new FormData(); - - form.set("model_id", params.modelId); - if (params.webhookIds && params.webhookIds.length > 0) { - form.set("webhook_ids", params.webhookIds.join(",")); - } - if (inputSource instanceof LocalInputSource) { - form.set("file", new Blob([inputSource.fileObject]), inputSource.filename); - } else { - form.set("url", (inputSource as UrlInput).url); - } - const path = `/v2/utilities/${slug}/enqueue`; - const options = { - method: "POST", - headers: this.settings.baseHeaders, - hostname: this.settings.hostname, - path: path, - body: form, - timeout: this.settings.timeout, - }; - return await sendRequestAndReadResponse(this.settings.dispatcher, options); - } - - - /** - * Sends a document to the inference queue. - * - * @param inputSource Local or remote file as an input. - * @param params {InferenceParameters} parameters relating to the enqueueing options. + * @param slug Slug of the inference to enqueue. + * @param params {ExtractionParameters} parameters relating to the enqueueing options. */ async #inferenceEnqueuePost( inputSource: InputSource, - params: InferenceParameters + slug: string, + params: ExtractionParameters | UtilityParameters ): Promise { - const form = new FormData(); - - form.set("model_id", params.modelId); - if (params.rag !== undefined && params.rag !== null) { - form.set("rag", params.rag.toString()); - } - if (params.polygon !== undefined && params.polygon !== null) { - form.set("polygon", params.polygon.toString().toLowerCase()); - } - if (params.confidence !== undefined && params.confidence !== null) { - form.set("confidence", params.confidence.toString().toLowerCase()); - } - if (params.rawText !== undefined && params.rawText !== null) { - form.set("raw_text", params.rawText.toString().toLowerCase()); - } - if (params.textContext !== undefined && params.textContext !== null) { - form.set("text_context", params.textContext); - } - if (params.dataSchema !== undefined && params.dataSchema !== null) { - form.set("data_schema", params.dataSchema.toString()); - } - if (params.webhookIds && params.webhookIds.length > 0) { - form.set("webhook_ids", params.webhookIds.join(",")); - } + const form = params.getFormData(); if (inputSource instanceof LocalInputSource) { form.set("file", new Blob([inputSource.fileObject]), inputSource.filename); } else { form.set("url", (inputSource as UrlInput).url); } - const path = "/v2/inferences/enqueue"; + const path = `/v2/${slug}/enqueue`; const options = { method: "POST", headers: this.settings.baseHeaders, diff --git a/src/v2/index.ts b/src/v2/index.ts index 6157c1fe..49a50f45 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -10,5 +10,5 @@ export { RagMetadata, ErrorResponse, } from "./parsing/index.js"; -export { InferenceParameters, DataSchema } from "./client/index.js"; +export { ExtractionParameters, DataSchema } from "./client/index.js"; export type { PollingOptions } from "./client/index.js"; diff --git a/tests/v2/client.integration.ts b/tests/v2/client.integration.ts index aec63533..62104570 100644 --- a/tests/v2/client.integration.ts +++ b/tests/v2/client.integration.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { Client, - InferenceParameters, + ExtractionParameters, PathInput, UrlInput, Base64Input, @@ -136,7 +136,7 @@ describe("MindeeV2 – Client Integration Tests", () => { it("Filled, single-page image – Base64Input - enqueueAndGetInference must succeed", async () => { const data = fs.readFileSync(sampleBase64Path, "utf8"); const source = new Base64Input({ inputString: data, filename: "receipt.jpg" }); - const params = new InferenceParameters({ + const params = new ExtractionParameters({ modelId, rag: false, rawText: false, @@ -190,7 +190,7 @@ describe("MindeeV2 – Client Integration Tests", () => { it("HTTPS URL – enqueue & get inference must succeed", async () => { const url = process.env.MINDEE_V2_SE_TESTS_BLANK_PDF_URL ?? "error-no-url-found"; const source = new UrlInput({ url }); - const params = new InferenceParameters({ + const params = new ExtractionParameters({ modelId, rag: false, rawText: false, @@ -207,7 +207,7 @@ describe("MindeeV2 – Client Integration Tests", () => { it("Data Schema Override - Overrides the data schema successfully", async () => { const source = new PathInput({ inputPath: emptyPdfPath }); - const params = new InferenceParameters({ + const params = new ExtractionParameters({ modelId, rag: false, rawText: false, diff --git a/tests/v2/client/inferenceParameter.spec.ts b/tests/v2/client/inferenceParameter.spec.ts index a3fab355..8d3e6b4c 100644 --- a/tests/v2/client/inferenceParameter.spec.ts +++ b/tests/v2/client/inferenceParameter.spec.ts @@ -1,7 +1,7 @@ import { StringDict } from "@/parsing/index.js"; import path from "path"; import { V2_RESOURCE_PATH } from "../../index.js"; -import { InferenceParameters } from "@/index.js"; +import { ExtractionParameters } from "@/index.js"; import { expect } from "chai"; import { DataSchema } from "@/index.js"; import { promises as fs } from "fs"; @@ -16,7 +16,7 @@ describe("MindeeV2 - Inference Parameter", () => { describe("Polling Options", () => { it("should provide sensible defaults", () => { - const paramsInstance = new InferenceParameters({ + const paramsInstance = new ExtractionParameters({ modelId: modelIdValue, }); expect(paramsInstance.modelId).to.equal(modelIdValue); @@ -37,22 +37,22 @@ describe("MindeeV2 - Inference Parameter", () => { }); it("shouldn't replace when unset", () => { - const params = new InferenceParameters({ + const params = new ExtractionParameters({ modelId: modelIdValue, }); expect(params.dataSchema).to.be.undefined; }); it("should equate no matter the type", () => { - const paramsDict = new InferenceParameters({ + const paramsDict = new ExtractionParameters({ modelId: modelIdValue, dataSchema: expectedDataSchemaDict, }); - const paramsString = new InferenceParameters({ + const paramsString = new ExtractionParameters({ modelId: modelIdValue, dataSchema: expectedDataSchemaString, }); - const paramsObject = new InferenceParameters({ + const paramsObject = new ExtractionParameters({ modelId: modelIdValue, dataSchema: expectedDataSchemaObject, }); From 1eb3b8d8c5697b2b565e7e70d390dbcfc7afc26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ianar=C3=A9=20s=C3=A9vi?= Date: Wed, 21 Jan 2026 20:16:02 +0100 Subject: [PATCH 4/5] start using generic inference handling --- src/v2/cli.ts | 107 +++++++----------- src/v2/client.ts | 18 +-- src/v2/parsing/inference/crop/cropResponse.ts | 2 +- .../extraction/extractionResponse.ts | 2 +- .../parsing/inference/split/splitInference.ts | 24 ++++ .../parsing/inference/split/splitResponse.ts | 6 + tests/v2/client.spec.ts | 3 +- 7 files changed, 88 insertions(+), 74 deletions(-) create mode 100644 src/v2/parsing/inference/split/splitInference.ts diff --git a/src/v2/cli.ts b/src/v2/cli.ts index c9a6eb4c..346b7537 100644 --- a/src/v2/cli.ts +++ b/src/v2/cli.ts @@ -2,7 +2,15 @@ import { Command, OptionValues } from "commander"; import { Client } from "./client.js"; import { PathInput } from "../input/index.js"; import * as console from "console"; -import { BaseInference } from "@/v2/parsing/inference/index.js"; +import { + BaseInference, ClassifyResponse, OcrResponse, SplitResponse, +} from "@/v2/parsing/inference/index.js"; +import { + BaseInferenceResponse, + CropResponse, + ExtractionResponse, + ResponseConstructor, +} from "@/v2/parsing/index.js"; const program = new Command(); @@ -19,39 +27,24 @@ function initClient(options: OptionValues): Client { } async function enqueueAndGetInference( + responseType: ResponseConstructor, inputPath: string, options: OptionValues ): Promise { const mindeeClient = initClient(options); const inputSource = new PathInput({ inputPath: inputPath }); - const response = await mindeeClient.enqueueAndGetExtraction(inputSource, { - modelId: options.model, - pollingOptions: { - initialDelaySec: 2, - delaySec: 1.5, - maxRetries: 80, + const response = await mindeeClient.enqueueAndGetInference( + responseType, + inputSource, + { + modelId: options.model, + pollingOptions: { + initialDelaySec: 2, + delaySec: 1.5, + maxRetries: 80, + } } - }); - if (!response.inference) { - throw Error("Inference could not be retrieved"); - } - printResponse(response.inference); -} - -async function enqueueAndGetUtility( - inputPath: string, - options: OptionValues -): Promise { - const mindeeClient = initClient(options); - const inputSource = new PathInput({ inputPath: inputPath }); - const response = await mindeeClient.enqueueAndGetUtility(inputSource, { - modelId: options.model, - pollingOptions: { - initialDelaySec: 2, - delaySec: 1.5, - maxRetries: 80, - } - }); + ); if (!response.inference) { throw Error("Inference could not be retrieved"); } @@ -84,42 +77,28 @@ export function cli() { .option("-d, --debug", "high verbosity mode") .option("-k, --api-key ", "your Mindee API key"); - const inferenceCmd: Command = program.command("extract") - .description("Send a file and extract results."); - addMainOptions(inferenceCmd); - - const cropCmd: Command = program.command("crop") - .description("Send a file and crop it."); - addMainOptions(cropCmd); - - const splitCmd: Command = program.command("split") - .description("Send a file and split it."); - addMainOptions(splitCmd); - - const ocrCmd: Command = program.command("ocr") - .description("Send a file and read its text content."); - addMainOptions(ocrCmd); - - const classifyCmd: Command = program.command("classify") - .description("Send a file and classify its content."); - addMainOptions(classifyCmd); - - - inferenceCmd.action(function ( - inputPath: string, - options: OptionValues, - ) { - const allOptions = { ...program.opts(), ...options }; - return enqueueAndGetInference(inputPath, allOptions); - }); - - cropCmd.action(function ( - inputPath: string, - options: OptionValues, - ) { - const allOptions = { ...program.opts(), ...options }; - return enqueueAndGetUtility(inputPath, allOptions); - }); + const inferenceTypes = [ + { name: "extract", description: "Extract data from a document.", responseType: ExtractionResponse }, + { name: "crop", description: "Crop a document.", responseType: CropResponse }, + { name: "split", description: "Split a document into pages.", responseType: SplitResponse }, + { name: "ocr", description: "Read text from a document.", responseType: OcrResponse }, + { name: "classify", description: "Classify a document.", responseType: ClassifyResponse }, + ]; + + for (const inference of inferenceTypes) { + const inferenceCmd: Command = program.command(inference.name) + .description(inference.description); + + addMainOptions(inferenceCmd); + + inferenceCmd.action(function ( + inputPath: string, + options: OptionValues, + ) { + const allOptions = { ...program.opts(), ...options }; + return enqueueAndGetInference(inference.responseType, inputPath, allOptions); + }); + } program.parse(process.argv); } diff --git a/src/v2/client.ts b/src/v2/client.ts index 9c5b0b46..fb489561 100644 --- a/src/v2/client.ts +++ b/src/v2/client.ts @@ -13,7 +13,7 @@ import { import { MindeeApiV2 } from "./http/mindeeApiV2.js"; import { MindeeHttpErrorV2 } from "./http/errors.js"; import { ExtractionParameters, UtilityParameters, ValidatedPollingOptions } from "./client/index.js"; -import { CropResponse, BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; /** * Options for the V2 Mindee Client. @@ -95,7 +95,8 @@ export class Client { return jobResponse; } - async enqueueUtility( + async enqueueInference( + responseType: ResponseConstructor, inputSource: InputSource, params: UtilityParameters | ConstructorParameters[0] ): Promise { @@ -108,7 +109,7 @@ export class Client { await inputSource.init(); const jobResponse = await this.mindeeApi.reqPostInferenceEnqueue( - CropResponse, inputSource, paramsInstance + responseType, inputSource, paramsInstance ); if (jobResponse.job.id === undefined || jobResponse.job.id.length === 0) { logger.error(`Failed enqueueing:\n${jobResponse.getRawHttp()}`); @@ -180,19 +181,22 @@ export class Client { ); } - async enqueueAndGetUtility( + async enqueueAndGetInference( + responseType: ResponseConstructor, inputSource: InputSource, params: UtilityParameters | ConstructorParameters[0] - ): Promise { + ): Promise { const paramsInstance = params instanceof UtilityParameters ? params : new UtilityParameters(params); const pollingOptions = paramsInstance.getValidatedPollingOptions(); - const jobResponse: JobResponse = await this.enqueueUtility(inputSource, params); + const jobResponse: JobResponse = await this.enqueueInference( + responseType, inputSource, paramsInstance + ); return await this.pollForInference( - CropResponse, pollingOptions, jobResponse.job.id + responseType, pollingOptions, jobResponse.job.id ); } diff --git a/src/v2/parsing/inference/crop/cropResponse.ts b/src/v2/parsing/inference/crop/cropResponse.ts index 482ffe8d..2a04dc49 100644 --- a/src/v2/parsing/inference/crop/cropResponse.ts +++ b/src/v2/parsing/inference/crop/cropResponse.ts @@ -4,7 +4,7 @@ import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceRespo export class CropResponse extends BaseInferenceResponse { /** - * The inference result for a crop utility request. + * The inference result for a crop request. */ public inference: CropInference; diff --git a/src/v2/parsing/inference/extraction/extractionResponse.ts b/src/v2/parsing/inference/extraction/extractionResponse.ts index a70f2caa..f8ab821d 100644 --- a/src/v2/parsing/inference/extraction/extractionResponse.ts +++ b/src/v2/parsing/inference/extraction/extractionResponse.ts @@ -4,7 +4,7 @@ import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceRespo export class ExtractionResponse extends BaseInferenceResponse { /** - * Inference result. + * The inference result for an extraction request. */ public inference: ExtractionInference; diff --git a/src/v2/parsing/inference/split/splitInference.ts b/src/v2/parsing/inference/split/splitInference.ts new file mode 100644 index 00000000..a195a9b9 --- /dev/null +++ b/src/v2/parsing/inference/split/splitInference.ts @@ -0,0 +1,24 @@ +import { StringDict } from "@/parsing/index.js"; +import { BaseInference } from "@/v2/parsing/inference/baseInference.js"; + +export class SplitInference extends BaseInference { + /** + * Result of a split inference. + */ + result: any; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.result = serverResponse["result"]; + } + + toString(): string { + return ( + "Inference\n" + + "#########\n" + + this.model.toString() + "\n" + + this.file.toString() + "\n" + + this.result.toString() + "\n" + ); + } +} diff --git a/src/v2/parsing/inference/split/splitResponse.ts b/src/v2/parsing/inference/split/splitResponse.ts index a0227d50..1d947724 100644 --- a/src/v2/parsing/inference/split/splitResponse.ts +++ b/src/v2/parsing/inference/split/splitResponse.ts @@ -1,9 +1,15 @@ import { StringDict } from "@/parsing/stringDict.js"; import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; +import { SplitInference } from "./splitInference.js"; export class SplitResponse extends BaseInferenceResponse { + /** + * The inference result for a split request. + */ + public inference: SplitInference; constructor(serverResponse: StringDict) { super(serverResponse); + this.inference = new SplitInference(serverResponse["inference"]); } } diff --git a/tests/v2/client.spec.ts b/tests/v2/client.spec.ts index 7b1f869c..331754cd 100644 --- a/tests/v2/client.spec.ts +++ b/tests/v2/client.spec.ts @@ -6,6 +6,7 @@ import { MindeeHttpErrorV2 } from "@/v2/http/index.js"; import assert from "node:assert/strict"; import { RESOURCE_PATH, V2_RESOURCE_PATH } from "../index.js"; import fs from "node:fs/promises"; +import { CropResponse } from "@/v2/parsing/index.js"; const mockAgent = new MockAgent(); setGlobalDispatcher(mockAgent); @@ -87,7 +88,7 @@ describe("MindeeV2 - ClientV2", () => { const inputDoc = new PathInput({ inputPath: filePath }); await assert.rejects( - client.enqueueUtility(inputDoc, { modelId: "dummy-model" }), + client.enqueueInference(CropResponse, inputDoc, { modelId: "dummy-model" }), (error: any) => { assert.strictEqual(error instanceof MindeeHttpErrorV2, true); assert.strictEqual(error.status, 400); From baaebc57a2bf7b4cb2b9c041edc4168f07325f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ianar=C3=A9=20s=C3=A9vi?= Date: Wed, 21 Jan 2026 21:38:00 +0100 Subject: [PATCH 5/5] break everything to pass the inference class instead --- src/v2/cli.ts | 26 ++++----- src/v2/client.ts | 48 ++++++--------- src/v2/http/mindeeApiV2.ts | 58 ++++++++++++++----- src/v2/parsing/index.ts | 10 +++- .../inference/baseInferenceResponse.ts | 10 +++- .../classification/classificationInference.ts | 24 ++++++++ .../classification/classificationResponse.ts | 10 ++++ .../classification/classifyResponse.ts | 9 --- src/v2/parsing/inference/crop/cropResponse.ts | 11 +--- .../extraction/extractionResponse.ts | 11 +--- src/v2/parsing/inference/index.ts | 6 +- src/v2/parsing/inference/ocr/ocrInference.ts | 24 ++++++++ src/v2/parsing/inference/ocr/ocrResponse.ts | 7 ++- .../parsing/inference/split/splitResponse.ts | 11 +--- tests/v2/client.integration.ts | 16 ++--- tests/v2/client.spec.ts | 7 ++- 16 files changed, 177 insertions(+), 111 deletions(-) create mode 100644 src/v2/parsing/inference/classification/classificationInference.ts create mode 100644 src/v2/parsing/inference/classification/classificationResponse.ts delete mode 100644 src/v2/parsing/inference/classification/classifyResponse.ts create mode 100644 src/v2/parsing/inference/ocr/ocrInference.ts diff --git a/src/v2/cli.ts b/src/v2/cli.ts index 346b7537..af9c5b8e 100644 --- a/src/v2/cli.ts +++ b/src/v2/cli.ts @@ -3,14 +3,14 @@ import { Client } from "./client.js"; import { PathInput } from "../input/index.js"; import * as console from "console"; import { - BaseInference, ClassifyResponse, OcrResponse, SplitResponse, + BaseInference, + ClassificationInference, + CropInference, + ExtractionInference, + OcrInference, + SplitInference, + InferenceResponseConstructor, } from "@/v2/parsing/inference/index.js"; -import { - BaseInferenceResponse, - CropResponse, - ExtractionResponse, - ResponseConstructor, -} from "@/v2/parsing/index.js"; const program = new Command(); @@ -27,7 +27,7 @@ function initClient(options: OptionValues): Client { } async function enqueueAndGetInference( - responseType: ResponseConstructor, + responseType: InferenceResponseConstructor, inputPath: string, options: OptionValues ): Promise { @@ -78,11 +78,11 @@ export function cli() { .option("-k, --api-key ", "your Mindee API key"); const inferenceTypes = [ - { name: "extract", description: "Extract data from a document.", responseType: ExtractionResponse }, - { name: "crop", description: "Crop a document.", responseType: CropResponse }, - { name: "split", description: "Split a document into pages.", responseType: SplitResponse }, - { name: "ocr", description: "Read text from a document.", responseType: OcrResponse }, - { name: "classify", description: "Classify a document.", responseType: ClassifyResponse }, + { name: "extract", description: "Extract data from a document.", responseType: ExtractionInference }, + { name: "crop", description: "Crop a document.", responseType: CropInference }, + { name: "split", description: "Split a document into pages.", responseType: SplitInference }, + { name: "ocr", description: "Read text from a document.", responseType: OcrInference }, + { name: "classify", description: "Classify a document.", responseType: ClassificationInference }, ]; for (const inference of inferenceTypes) { diff --git a/src/v2/client.ts b/src/v2/client.ts index fb489561..0e71be79 100644 --- a/src/v2/client.ts +++ b/src/v2/client.ts @@ -6,14 +6,15 @@ import { errorHandler } from "@/errors/handler.js"; import { LOG_LEVELS, logger } from "@/logger.js"; import { ErrorResponse, - ExtractionResponse, + ExtractionInference, JobResponse, - ResponseConstructor, + InferenceResponseConstructor, + BaseInference, + BaseInferenceResponse, } from "./parsing/index.js"; import { MindeeApiV2 } from "./http/mindeeApiV2.js"; import { MindeeHttpErrorV2 } from "./http/errors.js"; import { ExtractionParameters, UtilityParameters, ValidatedPollingOptions } from "./client/index.js"; -import { BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; /** * Options for the V2 Mindee Client. @@ -83,7 +84,7 @@ export class Client { await inputSource.init(); const jobResponse = await this.mindeeApi.reqPostInferenceEnqueue( - ExtractionResponse, inputSource, paramsInstance + ExtractionInference, inputSource, paramsInstance ); if (jobResponse.job.id === undefined || jobResponse.job.id.length === 0) { logger.error(`Failed enqueueing:\n${jobResponse.getRawHttp()}`); @@ -95,8 +96,8 @@ export class Client { return jobResponse; } - async enqueueInference( - responseType: ResponseConstructor, + async enqueueInference( + responseType: InferenceResponseConstructor, inputSource: InputSource, params: UtilityParameters | ConstructorParameters[0] ): Promise { @@ -130,10 +131,10 @@ export class Client { * @category Asynchronous * @returns a `Promise` containing the inference. */ - async getInference( - responseType: ResponseConstructor, + async getInference( + responseType: InferenceResponseConstructor, inferenceId: string - ): Promise { + ): Promise> { logger.debug( `Attempting to get inference with ID: ${inferenceId} using response type: ${responseType.name}` ); @@ -158,6 +159,7 @@ export class Client { * Send a document to an endpoint and poll the server until the result is sent or * until the maximum number of tries is reached. * + * @param responseType class of the inference to retrieve. * @param inputSource file or URL to parse. * @param params parameters relating to prediction options. * @@ -165,27 +167,11 @@ export class Client { * @category Synchronous * @returns a `Promise` containing parsing results. */ - async enqueueAndGetExtraction( - inputSource: InputSource, - params: ExtractionParameters | ConstructorParameters[0] - ): Promise { - const paramsInstance = params instanceof ExtractionParameters - ? params - : new ExtractionParameters(params); - - const pollingOptions = paramsInstance.getValidatedPollingOptions(); - - const jobResponse: JobResponse = await this.enqueueExtraction(inputSource, params); - return await this.pollForInference( - ExtractionResponse, pollingOptions, jobResponse.job.id - ); - } - - async enqueueAndGetInference( - responseType: ResponseConstructor, + async enqueueAndGetInference( + responseType: InferenceResponseConstructor, inputSource: InputSource, params: UtilityParameters | ConstructorParameters[0] - ): Promise { + ): Promise> { const paramsInstance = params instanceof UtilityParameters ? params : new UtilityParameters(params); @@ -205,11 +191,11 @@ export class Client { * until the maximum number of tries is reached. * @protected */ - protected async pollForInference( - responseType: ResponseConstructor, + protected async pollForInference( + responseType: InferenceResponseConstructor, pollingOptions: ValidatedPollingOptions, queueId: string, - ): Promise { + ): Promise> { logger.debug( `Waiting ${pollingOptions.initialDelaySec} seconds before polling.` ); diff --git a/src/v2/http/mindeeApiV2.ts b/src/v2/http/mindeeApiV2.ts index 229777e7..72f5f509 100644 --- a/src/v2/http/mindeeApiV2.ts +++ b/src/v2/http/mindeeApiV2.ts @@ -5,16 +5,26 @@ import { BaseResponse, ErrorResponse, ResponseConstructor, + InferenceResponseConstructor, JobResponse, CropResponse, OcrResponse, - SplitResponse, ExtractionResponse, BaseInferenceResponse, + SplitResponse, + ExtractionResponse, + BaseInference, ExtractionInference, } from "@/v2/parsing/index.js"; import { sendRequestAndReadResponse, BaseHttpResponse } from "@/http/apiCore.js"; import { InputSource, LocalInputSource, UrlInput } from "@/input/index.js"; import { MindeeDeserializationError } from "@/errors/index.js"; import { MindeeHttpErrorV2 } from "./errors.js"; import { logger } from "@/logger.js"; +import { + BaseInferenceResponse, + CropInference, + OcrInference, + SplitInference +} from "@/v2/parsing/inference/index.js"; + export class MindeeApiV2 { settings: ApiSettingsV2; @@ -23,23 +33,40 @@ export class MindeeApiV2 { this.settings = new ApiSettingsV2({ dispatcher: dispatcher, apiKey: apiKey }); } - #getSlugFromResponse( - responseClass: ResponseConstructor + #getSlugFromInference( + responseClass: InferenceResponseConstructor ): string { switch (responseClass as any) { - case CropResponse: + case CropInference: return "utilities/crop"; - case OcrResponse: + case OcrInference: return "utilities/ocr"; - case SplitResponse: + case SplitInference: return "utilities/split"; - case ExtractionResponse: + case ExtractionInference: return "inferences"; default: throw new Error("Unsupported response class."); } } + #getResponseClassFromInference( + inferenceClass: InferenceResponseConstructor + ): ResponseConstructor> { + switch (inferenceClass as any) { + case CropInference: + return CropResponse as any; + case OcrInference: + return OcrResponse as any; + case SplitInference: + return SplitResponse as any; + case ExtractionInference: + return ExtractionResponse as any; + default: + throw new Error("Unsupported inference class."); + } + } + /** * Sends a file to the extraction inference queue. * @param responseClass Class of the inference to enqueue. @@ -49,13 +76,13 @@ export class MindeeApiV2 { * @throws Error if the server's response contains one. * @returns a `Promise` containing a job response. */ - async reqPostInferenceEnqueue( - responseClass: ResponseConstructor, + async reqPostInferenceEnqueue( + responseClass: InferenceResponseConstructor, inputSource: InputSource, params: ExtractionParameters | UtilityParameters ): Promise { await inputSource.init(); - const slug = this.#getSlugFromResponse(responseClass); + const slug = this.#getSlugFromInference(responseClass); const result: BaseHttpResponse = await this.#inferenceEnqueuePost( inputSource, slug, params ); @@ -73,13 +100,14 @@ export class MindeeApiV2 { * @category Asynchronous * @returns a `Promise` containing either the parsed result, or information on the queue. */ - async reqGetInference( - responseClass: ResponseConstructor, + async reqGetInference( + responseClass: InferenceResponseConstructor, inferenceId: string, - ): Promise { - const slug = this.#getSlugFromResponse(responseClass); + ): Promise> { + const slug = this.#getSlugFromInference(responseClass); const queueResponse: BaseHttpResponse = await this.#inferenceResultReqGet(inferenceId, slug); - return this.#processResponse(queueResponse, responseClass); + const actualResponseClass = this.#getResponseClassFromInference(responseClass); + return this.#processResponse(queueResponse, actualResponseClass) as BaseInferenceResponse; } /** diff --git a/src/v2/parsing/index.ts b/src/v2/parsing/index.ts index d440caa2..85e829ca 100644 --- a/src/v2/parsing/index.ts +++ b/src/v2/parsing/index.ts @@ -9,17 +9,23 @@ export { JobWebhook } from "./job/index.js"; export { + BaseInference, + BaseInferenceResponse, InferenceFile, InferenceModel, ExtractionInference, ExtractionActiveOptions, ExtractionResponse, ExtractionResult, - ClassifyResponse, + ClassificationResponse, + ClassificationInference, CropResponse, + CropInference, OcrResponse, + OcrInference, SplitResponse, + SplitInference, } from "./inference/index.js"; export { RawText, RagMetadata } from "./inference/field/index.js"; export type { ResponseConstructor, BaseResponse } from "./baseResponse.js"; -export type { BaseInferenceResponse } from "./inference/index.js"; +export type { InferenceResponseConstructor } from "./inference/index.js"; diff --git a/src/v2/parsing/inference/baseInferenceResponse.ts b/src/v2/parsing/inference/baseInferenceResponse.ts index c87dcc10..25a93d08 100644 --- a/src/v2/parsing/inference/baseInferenceResponse.ts +++ b/src/v2/parsing/inference/baseInferenceResponse.ts @@ -2,17 +2,21 @@ import { StringDict } from "@/parsing/stringDict.js"; import { BaseResponse } from "@/v2/parsing/baseResponse.js"; import { BaseInference } from "./baseInference.js"; -export abstract class BaseInferenceResponse extends BaseResponse { +export abstract class BaseInferenceResponse extends BaseResponse { /** * The inference result for a crop utility request. */ - public inference: BaseInference; + public inference: T; /** * @param serverResponse JSON response from the server. */ protected constructor(serverResponse: StringDict) { super(serverResponse); - this.inference = serverResponse["inference"]; + this.inference = this.setInferenceType(serverResponse["inference"]); } + + protected abstract setInferenceType(inferenceResponse: StringDict): T; } + +export type InferenceResponseConstructor = new (serverResponse: StringDict) => T; diff --git a/src/v2/parsing/inference/classification/classificationInference.ts b/src/v2/parsing/inference/classification/classificationInference.ts new file mode 100644 index 00000000..916c71dd --- /dev/null +++ b/src/v2/parsing/inference/classification/classificationInference.ts @@ -0,0 +1,24 @@ +import { StringDict } from "@/parsing/index.js"; +import { BaseInference } from "@/v2/parsing/inference/baseInference.js"; + +export class ClassificationInference extends BaseInference { + /** + * Result of a classification inference. + */ + result: any; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.result = serverResponse["result"]; + } + + toString(): string { + return ( + "Inference\n" + + "#########\n" + + this.model.toString() + "\n" + + this.file.toString() + "\n" + + this.result.toString() + "\n" + ); + } +} diff --git a/src/v2/parsing/inference/classification/classificationResponse.ts b/src/v2/parsing/inference/classification/classificationResponse.ts new file mode 100644 index 00000000..e348e76e --- /dev/null +++ b/src/v2/parsing/inference/classification/classificationResponse.ts @@ -0,0 +1,10 @@ +import { StringDict } from "@/parsing/stringDict.js"; +import { BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; +import { ClassificationInference } from "./classificationInference.js"; + +export class ClassificationResponse extends BaseInferenceResponse { + + setInferenceType(inferenceResponse: StringDict): ClassificationInference { + return new ClassificationInference(inferenceResponse); + } +} diff --git a/src/v2/parsing/inference/classification/classifyResponse.ts b/src/v2/parsing/inference/classification/classifyResponse.ts deleted file mode 100644 index a3112f46..00000000 --- a/src/v2/parsing/inference/classification/classifyResponse.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { StringDict } from "@/parsing/stringDict.js"; -import { BaseInferenceResponse } from "@/v2/parsing/inference/index.js"; - -export class ClassifyResponse extends BaseInferenceResponse { - - constructor(serverResponse: StringDict) { - super(serverResponse); - } -} diff --git a/src/v2/parsing/inference/crop/cropResponse.ts b/src/v2/parsing/inference/crop/cropResponse.ts index 2a04dc49..3f8f1941 100644 --- a/src/v2/parsing/inference/crop/cropResponse.ts +++ b/src/v2/parsing/inference/crop/cropResponse.ts @@ -2,14 +2,9 @@ import { StringDict } from "@/parsing/stringDict.js"; import { CropInference } from "./cropInference.js"; import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; -export class CropResponse extends BaseInferenceResponse { - /** - * The inference result for a crop request. - */ - public inference: CropInference; +export class CropResponse extends BaseInferenceResponse { - constructor(serverResponse: StringDict) { - super(serverResponse); - this.inference = new CropInference(serverResponse["inference"]); + setInferenceType(inferenceResponse: StringDict): CropInference { + return new CropInference(inferenceResponse); } } diff --git a/src/v2/parsing/inference/extraction/extractionResponse.ts b/src/v2/parsing/inference/extraction/extractionResponse.ts index f8ab821d..e61e836c 100644 --- a/src/v2/parsing/inference/extraction/extractionResponse.ts +++ b/src/v2/parsing/inference/extraction/extractionResponse.ts @@ -2,14 +2,9 @@ import { ExtractionInference } from "./extractionInference.js"; import { StringDict } from "@/parsing/stringDict.js"; import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; -export class ExtractionResponse extends BaseInferenceResponse { - /** - * The inference result for an extraction request. - */ - public inference: ExtractionInference; +export class ExtractionResponse extends BaseInferenceResponse { - constructor(serverResponse: StringDict) { - super(serverResponse); - this.inference = new ExtractionInference(serverResponse["inference"]); + setInferenceType(inferenceResponse: StringDict): ExtractionInference { + return new ExtractionInference(inferenceResponse); } } diff --git a/src/v2/parsing/inference/index.ts b/src/v2/parsing/inference/index.ts index 6070f10f..ee712816 100644 --- a/src/v2/parsing/inference/index.ts +++ b/src/v2/parsing/inference/index.ts @@ -3,6 +3,7 @@ export { InferenceFile } from "./inferenceFile.js"; export { InferenceModel } from "./inferenceModel.js"; export { BaseInference } from "./baseInference.js"; export { BaseInferenceResponse } from "./baseInferenceResponse.js"; +export type { InferenceResponseConstructor } from "./baseInferenceResponse.js"; // Fields export * as field from "./field/index.js"; @@ -14,7 +15,8 @@ export { ExtractionResponse } from "./extraction/extractionResponse.js"; export { ExtractionResult } from "./extraction/extractionResult.js"; // Classification -export { ClassifyResponse } from "./classification/classifyResponse.js"; +export { ClassificationResponse } from "./classification/classificationResponse.js"; +export { ClassificationInference } from "./classification/classificationInference.js"; // Crop export { CropInference } from "./crop/cropInference.js"; @@ -24,7 +26,9 @@ export { CropResult } from "./crop/cropResult.js"; // OCR export { OcrResponse } from "./ocr/ocrResponse.js"; +export { OcrInference } from "./ocr/ocrInference.js"; // Split export { SplitResponse } from "./split/splitResponse.js"; +export { SplitInference } from "./split/splitInference.js"; diff --git a/src/v2/parsing/inference/ocr/ocrInference.ts b/src/v2/parsing/inference/ocr/ocrInference.ts new file mode 100644 index 00000000..c767fccf --- /dev/null +++ b/src/v2/parsing/inference/ocr/ocrInference.ts @@ -0,0 +1,24 @@ +import { StringDict } from "@/parsing/index.js"; +import { BaseInference } from "@/v2/parsing/inference/baseInference.js"; + +export class OcrInference extends BaseInference { + /** + * Result of an OCR inference. + */ + result: any; + + constructor(serverResponse: StringDict) { + super(serverResponse); + this.result = serverResponse["result"]; + } + + toString(): string { + return ( + "Inference\n" + + "#########\n" + + this.model.toString() + "\n" + + this.file.toString() + "\n" + + this.result.toString() + "\n" + ); + } +} diff --git a/src/v2/parsing/inference/ocr/ocrResponse.ts b/src/v2/parsing/inference/ocr/ocrResponse.ts index 5e37cf5c..f0c8e452 100644 --- a/src/v2/parsing/inference/ocr/ocrResponse.ts +++ b/src/v2/parsing/inference/ocr/ocrResponse.ts @@ -1,9 +1,10 @@ import { StringDict } from "@/parsing/stringDict.js"; import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; +import { OcrInference } from "./ocrInference.js"; -export class OcrResponse extends BaseInferenceResponse { +export class OcrResponse extends BaseInferenceResponse { - constructor(serverResponse: StringDict) { - super(serverResponse); + setInferenceType(inferenceResponse: StringDict): OcrInference { + return new OcrInference(inferenceResponse); } } diff --git a/src/v2/parsing/inference/split/splitResponse.ts b/src/v2/parsing/inference/split/splitResponse.ts index 1d947724..3336d93a 100644 --- a/src/v2/parsing/inference/split/splitResponse.ts +++ b/src/v2/parsing/inference/split/splitResponse.ts @@ -2,14 +2,9 @@ import { StringDict } from "@/parsing/stringDict.js"; import { BaseInferenceResponse } from "@/v2/parsing/inference/baseInferenceResponse.js"; import { SplitInference } from "./splitInference.js"; -export class SplitResponse extends BaseInferenceResponse { - /** - * The inference result for a split request. - */ - public inference: SplitInference; +export class SplitResponse extends BaseInferenceResponse { - constructor(serverResponse: StringDict) { - super(serverResponse); - this.inference = new SplitInference(serverResponse["inference"]); + setInferenceType(inferenceResponse: StringDict): SplitInference { + return new SplitInference(inferenceResponse); } } diff --git a/tests/v2/client.integration.ts b/tests/v2/client.integration.ts index 62104570..0ea135b1 100644 --- a/tests/v2/client.integration.ts +++ b/tests/v2/client.integration.ts @@ -80,7 +80,7 @@ describe("MindeeV2 – Client Integration Tests", () => { webhookIds: [], alias: "ts_integration_empty_multiple" }; - const response = await client.enqueueAndGetExtraction(source, params); + const response = await client.enqueueAndGetInference(source, params); expect(response).to.exist; expect(response.inference).to.be.instanceOf(ExtractionInference); @@ -108,7 +108,7 @@ describe("MindeeV2 – Client Integration Tests", () => { alias: "ts_integration_binary_filled_single" }; - const response = await client.enqueueAndGetExtraction(source, params); + const response = await client.enqueueAndGetInference(source, params); expect(response.inference).to.be.instanceOf(ExtractionInference); const inference: ExtractionInference = response.inference; @@ -146,7 +146,7 @@ describe("MindeeV2 – Client Integration Tests", () => { alias: "ts_integration_base64_filled_single" }); - const response = await client.enqueueAndGetExtraction(source, params); + const response = await client.enqueueAndGetInference(source, params); expect(response.inference).to.be.instanceOf(ExtractionInference); const inference: ExtractionInference = response.inference; @@ -199,8 +199,9 @@ describe("MindeeV2 – Client Integration Tests", () => { webhookIds: [], alias: "ts_integration_url_source" }); - const response: ExtractionResponse = await client.enqueueAndGetExtraction(source, params); - + const response: ExtractionResponse = await client.enqueueAndGetInference( + ExtractionInference, source, params + ); expect(response).to.exist; expect(response.inference).to.be.instanceOf(ExtractionInference); }).timeout(60000); @@ -217,8 +218,9 @@ describe("MindeeV2 – Client Integration Tests", () => { dataSchema: dataSchemaReplace, alias: "ts_integration_data_schema_replace" }); - const response = await client.enqueueAndGetExtraction(source, params); - + const response = await client.enqueueAndGetInference( + ExtractionInference, source, params + ); expect(response).to.exist; expect(response.inference).to.be.instanceOf(ExtractionInference); expect(response.inference.result.fields.get("test_replace")).to.exist; diff --git a/tests/v2/client.spec.ts b/tests/v2/client.spec.ts index 331754cd..bdb43af7 100644 --- a/tests/v2/client.spec.ts +++ b/tests/v2/client.spec.ts @@ -6,7 +6,7 @@ import { MindeeHttpErrorV2 } from "@/v2/http/index.js"; import assert from "node:assert/strict"; import { RESOURCE_PATH, V2_RESOURCE_PATH } from "../index.js"; import fs from "node:fs/promises"; -import { CropResponse } from "@/v2/parsing/index.js"; +import { CropInference, ExtractionInference } from "@/v2/parsing/index.js"; const mockAgent = new MockAgent(); setGlobalDispatcher(mockAgent); @@ -88,7 +88,7 @@ describe("MindeeV2 - ClientV2", () => { const inputDoc = new PathInput({ inputPath: filePath }); await assert.rejects( - client.enqueueInference(CropResponse, inputDoc, { modelId: "dummy-model" }), + client.enqueueInference(CropInference, inputDoc, { modelId: "dummy-model" }), (error: any) => { assert.strictEqual(error instanceof MindeeHttpErrorV2, true); assert.strictEqual(error.status, 400); @@ -101,7 +101,8 @@ describe("MindeeV2 - ClientV2", () => { const filePath = path.join(fileTypesDir, "receipt.jpg"); const inputDoc = new PathInput({ inputPath: filePath }); await assert.rejects( - client.enqueueAndGetExtraction( + client.enqueueAndGetInference( + ExtractionInference, inputDoc, { modelId: "dummy-model", rag: false } ),