diff --git a/src/v2/client/baseParameters.ts b/src/v2/client/baseParameters.ts index 403d45577..5d75a8f01 100644 --- a/src/v2/client/baseParameters.ts +++ b/src/v2/client/baseParameters.ts @@ -11,7 +11,7 @@ export interface BaseParametersConstructor { } /** - * Parameters accepted by the asynchronous **inference** v2 endpoint. + * Parameters accepted by all v2 products. * * All fields are optional except `modelId`. * @@ -22,10 +22,6 @@ export interface BaseParametersConstructor { * rag: true, * alias: "YOUR_ALIAS", * webhookIds: ["YOUR_WEBHOOK_ID_1", "YOUR_WEBHOOK_ID_2"], - * pollingOptions: { - * initialDelaySec: 2, - * delaySec: 1.5, - * } * }; */ export abstract class BaseParameters { diff --git a/src/v2/client/index.ts b/src/v2/client/index.ts index 9793b7525..4b5dd6a3b 100644 --- a/src/v2/client/index.ts +++ b/src/v2/client/index.ts @@ -1,3 +1,6 @@ export { PollingOptions } from "./pollingOptions.js"; -export type { PollingOptionsConstructor } from "./pollingOptions.js"; +export type { + PollingOptionsConstructor, + TimerOptions, +} from "./pollingOptions.js"; export { BaseParameters } from "./baseParameters.js"; diff --git a/src/v2/client/pollingOptions.ts b/src/v2/client/pollingOptions.ts index 89a42f7e0..86fbdf401 100644 --- a/src/v2/client/pollingOptions.ts +++ b/src/v2/client/pollingOptions.ts @@ -22,8 +22,8 @@ const minRetries = 2; * Parameters for the internal polling loop in `enqueueAndGetInference()`. * * Default behavior: - * - `initialDelaySec` = 2s - * - `delaySec` = 1.5s + * - `initialDelaySec` = 2 + * - `delaySec` = 1.5 * - `maxRetries` = 80 * * Validation rules: @@ -32,17 +32,21 @@ const minRetries = 2; * - `maxRetries` >= 2 * * The `initialTimerOptions` and `recurringTimerOptions` objects let you pass an - * `AbortSignal` or make the timer `unref`-ed to the `setTimeout()`. + * `AbortSignal` or make the timer `ref`-ed to the `setTimeout()`. * * @category ClientV2 * @example - * const params = { + * ``` + * const pollingOptions = { * initialDelaySec: 4, * delaySec: 2, * maxRetries: 50 * }; * - * const inference = await client.enqueueAndGetInference(inputDoc, params); + * const inference = await client.enqueueAndGetInference( + * inputDoc, params, pollingOptions + * ); + * ``` */ export class PollingOptions { /** Number of seconds to wait *before the first poll*. */ @@ -52,15 +56,9 @@ export class PollingOptions { /** Maximum number of polling attempts (including the first one). */ maxRetries: number; /** Options passed to the initial `setTimeout()`. */ - initialTimerOptions?: { - ref?: boolean, - signal?: AbortSignal - }; + initialTimerOptions?: TimerOptions; /** Options passed to every recurring `setTimeout()`. */ - recurringTimerOptions?: { - ref?: boolean, - signal?: AbortSignal - }; + recurringTimerOptions?: TimerOptions; constructor(params?: PollingOptionsConstructor) { if (!params) { diff --git a/src/v2/http/apiSettingsV2.ts b/src/v2/http/apiSettings.ts similarity index 93% rename from src/v2/http/apiSettingsV2.ts rename to src/v2/http/apiSettings.ts index 1fb295d79..8449c150b 100644 --- a/src/v2/http/apiSettingsV2.ts +++ b/src/v2/http/apiSettings.ts @@ -6,7 +6,7 @@ const API_V2_KEY_ENVVAR_NAME: string = "MINDEE_V2_API_KEY"; const API_V2_HOST_ENVVAR_NAME: string = "MINDEE_V2_API_HOST"; const DEFAULT_MINDEE_API_HOST: string = "api-v2.mindee.net"; -export class ApiSettingsV2 extends BaseSettings { +export class ApiSettings extends BaseSettings { baseHeaders: Record; constructor({ @@ -25,6 +25,7 @@ export class ApiSettingsV2 extends BaseSettings { "User-Agent": this.getUserAgent(), Authorization: `${this.apiKey}`, }; + /* eslint-enable @typescript-eslint/naming-convention */ } protected apiKeyFromEnv(): string { diff --git a/src/v2/http/errors.ts b/src/v2/http/errors.ts index 9c8f05c6c..cd5e85b37 100644 --- a/src/v2/http/errors.ts +++ b/src/v2/http/errors.ts @@ -1,6 +1,9 @@ import { ErrorDetails, ErrorItem, ErrorResponse } from "@/v2/parsing/index.js"; import { MindeeError } from "@/errors/index.js"; +/** + * HTTP error returned by the API. + */ export class MindeeHttpErrorV2 extends MindeeError implements ErrorDetails { public status: number; public detail: string; diff --git a/src/v2/http/mindeeApiV2.ts b/src/v2/http/mindeeApiV2.ts index f251c7f04..db75a16b7 100644 --- a/src/v2/http/mindeeApiV2.ts +++ b/src/v2/http/mindeeApiV2.ts @@ -1,4 +1,4 @@ -import { ApiSettingsV2 } from "./apiSettingsV2.js"; +import { ApiSettings } from "./apiSettings.js"; import { Dispatcher } from "undici"; import { BaseParameters } from "@/v2/client/index.js"; import { @@ -16,10 +16,10 @@ import { BaseProduct } from "@/v2/product/baseProduct.js"; export class MindeeApiV2 { - settings: ApiSettingsV2; + settings: ApiSettings; constructor(dispatcher?: Dispatcher, apiKey?: string) { - this.settings = new ApiSettingsV2({ dispatcher: dispatcher, apiKey: apiKey }); + this.settings = new ApiSettings({ dispatcher: dispatcher, apiKey: apiKey }); } /** @@ -80,7 +80,10 @@ export class MindeeApiV2 { result: BaseHttpResponse, responseClass: ResponseConstructor, ): T { - if (result.messageObj?.statusCode && (result.messageObj?.statusCode > 399 || result.messageObj?.statusCode < 200)) { + if ( + result.messageObj?.statusCode + && (result.messageObj?.statusCode > 399 || result.messageObj?.statusCode < 200) + ) { if (result.data?.status !== null) { throw new MindeeHttpErrorV2(new ErrorResponse(result.data)); } @@ -98,7 +101,9 @@ export class MindeeApiV2 { try { return new responseClass(result.data); } catch (e) { - logger.error(`Raised '${e}' Couldn't deserialize response object:\n${JSON.stringify(result.data)}`); + logger.error( + `Raised '${e}' Couldn't deserialize response object:\n${JSON.stringify(result.data)}` + ); throw new MindeeDeserializationError("Couldn't deserialize response object."); } } diff --git a/src/v2/index.ts b/src/v2/index.ts index 90fc52b2a..cb5810494 100644 --- a/src/v2/index.ts +++ b/src/v2/index.ts @@ -7,4 +7,5 @@ export { ErrorResponse, LocalResponse, } from "./parsing/index.js"; -export type { PollingOptions, BaseParameters } from "./client/index.js"; +export type { BaseParameters, TimerOptions } from "./client/index.js"; +export { PollingOptions } from "./client/index.js"; diff --git a/src/v2/parsing/inference/baseInference.ts b/src/v2/parsing/inference/baseInference.ts index 9c9cac402..d0aacae40 100644 --- a/src/v2/parsing/inference/baseInference.ts +++ b/src/v2/parsing/inference/baseInference.ts @@ -1,12 +1,17 @@ +import { StringDict } from "@/parsing/index.js"; import { InferenceModel } from "./inferenceModel.js"; import { InferenceFile } from "./inferenceFile.js"; -import { StringDict } from "@/parsing/index.js"; +import { InferenceJob } from "./inferenceJob.js"; export abstract class BaseInference { /** * Model info for the inference. */ public model: InferenceModel; + /** + * Job the inference belongs to. + */ + public job: InferenceJob; /** * File info for the inference. */ @@ -18,6 +23,7 @@ export abstract class BaseInference { protected constructor(serverResponse: StringDict) { this.id = serverResponse["id"]; + this.job = new InferenceJob(serverResponse["job"]); this.model = new InferenceModel(serverResponse["model"]); this.file = new InferenceFile(serverResponse["file"]); } diff --git a/src/v2/parsing/inference/inferenceJob.ts b/src/v2/parsing/inference/inferenceJob.ts new file mode 100644 index 000000000..120177351 --- /dev/null +++ b/src/v2/parsing/inference/inferenceJob.ts @@ -0,0 +1,20 @@ +import { StringDict } from "@/parsing/stringDict.js"; + +export class InferenceJob { + /** + * UUID of the Job. + */ + public id: string; + + constructor(serverResponse: StringDict) { + this.id = serverResponse["id"]; + } + + toString () { + return( + "Job\n" + + "===\n" + + `:ID: ${this.id}\n` + ); + } +} diff --git a/src/v2/product/baseProduct.ts b/src/v2/product/baseProduct.ts index 981779cd8..649c372d4 100644 --- a/src/v2/product/baseProduct.ts +++ b/src/v2/product/baseProduct.ts @@ -1,6 +1,11 @@ import { BaseParameters } from "@/v2/client/index.js"; import { ResponseConstructor } from "@/v2/parsing/index.js"; +/** + * Base class for all V2 product definitions. + * + * Child classes are passed to the Client when making requests. + */ export abstract class BaseProduct { static get parametersClass(): new (...args: any[]) => BaseParameters { throw new Error("Must define static parameters property"); diff --git a/src/v2/product/classification/classification.ts b/src/v2/product/classification/classification.ts index dcef46268..59c05eecf 100644 --- a/src/v2/product/classification/classification.ts +++ b/src/v2/product/classification/classification.ts @@ -2,6 +2,9 @@ import { ClassificationResponse } from "./classificationResponse.js"; import { ClassificationParameters } from "./params/index.js"; import { BaseProduct } from "@/v2/product/baseProduct.js"; +/** + * Automatically sort any image or scanned document into categories. + */ export class Classification extends BaseProduct { static get parametersClass() { return ClassificationParameters; diff --git a/src/v2/product/crop/crop.ts b/src/v2/product/crop/crop.ts index aaf1ade05..46dbf745d 100644 --- a/src/v2/product/crop/crop.ts +++ b/src/v2/product/crop/crop.ts @@ -2,6 +2,9 @@ import { CropResponse } from "./cropResponse.js"; import { CropParameters } from "./params/index.js"; import { BaseProduct } from "@/v2/product/baseProduct.js"; +/** + * Identify the borders of documents on each page, matching each one to a category. + */ export class Crop extends BaseProduct { static get parametersClass() { return CropParameters; diff --git a/src/v2/product/extraction/extraction.ts b/src/v2/product/extraction/extraction.ts index 96febbc2d..89eacc92e 100644 --- a/src/v2/product/extraction/extraction.ts +++ b/src/v2/product/extraction/extraction.ts @@ -2,6 +2,9 @@ import { ExtractionResponse } from "./extractionResponse.js"; import { ExtractionParameters } from "./params/index.js"; import { BaseProduct } from "@/v2/product/baseProduct.js"; +/** + * Automatically extract structured data from any image or scanned document. + */ export class Extraction extends BaseProduct { static get parametersClass() { return ExtractionParameters; diff --git a/src/v2/product/ocr/ocr.ts b/src/v2/product/ocr/ocr.ts index adce9d1c9..733943108 100644 --- a/src/v2/product/ocr/ocr.ts +++ b/src/v2/product/ocr/ocr.ts @@ -2,6 +2,9 @@ import { OcrResponse } from "./ocrResponse.js"; import { OcrParameters } from "./params/index.js"; import { BaseProduct } from "@/v2/product/baseProduct.js"; +/** + * Extract raw text (OCR) from any image or scanned document. + */ export class Ocr extends BaseProduct { static get parametersClass() { return OcrParameters; diff --git a/src/v2/product/split/split.ts b/src/v2/product/split/split.ts index 8933c863e..50816b1d4 100644 --- a/src/v2/product/split/split.ts +++ b/src/v2/product/split/split.ts @@ -2,6 +2,9 @@ import { SplitResponse } from "./splitResponse.js"; import { SplitParameters } from "./params/index.js"; import { BaseProduct } from "@/v2/product/baseProduct.js"; +/** + * Break a multipage source file into separate documents, associating a class for each one. + */ export class Split extends BaseProduct { static get parametersClass() { return SplitParameters; diff --git a/tests/v2/product/classification.spec.ts b/tests/v2/product/classification.spec.ts index f2acef9e8..cfa3a8b9d 100644 --- a/tests/v2/product/classification.spec.ts +++ b/tests/v2/product/classification.spec.ts @@ -13,7 +13,12 @@ describe("MindeeV2 - Classification Response", async () => { ClassificationResponse, path.join(V2_PRODUCT_PATH, "classification", "classification_single.json") ); - const classification = response.inference.result.classification; + const inference = response.inference; + + const model = inference.model; + assert.notStrictEqual(model, undefined); + + const classification = inference.result.classification; assert.strictEqual(classification.documentType, "invoice"); }); }); diff --git a/tests/v2/product/crop.spec.ts b/tests/v2/product/crop.spec.ts index 847222ece..6540ac0cc 100644 --- a/tests/v2/product/crop.spec.ts +++ b/tests/v2/product/crop.spec.ts @@ -13,17 +13,21 @@ describe("MindeeV2 - Crop Response", async () => { crop.CropResponse, path.join(V2_PRODUCT_PATH, "crop", "crop_single.json") ); + const inference = response.inference; + // Validate inference metadata - assert.strictEqual(response.inference.id, "12345678-1234-1234-1234-123456789abc"); - assert.strictEqual(response.inference.model.id, "test-model-id"); + assert.strictEqual(inference.id, "12345678-1234-1234-1234-123456789abc"); + assert.strictEqual(inference.model.id, "test-model-id"); + + assert.strictEqual(inference.job.id, "12345678-1234-1234-1234-jobid1234567"); // Validate file metadata - assert.strictEqual(response.inference.file.name, "sample.jpeg"); - assert.strictEqual(response.inference.file.pageCount, 1); - assert.strictEqual(response.inference.file.mimeType, "image/jpeg"); + assert.strictEqual(inference.file.name, "sample.jpeg"); + assert.strictEqual(inference.file.pageCount, 1); + assert.strictEqual(inference.file.mimeType, "image/jpeg"); // Validate crops - const crops: crop.CropItem[] = response.inference.result.crops; + const crops: crop.CropItem[] = inference.result.crops; assert.ok(Array.isArray(crops)); assert.strictEqual(crops.length, 1); @@ -49,16 +53,21 @@ describe("MindeeV2 - Crop Response", async () => { crop.CropResponse, path.join(V2_PRODUCT_PATH, "crop", "crop_multiple.json") ); + const inference = response.inference; + + const job = inference.job; + assert.strictEqual(job.id, "12345678-1234-1234-1234-jobid1234567"); + // Validate inference metadata - assert.strictEqual(response.inference.id, "12345678-1234-1234-1234-123456789abc"); - assert.strictEqual(response.inference.model.id, "test-model-id"); + assert.strictEqual(inference.id, "12345678-1234-1234-1234-123456789abc"); + assert.strictEqual(inference.model.id, "test-model-id"); // Validate file metadata - assert.strictEqual(response.inference.file.name, "default_sample.jpg"); - assert.strictEqual(response.inference.file.pageCount, 1); - assert.strictEqual(response.inference.file.mimeType, "image/jpeg"); + assert.strictEqual(inference.file.name, "default_sample.jpg"); + assert.strictEqual(inference.file.pageCount, 1); + assert.strictEqual(inference.file.mimeType, "image/jpeg"); - const crops: crop.CropItem[] = response.inference.result.crops; + const crops: crop.CropItem[] = inference.result.crops; assert.ok(Array.isArray(crops)); assert.strictEqual(crops.length, 2); diff --git a/tests/v2/product/extraction.spec.ts b/tests/v2/product/extraction.spec.ts index fb977589f..08c187da6 100644 --- a/tests/v2/product/extraction.spec.ts +++ b/tests/v2/product/extraction.spec.ts @@ -67,6 +67,9 @@ describe("MindeeV2 - Extraction Response", async () => { assert.notStrictEqual(inference, undefined); assert.strictEqual(inference.model.id, "12345678-1234-1234-1234-123456789abc"); + const job = inference.job; + assert.strictEqual(job.id, "12345678-1234-1234-1234-jobid1234567"); + const model = inference.model; assert.notStrictEqual(model, undefined); assert.strictEqual(model.id, "12345678-1234-1234-1234-123456789abc"); @@ -169,6 +172,10 @@ describe("MindeeV2 - Extraction Response", async () => { const response = await loadV2Response( ExtractionResponse, standardFieldPath ); + + const job = response.inference.job; + assert.strictEqual(job.id, "12345678-1234-1234-1234-jobid1234567"); + const fields = response.inference.result.fields; assert.ok(fields.get("field_simple_string") instanceof SimpleField); diff --git a/tests/v2/product/ocr.spec.ts b/tests/v2/product/ocr.spec.ts index 0c56f6af0..b5af66e4e 100644 --- a/tests/v2/product/ocr.spec.ts +++ b/tests/v2/product/ocr.spec.ts @@ -14,17 +14,19 @@ describe("MindeeV2 - OCR Response", async () => { ocr.OcrResponse, path.join(V2_PRODUCT_PATH, "ocr", "ocr_single.json") ); + const inference = response.inference; + // Validate inference metadata - assert.strictEqual(response.inference.id, "12345678-1234-1234-1234-123456789abc"); - assert.strictEqual(response.inference.model.id, "test-model-id"); + assert.strictEqual(inference.id, "12345678-1234-1234-1234-123456789abc"); + assert.strictEqual(inference.model.id, "test-model-id"); // Validate file metadata - assert.strictEqual(response.inference.file.name, "default_sample.jpg"); - assert.strictEqual(response.inference.file.pageCount, 1); - assert.strictEqual(response.inference.file.mimeType, "image/jpeg"); + assert.strictEqual(inference.file.name, "default_sample.jpg"); + assert.strictEqual(inference.file.pageCount, 1); + assert.strictEqual(inference.file.mimeType, "image/jpeg"); // Validate pages - const pages: ocr.OcrPage[] = response.inference.result.pages; + const pages: ocr.OcrPage[] = inference.result.pages; assert.ok(Array.isArray(pages)); assert.strictEqual(pages.length, 1); @@ -50,7 +52,15 @@ describe("MindeeV2 - OCR Response", async () => { ocr.OcrResponse, path.join(V2_PRODUCT_PATH, "ocr", "ocr_multiple.json") ); - const pages: ocr.OcrPage[] = response.inference.result.pages; + const inference = response.inference; + + const job = inference.job; + assert.strictEqual(job.id, "12345678-1234-1234-1234-jobid1234567"); + + const model = inference.model; + assert.notStrictEqual(model, undefined); + + const pages: ocr.OcrPage[] = inference.result.pages; assert.ok(Array.isArray(pages)); assert.strictEqual(pages.length, 3); diff --git a/tests/v2/product/split.spec.ts b/tests/v2/product/split.spec.ts index 3ae78eba6..928637c51 100644 --- a/tests/v2/product/split.spec.ts +++ b/tests/v2/product/split.spec.ts @@ -13,7 +13,12 @@ describe("MindeeV2 - Split Response", async () => { split.SplitResponse, path.join(V2_PRODUCT_PATH, "split", "split_single.json") ); - const splits: split.SplitRange[] = response.inference.result.splits; + const inference = response.inference; + + const model = inference.model; + assert.notStrictEqual(model, undefined); + + const splits: split.SplitRange[] = inference.result.splits; assert.ok(Array.isArray(splits)); assert.strictEqual(splits.length, 1); @@ -31,7 +36,12 @@ describe("MindeeV2 - Split Response", async () => { split.SplitResponse, path.join(V2_PRODUCT_PATH, "split", "split_multiple.json") ); - const splits: split.SplitRange[] = response.inference.result.splits; + const inference = response.inference; + + const model = inference.model; + assert.notStrictEqual(model, undefined); + + const splits: split.SplitRange[] = inference.result.splits; assert.ok(Array.isArray(splits)); assert.strictEqual(splits.length, 3);