diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index 1ffb5e2f8e5..9c98a12b776 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -315,8 +315,14 @@ def get_llm_provider( dynamic_api_key = get_secret_str("CEREBRAS_API_KEY") elif endpoint == NADIR_DEFAULT_API_BASE: custom_llm_provider = "nadir" # rebind-ok: mirrors sibling endpoint branches + # NADIR_API_KEY belongs to NADIR_API_BASE, the same scoping as the explicit nadir branch: + # it reaches the hosted API only when that is the configured base. dynamic_api_key = ( - get_secret_str("NADIR_API_KEY") if api_base.lower().startswith("https://") else None + get_secret_str("NADIR_API_KEY") + if api_base.lower().startswith("https://") + and (get_secret_str("NADIR_API_BASE") or NADIR_DEFAULT_API_BASE).rstrip("/") + == NADIR_DEFAULT_API_BASE + else None ) elif endpoint == "https://inference.baseten.co/v1": custom_llm_provider = "baseten" diff --git a/litellm/llms/nadir/decisions/transformation.py b/litellm/llms/nadir/decisions/transformation.py index 3c485069fec..41f52338bd0 100644 --- a/litellm/llms/nadir/decisions/transformation.py +++ b/litellm/llms/nadir/decisions/transformation.py @@ -142,8 +142,9 @@ class NadirDecisionsConfig(BaseDecisionsConfig): """Nadir as a Decisions provider: ``/v1/bucket`` answers a single simple/medium/complex choice question. Nadir is a tier classifier, not a general judge, so any other question set is rejected before a call. - ``NADIR_API_KEY`` is only sent to ``NADIR_API_BASE`` or the hosted API, the same scoping as the ``nadir`` - chat provider; with no key the call is anonymous, which Nadir serves on a per-IP rate-limited tier. + ``NADIR_API_KEY`` is only sent to ``NADIR_API_BASE`` (the hosted API when unset), whether the provider is named + or inferred from the base, the same scoping as the ``nadir`` chat provider; with no key the call is anonymous, + which Nadir serves on a per-IP rate-limited tier. """ path = "/v1/bucket" diff --git a/tests/unit/decisions/test_main.py b/tests/unit/decisions/test_main.py index d242c16fc1a..831468828fe 100644 --- a/tests/unit/decisions/test_main.py +++ b/tests/unit/decisions/test_main.py @@ -727,6 +727,28 @@ async def test_nadir_answers_a_tier_question_from_its_bucket_verdict_and_keeps_i assert "authorization" not in elsewhere.calls.last.request.headers +@pytest.mark.asyncio +async def test_nadir_inferred_from_the_hosted_base_sends_only_a_hosted_key( + monkeypatch: pytest.MonkeyPatch, respx_mock: respx.MockRouter +) -> None: + monkeypatch.setenv("NADIR_API_KEY", "ndr-env") + verdict: Final = {"routing_tier": "simple", "confidence": 0.8} + hosted: Final = respx_mock.post("https://api.getnadir.com/v1/bucket").respond(json=verdict) + questions: Final = {"tier": {"type": "choice", "criteria": {"simple": None, "medium": None, "complex": None}}} + + monkeypatch.delenv("NADIR_API_BASE", raising=False) + _ = await litellm.asystemone( + model="bucket", state="hi", questions=questions, api_base="https://api.getnadir.com/v1" + ) + monkeypatch.setenv("NADIR_API_BASE", "https://nadir.internal/v1") + _ = await litellm.asystemone( + model="bucket", state="hi", questions=questions, api_base="https://api.getnadir.com/v1" + ) + + assert hosted.calls[0].request.headers["authorization"] == "Bearer ndr-env" + assert "authorization" not in hosted.calls[1].request.headers + + @pytest.mark.asyncio async def test_nadir_refuses_a_question_it_cannot_answer_before_any_call(respx_mock: respx.MockRouter) -> None: upstream: Final = respx_mock.post("https://api.getnadir.com/v1/bucket")