mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-09 22:31:41 +00:00
fix(gemini realtime): deep-merge automaticActivityDetection on follow-up session.update
The follow-up setup merge already deep-merged generationConfig and realtimeInputConfig, but realtimeInputConfig.automaticActivityDetection itself is a nested dict. A partial VAD update (e.g. the guardrail-injected disabled=True from create_response=False) silently dropped unrelated knobs such as silenceDurationMs and prefixPaddingMs from the original setup. Deep-merge that block too so partial overrides only touch the fields they specify.
This commit is contained in:
parent
d56d875ea6
commit
459c1973b4
2 changed files with 60 additions and 1 deletions
|
|
@ -368,10 +368,29 @@ class GeminiRealtimeConfig(BaseRealtimeConfig):
|
|||
if isinstance(original_realtime_input_config, dict) and isinstance(
|
||||
new_realtime_input_config, dict
|
||||
):
|
||||
follow_up_setup["realtimeInputConfig"] = {
|
||||
merged_realtime_input_config = {
|
||||
**original_realtime_input_config,
|
||||
**new_realtime_input_config,
|
||||
}
|
||||
# Deep-merge ``automaticActivityDetection`` so a partial VAD
|
||||
# update (e.g. the guardrail-injected ``disabled: True`` from
|
||||
# ``create_response: False``) does not silently drop unrelated
|
||||
# knobs like ``silenceDurationMs`` / ``prefixPaddingMs`` from
|
||||
# the original setup.
|
||||
original_automatic_activity_detection = original_realtime_input_config.get(
|
||||
"automaticActivityDetection"
|
||||
)
|
||||
new_automatic_activity_detection = new_realtime_input_config.get(
|
||||
"automaticActivityDetection"
|
||||
)
|
||||
if isinstance(original_automatic_activity_detection, dict) and isinstance(
|
||||
new_automatic_activity_detection, dict
|
||||
):
|
||||
merged_realtime_input_config["automaticActivityDetection"] = {
|
||||
**original_automatic_activity_detection,
|
||||
**new_automatic_activity_detection,
|
||||
}
|
||||
follow_up_setup["realtimeInputConfig"] = merged_realtime_input_config
|
||||
verbose_logger.debug(
|
||||
"Gemini Realtime: Forwarding session.update as follow-up setup"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1047,3 +1047,43 @@ def test_gemini_follow_up_session_update_preserves_response_modalities_on_partia
|
|||
assert follow_up["generationConfig"]["responseModalities"] == ["AUDIO"]
|
||||
assert follow_up["generationConfig"]["maxOutputTokens"] == 2048
|
||||
assert follow_up["generationConfig"]["temperature"] == 0.7
|
||||
|
||||
|
||||
def test_gemini_subsequent_session_update_preserves_automatic_activity_detection_subfields():
|
||||
"""A follow-up turn_detection update that only sets ``create_response``
|
||||
(mapped to ``disabled``) must not drop ``silenceDurationMs`` /
|
||||
``prefixPaddingMs`` from the original ``automaticActivityDetection``
|
||||
block."""
|
||||
config = GeminiRealtimeConfig()
|
||||
|
||||
original_setup = {
|
||||
"setup": {
|
||||
"model": "models/gemini-2.5-flash-native-audio",
|
||||
"generationConfig": {"responseModalities": ["AUDIO"]},
|
||||
"realtimeInputConfig": {
|
||||
"automaticActivityDetection": {
|
||||
"disabled": False,
|
||||
"silenceDurationMs": 500,
|
||||
"prefixPaddingMs": 100,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
session_update = {
|
||||
"type": "session.update",
|
||||
"session": {"turn_detection": {"create_response": False}},
|
||||
}
|
||||
|
||||
messages = config.transform_realtime_request(
|
||||
json.dumps(session_update),
|
||||
"gemini-2.5-flash-native-audio",
|
||||
session_configuration_request=json.dumps(original_setup),
|
||||
)
|
||||
|
||||
automatic_activity_detection = json.loads(messages[0])["setup"][
|
||||
"realtimeInputConfig"
|
||||
]["automaticActivityDetection"]
|
||||
assert automatic_activity_detection["disabled"] is True
|
||||
assert automatic_activity_detection["silenceDurationMs"] == 500
|
||||
assert automatic_activity_detection["prefixPaddingMs"] == 100
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue