806 lines
27 KiB
HTML
806 lines
27 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Hermes Voice Lab CLI Readme</title>
|
|
<style>
|
|
:root {
|
|
color-scheme: light dark;
|
|
--bg: #f5f7f8;
|
|
--panel: #ffffff;
|
|
--text: #172027;
|
|
--muted: #64717d;
|
|
--line: #d7dee4;
|
|
--code-bg: #111820;
|
|
--code-text: #e8f0f5;
|
|
--accent: #20746b;
|
|
--accent-strong: #14554f;
|
|
--warn: #8c5a12;
|
|
--warn-bg: #fff5df;
|
|
--ok-bg: #e7f4ef;
|
|
--ok: #17614f;
|
|
--radius: 8px;
|
|
font-family:
|
|
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
"Segoe UI", sans-serif;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.55;
|
|
}
|
|
|
|
main {
|
|
width: min(1080px, calc(100% - 32px));
|
|
margin: 0 auto;
|
|
padding: 32px 0 48px;
|
|
}
|
|
|
|
header {
|
|
display: grid;
|
|
gap: 14px;
|
|
padding: 28px 0 24px;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3,
|
|
p {
|
|
margin: 0;
|
|
}
|
|
|
|
h1 {
|
|
font-size: clamp(2rem, 5vw, 4.5rem);
|
|
line-height: 0.98;
|
|
letter-spacing: 0;
|
|
max-width: 760px;
|
|
}
|
|
|
|
h2 {
|
|
font-size: 1.5rem;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
h3 {
|
|
font-size: 1rem;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
a {
|
|
color: var(--accent-strong);
|
|
font-weight: 650;
|
|
}
|
|
|
|
.eyebrow {
|
|
color: var(--accent-strong);
|
|
font-size: 0.78rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.lede {
|
|
color: var(--muted);
|
|
font-size: 1.05rem;
|
|
max-width: 760px;
|
|
}
|
|
|
|
.status-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 32px;
|
|
padding: 5px 10px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 999px;
|
|
background: var(--panel);
|
|
color: var(--muted);
|
|
font-size: 0.88rem;
|
|
font-weight: 650;
|
|
}
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr);
|
|
gap: 18px;
|
|
margin-top: 22px;
|
|
}
|
|
|
|
section,
|
|
aside {
|
|
min-width: 0;
|
|
}
|
|
|
|
.panel {
|
|
background: var(--panel);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
padding: 18px;
|
|
}
|
|
|
|
.stack {
|
|
display: grid;
|
|
gap: 18px;
|
|
}
|
|
|
|
.steps {
|
|
display: grid;
|
|
gap: 12px;
|
|
}
|
|
|
|
.step {
|
|
display: grid;
|
|
grid-template-columns: 36px minmax(0, 1fr);
|
|
gap: 12px;
|
|
align-items: start;
|
|
}
|
|
|
|
.step-number {
|
|
width: 36px;
|
|
height: 36px;
|
|
display: inline-grid;
|
|
place-items: center;
|
|
border-radius: 50%;
|
|
background: var(--ok-bg);
|
|
color: var(--ok);
|
|
font-weight: 800;
|
|
font-size: 0.95rem;
|
|
border: 1px solid color-mix(in srgb, var(--ok), transparent 65%);
|
|
}
|
|
|
|
pre {
|
|
margin: 10px 0 0;
|
|
padding: 14px;
|
|
border-radius: var(--radius);
|
|
overflow-x: auto;
|
|
background: var(--code-bg);
|
|
color: var(--code-text);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
font-size: 0.9rem;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
code {
|
|
font-family:
|
|
"Cascadia Code", "SFMono-Regular", Consolas, "Liberation Mono",
|
|
monospace;
|
|
font-size: 0.94em;
|
|
}
|
|
|
|
p code,
|
|
li code,
|
|
td code {
|
|
background: color-mix(in srgb, var(--line), transparent 55%);
|
|
border: 1px solid color-mix(in srgb, var(--line), transparent 20%);
|
|
border-radius: 5px;
|
|
padding: 1px 5px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
ul,
|
|
ol {
|
|
margin: 10px 0 0;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
li + li {
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.callout {
|
|
border-radius: var(--radius);
|
|
border: 1px solid color-mix(in srgb, var(--accent), transparent 65%);
|
|
background: var(--ok-bg);
|
|
color: var(--ok);
|
|
padding: 12px;
|
|
font-weight: 650;
|
|
}
|
|
|
|
.callout.warning {
|
|
border-color: color-mix(in srgb, var(--warn), transparent 60%);
|
|
background: var(--warn-bg);
|
|
color: var(--warn);
|
|
}
|
|
|
|
.waveform {
|
|
height: 88px;
|
|
display: grid;
|
|
grid-template-columns: repeat(32, 1fr);
|
|
gap: 4px;
|
|
align-items: center;
|
|
margin-top: 12px;
|
|
padding: 12px;
|
|
border-radius: var(--radius);
|
|
background: #12191f;
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
}
|
|
|
|
.waveform span {
|
|
display: block;
|
|
height: var(--h);
|
|
min-height: 6px;
|
|
border-radius: 999px;
|
|
background: linear-gradient(180deg, #9fe2d4, #3a9388);
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 10px;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
th,
|
|
td {
|
|
border-bottom: 1px solid var(--line);
|
|
padding: 10px 8px;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
|
|
th {
|
|
color: var(--muted);
|
|
font-size: 0.78rem;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.muted {
|
|
color: var(--muted);
|
|
}
|
|
|
|
footer {
|
|
color: var(--muted);
|
|
font-size: 0.9rem;
|
|
padding-top: 20px;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #0f1418;
|
|
--panel: #151d23;
|
|
--text: #e7edf1;
|
|
--muted: #9aa8b3;
|
|
--line: #2a3842;
|
|
--accent: #62c8b9;
|
|
--accent-strong: #8de0d3;
|
|
--warn: #f0c879;
|
|
--warn-bg: #2d2413;
|
|
--ok-bg: #14302a;
|
|
--ok: #9ce0d1;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 840px) {
|
|
main {
|
|
width: min(100% - 24px, 1080px);
|
|
padding-top: 18px;
|
|
}
|
|
|
|
.grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.step {
|
|
grid-template-columns: 30px minmax(0, 1fr);
|
|
}
|
|
|
|
.step-number {
|
|
width: 30px;
|
|
height: 30px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<header>
|
|
<p class="eyebrow">Hermes-Relay Voice Lab</p>
|
|
<h1>Realtime Voice / Streaming TTS CLI Readme</h1>
|
|
<p class="lede">
|
|
Use this provider lab to test voice providers, save WAV output,
|
|
capture JSONL event logs, compare expression settings, and separate
|
|
deterministic streaming TTS renderers from realtime voice-agent modes.
|
|
</p>
|
|
<div class="status-row" aria-label="Current status">
|
|
<span class="pill">Standalone CLI + relay voice routes</span>
|
|
<span class="pill">OpenAI + Grok + ElevenLabs</span>
|
|
<span class="pill">E2E approach eval</span>
|
|
<span class="pill">Streaming TTS target</span>
|
|
<span class="pill">Realtime agent test mode</span>
|
|
<span class="pill">Outputs ignored by git</span>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="grid">
|
|
<section class="stack">
|
|
<div class="panel">
|
|
<h2>Android Dev Build Test</h2>
|
|
<div class="steps">
|
|
<div class="step">
|
|
<span class="step-number">1</span>
|
|
<div>
|
|
<h3>Start the relay voice routes</h3>
|
|
<p class="muted">
|
|
The Android dev path uses <code>/voice/output/*</code> for
|
|
exact assistant speech and keeps <code>/voice/realtime/*</code>
|
|
for provider-agent lab testing.
|
|
Existing <code>/voice/config</code>,
|
|
<code>/voice/transcribe</code>, and <code>/voice/synthesize</code>
|
|
stay available as basic fallback utilities.
|
|
</p>
|
|
<pre><code>.\scripts\voice-relay-dev.ps1 -Provider grok</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">2</span>
|
|
<div>
|
|
<h3>Pair the Android Studio dev build</h3>
|
|
<p class="muted">
|
|
Use the normal relay pairing flow so the app has a relay
|
|
session token with the <code>voice:realtime</code> grant.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">3</span>
|
|
<div>
|
|
<h3>Open voice mode or the dev testbench</h3>
|
|
<p class="muted">
|
|
The main chat voice overlay uses the relay voice output path
|
|
when available. For isolated provider checks, open
|
|
<code>Settings > Voice</code>. Voice Output exposes
|
|
profile-scoped provider/model/voice dropdowns from relay
|
|
metadata, refreshes the selected provider's options on the
|
|
relay before saving, and keeps advanced manual entry plus
|
|
Save & test. The
|
|
experimental realtime provider editor is shown only when
|
|
Developer options are unlocked. For the standalone dev
|
|
testbench, open <code>Settings > Developer options >
|
|
Realtime voice lab</code>.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Fast Path</h2>
|
|
<div class="steps">
|
|
<div class="step">
|
|
<span class="step-number">1</span>
|
|
<div>
|
|
<h3>Check the local setup</h3>
|
|
<p class="muted">Run this from the repo root.</p>
|
|
<pre><code>cd C:\Users\Bailey\Desktop\Open-Projects\hermes-relay
|
|
python -m pip install -e ".[voice-lab]"
|
|
.\scripts\voice-lab.ps1 -Mode doctor</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">2</span>
|
|
<div>
|
|
<h3>List providers</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Mode providers</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">3</span>
|
|
<div>
|
|
<h3>Run a free local smoke test</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Provider stub</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">4</span>
|
|
<div>
|
|
<h3>Run Grok Voice Agent</h3>
|
|
<p class="muted">
|
|
SuperGrok/Premium+ works through the lab-owned xAI OAuth flow,
|
|
or you can use a direct xAI API key.
|
|
</p>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Mode auth -Provider grok
|
|
.\scripts\voice-lab.ps1 -Provider grok -Text "Hermes voice lab online."</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">5</span>
|
|
<div>
|
|
<h3>Run OpenAI Realtime</h3>
|
|
<p class="muted">
|
|
This spends API quota. The launcher uses
|
|
<code>VOICE_TOOLS_OPENAI_KEY</code> or <code>OPENAI_API_KEY</code>
|
|
from the environment or <code>VOICE_LAB_HOME\.env</code>.
|
|
</p>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Provider openai -Text "Hermes voice lab online."</code></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="step">
|
|
<span class="step-number">6</span>
|
|
<div>
|
|
<h3>Run the E2E approach eval</h3>
|
|
<p class="muted">
|
|
This compares realtime providers and streaming TTS with the
|
|
same acknowledgement, simulated tool wait, pronunciation, and
|
|
volume-consistency prompts.
|
|
</p>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Mode eval `
|
|
-Providers "xai_realtime,openai_realtime,elevenlabs_tts,stub" `
|
|
-OutputDir voice-lab-runs\e2e `
|
|
-Visual off `
|
|
-Json</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>What The Launcher Does</h2>
|
|
<p>
|
|
<code>scripts\voice-lab.ps1</code> wraps
|
|
<code>python -m plugin.voice_lab</code> with test-friendly defaults.
|
|
It picks a provider, creates an output directory, passes expression
|
|
settings, and writes run artifacts.
|
|
</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Option</th>
|
|
<th>Default</th>
|
|
<th>Use</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>-Mode</code></td>
|
|
<td><code>run</code></td>
|
|
<td><code>doctor</code>, <code>providers</code>, <code>auth</code>, <code>tui</code>, <code>run</code>, <code>test</code>, <code>stt</code>, <code>s2s</code>, <code>bench</code>, or <code>eval</code>.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-Providers</code></td>
|
|
<td><code>xai_realtime,openai_realtime,elevenlabs_tts,stub</code></td>
|
|
<td>Provider matrix for <code>-Mode eval</code>.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-Provider</code></td>
|
|
<td><code>auto</code></td>
|
|
<td>Uses Grok when lab-owned xAI OAuth or an xAI key is available, then OpenAI, otherwise <code>stub</code>.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-OutputDir</code></td>
|
|
<td><code>voice-lab-runs</code></td>
|
|
<td>Where WAV files and JSONL event logs are written.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-InputAudio</code></td>
|
|
<td>none</td>
|
|
<td>Required for <code>-Mode stt</code> and <code>-Mode s2s</code>.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-Visual</code></td>
|
|
<td><code>on</code></td>
|
|
<td>Uses the Textual TUI for interactive <code>-Mode tui</code> sessions and shows the PCM waveform meter for one-shot runs.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-Play</code></td>
|
|
<td><code>false</code></td>
|
|
<td>Plays the generated WAV from the terminal after the provider run completes.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-Model</code></td>
|
|
<td><code>gpt-realtime-2</code></td>
|
|
<td>OpenAI Realtime model override.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-Voice</code></td>
|
|
<td><code>marin</code></td>
|
|
<td>OpenAI Realtime voice override.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-XAIModel</code></td>
|
|
<td><code>grok-voice-latest</code></td>
|
|
<td>xAI/Grok Voice Agent model override.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-XAIVoice</code></td>
|
|
<td><code>eve</code></td>
|
|
<td>xAI/Grok Voice Agent voice override.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-ElevenLabsModel</code></td>
|
|
<td><code>eleven_flash_v2_5</code></td>
|
|
<td>ElevenLabs streaming TTS model override.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>-ElevenLabsVoice</code></td>
|
|
<td><code>JBFqnCBsd6RMkjVDRZzb</code></td>
|
|
<td>ElevenLabs voice ID override.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Common Commands</h2>
|
|
<h3>Persistent TUI lab</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Mode tui -Provider grok</code></pre>
|
|
|
|
<h3>Grok with SuperGrok OAuth</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Mode auth -Provider grok
|
|
|
|
.\scripts\voice-lab.ps1 `
|
|
-Provider grok `
|
|
-Text "Testing Grok realtime speech output."</code></pre>
|
|
|
|
<h3>Grok with a one-off xAI key</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Provider grok `
|
|
-XAIKey "xai-..." `
|
|
-Text "Testing Grok realtime speech output."</code></pre>
|
|
|
|
<h3>Grok with tool-call event scaffolding</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Provider grok `
|
|
-ProviderOption tool_scaffold=true `
|
|
-Text "Run a voice tool-call test."</code></pre>
|
|
|
|
<h3>OpenAI with a one-off key</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Provider openai `
|
|
-OpenAIKey "sk-..." `
|
|
-Text "Testing realtime speech output."</code></pre>
|
|
|
|
<h3>OpenAI with a different voice</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Provider openai `
|
|
-Voice marin `
|
|
-Tone calm `
|
|
-Intensity 0.35 `
|
|
-Text "Read this in a precise, steady tone."</code></pre>
|
|
|
|
<h3>ElevenLabs streaming TTS</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Provider elevenlabs `
|
|
-ElevenLabsKey "..." `
|
|
-Text "Testing crisp streamed TTS output."</code></pre>
|
|
|
|
<h3>E2E approach evaluation</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Mode eval `
|
|
-Providers "xai_realtime,openai_realtime,elevenlabs_tts,stub" `
|
|
-OutputDir voice-lab-runs\e2e `
|
|
-Visual off `
|
|
-Json</code></pre>
|
|
|
|
<h3>Disable the waveform</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Provider grok -Visual off</code></pre>
|
|
|
|
<h3>Play the rendered WAV</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Provider grok -Text "Play this after rendering." -Play</code></pre>
|
|
|
|
<h3>Emit JSON for automation</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Provider stub -Json -Visual off</code></pre>
|
|
|
|
<h3>STT scaffold</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Mode stt `
|
|
-Provider stub `
|
|
-InputAudio voice-lab-runs\sample.wav `
|
|
-ExpectedText "expected transcript"</code></pre>
|
|
|
|
<h3>Speech-to-speech scaffold</h3>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Mode s2s `
|
|
-Provider stub `
|
|
-InputAudio voice-lab-runs\sample.wav `
|
|
-Text "I heard the sample and am responding."</code></pre>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Batch Runs</h2>
|
|
<p>
|
|
Create a text file with one prompt per non-empty line, then run
|
|
<code>-Mode bench</code>.
|
|
</p>
|
|
<pre><code>.\scripts\voice-lab.ps1 `
|
|
-Mode bench `
|
|
-Provider grok `
|
|
-ScriptFile .\scripts\voice-lab-phrases.txt `
|
|
-OutputDir voice-lab-runs `
|
|
-Visual on</code></pre>
|
|
<p class="muted">
|
|
Event logs are written under <code>voice-lab-runs\events</code>
|
|
unless <code>-NoEventLog</code> is passed.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>TUI Mode</h2>
|
|
<p>
|
|
The persistent TUI uses a Textual-backed, PulseForge-style keyboard
|
|
interface in an interactive terminal. Piped runs, <code>-Json</code>,
|
|
and <code>-Visual off</code> fall back to the simpler console loop
|
|
so automation stays clean.
|
|
</p>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Mode tui -Provider grok</code></pre>
|
|
<ul>
|
|
<li>Reuse provider auth across turns.</li>
|
|
<li>Tab through dropdowns for provider, model, voice, emotion, tone, pace, style, and command execution.</li>
|
|
<li>Use footer keys: <code>F2</code> provider, <code>F3</code> command, <code>F4</code> prompt, <code>F5</code> emotion, <code>F6</code> tone, <code>F7</code> tool, <code>F8</code> play, <code>F9</code> replay, <code>F10</code> rerun, <code>Ctrl+Q</code> quit.</li>
|
|
<li>Use the command dropdown for run, replay, rerun, save, log, open log, provider option, play, tool, JSON, help, prompt focus, and quit.</li>
|
|
<li>Show generated PCM waveform, latency, provider status, and artifact paths per turn.</li>
|
|
<li>Fallback command mode still supports <code>:provider</code>, <code>:voice</code>, <code>:model</code>, <code>:emotion</code>, <code>:replay</code>, and <code>:save</code>.</li>
|
|
<li>Keep non-interactive <code>-Json -Visual off</code> commands stable for automation.</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
<aside class="stack">
|
|
<div class="panel">
|
|
<h2>Voice Output Framing</h2>
|
|
<p>
|
|
Normal assistant narration should be a speech renderer: final Hermes
|
|
text in, exact audio out. Realtime voice-agent providers stay
|
|
separate for speech-to-speech, provider turn-taking, and tool-event
|
|
experiments.
|
|
</p>
|
|
<ul>
|
|
<li><code>streaming_tts_renderer</code>: primary target for Grok TTS, OpenAI TTS, ElevenLabs, Hermes fallback, and short tool-status lines.</li>
|
|
<li><code>realtime_agent</code>: provider-agent mode for Grok/OpenAI Realtime experiments and non-executing tool-call scaffolds.</li>
|
|
<li>Hermes remains the owner of chat text, tool execution, approvals, and final answers.</li>
|
|
<li>Android sends the active profile to voice config/session routes so named profiles can keep separate <code>voice_output</code> and <code>realtime_voice</code> choices.</li>
|
|
<li>Provider option refreshes stay server-side, return grouped/searchable voice metadata, and validate model/voice/sample-rate compatibility before saving.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Status</h2>
|
|
<div class="callout">
|
|
The Android voice overlay uses relay-mediated streaming TTS for
|
|
deterministic speech rendering and keeps realtime providers as
|
|
voice-agent test modes.
|
|
</div>
|
|
<ul>
|
|
<li>Current Android dev voice uses <code>/voice/output/config</code>, <code>/voice/output/session</code>, and <code>/voice/output/{session_id}</code> when available.</li>
|
|
<li>Basic fallback remains <code>/voice/config</code>, <code>/voice/transcribe</code>, and <code>/voice/synthesize</code>.</li>
|
|
<li>OpenAI Realtime and xAI/Grok Realtime are live provider adapters.</li>
|
|
<li>ElevenLabs streaming TTS is a live cascaded-voice comparison adapter.</li>
|
|
<li><code>xai_tts</code> and <code>openai_tts</code> are first-class streaming TTS renderers.</li>
|
|
<li>xAI custom voices are fetched with pagination and option results are cached briefly by the relay; OpenAI voices remain static from official docs.</li>
|
|
<li><code>stt</code> and <code>s2s</code> are standalone lab scaffolds, not relay route changes.</li>
|
|
<li><code>stub</code> is a local deterministic WAV generator.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Credentials</h2>
|
|
<p>
|
|
The OpenAI provider reads <code>VOICE_TOOLS_OPENAI_KEY</code> first,
|
|
then <code>OPENAI_API_KEY</code>. Both can live in the shell
|
|
environment or <code>VOICE_LAB_HOME\.env</code>.
|
|
</p>
|
|
<pre><code>VOICE_TOOLS_OPENAI_KEY=sk-...</code></pre>
|
|
<p>
|
|
The Grok provider supports lab-owned xAI OAuth from
|
|
<code>VOICE_LAB_HOME\auth\xai-oauth.json</code>.
|
|
It also accepts <code>VOICE_TOOLS_XAI_KEY</code>, <code>XAI_API_KEY</code>,
|
|
or an ephemeral xAI realtime token.
|
|
</p>
|
|
<pre><code>.\scripts\voice-lab.ps1 -Mode auth -Provider grok
|
|
VOICE_TOOLS_XAI_KEY=xai-...</code></pre>
|
|
<p>
|
|
The ElevenLabs provider reads <code>VOICE_TOOLS_ELEVENLABS_KEY</code>
|
|
first, then <code>ELEVENLABS_API_KEY</code>.
|
|
</p>
|
|
<pre><code>VOICE_TOOLS_ELEVENLABS_KEY=...</code></pre>
|
|
<div class="callout warning">
|
|
Do not commit API keys. Do not scrape Grok.com or X browser/session
|
|
cookies; use the lab-owned OAuth flow for subscription auth.
|
|
</div>
|
|
<p class="muted">
|
|
References:
|
|
<a href="https://docs.x.ai/developers/model-capabilities/audio/text-to-speech">xAI TTS</a>,
|
|
<a href="https://docs.x.ai/developers/model-capabilities/audio/voice-agent">xAI Voice Agent</a>,
|
|
<a href="https://developers.openai.com/api/docs/guides/text-to-speech">OpenAI TTS</a>,
|
|
<a href="https://developers.openai.com/api/docs/guides/realtime-conversations">OpenAI Realtime</a>,
|
|
<a href="https://auth.x.ai/.well-known/openid-configuration">xAI OAuth discovery</a>,
|
|
and <a href="https://x.ai/news/grok-hermes">xAI SuperGrok surface announcement</a>.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Waveform</h2>
|
|
<p class="muted">
|
|
The CLI shows a compact terminal meter from generated PCM chunk RMS levels.
|
|
Before audio arrives, it idles low so provider connection state is still visible.
|
|
</p>
|
|
<div class="waveform" aria-hidden="true">
|
|
<span style="--h: 18%"></span>
|
|
<span style="--h: 36%"></span>
|
|
<span style="--h: 64%"></span>
|
|
<span style="--h: 28%"></span>
|
|
<span style="--h: 72%"></span>
|
|
<span style="--h: 46%"></span>
|
|
<span style="--h: 22%"></span>
|
|
<span style="--h: 58%"></span>
|
|
<span style="--h: 82%"></span>
|
|
<span style="--h: 44%"></span>
|
|
<span style="--h: 30%"></span>
|
|
<span style="--h: 68%"></span>
|
|
<span style="--h: 52%"></span>
|
|
<span style="--h: 24%"></span>
|
|
<span style="--h: 76%"></span>
|
|
<span style="--h: 40%"></span>
|
|
<span style="--h: 62%"></span>
|
|
<span style="--h: 34%"></span>
|
|
<span style="--h: 86%"></span>
|
|
<span style="--h: 48%"></span>
|
|
<span style="--h: 26%"></span>
|
|
<span style="--h: 70%"></span>
|
|
<span style="--h: 56%"></span>
|
|
<span style="--h: 32%"></span>
|
|
<span style="--h: 80%"></span>
|
|
<span style="--h: 42%"></span>
|
|
<span style="--h: 20%"></span>
|
|
<span style="--h: 60%"></span>
|
|
<span style="--h: 74%"></span>
|
|
<span style="--h: 38%"></span>
|
|
<span style="--h: 66%"></span>
|
|
<span style="--h: 28%"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Artifacts</h2>
|
|
<ul>
|
|
<li><code>*.wav</code> contains the rendered audio.</li>
|
|
<li><code>*.jsonl</code> contains raw provider event summaries.</li>
|
|
<li><code>summary.json</code>, <code>runs.jsonl</code>, and <code>report.md</code> are written by <code>-Mode eval</code>.</li>
|
|
<li><code>-Play</code> plays the rendered WAV after the run.</li>
|
|
<li><code>voice-lab-runs/</code> and <code>realtime-voice-runs/</code> are ignored by git.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h2>Troubleshooting</h2>
|
|
<ul>
|
|
<li>Run <code>.\scripts\voice-lab.ps1 -Mode doctor</code> first.</li>
|
|
<li>If Grok fails immediately, run <code>.\scripts\voice-lab.ps1 -Mode auth -Provider grok</code>, or set <code>XAI_API_KEY</code> / <code>VOICE_TOOLS_XAI_KEY</code>.</li>
|
|
<li>If OpenAI fails immediately, verify the key exists and is not committed.</li>
|
|
<li>If provider output is noisy, use <code>-Visual off</code>.</li>
|
|
<li>If automation needs clean output, use <code>-Json -Visual off</code>.</li>
|
|
</ul>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
|
|
<footer>
|
|
Source docs: <a href="./realtime-voice-poc.md">realtime-voice-poc.md</a>.
|
|
Current standalone package: <code>plugin.voice_lab</code>.
|
|
</footer>
|
|
</main>
|
|
</body>
|
|
</html>
|