From 414503b8b631020f48da02a7992d134ecaced3d8 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Sat, 22 Aug 2026 08:04:24 +0900 Subject: [PATCH] fix(voice): forward streamed STT language and prompt Co-authored-by: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> --- src/agents/voice/models/openai_stt.py | 11 +++- tests/voice/test_openai_stt_session_config.py | 61 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/voice/test_openai_stt_session_config.py diff --git a/src/agents/voice/models/openai_stt.py b/src/agents/voice/models/openai_stt.py index cf504d8892..a1d95746ea 100644 --- a/src/agents/voice/models/openai_stt.py +++ b/src/agents/voice/models/openai_stt.py @@ -175,6 +175,15 @@ async def _event_listener(self) -> None: async def _configure_session(self) -> None: assert self._websocket is not None, "Websocket not initialized" + transcription_config: dict[str, Any] = {"model": self._model} + if self._settings.language is not None: + if self._model in {"gpt-transcribe", "gpt-live-transcribe"}: + transcription_config["languages"] = [self._settings.language] + else: + transcription_config["language"] = self._settings.language + if self._settings.prompt is not None: + transcription_config["prompt"] = self._settings.prompt + await self._websocket.send( json.dumps( { @@ -184,7 +193,7 @@ async def _configure_session(self) -> None: "audio": { "input": { "format": {"type": "audio/pcm", "rate": 24000}, - "transcription": {"model": self._model}, + "transcription": transcription_config, "turn_detection": self._turn_detection, } }, diff --git a/tests/voice/test_openai_stt_session_config.py b/tests/voice/test_openai_stt_session_config.py new file mode 100644 index 0000000000..65388f6c7c --- /dev/null +++ b/tests/voice/test_openai_stt_session_config.py @@ -0,0 +1,61 @@ +import json +from unittest.mock import AsyncMock + +import pytest + +from agents.voice import StreamedAudioInput, STTModelSettings +from agents.voice.models.openai_stt import OpenAISTTTranscriptionSession + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("model", "language_field", "language_value"), + [ + ("gpt-4o-transcribe", "language", "fr"), + ("gpt-transcribe", "languages", ["fr"]), + ("gpt-live-transcribe", "languages", ["fr"]), + ], +) +async def test_streaming_stt_sends_language_and_prompt( + model: str, + language_field: str, + language_value: str | list[str], +) -> None: + session = OpenAISTTTranscriptionSession( + input=StreamedAudioInput(), + client=AsyncMock(api_key="FAKE_KEY"), + model=model, + settings=STTModelSettings(language="fr", prompt="domain vocabulary"), + trace_include_sensitive_data=False, + trace_include_sensitive_audio_data=False, + ) + websocket = AsyncMock() + session._websocket = websocket + + await session._configure_session() + + payload = json.loads(websocket.send.await_args.args[0]) + assert payload["session"]["audio"]["input"]["transcription"] == { + "model": model, + language_field: language_value, + "prompt": "domain vocabulary", + } + + +@pytest.mark.asyncio +async def test_streaming_stt_omits_unset_language_and_prompt() -> None: + session = OpenAISTTTranscriptionSession( + input=StreamedAudioInput(), + client=AsyncMock(api_key="FAKE_KEY"), + model="gpt-4o-transcribe", + settings=STTModelSettings(), + trace_include_sensitive_data=False, + trace_include_sensitive_audio_data=False, + ) + websocket = AsyncMock() + session._websocket = websocket + + await session._configure_session() + + payload = json.loads(websocket.send.await_args.args[0]) + assert payload["session"]["audio"]["input"]["transcription"] == {"model": "gpt-4o-transcribe"}