fix(videos): pass api_key from litellm_params to video remix handlers (#21965)

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.
This commit is contained in:
Lei Nie
2026-02-23 17:15:24 -08:00
committed by GitHub
parent a749598693
commit eed2e4ee5f
2 changed files with 115 additions and 2 deletions
@@ -5602,7 +5602,7 @@ class BaseLLMHTTPHandler:
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 @@ class BaseLLMHTTPHandler:
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="",
)
+113
View File
@@ -14,6 +14,7 @@ import litellm
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 @@ class TestVideoEndpointsProxyLitellmParams:
)
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__])