From c2dd7bd98a9a6abbd92159afd58a023b79480cc5 Mon Sep 17 00:00:00 2001 From: kerry Date: Thu, 17 Sep 2026 17:36:11 +0000 Subject: [PATCH 1/2] fix(mock_completion): stamp the resolved provider on mock responses so router custom pricing resolves Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/main.py | 13 ++++++----- tests/test_litellm/test_main.py | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/litellm/main.py b/litellm/main.py index 1c6e47bfb11..40fd5297931 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -999,12 +999,15 @@ def mock_completion( ), ) - try: - _, custom_llm_provider, _, _ = litellm.utils.get_llm_provider(model=model) + if custom_llm_provider is not None: model_response._hidden_params["custom_llm_provider"] = custom_llm_provider - except Exception: - # dont let setting a hidden param block a mock_respose - pass + else: + try: + _, inferred_provider, _, _ = litellm.utils.get_llm_provider(model=model) + model_response._hidden_params["custom_llm_provider"] = inferred_provider + except Exception: + # dont let setting a hidden param block a mock_respose + pass if logging is not None: logging.post_call( diff --git a/tests/test_litellm/test_main.py b/tests/test_litellm/test_main.py index 7fcdc8473d7..78428b6c678 100644 --- a/tests/test_litellm/test_main.py +++ b/tests/test_litellm/test_main.py @@ -2432,6 +2432,46 @@ def test_mock_completion_usage_falls_back_to_default_without_admission_count(): assert response.usage.prompt_tokens == litellm_main.DEFAULT_MOCK_RESPONSE_PROMPT_TOKEN_COUNT +_AZURE_AI_CUSTOM_PRICED_DEPLOYMENT: Final = { + "model_name": "azure-ai-custom-priced", + "litellm_params": { + "model": "azure_ai/gpt-5.6", + "api_key": "mock", + "api_base": "https://example.services.ai.azure.com", + "mock_response": "ok", + "input_cost_per_token": 3e-6, + "output_cost_per_token": 7e-6, + "cache_read_input_token_cost": 1e-7, + "cache_creation_input_token_cost": 5e-7, + }, + "model_info": {"id": "azure-ai-custom-priced-deployment-id"}, +} + + +def _expected_custom_price(response: litellm.ModelResponse) -> float: + params: Final = _AZURE_AI_CUSTOM_PRICED_DEPLOYMENT["litellm_params"] + return ( + response.usage.prompt_tokens * params["input_cost_per_token"] + + response.usage.completion_tokens * params["output_cost_per_token"] + ) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("use_async", (False, True)) +async def test_mock_completion_prices_azure_ai_router_deployment_with_custom_pricing(use_async: bool): + router: Final = litellm.Router(model_list=[_AZURE_AI_CUSTOM_PRICED_DEPLOYMENT]) + messages: Final = [{"role": "user", "content": "hello"}] + + response: Final = ( + await router.acompletion(model="azure-ai-custom-priced", messages=messages) + if use_async + else router.completion(model="azure-ai-custom-priced", messages=messages) + ) + + assert response._hidden_params["response_cost"] == pytest.approx(_expected_custom_price(response)) + assert response._hidden_params["custom_llm_provider"] == "azure_ai" + + _ADMISSION_INPUT_TOKENS: Final = 51234 From 3cf42f6565340f2a11bccaff8588c9cb2c96d3ed Mon Sep 17 00:00:00 2001 From: kerry Date: Thu, 17 Sep 2026 18:07:58 +0000 Subject: [PATCH 2/2] test(mock_completion): cover the provider inference fallback for direct calls Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- tests/test_litellm/test_main.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_litellm/test_main.py b/tests/test_litellm/test_main.py index 78428b6c678..d1fd1d0c4a0 100644 --- a/tests/test_litellm/test_main.py +++ b/tests/test_litellm/test_main.py @@ -2472,6 +2472,21 @@ async def test_mock_completion_prices_azure_ai_router_deployment_with_custom_pri assert response._hidden_params["custom_llm_provider"] == "azure_ai" +@pytest.mark.parametrize( + ("model", "expected_provider"), + (("anthropic/claude-sonnet-5", "anthropic"), ("no-such-provider-model", None)), +) +def test_mock_completion_infers_provider_when_called_directly_without_one(model: str, expected_provider: str | None): + response: Final = litellm.mock_completion( + model=model, + messages=[{"role": "user", "content": "hello"}], + mock_response="ok", + ) + + assert response.choices[0].message.content == "ok" + assert response._hidden_params.get("custom_llm_provider") == expected_provider + + _ADMISSION_INPUT_TOKENS: Final = 51234