Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/code_samples/v2_crop.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
);

// Set product parameters
const params = {
const productParams = {
modelId: modelId,
};

Expand All @@ -23,7 +23,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
const response = await mindeeClient.enqueueAndGetResult(
mindee.product.Crop,
inputSource,
params,
productParams,
);

// print a string summary
Expand Down
4 changes: 2 additions & 2 deletions docs/code_samples/v2_extraction.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
);

// Set product parameters
const params = {
const productParams = {
modelId: modelId,

// Options: set to `true` or `false` to override defaults
Expand All @@ -35,7 +35,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
const response = await mindeeClient.enqueueAndGetResult(
mindee.product.Extraction,
inputSource,
params,
productParams,
);

// print a string summary
Expand Down
4 changes: 2 additions & 2 deletions docs/code_samples/v2_ocr.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
);

// Set product parameters
const params = {
const productParams = {
modelId: modelId,
};

Expand All @@ -23,7 +23,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
const response = await mindeeClient.enqueueAndGetResult(
mindee.product.Ocr,
inputSource,
params,
productParams,
);

// print a string summary
Expand Down
4 changes: 2 additions & 2 deletions docs/code_samples/v2_split.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
);

// Set product parameters
const params = {
const productParams = {
modelId: modelId,
};

Expand All @@ -23,7 +23,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
const response = await mindeeClient.enqueueAndGetResult(
mindee.product.Split,
inputSource,
params,
productParams,
);

// print a string summary
Expand Down
15 changes: 8 additions & 7 deletions src/v2/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { LOG_LEVELS, logger } from "@/logger.js";
import { ErrorResponse, JobResponse } from "./parsing/index.js";
import { MindeeApiV2 } from "./http/mindeeApiV2.js";
import { MindeeHttpErrorV2 } from "./http/errors.js";
import { ValidatedPollingOptions } from "./client/index.js";
import { PollingOptions, PollingOptionsConstructor } from "./client/index.js";
import { BaseProduct } from "@/v2/product/baseProduct.js";

/**
Expand Down Expand Up @@ -124,6 +124,7 @@ export class Client {
* @param inputSource file or URL to parse.
* @param params parameters relating to prediction options.
*
* @param pollingOptions options for the polling loop, see {@link PollingOptions}.
* @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`.
* @category Synchronous
* @returns a `Promise` containing parsing results.
Expand All @@ -132,16 +133,17 @@ export class Client {
product: P,
inputSource: InputSource,
params: InstanceType<P["parametersClass"]> | ConstructorParameters<P["parametersClass"]>[0],
pollingOptions?: PollingOptionsConstructor,
): Promise<InstanceType<P["responseClass"]>> {
const paramsInstance = new product.parametersClass(params);

const pollingOptions = paramsInstance.getValidatedPollingOptions();
const pollingOptionsInstance = new PollingOptions(pollingOptions);

const jobResponse: JobResponse = await this.enqueue(
product, inputSource, paramsInstance
);
return await this.pollForResult(
product, pollingOptions, jobResponse.job.id
product, pollingOptionsInstance, jobResponse.job.id
);
}

Expand All @@ -152,7 +154,7 @@ export class Client {
*/
protected async pollForResult<P extends typeof BaseProduct>(
product: typeof BaseProduct,
pollingOptions: ValidatedPollingOptions,
pollingOptions: PollingOptions,
queueId: string,
): Promise<InstanceType<P["responseClass"]>> {
logger.debug(
Expand Down Expand Up @@ -193,9 +195,8 @@ export class Client {
}

throw new MindeeError(
"Asynchronous parsing request timed out after " +
pollingOptions.delaySec * retryCounter +
" seconds"
`Polling failed to retrieve a result after ${retryCounter} attempts. ` +
"You can increase poll attempts by passing the pollingOptions argument to enqueueAndGetResult()"
);
}
}
45 changes: 0 additions & 45 deletions src/v2/client/baseParameters.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { ValidatedPollingOptions } from "@/v2/client/pollingOptions.js";
import { PollingOptions } from "@/v2/index.js";
import { MindeeConfigurationError } from "@/errors/index.js";

/**
Expand All @@ -9,7 +7,6 @@ export interface BaseParametersConstructor {
modelId: string;
alias?: string;
webhookIds?: string[];
pollingOptions?: PollingOptions;
closeFile?: boolean;
}

Expand Down Expand Up @@ -46,10 +43,6 @@ export abstract class BaseParameters {
* 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.
Expand All @@ -64,44 +57,6 @@ export abstract class BaseParameters {
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;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/v2/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type { PollingOptions, ValidatedPollingOptions } from "./pollingOptions.js";
export { PollingOptions } from "./pollingOptions.js";
export type { PollingOptionsConstructor } from "./pollingOptions.js";
export { BaseParameters } from "./baseParameters.js";
83 changes: 74 additions & 9 deletions src/v2/client/pollingOptions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
import { MindeeConfigurationError } from "@/errors/index.js";
import { logger } from "@/logger.js";

export interface TimerOptions {
ref?: boolean,
signal?: AbortSignal
}

export interface PollingOptionsConstructor {
initialDelaySec?: number;
delaySec?: number;
maxRetries?: number;
initialTimerOptions?: TimerOptions;
recurringTimerOptions?: TimerOptions;
}

const minInitialDelay = 1;
const minDelaySec = 1;
const minRetries = 2;

/**
* Parameters for the internal polling loop in `enqueueAndGetInference()`.
*
Expand All @@ -24,13 +44,13 @@
*
* const inference = await client.enqueueAndGetInference(inputDoc, params);
*/
export interface PollingOptions {
export class PollingOptions {
/** Number of seconds to wait *before the first poll*. */
initialDelaySec?: number;
initialDelaySec: number;
/** Interval in seconds between two consecutive polls. */
delaySec?: number;
delaySec: number;
/** Maximum number of polling attempts (including the first one). */
maxRetries?: number;
maxRetries: number;
/** Options passed to the initial `setTimeout()`. */
initialTimerOptions?: {
ref?: boolean,
Expand All @@ -40,11 +60,56 @@ export interface PollingOptions {
recurringTimerOptions?: {
ref?: boolean,
signal?: AbortSignal
};

constructor(params?: PollingOptionsConstructor) {
if (!params) {
params = {};
}
if (!params.initialDelaySec) {
this.initialDelaySec = 2;
} else {
this.initialDelaySec = params.initialDelaySec;
}
if (!params.delaySec) {
this.delaySec = 1.5;
} else {
this.delaySec = params.delaySec;
}
if (!params.maxRetries) {
this.maxRetries = 80;
} else {
this.maxRetries = params.maxRetries;
}
if (params.initialTimerOptions) {
this.initialTimerOptions = params.initialTimerOptions;
}
if (params.recurringTimerOptions) {
this.recurringTimerOptions = params.recurringTimerOptions;
}
this.validateOptions();
logger.debug(`Polling options initialized: ${this.toString()}`);
}
}

export interface ValidatedPollingOptions extends PollingOptions {
initialDelaySec: number;
delaySec: number;
maxRetries: number;
validateOptions() {
if (this.delaySec < minDelaySec) {
throw new MindeeConfigurationError(
`Cannot set auto-parsing delay to less than ${minDelaySec} second(s).`
);
}
if (this.initialDelaySec < minInitialDelay) {
throw new MindeeConfigurationError(
`Cannot set initial parsing delay to less than ${minInitialDelay} second(s).`
);
}
if (this.maxRetries < minRetries) {
throw new MindeeConfigurationError(
`Cannot set retry to less than ${minRetries}.`
);
}
}

toString(): string {
return `{ initialDelaySec: ${this.initialDelaySec}, delaySec: ${this.delaySec}, maxRetries: ${this.maxRetries} }`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
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 } from "../index.js";
import { RESOURCE_PATH, V2_PRODUCT_PATH } from "../../index.js";
import { Extraction } from "@/v2/product/index.js";

function check422(err: unknown) {
Expand All @@ -37,7 +37,7 @@ function checkEmptyActiveOptions(inference: ExtractionInference) {
assert.equal(inference.activeOptions?.textContext, false);
}

describe("MindeeV2 – Client Integration Tests", () => {
describe("MindeeV2 – Integration - Client", () => {
let client: Client;
let modelId: string;

Expand Down Expand Up @@ -73,7 +73,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
dataSchemaReplace = fs.readFileSync(dataSchemaReplacePath).toString();
});

it("Empty, multi-page PDF – PathInput - enqueueAndGetResult must succeed", async () => {
it("enqueueAndGetResult must succeed: Empty, multi-page PDF – PathInput", async () => {
const source = new PathInput({ inputPath: emptyPdfPath });
const params = {
modelId,
Expand All @@ -100,7 +100,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
checkEmptyActiveOptions(inference);
}).timeout(60000);

it("Filled, single-page image – PathInput - enqueueAndGetResult must succeed", async () => {
it("enqueueAndGetResult must succeed: Filled, single-page image – PathInput", async () => {
const source = new PathInput({ inputPath: sampleImagePath });
const params = {
modelId,
Expand Down Expand Up @@ -139,7 +139,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
assert.equal(inference.result.rawText?.pages.length, 1);
}).timeout(120000);

it("Filled, single-page image – Base64Input - enqueueAndGetResult must succeed", async () => {
it("enqueueAndGetResult must succeed: Filled, single-page image – Base64Input", async () => {
const data = fs.readFileSync(sampleBase64Path, "utf8");
const source = new Base64Input({ inputString: data, filename: "receipt.jpg" });
const params = {
Expand Down Expand Up @@ -170,7 +170,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
checkEmptyActiveOptions(inference);
}).timeout(120000);

it("Invalid model ID – enqueue must raise 422", async () => {
it("enqueue must raise 422: Invalid model ID", async () => {
const source = new PathInput({ inputPath: emptyPdfPath });
const badParams = { modelId: "00000000-0000-0000-0000-000000000000" };

Expand All @@ -182,7 +182,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
}
}).timeout(60000);

it("Invalid job ID – getInference must raise 422", async () => {
it("getResult must raise 422: Invalid job ID", async () => {
try {
await client.getResult(
Extraction,
Expand All @@ -194,7 +194,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
}
}).timeout(60000);

it("HTTPS URL – enqueueAndGetResult must succeed", async () => {
it("enqueueAndGetResult must succeed: HTTPS URL", async () => {
const url = process.env.MINDEE_V2_SE_TESTS_BLANK_PDF_URL ?? "error-no-url-found";
const source = new UrlInput({ url });
const params = new ExtractionParameters({
Expand All @@ -213,7 +213,7 @@ describe("MindeeV2 – Client Integration Tests", () => {
assert.ok(response.inference instanceof ExtractionInference);
}).timeout(60000);

it("Data Schema Override - Overrides the data schema successfully", async () => {
it("should override the data schema successfully", async () => {
const source = new PathInput({ inputPath: emptyPdfPath });
const params = new ExtractionParameters({
modelId,
Expand Down
Loading