fix(decisions): honor base_url alias for custom backends (#48505)

Co-authored-by: Azmi Muwahid <azmimuwahid@gmail.com>
This commit is contained in:
tin-berri 2026-10-26 21:46:43 -04:00 • committed by GitHub
parent bdfe7ae195
commit 9ed502f375
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 2 deletions

View file

@ -456,10 +456,14 @@ def prepare_call(
extra_headers: Mapping[str, str] | None,
kwargs: Mapping[str, object],
) -> DecisionsCall:
base_url_alias: Final = kwargs.get("base_url")
call_api_base: Final = (
base_url_alias if api_base is None and isinstance(base_url_alias, str) and base_url_alias else api_base
)
upstream_model, provider, dynamic_api_key, dynamic_api_base = litellm.get_llm_provider(
model=model,
custom_llm_provider=custom_llm_provider,
api_base=api_base,
api_base=call_api_base,
api_key=api_key,
)
provider_config: Final = _provider_config(upstream_model, provider)
@ -488,7 +492,7 @@ def prepare_call(
try:
resolved_api_base, static_api_key = provider_config.resolve_connection(
dynamic_api_base or api_base, dynamic_api_key or api_key
dynamic_api_base or call_api_base, dynamic_api_key or api_key
)
except ValueError as error:
raise litellm.BadRequestError(message=str(error), model=model, llm_provider=provider) from error

View file

@ -400,6 +400,68 @@ async def test_systemone_uses_evaluation_pricing(respx_mock: respx.MockRouter, d
)
@pytest.mark.asyncio
@pytest.mark.parametrize(
("base_url", "expected_api_base"),
[
("https://onprem.example/v1", "https://onprem.example/v1"),
("", "https://fallback.example/v1"),
(None, "https://fallback.example/v1"),
(123, "https://fallback.example/v1"),
],
)
async def test_openai_decisions_honors_base_url_alias(
respx_mock: respx.MockRouter,
monkeypatch: pytest.MonkeyPatch,
base_url: str | int | None,
expected_api_base: str,
) -> None:
monkeypatch.setenv("OPENAI_BASE_URL", "https://fallback.example/v1")
route: Final = respx_mock.post(f"{expected_api_base}/decisions").respond(json=_OPENAI_RESPONSE)
response: Final = await litellm.adecisions(
model="openai/gpt-6-luna",
input="The package arrived broken.",
questions=[{"type": "predicate", "name": "is_defect", "instructions": "Is this a defect?"}],
api_key="backend-secret-key",
base_url=base_url,
)
assert route.called
assert route.calls[0].request.headers["authorization"] == "Bearer backend-secret-key"
assert response._hidden_params["model"] == "openai/gpt-6-luna"
@pytest.mark.asyncio
async def test_openai_systemone_honors_base_url_alias(respx_mock: respx.MockRouter) -> None:
route: Final = respx_mock.post("https://onprem.example/v1/decisions").respond(json=_OPENAI_RESPONSE)
response: Final = await litellm.asystemone(
model="openai/gpt-6-luna",
state="The package arrived broken.",
questions=_QUESTIONS,
api_key="backend-secret-key",
base_url="https://onprem.example/v1",
)
assert route.called
assert route.calls[0].request.headers["authorization"] == "Bearer backend-secret-key"
assert response._hidden_params["model"] == "openai/gpt-6-luna"
@pytest.mark.asyncio
async def test_openai_decisions_api_base_wins_over_base_url_alias(respx_mock: respx.MockRouter) -> None:
route: Final = respx_mock.post("https://api.openai.com/v1/decisions").respond(json=_OPENAI_RESPONSE)
await litellm.adecisions(
model="openai/gpt-6-luna",
input="The package arrived broken.",
questions=[{"type": "predicate", "name": "is_defect", "instructions": "Is this a defect?"}],
api_key="test-key",
api_base="https://api.openai.com/v1",
base_url="https://onprem.example/v1",
)
assert route.called
@pytest.mark.asyncio
async def test_systemone_falls_back_to_plain_pricing_without_decisions_row(
respx_mock: respx.MockRouter,