Skip to content

Commit c3e2a9b

Browse files
committed
feat: implement ai model download using web workers
1 parent 9a733fc commit c3e2a9b

File tree

10 files changed

+139
-140
lines changed

10 files changed

+139
-140
lines changed

lefthook.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pre-commit:
55
run: pnpm biome check --write --unsafe --staged --no-errors-on-unmatched && git add -u
66
typecheck:
77
run: pnpm tsc
8-
build:
9-
run: pnpm build
8+
# build:
9+
# run: pnpm build
1010
test:
1111
run: pnpm test:ci

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "@codethicket/react-ai-translator",
2+
"name": "@joelshejar/react-ai-translator",
33
"description": "",
4-
"version": "0.0.0",
4+
"version": "0.0.57",
55
"author": "Joel Rajesh",
66
"license": "MIT",
77
"keywords": [],
@@ -23,10 +23,10 @@
2323
"link:self": "pnpm link --global",
2424
"prepare": "lefthook install"
2525
},
26+
"module": "./dist/index.mjs",
2627
"types": "./dist/index.d.ts",
2728
"exports": {
2829
".": {
29-
"require": "./dist/index.js",
3030
"import": "./dist/index.mjs"
3131
}
3232
},
File renamed without changes.

src/hooks/useTranslation.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { useEffect, useRef, useState } from "react";
2+
3+
const useTranslation = (_workerScript) => {
4+
const worker = useRef(null);
5+
6+
// States for managing translation
7+
const [loading, setLoading] = useState(false);
8+
const [modelLoading, setModelLoading] = useState(false);
9+
const [progress, setProgress] = useState([]);
10+
const [translatedText, setTranslatedText] = useState("");
11+
const [error, setError] = useState(null);
12+
13+
console.log("hhhhh");
14+
15+
useEffect(() => {
16+
if (!worker.current) {
17+
// Initialize the worker
18+
worker.current = new Worker(new URL("./worker.mjs", import.meta.url), {
19+
type: "module",
20+
});
21+
}
22+
23+
// Handle worker messages
24+
const onMessage = (e) => {
25+
const { status, output, progress: progressValue, file } = e.data;
26+
27+
console.log(status, "status");
28+
29+
switch (status) {
30+
case "initiate": {
31+
console.log("initiate");
32+
setLoading(true);
33+
setModelLoading(true);
34+
setProgress((prev) => [...prev, { file, progress: progressValue }]);
35+
break;
36+
}
37+
38+
case "progress": {
39+
console.log("progress");
40+
setProgress((prev) =>
41+
prev.map((item) =>
42+
item.file === file ? { ...item, progress: progressValue } : item,
43+
),
44+
);
45+
break;
46+
}
47+
48+
case "done": {
49+
console.log("done");
50+
setModelLoading(false);
51+
setProgress((prev) => prev.filter((item) => item.file !== file));
52+
break;
53+
}
54+
55+
case "ready": {
56+
console.log("ready");
57+
setLoading(false);
58+
break;
59+
}
60+
61+
case "update": {
62+
console.log("update");
63+
console.log(output, "output");
64+
// Append partial translations
65+
setTranslatedText(output);
66+
break;
67+
}
68+
69+
case "complete": {
70+
console.log("complete");
71+
setLoading(false);
72+
break;
73+
}
74+
}
75+
};
76+
77+
worker.current.addEventListener("message", onMessage);
78+
79+
return () => {
80+
// worker.current.terminate(); // Clean up worker on unmount
81+
// worker.current = null;
82+
};
83+
}, []);
84+
85+
// Function to send translation requests to the worker
86+
const translate = (text, srcLang, tgtLang) => {
87+
console.log("kkkk");
88+
if (!worker.current) {
89+
console.error("Worker is not initialized.");
90+
return;
91+
}
92+
93+
setLoading(true);
94+
setError(null);
95+
setTranslatedText(""); // Reset the output
96+
97+
console.log("lllll", text, srcLang, tgtLang);
98+
99+
worker.current.postMessage({
100+
text,
101+
src_lang: srcLang,
102+
tgt_lang: tgtLang,
103+
});
104+
};
105+
106+
return {
107+
translate,
108+
translatedText,
109+
loading,
110+
modelLoading,
111+
progress,
112+
error,
113+
};
114+
};
115+
116+
export default useTranslation;

src/hooks/useTranslation.ts

Lines changed: 0 additions & 122 deletions
This file was deleted.
File renamed without changes.

src/stories/Example.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Meta, StoryFn } from "@storybook/react";
22

3-
import { Example } from "..";
3+
import { Example } from "../Example";
44

55
export default {
66
title: "Example",

src/worker.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { pipeline } from "@xenova/transformers";
55
* pipeline is loaded. This is because loading the pipeline is an expensive
66
* operation and we don't want to do it every time we want to translate a sentence.
77
*/
8-
9-
// biome-disable-next-line complexity/noStaticOnlyClass
108
class MyTranslationPipeline {
119
static task = "translation";
1210
static model = "Xenova/nllb-200-distilled-600M";
@@ -17,7 +15,7 @@ class MyTranslationPipeline {
1715
MyTranslationPipeline.instance = pipeline(
1816
MyTranslationPipeline.task,
1917
MyTranslationPipeline.model,
20-
{ progress_callback: progressCallback },
18+
{ dtype: "q8", progress_callback: progressCallback },
2119
);
2220
}
2321

@@ -38,7 +36,7 @@ self.addEventListener("message", async (event) => {
3836
// Actually perform the translation
3937

4038
console.log(event.data.tgt_lang, event.data.src_lang, "langcode");
41-
const output = await translator(event.data.text, {
39+
const output = await translator?.(event.data.text, {
4240
tgt_lang: event.data.tgt_lang,
4341
src_lang: event.data.src_lang,
4442

tests/Example.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import renderer from "react-test-renderer";
22
import { expect, it } from "vitest";
3-
import { Example } from "../src";
3+
import { Example } from "../src/Example";
44

55
it("renders correctly", () => {
66
const tree = renderer

tsup.config.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@ import path from "node:path";
55
import { type Options, defineConfig } from "tsup";
66

77
const common: Options = {
8-
entry: ["src/index.ts"],
8+
entry: ["src/index.js", "src/worker.js"],
99
treeshake: false,
1010
sourcemap: "inline",
1111
minify: true,
1212
clean: true,
1313
dts: true,
1414
splitting: false,
15-
format: ["cjs", "esm"],
15+
format: ["esm"],
1616
external: ["react"],
1717
injectStyle: false,
18+
target: "es2021",
19+
platform: "browser",
20+
// dts: {
21+
// entry: 'src/index.js', // or whichever main file you want
22+
// },
23+
// loader: {
24+
// '.worker.js': 'file', // Handle worker files as static files
25+
// },
1826
};
1927

2028
const getPackageName = async () => {
@@ -66,11 +74,10 @@ const linkSelf = async () => {
6674
};
6775

6876
export default defineConfig({
69-
async onSuccess() {
70-
// If you want need to add a use statement to files, you can use the following code:
71-
// await _addUseStatement('dist/react', 'client');
77+
// async onSuccess() {
78+
// // If you want need to add a use statement to files, you can use the following code:
79+
// // await _addUseStatement('dist/react', 'client');
7280

73-
await linkSelf();
74-
},
81+
// },
7582
...common,
7683
});

0 commit comments

Comments
 (0)