mindee
Version:
Mindee Client Library for Node.js
381 lines (380 loc) • 18.1 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_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];
};
;