You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
4.4 KiB
100 lines
4.4 KiB
{% extends "base.html" %} |
|
{% block title %}Настройки — LLM{% endblock %} |
|
|
|
{% block content %} |
|
<section class="{% if ui_variant == 'legacy' %}surface-card{% else %}rounded-2xl bg-white shadow-sm border border-ink-300/60{% endif %} p-6 max-w-2xl"> |
|
<div class="flex items-center gap-2"> |
|
<span class="material-symbols-outlined text-brand-600">settings</span> |
|
<h1 class="text-2xl font-semibold">Настройки</h1> |
|
</div> |
|
|
|
<h2 class="mt-5 font-semibold {% if ui_variant == 'legacy' %}font-headline{% endif %}">Подключение к LLM</h2> |
|
<p class="mt-1 text-sm text-ink-500"> |
|
Ключ задаётся в <code class="px-1 py-0.5 rounded bg-ink-100">.env</code> сервера |
|
(общий, не на пользователя). Поддерживаются DeepSeek и OpenAI-совместимые API. |
|
После изменения <code class="px-1 py-0.5 rounded bg-ink-100">.env</code> нужен рестарт процесса. |
|
</p> |
|
|
|
<dl class="mt-4 grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-2 text-sm"> |
|
<dt class="text-ink-500">Статус ключа</dt> |
|
<dd> |
|
{% if configured %} |
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-green-50 text-green-700 border border-green-200"> |
|
<span class="material-symbols-outlined text-base">check_circle</span> Задан |
|
</span> |
|
{% else %} |
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-red-50 text-red-700 border border-red-200"> |
|
<span class="material-symbols-outlined text-base">error</span> Не задан |
|
</span> |
|
{% endif %} |
|
</dd> |
|
<dt class="text-ink-500">Провайдер</dt> |
|
<dd>{{ provider or '—' }}</dd> |
|
<dt class="text-ink-500">Модель</dt> |
|
<dd>{{ model or '—' }}</dd> |
|
<dt class="text-ink-500">Base URL</dt> |
|
<dd class="break-all">{{ base_url or '—' }}</dd> |
|
</dl> |
|
|
|
{% if not configured %} |
|
<div class="mt-5 rounded-lg bg-ink-100/60 border border-ink-300/60 p-4 text-sm"> |
|
<p class="font-medium">Как задать ключ</p> |
|
<pre class="mt-2 text-xs whitespace-pre-wrap font-mono">DEEPSEEK_API_KEY=sk-... |
|
# либо |
|
OPENAI_API_KEY=sk-... |
|
# опционально: |
|
# LLM_BASE_URL=https://api.deepseek.com/v1 |
|
# LLM_MODEL=deepseek-chat</pre> |
|
<p class="mt-2 text-ink-500"> |
|
Файл: <code>flask_app/.env</code>. После сохранения — рестарт процесса. |
|
</p> |
|
</div> |
|
{% endif %} |
|
|
|
<div class="mt-5 flex items-center gap-3"> |
|
<button id="btn-ping" |
|
class="{% if ui_variant == 'legacy' %}btn btn-primary{% else %}inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-brand-600 hover:bg-brand-700 text-white text-sm{% endif %}"> |
|
<span class="material-symbols-outlined text-base">cable</span> |
|
Проверить подключение |
|
</button> |
|
<span id="ping-status" class="text-sm text-ink-500"></span> |
|
</div> |
|
|
|
<div id="ping-result" class="mt-4 hidden text-sm rounded-lg p-3 border"></div> |
|
</section> |
|
{% endblock %} |
|
|
|
{% block scripts %} |
|
<script> |
|
(() => { |
|
const btn = document.getElementById('btn-ping'); |
|
const status = document.getElementById('ping-status'); |
|
const result = document.getElementById('ping-result'); |
|
btn.addEventListener('click', async () => { |
|
status.textContent = 'Запрос…'; |
|
btn.disabled = true; |
|
try { |
|
const r = await fetch('/api/llm/ping', { method: 'POST' }); |
|
const d = await r.json(); |
|
result.classList.remove('hidden', 'bg-green-50', 'border-green-200', 'text-green-800', |
|
'bg-red-50', 'border-red-200', 'text-red-800'); |
|
if (d.ok) { |
|
result.classList.add('bg-green-50', 'border-green-200', 'text-green-800'); |
|
result.innerHTML = `<b>OK</b> · ${d.provider} / ${d.model} · ${d.latencyMs} мс` |
|
+ (d.sample ? `<br><span class="text-xs opacity-80">Ответ: ${d.sample.replace(/</g,'<')}</span>` : ''); |
|
} else { |
|
result.classList.add('bg-red-50', 'border-red-200', 'text-red-800'); |
|
result.innerHTML = `<b>Ошибка</b> · ${d.code || ''}<br>${(d.error || '').replace(/</g,'<')}`; |
|
} |
|
} catch (e) { |
|
result.classList.remove('hidden'); |
|
result.classList.add('bg-red-50', 'border-red-200', 'text-red-800'); |
|
result.textContent = e.message || 'Сбой запроса.'; |
|
} finally { |
|
btn.disabled = false; |
|
status.textContent = ''; |
|
} |
|
}); |
|
})(); |
|
</script> |
|
{% endblock %}
|
|
|