From f4103c51a6b314855f7bc94759ce2f6401edb68d Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Thu, 12 Mar 2026 18:40:37 +0530 Subject: [PATCH] address greptile review feedback (greploop iteration 1) - Add api-version query param to Azure realtime URLs - Remove Content-Type from Azure realtime_calls headers (httpx sets it) - Add token expiry validation in proxy_realtime_calls endpoint - Fix type annotations for upstream_resp Made-with: Cursor --- .../llms/azure/realtime/http_transformation.py | 11 ++++++++--- litellm/proxy/realtime_endpoints/endpoints.py | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/litellm/llms/azure/realtime/http_transformation.py b/litellm/llms/azure/realtime/http_transformation.py index 069b924d691..ef9a2d92d48 100644 --- a/litellm/llms/azure/realtime/http_transformation.py +++ b/litellm/llms/azure/realtime/http_transformation.py @@ -24,9 +24,10 @@ class AzureRealtimeHTTPConfig(BaseRealtimeHTTPConfig): or "" ) - def get_complete_url(self, api_base: Optional[str], model: str) -> str: + def get_complete_url(self, api_base: Optional[str], model: str, api_version: Optional[str] = None) -> str: base = self.get_api_base(api_base).rstrip("/") - return f"{base}/v1/realtime/client_secrets" + version = api_version or get_secret_str("AZURE_API_VERSION") or "2024-12-17" + return f"{base}/openai/realtime/client_secrets?api-version={version}" def validate_environment( self, @@ -40,8 +41,12 @@ class AzureRealtimeHTTPConfig(BaseRealtimeHTTPConfig): "Content-Type": "application/json", } + def get_realtime_calls_url(self, api_base: Optional[str], model: str, api_version: Optional[str] = None) -> str: + base = self.get_api_base(api_base).rstrip("/") + version = api_version or get_secret_str("AZURE_API_VERSION") or "2024-12-17" + return f"{base}/openai/realtime/calls?api-version={version}" + def get_realtime_calls_headers(self, ephemeral_key: str) -> dict: return { "api-key": ephemeral_key, - "Content-Type": "application/sdp", } diff --git a/litellm/proxy/realtime_endpoints/endpoints.py b/litellm/proxy/realtime_endpoints/endpoints.py index 70fb897c14c..75587f08289 100644 --- a/litellm/proxy/realtime_endpoints/endpoints.py +++ b/litellm/proxy/realtime_endpoints/endpoints.py @@ -1,8 +1,10 @@ #### Realtime WebRTC Endpoints ##### import json +import time from typing import Any, Dict, Optional +import httpx from fastapi import APIRouter, Depends, HTTPException, Request, Response from fastapi import status as http_status @@ -148,7 +150,7 @@ async def create_realtime_client_secret( llm_router=llm_router, user_model=user_model, ) - upstream_resp = await llm_call + upstream_resp: httpx.Response = await llm_call # type: ignore except Exception as e: await proxy_logging_obj.post_call_failure_hook( @@ -264,6 +266,16 @@ async def proxy_realtime_calls( sdp_body: bytes = await request.body() decoded_payload = _decode_realtime_token_payload(decrypted_token_value) if decoded_payload is not None: + # Check token expiry + expires_at = decoded_payload.get("expires_at") + if expires_at is not None and isinstance(expires_at, int): + if time.time() > expires_at: + return Response( + content=json.dumps({"error": "Token has expired"}), + status_code=http_status.HTTP_401_UNAUTHORIZED, + media_type="application/json", + ) + openai_ephemeral_key = decoded_payload.get("ephemeral_key", "") model = ( decoded_payload.get("model_id") @@ -319,7 +331,7 @@ async def proxy_realtime_calls( llm_router=llm_router, user_model=user_model, ) - upstream_resp = await llm_call + upstream_resp: httpx.Response = await llm_call # type: ignore except Exception as e: await proxy_logging_obj.post_call_failure_hook(