mindee
Version:
Mindee Client Library for Node.js
278 lines (277 loc) • 14.3 kB
JavaScript
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];
};