From 3b6c1707393f103e2fe88ce13043dde91b21a294 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Tue, 25 Nov 2025 07:10:55 +0530 Subject: [PATCH] Fix the azure auth format for videos (#17009) * fix the azure auth in correct format * Add litellm param in validate_environment method * fix lint errors --- litellm/llms/azure/videos/transformation.py | 34 ++++++------ .../llms/base_llm/videos/transformation.py | 1 + litellm/llms/custom_httpx/llm_http_handler.py | 2 + litellm/llms/gemini/videos/transformation.py | 5 ++ litellm/llms/openai/videos/transformation.py | 5 ++ .../llms/runwayml/videos/transformation.py | 5 ++ .../llms/vertex_ai/videos/transformation.py | 8 ++- .../videos/test_azure_video_transformation.py | 53 +++++++++++-------- 8 files changed, 70 insertions(+), 43 deletions(-) diff --git a/litellm/llms/azure/videos/transformation.py b/litellm/llms/azure/videos/transformation.py index 3af9e0778bc..a6fbd8cef8b 100644 --- a/litellm/llms/azure/videos/transformation.py +++ b/litellm/llms/azure/videos/transformation.py @@ -1,9 +1,8 @@ from typing import TYPE_CHECKING, Any, Dict, Optional from litellm.types.videos.main import VideoCreateOptionalRequestParams -from litellm.secret_managers.main import get_secret_str +from litellm.types.router import GenericLiteLLMParams from litellm.llms.azure.common_utils import BaseAzureLLM -import litellm from litellm.llms.openai.videos.transformation import OpenAIVideoConfig if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj @@ -56,22 +55,27 @@ class AzureVideoConfig(OpenAIVideoConfig): headers: dict, model: str, api_key: Optional[str] = None, + litellm_params: Optional[GenericLiteLLMParams] = None, ) -> dict: - api_key = ( - api_key - or litellm.api_key - or litellm.azure_key - or get_secret_str("AZURE_OPENAI_API_KEY") - or get_secret_str("AZURE_API_KEY") + """ + Validate Azure environment and set up authentication headers. + Uses _base_validate_azure_environment to properly handle credentials from litellm_credential_name. + """ + # If litellm_params is provided, use it; otherwise create a new one + if litellm_params is None: + litellm_params = GenericLiteLLMParams() + + if api_key and not litellm_params.api_key: + litellm_params.api_key = api_key + + # Use the base Azure validation method which properly handles: + # 1. Credentials from litellm_credential_name via litellm_params + # 2. Sets the correct "api-key" header (not "Authorization: Bearer") + return BaseAzureLLM._base_validate_azure_environment( + headers=headers, + litellm_params=litellm_params ) - headers.update( - { - "Authorization": f"Bearer {api_key}", - } - ) - return headers - def get_complete_url( self, model: str, diff --git a/litellm/llms/base_llm/videos/transformation.py b/litellm/llms/base_llm/videos/transformation.py index 7e990b42650..50cada42b87 100644 --- a/litellm/llms/base_llm/videos/transformation.py +++ b/litellm/llms/base_llm/videos/transformation.py @@ -66,6 +66,7 @@ class BaseVideoConfig(ABC): headers: dict, model: str, api_key: Optional[str] = None, + litellm_params: Optional[GenericLiteLLMParams] = None, ) -> dict: return {} diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 383dabb3931..fdd504e2f57 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -4126,6 +4126,7 @@ class BaseLLMHTTPHandler: headers=video_generation_optional_request_params.get("extra_headers", {}) or {}, model=model, + litellm_params=litellm_params, ) if extra_headers: @@ -4226,6 +4227,7 @@ class BaseLLMHTTPHandler: headers=video_generation_optional_request_params.get("extra_headers", {}) or {}, model=model, + litellm_params=litellm_params, ) if extra_headers: diff --git a/litellm/llms/gemini/videos/transformation.py b/litellm/llms/gemini/videos/transformation.py index ce2519e9177..4120d1cad22 100644 --- a/litellm/llms/gemini/videos/transformation.py +++ b/litellm/llms/gemini/videos/transformation.py @@ -160,11 +160,16 @@ class GeminiVideoConfig(BaseVideoConfig): headers: dict, model: str, api_key: Optional[str] = None, + litellm_params: Optional[GenericLiteLLMParams] = None, ) -> dict: """ Validate environment and add Gemini API key to headers. Gemini uses x-goog-api-key header for authentication. """ + # Use api_key from litellm_params if available, otherwise fall back to other sources + if litellm_params and litellm_params.api_key: + api_key = api_key or litellm_params.api_key + api_key = ( api_key or litellm.api_key diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py index d1d3fc2919e..abdcd2fbe7b 100644 --- a/litellm/llms/openai/videos/transformation.py +++ b/litellm/llms/openai/videos/transformation.py @@ -61,7 +61,12 @@ class OpenAIVideoConfig(BaseVideoConfig): headers: dict, model: str, api_key: Optional[str] = None, + litellm_params: Optional[GenericLiteLLMParams] = None, ) -> dict: + # Use api_key from litellm_params if available, otherwise fall back to other sources + if litellm_params and litellm_params.api_key: + api_key = api_key or litellm_params.api_key + api_key = ( api_key or litellm.api_key diff --git a/litellm/llms/runwayml/videos/transformation.py b/litellm/llms/runwayml/videos/transformation.py index 651acff6fc4..5a46ebb664b 100644 --- a/litellm/llms/runwayml/videos/transformation.py +++ b/litellm/llms/runwayml/videos/transformation.py @@ -114,11 +114,16 @@ class RunwayMLVideoConfig(BaseVideoConfig): headers: dict, model: str, api_key: Optional[str] = None, + litellm_params: Optional[GenericLiteLLMParams] = None, ) -> dict: """ Validate environment and set up authentication headers. RunwayML uses Bearer token authentication via RUNWAYML_API_SECRET. """ + # Use api_key from litellm_params if available, otherwise fall back to other sources + if litellm_params and litellm_params.api_key: + api_key = api_key or litellm_params.api_key + api_key = ( api_key or litellm.api_key diff --git a/litellm/llms/vertex_ai/videos/transformation.py b/litellm/llms/vertex_ai/videos/transformation.py index 2b6d43dd708..0f7b71c9262 100644 --- a/litellm/llms/vertex_ai/videos/transformation.py +++ b/litellm/llms/vertex_ai/videos/transformation.py @@ -160,13 +160,11 @@ class VertexAIVideoConfig(BaseVideoConfig, VertexBase): def validate_environment( self, - headers: Dict, + headers: dict, model: str, api_key: Optional[str] = None, - api_base: Optional[str] = None, - litellm_params: Optional[dict] = None, - **kwargs, - ) -> Dict: + litellm_params: Optional[GenericLiteLLMParams] = None, + ) -> dict: """ Validate environment and return headers for Vertex AI OCR. diff --git a/tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py b/tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py index 640933179a6..b3d7945db39 100644 --- a/tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py +++ b/tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py @@ -65,8 +65,13 @@ class TestAzureVideoConfig: assert result["size"] == "1280x720" assert result["user"] == "test_user" - def test_validate_environment_with_api_key(self): - """Test environment validation with provided API key.""" + @patch('litellm.llms.azure.common_utils.litellm') + def test_validate_environment_with_api_key(self, mock_litellm): + """Test environment validation with provided API key - should use api-key header for Azure.""" + # Since validate_environment passes litellm_params=None, it relies on litellm.api_key or litellm.azure_key + mock_litellm.api_key = self.api_key + mock_litellm.azure_key = None + headers = {"Content-Type": "application/json"} result_headers = self.config.validate_environment( @@ -75,14 +80,15 @@ class TestAzureVideoConfig: api_key=self.api_key ) - assert "Authorization" in result_headers - assert result_headers["Authorization"] == f"Bearer {self.api_key}" + # Azure uses "api-key" header, not "Authorization: Bearer" + assert "api-key" in result_headers + assert result_headers["api-key"] == self.api_key assert result_headers["Content-Type"] == "application/json" - @patch('litellm.llms.azure.videos.transformation.get_secret_str') - @patch('litellm.llms.azure.videos.transformation.litellm') + @patch('litellm.llms.azure.common_utils.get_secret_str') + @patch('litellm.llms.azure.common_utils.litellm') def test_validate_environment_without_api_key(self, mock_litellm, mock_get_secret): - """Test environment validation without provided API key.""" + """Test environment validation without provided API key - should fallback to secret manager.""" mock_litellm.api_key = None mock_litellm.azure_key = None mock_get_secret.return_value = "secret-api-key" @@ -95,8 +101,8 @@ class TestAzureVideoConfig: api_key=None ) - assert "Authorization" in result_headers - assert result_headers["Authorization"] == "Bearer secret-api-key" + assert "api-key" in result_headers + assert result_headers["api-key"] == "secret-api-key" def test_get_complete_url(self): """Test URL construction for Azure video API.""" @@ -320,23 +326,24 @@ class TestAzureVideoConfig: logging_obj=logging_obj ) - def test_azure_specific_environment_validation(self): + @patch('litellm.llms.azure.common_utils.litellm') + def test_azure_specific_environment_validation(self, mock_litellm): """Test Azure-specific environment validation with different key sources.""" + # Test with azure_key + mock_litellm.api_key = None + mock_litellm.azure_key = "azure-test-key" + mock_litellm.openai_key = None + headers = {"Content-Type": "application/json"} - # Test with azure_key - with patch('litellm.llms.azure.videos.transformation.litellm') as mock_litellm: - mock_litellm.api_key = None - mock_litellm.azure_key = "azure-test-key" - mock_litellm.openai_key = None - - result_headers = self.config.validate_environment( - headers=headers, - model=self.model, - api_key=None - ) - - assert result_headers["Authorization"] == "Bearer azure-test-key" + result_headers = self.config.validate_environment( + headers=headers, + model=self.model, + api_key=None + ) + + assert "api-key" in result_headers + assert result_headers["api-key"] == "azure-test-key" def test_usage_data_creation_in_video_create(self): """Test that usage data is created correctly in video create response."""