diff --git a/public/index.html b/public/index.html index 7be2c74..4535335 100644 --- a/public/index.html +++ b/public/index.html @@ -52,7 +52,7 @@ let mediaRecorder, chunks = [], userBlob = null; let isRecording = false, micAnalyser = null, micAnimId = null; - let audioContext = null, sessionSid = null; + let audioContext = null, chatHistory = []; let thinkStart = 0, thinkTimer = null; // История — массив {q, a, time} @@ -191,13 +191,15 @@ const cRes = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ model: 'gpt-oss-120b', message: text, sid: sessionSid }), + body: JSON.stringify({ model: 'gpt-oss-120b', message: text, history: chatHistory }), signal: (() => { const a = new AbortController(); setTimeout(() => a.abort(), 120000); return a.signal; })(), }); stopThinkTimer(); const cData = await cRes.json(); - if (cData.sid) sessionSid = cData.sid; const resp = cData.response || cData.error || 'Ошибка'; + chatHistory.push({ role: 'user', content: text }); + chatHistory.push({ role: 'assistant', content: resp }); + if (chatHistory.length > 40) chatHistory = chatHistory.slice(-40); chat.innerHTML += '
🤖 ' + fmt(resp) + '
'; chat.scrollTop = chat.scrollHeight; setStatus('✅ Готово — ' + ((Date.now() - thinkStart) / 1000).toFixed(1) + 'с'); @@ -223,9 +225,8 @@ } function stopThinkTimer() { if (thinkTimer) { clearInterval(thinkTimer); thinkTimer = null; } } - document.getElementById('btnReset').onclick = async () => { - await fetch('/api/reset', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sid: sessionSid }) }); - sessionSid = null; chat.innerHTML = ''; setStatus('🧹 Очищено'); + document.getElementById('btnReset').onclick = () => { + chatHistory = []; chat.innerHTML = ''; setStatus('🧹 Очищено'); }; diff --git a/server.js b/server.js index cdfef69..a4fef17 100644 --- a/server.js +++ b/server.js @@ -40,63 +40,34 @@ app.post('/api/whisper', upload.single('audio'), async (req, res) => { } }); -// ====== Session memory ====== -const sessions = {}; // sessionId → messages[] - -app.get('/api/history', (req, res) => { - const sid = req.query.sid || 'default'; - res.json({ messages: sessions[sid] || [] }); -}); - app.post('/api/chat', async (req, res) => { try { - const { model, message, sid, history } = req.body; + const { model, message, history } = req.body; const m = MODELS[model]; if (!m) return res.status(400).json({ error: 'Неизвестная модель' }); - // Собираем историю: system + предыдущие сообщения + новое - const id = sid || 'default'; - const msgs = history || sessions[id] || []; const fullMessages = [ { role: 'system', content: m.system }, - ...msgs, + ...(history || []), { role: 'user', content: message || 'Привет' }, ]; - // Обрезаем чтобы не выйти за лимит токенов + // Обрезаем чтобы не выйти за лимит while (JSON.stringify(fullMessages).length > 32000 && fullMessages.length > 2) fullMessages.splice(1, 2); const r = await fetch(`${API_BASE}/v1/chat/completions`, { method: 'POST', headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, - body: JSON.stringify({ - model: m.id, temperature: m.temp, max_tokens: m.tokens, - messages: fullMessages, - }), + body: JSON.stringify({ model: m.id, temperature: m.temp, max_tokens: m.tokens, messages: fullMessages }), }); const data = await r.json(); - const response = data.choices?.[0]?.message?.content || 'Нет ответа'; - - // Сохраняем историю - sessions[id] = [ - ...msgs, - { role: 'user', content: message }, - { role: 'assistant', content: response }, - ]; - - res.json({ response, sid: id }); + res.json({ response: data.choices?.[0]?.message?.content || 'Нет ответа' }); } catch (e) { res.status(500).json({ error: e.message }); } }); -app.post('/api/reset', (req, res) => { - const sid = req.body.sid || 'default'; - delete sessions[sid]; - res.json({ ok: true }); -}); - app.get('/', (_req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html'))); app.listen(PORT, () => console.log(`Say:${PORT}`)); \ No newline at end of file