fix(router): resolve provider from api_base in deployment validation and acompletion

Router._add_deployment called get_llm_provider without the deployment's api_base, so a config entry with a bare model plus a known OpenAI-compatible endpoint failed startup validation with LLM Provider NOT provided and the proxy returned 400 no healthy deployments for that model group. acompletion had the same gap at request time: it forwarded only base_url into its get_llm_provider call, dropping the api_base kwarg the router passes. Both now forward api_base so endpoint matching resolves the provider the same way sync completion already does
This commit is contained in:
mateo-berri 2026-08-25 10:33:40 -07:00
parent 92fe35854b
commit 0bd4d323da
4 changed files with 77 additions and 1 deletions

View file

@ -602,7 +602,7 @@ async def acompletion(
_, custom_llm_provider, _, _ = get_llm_provider(
model=model,
custom_llm_provider=custom_llm_provider,
api_base=base_url,
api_base=kwargs.get("api_base") or base_url,
)
fallbacks = fallbacks or litellm.model_fallbacks

View file

@ -8341,6 +8341,7 @@ class Router:
) = litellm.get_llm_provider(
model=deployment.litellm_params.model,
custom_llm_provider=deployment.litellm_params.get("custom_llm_provider", None),
api_base=deployment.litellm_params.api_base,
)
# done reading model["litellm_params"]
# Check if provider is supported: either in enum or JSON-configured

View file

@ -2944,3 +2944,16 @@ def test_a_stream_that_reported_no_usage_is_still_billed(local_cost_map):
assert cost == pytest.approx(
_priced_at(rebuilt.usage.prompt_tokens, rebuilt.usage.completion_tokens)
)
@pytest.mark.asyncio
async def test_acompletion_resolves_provider_from_api_base():
response = await litellm.acompletion(
model="deepseek-chat",
api_base="https://api.deepseek.com/v1",
api_key="fake-key",
messages=[{"role": "user", "content": "hi"}],
mock_response="resolved",
)
assert response.choices[0].message.content == "resolved"

View file

@ -8921,3 +8921,65 @@ class TestAzureBaseModelFallbackLogging:
deployment=None, received_model_name="my-group", id="azure-base-model-test-id"
)
assert model_info["max_input_tokens"] == litellm.model_cost["azure/gpt-4o-mini"]["max_input_tokens"]
class TestAddDeploymentApiBaseProviderResolution:
def test_bare_model_with_known_api_base_initializes(self):
router = litellm.Router(
model_list=[
{
"model_name": "groq-pinned",
"litellm_params": {
"model": "llama-3.3-70b-versatile",
"api_base": "https://api.groq.com/openai/v1",
"api_key": "fake-key",
},
},
{
"model_name": "deepseek-pinned",
"litellm_params": {
"model": "deepseek-chat",
"api_base": "https://api.deepseek.com/v1",
"api_key": "fake-key",
},
},
]
)
model_list = router.get_model_list()
assert model_list is not None
assert {m["model_name"] for m in model_list} == {"groq-pinned", "deepseek-pinned"}
def test_bare_model_with_unknown_api_base_still_raises(self):
with pytest.raises(litellm.BadRequestError, match="LLM Provider NOT provided"):
litellm.Router(
model_list=[
{
"model_name": "mystery",
"litellm_params": {
"model": "some-unknown-model",
"api_base": "https://llm.internal.example.com/v1",
"api_key": "fake-key",
},
}
]
)
def test_explicit_custom_llm_provider_beats_api_base_endpoint_match(self):
router = litellm.Router(
model_list=[
{
"model_name": "openai-via-gateway",
"litellm_params": {
"model": "gpt-3.5-turbo",
"custom_llm_provider": "openai",
"api_base": "https://api.groq.com/openai/v1",
"api_key": "fake-key",
},
}
]
)
deployment = router.get_deployment_by_model_group_name("openai-via-gateway")
assert deployment is not None
assert deployment.litellm_params.custom_llm_provider == "openai"