diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 4f04fc471b5..80f4d1f2133 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -2768,7 +2768,9 @@ def handle_realtime_stream_cost_calculation( potential_model_names=potential_model_names, combined_usage_object=( RealtimeAPITokenUsageProcessor.collect_and_combine_usage_from_realtime_stream_results( - [event for event in results if event.get("type") != "response.event"] + [ # mutable-ok: collector requires a concrete event list + event for event in results if event.get("type") != "response.event" + ] ) if any(event.get("type") == "response.event" for event in results) else combined_usage_object @@ -2832,7 +2834,7 @@ class _LiveBackendEnvelope(BaseModel): def _live_backend_responses( results: OpenAIRealtimeStreamList, logging_obj: LitellmLoggingObject | None = None ) -> tuple[ResponsesAPIResponse, ...]: - responses: Final = { + responses: Final = { # mutable-ok: deduplicate terminal backend responses by response id response.id: response for result in results if result.get("type") == "response.event" diff --git a/litellm/images/main.py b/litellm/images/main.py index 662903ee35e..64bc5d9b382 100644 --- a/litellm/images/main.py +++ b/litellm/images/main.py @@ -870,7 +870,14 @@ def image_edit( extra_body if isinstance(extra_body, dict) else None, ) if image_edit_provider_config.use_multipart_form_data() - else {**non_default_params, **(extra_body if isinstance(extra_body, dict) else {})} + else { # mutable-ok: image provider update requires a concrete request-parameter dict + **non_default_params, + **( + extra_body + if isinstance(extra_body, dict) + else {} # mutable-ok: empty fallback is consumed immediately + ), + } ) # Pre Call logging diff --git a/litellm/litellm_core_utils/realtime_streaming.py b/litellm/litellm_core_utils/realtime_streaming.py index f487e7513cf..850cfee2923 100644 --- a/litellm/litellm_core_utils/realtime_streaming.py +++ b/litellm/litellm_core_utils/realtime_streaming.py @@ -157,7 +157,12 @@ class RealTimeStreaming: self.messages: list[OpenAIRealtimeEvents] = [] if account_usage and live_initialization_seconds > 0: self.messages.append( - {"type": "litellm.live.initialization", "usage": {"seconds": live_initialization_seconds}} + { # mutable-ok: initialization event is appended to the mutable event history + "type": "litellm.live.initialization", + "usage": { # mutable-ok: usage payload is consumed as part of the typed event + "seconds": live_initialization_seconds, + }, + } ) self._backend_sent_frames: bool = False self.input_message: dict = {} diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 2f3210dad0d..25354b9fe40 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -12176,7 +12176,9 @@ async def realtime_websocket_endpoint( # Only use explicit parameters, not all query params query_params: Final = cast( RealtimeQueryParams, - dict(_realtime_query_params_template(model, intent) + ((("call_id", call_id),) if call_id is not None else ())), + dict( # mutable-ok: FastAPI request query params must be materialized as a dict + _realtime_query_params_template(model, intent) + ((("call_id", call_id),) if call_id is not None else ()) + ), ) data: dict[str, object] = { diff --git a/litellm/proxy/realtime_endpoints/endpoints.py b/litellm/proxy/realtime_endpoints/endpoints.py index c1da14cb667..d66976d3e6f 100644 --- a/litellm/proxy/realtime_endpoints/endpoints.py +++ b/litellm/proxy/realtime_endpoints/endpoints.py @@ -362,9 +362,9 @@ async def create_realtime_client_secret( return RealtimeClientSecretResponse(**upstream_json) -@router.post("/v1/live", tags=["realtime"]) -@router.post("/live", tags=["realtime"]) -@router.post("/openai/v1/live", tags=["realtime"]) +@router.post("/v1/live", tags=["realtime"]) # mutable-ok: FastAPI route metadata uses a mutable tag list +@router.post("/live", tags=["realtime"]) # mutable-ok: FastAPI route metadata uses a mutable tag list +@router.post("/openai/v1/live", tags=["realtime"]) # mutable-ok: FastAPI route metadata uses a mutable tag list async def proxy_live_calls(request: Request) -> Response: from litellm.proxy.realtime_endpoints.call_sessions import create_codex_realtime_call