From 66949c07d85d0df51e626cda347ec557eacbe1de Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 28 Mar 2026 14:42:04 +0100 Subject: [PATCH] fix: accumulate transcript chunks into single messages per turn Streaming transcription words now merge into one bubble per agent/user turn instead of creating separate entries for each chunk. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../configurator/VoiceAgentProvider.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/configurator/VoiceAgentProvider.tsx b/src/components/configurator/VoiceAgentProvider.tsx index 1747953..ef6e099 100644 --- a/src/components/configurator/VoiceAgentProvider.tsx +++ b/src/components/configurator/VoiceAgentProvider.tsx @@ -132,6 +132,7 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi const [completedBrief, setCompletedBrief] = useState(null); const [completedFormData, setCompletedFormData] = useState(null); + const turnCompleteRef = useRef(true); const wsRef = useRef(null); const mediaStreamRef = useRef(null); const audioContextRef = useRef(null); @@ -141,7 +142,15 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi const animFrameRef = useRef(0); const addTranscript = useCallback((role: 'user' | 'agent', text: string) => { - setTranscript((prev) => [...prev, { role, text, timestamp: Date.now() }]); + setTranscript((prev) => { + const last = prev[prev.length - 1]; + // Append to last entry if same role and turn is still ongoing + if (last && last.role === role && !turnCompleteRef.current) { + return [...prev.slice(0, -1), { ...last, text: last.text + text }]; + } + turnCompleteRef.current = false; + return [...prev, { role, text, timestamp: Date.now() }]; + }); }, []); const trackAmplitude = useCallback(() => { @@ -369,6 +378,10 @@ export default function VoiceAgentProvider({ locale, children }: VoiceAgentProvi if (msg.serverContent.outputTranscription?.text) { addTranscript('agent', msg.serverContent.outputTranscription.text); } + // Turn complete — next output starts a new transcript entry + if (msg.serverContent.turnComplete || msg.serverContent.generationComplete) { + turnCompleteRef.current = true; + } } // Tool call