Voice debug drawer can select device and asr no duplicate
This commit is contained in:
@@ -1076,6 +1076,7 @@ export const DebugDrawer: React.FC<{
|
|||||||
const micProcessorRef = useRef<ScriptProcessorNode | null>(null);
|
const micProcessorRef = useRef<ScriptProcessorNode | null>(null);
|
||||||
const micGainRef = useRef<GainNode | null>(null);
|
const micGainRef = useRef<GainNode | null>(null);
|
||||||
const userDraftIndexRef = useRef<number | null>(null);
|
const userDraftIndexRef = useRef<number | null>(null);
|
||||||
|
const lastUserFinalRef = useRef<string>('');
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1382,6 +1383,7 @@ export const DebugDrawer: React.FC<{
|
|||||||
try {
|
try {
|
||||||
setCallStatus('calling');
|
setCallStatus('calling');
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
|
lastUserFinalRef.current = '';
|
||||||
setWsError('');
|
setWsError('');
|
||||||
closeWs();
|
closeWs();
|
||||||
if (textTtsEnabled) await ensureAudioContext();
|
if (textTtsEnabled) await ensureAudioContext();
|
||||||
@@ -1406,6 +1408,7 @@ export const DebugDrawer: React.FC<{
|
|||||||
closeWs();
|
closeWs();
|
||||||
setCallStatus('idle');
|
setCallStatus('idle');
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
|
lastUserFinalRef.current = '';
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1450,6 +1453,7 @@ export const DebugDrawer: React.FC<{
|
|||||||
setWsError('');
|
setWsError('');
|
||||||
// Start every text debug run as a fresh session transcript.
|
// Start every text debug run as a fresh session transcript.
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
|
lastUserFinalRef.current = '';
|
||||||
assistantDraftIndexRef.current = null;
|
assistantDraftIndexRef.current = null;
|
||||||
// Force a fresh WS session so updated assistant runtime config
|
// Force a fresh WS session so updated assistant runtime config
|
||||||
// (voice/model/provider/speed) is applied on session.start.
|
// (voice/model/provider/speed) is applied on session.start.
|
||||||
@@ -1566,6 +1570,7 @@ export const DebugDrawer: React.FC<{
|
|||||||
pendingRejectRef.current = null;
|
pendingRejectRef.current = null;
|
||||||
assistantDraftIndexRef.current = null;
|
assistantDraftIndexRef.current = null;
|
||||||
userDraftIndexRef.current = null;
|
userDraftIndexRef.current = null;
|
||||||
|
lastUserFinalRef.current = '';
|
||||||
setTextSessionStarted(false);
|
setTextSessionStarted(false);
|
||||||
stopPlaybackImmediately();
|
stopPlaybackImmediately();
|
||||||
if (isOpen) setWsStatus('disconnected');
|
if (isOpen) setWsStatus('disconnected');
|
||||||
@@ -1670,7 +1675,8 @@ export const DebugDrawer: React.FC<{
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
const next = [...prev];
|
const next = [...prev];
|
||||||
next[idx] = { ...next[idx], text: next[idx].text + delta };
|
// ASR interim is typically the latest partial text, not a true text delta.
|
||||||
|
next[idx] = { ...next[idx], text: delta };
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -1678,15 +1684,37 @@ export const DebugDrawer: React.FC<{
|
|||||||
|
|
||||||
if (type === 'transcript.final') {
|
if (type === 'transcript.final') {
|
||||||
const finalText = String(payload.text || '');
|
const finalText = String(payload.text || '');
|
||||||
|
if (!finalText) {
|
||||||
|
userDraftIndexRef.current = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lastUserFinalRef.current === finalText) {
|
||||||
|
userDraftIndexRef.current = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
setMessages((prev) => {
|
setMessages((prev) => {
|
||||||
const idx = userDraftIndexRef.current;
|
const idx = userDraftIndexRef.current;
|
||||||
userDraftIndexRef.current = null;
|
userDraftIndexRef.current = null;
|
||||||
if (idx !== null && prev[idx] && prev[idx].role === 'user') {
|
if (idx !== null && prev[idx] && prev[idx].role === 'user') {
|
||||||
const next = [...prev];
|
const next = [...prev];
|
||||||
next[idx] = { ...next[idx], text: finalText || next[idx].text };
|
next[idx] = { ...next[idx], text: finalText || next[idx].text };
|
||||||
|
lastUserFinalRef.current = finalText;
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
if (!finalText) return prev;
|
const last = prev[prev.length - 1];
|
||||||
|
if (last?.role === 'user') {
|
||||||
|
if (last.text === finalText) {
|
||||||
|
lastUserFinalRef.current = finalText;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
if (finalText.startsWith(last.text) || last.text.startsWith(finalText)) {
|
||||||
|
const next = [...prev];
|
||||||
|
next[next.length - 1] = { ...last, text: finalText };
|
||||||
|
lastUserFinalRef.current = finalText;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastUserFinalRef.current = finalText;
|
||||||
return [...prev, { role: 'user', text: finalText }];
|
return [...prev, { role: 'user', text: finalText }];
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -1986,6 +2014,17 @@ export const DebugDrawer: React.FC<{
|
|||||||
<div className="flex-1 flex flex-col min-h-0 space-y-2">
|
<div className="flex-1 flex flex-col min-h-0 space-y-2">
|
||||||
{mode === 'voice' ? (
|
{mode === 'voice' ? (
|
||||||
<div className="flex flex-col h-full min-h-0 animate-in fade-in">
|
<div className="flex flex-col h-full min-h-0 animate-in fade-in">
|
||||||
|
<div className="mb-2">
|
||||||
|
<select
|
||||||
|
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1 text-foreground"
|
||||||
|
value={selectedMic}
|
||||||
|
onChange={(e) => setSelectedMic(e.target.value)}
|
||||||
|
>
|
||||||
|
{devices.filter(d => d.kind === 'audioinput').map(d => (
|
||||||
|
<option key={d.deviceId} value={d.deviceId}>{d.label || 'Mic'}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div className="h-1/3 min-h-[150px] shrink-0 border border-white/5 rounded-md bg-black/20 flex flex-col items-center justify-center text-muted-foreground space-y-4 mb-2 relative overflow-hidden">
|
<div className="h-1/3 min-h-[150px] shrink-0 border border-white/5 rounded-md bg-black/20 flex flex-col items-center justify-center text-muted-foreground space-y-4 mb-2 relative overflow-hidden">
|
||||||
<div className="h-24 w-24 rounded-full bg-primary/10 flex items-center justify-center animate-pulse relative z-10">
|
<div className="h-24 w-24 rounded-full bg-primary/10 flex items-center justify-center animate-pulse relative z-10">
|
||||||
<Mic className="h-10 w-10 text-primary" />
|
<Mic className="h-10 w-10 text-primary" />
|
||||||
|
|||||||
Reference in New Issue
Block a user