File size: 5,291 Bytes
e9d3310
 
 
 
 
 
 
 
 
 
 
dd1b723
 
e9d3310
 
 
 
 
 
 
 
 
 
 
 
 
 
dd1b723
e9d3310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd1b723
e9d3310
 
 
 
 
 
dd1b723
e9d3310
 
 
 
 
 
 
 
 
 
 
 
 
 
dd1b723
e9d3310
 
 
dd1b723
e9d3310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd1b723
e9d3310
 
 
 
 
 
 
dd1b723
e9d3310
 
 
 
 
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
import { pipeline, TextStreamer } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';

class GemmaChatbot {
    constructor() {
        this.generator = null;
        this.messages = [
            { role: "system", content: "You are a helpful, friendly AI assistant. Keep your responses concise and helpful." }
        ];
        this.isGenerating = false;
        this.initializeElements();
        this.initializeModel();
    }

    initializeElements() {
        this.chatMessages = document.getElementById('chatMessages');
        this.userInput = document.getElementById('userInput');
        this.sendButton = document.getElementById('sendButton');
        this.loadingIndicator = document.getElementById('loadingIndicator');

        this.sendButton.addEventListener('click', () => this.sendMessage());
        this.userInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                this.sendMessage();
            }
        });
    }

    async initializeModel() {
        try {
            console.log('Initializing Gemma model...');
            this.generator = await pipeline(
                'text-generation',
                'onnx-community/gemma-3-270m-it-ONNX',
                { dtype: 'fp32' }
            );
            console.log('Model loaded successfully');
            this.enableChat();
        } catch (error) {
            console.error('Failed to initialize model:', error);
            this.showError('Failed to load AI model. Please refresh the page to try again.');
        }
    }

    enableChat() {
        this.loadingIndicator.style.display = 'none';
        this.userInput.disabled = false;
        this.sendButton.disabled = false;
        this.userInput.focus();
    }

    addMessage(content, isUser = false) {
        const messageDiv = document.createElement('div');
        messageDiv.className = `message ${isUser ? 'user' : 'assistant'}`;
        
        const contentDiv = document.createElement('div');
        contentDiv.className = 'message-content';
        contentDiv.textContent = content;
        
        messageDiv.appendChild(contentDiv);
        this.chatMessages.appendChild(messageDiv);
        this.scrollToBottom();
        
        return contentDiv;
    }

    scrollToBottom() {
        this.chatMessages.scrollTop = this.chatMessages.scrollHeight;
    }

    async sendMessage() {
        const message = this.userInput.value.trim();
        if (!message || this.isGenerating || !this.generator) return;

        this.isGenerating = true;
        this.userInput.value = '';
        this.userInput.disabled = true;
        this.sendButton.disabled = true;

        // Add user message
        this.addMessage(message, true);
        this.messages.push({ role: "user", content: message });

        // Add assistant message placeholder
        const assistantMessageDiv = document.createElement('div');
        assistantMessageDiv.className = 'message assistant';
        
        const contentDiv = document.createElement('div');
        contentDiv.className = 'message-content';
        
        const typingIndicator = document.createElement('span');
        typingIndicator.className = 'typing-indicator';
        typingIndicator.innerHTML = '<span></span><span></span><span></span>';
        contentDiv.appendChild(typingIndicator);
        
        assistantMessageDiv.appendChild(contentDiv);
        this.chatMessages.appendChild(assistantMessageDiv);
        this.scrollToBottom();

        try {
            let fullResponse = '';
            
            // Create custom streamer
            const streamer = new TextStreamer(this.generator.tokenizer, {
                skip_prompt: true,
                skip_special_tokens: true,
                callback_function: (text) => {
                    fullResponse += text;
                    contentDiv.textContent = fullResponse;
                    this.scrollToBottom();
                }
            });

            // Generate response
            const output = await this.generator(this.messages, {
                max_new_tokens: 256,
                do_sample: true,
                temperature: 0.7,
                top_p: 0.9,
                streamer: streamer
            });

            const generatedContent = output[0].generated_text.at(-1).content;
            this.messages.push({ role: "assistant", content: generatedContent });
            
            // Ensure final content is displayed
            contentDiv.textContent = generatedContent;
            
        } catch (error) {
            console.error('Generation error:', error);
            contentDiv.textContent = 'Sorry, I encountered an error. Please try again.';
        } finally {
            this.isGenerating = false;
            this.userInput.disabled = false;
            this.sendButton.disabled = false;
            this.userInput.focus();
        }
    }

    showError(message) {
        this.loadingIndicator.innerHTML = `
            <div class="error-message">
                <p>⚠️ ${message}</p>
            </div>
        `;
    }
}

// Initialize chatbot when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
    new GemmaChatbot();
});