Adds an option to transcribe server STT audio when stopping voice input. (#1219)

* feat(voice): add transcribe-on-stop recognition option

* fix(voice): tighten transcribe-on-stop cleanup

* fix(voice): address transcribe-on-stop review feedback

* fix(voice): scope transcribe-on-stop to server STT

---------

Co-authored-by: Konstantin Zolin <kzolin@alfabank.ru>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
kostazol
2026-05-13 15:30:25 +03:00
committed by GitHub
co-authored by Konstantin Zolin Bohdan Triapitsyn
parent 9347f97a98
commit c1948fe691
19 changed files with 173 additions and 12 deletions
+52
View File
@@ -53,6 +53,8 @@ export interface UseBrowserVoiceReturn {
startVoice: () => void;
/** Stop voice mode */
stopVoice: () => void;
/** Finish current voice input and process it */
finishVoiceInput: () => void;
/** Whether conversation mode is active */
conversationMode: boolean;
/** Toggle conversation mode */
@@ -834,6 +836,55 @@ export function useBrowserVoice(): UseBrowserVoiceReturn {
setStatus('idle');
setError(null);
}, [stopServerTTS, stopSayTTS]);
const finishVoiceInput = useCallback(() => {
if (!isActiveRef.current) {
return;
}
pendingResumeOnVisibleRef.current = false;
if (deviceChangeRestartTimerRef.current) {
clearTimeout(deviceChangeRestartTimerRef.current);
deviceChangeRestartTimerRef.current = null;
}
setStatus('processing');
if (sttProvider === 'server') {
void audioStreamService.finishListening().then(() => {
window.setTimeout(() => {
if (!isActiveRef.current) {
return;
}
if (pendingFinalTranscriptRef.current || finalTranscriptTimerRef.current) {
return;
}
if (processingMessageRef.current) {
return;
}
isActiveRef.current = false;
processingMessageRef.current = false;
setStatus('idle');
}, FINAL_TRANSCRIPT_SETTLE_MS + 200);
});
return;
}
browserVoiceService.stopListening();
window.setTimeout(() => {
if (!isActiveRef.current) {
return;
}
if (pendingFinalTranscriptRef.current || finalTranscriptTimerRef.current) {
return;
}
if (processingMessageRef.current) {
return;
}
isActiveRef.current = false;
processingMessageRef.current = false;
setStatus('idle');
}, FINAL_TRANSCRIPT_SETTLE_MS + 300);
}, [sttProvider]);
// Cleanup on unmount
useEffect(() => {
@@ -863,6 +914,7 @@ export function useBrowserVoice(): UseBrowserVoiceReturn {
setLanguage,
startVoice,
stopVoice,
finishVoiceInput,
conversationMode,
toggleConversationMode,
prepareVoice,