From ef226c8a8e61d293faeeb2c4f2bf4597e8e08c57 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Tue, 28 Apr 2026 16:19:14 -0400 Subject: [PATCH] fix: silence _settings NotGiven leaks and tighten Google STT language method Six pyright errors followed the same pattern: a value flowed out of `self._settings.X` (typed `T | _NotGiven`) into a context that wanted the plain `T`. Wrap each with `assert_given(...)` so the sentinel gets stripped at the boundary: - aws/nova_sonic/llm.py: `_settings.model` (in InvokeModel...Input) and `_settings.system_instruction` (passed to the adapter). - deepgram/flux/base.py: iterating `_settings.keyterm`. - google/stt.py: iterating `_settings.languages`. - google/tts.py: iterating `_settings.speaker_configs`. - openai/base_llm.py: `_settings.system_instruction` passed to the adapter. Also takes a deeper pass at the related Google STT issue: the override of `language_to_service_language` had been broadened to take `Language | list[Language]` and return `str | list[str]`, a Liskov violation against the base's `Language -> str | None` contract. External callers always pass a single Language, and the only consumer of the list path was Google STT's own `_get_language_codes`. Restore the override to a single-Language signature and let `_get_language_codes` iterate. The override is also tightened to return `str` (narrower than the base's `str | None`, which is LSP-compatible) since it always falls back to `"en-US"` rather than returning None. Net: -7 pyright errors (full-config run: 782 -> 775). --- src/pipecat/services/aws/nova_sonic/llm.py | 6 ++++-- src/pipecat/services/deepgram/flux/base.py | 2 +- src/pipecat/services/google/stt.py | 20 ++++++++++++-------- src/pipecat/services/google/tts.py | 2 +- src/pipecat/services/openai/base_llm.py | 4 ++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/pipecat/services/aws/nova_sonic/llm.py b/src/pipecat/services/aws/nova_sonic/llm.py index 55a4dd813..7c09ad6d0 100644 --- a/src/pipecat/services/aws/nova_sonic/llm.py +++ b/src/pipecat/services/aws/nova_sonic/llm.py @@ -974,7 +974,9 @@ class AWSNovaSonicLLMService(LLMService[AWSNovaSonicLLMAdapter]): async def open_stream(self, client): """Open a bidirectional stream on the given client.""" return await client.invoke_model_with_bidirectional_stream( - InvokeModelWithBidirectionalStreamOperationInput(model_id=self._settings.model) + InvokeModelWithBidirectionalStreamOperationInput( + model_id=assert_given(self._settings.model) + ) ) async def send_event(self, event_json: str, stream): @@ -1127,7 +1129,7 @@ class AWSNovaSonicLLMService(LLMService[AWSNovaSonicLLMAdapter]): return None, [] adapter = self.get_llm_adapter() llm_params = adapter.get_llm_invocation_params( - self._context, system_instruction=self._settings.system_instruction + self._context, system_instruction=assert_given(self._settings.system_instruction) ) tools = ( llm_params["tools"] diff --git a/src/pipecat/services/deepgram/flux/base.py b/src/pipecat/services/deepgram/flux/base.py index f2042d799..173da5dc7 100644 --- a/src/pipecat/services/deepgram/flux/base.py +++ b/src/pipecat/services/deepgram/flux/base.py @@ -253,7 +253,7 @@ class DeepgramFluxSTTBase(STTService): params.append(f"mip_opt_out={str(self._mip_opt_out).lower()}") # Add keyterm parameters (can have multiple) - for keyterm in self._settings.keyterm: + for keyterm in assert_given(self._settings.keyterm): params.append(urlencode({"keyterm": keyterm})) # Add tag parameters (can have multiple) diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py index a386566c5..5b64869e9 100644 --- a/src/pipecat/services/google/stt.py +++ b/src/pipecat/services/google/stt.py @@ -620,17 +620,20 @@ class GoogleSTTService(STTService): """ return True - def language_to_service_language(self, language: Language | list[Language]) -> str | list[str]: - """Convert Language enum(s) to Google STT language code(s). + def language_to_service_language(self, language: Language) -> str: + """Convert a Language enum to a Google STT language code. + + Narrower return type than the base class's ``str | None``: this + override always returns a string, falling back to ``"en-US"`` for + languages not in the verified mapping (see + :func:`language_to_google_stt_language`). Args: - language: Single Language enum or list of Language enums. + language: The Language enum value to convert. Returns: - str | List[str]: Google STT language code(s). + The Google STT language code. """ - if isinstance(language, list): - return [language_to_google_stt_language(lang) or "en-US" for lang in language] return language_to_google_stt_language(language) or "en-US" def _get_language_codes(self) -> list[str]: @@ -642,8 +645,9 @@ class GoogleSTTService(STTService): Returns: List[str]: Google STT language code strings. """ - if self._settings.languages: - return [self.language_to_service_language(lang) for lang in self._settings.languages] + languages = assert_given(self._settings.languages) + if languages: + return [self.language_to_service_language(lang) for lang in languages] language_codes = assert_given(self._settings.language_codes) if language_codes: return list(language_codes) diff --git a/src/pipecat/services/google/tts.py b/src/pipecat/services/google/tts.py index 58bc31691..94f689d44 100644 --- a/src/pipecat/services/google/tts.py +++ b/src/pipecat/services/google/tts.py @@ -1417,7 +1417,7 @@ class GeminiTTSService(GoogleBaseTTSService): if self._settings.multi_speaker and self._settings.speaker_configs: # Multi-speaker mode speaker_voice_configs = [] - for speaker_config in self._settings.speaker_configs: + for speaker_config in assert_given(self._settings.speaker_configs): speaker_voice_configs.append( texttospeech_v1.MultispeakerPrebuiltVoice( speaker_alias=speaker_config["speaker_alias"], diff --git a/src/pipecat/services/openai/base_llm.py b/src/pipecat/services/openai/base_llm.py index 72d4360a9..b5287067f 100644 --- a/src/pipecat/services/openai/base_llm.py +++ b/src/pipecat/services/openai/base_llm.py @@ -39,7 +39,7 @@ from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.frame_processor import FrameDirection from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.settings import NOT_GIVEN as _NOT_GIVEN -from pipecat.services.settings import LLMSettings, _NotGiven +from pipecat.services.settings import LLMSettings, _NotGiven, assert_given from pipecat.utils.tracing.service_decorators import traced_llm @@ -299,7 +299,7 @@ class BaseOpenAILLMService(LLMService[OpenAILLMAdapter]): params_from_context = adapter.get_llm_invocation_params( context, - system_instruction=self._settings.system_instruction, + system_instruction=assert_given(self._settings.system_instruction), convert_developer_to_user=not self.supports_developer_role, )