From b8bfb501b95343b90983cbe5dc74d4f8719e601e Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 9 Jul 2026 23:58:25 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=9D=8C=EC=84=B1=ED=86=B5=ED=99=94=20S?= =?UTF-8?q?TT=20=ED=99=98=EA=B0=81=20=EC=99=84=ED=99=94=20+=20=ED=88=B4?= =?UTF-8?q?=EC=BD=9C=20=ED=84=B4=20TTS=20=EB=88=84=EB=9D=BD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20+=20=EB=B3=BC=EB=A5=A8=20=EC=8A=AC=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=8D=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - voice_engine.py: condition_on_previous_text=False로 whisper가 애매한 구간에서 그럴듯한 문장을 지어내는 환각 완화 - app.js/voice-call.js: 툴 사용 턴(웹검색 등)에서 finalAnswer가 partialContent를 통째로 덮어쓰면서 음성통화의 spokenUpTo 오프셋이 무효화돼 텍스트는 나오는데 음성이 안 나오는 버그 수정 - index.html/voice-call.js: 통화 중 GainNode 기반 인앱 볼륨 슬라이더 추가 (일부 모바일 브라우저에서 마이크 사용 중엔 하드웨어 볼륨 버튼이 통화 오디오에 반영 안 되는 문제 우회) Co-Authored-By: Claude Sonnet 5 --- src/tools/voice_engine.py | 8 ++++++-- web-ui/app.js | 8 ++++++++ web-ui/index.html | 18 ++++++++++++++++++ web-ui/voice-call.js | 29 ++++++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/tools/voice_engine.py b/src/tools/voice_engine.py index 9bd1b12..b2a8863 100644 --- a/src/tools/voice_engine.py +++ b/src/tools/voice_engine.py @@ -119,7 +119,9 @@ class Engine: f.write(audio_bytes) path = f.name try: - segments, info = self.stt_model.transcribe(path, language=language or None, beam_size=5, vad_filter=True) + segments, info = self.stt_model.transcribe( + path, language=language or None, beam_size=5, vad_filter=True, condition_on_previous_text=False, + ) text = ''.join(seg.text for seg in segments).strip() return text, (info.language if info else language) finally: @@ -140,7 +142,9 @@ class Engine: # ── Streaming STT (partial decode of an accumulating PCM buffer) ────── def transcribe_pcm(self, pcm: bytes, language: str): audio = pcm16_bytes_to_float(pcm) - segments, info = self.stt_model.transcribe(audio, language=language or None, beam_size=1, vad_filter=True) + segments, info = self.stt_model.transcribe( + audio, language=language or None, beam_size=1, vad_filter=True, condition_on_previous_text=False, + ) text = ''.join(seg.text for seg in segments).strip() return text, (info.language if info else language) diff --git a/web-ui/app.js b/web-ui/app.js index 3730f4e..4793b76 100644 --- a/web-ui/app.js +++ b/web-ui/app.js @@ -1561,6 +1561,7 @@ async function sendChat(queuedMessage = null) { const token = event.text || event.content || event.delta || ''; if (token) { partialContent += token; + if (window._voiceCallOnToken) window._voiceCallOnToken(partialContent); } break; } @@ -1813,6 +1814,11 @@ async function sendChat(queuedMessage = null) { if (s.finalAnswer) { addProcessEntry('final', s.finalAnswer); partialContent = s.finalAnswer; // track for stop + // partialContent just got replaced wholesale (not appended to), so + // any voice-call spokenUpTo offset computed against the old string + // is now meaningless — reset before feeding the new text through. + if (window._voiceCallResetSpoken) window._voiceCallResetSpoken(); + if (window._voiceCallOnToken) window._voiceCallOnToken(partialContent); } break; } @@ -1903,6 +1909,8 @@ async function sendChat(queuedMessage = null) { } } + if (window._voiceCallOnTurnDone) window._voiceCallOnTurnDone(finalReply || partialContent); + if (finalReply) { // Append file cards for any attachments sent via the 'files' SSE event if (turnFileLinks.length > 0) { diff --git a/web-ui/index.html b/web-ui/index.html index f04d990..6bba984 100644 --- a/web-ui/index.html +++ b/web-ui/index.html @@ -387,10 +387,18 @@ +
+