Update web client
This commit is contained in:
96
docs/duplex_interaction.svg
Normal file
96
docs/duplex_interaction.svg
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<svg width="1200" height="620" viewBox="0 0 1200 620" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.box { fill:#11131a; stroke:#3a3f4b; stroke-width:1.2; rx:10; ry:10; }
|
||||||
|
.title { font: 600 14px 'Arial'; fill:#f2f3f7; }
|
||||||
|
.text { font: 12px 'Arial'; fill:#c8ccd8; }
|
||||||
|
.arrow { stroke:#7aa2ff; stroke-width:1.6; marker-end:url(#arrow); fill:none; }
|
||||||
|
.arrow2 { stroke:#2dd4bf; stroke-width:1.6; marker-end:url(#arrow); fill:none; }
|
||||||
|
.arrow3 { stroke:#ff6b6b; stroke-width:1.6; marker-end:url(#arrow); fill:none; }
|
||||||
|
.label { font: 11px 'Arial'; fill:#9aa3b2; }
|
||||||
|
</style>
|
||||||
|
<marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
|
||||||
|
<path d="M0,0 L8,4 L0,8 Z" fill="#7aa2ff"/>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect x="40" y="40" width="250" height="120" class="box"/>
|
||||||
|
<text x="60" y="70" class="title">Web Client</text>
|
||||||
|
<text x="60" y="95" class="text">WS JSON commands</text>
|
||||||
|
<text x="60" y="115" class="text">WS binary PCM audio</text>
|
||||||
|
|
||||||
|
<rect x="350" y="40" width="250" height="120" class="box"/>
|
||||||
|
<text x="370" y="70" class="title">FastAPI /ws</text>
|
||||||
|
<text x="370" y="95" class="text">Session + Transport</text>
|
||||||
|
|
||||||
|
<rect x="660" y="40" width="250" height="120" class="box"/>
|
||||||
|
<text x="680" y="70" class="title">DuplexPipeline</text>
|
||||||
|
<text x="680" y="95" class="text">process_audio / process_text</text>
|
||||||
|
|
||||||
|
<rect x="920" y="40" width="240" height="120" class="box"/>
|
||||||
|
<text x="940" y="70" class="title">ConversationManager</text>
|
||||||
|
<text x="940" y="95" class="text">turns + state</text>
|
||||||
|
|
||||||
|
<rect x="660" y="200" width="180" height="100" class="box"/>
|
||||||
|
<text x="680" y="230" class="title">VADProcessor</text>
|
||||||
|
<text x="680" y="255" class="text">speech/silence</text>
|
||||||
|
|
||||||
|
<rect x="860" y="200" width="180" height="100" class="box"/>
|
||||||
|
<text x="880" y="230" class="title">EOU Detector</text>
|
||||||
|
<text x="880" y="255" class="text">end-of-utterance</text>
|
||||||
|
|
||||||
|
<rect x="1060" y="200" width="120" height="100" class="box"/>
|
||||||
|
<text x="1075" y="230" class="title">ASR</text>
|
||||||
|
<text x="1075" y="255" class="text">transcripts</text>
|
||||||
|
|
||||||
|
<rect x="920" y="350" width="240" height="110" class="box"/>
|
||||||
|
<text x="940" y="380" class="title">LLM (stream)</text>
|
||||||
|
<text x="940" y="405" class="text">llmResponse events</text>
|
||||||
|
|
||||||
|
<rect x="660" y="350" width="220" height="110" class="box"/>
|
||||||
|
<text x="680" y="380" class="title">TTS (stream)</text>
|
||||||
|
<text x="680" y="405" class="text">PCM audio</text>
|
||||||
|
|
||||||
|
<rect x="40" y="350" width="250" height="110" class="box"/>
|
||||||
|
<text x="60" y="380" class="title">Web Client</text>
|
||||||
|
<text x="60" y="405" class="text">audio playback + UI</text>
|
||||||
|
|
||||||
|
<path d="M290 80 L350 80" class="arrow"/>
|
||||||
|
<text x="300" y="70" class="label">JSON / PCM</text>
|
||||||
|
|
||||||
|
<path d="M600 80 L660 80" class="arrow"/>
|
||||||
|
<text x="615" y="70" class="label">dispatch</text>
|
||||||
|
|
||||||
|
<path d="M910 80 L920 80" class="arrow"/>
|
||||||
|
<text x="880" y="70" class="label">turn mgmt</text>
|
||||||
|
|
||||||
|
<path d="M750 160 L750 200" class="arrow"/>
|
||||||
|
<text x="705" y="190" class="label">audio chunks</text>
|
||||||
|
|
||||||
|
<path d="M840 250 L860 250" class="arrow"/>
|
||||||
|
<text x="835" y="240" class="label">vad status</text>
|
||||||
|
|
||||||
|
<path d="M1040 250 L1060 250" class="arrow"/>
|
||||||
|
<text x="1010" y="240" class="label">audio buffer</text>
|
||||||
|
|
||||||
|
<path d="M950 300 L950 350" class="arrow2"/>
|
||||||
|
<text x="930" y="340" class="label">EOU -> LLM</text>
|
||||||
|
|
||||||
|
<path d="M880 405 L920 405" class="arrow2"/>
|
||||||
|
<text x="870" y="395" class="label">text stream</text>
|
||||||
|
|
||||||
|
<path d="M660 405 L290 405" class="arrow2"/>
|
||||||
|
<text x="430" y="395" class="label">PCM audio</text>
|
||||||
|
|
||||||
|
<path d="M660 450 L350 450" class="arrow"/>
|
||||||
|
<text x="420" y="440" class="label">events: trackStart/End</text>
|
||||||
|
|
||||||
|
<path d="M350 450 L290 450" class="arrow"/>
|
||||||
|
<text x="315" y="440" class="label">UI updates</text>
|
||||||
|
|
||||||
|
<path d="M750 200 L750 160" class="arrow3"/>
|
||||||
|
<text x="700" y="145" class="label">barge-in detection</text>
|
||||||
|
|
||||||
|
<path d="M760 170 L920 170" class="arrow3"/>
|
||||||
|
<text x="820" y="160" class="label">interrupt event + cancel</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -331,7 +331,6 @@
|
|||||||
<button class="secondary" id="refreshDevicesBtn">Refresh Devices</button>
|
<button class="secondary" id="refreshDevicesBtn">Refresh Devices</button>
|
||||||
<button class="good" id="startMicBtn">Start Mic</button>
|
<button class="good" id="startMicBtn">Start Mic</button>
|
||||||
<button class="secondary" id="stopMicBtn">Stop Mic</button>
|
<button class="secondary" id="stopMicBtn">Stop Mic</button>
|
||||||
<button class="bad" id="interruptBtn">Interrupt</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Chat</h2>
|
<h2>Chat</h2>
|
||||||
@@ -372,7 +371,6 @@
|
|||||||
const outputSelect = document.getElementById("outputSelect");
|
const outputSelect = document.getElementById("outputSelect");
|
||||||
const startMicBtn = document.getElementById("startMicBtn");
|
const startMicBtn = document.getElementById("startMicBtn");
|
||||||
const stopMicBtn = document.getElementById("stopMicBtn");
|
const stopMicBtn = document.getElementById("stopMicBtn");
|
||||||
const interruptBtn = document.getElementById("interruptBtn");
|
|
||||||
const refreshDevicesBtn = document.getElementById("refreshDevicesBtn");
|
const refreshDevicesBtn = document.getElementById("refreshDevicesBtn");
|
||||||
const sendChatBtn = document.getElementById("sendChatBtn");
|
const sendChatBtn = document.getElementById("sendChatBtn");
|
||||||
const clearLogBtn = document.getElementById("clearLogBtn");
|
const clearLogBtn = document.getElementById("clearLogBtn");
|
||||||
@@ -504,6 +502,7 @@
|
|||||||
setStatus(true, "Session open");
|
setStatus(true, "Session open");
|
||||||
logLine("sys", "WebSocket connected");
|
logLine("sys", "WebSocket connected");
|
||||||
ensureAudioContext();
|
ensureAudioContext();
|
||||||
|
sendCommand({ command: "invite", option: { codec: "pcm", sampleRate: targetSampleRate } });
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
@@ -653,7 +652,6 @@
|
|||||||
});
|
});
|
||||||
startMicBtn.addEventListener("click", startMic);
|
startMicBtn.addEventListener("click", startMic);
|
||||||
stopMicBtn.addEventListener("click", stopMic);
|
stopMicBtn.addEventListener("click", stopMic);
|
||||||
interruptBtn.addEventListener("click", () => sendCommand({ command: "interrupt" }));
|
|
||||||
sendChatBtn.addEventListener("click", () => {
|
sendChatBtn.addEventListener("click", () => {
|
||||||
const text = chatInput.value.trim();
|
const text = chatInput.value.trim();
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user