From f5593c38cd8ba05a2c81a835f4f86cccd9fe3072 Mon Sep 17 00:00:00 2001 From: Ishaan Date: Sat, 15 Aug 2026 09:22:51 +0000 Subject: [PATCH] fix: forward metadata into speech() litellm_params_dict so Gemini TTS spend is tracked ## TLDR Signed-off-by: Ishaan --- litellm/main.py | 2 +- .../llms/gemini/test_gemini_tts.py | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/litellm/main.py b/litellm/main.py index 2a8ed6c87b6..55614c55168 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -7945,7 +7945,7 @@ def speech( if max_retries is None: max_retries = litellm.num_retries or openai.DEFAULT_MAX_RETRIES - litellm_params_dict: Final = get_litellm_params(**kwargs) + litellm_params_dict: Final = get_litellm_params(metadata=metadata, **kwargs) # Get provider-specific text-to-speech config and map parameters text_to_speech_provider_config = ProviderConfigManager.get_provider_text_to_speech_config( diff --git a/tests/test_litellm/llms/gemini/test_gemini_tts.py b/tests/test_litellm/llms/gemini/test_gemini_tts.py index 98f3ac0f4e5..152f51ba5d1 100644 --- a/tests/test_litellm/llms/gemini/test_gemini_tts.py +++ b/tests/test_litellm/llms/gemini/test_gemini_tts.py @@ -420,5 +420,44 @@ class TestGeminiTTSSpeechConfigInRequestBody: assert "AUDIO" in generation_config["responseModalities"] +class TestSpeechToCompletionBridgeMetadata: + """Regression for #37015: metadata must be forwarded into the inner completion call.""" + + def test_metadata_forwarded_to_bridge(self): + from unittest.mock import MagicMock, patch + + from litellm.endpoints.speech.speech_to_completion_bridge.transformation import ( + SpeechToCompletionBridgeTransformationHandler, + ) + from litellm.litellm_core_utils.get_litellm_params import get_litellm_params + + metadata = {"user_api_key": "test_key_hash", "user_api_key_user_id": "user123"} + litellm_params_dict = get_litellm_params(metadata=metadata) + + assert litellm_params_dict["metadata"] == metadata, ( + "metadata must be present in litellm_params_dict; without this the inner " + "completion call overwrites it with None and spend tracking is lost" + ) + + handler = SpeechToCompletionBridgeTransformationHandler() + logging_obj = MagicMock() + + request_kwargs = handler.transform_request( + model="gemini-2.5-flash-preview-tts", + input="hello", + voice="Kore", + optional_params={}, + litellm_params=litellm_params_dict, + headers={}, + litellm_logging_obj=logging_obj, + custom_llm_provider="gemini", + ) + + assert request_kwargs["metadata"] == metadata, ( + "metadata must survive transform_request so the inner completion call " + "does not overwrite the logging object's metadata with None" + ) + + if __name__ == "__main__": pytest.main([__file__])