Conversation Playground (new section): - WhatsApp-style chat UI with user/assistant speech bubbles - Click-to-record mic button using MediaRecorder API - STT → LLM streaming → TTS pipeline via SSE (POST /api/conversation/turn) - LLM tokens stream into assistant bubble in real time - Audio auto-plays when TTS synthesises the reply - Right-side stats panel: STT / LLM TTFT / LLM total / TTS / Total with bar chart - Turn history list with per-turn total time and pass/fail indicator - Configurable: STT backend, LLM URL + model, TTS backend + voice, system prompt - Conversation history maintained across turns (last 20 messages sent to LLM) - GET /api/conversation/llm-models proxies model list from any OpenAI-compatible LLM XTTS v2 backend: - Registers xtts as a first-class TTS backend (xtts_url setting, display name, capabilities, health/voice discovery, OpenAI-compatible generation) - Added XTTS URL field to Settings → Connections - Use-as-TTS button now saves to xtts_url (not tts_url) - Batch benchmark backend select now refreshes alongside perf/preview selectors VibeVoice fix: - Added /voices to _TTS_VOICE_ENDPOINTS so VibeVoice voices are discovered Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
102 lines
5.4 KiB
HTML
102 lines
5.4 KiB
HTML
<div class="section-head">
|
|
<span class="section-icon"><span class="mdi mdi-forum-outline"></span></span>
|
|
<div class="section-title">
|
|
<h2>Conversation Playground</h2>
|
|
<p>Talk to an AI voice agent. Full STT → LLM → TTS pipeline with real-time streaming and latency stats.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Config bar -->
|
|
<div class="conv-config-bar card">
|
|
<div class="conv-config-row">
|
|
<div class="conv-config-group">
|
|
<label class="conv-cfg-label"><span class="mdi mdi-microphone"></span> Speech to Text</label>
|
|
<select id="conv-stt-select"><option value="configured">Checking...</option></select>
|
|
</div>
|
|
<div class="conv-config-group">
|
|
<label class="conv-cfg-label"><span class="mdi mdi-brain"></span> Language Model</label>
|
|
<div style="display:flex;gap:6px">
|
|
<input id="conv-llm-url" class="conv-url-inp" type="text" placeholder="http://localhost:11434/v1" spellcheck="false">
|
|
<button class="btn-secondary" id="conv-llm-fetch-btn" title="Fetch models"><span class="mdi mdi-refresh"></span></button>
|
|
<select id="conv-llm-model-select"><option value="">— fetch models —</option></select>
|
|
</div>
|
|
</div>
|
|
<div class="conv-config-group">
|
|
<label class="conv-cfg-label"><span class="mdi mdi-text-to-speech"></span> Text to Speech</label>
|
|
<div style="display:flex;gap:6px">
|
|
<select id="conv-tts-backend-select"><option value="">Checking...</option></select>
|
|
<button class="btn-secondary" id="conv-tts-fetch-btn" title="Fetch voices"><span class="mdi mdi-refresh"></span></button>
|
|
<select id="conv-tts-voice-select"><option value="">— fetch voices —</option></select>
|
|
</div>
|
|
</div>
|
|
<div class="conv-config-group conv-config-group--actions">
|
|
<button class="btn-secondary" id="conv-clear-btn" title="Clear conversation"><span class="mdi mdi-delete-outline"></span> Clear</button>
|
|
</div>
|
|
</div>
|
|
<div class="conv-prompt-row">
|
|
<label class="conv-cfg-label"><span class="mdi mdi-text-box-outline"></span> System prompt</label>
|
|
<textarea id="conv-system-prompt" class="conv-system-textarea" rows="1" spellcheck="false">You are a helpful voice assistant. Keep replies short and conversational.</textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main area: chat + stats -->
|
|
<div class="conv-main">
|
|
|
|
<!-- Chat window -->
|
|
<div class="conv-chat-panel">
|
|
<div class="conv-chat-window" id="conv-chat-window">
|
|
<div class="conv-chat-welcome">
|
|
<span class="mdi mdi-forum-outline" style="font-size:32px;opacity:.25"></span>
|
|
<p>Press the microphone button below and start talking.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mic bar -->
|
|
<div class="conv-mic-bar">
|
|
<div class="conv-mic-status" id="conv-mic-status">Ready</div>
|
|
<button class="conv-mic-btn" id="conv-mic-btn" title="Click to record">
|
|
<span class="mdi mdi-microphone" id="conv-mic-icon"></span>
|
|
</button>
|
|
<div class="conv-mic-timer" id="conv-mic-timer"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats panel -->
|
|
<div class="conv-stats-panel">
|
|
<div class="conv-stats-head">Latency</div>
|
|
|
|
<div class="conv-pipeline">
|
|
<div class="conv-pipe-step" id="cps-stt">
|
|
<div class="conv-pipe-label"><span class="mdi mdi-microphone-outline"></span> STT</div>
|
|
<div class="conv-pipe-bar"><div class="conv-pipe-fill" id="cpf-stt"></div></div>
|
|
<div class="conv-pipe-val" id="cpv-stt">—</div>
|
|
</div>
|
|
<div class="conv-pipe-step" id="cps-ttft">
|
|
<div class="conv-pipe-label"><span class="mdi mdi-timer-outline"></span> LLM first token</div>
|
|
<div class="conv-pipe-bar"><div class="conv-pipe-fill" id="cpf-ttft"></div></div>
|
|
<div class="conv-pipe-val" id="cpv-ttft">—</div>
|
|
</div>
|
|
<div class="conv-pipe-step" id="cps-llm">
|
|
<div class="conv-pipe-label"><span class="mdi mdi-brain"></span> LLM total</div>
|
|
<div class="conv-pipe-bar"><div class="conv-pipe-fill" id="cpf-llm"></div></div>
|
|
<div class="conv-pipe-val" id="cpv-llm">—</div>
|
|
</div>
|
|
<div class="conv-pipe-step" id="cps-tts">
|
|
<div class="conv-pipe-label"><span class="mdi mdi-text-to-speech"></span> TTS</div>
|
|
<div class="conv-pipe-bar"><div class="conv-pipe-fill" id="cpf-tts"></div></div>
|
|
<div class="conv-pipe-val" id="cpv-tts">—</div>
|
|
</div>
|
|
<div class="conv-pipe-step conv-pipe-total" id="cps-total">
|
|
<div class="conv-pipe-label"><span class="mdi mdi-timer-check-outline"></span> Total</div>
|
|
<div class="conv-pipe-bar"><div class="conv-pipe-fill" id="cpf-total" style="background:var(--accent)"></div></div>
|
|
<div class="conv-pipe-val" id="cpv-total">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="conv-stats-head" style="margin-top:14px">Turn history</div>
|
|
<div class="conv-turn-history" id="conv-turn-history">
|
|
<div class="conv-history-empty">No turns yet.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|