rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
438d1fa
·
1 Parent(s): 3f15c03

fix(voice): KI-256 — PTT primary + live voice BETA confirm gate

Browse files

Per user decision: keep BOTH PTT (default + reliable) and live voice
(BETA), but make the trade-off explicit so users don't enable always-on
voice expecting production reliability.

Architecture (no change to STT/TTS layer — Sarvam preserved everywhere):
- PTT (default): MediaRecorder → Sarvam STT (authoritative, Indic-aware)
+ Web Speech runs in parallel ONLY for live interim transcript display
in the gray-italic strip. Sarvam transcript wins on release.
- Live voice (BETA, off by default, requires one-time accept): existing
useStreamingVoice with Web Speech driving utterance commit + Sarvam
blob as cleanup. Same path as before, just gated behind a warning.
- Sarvam TTS for ALL bot replies regardless of input mode.

Indic preserved: when user types in Hindi/Tamil/Marathi (via PTT or
live), Sarvam STT transcribes the blob. Web Speech may garble the
interim display on Indic but its result is discarded — the Sarvam
text submitted to /api/chat is correct.

Frontend changes (page.tsx only):
- Clicking the "Voice off · Always-on (BETA)" pill now shows a
confirm() listing the 3 known instability modes (cut-off mid sentence,
echo, late utterance pickup) + recommending PTT + noting Sarvam handles
Hindi/Indic correctly. User must accept once; persisted via
localStorage.insurance_live_beta_ack so they don't get nagged again.
- Pill colour shifted from emerald → amber when live is on, to signal
BETA state visually even after acceptance.
- Pill label: "Voice off · Always-on (BETA)" / "Voice on · BETA —
unstable" / "Listening… (BETA)". Title tooltips updated.
- userPrefersLive default unchanged (already false, see KI-131/ADR-033).
- Stale localStorage wipe-on-load also unchanged.

Sarvam pipeline, Web Speech interim display in PTT, useStreamingVoice
hook, voice_resilience.ts — ALL untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. frontend/src/app/page.tsx +29 -7
frontend/src/app/page.tsx CHANGED
@@ -1459,24 +1459,46 @@ export default function Page() {
1459
  {!live.micPermissionDenied ? (
1460
  <button
1461
  type="button"
1462
- onClick={() => setUserPrefersLive((p) => !p)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1463
  className={`flex items-center gap-1.5 px-2 py-0.5 rounded-full border text-xs font-medium transition cursor-pointer ${
1464
  userPrefersLive
1465
- ? "border-emerald-300 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300"
1466
  : "border-[var(--border)] text-[var(--muted-foreground)] hover:border-[var(--primary)] hover:text-[var(--primary)]"
1467
  }`}
1468
  title={userPrefersLive
1469
- ? "Voice on bot is always listening, you can speak over it. Click to turn off."
1470
- : "Click to turn on always-on listening (you can also use 🎤 push-to-talk for one turn)"}
1471
  >
1472
  <span className={`inline-block w-2 h-2 rounded-full ${
1473
  userPrefersLive
1474
- ? (live.recording ? "bg-red-500 animate-pulse" : "bg-emerald-500 animate-pulse")
1475
  : "bg-gray-400"
1476
  }`} />
1477
  {userPrefersLive
1478
- ? (live.recording ? "Listening…" : "Voice on just speak")
1479
- : "Voice off click to enable always-on"}
1480
  </button>
1481
  ) : (
1482
  <span className="text-rose-500 text-xs" title="Allow mic in your browser site settings, or use the 🎤 push-to-talk button">
 
1459
  {!live.micPermissionDenied ? (
1460
  <button
1461
  type="button"
1462
+ onClick={() => {
1463
+ // KI-256 — show a one-time confirm when enabling live voice
1464
+ // so users know it's beta + may cut them off / echo.
1465
+ // Once they accept, persist a flag so we don't nag again.
1466
+ if (!userPrefersLive) {
1467
+ const seen = (typeof window !== "undefined")
1468
+ ? window.localStorage.getItem("insurance_live_beta_ack") === "1"
1469
+ : true;
1470
+ if (!seen) {
1471
+ const ok = window.confirm(
1472
+ "Always-on voice is BETA and currently unstable:\n\n" +
1473
+ "• May cut you off mid-sentence\n" +
1474
+ "• May echo the bot's own voice\n" +
1475
+ "• May pick up later utterances incorrectly\n\n" +
1476
+ "Push-to-talk (🎤 button) is fully stable and uses Sarvam STT (handles Hindi/Indic correctly).\n\n" +
1477
+ "Enable always-on anyway?"
1478
+ );
1479
+ if (!ok) return;
1480
+ try { window.localStorage.setItem("insurance_live_beta_ack", "1"); } catch { /* ignore */ }
1481
+ }
1482
+ }
1483
+ setUserPrefersLive((p) => !p);
1484
+ }}
1485
  className={`flex items-center gap-1.5 px-2 py-0.5 rounded-full border text-xs font-medium transition cursor-pointer ${
1486
  userPrefersLive
1487
+ ? "border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-100 dark:border-amber-700 dark:bg-amber-900/30 dark:text-amber-300"
1488
  : "border-[var(--border)] text-[var(--muted-foreground)] hover:border-[var(--primary)] hover:text-[var(--primary)]"
1489
  }`}
1490
  title={userPrefersLive
1491
+ ? "Always-on voice is BETA may cut you off / echo. PTT (🎤) is more reliable."
1492
+ : "Always-on voice (BETA unstable). Prefer 🎤 push-to-talk for reliable input."}
1493
  >
1494
  <span className={`inline-block w-2 h-2 rounded-full ${
1495
  userPrefersLive
1496
+ ? (live.recording ? "bg-red-500 animate-pulse" : "bg-amber-500 animate-pulse")
1497
  : "bg-gray-400"
1498
  }`} />
1499
  {userPrefersLive
1500
+ ? (live.recording ? "Listening… (BETA)" : "Voice on · BETA — unstable")
1501
+ : "Voice off · Always-on (BETA)"}
1502
  </button>
1503
  ) : (
1504
  <span className="text-rose-500 text-xs" title="Allow mic in your browser site settings, or use the 🎤 push-to-talk button">