UNPKG

mindee

Version:

Mindee Client Library for Node.js

381 lines (380 loc) 18.1 kB
"use strict"; 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_buildApiSettings, _Client_initializeOTSEndpoint, _Client_cleanAccountName, _Client_getOtsEndpoint; Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = void 0; const http_1 = require("./http"); const common_1 = require("./parsing/common"); const handler_1 = require("./errors/handler"); const logger_1 = require("./logger"); const inference_1 = require("./parsing/common/inference"); const product_1 = require("./product"); const promises_1 = require("node:timers/promises"); const errors_1 = require("./errors"); const workflowResponse_1 = require("./parsing/common/workflowResponse"); const workflowEndpoint_1 = require("./http/workflowEndpoint"); const input_1 = require("./input"); /** * Mindee Client class that centralizes most basic operations. * * @category Client */ class Client { /** * @param {ClientOptions} options options for the initialization of a client. */ constructor({ apiKey, throwOnError, debug } = { apiKey: "", throwOnError: true, debug: false, }) { _Client_instances.add(this); this.apiKey = apiKey ? apiKey : ""; handler_1.errorHandler.throwOnError = throwOnError ?? true; logger_1.logger.level = debug ?? process.env.MINDEE_DEBUG ? logger_1.LOG_LEVELS["debug"] : logger_1.LOG_LEVELS["warn"]; logger_1.logger.debug("Client 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 common_1.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 common_1.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 common_1.AsyncPredictResponse(productClass, docResponse.data); } async loadPrediction(productClass, localResponse) { /** * Load a prediction. * * @param productClass Product class to use for calling the API and parsing the response. * @param localResponse Local response to load. * @category Asynchronous * @returns A valid prediction */ try { const asDict = await localResponse.asDict(); if (Object.prototype.hasOwnProperty.call(asDict, "job")) { return new common_1.AsyncPredictResponse(productClass, asDict); } return new common_1.PredictResponse(productClass, asDict); } catch { throw new errors_1.MindeeError("No prediction found in local response."); } } /** * 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_1.WorkflowEndpoint(__classPrivateFieldGet(this, _Client_instances, "m", _Client_buildApiSettings).call(this), 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_1.WorkflowResponse(product_1.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 common_1.PredictResponse(productClass, response.data); } /** * Send a 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 common_1.FeedbackResponse(response.data); } /** * Send a document to an asynchronous endpoint and poll the server until the result is sent or * until the maximum amount 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_1.logger.debug(`Successfully enqueued document with job id: ${queueId}.`); await (0, promises_1.setTimeout)(validatedAsyncParams.initialDelaySec * 1000, undefined, validatedAsyncParams.initialTimerOptions); let retryCounter = 1; let pollResults; pollResults = await this.parseQueued(productClass, queueId, validatedAsyncParams); while (retryCounter < validatedAsyncParams.maxRetries) { logger_1.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 (0, promises_1.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) { const cleanAccountName = __classPrivateFieldGet(this, _Client_instances, "m", _Client_cleanAccountName).call(this, product_1.CustomV1, accountName); if (!endpointName || endpointName.length === 0) { throw new Error("Missing parameter 'endpointName' for custom build!"); } let cleanEndpointVersion; if (!endpointVersion || endpointVersion.length === 0) { logger_1.logger.debug("Warning: No version provided for a custom build, will attempt to poll version 1 by default."); cleanEndpointVersion = "1"; } else { cleanEndpointVersion = endpointVersion; } return __classPrivateFieldGet(this, _Client_instances, "m", _Client_buildProductEndpoint).call(this, endpointName, cleanAccountName, cleanEndpointVersion); } /** * Load an input document from a local path. * @param inputPath */ docFromPath(inputPath) { return new input_1.PathInput({ inputPath: inputPath, }); } /** * Load an input document from a base64 encoded string. * @param inputString input content, as a string. * @param filename file name. */ docFromBase64(inputString, filename) { return new input_1.Base64Input({ inputString: inputString, filename: filename, }); } /** * Load an input document from a `stream.Readable` object. * @param inputStream input content, as a readable stream. * @param filename file name. */ docFromStream(inputStream, filename) { return new input_1.StreamInput({ inputStream: inputStream, filename: filename, }); } /** * Load an input document from bytes. * @param inputBytes input content, as a Uint8Array or Buffer. * @param filename file name. */ docFromBytes(inputBytes, filename) { return new input_1.BytesInput({ inputBytes: inputBytes, filename: filename, }); } /** * Load an input document from a URL. * @param url input url. Must be HTTPS. */ docFromUrl(url) { return new input_1.UrlInput({ url: url, }); } /** * Load an input document from a Buffer. * @param buffer input content, as a buffer. * @param filename file name. */ docFromBuffer(buffer, filename) { return new input_1.BufferInput({ buffer: buffer, filename: filename, }); } } exports.Client = Client; _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 http_1.Endpoint(endpointName, accountName, endpointVersion, __classPrivateFieldGet(this, _Client_instances, "m", _Client_buildApiSettings).call(this)); }, _Client_buildApiSettings = function _Client_buildApiSettings() { return new http_1.ApiSettings({ apiKey: this.apiKey, }); }, _Client_initializeOTSEndpoint = function _Client_initializeOTSEndpoint(productClass) { if (productClass.name === "CustomV1") { throw new Error("Incorrect parameters for Custom build."); } const [endpointName, endpointVersion] = __classPrivateFieldGet(this, _Client_instances, "m", _Client_getOtsEndpoint).call(this, productClass); return __classPrivateFieldGet(this, _Client_instances, "m", _Client_buildProductEndpoint).call(this, endpointName, http_1.STANDARD_API_OWNER, endpointVersion); }, _Client_cleanAccountName = function _Client_cleanAccountName(productClass, accountName) { if (productClass.name === "CustomV1") { if (!accountName || accountName.length === 0) { logger_1.logger.debug(`Warning: no account name provided for custom build, ${http_1.STANDARD_API_OWNER} will be used by default`); return http_1.STANDARD_API_OWNER; } return accountName; } return http_1.STANDARD_API_OWNER; }, _Client_getOtsEndpoint = function _Client_getOtsEndpoint(productClass) { const [endpointName, endpointVersion] = inference_1.InferenceFactory.getEndpoint(productClass); return [endpointName, endpointVersion]; };