mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix: handle Azure URL query params in container transform methods
Override transform methods that append path segments to api_base, using URL parsing to insert subpaths before query parameters. Without this, Azure's ?api-version=... query param causes malformed URLs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
41400c0a3b
commit
7339be571c
2 changed files with 114 additions and 5 deletions
|
|
@ -1,4 +1,5 @@
|
|||
from typing import Optional
|
||||
from typing import Any, Dict, Optional, Tuple
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
import httpx
|
||||
|
||||
|
|
@ -83,3 +84,83 @@ class AzureOpenAIContainerConfig(OpenAIContainerConfig):
|
|||
] = container_cost
|
||||
|
||||
return container_obj
|
||||
|
||||
def _construct_url_with_subpath(self, api_base: str, subpath: str) -> str:
|
||||
"""Construct a URL by inserting subpath before query parameters.
|
||||
|
||||
Azure URLs contain ?api-version=... query params. Naively appending
|
||||
path segments via string concatenation would place them after the
|
||||
query string, producing malformed URLs. This helper parses the URL
|
||||
and appends the subpath to the path component only.
|
||||
"""
|
||||
parsed = urlparse(api_base)
|
||||
new_path = f"{parsed.path.rstrip('/')}/{subpath}"
|
||||
return urlunparse(
|
||||
(
|
||||
parsed.scheme,
|
||||
parsed.netloc,
|
||||
new_path,
|
||||
parsed.params,
|
||||
parsed.query,
|
||||
parsed.fragment,
|
||||
)
|
||||
)
|
||||
|
||||
def transform_container_retrieve_request(
|
||||
self,
|
||||
container_id: str,
|
||||
api_base: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
headers: dict,
|
||||
) -> Tuple[str, Dict]:
|
||||
url = self._construct_url_with_subpath(api_base, container_id)
|
||||
data: Dict[str, Any] = {}
|
||||
return url, data
|
||||
|
||||
def transform_container_delete_request(
|
||||
self,
|
||||
container_id: str,
|
||||
api_base: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
headers: dict,
|
||||
) -> Tuple[str, Dict]:
|
||||
url = self._construct_url_with_subpath(api_base, container_id)
|
||||
data: Dict[str, Any] = {}
|
||||
return url, data
|
||||
|
||||
def transform_container_file_list_request(
|
||||
self,
|
||||
container_id: str,
|
||||
api_base: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
headers: dict,
|
||||
after: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
order: Optional[str] = None,
|
||||
extra_query: Optional[Dict[str, Any]] = None,
|
||||
) -> Tuple[str, Dict]:
|
||||
url = self._construct_url_with_subpath(api_base, f"{container_id}/files")
|
||||
params: Dict[str, Any] = {}
|
||||
if after is not None:
|
||||
params["after"] = after
|
||||
if limit is not None:
|
||||
params["limit"] = str(limit)
|
||||
if order is not None:
|
||||
params["order"] = order
|
||||
if extra_query:
|
||||
params.update(extra_query)
|
||||
return url, params
|
||||
|
||||
def transform_container_file_content_request(
|
||||
self,
|
||||
container_id: str,
|
||||
file_id: str,
|
||||
api_base: str,
|
||||
litellm_params: GenericLiteLLMParams,
|
||||
headers: dict,
|
||||
) -> Tuple[str, Dict]:
|
||||
url = self._construct_url_with_subpath(
|
||||
api_base, f"{container_id}/files/{file_id}/content"
|
||||
)
|
||||
params: Dict[str, Any] = {}
|
||||
return url, params
|
||||
|
|
|
|||
|
|
@ -205,8 +205,32 @@ class TestAzureContainerTransformations:
|
|||
assert isinstance(result, ContainerListResponse)
|
||||
assert len(result.data) == 1
|
||||
|
||||
def test_transform_container_retrieve_request_with_query_params(self):
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers?api-version=preview"
|
||||
url, data = self.config.transform_container_retrieve_request(
|
||||
container_id="cntr_123",
|
||||
api_base=api_base,
|
||||
litellm_params={},
|
||||
headers={"api-key": "test"},
|
||||
)
|
||||
assert "/containers/cntr_123" in url
|
||||
assert "?api-version=preview" in url
|
||||
assert url.index("/cntr_123") < url.index("?api-version")
|
||||
|
||||
def test_transform_container_delete_request_with_query_params(self):
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers?api-version=preview"
|
||||
url, data = self.config.transform_container_delete_request(
|
||||
container_id="cntr_123",
|
||||
api_base=api_base,
|
||||
litellm_params={},
|
||||
headers={"api-key": "test"},
|
||||
)
|
||||
assert "/containers/cntr_123" in url
|
||||
assert "?api-version=preview" in url
|
||||
assert url.index("/cntr_123") < url.index("?api-version")
|
||||
|
||||
def test_transform_container_file_list_request(self):
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers"
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers?api-version=preview"
|
||||
url, params = self.config.transform_container_file_list_request(
|
||||
container_id="cntr_123",
|
||||
api_base=api_base,
|
||||
|
|
@ -214,11 +238,13 @@ class TestAzureContainerTransformations:
|
|||
headers={"api-key": "test"},
|
||||
limit=10,
|
||||
)
|
||||
assert "cntr_123/files" in url
|
||||
assert "/containers/cntr_123/files" in url
|
||||
assert "?api-version=preview" in url
|
||||
assert url.index("/cntr_123/files") < url.index("?api-version")
|
||||
assert params["limit"] == "10"
|
||||
|
||||
def test_transform_container_file_content_request(self):
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers"
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers?api-version=preview"
|
||||
url, params = self.config.transform_container_file_content_request(
|
||||
container_id="cntr_123",
|
||||
file_id="file_456",
|
||||
|
|
@ -226,7 +252,9 @@ class TestAzureContainerTransformations:
|
|||
litellm_params={},
|
||||
headers={"api-key": "test"},
|
||||
)
|
||||
assert "cntr_123/files/file_456/content" in url
|
||||
assert "/containers/cntr_123/files/file_456/content" in url
|
||||
assert "?api-version=preview" in url
|
||||
assert url.index("/file_456/content") < url.index("?api-version")
|
||||
|
||||
def test_transform_container_file_content_response(self):
|
||||
mock_response = MagicMock(spec=httpx.Response)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue