From aebe897cdb23de1ca942ba61efe9721f3939608a Mon Sep 17 00:00:00 2001 From: perzeuss <11357019+perzeuss@users.noreply.github.com> Date: Wed, 9 Aug 2023 20:05:57 +0200 Subject: [PATCH] fix: use workaround for dynamic import resolves #953 --- .../TransformersEmbeddingFunction.ts | 3 +- .../src/embeddings/WebAIEmbeddingFunction.ts | 15 ++- clients/js/src/utils.ts | 108 ++++++++++-------- 3 files changed, 76 insertions(+), 50 deletions(-) diff --git a/clients/js/src/embeddings/TransformersEmbeddingFunction.ts b/clients/js/src/embeddings/TransformersEmbeddingFunction.ts index 887910c159bc..e2f242331556 100644 --- a/clients/js/src/embeddings/TransformersEmbeddingFunction.ts +++ b/clients/js/src/embeddings/TransformersEmbeddingFunction.ts @@ -1,3 +1,4 @@ +import { dynamicImportModule } from "../utils"; import { IEmbeddingFunction } from "./IEmbeddingFunction"; // Dynamically import module @@ -30,7 +31,7 @@ export class TransformersEmbeddingFunction implements IEmbeddingFunction { // Also, since we use `"module": "commonjs"` in tsconfig.json, we use the following workaround to ensure // the dynamic import is not transpiled to a `require` statement. // For more information, see https://github.com/microsoft/TypeScript/issues/43329#issuecomment-1008361973 - TransformersApi = Function('return import("@xenova/transformers")')(); + TransformersApi = dynamicImportModule("@xenova/transformers"); } catch (e) { throw new Error( "Please install the @xenova/transformers package to use the TransformersEmbeddingFunction, `npm install -S @xenova/transformers`." diff --git a/clients/js/src/embeddings/WebAIEmbeddingFunction.ts b/clients/js/src/embeddings/WebAIEmbeddingFunction.ts index e76bc5c20f0c..083ce3199450 100755 --- a/clients/js/src/embeddings/WebAIEmbeddingFunction.ts +++ b/clients/js/src/embeddings/WebAIEmbeddingFunction.ts @@ -1,3 +1,4 @@ +import { dynamicImportModule } from "../utils"; import { IEmbeddingFunction } from "./IEmbeddingFunction"; /** @@ -158,14 +159,16 @@ export class WebAIEmbeddingFunction implements IEmbeddingFunction { this.proxy = proxy ? proxy : true; try { // @ts-ignore - const webAI = await import("@visheratin/web-ai"); + const webAI = await dynamicImportModule("@visheratin/web-ai"); if (wasmPath) { webAI.SessionParams.wasmRoot = wasmPath; } switch (modality) { case "text": { // @ts-ignore - const webAIText = await import("@visheratin/web-ai/text"); + const webAIText = await dynamicImportModule( + "@visheratin/web-ai/text" + ); let id = "mini-lm-v2-quant"; //default text model if (modelID) { id = modelID; @@ -183,7 +186,9 @@ export class WebAIEmbeddingFunction implements IEmbeddingFunction { } case "image": { // @ts-ignore - const webAIImage = await import("@visheratin/web-ai/image"); + const webAIImage = await dynamicImportModule( + "@visheratin/web-ai/image" + ); let id = "efficientformer-l1-feature-quant"; //default image model if (modelID) { id = modelID; @@ -201,7 +206,9 @@ export class WebAIEmbeddingFunction implements IEmbeddingFunction { } case "multimodal": { // @ts-ignore - const webAIImage = await import("@visheratin/web-ai/multimodal"); + const webAIImage = await dynamicImportModule( + "@visheratin/web-ai/multimodal" + ); let id = "clip-base-quant"; //default multimodal model if (modelID) { id = modelID; diff --git a/clients/js/src/utils.ts b/clients/js/src/utils.ts index 619152854830..5c43b413d9c0 100644 --- a/clients/js/src/utils.ts +++ b/clients/js/src/utils.ts @@ -1,66 +1,84 @@ -import { Api } from "./generated" +import { Api } from "./generated"; import Count200Response = Api.Count200Response; // a function to convert a non-Array object to an Array export function toArray(obj: T | Array): Array { - if (Array.isArray(obj)) { - return obj; - } else { - return [obj]; - } + if (Array.isArray(obj)) { + return obj; + } else { + return [obj]; + } } // a function to convert an array to array of arrays -export function toArrayOfArrays(obj: Array> | Array): Array> { - if (Array.isArray(obj[0])) { - return obj as Array>; - } else { - return [obj] as Array>; - } +export function toArrayOfArrays( + obj: Array> | Array +): Array> { + if (Array.isArray(obj[0])) { + return obj as Array>; + } else { + return [obj] as Array>; + } } // we need to override constructors to make it work with jest // https://stackoverflow.com/questions/76007003/jest-tobeinstanceof-expected-constructor-array-received-constructor-array export function repack(value: unknown): any { - if (Boolean(value) && typeof value === "object") { - if (Array.isArray(value)) { - return new Array(...value); - } else { - return { ...value }; - } + if (Boolean(value) && typeof value === "object") { + if (Array.isArray(value)) { + return new Array(...value); } else { - return value; + return { ...value }; } + } else { + return value; + } } export async function handleError(error: unknown) { - - if (error instanceof Response) { - try { - const res = await error.json(); - if ("error" in res) { - return { error: res.error }; - } - } catch (e: unknown) { - return { - //@ts-ignore - error: - e && typeof e === "object" && "message" in e - ? e.message - : "unknown error", - }; - } + if (error instanceof Response) { + try { + const res = await error.json(); + if ("error" in res) { + return { error: res.error }; + } + } catch (e: unknown) { + return { + //@ts-ignore + error: + e && typeof e === "object" && "message" in e + ? e.message + : "unknown error", + }; } - return { error }; + } + return { error }; } -export async function handleSuccess(response: Response | string | Count200Response) { - switch (true) { - case response instanceof Response: - return repack(await (response as Response).json()); - case typeof response === "string": - return repack((response as string)); // currently version is the only thing that return non-JSON - default: - return repack(response); - } +export async function handleSuccess( + response: Response | string | Count200Response +) { + switch (true) { + case response instanceof Response: + return repack(await (response as Response).json()); + case typeof response === "string": + return repack(response as string); // currently version is the only thing that return non-JSON + default: + return repack(response); + } +} + +/** + * Dynamically imports a specified module, providing a workaround for browser environments. + * This function is necessary due to the `"module": "commonjs"` setting in tsconfig.json, + * which would ordinarily transpile dynamic imports into `require` statements. + * This function ensures that the dynamic import remains intact for browser compatibility. + * Refer to this TypeScript issue for further details: + * {@link https://github.com/microsoft/TypeScript/issues/43329#issuecomment-1008361973} + * + * @param {string} moduleName - Specifies the module to import. + * @returns {Promise} Returns a Promise that resolves to the imported module. + */ +export async function dynamicImportModule(moduleName: string) { + return Function(`return import("${moduleName}}")`)(); }