diff --git a/litellm/llms/watsonx/passthrough/transformation.py b/litellm/llms/watsonx/passthrough/transformation.py index 856eddc2907..7fc464d1f25 100644 --- a/litellm/llms/watsonx/passthrough/transformation.py +++ b/litellm/llms/watsonx/passthrough/transformation.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, List, Optional, Tuple from litellm.llms.base_llm.passthrough.transformation import BasePassthroughConfig -from litellm.llms.watsonx.common_utils import IBMWatsonXMixin +from litellm.llms.watsonx.common_utils import IBMWatsonXMixin from litellm.secret_managers.main import get_secret_str if TYPE_CHECKING: @@ -28,11 +28,11 @@ class WatsonxPassthroughConfig(IBMWatsonXMixin, BasePassthroughConfig): ) -> Tuple["URL", str]: """ Construct complete Watsonx URL with version parameter. - + This ensures the version parameter is ALWAYS included in the URL, solving the query parameter issue. """ - base_target_url = self.get_api_base(api_base) or self._get_base_url(api_base) + base_target_url = self.get_api_base(api_base) # Use the format_url helper to construct URL with query params complete_url = self.format_url( @@ -47,13 +47,20 @@ class WatsonxPassthroughConfig(IBMWatsonXMixin, BasePassthroughConfig): def get_api_base( api_base: Optional[str] = None, ) -> Optional[str]: - return api_base or get_secret_str("WATSONX_API_BASE") + return api_base or IBMWatsonXMixin._get_base_url( + self=IBMWatsonXMixin, api_base=api_base + ) @staticmethod def get_api_key( api_key: Optional[str] = None, ) -> Optional[str]: - return api_key or get_secret_str("WATSON_API_KEY") + return ( + api_key + or IBMWatsonXMixin.get_watsonx_credentials( + optional_params=dict(), api_base=None, api_key=api_key + )["api_key"] + ) @staticmethod def get_base_model(model: str) -> Optional[str]: @@ -62,4 +69,4 @@ class WatsonxPassthroughConfig(IBMWatsonXMixin, BasePassthroughConfig): def get_models( self, api_key: Optional[str] = None, api_base: Optional[str] = None ) -> List[str]: - return super().get_models(api_key, api_base) \ No newline at end of file + return super().get_models(api_key, api_base) diff --git a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py index 33f1d1dd5e2..751616caf2c 100644 --- a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py @@ -2425,6 +2425,7 @@ def create_generic_websocket_passthrough_endpoint( cost_per_request=cost_per_request, ) + @router.api_route( "/watsonx/{endpoint:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"], @@ -2455,8 +2456,7 @@ async def watsonx_proxy_route( if provider_config is None: raise HTTPException( - status_code=404, - detail="Watsonx passthrough config not found" + status_code=404, detail="Watsonx passthrough config not found" ) # Get complete URL with version parameter @@ -2465,7 +2465,7 @@ async def watsonx_proxy_route( api_key=None, model="", endpoint=endpoint, - request_query_params=dict(request.query_params), + request_query_params=None, litellm_params={}, ) @@ -2491,8 +2491,9 @@ async def watsonx_proxy_route( if _request_body.get("stream"): is_streaming_request = True - request_query_params = dict() - request_query_params["version"] = litellm.WATSONX_DEFAULT_API_VERSION + request_query_params = dict(request.query_params) + if request_query_params.get("version") is None: + request_query_params["version"] = litellm.WATSONX_DEFAULT_API_VERSION # Create pass-through endpoint endpoint_func = create_pass_through_route( diff --git a/tests/test_litellm/llms/watsonx/passthrough/test_watsonx_passthrough_transformation.py b/tests/test_litellm/llms/watsonx/passthrough/test_watsonx_passthrough_transformation.py index 1c10f52207a..d1db04f5215 100644 --- a/tests/test_litellm/llms/watsonx/passthrough/test_watsonx_passthrough_transformation.py +++ b/tests/test_litellm/llms/watsonx/passthrough/test_watsonx_passthrough_transformation.py @@ -76,7 +76,7 @@ class TestWatsonxPassthroughConfig: assert "version=2024-03-19" in str(complete_url) assert base_target_url == api_base - @patch("litellm.llms.watsonx.passthrough.transformation.get_secret_str") + @patch("litellm.llms.watsonx.common_utils.get_secret_str") def test_get_complete_url_with_env_api_base(self, mock_get_secret): """Test URL construction with api_base from environment.""" config = WatsonxPassthroughConfig() @@ -141,7 +141,7 @@ class TestWatsonxPassthroughConfig: assert base_target_url == api_base assert "version=2024-03-19" not in str(complete_url) - @patch("litellm.llms.watsonx.passthrough.transformation.get_secret_str") + @patch("litellm.llms.watsonx.common_utils.get_secret_str") def test_get_api_base_with_explicit_value(self, mock_get_secret): """Test get_api_base returns explicit value when provided.""" explicit_base = "https://custom.watsonx.com" @@ -151,7 +151,7 @@ class TestWatsonxPassthroughConfig: assert result == explicit_base mock_get_secret.assert_not_called() - @patch("litellm.llms.watsonx.passthrough.transformation.get_secret_str") + @patch("litellm.llms.watsonx.common_utils.get_secret_str") def test_get_api_base_from_environment(self, mock_get_secret): """Test get_api_base retrieves from environment when not provided.""" env_base = "https://env.watsonx.com" @@ -162,7 +162,7 @@ class TestWatsonxPassthroughConfig: assert result == env_base mock_get_secret.assert_called_once_with("WATSONX_API_BASE") - @patch("litellm.llms.watsonx.passthrough.transformation.get_secret_str") + @patch("litellm.llms.watsonx.common_utils.get_secret_str") def test_get_api_key_with_explicit_value(self, mock_get_secret): """Test get_api_key returns explicit value when provided.""" explicit_key = "test-api-key-123" @@ -172,7 +172,7 @@ class TestWatsonxPassthroughConfig: assert result == explicit_key mock_get_secret.assert_not_called() - @patch("litellm.llms.watsonx.passthrough.transformation.get_secret_str") + @patch("litellm.llms.watsonx.common_utils.get_secret_str") def test_get_api_key_from_environment(self, mock_get_secret): """Test get_api_key retrieves from environment when not provided.""" env_key = "env-api-key-456" @@ -181,7 +181,7 @@ class TestWatsonxPassthroughConfig: result = WatsonxPassthroughConfig.get_api_key(api_key=None) assert result == env_key - mock_get_secret.assert_called_once_with("WATSON_API_KEY") + mock_get_secret.assert_any_call("WATSONX_APIKEY") def test_get_base_model_returns_model(self): """Test get_base_model returns the model as-is."""