Update LLMs

This commit is contained in:
Mark Backman
2026-03-04 21:12:44 -05:00
parent 14c663d03f
commit 53a47c0ef4
27 changed files with 144 additions and 151 deletions

View File

@@ -75,10 +75,12 @@ class AWSBedrockLLMSettings(LLMSettings):
"""Settings for AWS Bedrock LLM services. """Settings for AWS Bedrock LLM services.
Parameters: Parameters:
stop_sequences: List of strings that stop generation.
latency: Performance mode - "standard" or "optimized". latency: Performance mode - "standard" or "optimized".
additional_model_request_fields: Additional model-specific parameters. additional_model_request_fields: Additional model-specific parameters.
""" """
stop_sequences: List[str] | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
latency: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) latency: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
additional_model_request_fields: Dict[str, Any] | _NotGiven = field( additional_model_request_fields: Dict[str, Any] | _NotGiven = field(
default_factory=lambda: NOT_GIVEN default_factory=lambda: NOT_GIVEN
@@ -810,6 +812,10 @@ class AWSBedrockLLMService(LLMService):
deprecated parameters and *settings* are provided, *settings* deprecated parameters and *settings* are provided, *settings*
values take precedence. values take precedence.
stop_sequences: List of strings that stop generation. stop_sequences: List of strings that stop generation.
.. deprecated:: 0.0.105
Use ``settings=AWSBedrockLLMSettings(stop_sequences=...)`` instead.
client_config: Custom boto3 client configuration. client_config: Custom boto3 client configuration.
retry_timeout_secs: Request timeout in seconds for retry logic. retry_timeout_secs: Request timeout in seconds for retry logic.
retry_on_timeout: Whether to retry the request once if it times out. retry_on_timeout: Whether to retry the request once if it times out.
@@ -828,6 +834,7 @@ class AWSBedrockLLMService(LLMService):
seed=None, seed=None,
filter_incomplete_user_turns=False, filter_incomplete_user_turns=False,
user_turn_completion_config=None, user_turn_completion_config=None,
stop_sequences=None,
latency=None, latency=None,
additional_model_request_fields={}, additional_model_request_fields={},
) )
@@ -836,6 +843,9 @@ class AWSBedrockLLMService(LLMService):
if model is not None: if model is not None:
_warn_deprecated_param("model", AWSBedrockLLMSettings, "model") _warn_deprecated_param("model", AWSBedrockLLMSettings, "model")
default_settings.model = model default_settings.model = model
if stop_sequences is not None:
_warn_deprecated_param("stop_sequences", AWSBedrockLLMSettings, "stop_sequences")
default_settings.stop_sequences = stop_sequences
# 3. Apply params overrides — only if settings not provided # 3. Apply params overrides — only if settings not provided
if params is not None: if params is not None:
@@ -844,6 +854,8 @@ class AWSBedrockLLMService(LLMService):
default_settings.max_tokens = params.max_tokens default_settings.max_tokens = params.max_tokens
default_settings.temperature = params.temperature default_settings.temperature = params.temperature
default_settings.top_p = params.top_p default_settings.top_p = params.top_p
if params.stop_sequences:
default_settings.stop_sequences = params.stop_sequences
default_settings.latency = params.latency default_settings.latency = params.latency
if isinstance(params.additional_model_request_fields, dict): if isinstance(params.additional_model_request_fields, dict):
default_settings.additional_model_request_fields = ( default_settings.additional_model_request_fields = (
@@ -856,14 +868,6 @@ class AWSBedrockLLMService(LLMService):
super().__init__(settings=default_settings, **kwargs) super().__init__(settings=default_settings, **kwargs)
# Handle stop_sequences (not a settings field)
if stop_sequences is not None:
self._stop_sequences = stop_sequences
elif params is not None:
self._stop_sequences = params.stop_sequences or []
else:
self._stop_sequences = []
# Initialize the AWS Bedrock client # Initialize the AWS Bedrock client
if not client_config: if not client_config:
client_config = Config( client_config = Config(
@@ -915,6 +919,8 @@ class AWSBedrockLLMService(LLMService):
inference_config["temperature"] = self._settings.temperature inference_config["temperature"] = self._settings.temperature
if self._settings.top_p is not None: if self._settings.top_p is not None:
inference_config["topP"] = self._settings.top_p inference_config["topP"] = self._settings.top_p
if self._settings.stop_sequences:
inference_config["stopSequences"] = self._settings.stop_sequences
return inference_config return inference_config
async def run_inference( async def run_inference(

View File

@@ -191,12 +191,12 @@ class AWSNovaSonicLLMSettings(LLMSettings):
"""Settings for AWS Nova Sonic LLM service. """Settings for AWS Nova Sonic LLM service.
Parameters: Parameters:
voice_id: Voice for speech synthesis. voice: Voice identifier for speech synthesis.
endpointing_sensitivity: Controls how quickly Nova Sonic decides the endpointing_sensitivity: Controls how quickly Nova Sonic decides the
user has stopped speaking. Can be "LOW", "MEDIUM", or "HIGH". user has stopped speaking. Can be "LOW", "MEDIUM", or "HIGH".
""" """
voice_id: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) voice: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
endpointing_sensitivity: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN) endpointing_sensitivity: str | None | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
@@ -250,7 +250,7 @@ class AWSNovaSonicLLMService(LLMService):
- Nova Sonic (the older model): see https://docs.aws.amazon.com/nova/latest/userguide/available-voices.html. - Nova Sonic (the older model): see https://docs.aws.amazon.com/nova/latest/userguide/available-voices.html.
.. deprecated:: .. deprecated::
Use ``settings=AWSNovaSonicLLMSettings(voice_id=...)`` instead. Use ``settings=AWSNovaSonicLLMSettings(voice=...)`` instead.
params: Model parameters for audio configuration and inference. params: Model parameters for audio configuration and inference.
@@ -273,7 +273,7 @@ class AWSNovaSonicLLMService(LLMService):
# 1. Initialize default_settings with hardcoded defaults # 1. Initialize default_settings with hardcoded defaults
default_settings = AWSNovaSonicLLMSettings( default_settings = AWSNovaSonicLLMSettings(
model="amazon.nova-2-sonic-v1:0", model="amazon.nova-2-sonic-v1:0",
voice_id="matthew", voice="matthew",
temperature=0.7, temperature=0.7,
max_tokens=1024, max_tokens=1024,
top_p=0.9, top_p=0.9,
@@ -291,8 +291,8 @@ class AWSNovaSonicLLMService(LLMService):
_warn_deprecated_param("model", AWSNovaSonicLLMSettings, "model") _warn_deprecated_param("model", AWSNovaSonicLLMSettings, "model")
default_settings.model = model default_settings.model = model
if voice_id != "matthew": if voice_id != "matthew":
_warn_deprecated_param("voice_id", AWSNovaSonicLLMSettings, "voice_id") _warn_deprecated_param("voice_id", AWSNovaSonicLLMSettings, "voice")
default_settings.voice_id = voice_id default_settings.voice = voice_id
# 3. Apply params overrides — only if settings not provided # 3. Apply params overrides — only if settings not provided
if params is not None: if params is not None:
@@ -811,7 +811,7 @@ class AWSNovaSonicLLMService(LLMService):
"sampleRateHertz": {self._output_sample_rate}, "sampleRateHertz": {self._output_sample_rate},
"sampleSizeBits": {self._output_sample_size}, "sampleSizeBits": {self._output_sample_size},
"channelCount": {self._output_channel_count}, "channelCount": {self._output_channel_count},
"voiceId": "{self._settings.voice_id}", "voiceId": "{self._settings.voice}",
"encoding": "base64", "encoding": "base64",
"audioType": "SPEECH" "audioType": "SPEECH"
}}{tools_config} }}{tools_config}

View File

@@ -35,6 +35,8 @@ class AzureRealtimeLLMService(OpenAIRealtimeLLMService):
real-time audio and text communication capabilities as the base OpenAI service. real-time audio and text communication capabilities as the base OpenAI service.
""" """
_settings: AzureRealtimeLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -31,6 +31,8 @@ class CerebrasLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: CerebrasLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -31,6 +31,8 @@ class DeepSeekLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: DeepSeekLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -31,6 +31,8 @@ class FireworksLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: FireworksLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -610,6 +610,7 @@ class GeminiLiveLLMSettings(LLMSettings):
"""Settings for Gemini Live LLM services. """Settings for Gemini Live LLM services.
Parameters: Parameters:
voice: TTS voice identifier (e.g. ``"Charon"``).
modalities: Response modalities. modalities: Response modalities.
language: Language for generation. language: Language for generation.
media_resolution: Media resolution setting. media_resolution: Media resolution setting.
@@ -620,6 +621,7 @@ class GeminiLiveLLMSettings(LLMSettings):
proactivity: Proactivity configuration. proactivity: Proactivity configuration.
""" """
voice: str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
modalities: GeminiModalities | _NotGiven = field(default_factory=lambda: NOT_GIVEN) modalities: GeminiModalities | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
language: Language | str | _NotGiven = field(default_factory=lambda: NOT_GIVEN) language: Language | str | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
media_resolution: GeminiMediaResolution | _NotGiven = field(default_factory=lambda: NOT_GIVEN) media_resolution: GeminiMediaResolution | _NotGiven = field(default_factory=lambda: NOT_GIVEN)
@@ -680,6 +682,9 @@ class GeminiLiveLLMService(LLMService):
Use ``settings=GeminiLiveLLMSettings(model=...)`` instead. Use ``settings=GeminiLiveLLMSettings(model=...)`` instead.
voice_id: TTS voice identifier. Defaults to "Charon". voice_id: TTS voice identifier. Defaults to "Charon".
.. deprecated:: 0.0.105
Use ``settings=GeminiLiveLLMSettings(voice=...)`` instead.
start_audio_paused: Whether to start with audio input paused. Defaults to False. start_audio_paused: Whether to start with audio input paused. Defaults to False.
start_video_paused: Whether to start with video input paused. Defaults to False. start_video_paused: Whether to start with video input paused. Defaults to False.
system_instruction: System prompt for the model. Defaults to None. system_instruction: System prompt for the model. Defaults to None.
@@ -712,6 +717,7 @@ class GeminiLiveLLMService(LLMService):
# 1. Initialize default_settings with hardcoded defaults # 1. Initialize default_settings with hardcoded defaults
default_settings = GeminiLiveLLMSettings( default_settings = GeminiLiveLLMSettings(
model="models/gemini-2.5-flash-native-audio-preview-12-2025", model="models/gemini-2.5-flash-native-audio-preview-12-2025",
voice="Charon",
frequency_penalty=None, frequency_penalty=None,
max_tokens=4096, max_tokens=4096,
presence_penalty=None, presence_penalty=None,
@@ -736,6 +742,9 @@ class GeminiLiveLLMService(LLMService):
if model is not None: if model is not None:
_warn_deprecated_param("model", GeminiLiveLLMSettings, "model") _warn_deprecated_param("model", GeminiLiveLLMSettings, "model")
default_settings.model = model default_settings.model = model
if voice_id != "Charon":
_warn_deprecated_param("voice_id", GeminiLiveLLMSettings, "voice")
default_settings.voice = voice_id
# 3. Apply params overrides — only if settings not provided # 3. Apply params overrides — only if settings not provided
if params is not None: if params is not None:
@@ -776,7 +785,6 @@ class GeminiLiveLLMService(LLMService):
self._last_sent_time = 0 self._last_sent_time = 0
self._base_url = base_url self._base_url = base_url
self._voice_id = voice_id
self._language_code = params.language if params is not None else Language.EN_US self._language_code = params.language if params is not None else Language.EN_US
self._system_instruction_from_init = system_instruction self._system_instruction_from_init = system_instruction
@@ -1184,7 +1192,7 @@ class GeminiLiveLLMService(LLMService):
response_modalities=[Modality(self._settings.modalities.value)], response_modalities=[Modality(self._settings.modalities.value)],
speech_config=SpeechConfig( speech_config=SpeechConfig(
voice_config=VoiceConfig( voice_config=VoiceConfig(
prebuilt_voice_config={"voice_name": self._voice_id} prebuilt_voice_config={"voice_name": self._settings.voice}
), ),
language_code=self._settings.language, language_code=self._settings.language,
), ),

View File

@@ -57,6 +57,8 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService):
responses, and tool usage. responses, and tool usage.
""" """
_settings: GeminiLiveVertexLLMSettings
def __init__( def __init__(
self, self,
*, *,
@@ -90,6 +92,9 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService):
Use ``settings=GeminiLiveLLMSettings(model=...)`` instead. Use ``settings=GeminiLiveLLMSettings(model=...)`` instead.
voice_id: TTS voice identifier. Defaults to "Charon". voice_id: TTS voice identifier. Defaults to "Charon".
.. deprecated:: 0.0.105
Use ``settings=GeminiLiveVertexLLMSettings(voice=...)`` instead.
start_audio_paused: Whether to start with audio input paused. Defaults to False. start_audio_paused: Whether to start with audio input paused. Defaults to False.
start_video_paused: Whether to start with video input paused. Defaults to False. start_video_paused: Whether to start with video input paused. Defaults to False.
system_instruction: System prompt for the model. Defaults to None. system_instruction: System prompt for the model. Defaults to None.
@@ -132,6 +137,7 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService):
# 1. Initialize default_settings with hardcoded defaults # 1. Initialize default_settings with hardcoded defaults
default_settings = GeminiLiveVertexLLMSettings( default_settings = GeminiLiveVertexLLMSettings(
model="google/gemini-live-2.5-flash-native-audio", model="google/gemini-live-2.5-flash-native-audio",
voice="Charon",
frequency_penalty=None, frequency_penalty=None,
max_tokens=4096, max_tokens=4096,
presence_penalty=None, presence_penalty=None,
@@ -156,6 +162,9 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService):
if model is not None: if model is not None:
_warn_deprecated_param("model", GeminiLiveVertexLLMSettings, "model") _warn_deprecated_param("model", GeminiLiveVertexLLMSettings, "model")
default_settings.model = model default_settings.model = model
if voice_id != "Charon":
_warn_deprecated_param("voice_id", GeminiLiveVertexLLMSettings, "voice")
default_settings.voice = voice_id
# 3. Apply params overrides — only if settings not provided # 3. Apply params overrides — only if settings not provided
if params is not None: if params is not None:
@@ -193,7 +202,6 @@ class GeminiLiveVertexLLMService(GeminiLiveLLMService):
# api_key is required by parent class, but actually not used with # api_key is required by parent class, but actually not used with
# Vertex # Vertex
api_key="dummy", api_key="dummy",
voice_id=voice_id,
start_audio_paused=start_audio_paused, start_audio_paused=start_audio_paused,
start_video_paused=start_video_paused, start_video_paused=start_video_paused,
system_instruction=system_instruction, system_instruction=system_instruction,

View File

@@ -58,6 +58,8 @@ class GoogleLLMOpenAIBetaService(OpenAILLMService):
https://ai.google.dev/gemini-api/docs/openai https://ai.google.dev/gemini-api/docs/openai
""" """
_settings: GoogleOpenAILLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -59,6 +59,8 @@ class GoogleVertexLLMService(GoogleLLMService):
https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference
""" """
_settings: GoogleVertexLLMSettings
class InputParams(GoogleLLMService.InputParams): class InputParams(GoogleLLMService.InputParams):
"""Input parameters specific to Vertex AI. """Input parameters specific to Vertex AI.

View File

@@ -86,6 +86,8 @@ class GrokLLMService(OpenAILLMService):
processing and reports final totals. processing and reports final totals.
""" """
_settings: GrokLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -13,7 +13,7 @@ https://docs.x.ai/docs/guides/voice/agent
import base64 import base64
import json import json
import time import time
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Any, Optional from typing import Any, Optional
from loguru import logger from loguru import logger
@@ -56,7 +56,7 @@ from pipecat.processors.aggregators.llm_response_universal import (
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
from pipecat.processors.frame_processor import FrameDirection from pipecat.processors.frame_processor import FrameDirection
from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.llm_service import FunctionCallFromLLM, LLMService
from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven, _warn_deprecated_param from pipecat.services.settings import LLMSettings
from pipecat.utils.time import time_now_iso8601 from pipecat.utils.time import time_now_iso8601
from . import events from . import events
@@ -88,15 +88,9 @@ class CurrentAudioResponse:
@dataclass @dataclass
class GrokRealtimeLLMSettings(LLMSettings): class GrokRealtimeLLMSettings(LLMSettings):
"""Settings for Grok Realtime LLM services. """Settings for Grok Realtime LLM services."""
Parameters: pass
session_properties: Grok Realtime session configuration.
"""
session_properties: events.SessionProperties | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
class GrokRealtimeLLMService(LLMService): class GrokRealtimeLLMService(LLMService):
@@ -137,19 +131,13 @@ class GrokRealtimeLLMService(LLMService):
base_url: WebSocket base URL for the realtime API. base_url: WebSocket base URL for the realtime API.
Defaults to "wss://api.x.ai/v1/realtime". Defaults to "wss://api.x.ai/v1/realtime".
session_properties: Configuration properties for the realtime session. session_properties: Configuration properties for the realtime session.
.. deprecated::
Use ``settings=GrokRealtimeLLMSettings(session_properties=...)``
instead.
If None, uses default SessionProperties with voice "Ara". If None, uses default SessionProperties with voice "Ara".
To set a different voice, configure it in session_properties: To set a different voice, configure it in session_properties:
session_properties = events.SessionProperties(voice="Rex") session_properties = events.SessionProperties(voice="Rex")
Available voices: Ara, Rex, Sal, Eve, Leo. Available voices: Ara, Rex, Sal, Eve, Leo.
settings: Grok Realtime LLM settings. If provided together with deprecated settings: Runtime-updatable settings for this service.
top-level parameters, the ``settings`` values take precedence.
start_audio_paused: Whether to start with audio input paused. Defaults to False. start_audio_paused: Whether to start with audio input paused. Defaults to False.
**kwargs: Additional arguments passed to parent LLMService. **kwargs: Additional arguments passed to parent LLMService.
""" """
@@ -165,17 +153,9 @@ class GrokRealtimeLLMService(LLMService):
seed=None, seed=None,
filter_incomplete_user_turns=False, filter_incomplete_user_turns=False,
user_turn_completion_config=None, user_turn_completion_config=None,
session_properties=events.SessionProperties(),
) )
# 2. Apply direct init arg overrides (deprecated) # 2. Apply settings delta (canonical API, always wins)
if session_properties is not None:
_warn_deprecated_param(
"session_properties", GrokRealtimeLLMSettings, "session_properties"
)
default_settings.session_properties = session_properties
# 4. Apply settings delta (canonical API, always wins)
if settings is not None: if settings is not None:
default_settings.apply_update(settings) default_settings.apply_update(settings)
@@ -187,6 +167,7 @@ class GrokRealtimeLLMService(LLMService):
self.api_key = api_key self.api_key = api_key
self.base_url = base_url self.base_url = base_url
self._session_properties = session_properties or events.SessionProperties()
self._audio_input_paused = start_audio_paused self._audio_input_paused = start_audio_paused
self._websocket = None self._websocket = None
@@ -235,13 +216,13 @@ class GrokRealtimeLLMService(LLMService):
Configured sample rate or None if not manually configured. Configured sample rate or None if not manually configured.
For PCMU/PCMA formats, returns 8000 Hz (G.711 standard). For PCMU/PCMA formats, returns 8000 Hz (G.711 standard).
""" """
if not self._settings.session_properties.audio: if not self._session_properties.audio:
return None return None
audio_config = ( audio_config = (
self._settings.session_properties.audio.input self._session_properties.audio.input
if direction == "input" if direction == "input"
else self._settings.session_properties.audio.output else self._session_properties.audio.output
) )
if audio_config and audio_config.format: if audio_config and audio_config.format:
@@ -271,8 +252,8 @@ class GrokRealtimeLLMService(LLMService):
def _is_turn_detection_enabled(self) -> bool: def _is_turn_detection_enabled(self) -> bool:
"""Check if server-side VAD is enabled.""" """Check if server-side VAD is enabled."""
if self._settings.session_properties.turn_detection: if self._session_properties.turn_detection:
return self._settings.session_properties.turn_detection.type == "server_vad" return self._session_properties.turn_detection.type == "server_vad"
return False return False
async def _handle_interruption(self): async def _handle_interruption(self):
@@ -339,7 +320,7 @@ class GrokRealtimeLLMService(LLMService):
input_sample_rate: Sample rate for audio input (Hz). input_sample_rate: Sample rate for audio input (Hz).
output_sample_rate: Sample rate for audio output (Hz). output_sample_rate: Sample rate for audio output (Hz).
""" """
props = self._settings.session_properties props = self._session_properties
if not props.audio: if not props.audio:
props.audio = events.AudioConfiguration() props.audio = events.AudioConfiguration()
if not props.audio.input: if not props.audio.input:
@@ -395,7 +376,12 @@ class GrokRealtimeLLMService(LLMService):
# directly. The frame.delta path falls through to super, which calls # directly. The frame.delta path falls through to super, which calls
# _update_settings → our override handles the rest. # _update_settings → our override handles the rest.
if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None:
self._settings.session_properties = events.SessionProperties(**frame.settings) # Capture current audio config before replacing session properties.
input_rate = self._get_configured_sample_rate("input")
output_rate = self._get_configured_sample_rate("output")
self._session_properties = events.SessionProperties(**frame.settings)
if input_rate and output_rate:
self._ensure_audio_config(input_rate, output_rate)
await self._send_session_update() await self._send_session_update()
await self.push_frame(frame, direction) await self.push_frame(frame, direction)
return return
@@ -498,29 +484,14 @@ class GrokRealtimeLLMService(LLMService):
await self.push_error(error_msg=f"Error sending client event: {e}", exception=e) await self.push_error(error_msg=f"Error sending client event: {e}", exception=e)
async def _update_settings(self, delta): async def _update_settings(self, delta):
"""Apply a settings delta, sending a session update if needed.""" """Apply a settings delta."""
# Capture current sample rates before the update replaces them.
input_rate = self._get_configured_sample_rate("input")
output_rate = self._get_configured_sample_rate("output")
changed = await super()._update_settings(delta) changed = await super()._update_settings(delta)
self._warn_unhandled_updated_settings(changed.keys())
if "session_properties" in changed:
if input_rate and output_rate:
self._ensure_audio_config(input_rate, output_rate)
else:
logger.warning(
"Attempting to apply session properties update without configured sample rates. "
"Audio configuration may be incomplete."
)
await self._send_session_update()
self._warn_unhandled_updated_settings(changed.keys() - {"session_properties"})
return changed return changed
async def _send_session_update(self): async def _send_session_update(self):
"""Update session settings on the server.""" """Update session settings on the server."""
settings = self._settings.session_properties settings = self._session_properties
adapter: GrokRealtimeLLMAdapter = self.get_llm_adapter() adapter: GrokRealtimeLLMAdapter = self.get_llm_adapter()
if self._context: if self._context:

View File

@@ -30,6 +30,8 @@ class GroqLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: GroqLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -33,6 +33,8 @@ class MistralLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: MistralLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -36,6 +36,8 @@ class NvidiaLLMService(OpenAILLMService):
in token usage reporting between NIM (incremental) and OpenAI (final summary). in token usage reporting between NIM (incremental) and OpenAI (final summary).
""" """
_settings: NvidiaLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -30,6 +30,8 @@ class OLLamaLLMService(OpenAILLMService):
providing a compatible interface for running large language models locally. providing a compatible interface for running large language models locally.
""" """
_settings: OllamaLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -53,11 +53,9 @@ class OpenAILLMSettings(LLMSettings):
Parameters: Parameters:
max_completion_tokens: Maximum completion tokens to generate. max_completion_tokens: Maximum completion tokens to generate.
service_tier: Service tier to use (e.g., "auto", "flex", "priority").
""" """
max_completion_tokens: int | _NotGiven = field(default_factory=lambda: _NOT_GIVEN) max_completion_tokens: int | _NotGiven = field(default_factory=lambda: _NOT_GIVEN)
service_tier: str | _NotGiven = field(default_factory=lambda: _NOT_GIVEN)
class BaseOpenAILLMService(LLMService): class BaseOpenAILLMService(LLMService):
@@ -118,6 +116,7 @@ class BaseOpenAILLMService(LLMService):
organization=None, organization=None,
project=None, project=None,
default_headers: Optional[Mapping[str, str]] = None, default_headers: Optional[Mapping[str, str]] = None,
service_tier: Optional[str] = None,
params: Optional[InputParams] = None, params: Optional[InputParams] = None,
settings: Optional[OpenAILLMSettings] = None, settings: Optional[OpenAILLMSettings] = None,
retry_timeout_secs: Optional[float] = 5.0, retry_timeout_secs: Optional[float] = 5.0,
@@ -138,6 +137,7 @@ class BaseOpenAILLMService(LLMService):
organization: OpenAI organization ID. organization: OpenAI organization ID.
project: OpenAI project ID. project: OpenAI project ID.
default_headers: Additional HTTP headers to include in requests. default_headers: Additional HTTP headers to include in requests.
service_tier: Service tier to use (e.g., "auto", "flex", "priority").
params: Input parameters for model configuration and behavior. params: Input parameters for model configuration and behavior.
.. deprecated:: 0.0.105 .. deprecated:: 0.0.105
@@ -161,7 +161,6 @@ class BaseOpenAILLMService(LLMService):
top_k=None, top_k=None,
max_tokens=NOT_GIVEN, max_tokens=NOT_GIVEN,
max_completion_tokens=NOT_GIVEN, max_completion_tokens=NOT_GIVEN,
service_tier=NOT_GIVEN,
filter_incomplete_user_turns=False, filter_incomplete_user_turns=False,
user_turn_completion_config=None, user_turn_completion_config=None,
extra={}, extra={},
@@ -180,7 +179,6 @@ class BaseOpenAILLMService(LLMService):
default_settings.top_p = params.top_p default_settings.top_p = params.top_p
default_settings.max_tokens = params.max_tokens default_settings.max_tokens = params.max_tokens
default_settings.max_completion_tokens = params.max_completion_tokens default_settings.max_completion_tokens = params.max_completion_tokens
default_settings.service_tier = params.service_tier
if isinstance(params.extra, dict): if isinstance(params.extra, dict):
default_settings.extra = params.extra default_settings.extra = params.extra
@@ -192,6 +190,7 @@ class BaseOpenAILLMService(LLMService):
settings=default_settings, settings=default_settings,
**kwargs, **kwargs,
) )
self._service_tier = service_tier
self._retry_timeout_secs = retry_timeout_secs self._retry_timeout_secs = retry_timeout_secs
self._retry_on_timeout = retry_on_timeout self._retry_on_timeout = retry_on_timeout
self._system_instruction = system_instruction self._system_instruction = system_instruction
@@ -321,7 +320,7 @@ class BaseOpenAILLMService(LLMService):
"top_p": self._settings.top_p, "top_p": self._settings.top_p,
"max_tokens": self._settings.max_tokens, "max_tokens": self._settings.max_tokens,
"max_completion_tokens": self._settings.max_completion_tokens, "max_completion_tokens": self._settings.max_completion_tokens,
"service_tier": self._settings.service_tier, "service_tier": self._service_tier,
} }
# Messages, tools, tool_choice # Messages, tools, tool_choice

View File

@@ -76,6 +76,7 @@ class OpenAILLMService(BaseOpenAILLMService):
self, self,
*, *,
model: Optional[str] = None, model: Optional[str] = None,
service_tier: Optional[str] = None,
params: Optional[BaseOpenAILLMService.InputParams] = None, params: Optional[BaseOpenAILLMService.InputParams] = None,
settings: Optional[OpenAILLMSettings] = None, settings: Optional[OpenAILLMSettings] = None,
**kwargs, **kwargs,
@@ -88,6 +89,7 @@ class OpenAILLMService(BaseOpenAILLMService):
.. deprecated:: 0.0.105 .. deprecated:: 0.0.105
Use ``settings=OpenAILLMSettings(model=...)`` instead. Use ``settings=OpenAILLMSettings(model=...)`` instead.
service_tier: Service tier to use (e.g., "auto", "flex", "priority").
params: Input parameters for model configuration. params: Input parameters for model configuration.
.. deprecated:: 0.0.105 .. deprecated:: 0.0.105
@@ -108,7 +110,6 @@ class OpenAILLMService(BaseOpenAILLMService):
top_k=None, top_k=None,
max_tokens=NOT_GIVEN, max_tokens=NOT_GIVEN,
max_completion_tokens=NOT_GIVEN, max_completion_tokens=NOT_GIVEN,
service_tier=NOT_GIVEN,
filter_incomplete_user_turns=False, filter_incomplete_user_turns=False,
user_turn_completion_config=None, user_turn_completion_config=None,
extra={}, extra={},
@@ -119,6 +120,10 @@ class OpenAILLMService(BaseOpenAILLMService):
_warn_deprecated_param("model", OpenAILLMSettings, "model") _warn_deprecated_param("model", OpenAILLMSettings, "model")
default_settings.model = model default_settings.model = model
# Handle service_tier from deprecated params
if params is not None and not settings and params.service_tier is not NOT_GIVEN:
service_tier = service_tier or params.service_tier
# 3. Apply params overrides — only if settings not provided # 3. Apply params overrides — only if settings not provided
if params is not None: if params is not None:
_warn_deprecated_param("params", OpenAILLMSettings) _warn_deprecated_param("params", OpenAILLMSettings)
@@ -130,7 +135,6 @@ class OpenAILLMService(BaseOpenAILLMService):
default_settings.top_p = params.top_p default_settings.top_p = params.top_p
default_settings.max_tokens = params.max_tokens default_settings.max_tokens = params.max_tokens
default_settings.max_completion_tokens = params.max_completion_tokens default_settings.max_completion_tokens = params.max_completion_tokens
default_settings.service_tier = params.service_tier
if isinstance(params.extra, dict): if isinstance(params.extra, dict):
default_settings.extra = params.extra default_settings.extra = params.extra
@@ -138,7 +142,7 @@ class OpenAILLMService(BaseOpenAILLMService):
if settings is not None: if settings is not None:
default_settings.apply_update(settings) default_settings.apply_update(settings)
super().__init__(settings=default_settings, **kwargs) super().__init__(service_tier=service_tier, settings=default_settings, **kwargs)
def create_context_aggregator( def create_context_aggregator(
self, self,

View File

@@ -10,7 +10,7 @@ import base64
import io import io
import json import json
import time import time
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Any, Optional from typing import Any, Optional
from loguru import logger from loguru import logger
@@ -59,7 +59,7 @@ from pipecat.processors.aggregators.openai_llm_context import (
) )
from pipecat.processors.frame_processor import FrameDirection from pipecat.processors.frame_processor import FrameDirection
from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.llm_service import FunctionCallFromLLM, LLMService
from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven, _warn_deprecated_param from pipecat.services.settings import LLMSettings, _warn_deprecated_param
from pipecat.transcriptions.language import Language from pipecat.transcriptions.language import Language
from pipecat.utils.time import time_now_iso8601 from pipecat.utils.time import time_now_iso8601
from pipecat.utils.tracing.service_decorators import traced_openai_realtime, traced_stt from pipecat.utils.tracing.service_decorators import traced_openai_realtime, traced_stt
@@ -93,15 +93,9 @@ class CurrentAudioResponse:
@dataclass @dataclass
class OpenAIRealtimeLLMSettings(LLMSettings): class OpenAIRealtimeLLMSettings(LLMSettings):
"""Settings for OpenAI Realtime LLM services. """Settings for OpenAI Realtime LLM services."""
Parameters: pass
session_properties: OpenAI Realtime session configuration.
"""
session_properties: events.SessionProperties | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
class OpenAIRealtimeLLMService(LLMService): class OpenAIRealtimeLLMService(LLMService):
@@ -145,15 +139,8 @@ class OpenAIRealtimeLLMService(LLMService):
base_url: WebSocket base URL for the realtime API. base_url: WebSocket base URL for the realtime API.
Defaults to "wss://api.openai.com/v1/realtime". Defaults to "wss://api.openai.com/v1/realtime".
session_properties: Configuration properties for the realtime session. session_properties: Configuration properties for the realtime session.
If None, uses default SessionProperties.
.. deprecated:: settings: Runtime-updatable settings for this service.
Use ``settings=OpenAIRealtimeLLMSettings(session_properties=...)``
instead.
These are session-level settings that can be updated during the session
(except for voice and model). If None, uses default SessionProperties.
settings: Realtime LLM settings. If provided together with deprecated
top-level parameters, the ``settings`` values take precedence.
start_audio_paused: Whether to start with audio input paused. Defaults to False. start_audio_paused: Whether to start with audio input paused. Defaults to False.
start_video_paused: Whether to start with video input paused. Defaults to False. start_video_paused: Whether to start with video input paused. Defaults to False.
video_frame_detail: Detail level for video processing. Can be "auto", "low", or "high". video_frame_detail: Detail level for video processing. Can be "auto", "low", or "high".
@@ -192,20 +179,14 @@ class OpenAIRealtimeLLMService(LLMService):
seed=None, seed=None,
filter_incomplete_user_turns=False, filter_incomplete_user_turns=False,
user_turn_completion_config=None, user_turn_completion_config=None,
session_properties=events.SessionProperties(),
) )
# 2. Apply direct init arg overrides (deprecated) # 2. Apply direct init arg overrides (deprecated)
if model is not None: if model is not None:
_warn_deprecated_param("model", OpenAIRealtimeLLMSettings, "model") _warn_deprecated_param("model", OpenAIRealtimeLLMSettings, "model")
default_settings.model = model default_settings.model = model
if session_properties is not None:
_warn_deprecated_param(
"session_properties", OpenAIRealtimeLLMSettings, "session_properties"
)
default_settings.session_properties = session_properties
# 4. Apply settings delta (canonical API, always wins) # 3. Apply settings delta (canonical API, always wins)
if settings is not None: if settings is not None:
default_settings.apply_update(settings) default_settings.apply_update(settings)
@@ -220,6 +201,7 @@ class OpenAIRealtimeLLMService(LLMService):
self.api_key = api_key self.api_key = api_key
self.base_url = full_url self.base_url = full_url
self._session_properties = session_properties or events.SessionProperties()
self._audio_input_paused = start_audio_paused self._audio_input_paused = start_audio_paused
self._video_input_paused = start_video_paused self._video_input_paused = start_video_paused
self._video_frame_detail = video_frame_detail self._video_frame_detail = video_frame_detail
@@ -282,12 +264,12 @@ class OpenAIRealtimeLLMService(LLMService):
def _is_modality_enabled(self, modality: str) -> bool: def _is_modality_enabled(self, modality: str) -> bool:
"""Check if a specific modality is enabled, "text" or "audio".""" """Check if a specific modality is enabled, "text" or "audio"."""
modalities = self._settings.session_properties.output_modalities or ["audio", "text"] modalities = self._session_properties.output_modalities or ["audio", "text"]
return modality in modalities return modality in modalities
def _get_enabled_modalities(self) -> list[str]: def _get_enabled_modalities(self) -> list[str]:
"""Get the list of enabled modalities.""" """Get the list of enabled modalities."""
modalities = self._settings.session_properties.output_modalities or ["audio", "text"] modalities = self._session_properties.output_modalities or ["audio", "text"]
# API only supports single modality responses: either ["text"] or ["audio"] # API only supports single modality responses: either ["text"] or ["audio"]
if "audio" in modalities: if "audio" in modalities:
return ["audio"] return ["audio"]
@@ -360,9 +342,9 @@ class OpenAIRealtimeLLMService(LLMService):
# None and False are different. Check for False. None means we're using OpenAI's # None and False are different. Check for False. None means we're using OpenAI's
# built-in turn detection defaults. # built-in turn detection defaults.
turn_detection_disabled = ( turn_detection_disabled = (
self._settings.session_properties.audio self._session_properties.audio
and self._settings.session_properties.audio.input and self._session_properties.audio.input
and self._settings.session_properties.audio.input.turn_detection is False and self._session_properties.audio.input.turn_detection is False
) )
if turn_detection_disabled: if turn_detection_disabled:
await self.send_client_event(events.InputAudioBufferClearEvent()) await self.send_client_event(events.InputAudioBufferClearEvent())
@@ -382,9 +364,9 @@ class OpenAIRealtimeLLMService(LLMService):
# None and False are different. Check for False. None means we're using OpenAI's # None and False are different. Check for False. None means we're using OpenAI's
# built-in turn detection defaults. # built-in turn detection defaults.
turn_detection_disabled = ( turn_detection_disabled = (
self._settings.session_properties.audio self._session_properties.audio
and self._settings.session_properties.audio.input and self._session_properties.audio.input
and self._settings.session_properties.audio.input.turn_detection is False and self._session_properties.audio.input.turn_detection is False
) )
if turn_detection_disabled: if turn_detection_disabled:
await self.send_client_event(events.InputAudioBufferCommitEvent()) await self.send_client_event(events.InputAudioBufferCommitEvent())
@@ -457,7 +439,7 @@ class OpenAIRealtimeLLMService(LLMService):
# directly. The frame.delta path falls through to super, which calls # directly. The frame.delta path falls through to super, which calls
# _update_settings → our override handles the rest. # _update_settings → our override handles the rest.
if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None:
self._settings.session_properties = events.SessionProperties(**frame.settings) self._session_properties = events.SessionProperties(**frame.settings)
await self._send_session_update() await self._send_session_update()
await self.push_frame(frame, direction) await self.push_frame(frame, direction)
return return
@@ -576,15 +558,13 @@ class OpenAIRealtimeLLMService(LLMService):
await self.push_error(error_msg=f"Error sending client event: {e}", exception=e) await self.push_error(error_msg=f"Error sending client event: {e}", exception=e)
async def _update_settings(self, delta): async def _update_settings(self, delta):
"""Apply a settings delta, sending a session update if needed.""" """Apply a settings delta."""
changed = await super()._update_settings(delta) changed = await super()._update_settings(delta)
if "session_properties" in changed: self._warn_unhandled_updated_settings(changed.keys())
await self._send_session_update()
self._warn_unhandled_updated_settings(changed.keys() - {"session_properties"})
return changed return changed
async def _send_session_update(self): async def _send_session_update(self):
settings = self._settings.session_properties settings = self._session_properties
adapter: OpenAIRealtimeLLMAdapter = self.get_llm_adapter() adapter: OpenAIRealtimeLLMAdapter = self.get_llm_adapter()
if self._context: if self._context:

View File

@@ -42,6 +42,8 @@ class AzureRealtimeBetaLLMService(OpenAIRealtimeBetaLLMService):
real-time audio and text communication capabilities as the base OpenAI service. real-time audio and text communication capabilities as the base OpenAI service.
""" """
_settings: AzureRealtimeBetaLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -10,7 +10,7 @@ import base64
import json import json
import time import time
import warnings import warnings
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Optional from typing import Optional
from loguru import logger from loguru import logger
@@ -54,7 +54,7 @@ from pipecat.processors.aggregators.openai_llm_context import (
from pipecat.processors.frame_processor import FrameDirection from pipecat.processors.frame_processor import FrameDirection
from pipecat.services.llm_service import FunctionCallFromLLM, LLMService from pipecat.services.llm_service import FunctionCallFromLLM, LLMService
from pipecat.services.openai.llm import OpenAIContextAggregatorPair from pipecat.services.openai.llm import OpenAIContextAggregatorPair
from pipecat.services.settings import NOT_GIVEN, LLMSettings, _NotGiven, _warn_deprecated_param from pipecat.services.settings import LLMSettings, _warn_deprecated_param
from pipecat.transcriptions.language import Language from pipecat.transcriptions.language import Language
from pipecat.utils.time import time_now_iso8601 from pipecat.utils.time import time_now_iso8601
from pipecat.utils.tracing.service_decorators import traced_openai_realtime, traced_stt from pipecat.utils.tracing.service_decorators import traced_openai_realtime, traced_stt
@@ -94,15 +94,9 @@ class CurrentAudioResponse:
@dataclass @dataclass
class OpenAIRealtimeBetaLLMSettings(LLMSettings): class OpenAIRealtimeBetaLLMSettings(LLMSettings):
"""Settings for OpenAI Realtime Beta LLM services. """Settings for OpenAI Realtime Beta LLM services."""
Parameters: pass
session_properties: OpenAI Realtime session configuration.
"""
session_properties: events.SessionProperties | _NotGiven = field(
default_factory=lambda: NOT_GIVEN
)
class OpenAIRealtimeBetaLLMService(LLMService): class OpenAIRealtimeBetaLLMService(LLMService):
@@ -146,14 +140,8 @@ class OpenAIRealtimeBetaLLMService(LLMService):
base_url: WebSocket base URL for the realtime API. base_url: WebSocket base URL for the realtime API.
Defaults to "wss://api.openai.com/v1/realtime". Defaults to "wss://api.openai.com/v1/realtime".
session_properties: Configuration properties for the realtime session. session_properties: Configuration properties for the realtime session.
.. deprecated::
Use ``settings=OpenAIRealtimeBetaLLMSettings(session_properties=...)``
instead.
If None, uses default SessionProperties. If None, uses default SessionProperties.
settings: Realtime Beta LLM settings. If provided together with deprecated settings: Runtime-updatable settings for this service.
top-level parameters, the ``settings`` values take precedence.
start_audio_paused: Whether to start with audio input paused. Defaults to False. start_audio_paused: Whether to start with audio input paused. Defaults to False.
send_transcription_frames: Whether to emit transcription frames. Defaults to True. send_transcription_frames: Whether to emit transcription frames. Defaults to True.
**kwargs: Additional arguments passed to parent LLMService. **kwargs: Additional arguments passed to parent LLMService.
@@ -179,20 +167,13 @@ class OpenAIRealtimeBetaLLMService(LLMService):
seed=None, seed=None,
filter_incomplete_user_turns=False, filter_incomplete_user_turns=False,
user_turn_completion_config=None, user_turn_completion_config=None,
session_properties=events.SessionProperties(),
) )
# 2. Apply direct init arg overrides (deprecated) # 2. Apply direct init arg overrides (deprecated)
if model is not None: if model is not None:
_warn_deprecated_param("model", OpenAIRealtimeBetaLLMSettings, "model") _warn_deprecated_param("model", OpenAIRealtimeBetaLLMSettings, "model")
default_settings.model = model default_settings.model = model
if session_properties is not None: # 3. Apply settings delta (canonical API, always wins)
_warn_deprecated_param(
"session_properties", OpenAIRealtimeBetaLLMSettings, "session_properties"
)
default_settings.session_properties = session_properties
# 4. Apply settings delta (canonical API, always wins)
if settings is not None: if settings is not None:
default_settings.apply_update(settings) default_settings.apply_update(settings)
@@ -205,6 +186,7 @@ class OpenAIRealtimeBetaLLMService(LLMService):
self.api_key = api_key self.api_key = api_key
self.base_url = full_url self.base_url = full_url
self._session_properties = session_properties or events.SessionProperties()
self._audio_input_paused = start_audio_paused self._audio_input_paused = start_audio_paused
self._send_transcription_frames = send_transcription_frames self._send_transcription_frames = send_transcription_frames
self._websocket = None self._websocket = None
@@ -243,12 +225,12 @@ class OpenAIRealtimeBetaLLMService(LLMService):
def _is_modality_enabled(self, modality: str) -> bool: def _is_modality_enabled(self, modality: str) -> bool:
"""Check if a specific modality is enabled, "text" or "audio".""" """Check if a specific modality is enabled, "text" or "audio"."""
modalities = self._settings.session_properties.modalities or ["audio", "text"] modalities = self._session_properties.modalities or ["audio", "text"]
return modality in modalities return modality in modalities
def _get_enabled_modalities(self) -> list[str]: def _get_enabled_modalities(self) -> list[str]:
"""Get the list of enabled modalities.""" """Get the list of enabled modalities."""
return self._settings.session_properties.modalities or ["audio", "text"] return self._session_properties.modalities or ["audio", "text"]
async def retrieve_conversation_item(self, item_id: str): async def retrieve_conversation_item(self, item_id: str):
"""Retrieve a conversation item by ID from the server. """Retrieve a conversation item by ID from the server.
@@ -315,7 +297,7 @@ class OpenAIRealtimeBetaLLMService(LLMService):
async def _handle_interruption(self): async def _handle_interruption(self):
# None and False are different. Check for False. None means we're using OpenAI's # None and False are different. Check for False. None means we're using OpenAI's
# built-in turn detection defaults. # built-in turn detection defaults.
if self._settings.session_properties.turn_detection is False: if self._session_properties.turn_detection is False:
await self.send_client_event(events.InputAudioBufferClearEvent()) await self.send_client_event(events.InputAudioBufferClearEvent())
await self.send_client_event(events.ResponseCancelEvent()) await self.send_client_event(events.ResponseCancelEvent())
await self._truncate_current_audio_response() await self._truncate_current_audio_response()
@@ -332,7 +314,7 @@ class OpenAIRealtimeBetaLLMService(LLMService):
async def _handle_user_stopped_speaking(self, frame): async def _handle_user_stopped_speaking(self, frame):
# None and False are different. Check for False. None means we're using OpenAI's # None and False are different. Check for False. None means we're using OpenAI's
# built-in turn detection defaults. # built-in turn detection defaults.
if self._settings.session_properties.turn_detection is False: if self._session_properties.turn_detection is False:
await self.send_client_event(events.InputAudioBufferCommitEvent()) await self.send_client_event(events.InputAudioBufferCommitEvent())
await self.send_client_event(events.ResponseCreateEvent()) await self.send_client_event(events.ResponseCreateEvent())
@@ -403,7 +385,7 @@ class OpenAIRealtimeBetaLLMService(LLMService):
# directly. The frame.delta path falls through to super, which calls # directly. The frame.delta path falls through to super, which calls
# _update_settings → our override handles the rest. # _update_settings → our override handles the rest.
if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None: if isinstance(frame, LLMUpdateSettingsFrame) and frame.delta is None:
self._settings.session_properties = events.SessionProperties(**frame.settings) self._session_properties = events.SessionProperties(**frame.settings)
await self._send_session_update() await self._send_session_update()
await self.push_frame(frame, direction) await self.push_frame(frame, direction)
return return
@@ -520,14 +502,13 @@ class OpenAIRealtimeBetaLLMService(LLMService):
await self.push_error(error_msg=f"Error sending client event: {e}", exception=e) await self.push_error(error_msg=f"Error sending client event: {e}", exception=e)
async def _update_settings(self, delta): async def _update_settings(self, delta):
"""Apply a settings delta, sending a session update if needed.""" """Apply a settings delta."""
changed = await super()._update_settings(delta) changed = await super()._update_settings(delta)
if "session_properties" in changed: self._warn_unhandled_updated_settings(changed.keys())
await self._send_session_update()
return changed return changed
async def _send_session_update(self): async def _send_session_update(self):
settings = self._settings.session_properties settings = self._session_properties
# tools given in the context override the tools in the session properties # tools given in the context override the tools in the session properties
if self._context and self._context.tools: if self._context and self._context.tools:
settings.tools = self._context.tools settings.tools = self._context.tools

View File

@@ -43,6 +43,8 @@ class OpenPipeLLMService(OpenAILLMService):
for model training and evaluation. for model training and evaluation.
""" """
_settings: OpenPipeLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -34,6 +34,8 @@ class OpenRouterLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: OpenRouterLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -38,6 +38,8 @@ class PerplexityLLMService(OpenAILLMService):
in token usage reporting between Perplexity (incremental) and OpenAI (final summary). in token usage reporting between Perplexity (incremental) and OpenAI (final summary).
""" """
_settings: PerplexityLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -30,6 +30,8 @@ class QwenLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: QwenLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -42,6 +42,8 @@ class SambaNovaLLMService(OpenAILLMService): # type: ignore
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: SambaNovaLLMSettings
def __init__( def __init__(
self, self,
*, *,

View File

@@ -30,6 +30,8 @@ class TogetherLLMService(OpenAILLMService):
maintaining full compatibility with OpenAI's interface and functionality. maintaining full compatibility with OpenAI's interface and functionality.
""" """
_settings: TogetherLLMSettings
def __init__( def __init__(
self, self,
*, *,