hantech commited on
Commit
671f7c3
·
verified ·
1 Parent(s): e2f9eae

Create worker.js

Browse files
Files changed (1) hide show
  1. worker.js +141 -0
worker.js ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { pipeline, env, cos_sim } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.0.0-alpha.19/dist/transformers.min.js';
2
+
3
+ // Cấu hình WebGPU
4
+ env.backends.onnx.wasm.proxy = false;
5
+
6
+ // --- CẤU HÌNH MODEL (KHÔNG ĐỔI THEO YÊU CẦU) ---
7
+ // 1. Embedding Model
8
+ const EMBEDDING_MODEL_ID = 'onnx-community/embeddinggemma-300m-ONNX';
9
+
10
+ // 2. LLM Model: Granite 4.0 Nano
11
+ // Lưu ý: Nếu phiên bản ONNX của Granite 4.0 chưa public dưới ID này,
12
+ // bạn cần trỏ tới đúng repo onnx (ví dụ ibm-granite/granite-3.0-2b-instruct nếu 4.0 chưa có onnx).
13
+ // Tuy nhiên, tôi giữ nguyên tham chiếu "Granite" như yêu cầu.
14
+ const LLM_MODEL_ID = 'ibm-granite/granite-4.0-350m-instruct'; // Kiểm tra lại tên chính xác trên HF Hub nếu lỗi
15
+
16
+ let extractor = null;
17
+ let generator = null;
18
+ let vectorStore = []; // Lưu trữ chunks và vectors: { text: string, vector: number[] }
19
+
20
+ // Khởi tạo Models
21
+ async function initModels() {
22
+ try {
23
+ console.log("Đang tải Embedding Model...");
24
+ extractor = await pipeline('feature-extraction', EMBEDDING_MODEL_ID, {
25
+ device: 'webgpu',
26
+ dtype: 'fp32', // Embedding thường cần độ chính xác
27
+ });
28
+
29
+ console.log("Đang tải LLM Granite 4.0...");
30
+ generator = await pipeline('text-generation', LLM_MODEL_ID, {
31
+ device: 'webgpu',
32
+ dtype: 'q4', // Quantization 4-bit để chạy mượt trên browser
33
+ use_external_data_format: true
34
+ });
35
+
36
+ self.postMessage({ type: 'init_complete' });
37
+ } catch (e) {
38
+ self.postMessage({ type: 'error', payload: "Lỗi tải model: " + e.message });
39
+ }
40
+ }
41
+
42
+ // Xử lý chunking văn bản
43
+ function chunkText(text, chunkSize = 300, overlap = 50) {
44
+ const sentences = text.match(/[^.!?]+[.!?]+|[^.!?]+$/g) || [text];
45
+ let chunks = [];
46
+ let currentChunk = "";
47
+
48
+ for (let sentence of sentences) {
49
+ if ((currentChunk + sentence).length > chunkSize) {
50
+ chunks.push(currentChunk.trim());
51
+ currentChunk = sentence.slice(-overlap); // Overlap đơn giản
52
+ } else {
53
+ currentChunk += " " + sentence;
54
+ }
55
+ }
56
+ if (currentChunk) chunks.push(currentChunk.trim());
57
+ return chunks;
58
+ }
59
+
60
+ // Tạo embeddings cho văn bản
61
+ async function ingestText(text) {
62
+ const chunks = chunkText(text);
63
+ vectorStore = []; // Reset store
64
+
65
+ for (const chunk of chunks) {
66
+ const output = await extractor(chunk, { pooling: 'mean', normalize: true });
67
+ vectorStore.push({
68
+ text: chunk,
69
+ vector: output.data
70
+ });
71
+ }
72
+ console.log(`Đã index ${vectorStore.length} đoạn văn bản.`);
73
+ }
74
+
75
+ // Tìm kiếm RAG
76
+ async function retrieve(query) {
77
+ const queryOutput = await extractor(query, { pooling: 'mean', normalize: true });
78
+ const queryVector = queryOutput.data;
79
+
80
+ // Tính Cosine Similarity
81
+ const scored = vectorStore.map(item => {
82
+ return {
83
+ text: item.text,
84
+ score: cos_sim(queryVector, item.vector)
85
+ };
86
+ });
87
+
88
+ // Lấy top 3 đoạn liên quan nhất
89
+ scored.sort((a, b) => b.score - a.score);
90
+ return scored.slice(0, 3).map(i => i.text).join("\n\n");
91
+ }
92
+
93
+ // Xử lý tin nhắn từ Main Thread
94
+ self.onmessage = async (e) => {
95
+ if (!extractor || !generator) {
96
+ await initModels();
97
+ }
98
+
99
+ const { type, payload } = e.data;
100
+
101
+ if (type === 'ingest_text') {
102
+ await ingestText(payload);
103
+ } else if (type === 'query') {
104
+ // 1. Retrieve Context
105
+ const context = await retrieve(payload);
106
+
107
+ // 2. Tạo Prompt cho Granite
108
+ // Định dạng prompt cơ bản cho instruction tuned model
109
+ const prompt = `<|system|>
110
+ Bạn là trợ lý AI hữu ích. Hãy trả lời câu hỏi dựa trên ngữ cảnh được cung cấp bên dưới bằng Tiếng Việt.
111
+ Ngữ cảnh:
112
+ ${context}
113
+ <|user|>
114
+ ${payload}
115
+ <|assistant|>
116
+ `;
117
+
118
+ // 3. Generate Answer
119
+ try {
120
+ const output = await generator(prompt, {
121
+ max_new_tokens: 256,
122
+ temperature: 0.7,
123
+ do_sample: true,
124
+ });
125
+
126
+ // Lấy phần trả lời sau tag assistant (tuỳ thuộc format model)
127
+ let answer = output[0].generated_text;
128
+ // Cắt bớt phần prompt nếu cần thiết
129
+ if (answer.includes("<|assistant|>")) {
130
+ answer = answer.split("<|assistant|>")[1];
131
+ }
132
+
133
+ self.postMessage({ type: 'answer', payload: answer });
134
+ } catch (err) {
135
+ self.postMessage({ type: 'error', payload: err.message });
136
+ }
137
+ }
138
+ };
139
+
140
+ // Khởi tạo ngay khi worker chạy
141
+ initModels();