Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use workaround for dynamic import #956

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d62d61c
fix: use workaround for dynamic import
perzeuss Aug 9, 2023
c7981d9
refactor: undo pretter formatting
perzeuss Aug 10, 2023
2b97985
refactor: remove no longer required ts-ignore
perzeuss Aug 16, 2023
98b9ba0
feat: define optional dependencies in js client
perzeuss Aug 16, 2023
c59d254
refactor: rename util and rewrite jsdoc
perzeuss Aug 16, 2023
4e3ec56
feat: define node 14+ as engine because we use dynamic imports
perzeuss Aug 16, 2023
167d17f
doc: fix typo
perzeuss Aug 16, 2023
3b7f182
chore: increase supported node engine to 14.17.0
perzeuss Aug 16, 2023
7cc80be
fix: typo in importOptionalModule
perzeuss Aug 17, 2023
b282fea
feat: add openai to optional dependencies
perzeuss Aug 17, 2023
1603fe0
chore: define supported openai version
perzeuss Aug 17, 2023
7c511e6
chore: update dependency pinning for optional deps
perzeuss Aug 17, 2023
255261a
fix: use workaround for dynamic import
perzeuss Aug 9, 2023
be3743b
refactor: undo pretter formatting
perzeuss Aug 10, 2023
739ae8b
refactor: remove no longer required ts-ignore
perzeuss Aug 16, 2023
560e4db
feat: define optional dependencies in js client
perzeuss Aug 16, 2023
26d40cb
refactor: rename util and rewrite jsdoc
perzeuss Aug 16, 2023
ade2405
feat: define node 14+ as engine because we use dynamic imports
perzeuss Aug 16, 2023
6b5d356
chore: increase supported node engine to 14.17.0
perzeuss Aug 16, 2023
186acbc
fix: typo in importOptionalModule
perzeuss Aug 17, 2023
37da232
feat: add openai to optional dependencies
perzeuss Aug 17, 2023
7f422d0
chore: define supported openai version
perzeuss Aug 17, 2023
473ea43
chore: update dependency pinning for optional deps
perzeuss Aug 17, 2023
3aabd8a
feat: use correct way to define optional dependencies
perzeuss Aug 30, 2023
0946be5
refactor: remove duplicate fn
perzeuss Aug 31, 2023
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
29 changes: 28 additions & 1 deletion clients/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"author": "",
"license": "Apache-2.0",
"devDependencies": {
"@openapi-generator-plus/typescript-fetch-client-generator": "^1.5.0",
"@types/jest": "^29.5.0",
"jest": "^29.5.0",
"npm-run-all": "^4.1.5",
"openapi-generator-plus": "^2.6.0",
"@openapi-generator-plus/typescript-fetch-client-generator": "^1.5.0",
"prettier": "2.8.7",
"rimraf": "^5.0.0",
"ts-jest": "^29.1.0",
Expand Down Expand Up @@ -47,7 +47,34 @@
"prettier": "prettier --write .",
"release": "run-s build test:run && npm publish"
},
"engines": {
"node": ">=14.17.0"
},
"dependencies": {
"isomorphic-fetch": "^3.0.0"
},
"peerDependencies": {
"@visheratin/web-ai": "^1.0.0",
"@visheratin/web-ai-node": "^1.0.0",
"@xenova/transformers": "^2.0.0",
"cohere-ai": "^6.0.0",
"openai": "^3.0.0 | ^4.0.0"
},
"peerDependenciesMeta": {
"@visheratin/web-ai": {
"optional": true
},
"@visheratin/web-ai-node": {
"optional": true
},
"@xenova/transformers": {
"optional": true
},
"cohere-ai": {
"optional": true
},
"openai": {
"optional": true
}
}
}
9 changes: 4 additions & 5 deletions clients/js/src/embeddings/TransformersEmbeddingFunction.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { importOptionalModule } from "../utils";
import { IEmbeddingFunction } from "./IEmbeddingFunction";

// Dynamically import module
Expand Down Expand Up @@ -26,11 +27,9 @@ export class TransformersEmbeddingFunction implements IEmbeddingFunction {
progress_callback?: Function | null;
} = {}) {
try {
// Since Transformers.js is an ESM package, we use the dynamic `import` syntax instead of `require`.
// 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")')();
// Use dynamic import to support browser environments because we do not have a bundler that handles browser support.
// The util importOptionalModule is used to prevent issues when bundlers try to locate the dependency even when it's optional.
TransformersApi = importOptionalModule("@xenova/transformers");
} catch (e) {
throw new Error(
"Please install the @xenova/transformers package to use the TransformersEmbeddingFunction, `npm install -S @xenova/transformers`."
Expand Down
19 changes: 11 additions & 8 deletions clients/js/src/embeddings/WebAIEmbeddingFunction.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { importOptionalModule } from "../utils";
import { IEmbeddingFunction } from "./IEmbeddingFunction";

/**
Expand Down Expand Up @@ -157,15 +158,15 @@ export class WebAIEmbeddingFunction implements IEmbeddingFunction {
) {
this.proxy = proxy ? proxy : true;
try {
// @ts-ignore
const webAI = await import("@visheratin/web-ai");
const webAI = await importOptionalModule("@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 importOptionalModule(
"@visheratin/web-ai/text"
);
let id = "mini-lm-v2-quant"; //default text model
if (modelID) {
id = modelID;
Expand All @@ -182,8 +183,9 @@ export class WebAIEmbeddingFunction implements IEmbeddingFunction {
);
}
case "image": {
// @ts-ignore
const webAIImage = await import("@visheratin/web-ai/image");
const webAIImage = await importOptionalModule(
"@visheratin/web-ai/image"
);
let id = "efficientformer-l1-feature-quant"; //default image model
if (modelID) {
id = modelID;
Expand All @@ -200,8 +202,9 @@ export class WebAIEmbeddingFunction implements IEmbeddingFunction {
);
}
case "multimodal": {
// @ts-ignore
const webAIImage = await import("@visheratin/web-ai/multimodal");
const webAIImage = await importOptionalModule(
"@visheratin/web-ai/multimodal"
);
let id = "clip-base-quant"; //default multimodal model
if (modelID) {
id = modelID;
Expand Down
108 changes: 63 additions & 45 deletions clients/js/src/utils.ts
Original file line number Diff line number Diff line change
@@ -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<T>(obj: T | Array<T>): Array<T> {
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<T>(obj: Array<Array<T>> | Array<T>): Array<Array<T>> {
if (Array.isArray(obj[0])) {
return obj as Array<Array<T>>;
} else {
return [obj] as Array<Array<T>>;
}
export function toArrayOfArrays<T>(
obj: Array<Array<T>> | Array<T>
): Array<Array<T>> {
if (Array.isArray(obj[0])) {
return obj as Array<Array<T>>;
} else {
return [obj] as Array<Array<T>>;
}
}

// 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 };
perzeuss marked this conversation as resolved.
Show resolved Hide resolved
}
} 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 because we dynamically import optional dependencies
* which can cause issues with bundlers that detect the import and throw an error
* on build time when the dependency is not installed.
* Using this workaround, the dynamic import is only evaluated on runtime
* where we work with try-catch when importing optional dependencies.
*
* @param {string} moduleName - Specifies the module to import.
* @returns {Promise<any>} Returns a Promise that resolves to the imported module.
*/
export async function importOptionalModule(moduleName: string) {
perzeuss marked this conversation as resolved.
Show resolved Hide resolved
return Function(`return import("${moduleName}")`)();
}
Loading