Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 212 additions & 32 deletions src/v2/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { PollingOptions, PollingOptionsConstructor } from "./clientOptions/index
import { BaseProduct } from "@/v2/product/baseProduct.js";
import { BaseSearch } from "@/v2/search/baseSearch.js";
import { ModelSearch } from "@/v2/search/models/modelSearch.js";
import { LocalInputSource } from "@/input/index.js";

/**
* Options for the V2 Mindee Client.
Expand All @@ -35,7 +36,9 @@ export interface ClientOptions {
* Mindee Client V2 class that centralizes most basic operations.
*/
export class Client {
/** Mindee V2 API handler. */
/**
* Mindee V2 API handler.
*/
protected mindeeApi: MindeeApiV2;

/**
Expand All @@ -58,38 +61,8 @@ export class Client {
}

/**
* 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.
* @deprecated Use `search(ModelSearch, {})` instead.
* Enqueues a product inference job without waiting for completion.
*/
async searchModels(name?: string, modelType?: string): Promise<SearchResponse> {
return await this.search(ModelSearch, { name: name, modelType: modelType });
}

/**
* Search for resources matching the given criteria.
* @param search Search definition class to use.
* @param searchParameters Search parameters.
* @returns a `Promise` containing the search response with the matching resources.
*/
async search<S extends typeof BaseSearch>(
search: S,
searchParameters: InstanceType<S["parametersClass"]> | ConstructorParameters<S["parametersClass"]>[0],
): Promise<InstanceType<S["responseClass"]>> {
if (!searchParameters) {
throw new MindeeError("Search parameters are required.");
}

const paramsInstance = searchParameters instanceof search.parametersClass
? searchParameters
: new search.parametersClass(searchParameters);

return await this.mindeeApi.reqGetSearch(search, paramsInstance);
}

/** Enqueues a product inference job without waiting for completion. */
async enqueue<P extends typeof BaseProduct>(
product: P,
inputSource: InputSource,
Expand Down Expand Up @@ -252,4 +225,211 @@ export class Client {
"You can increase poll attempts by passing the pollingOptions argument to enqueueAndGetResult()"
);
}

/**
* 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.
* @deprecated Use `search(ModelSearch, {})` instead.
*/
async searchModels(name?: string, modelType?: string): Promise<SearchResponse> {
return await this.search(ModelSearch, { name: name, modelType: modelType });
}

/**
* Search for resources matching the given criteria.
* @param search Search definition class to use.
* @param searchParameters Search parameters.
* @returns a `Promise` containing the search response with the matching resources.
*/
async search<S extends typeof BaseSearch>(
search: S,
searchParameters: InstanceType<S["parametersClass"]> | ConstructorParameters<S["parametersClass"]>[0],
): Promise<InstanceType<S["responseClass"]>> {
if (!searchParameters) {
throw new MindeeError("Search parameters are required.");
}

const paramsInstance = searchParameters instanceof search.parametersClass
? searchParameters
: new search.parametersClass(searchParameters);

return await this.mindeeApi.reqGetSearch(search, paramsInstance);
}

/**
* Not recommended for general use, prefer `getReadyRagDocumentPoll`.
* You will need to poll until the document is ready for use.
* Get a document's info and annotations from the RAG database.
*
* @param product the product the RAG database belongs to.
* @param documentId the document's ID.
*/
async getRagDocument<P extends typeof BaseProduct>(
product: P,
documentId: string
): Promise<InstanceType<P["annotationResponseClass"]>> {
logger.debug(`Getting RAG document ID: ${documentId}`);
return await this.mindeeApi.reqGetRagAnnotation(product, documentId);
}

/**
* Get a document's info and annotations from the RAG database.
*
* @param product the product the RAG database belongs to.
* @param documentId the document's ID.
* @param pollingOptions options for the polling loop, see {@link PollingOptions}.
* @returns a `Promise` containing the RAG document annotation.
*/
async getReadyRagDocumentPoll<P extends typeof BaseProduct>(
product: P,
documentId: string,
pollingOptions?: PollingOptionsConstructor
): Promise<InstanceType<P["annotationResponseClass"]>> {
const initialResponse = await this.getRagDocument(product, documentId);

if (initialResponse.status !== "Processing") {
return initialResponse;
}

const pollingOptionsInstance = new PollingOptions(pollingOptions);
return await this.pollForRagDocument(product, initialResponse, pollingOptionsInstance);
}

/**
* Not recommended for general use, prefer `uploadAndGetRagDocumentPoll`.
* You will need to poll until the document is ready for use.
* Add a document to the RAG database.
*
* @param product The product the RAG database belongs to.
* @param inputSource The file to upload.
* @param parameters The parameters to use for the upload.
*/
async uploadRagDocument<P extends typeof BaseProduct>(
product: P,
inputSource: LocalInputSource,
parameters: InstanceType<P["ragDocumentUploadClass"]> | ConstructorParameters<P["ragDocumentUploadClass"]>[0]
): Promise<InstanceType<P["annotationResponseClass"]>> {
logger.debug("Adding a document to the RAG database");
const paramsInstance = new product.ragDocumentUploadClass(parameters);
await inputSource.init();
return await this.mindeeApi.reqPostRagDocument(product, paramsInstance, inputSource);
}

/**
* Add a document to the RAG database and return the initial annotation.
*
* @param product The product the RAG database belongs to.
* @param inputSource The file to upload.
* @param parameters The parameters to use for the upload.
* @param pollingOptions options for the polling loop, see {@link PollingOptions}.
*/
async uploadAndGetRagDocumentPoll<P extends typeof BaseProduct>(
product: P,
inputSource: LocalInputSource,
parameters: InstanceType<P["ragDocumentUploadClass"]> | ConstructorParameters<P["ragDocumentUploadClass"]>[0],
pollingOptions?: PollingOptionsConstructor
): Promise<InstanceType<P["annotationResponseClass"]>> {
const pollingOptionsInstance = new PollingOptions(pollingOptions);

const initialResponse = await this.uploadRagDocument(product, inputSource, parameters);

return await this.pollForRagDocument(product, initialResponse, pollingOptionsInstance);
}

/**
* Update a document's annotations in the RAG database and poll until ready.
*
* @param product The product the RAG database belongs to.
* @param parameters The parameters to use for the update.
* @param pollingOptions options for the polling loop, see {@link PollingOptions}.
* @returns a `Promise` containing the RAG document annotation.
*/
async updateAndGetRagAnnotationPoll<P extends typeof BaseProduct>(
product: P,
parameters: InstanceType<P["annotationParametersClass"]> | ConstructorParameters<P["annotationParametersClass"]>[0],
pollingOptions?: PollingOptionsConstructor
): Promise<InstanceType<P["annotationResponseClass"]>> {
const paramsInstance = new product.annotationParametersClass(parameters);
logger.debug(`Updating RAG document ID: ${paramsInstance.documentId}`);

const initialResponse = await this.mindeeApi.reqPatchRagAnnotation(product, paramsInstance);

if (initialResponse.status !== "Processing") {
return initialResponse;
}

const pollingOptionsInstance = new PollingOptions(pollingOptions);
return await this.pollForRagDocument(product, initialResponse, pollingOptionsInstance);
}

/**
* Get a document's info and annotations from the RAG database.
* @param product the product the RAG database belongs to.
* @param documentId the document's ID.
*/
async deleteRagDocument<P extends typeof BaseProduct>(
product: P,
documentId: string
): Promise<boolean> {
return await this.mindeeApi.reqDeleteRagDocument(product, documentId);
}

/**
* Poll until the document is finished processing or the max number of attempts is reached.
*
* @param product The product the RAG database belongs to.
* @param initialResponse The initial response containing the document's ID.
* @param pollingOptions Options for the polling loop, see {@link PollingOptions}.
* @returns A `Promise` containing the RAG document annotation.
* @protected
*/
protected async pollForRagDocument<P extends typeof BaseProduct>(
product: P,
initialResponse: InstanceType<P["annotationResponseClass"]>,
pollingOptions: PollingOptions
): Promise<InstanceType<P["annotationResponseClass"]>> {
logger.debug(`Polling for RAG document ID: ${initialResponse.id}`);
const maxRetries = pollingOptions.maxRetries + 1;

logger.debug(
`Waiting ${pollingOptions.initialDelaySec} seconds before attempting to retrieve the result...`
);
await setTimeout(
pollingOptions.initialDelaySec * 1000,
undefined,
pollingOptions.initialTimerOptions
);

const documentId = initialResponse.id;
let retryCount = 1;

while (retryCount < maxRetries) {
await setTimeout(
pollingOptions.delaySec * 1000,
undefined,
pollingOptions.recurringTimerOptions
);

logger.debug(
`Poll attempt ${retryCount} of ${pollingOptions.maxRetries}`
);

const response = await this.getRagDocument(product, documentId);

retryCount++;

switch (response.status) {
case "Processing":
continue;
case "Failed":
throw new MindeeError("Job failed without an error payload.");
default:
return response;
}
}

throw new MindeeError(`RAG polling not complete after ${retryCount} attempts.`);
}
}
34 changes: 34 additions & 0 deletions src/v2/clientOptions/baseAnnotationParameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { MindeeConfigurationError } from "@/errors/index.js";

/**
* Constructor parameters for BaseAnnotationParameters and its subclasses.
*/
export interface BaseAnnotationParametersConstructor {
documentId: string;
}

/**
* Base parameters for document annotations.
*/
export abstract class BaseAnnotationParameters {
/**
* UUID of the annotated document.
*/
public readonly documentId: string;

/**
* Default constructor.
*/
protected constructor(params: BaseAnnotationParametersConstructor) {
if (params.documentId === undefined || params.documentId === null || params.documentId === "") {
throw new MindeeConfigurationError("Document ID must be provided");
}
// Note: DocumentId is included in the request URL path, it is not a parameter.
this.documentId = params.documentId.trim();
}

/**
* Gets the request parameters for the upload request.
*/
public abstract getRequestParameters(): Record<string, string>;
}
37 changes: 37 additions & 0 deletions src/v2/clientOptions/baseRagDocumentUploadParameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { MindeeConfigurationError } from "@/errors/index.js";

/**
* Constructor parameters for BaseRagDocumentUploadParameters and its subclasses.
*/
export interface BaseRagDocumentUploadParametersConstructor {
modelId: string;
}

/**
* Base parameters for document upload operations.
*/
export abstract class BaseRagDocumentUploadParameters {
/**
* UUID of the model that the uploaded RAG document is linked to.
*/
public readonly modelId: string;

/**
* Default constructor.
*/
protected constructor(params: BaseRagDocumentUploadParametersConstructor) {
if (params.modelId === undefined || params.modelId === null || params.modelId === "") {
throw new MindeeConfigurationError("Model ID must be provided");
}
this.modelId = params.modelId.trim();
}

/**
* Gets the request parameters for the upload request.
*/
public getRequestParameters(): Record<string, string> {
const parameters: Record<string, string> = {};
parameters["model_id"] = this.modelId;
return parameters;
}
}
2 changes: 2 additions & 0 deletions src/v2/clientOptions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export type {
} from "./pollingOptions.js";
export { BaseProductParameters } from "./baseProductParameters.js";
export { BaseSearchParameters } from "./baseSearchParameters.js";
export { BaseRagDocumentUploadParameters } from "./baseRagDocumentUploadParameters.js";
export { BaseAnnotationParameters } from "./baseAnnotationParameters.js";
Loading
Loading