Narrow settings.model at service boundaries, not via truthiness

Two services were reading `_settings.model` (typed `str | _NotGiven |
None` because NOT_GIVEN is the default) and coercing it with `or ""`
or similar. `_NotGiven.__bool__` returns False, so the runtime
behavior happened to work, but the type was a lie — pyright saw
`str | _NotGiven` flowing into APIs that required `str` or `str | None`.

- `AIService._sync_model_name_to_metrics`: use `isinstance(model, str)`
  narrowing with an empty-string fallback. Equivalent runtime behavior,
  honest type, no truthiness dependency on a sentinel.
- `SarvamLLMService.__init__`: validate the model is a real string
  before handing it to `_validate_model(str)`. A non-string model at
  this point is a configuration bug; raise `ValueError` so the error
  is clear and survives `python -O` (unlike an assert).
This commit is contained in:
Mark Backman
2026-04-22 11:50:51 -04:00
parent 5872006d6b
commit b64ed3f9e2
3 changed files with 9 additions and 13 deletions

View File

@@ -2,14 +2,8 @@
"typeCheckingMode": "basic", "typeCheckingMode": "basic",
"pythonVersion": "3.11", "pythonVersion": "3.11",
"pythonPlatform": "All", "pythonPlatform": "All",
"include": [ "include": ["scripts", "src/pipecat"],
"scripts", "exclude": ["**/*_pb2.py", "**/__pycache__"],
"src/pipecat"
],
"exclude": [
"**/*_pb2.py",
"**/__pycache__"
],
"ignore": [ "ignore": [
"tests", "tests",
"src/pipecat/adapters/base_llm_adapter.py", "src/pipecat/adapters/base_llm_adapter.py",
@@ -40,7 +34,6 @@
"src/pipecat/processors/frameworks/rtvi/processor.py", "src/pipecat/processors/frameworks/rtvi/processor.py",
"src/pipecat/processors/frameworks/strands_agents.py", "src/pipecat/processors/frameworks/strands_agents.py",
"src/pipecat/processors/gstreamer/pipeline_source.py", "src/pipecat/processors/gstreamer/pipeline_source.py",
"src/pipecat/services/ai_service.py",
"src/pipecat/services/anthropic/llm.py", "src/pipecat/services/anthropic/llm.py",
"src/pipecat/services/assemblyai/stt.py", "src/pipecat/services/assemblyai/stt.py",
"src/pipecat/services/asyncai/tts.py", "src/pipecat/services/asyncai/tts.py",
@@ -106,7 +99,6 @@
"src/pipecat/services/resembleai/tts.py", "src/pipecat/services/resembleai/tts.py",
"src/pipecat/services/rime/tts.py", "src/pipecat/services/rime/tts.py",
"src/pipecat/services/sambanova/llm.py", "src/pipecat/services/sambanova/llm.py",
"src/pipecat/services/sarvam/llm.py",
"src/pipecat/services/sarvam/stt.py", "src/pipecat/services/sarvam/stt.py",
"src/pipecat/services/sarvam/tts.py", "src/pipecat/services/sarvam/tts.py",
"src/pipecat/services/simli/video.py", "src/pipecat/services/simli/video.py",
@@ -138,4 +130,4 @@
"src/pipecat/transports/whatsapp/client.py" "src/pipecat/transports/whatsapp/client.py"
], ],
"reportMissingImports": false "reportMissingImports": false
} }

View File

@@ -66,8 +66,9 @@ class AIService(FrameProcessor):
Args: Args:
model: The name of the AI model to use. model: The name of the AI model to use.
""" """
model = self._settings.model
self.set_core_metrics_data( self.set_core_metrics_data(
MetricsData(processor=self.name, model=self._settings.model or "") MetricsData(processor=self.name, model=model if isinstance(model, str) else "")
) )
async def start(self, frame: StartFrame): async def start(self, frame: StartFrame):

View File

@@ -83,7 +83,10 @@ class SarvamLLMService(OpenAILLMService):
if settings is not None: if settings is not None:
default_settings.apply_update(settings) default_settings.apply_update(settings)
self._validate_model(default_settings.model) model = default_settings.model
if not isinstance(model, str):
raise ValueError("Sarvam LLM requires a non-empty model string.")
self._validate_model(model)
super().__init__( super().__init__(
api_key=api_key, api_key=api_key,