File size: 4,351 Bytes
7abca39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10ad34e
6c74059
7abca39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6c74059
7abca39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
const form = document.getElementById('chat-form');
const input = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const chatHistory = document.getElementById('chat-history');
const apiKeyInput = document.getElementById('api-key');
const dot = document.getElementById('connection-dot');
const statusText = document.getElementById('connection-text');

let conversation = [];

// Auto-adjust textarea height
input.addEventListener('input', function () {
    this.style.height = 'auto';
    this.style.height = (this.scrollHeight) + 'px';
    if (this.value.trim() !== '' && apiKeyInput.value.trim() !== '') {
        sendBtn.disabled = false;
    } else {
        sendBtn.disabled = true;
    }
});

input.addEventListener('keydown', function (e) {
    if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        if (!sendBtn.disabled) form.dispatchEvent(new Event('submit'));
    }
});

apiKeyInput.addEventListener('input', () => {
    if (apiKeyInput.value.trim().length > 10) {
        dot.className = 'dot online';
        statusText.textContent = 'API Key Ready';
        if (input.value.trim() !== '') sendBtn.disabled = false;
    } else {
        dot.className = 'dot offline';
        statusText.textContent = 'Waiting for key...';
        sendBtn.disabled = true;
    }
});

function appendBubble(role, htmlContent) {
    const bubble = document.createElement('div');
    bubble.className = `bubble ${role}`;

    const avatar = document.createElement('div');
    avatar.className = 'avatar';
    avatar.innerHTML = role === 'assistant' ? '<i class="fa-solid fa-robot"></i>' : '<i class="fa-solid fa-user"></i>';

    const msg = document.createElement('div');
    msg.className = 'message';
    msg.innerHTML = htmlContent;

    bubble.appendChild(avatar);
    bubble.appendChild(msg);
    chatHistory.appendChild(bubble);
    chatHistory.scrollTop = chatHistory.scrollHeight;

    return msg;
}

form.addEventListener('submit', async (e) => {
    e.preventDefault();
    const userText = input.value.trim();
    if (!userText) return;

    const apiKey = apiKeyInput.value.trim();
    const llmModel = document.getElementById('llm-model').value.trim();
    const embedModel = document.getElementById('embed-model').value.trim();
    const endpoint = "https://prod-infinit-memory-production.up.railway.app/v1/chat/completions";
    const username = document.getElementById('username').value.trim() || 'demo_user';

    if (!apiKey) {
        alert("Please enter your OpenRouter API Key.");
        return;
    }

    // UI Update
    input.value = '';
    input.style.height = 'auto';
    sendBtn.disabled = true;
    appendBubble('user', `<p>${userText}</p>`);

    conversation.push({ role: "user", content: userText });

    // Show typing
    const typingMsg = appendBubble('assistant', '<div class="typing-indicator">Connecting to Backend...<br><small><i>Injecting context and pinging OpenRouter</i></small></div>');

    try {
        const payload = {
            model: llmModel,
            embed_model: embedModel,
            user: username,
            messages: conversation
        };

        const response = await fetch(endpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`
            },
            body: JSON.stringify(payload)
        });

        if (!response.ok) {
            let errText = await response.text();
            throw new Error(`Server Error (${response.status}): ${errText}`);
        }

        const data = await response.json();
        const aiAnswer = data.choices[0].message.content;

        // Update conversation history
        conversation.push({ role: "assistant", content: aiAnswer });

        // Parse markdown and show
        typingMsg.innerHTML = marked.parse(aiAnswer);

    } catch (err) {
        console.error(err);
        typingMsg.innerHTML = `<span style="color: #ef4444;">Connection failed: ${err.message}</span>`;
        // Remove the failed user message from internal state
        conversation.pop();
    }

    if (input.value.trim() !== '') sendBtn.disabled = false;
});