Skip to content

Commit f31ada6

Browse files
authored
💥 ♻️ separate polling options (#441)
1 parent d323390 commit f31ada6

15 files changed

Lines changed: 196 additions & 123 deletions

docs/code_samples/v2_crop.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
1212
);
1313

1414
// Set product parameters
15-
const params = {
15+
const productParams = {
1616
modelId: modelId,
1717
};
1818

@@ -23,7 +23,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
2323
const response = await mindeeClient.enqueueAndGetResult(
2424
mindee.product.Crop,
2525
inputSource,
26-
params,
26+
productParams,
2727
);
2828

2929
// print a string summary

docs/code_samples/v2_extraction.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
1212
);
1313

1414
// Set product parameters
15-
const params = {
15+
const productParams = {
1616
modelId: modelId,
1717

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

4141
// print a string summary

docs/code_samples/v2_ocr.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
1212
);
1313

1414
// Set product parameters
15-
const params = {
15+
const productParams = {
1616
modelId: modelId,
1717
};
1818

@@ -23,7 +23,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
2323
const response = await mindeeClient.enqueueAndGetResult(
2424
mindee.product.Ocr,
2525
inputSource,
26-
params,
26+
productParams,
2727
);
2828

2929
// print a string summary

docs/code_samples/v2_split.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const mindeeClient = new mindee.Client(
1212
);
1313

1414
// Set product parameters
15-
const params = {
15+
const productParams = {
1616
modelId: modelId,
1717
};
1818

@@ -23,7 +23,7 @@ const inputSource = new mindee.PathInput({ inputPath: filePath });
2323
const response = await mindeeClient.enqueueAndGetResult(
2424
mindee.product.Split,
2525
inputSource,
26-
params,
26+
productParams,
2727
);
2828

2929
// print a string summary

src/v2/client.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { LOG_LEVELS, logger } from "@/logger.js";
77
import { ErrorResponse, JobResponse } from "./parsing/index.js";
88
import { MindeeApiV2 } from "./http/mindeeApiV2.js";
99
import { MindeeHttpErrorV2 } from "./http/errors.js";
10-
import { ValidatedPollingOptions } from "./client/index.js";
10+
import { PollingOptions, PollingOptionsConstructor } from "./client/index.js";
1111
import { BaseProduct } from "@/v2/product/baseProduct.js";
1212

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

138-
const pollingOptions = paramsInstance.getValidatedPollingOptions();
140+
const pollingOptionsInstance = new PollingOptions(pollingOptions);
139141

140142
const jobResponse: JobResponse = await this.enqueue(
141143
product, inputSource, paramsInstance
142144
);
143145
return await this.pollForResult(
144-
product, pollingOptions, jobResponse.job.id
146+
product, pollingOptionsInstance, jobResponse.job.id
145147
);
146148
}
147149

@@ -152,7 +154,7 @@ export class Client {
152154
*/
153155
protected async pollForResult<P extends typeof BaseProduct>(
154156
product: typeof BaseProduct,
155-
pollingOptions: ValidatedPollingOptions,
157+
pollingOptions: PollingOptions,
156158
queueId: string,
157159
): Promise<InstanceType<P["responseClass"]>> {
158160
logger.debug(
@@ -193,9 +195,8 @@ export class Client {
193195
}
194196

195197
throw new MindeeError(
196-
"Asynchronous parsing request timed out after " +
197-
pollingOptions.delaySec * retryCounter +
198-
" seconds"
198+
`Polling failed to retrieve a result after ${retryCounter} attempts. ` +
199+
"You can increase poll attempts by passing the pollingOptions argument to enqueueAndGetResult()"
199200
);
200201
}
201202
}

src/v2/client/baseParameters.ts

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { ValidatedPollingOptions } from "@/v2/client/pollingOptions.js";
2-
import { PollingOptions } from "@/v2/index.js";
31
import { MindeeConfigurationError } from "@/errors/index.js";
42

53
/**
@@ -9,7 +7,6 @@ export interface BaseParametersConstructor {
97
modelId: string;
108
alias?: string;
119
webhookIds?: string[];
12-
pollingOptions?: PollingOptions;
1310
closeFile?: boolean;
1411
}
1512

@@ -46,10 +43,6 @@ export abstract class BaseParameters {
4643
* If empty, no webhooks will be used.
4744
*/
4845
webhookIds?: string[];
49-
/**
50-
* Client-side polling configuration (see {@link PollingOptions}).
51-
*/
52-
pollingOptions?: PollingOptions;
5346
/**
5447
* By default, the file is closed once the upload is finished.
5548
* Set to `false` to keep it open.
@@ -64,44 +57,6 @@ export abstract class BaseParameters {
6457
this.alias = params.alias;
6558
this.webhookIds = params.webhookIds;
6659
this.closeFile = params.closeFile;
67-
this.pollingOptions = params.pollingOptions;
68-
}
69-
70-
/**
71-
* Checks the values for asynchronous parsing. Returns their corrected value if they are undefined.
72-
* @returns A valid `AsyncOptions`.
73-
*/
74-
getValidatedPollingOptions(): ValidatedPollingOptions {
75-
const minDelaySec = 1;
76-
const minInitialDelay = 1;
77-
const minRetries = 2;
78-
let newAsyncParams: PollingOptions;
79-
if (this.pollingOptions === undefined) {
80-
newAsyncParams = {
81-
delaySec: 1.5,
82-
initialDelaySec: 2,
83-
maxRetries: 80
84-
};
85-
} else {
86-
newAsyncParams = { ...this.pollingOptions };
87-
if (
88-
!newAsyncParams.delaySec ||
89-
!newAsyncParams.initialDelaySec ||
90-
!newAsyncParams.maxRetries
91-
) {
92-
throw Error("Invalid polling options.");
93-
}
94-
if (newAsyncParams.delaySec < minDelaySec) {
95-
throw Error(`Cannot set auto-parsing delay to less than ${minDelaySec} second(s).`);
96-
}
97-
if (newAsyncParams.initialDelaySec < minInitialDelay) {
98-
throw Error(`Cannot set initial parsing delay to less than ${minInitialDelay} second(s).`);
99-
}
100-
if (newAsyncParams.maxRetries < minRetries) {
101-
throw Error(`Cannot set retry to less than ${minRetries}.`);
102-
}
103-
}
104-
return newAsyncParams as ValidatedPollingOptions;
10560
}
10661

10762
/**

src/v2/client/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
export type { PollingOptions, ValidatedPollingOptions } from "./pollingOptions.js";
1+
export { PollingOptions } from "./pollingOptions.js";
2+
export type { PollingOptionsConstructor } from "./pollingOptions.js";
23
export { BaseParameters } from "./baseParameters.js";

src/v2/client/pollingOptions.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
import { MindeeConfigurationError } from "@/errors/index.js";
2+
import { logger } from "@/logger.js";
3+
4+
export interface TimerOptions {
5+
ref?: boolean,
6+
signal?: AbortSignal
7+
}
8+
9+
export interface PollingOptionsConstructor {
10+
initialDelaySec?: number;
11+
delaySec?: number;
12+
maxRetries?: number;
13+
initialTimerOptions?: TimerOptions;
14+
recurringTimerOptions?: TimerOptions;
15+
}
16+
17+
const minInitialDelay = 1;
18+
const minDelaySec = 1;
19+
const minRetries = 2;
20+
121
/**
222
* Parameters for the internal polling loop in `enqueueAndGetInference()`.
323
*
@@ -24,13 +44,13 @@
2444
*
2545
* const inference = await client.enqueueAndGetInference(inputDoc, params);
2646
*/
27-
export interface PollingOptions {
47+
export class PollingOptions {
2848
/** Number of seconds to wait *before the first poll*. */
29-
initialDelaySec?: number;
49+
initialDelaySec: number;
3050
/** Interval in seconds between two consecutive polls. */
31-
delaySec?: number;
51+
delaySec: number;
3252
/** Maximum number of polling attempts (including the first one). */
33-
maxRetries?: number;
53+
maxRetries: number;
3454
/** Options passed to the initial `setTimeout()`. */
3555
initialTimerOptions?: {
3656
ref?: boolean,
@@ -40,11 +60,56 @@ export interface PollingOptions {
4060
recurringTimerOptions?: {
4161
ref?: boolean,
4262
signal?: AbortSignal
63+
};
64+
65+
constructor(params?: PollingOptionsConstructor) {
66+
if (!params) {
67+
params = {};
68+
}
69+
if (!params.initialDelaySec) {
70+
this.initialDelaySec = 2;
71+
} else {
72+
this.initialDelaySec = params.initialDelaySec;
73+
}
74+
if (!params.delaySec) {
75+
this.delaySec = 1.5;
76+
} else {
77+
this.delaySec = params.delaySec;
78+
}
79+
if (!params.maxRetries) {
80+
this.maxRetries = 80;
81+
} else {
82+
this.maxRetries = params.maxRetries;
83+
}
84+
if (params.initialTimerOptions) {
85+
this.initialTimerOptions = params.initialTimerOptions;
86+
}
87+
if (params.recurringTimerOptions) {
88+
this.recurringTimerOptions = params.recurringTimerOptions;
89+
}
90+
this.validateOptions();
91+
logger.debug(`Polling options initialized: ${this.toString()}`);
4392
}
44-
}
4593

46-
export interface ValidatedPollingOptions extends PollingOptions {
47-
initialDelaySec: number;
48-
delaySec: number;
49-
maxRetries: number;
94+
validateOptions() {
95+
if (this.delaySec < minDelaySec) {
96+
throw new MindeeConfigurationError(
97+
`Cannot set auto-parsing delay to less than ${minDelaySec} second(s).`
98+
);
99+
}
100+
if (this.initialDelaySec < minInitialDelay) {
101+
throw new MindeeConfigurationError(
102+
`Cannot set initial parsing delay to less than ${minInitialDelay} second(s).`
103+
);
104+
}
105+
if (this.maxRetries < minRetries) {
106+
throw new MindeeConfigurationError(
107+
`Cannot set retry to less than ${minRetries}.`
108+
);
109+
}
110+
}
111+
112+
toString(): string {
113+
return `{ initialDelaySec: ${this.initialDelaySec}, delaySec: ${this.delaySec}, maxRetries: ${this.maxRetries} }`;
114+
}
50115
}
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import { SimpleField } from "@/v2/parsing/inference/field/index.js";
1616
import { MindeeHttpErrorV2 } from "@/v2/http/index.js";
1717
import * as fs from "node:fs";
18-
import { RESOURCE_PATH, V2_PRODUCT_PATH } from "../index.js";
18+
import { RESOURCE_PATH, V2_PRODUCT_PATH } from "../../index.js";
1919
import { Extraction } from "@/v2/product/index.js";
2020

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

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

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

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

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

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

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

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

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

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

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

0 commit comments

Comments
 (0)