From ddbb8149754057f7b6f568e9475a76070db45e09 Mon Sep 17 00:00:00 2001 From: Lei Nie Date: Mon, 23 Feb 2026 20:01:11 +0000 Subject: [PATCH] fix(videos): pass api_key from litellm_params to video remix handlers video_remix_handler and async_video_remix_handler were not falling back to litellm_params.api_key when the api_key parameter was None, causing Authorization: Bearer None to be sent to the provider. This matches the pattern already used by async_video_generation_handler. --- litellm/llms/custom_httpx/llm_http_handler.py | 4 +- tests/test_litellm/test_video_generation.py | 113 ++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index ac59ff3d0ed0..7267532933db 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -5602,7 +5602,7 @@ def video_remix_handler( sync_httpx_client = client headers = video_remix_provider_config.validate_environment( - api_key=api_key, + api_key=api_key or litellm_params.get("api_key", None), headers=extra_headers or {}, model="", ) @@ -5684,7 +5684,7 @@ async def async_video_remix_handler( async_httpx_client = client headers = video_remix_provider_config.validate_environment( - api_key=api_key, + api_key=api_key or litellm_params.get("api_key", None), headers=extra_headers or {}, model="", ) diff --git a/tests/test_litellm/test_video_generation.py b/tests/test_litellm/test_video_generation.py index 363e40630733..47a60b09af7f 100644 --- a/tests/test_litellm/test_video_generation.py +++ b/tests/test_litellm/test_video_generation.py @@ -14,6 +14,7 @@ from litellm.cost_calculator import default_video_cost_calculator from litellm.integrations.custom_logger import CustomLogger from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler from litellm.llms.gemini.videos.transformation import GeminiVideoConfig from litellm.llms.openai.videos.transformation import OpenAIVideoConfig @@ -1437,5 +1438,117 @@ def create_mock_coroutine(*args, **kwargs): ) +def test_video_remix_handler_uses_api_key_from_litellm_params(): + """Sync remix handler should fall back to litellm_params api_key when api_key param is None.""" + handler = BaseLLMHTTPHandler() + config = OpenAIVideoConfig() + + with patch.object(config, "validate_environment") as mock_validate: + mock_validate.return_value = {"Authorization": "Bearer deployment-key"} + + with patch.object(config, "transform_video_remix_request") as mock_transform: + mock_transform.return_value = ("https://api.openai.com/v1/videos/video_123/remix", {"prompt": "remix it"}) + + with patch.object(config, "transform_video_remix_response") as mock_resp: + mock_resp.return_value = MagicMock() + + mock_client = MagicMock() + mock_client.post.return_value = MagicMock(status_code=200) + + with patch( + "litellm.llms.custom_httpx.llm_http_handler._get_httpx_client", + return_value=mock_client, + ): + handler.video_remix_handler( + video_id="video_123", + prompt="remix it", + video_remix_provider_config=config, + custom_llm_provider="openai", + litellm_params={"api_key": "deployment-key", "api_base": "https://api.openai.com/v1"}, + logging_obj=MagicMock(), + timeout=5.0, + api_key=None, + _is_async=False, + ) + + mock_validate.assert_called_once() + assert mock_validate.call_args.kwargs["api_key"] == "deployment-key" + + +@pytest.mark.asyncio +async def test_async_video_remix_handler_uses_api_key_from_litellm_params(): + """Async remix handler should fall back to litellm_params api_key when api_key param is None.""" + handler = BaseLLMHTTPHandler() + config = OpenAIVideoConfig() + + with patch.object(config, "validate_environment") as mock_validate: + mock_validate.return_value = {"Authorization": "Bearer deployment-key"} + + with patch.object(config, "transform_video_remix_request") as mock_transform: + mock_transform.return_value = ("https://api.openai.com/v1/videos/video_123/remix", {"prompt": "remix it"}) + + with patch.object(config, "transform_video_remix_response") as mock_resp: + mock_resp.return_value = MagicMock() + + mock_client = MagicMock(spec=AsyncHTTPHandler) + mock_response = MagicMock(status_code=200) + mock_client.post = AsyncMock(return_value=mock_response) + + with patch( + "litellm.llms.custom_httpx.llm_http_handler.get_async_httpx_client", + return_value=mock_client, + ): + await handler.async_video_remix_handler( + video_id="video_123", + prompt="remix it", + video_remix_provider_config=config, + custom_llm_provider="openai", + litellm_params={"api_key": "deployment-key", "api_base": "https://api.openai.com/v1"}, + logging_obj=MagicMock(), + timeout=5.0, + api_key=None, + ) + + mock_validate.assert_called_once() + assert mock_validate.call_args.kwargs["api_key"] == "deployment-key" + + +def test_video_remix_handler_prefers_explicit_api_key(): + """Sync remix handler should prefer explicit api_key over litellm_params.""" + handler = BaseLLMHTTPHandler() + config = OpenAIVideoConfig() + + with patch.object(config, "validate_environment") as mock_validate: + mock_validate.return_value = {"Authorization": "Bearer explicit-key"} + + with patch.object(config, "transform_video_remix_request") as mock_transform: + mock_transform.return_value = ("https://api.openai.com/v1/videos/video_123/remix", {"prompt": "remix it"}) + + with patch.object(config, "transform_video_remix_response") as mock_resp: + mock_resp.return_value = MagicMock() + + mock_client = MagicMock() + mock_client.post.return_value = MagicMock(status_code=200) + + with patch( + "litellm.llms.custom_httpx.llm_http_handler._get_httpx_client", + return_value=mock_client, + ): + handler.video_remix_handler( + video_id="video_123", + prompt="remix it", + video_remix_provider_config=config, + custom_llm_provider="openai", + litellm_params={"api_key": "deployment-key", "api_base": "https://api.openai.com/v1"}, + logging_obj=MagicMock(), + timeout=5.0, + api_key="explicit-key", + _is_async=False, + ) + + mock_validate.assert_called_once() + assert mock_validate.call_args.kwargs["api_key"] == "explicit-key" + + if __name__ == "__main__": pytest.main([__file__])