diff --git a/changelog/4004.added.md b/changelog/4004.added.md new file mode 100644 index 000000000..f0fd28767 --- /dev/null +++ b/changelog/4004.added.md @@ -0,0 +1 @@ +- Added optional `service` field to `ServiceUpdateSettingsFrame` (and its subclasses `LLMUpdateSettingsFrame`, `TTSUpdateSettingsFrame`, `STTUpdateSettingsFrame`) to target a specific service instance. When `service` is set, only the matching service applies the settings; others forward the frame unchanged. This enables updating a single service when multiple services of the same type exist in the pipeline. diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 390eb93dd..f58dc957f 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -2154,10 +2154,15 @@ class ServiceUpdateSettingsFrame(ControlFrame, UninterruptibleFrame): delta: :class:`~pipecat.services.settings.ServiceSettings` delta-mode object describing the fields to change. + + service: Optional target service instance. When provided, only that + service will apply the settings; other services will forward the + frame unchanged. """ settings: Mapping[str, Any] = field(default_factory=dict) delta: Optional["ServiceSettings"] = None + service: Optional["FrameProcessor"] = None @dataclass diff --git a/src/pipecat/services/llm_service.py b/src/pipecat/services/llm_service.py index 7944f413a..a479fcfc6 100644 --- a/src/pipecat/services/llm_service.py +++ b/src/pipecat/services/llm_service.py @@ -403,7 +403,9 @@ class LLMService(UserTurnCompletionLLMServiceMixin, AIService): elif isinstance(frame, LLMConfigureOutputFrame): self._skip_tts = frame.skip_tts elif isinstance(frame, LLMUpdateSettingsFrame): - if frame.delta is not None: + if frame.service is not None and frame.service is not self: + await self.push_frame(frame, direction) + elif frame.delta is not None: await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. diff --git a/src/pipecat/services/stt_service.py b/src/pipecat/services/stt_service.py index ebf007f6f..a16aa0eaa 100644 --- a/src/pipecat/services/stt_service.py +++ b/src/pipecat/services/stt_service.py @@ -357,7 +357,9 @@ class STTService(AIService): await self._handle_vad_user_stopped_speaking(frame) await self.push_frame(frame, direction) elif isinstance(frame, STTUpdateSettingsFrame): - if frame.delta is not None: + if frame.service is not None and frame.service is not self: + await self.push_frame(frame, direction) + elif frame.delta is not None: await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object. diff --git a/src/pipecat/services/tts_service.py b/src/pipecat/services/tts_service.py index da1bcaf87..a79156018 100644 --- a/src/pipecat/services/tts_service.py +++ b/src/pipecat/services/tts_service.py @@ -738,7 +738,9 @@ class TTSService(AIService): self._turn_context_id = saved_turn_context_id self._processing_text = processing_text elif isinstance(frame, TTSUpdateSettingsFrame): - if frame.delta is not None: + if frame.service is not None and frame.service is not self: + await self.push_frame(frame, direction) + elif frame.delta is not None: await self._update_settings(frame.delta) elif frame.settings: # Backward-compatible path: convert legacy dict to settings object.