UNPKG

mindee

Version:

Mindee Client Library for Node.js

143 lines (142 loc) 6.75 kB
import { setTimeout } from "node:timers/promises"; import { MindeeError } from "../errors/index.js"; import { errorHandler } from "../errors/handler.js"; import { LOG_LEVELS, logger } from "../logger.js"; import { MindeeApiV2 } from "./http/mindeeApiV2.js"; import { MindeeHttpErrorV2 } from "./http/errors.js"; import { PollingOptions } from "./clientOptions/index.js"; /** * Mindee Client V2 class that centralizes most basic operations. */ export class Client { /** * @param {ClientOptions} options options for the initialization of a client. */ constructor({ apiKey, debug, dispatcher } = { apiKey: undefined, debug: false, dispatcher: undefined, }) { this.mindeeApi = new MindeeApiV2(dispatcher, apiKey); errorHandler.throwOnError = true; logger.level = debug ?? process.env.MINDEE_DEBUG ? LOG_LEVELS["debug"] : LOG_LEVELS["warn"]; logger.debug("Client V2 Initialized"); } /** * Search for models available to the account. * @param name Optional name filter. * @param modelType Optional model type filter. * @returns a `Promise` containing the search response. */ async searchModels(name, modelType) { return await this.mindeeApi.reqGetSearchModel(name, modelType); } async enqueue(product, inputSource, params) { if (inputSource === undefined) { throw new MindeeError("An input document is required."); } const paramsInstance = params instanceof product.parametersClass ? params : new product.parametersClass(params); await inputSource.init(); const jobResponse = await this.mindeeApi.reqPostProductEnqueue(product, 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; } /** * Retrieves the result of a previously enqueued request. * * @param product the product 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`. * @returns a `Promise` containing the inference. */ async getResult(product, inferenceId) { logger.debug(`Attempting to get inference with ID: ${inferenceId} using response type: ${product.name}`); return await this.mindeeApi.reqGetProductResultById(product, inferenceId); } /** * Retrieves the result of a previously enqueued request. * This method is used when manually polling the server for the result URL. * * @param product the product to retrieve. * @param url URL as given in the `getJob()` response. * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. * @returns a `Promise` containing the inference. */ async getResultByUrl(product, url) { logger.debug(`Attempting to get inference from: ${url} using response type: ${product.name}`); return await this.mindeeApi.reqGetProductResultByUrl(product, url); } /** * Get the processing status of a previously enqueued request. * Can be used for polling. * * @param jobId id of the queue to poll. * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. * @returns a `Promise` containing a `Job`, which also contains a `Document` if the * parsing is complete. */ async getJob(jobId) { return await this.mindeeApi.reqGetJobById(jobId); } /** * Enqueue a request and poll the server until the result is sent or * until the maximum number of tries is reached. * * @param product the product to retrieve. * @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`. * @returns a `Promise` containing parsing results. */ async enqueueAndGetResult(product, inputSource, params, pollingOptions) { const paramsInstance = new product.parametersClass(params); const pollingOptionsInstance = new PollingOptions(pollingOptions); const jobResponse = await this.enqueue(product, inputSource, paramsInstance); return await this.pollForResult(product, pollingOptionsInstance, jobResponse); } /** * 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 */ async pollForResult(product, pollingOptions, jobResponse) { 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: ${jobResponse.job.id}.`); let retryCounter = 1; let pollResults; while (retryCounter < pollingOptions.maxRetries + 1) { logger.debug(`Attempt ${retryCounter} of ${pollingOptions.maxRetries}`); pollResults = await this.mindeeApi.reqGetJobByUrl(jobResponse.job.pollingUrl); const error = 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") { if (!pollResults.job.resultUrl) { throw new MindeeError("The result URL is undefined. This is a server error, try again later or contact support."); } return this.getResultByUrl(product, pollResults.job.resultUrl); } await setTimeout(pollingOptions.delaySec * 1000, undefined, pollingOptions.recurringTimerOptions); retryCounter++; } throw new MindeeError(`Polling failed to retrieve a result after ${retryCounter} attempts. ` + "You can increase poll attempts by passing the pollingOptions argument to enqueueAndGetResult()"); } }