fix(dictation): invalidate run before delayed teardown, remove unwrap

Address two review comments:

1. (useRealtimeDictation.ts) Increment activeRunIdRef immediately in the
   manual-commit cleanup path so late transcript events from the 3s grace
   window are rejected by handleRealtimeEvent. Previously, the run stayed
   valid during the timeout, allowing transcripts to write back into the
   composer after a send, edit-save, or navigation.

2. (transcribe.rs) Replace as_object_mut().unwrap() with a branch that
   builds the correct JSON literal directly. Avoids introducing an unwrap
   in a production path per AGENTS.md rules.
This commit is contained in:
klopez4212
2026-07-11 16:18:37 +01:00
parent 625037e35d
commit cd6a707fc0
2 changed files with 19 additions and 18 deletions
+12 -12
View File
@@ -189,18 +189,18 @@ async fn can_mint_session(
fn build_session_payload(model: &str) -> Value {
let uses_manual_commit = model.contains("realtime-whisper");
let mut audio_input = serde_json::json!({
"transcription": {
"model": model,
}
});
if !uses_manual_commit {
audio_input.as_object_mut().unwrap().insert(
"turn_detection".to_string(),
serde_json::json!({ "type": "server_vad" }),
);
}
let audio_input = if uses_manual_commit {
// Manual commit mode — omit turn_detection entirely.
serde_json::json!({
"transcription": { "model": model }
})
} else {
// Server VAD mode — include turn_detection.
serde_json::json!({
"transcription": { "model": model },
"turn_detection": { "type": "server_vad" }
})
};
serde_json::json!({
"session": {
@@ -102,6 +102,11 @@ export function useRealtimeDictation({
if (needsCommit && dc) {
commitAudioBuffer(dc);
// Mark the run stale immediately so late transcript events from the
// data channel are rejected by handleRealtimeEvent. This prevents
// transcripts from writing back into the composer after a send,
// edit-save, or navigation that triggered this cleanup.
activeRunIdRef.current += 1;
// Stop the mic immediately so no new audio is sent after commit.
for (const track of streamRef.current?.getTracks() ?? []) {
track.stop();
@@ -109,16 +114,12 @@ export function useRealtimeDictation({
streamRef.current = null;
audioCaptureRef.current?.close();
audioCaptureRef.current = null;
// Delay full teardown to allow the final transcript to arrive.
// Delay WebRTC teardown briefly for clean protocol shutdown (the
// commit message needs to reach OpenAI before the channel closes).
const pc = peerConnectionRef.current;
peerConnectionRef.current = null;
dataChannelRef.current = null;
const runId = activeRunIdRef.current;
setTimeout(() => {
// Only tear down if no new run started in the meantime.
if (activeRunIdRef.current === runId) {
activeRunIdRef.current += 1;
}
dc.close();
pc?.close();
}, 3000);