UNPKG

mindee

Version:

Mindee Client Library for Node.js

278 lines (277 loc) 14.3 kB
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _Client_instances, _Client_setAsyncParams, _Client_buildProductEndpoint, _Client_initializeOTSEndpoint, _Client_getOtsEndpoint; import { setTimeout } from "node:timers/promises"; import { errorHandler } from "../errors/handler.js"; import { LOG_LEVELS, logger } from "../logger.js"; import { ApiSettingsV1, Endpoint, STANDARD_API_OWNER, } from "./http/index.js"; import { AsyncPredictResponse, FeedbackResponse, PredictResponse, WorkflowResponse, } from "./parsing/common/index.js"; import { InferenceFactory } from "./parsing/common/inference.js"; import { GeneratedV1 } from "./product/index.js"; import { WorkflowEndpoint } from "./http/index.js"; /** * Mindee Client class that centralizes most basic operations. * * @category Client */ export class Client { /** * @param {ClientOptions} options options for the initialization of a client. */ constructor({ apiKey, throwOnError, debug, dispatcher } = { apiKey: undefined, throwOnError: true, debug: false, dispatcher: undefined, }) { _Client_instances.add(this); this.apiSettings = new ApiSettingsV1({ apiKey: apiKey, dispatcher: dispatcher, }); errorHandler.throwOnError = throwOnError ?? true; logger.level = debug ?? process.env.MINDEE_DEBUG ? LOG_LEVELS["debug"] : LOG_LEVELS["warn"]; logger.debug("Client V1 Initialized"); } /** * Send a document to a synchronous endpoint and parse the predictions. * * @param productClass product class to use for calling the API and parsing the response. * @param inputSource file to parse. * @param params parameters relating to prediction options. * @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. */ async parse(productClass, inputSource, params = { endpoint: undefined, allWords: undefined, fullText: undefined, cropper: undefined, pageOptions: undefined, }) { const endpoint = params?.endpoint ?? __classPrivateFieldGet(this, _Client_instances, "m", _Client_initializeOTSEndpoint).call(this, productClass); if (inputSource === undefined) { throw new Error("The 'parse' function requires an input document."); } const rawPrediction = await endpoint.predict({ inputDoc: inputSource, includeWords: this.getBooleanParam(params.allWords), fullText: this.getBooleanParam(params.fullText), pageOptions: params.pageOptions, cropper: this.getBooleanParam(params.cropper), }); return new PredictResponse(productClass, rawPrediction.data); } /** * Send the document to an asynchronous endpoint and return its ID in the queue. * @param productClass product class to use for calling the API and parsing the response. * @param inputSource file to parse. * @param params parameters relating to prediction options. * @category Asynchronous * @returns a `Promise` containing the job (queue) corresponding to a document. */ async enqueue(productClass, inputSource, params = {}) { const endpoint = params?.endpoint ?? __classPrivateFieldGet(this, _Client_instances, "m", _Client_initializeOTSEndpoint).call(this, productClass); if (inputSource === undefined) { throw new Error("The 'enqueue' function requires an input document."); } const rawResponse = await endpoint.predictAsync({ inputDoc: inputSource, includeWords: this.getBooleanParam(params.allWords), fullText: this.getBooleanParam(params.fullText), pageOptions: params?.pageOptions, cropper: this.getBooleanParam(params.cropper), rag: this.getBooleanParam(params.rag), workflowId: params.workflowId }); return new AsyncPredictResponse(productClass, rawResponse.data); } /** * Polls a queue and returns its status as well as the prediction results if the parsing is done. * * @param productClass product class to use for calling the API and parsing the response. * @param queueId id of the queue to poll. * @param params parameters relating to prediction options. * @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 parseQueued(productClass, queueId, params = {}) { const endpoint = params?.endpoint ?? __classPrivateFieldGet(this, _Client_instances, "m", _Client_initializeOTSEndpoint).call(this, productClass); const docResponse = await endpoint.getQueuedDocument(queueId); return new AsyncPredictResponse(productClass, docResponse.data); } /** * Send the document to an asynchronous endpoint and return its ID in the queue. * @param inputSource file to send to the API. * @param workflowId ID of the workflow. * @param params parameters relating to prediction options. * @category Workflow * @returns a `Promise` containing the job (queue) corresponding to a document. */ async executeWorkflow(inputSource, workflowId, params = {}) { const workflowEndpoint = new WorkflowEndpoint(this.apiSettings, workflowId); if (inputSource === undefined) { throw new Error("The 'executeWorkflow' function requires an input document."); } const rawResponse = await workflowEndpoint.executeWorkflow({ inputDoc: inputSource, alias: params.alias, priority: params.priority, pageOptions: params?.pageOptions, fullText: this.getBooleanParam(params.fullText), }); return new WorkflowResponse(GeneratedV1, rawResponse.data); } /** * Fetch prediction results from a document already processed. * * @param productClass product class to use for calling the API and parsing the response. * @param documentId id of the document to fetch. * @param params optional parameters. * @param params.endpoint Endpoint, only specify if using a custom product. * @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. */ async getDocument(productClass, documentId, params = {}) { const endpoint = params?.endpoint ?? __classPrivateFieldGet(this, _Client_instances, "m", _Client_initializeOTSEndpoint).call(this, productClass); const response = await endpoint.getDocument(documentId); return new PredictResponse(productClass, response.data); } /** * Send feedback for a document. * * @param productClass product class to use for calling the API and parsing the response. * @param documentId id of the document to send feedback for. * @param feedback the feedback to send. * @param params optional parameters. * @param params.endpoint Endpoint, only specify if using a custom product. * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. * @category Synchronous * @returns a `Promise` containing feedback results. */ async sendFeedback(productClass, documentId, feedback, params = {}) { const endpoint = params?.endpoint ?? __classPrivateFieldGet(this, _Client_instances, "m", _Client_initializeOTSEndpoint).call(this, productClass); const response = await endpoint.sendFeedback(documentId, feedback); return new FeedbackResponse(response.data); } /** * Send a document to an asynchronous endpoint and poll the server until the result is sent or * until the maximum number of tries is reached. * * @param productClass product class to use for calling the API and parsing the response. * @param inputSource document to parse. * @param asyncParams parameters relating to prediction options. * * @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. */ async enqueueAndParse(productClass, inputSource, asyncParams = { endpoint: undefined, allWords: undefined, fullText: undefined, cropper: undefined, pageOptions: undefined, initialDelaySec: 2, delaySec: 1.5, maxRetries: 80, initialTimerOptions: undefined, recurringTimerOptions: undefined, }) { const validatedAsyncParams = __classPrivateFieldGet(this, _Client_instances, "m", _Client_setAsyncParams).call(this, asyncParams); const enqueueResponse = await this.enqueue(productClass, inputSource, validatedAsyncParams); if (enqueueResponse.job.id === undefined || enqueueResponse.job.id.length === 0) { throw Error("Enqueueing of the document failed."); } const queueId = enqueueResponse.job.id; logger.debug(`Successfully enqueued document with job id: ${queueId}.`); await setTimeout(validatedAsyncParams.initialDelaySec * 1000, undefined, validatedAsyncParams.initialTimerOptions); let retryCounter = 1; let pollResults; pollResults = await this.parseQueued(productClass, queueId, validatedAsyncParams); while (retryCounter < validatedAsyncParams.maxRetries) { logger.debug(`Polling server for parsing result with queueId: ${queueId}. Attempt n°${retryCounter}/${validatedAsyncParams.maxRetries}. Job status: ${pollResults.job.status}.`); if (pollResults.job.status === "completed") { break; } await setTimeout(validatedAsyncParams.delaySec * 1000, undefined, validatedAsyncParams.recurringTimerOptions); pollResults = await this.parseQueued(productClass, queueId, validatedAsyncParams); retryCounter++; } if (pollResults.job.status !== "completed") { throw Error("Asynchronous parsing request timed out after " + validatedAsyncParams.delaySec * retryCounter + " seconds"); } return pollResults; } /** * Forces boolean coercion on truthy/falsy parameters. * @param param input parameter to check. * @returns a strict boolean value. */ getBooleanParam(param) { return param !== undefined ? param : false; } /** * Creates a custom endpoint with the given values. Raises an error if the endpoint is invalid. * @param endpointName Name of the custom Endpoint. * @param accountName Name of the account tied to the Endpoint. * @param endpointVersion Version of the custom Endpoint. * @typeParam T an extension of an `Inference`. Can be omitted as it will be inferred from the `productClass`. * * @returns Endpoint a new product endpoint */ createEndpoint(endpointName, accountName, endpointVersion) { if (!endpointName || endpointName.length === 0) { throw new Error("Missing parameter 'endpointName' for custom build!"); } let cleanEndpointVersion; if (!endpointVersion || endpointVersion.length === 0) { logger.debug("No version provided for a custom build, will poll using version 1 by default."); cleanEndpointVersion = "1"; } else { cleanEndpointVersion = endpointVersion; } return __classPrivateFieldGet(this, _Client_instances, "m", _Client_buildProductEndpoint).call(this, endpointName, accountName, cleanEndpointVersion); } } _Client_instances = new WeakSet(), _Client_setAsyncParams = function _Client_setAsyncParams(asyncParams) { const minDelaySec = 1; const minInitialDelay = 1; const minRetries = 2; const newAsyncParams = { ...asyncParams }; newAsyncParams.delaySec ?? (newAsyncParams.delaySec = 1.5); newAsyncParams.initialDelaySec ?? (newAsyncParams.initialDelaySec = 2); newAsyncParams.maxRetries ?? (newAsyncParams.maxRetries = 80); 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; }, _Client_buildProductEndpoint = function _Client_buildProductEndpoint(endpointName, accountName, endpointVersion) { return new Endpoint(endpointName, accountName, endpointVersion, this.apiSettings); }, _Client_initializeOTSEndpoint = function _Client_initializeOTSEndpoint(productClass) { const [endpointName, endpointVersion] = __classPrivateFieldGet(this, _Client_instances, "m", _Client_getOtsEndpoint).call(this, productClass); return __classPrivateFieldGet(this, _Client_instances, "m", _Client_buildProductEndpoint).call(this, endpointName, STANDARD_API_OWNER, endpointVersion); }, _Client_getOtsEndpoint = function _Client_getOtsEndpoint(productClass) { const [endpointName, endpointVersion] = InferenceFactory.getEndpoint(productClass); return [endpointName, endpointVersion]; };