File size: 4,523 Bytes
1d3f990
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/**

 * Ahmad Bot Web Worker

 * Offloads model inference to prevent UI blocking

 * Communicates with main thread via message passing

 */

let engine = null;
let isInitializing = false;

/**

 * Handle messages from main thread

 */
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,
        });
    }
};

/**

 * Initialize WebLLM engine

 */
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 {
        // Import WebLLM if available
        const { modelId = 'Llama-2-7b-chat-hf-q4f32_1-MLC' } = data;

        // Check if WebLLM is available in worker
        if (typeof self.webllm === 'undefined') {
            throw new Error('WebLLM not available in worker. Ensure script is imported.');
        }

        // Initialize engine
        engine = new self.webllm.MLCEngine({
            model: modelId,
            temperature: 0.3,
            top_p: 0.85,
            max_gen_len: 512,
        });

        // Wait for ready
        await engine.ready;

        isInitializing = false;

        self.postMessage({
            type: 'INIT_ACK',
            status: 'READY',
            model: modelId,
        });

    } catch (error) {
        isInitializing = false;
        throw error;
    }
}

/**

 * Generate response with streaming

 */
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;

        // Create stream
        const stream = await engine.chat.completions.create({
            messages: messages,
            stream: true,
            temperature: 0.3,
            top_p: 0.85,
        });

        // Consume stream and send tokens
        for await (const chunk of stream) {
            const token = chunk.choices?.[0]?.delta?.content || '';

            if (token) {
                fullResponse += token;
                tokenCount++;

                // Send token to main thread
                self.postMessage({
                    type: 'TOKEN',
                    requestId: requestId,
                    token: token,
                    tokenCount: tokenCount,
                });

                // Yield to prevent blocking
                if (tokenCount % 10 === 0) {
                    await new Promise(r => setTimeout(r, 0));
                }
            }
        }

        // Send completion
        self.postMessage({
            type: 'GENERATION_COMPLETE',
            requestId: requestId,
            fullResponse: fullResponse,
            tokenCount: tokenCount,
        });

    } catch (error) {
        self.postMessage({
            type: 'GENERATION_ERROR',
            requestId: data.requestId,
            error: error.message,
        });
    }
}

/**

 * Stop current generation

 */
function handleStop() {
    if (engine) {
        engine.interruptGenerate?.();
    }

    self.postMessage({
        type: 'GENERATION_STOPPED',
    });
}

/**

 * Get engine status

 */
function handleStatus() {
    self.postMessage({
        type: 'STATUS_ACK',
        isReady: engine !== null,
        isInitializing: isInitializing,
    });
}

// Signal to main thread that worker is ready
self.postMessage({
    type: 'WORKER_READY',
});