(function () { var API = "http://127.0.0.1:8090", TOKEN = ""; var chat = document.getElementById('rag-chat'); var form = document.getElementById('rag-form'); var qEl = document.getElementById('rag-q'); var btn = document.getElementById('rag-send'); var statusEl = document.getElementById('rag-status'); var modeBtnA = document.getElementById('rag-mode-answer'); var modeBtnS = document.getElementById('rag-mode-search'); var dead = false; var mode = 'answer'; function setMode(m) { mode = m; var on = 'font:inherit;font-size:13px;padding:7px 14px;border:1px solid #8a6d1f;border-radius:8px;background:#8a6d1f;color:#fff;cursor:pointer;'; var off = 'font:inherit;font-size:13px;padding:7px 14px;border:1px solid #d6d0c4;border-radius:8px;background:#fff;color:#6b7280;cursor:pointer;'; modeBtnA.style.cssText = m === 'answer' ? on : off; modeBtnS.style.cssText = m === 'search' ? on : off; } modeBtnA.addEventListener('click', function () { setMode('answer'); }); modeBtnS.addEventListener('click', function () { setMode('search'); }); // готовые вопросы: снижаем порог входа в демо var SAMPLES = [ 'Какой лимит запросов у Битрикс24?', 'Как обновить OpenCode до V2?', 'Как проверить уникальность статьи?', 'Как запустить Ollama, если она не стартует?' ]; function addChips() { if (document.getElementById('rag-chips')) return; var box = document.createElement('div'); box.id = 'rag-chips'; box.style.cssText = 'display:flex;flex-wrap:wrap;gap:6px;margin:10px 0 2px;'; SAMPLES.forEach(function (q) { var b = document.createElement('button'); b.type = 'button'; b.textContent = q; b.style.cssText = 'font:inherit;font-size:12.5px;padding:6px 12px;border:1px solid #d6d0c4;border-radius:16px;background:#fff;cursor:pointer;'; b.addEventListener('click', function () { qEl.value = q; form.dispatchEvent(new Event('submit')); }); box.appendChild(b); }); statusEl.parentNode.insertBefore(box, chat); } fetch(API + '/api/health', TOKEN ? {headers: {'X-RAG-Token': TOKEN}} : undefined) .then(function (r) { return r.json(); }) .then(function (h) { statusEl.textContent = h.ok ? ('в базе ' + h.chunks + ' фрагментов · модель ' + h.gen_model + ' · локальный контур') : 'база знаний не отвечает'; if (h.ok) addChips(); }) .catch(function () { dead = true; statusEl.innerHTML = 'Демо на связи, когда сервер владельца онлайн — попробуйте позже. ' + 'Ваш экземпляр будет работать на вашем сервере с вашими документами.'; btn.disabled = true; }); function esc(s) { return s.replace(/[&<>]/g, function (c) { return {'&':'&','<':'<','>':'>'}[c]; }); } form.addEventListener('submit', function (e) { e.preventDefault(); if (dead) return; var q = qEl.value.trim(); if (!q) return; qEl.value = ''; var d = document.createElement('div'); d.style.cssText = 'background:#fff;border:1px solid #e5e1d8;border-radius:10px;padding:12px 14px;'; d.innerHTML = '
Вы
' + '
' + esc(q) + '
'; chat.appendChild(d); var w = document.createElement('div'); w.style.cssText = 'background:#fff;border:1px solid #e5e1d8;border-radius:10px;padding:12px 14px;'; var label = mode === 'search' ? '⚡ Поиск источников' : 'Ассистент'; var waiting = mode === 'search' ? 'ищу выдержки…' : 'думаю… поиск по документам'; w.innerHTML = '
' + label + '
' + '
' + waiting + '
'; chat.appendChild(w); w.scrollIntoView({behavior: 'smooth'}); fetch(API + '/api/ask', { method: 'POST', headers: {'Content-Type': 'application/json' + (TOKEN ? ',X-RAG-Token:' + TOKEN : '')}, body: JSON.stringify({question: q, mode: mode}) }).then(function (r) { return r.json(); }).then(function (res) { var b = w.children[1]; if (res.error) { b.style.color = '#a33'; b.textContent = 'ошибка: ' + res.error; return; } if (res.mode === 'search' || res.answer === null) { var html = '
Найдено выдержек: ' + (res.sources ? res.sources.length : 0) + '
'; (res.sources || []).forEach(function (s, i) { html += '
' + '
[' + (i + 1) + '] ' + esc(s.source) + '' + (s.heading ? ' § ' + esc(s.heading) : '') + ' · score ' + s.score + '
' + '
' + esc(s.snippet) + '…
'; }); if (!res.sources || !res.sources.length) { html += '
По этому вопросу релевантных выдержек не нашлось.
'; } html += '
⚡ поиск ' + res.took_ms + ' мс · без генерации: дословные выдержки из документов
'; b.style.color = ''; b.innerHTML = html; return; } var html = esc(res.answer || 'пустой ответ'); if (res.sources && res.sources.length) { html += '
' + 'Источники (' + res.sources.length + ')
    ' + res.sources.map(function (s) { return '
  1. ' + esc(s.source) + '' + (s.heading ? ' § ' + esc(s.heading) : '') + '
  2. '; }).join('') + '
'; } html += '
🤖 поиск ' + res.took_ms + ' мс + генерация локальной моделью' + (res.llm_ms ? ' (' + (res.llm_ms / 1000).toFixed(1) + ' с)' : '') + ' · сравнить без ИИ
'; b.style.color = ''; b.innerHTML = html; }).catch(function (err) { var b = w.children[1]; b.style.color = '#a33'; b.textContent = 'ошибка сети: ' + err; }); }); })();