From d9c5a578f4a309d74efd6b2ebf87e4a0d4042d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Mon, 1 Jun 2026 22:35:57 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D1=8C=20?= =?UTF-8?q?=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20=E2=80=94=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D0=BA=D1=81=D1=82=20=D0=B4=D0=B8=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B0,=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=A1=D0=B1=D1=80=D0=BE=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 20 +++++++++++++++++--- server.js | 48 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/public/index.html b/public/index.html index c0ae255..a397e7d 100644 --- a/public/index.html +++ b/public/index.html @@ -21,6 +21,7 @@
+
@@ -37,7 +38,7 @@ let mediaRecorder, chunks = [], userBlob = null; let isRecording = false, micAnalyser = null, micAnimId = null; - let audioContext = null; + let audioContext = null, sessionSid = null; function setStatus(msg) { status.textContent = msg; } @@ -133,10 +134,12 @@ const cRes = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ model: 'gpt-oss-120b', message: text }), - signal: (() => { const ac = new AbortController(); setTimeout(() => ac.abort(), 60000); return ac.signal; })(), + body: JSON.stringify({ model: 'gpt-oss-120b', message: text, sid: sessionSid }), + signal: (() => { const ac = new AbortController(); setTimeout(() => ac.abort(), 120000); return ac.signal; })(), }); const cData = await cRes.json(); + if (cData.sid) sessionSid = cData.sid; + const cData = await cRes.json(); chat.innerHTML += '
🤖 GPT-OSS: ' + (cData.response || cData.error || 'Ошибка') + '
'; chat.scrollTop = chat.scrollHeight; setStatus('✅ Готово'); @@ -149,6 +152,17 @@ userBlob = null; btnPlay.disabled = true; } + + 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('🧹 История очищена'); + }; \ No newline at end of file diff --git a/server.js b/server.js index e1b7ff9..cdfef69 100644 --- a/server.js +++ b/server.js @@ -40,29 +40,63 @@ 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 } = req.body; + const { model, message, sid, history } = req.body; const m = MODELS[model]; - if (!m) return res.status(400).json({ error: 'Unknown 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, + { 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: [ - { role: 'system', content: m.system }, - { role: 'user', content: message || 'Привет' }, - ], + messages: fullMessages, }), }); const data = await r.json(); - res.json({ response: data.choices?.[0]?.message?.content || 'Нет ответа' }); + const response = data.choices?.[0]?.message?.content || 'Нет ответа'; + + // Сохраняем историю + sessions[id] = [ + ...msgs, + { role: 'user', content: message }, + { role: 'assistant', content: response }, + ]; + + res.json({ response, sid: id }); } 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