Spaces:
Configuration error
Configuration error
| document.addEventListener('DOMContentLoaded', () => { | |
| // Chat Interface Logic | |
| const chatMessages = document.getElementById('chat-messages'); | |
| const chatInput = document.getElementById('chat-input'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| const modelSelector = document.getElementById('model-selector'); | |
| const reasoningToggle = document.getElementById('reasoning-toggle'); | |
| const apiKeyInput = document.getElementById('api-key'); | |
| const tokenCount = document.getElementById('token-count'); | |
| const latencyDisplay = document.getElementById('latency-display'); | |
| let conversationHistory = []; | |
| let totalTokens = 0; | |
| // Default API Key (user can override) | |
| const DEFAULT_API_KEY = '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'; | |
| function getApiKey() { | |
| return apiKeyInput.value.trim() || DEFAULT_API_KEY; | |
| } | |
| function addMessage(role, content, reasoning = null) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = 'flex gap-3' + (role === 'user' ? ' flex-row-reverse' : ''); | |
| const avatar = role === 'user' | |
| ? `<div class="w-8 h-8 rounded-full bg-ai-500/20 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="user" class="w-4 h-4 text-ai-400"></i> | |
| </div>` | |
| : `<div class="w-8 h-8 rounded-full bg-cyber-500/20 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="bot" class="w-4 h-4 text-cyber-400"></i> | |
| </div>`; | |
| let contentHtml = `<p class="text-slate-300 text-sm whitespace-pre-wrap">${escapeHtml(content)}</p>`; | |
| if (reasoning) { | |
| contentHtml = ` | |
| <details class="mb-2"> | |
| <summary class="text-xs text-amber-400 cursor-pointer hover:text-amber-300 flex items-center gap-1"> | |
| <i data-feather="zap" class="w-3 h-3"></i> Reasoning Process | |
| </summary> | |
| <div class="mt-2 p-2 bg-slate-900/50 rounded border border-slate-700 text-xs text-slate-400 font-mono whitespace-pre-wrap">${escapeHtml(reasoning)}</div> | |
| </details> | |
| ${contentHtml} | |
| `; | |
| } | |
| messageDiv.innerHTML = ` | |
| ${avatar} | |
| <div class="bg-slate-800/50 border border-slate-700 rounded-lg p-3 max-w-2xl ${role === 'user' ? 'bg-ai-500/10 border-ai-500/30' : ''}"> | |
| <div class="text-xs text-slate-500 mb-1 font-mono">${role === 'user' ? 'YOU' : modelSelector.options[modelSelector.selectedIndex].text.split('(')[0].trim()}</div> | |
| ${contentHtml} | |
| </div> | |
| `; | |
| chatMessages.appendChild(messageDiv); | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| if (window.feather) { | |
| window.feather.replace(); | |
| } | |
| } | |
| function escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| async function sendMessage() { | |
| const message = chatInput.value.trim(); | |
| if (!message) return; | |
| const model = modelSelector.value; | |
| const useReasoning = reasoningToggle.checked && ( | |
| model.includes('nemotron') || model.includes('gpt-oss') | |
| ); | |
| const apiKey = getApiKey(); | |
| // Add user message | |
| addMessage('user', message); | |
| chatInput.value = ''; | |
| // Add to conversation history | |
| conversationHistory.push({ role: 'user', content: message }); | |
| // Show loading | |
| const loadingDiv = document.createElement('div'); | |
| loadingDiv.className = 'flex gap-3'; | |
| loadingDiv.id = 'loading-indicator'; | |
| loadingDiv.innerHTML = ` | |
| <div class="w-8 h-8 rounded-full bg-cyber-500/20 flex items-center justify-center flex-shrink-0"> | |
| <i data-feather="bot" class="w-4 h-4 text-cyber-400 animate-pulse"></i> | |
| </div> | |
| <div class="bg-slate-800/50 border border-slate-700 rounded-lg p-3"> | |
| <div class="flex gap-1"> | |
| <div class="w-2 h-2 bg-cyber-400 rounded-full animate-bounce"></div> | |
| <div class="w-2 h-2 bg-cyber-400 rounded-full animate-bounce" style="animation-delay: 0.1s"></div> | |
| <div class="w-2 h-2 bg-cyber-400 rounded-full animate-bounce" style="animation-delay: 0.2s"></div> | |
| </div> | |
| </div> | |
| `; | |
| chatMessages.appendChild(loadingDiv); | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| const startTime = performance.now(); | |
| try { | |
| const requestBody = { | |
| model: model, | |
| messages: conversationHistory | |
| }; | |
| if (useReasoning) { | |
| requestBody.extra_body = { reasoning: { enabled: true } }; | |
| } | |
| const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${apiKey}`, | |
| 'HTTP-Referer': window.location.href, | |
| 'X-Title': 'NexusNarrative Core' | |
| }, | |
| body: JSON.stringify(requestBody) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`API Error: ${response.status} ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| const endTime = performance.now(); | |
| const latency = Math.round(endTime - startTime); | |
| // Remove loading | |
| document.getElementById('loading-indicator')?.remove(); | |
| // Extract response | |
| const assistantMessage = data.choices[0].message; | |
| const reasoningDetails = assistantMessage.reasoning_details || null; | |
| // Update stats | |
| totalTokens += data.usage?.total_tokens || 0; | |
| tokenCount.textContent = `Tokens: ${totalTokens.toLocaleString()}`; | |
| latencyDisplay.textContent = `Latency: ${latency}ms`; | |
| // Add assistant response | |
| addMessage('assistant', assistantMessage.content, reasoningDetails); | |
| // Update conversation history | |
| if (reasoningDetails) { | |
| conversationHistory.push({ | |
| role: 'assistant', | |
| content: assistantMessage.content, | |
| reasoning_details: reasoningDetails | |
| }); | |
| } else { | |
| conversationHistory.push({ | |
| role: 'assistant', | |
| content: assistantMessage.content | |
| }); | |
| } | |
| } catch (error) { | |
| document.getElementById('loading-indicator')?.remove(); | |
| addMessage('assistant', `Erreur: ${error.message}`); | |
| } | |
| } | |
| // Event listeners | |
| sendBtn.addEventListener('click', sendMessage); | |
| chatInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') sendMessage(); | |
| }); | |
| // Simulate live logs in the terminal | |
| const logsContainer = document.getElementById('terminal-logs'); | |
| const messages = [ | |
| { type: 'DIRECTOR', text: 'Optimizing task queue...', color: 'text-cyber-400' }, | |
| { type: 'SYSTEM', text: 'Garbage collection cleared 24MB.', color: 'text-slate-500' }, | |
| { type: 'WRITER', text: 'Drafting chapter 4 scene 2...', color: 'text-ai-400' }, | |
| { type: 'LORE', text: 'Updating character vector: "Kael"', color: 'text-amber-400' }, | |
| { type: 'SYSTEM', text: 'Health check passed. Latency: 14ms', color: 'text-slate-500' } | |
| ]; | |
| let msgIndex = 0; | |
| function addLog() { | |
| if(!logsContainer) return; | |
| const msg = messages[msgIndex % messages.length]; | |
| const div = document.createElement('div'); | |
| div.className = 'mb-1 opacity-0 transition-opacity duration-500'; | |
| div.innerHTML = `<span class="${msg.color}">[${msg.type}]</span> ${msg.text}`; | |
| // Insert before the last prompt line | |
| const lastLine = logsContainer.lastElementChild; | |
| logsContainer.insertBefore(div, lastLine); | |
| // Trigger reflow for animation | |
| setTimeout(() => div.classList.remove('opacity-0'), 50); | |
| // Auto scroll | |
| logsContainer.scrollTop = logsContainer.scrollHeight; | |
| msgIndex++; | |
| // Random interval between 2s and 5s | |
| const nextTime = Math.random() * 3000 + 2000; | |
| setTimeout(addLog, nextTime); | |
| } | |
| // Start simulation after 1s | |
| setTimeout(addLog, 1000); | |
| }); |