Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion litellm/proxy/openai_files_endpoints/files_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,10 @@ async def delete_file(
file_id=original_file_id,
)

response = await litellm.afile_delete(**data) # type: ignore
response = await litellm.afile_delete(
custom_llm_provider=credentials["custom_llm_provider"], # type: ignore
**data,
) # type: ignore

verbose_proxy_logger.debug(
f"Deleted file using model: {model_used}"
Expand Down
70 changes: 55 additions & 15 deletions litellm/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -3864,14 +3864,29 @@ async def _ageneric_api_call_with_fallbacks_helper(
self._add_deployment_model_to_endpoint_for_llm_passthrough_route(
kwargs=kwargs, model=model, model_name=model_name
)
### get custom
response = original_generic_function(
**{
**data,
"caching": self.cache_responses,
**kwargs,
}
)

# Get custom_llm_provider from deployment params
try:
custom_llm_provider = data.get("custom_llm_provider")
_, inferred_custom_llm_provider, _, _ = get_llm_provider(
model=data["model"],
custom_llm_provider=custom_llm_provider,
)
custom_llm_provider = custom_llm_provider or inferred_custom_llm_provider
except Exception:
custom_llm_provider = None

# Build response kwargs
response_kwargs = {
**data,
"caching": self.cache_responses,
**kwargs,
}
# Only set custom_llm_provider if it's not None
if custom_llm_provider is not None:
response_kwargs["custom_llm_provider"] = custom_llm_provider

response = original_generic_function(**response_kwargs)

rpm_semaphore = self._get_client(
deployment=deployment,
Expand Down Expand Up @@ -3961,7 +3976,12 @@ def _generic_api_call_with_fallbacks(
self.routing_strategy_pre_call_checks(deployment=deployment)

try:
_, custom_llm_provider, _, _ = get_llm_provider(model=data["model"])
custom_llm_provider = data.get("custom_llm_provider")
_, inferred_custom_llm_provider, _, _ = get_llm_provider(
model=data["model"],
custom_llm_provider=custom_llm_provider,
)
custom_llm_provider = custom_llm_provider or inferred_custom_llm_provider
except Exception:
custom_llm_provider = None

Expand Down Expand Up @@ -4219,9 +4239,14 @@ async def create_file_for_deployment(deployment: dict) -> OpenAIFileObject:
self.total_calls[model_name] += 1

## REPLACE MODEL IN FILE WITH SELECTED DEPLOYMENT ##
stripped_model, custom_llm_provider, _, _ = get_llm_provider(
model=data["model"]
# For DB/config deployments, use provider from deployment params
custom_llm_provider = data.get("custom_llm_provider")
stripped_model, inferred_custom_llm_provider, _, _ = get_llm_provider(
model=data["model"],
custom_llm_provider=custom_llm_provider,
)
# Preserve explicitly stored provider, fallback to inferred
custom_llm_provider = custom_llm_provider or inferred_custom_llm_provider

## REPLACE MODEL IN FILE WITH SELECTED DEPLOYMENT ##
purpose = cast(Optional[OpenAIFilesPurpose], kwargs.get("purpose"))
Expand Down Expand Up @@ -4367,8 +4392,13 @@ async def avector_store_create(
)
self.total_calls[model_name] += 1

# Get custom provider
_, custom_llm_provider, _, _ = get_llm_provider(model=data["model"])
# Get custom provider from deployment params
custom_llm_provider = data.get("custom_llm_provider")
_, inferred_custom_llm_provider, _, _ = get_llm_provider(
model=data["model"],
custom_llm_provider=custom_llm_provider,
)
custom_llm_provider = custom_llm_provider or inferred_custom_llm_provider

response = avector_store_create_sdk(
**{
Expand Down Expand Up @@ -4486,7 +4516,12 @@ async def _acreate_batch(
self.total_calls[model_name] += 1

## SET CUSTOM PROVIDER TO SELECTED DEPLOYMENT ##
_, custom_llm_provider, _, _ = get_llm_provider(model=data["model"])
custom_llm_provider = data.get("custom_llm_provider")
_, inferred_custom_llm_provider, _, _ = get_llm_provider(
model=data["model"],
custom_llm_provider=custom_llm_provider,
)
custom_llm_provider = custom_llm_provider or inferred_custom_llm_provider

response = litellm.acreate_batch(
**{
Expand Down Expand Up @@ -4720,7 +4755,12 @@ async def _acancel_batch(
self.total_calls[model_name] += 1

## SET CUSTOM PROVIDER TO SELECTED DEPLOYMENT ##
_, custom_llm_provider, _, _ = get_llm_provider(model=data["model"])
custom_llm_provider = data.get("custom_llm_provider")
_, inferred_custom_llm_provider, _, _ = get_llm_provider(
model=data["model"],
custom_llm_provider=custom_llm_provider,
)
custom_llm_provider = custom_llm_provider or inferred_custom_llm_provider

response = litellm.acancel_batch(
**{
Expand Down
73 changes: 73 additions & 0 deletions tests/test_litellm/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,79 @@ async def test_async_router_acreate_file_with_jsonl():
assert first_call_content == non_jsonl_content


@pytest.mark.asyncio
async def test_async_router_acreate_file_uses_deployment_custom_llm_provider():
"""
Ensure file routing preserves deployment custom_llm_provider instead of
inferring provider from model string alone.
"""
from unittest.mock import MagicMock, patch

router = litellm.Router(
model_list=[
{
"model_name": "team-azure-batch",
"litellm_params": {
"model": "gpt-4.1-mini",
"custom_llm_provider": "azure",
"api_base": "https://example-resource.openai.azure.com",
},
},
],
)

with patch("litellm.acreate_file", return_value=MagicMock()) as mock_acreate_file:
await router.acreate_file(
model="team-azure-batch",
purpose="batch",
file=MagicMock(),
)

assert mock_acreate_file.call_count == 1
assert mock_acreate_file.call_args.kwargs["custom_llm_provider"] == "azure"


@pytest.mark.asyncio
async def test_async_router_afile_content_uses_deployment_custom_llm_provider():
"""
Regression test: Ensure afile_content preserves deployment custom_llm_provider
when model name lacks provider prefix (e.g., "gpt-4.1-mini" instead of "azure/gpt-4.1-mini").

This prevents "None is not a valid LlmProviders" errors when calling file content operations.
"""
from unittest.mock import AsyncMock, MagicMock, patch
from litellm.types.llms.openai import HttpxBinaryResponseContent

router = litellm.Router(
model_list=[
{
"model_name": "team-azure-batch",
"litellm_params": {
"model": "gpt-4.1-mini", # No provider prefix
"custom_llm_provider": "azure",
"api_base": "https://example-resource.openai.azure.com",
"api_key": "test-key",
},
},
],
)

# Mock the Azure file handler's afile_content method
mock_response = MagicMock(spec=HttpxBinaryResponseContent)
mock_response.response = MagicMock()

with patch("litellm.llms.azure.files.handler.AzureOpenAIFilesAPI.afile_content",
return_value=mock_response) as mock_afile_content:
result = await router.afile_content(
model="team-azure-batch",
file_id="file-123",
)

# Verify the call was made (proves custom_llm_provider was correctly passed)
assert mock_afile_content.call_count == 1
assert result == mock_response
Comment thread
Sameerlite marked this conversation as resolved.
Comment thread
Sameerlite marked this conversation as resolved.


@pytest.mark.asyncio
async def test_arouter_async_get_healthy_deployments():
"""
Expand Down
Loading