| |
| |
| |
| |
|
|
|
|
| let engine = null;
|
| let isInitializing = false;
|
|
|
| |
| |
|
|
| self.onmessage = async (event) => {
|
| const { type, data } = event.data;
|
|
|
| try {
|
| switch (type) {
|
| case 'INIT':
|
| await handleInit(data);
|
| break;
|
| case 'GENERATE':
|
| await handleGenerate(data);
|
| break;
|
| case 'STOP':
|
| handleStop();
|
| break;
|
| case 'STATUS':
|
| handleStatus();
|
| break;
|
| default:
|
| console.warn(`[Ahmad Bot Worker] Unknown message type: ${type}`);
|
| }
|
| } catch (error) {
|
| self.postMessage({
|
| type: 'ERROR',
|
| error: error.message,
|
| stack: error.stack,
|
| });
|
| }
|
| };
|
|
|
| |
| |
|
|
| async function handleInit(data) {
|
| if (isInitializing || engine) {
|
| self.postMessage({
|
| type: 'INIT_ACK',
|
| status: 'ALREADY_INITIALIZED',
|
| });
|
| return;
|
| }
|
|
|
| isInitializing = true;
|
| self.postMessage({
|
| type: 'INIT_PROGRESS',
|
| message: 'Initializing WebLLM engine...',
|
| });
|
|
|
| try {
|
|
|
| const { modelId = 'Llama-2-7b-chat-hf-q4f32_1-MLC' } = data;
|
|
|
|
|
| if (typeof self.webllm === 'undefined') {
|
| throw new Error('WebLLM not available in worker. Ensure script is imported.');
|
| }
|
|
|
|
|
| engine = new self.webllm.MLCEngine({
|
| model: modelId,
|
| temperature: 0.3,
|
| top_p: 0.85,
|
| max_gen_len: 512,
|
| });
|
|
|
|
|
| await engine.ready;
|
|
|
| isInitializing = false;
|
|
|
| self.postMessage({
|
| type: 'INIT_ACK',
|
| status: 'READY',
|
| model: modelId,
|
| });
|
|
|
| } catch (error) {
|
| isInitializing = false;
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
|
|
| async function handleGenerate(data) {
|
| if (!engine) {
|
| throw new Error('Engine not initialized');
|
| }
|
|
|
| const { messages, requestId } = data;
|
|
|
| try {
|
| self.postMessage({
|
| type: 'GENERATION_START',
|
| requestId: requestId,
|
| });
|
|
|
| let fullResponse = '';
|
| let tokenCount = 0;
|
|
|
|
|
| const stream = await engine.chat.completions.create({
|
| messages: messages,
|
| stream: true,
|
| temperature: 0.3,
|
| top_p: 0.85,
|
| });
|
|
|
|
|
| for await (const chunk of stream) {
|
| const token = chunk.choices?.[0]?.delta?.content || '';
|
|
|
| if (token) {
|
| fullResponse += token;
|
| tokenCount++;
|
|
|
|
|
| self.postMessage({
|
| type: 'TOKEN',
|
| requestId: requestId,
|
| token: token,
|
| tokenCount: tokenCount,
|
| });
|
|
|
|
|
| if (tokenCount % 10 === 0) {
|
| await new Promise(r => setTimeout(r, 0));
|
| }
|
| }
|
| }
|
|
|
|
|
| self.postMessage({
|
| type: 'GENERATION_COMPLETE',
|
| requestId: requestId,
|
| fullResponse: fullResponse,
|
| tokenCount: tokenCount,
|
| });
|
|
|
| } catch (error) {
|
| self.postMessage({
|
| type: 'GENERATION_ERROR',
|
| requestId: data.requestId,
|
| error: error.message,
|
| });
|
| }
|
| }
|
|
|
| |
| |
|
|
| function handleStop() {
|
| if (engine) {
|
| engine.interruptGenerate?.();
|
| }
|
|
|
| self.postMessage({
|
| type: 'GENERATION_STOPPED',
|
| });
|
| }
|
|
|
| |
| |
|
|
| function handleStatus() {
|
| self.postMessage({
|
| type: 'STATUS_ACK',
|
| isReady: engine !== null,
|
| isInitializing: isInitializing,
|
| });
|
| }
|
|
|
|
|
| self.postMessage({
|
| type: 'WORKER_READY',
|
| });
|
|
|