From 34eac7ce54d7cb905b063d1dc95f19236822782f Mon Sep 17 00:00:00 2001 From: kerry Date: Tue, 10 Nov 2026 18:44:25 +0000 Subject: [PATCH] fix(cost-map): bill web search on the azure_ai Claude rows that were missing a search price azure_ai/claude-opus-4-5, sonnet-4-5, sonnet-4-6 and mythos-5-1 had no search_context_cost_per_query while their Anthropic rows bill $0.01 per query, so web searches through Azure on them cost $0. Copy the price and mark them supports_web_search in both catalog copies, and add a test that keeps every azure_ai Claude row's search price in step with its bare row. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 24 +++++++++++++++++++ model_prices_and_context_window.json | 24 +++++++++++++++++++ .../test_model_prices_and_context_window.py | 23 ++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index a4c16fbeaa3..ee3664d22d3 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3921,6 +3921,11 @@ "max_input_tokens": 200000, "max_output_tokens": 64000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": true, "supports_computer_use": true, @@ -3932,6 +3937,7 @@ "supports_tool_choice": true, "supports_vision": true, "supports_output_config": true, + "supports_web_search": true, "prompt_cache_min_tokens": 4096, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/model-retirement-schedule" }, @@ -4088,6 +4094,11 @@ "max_input_tokens": 1000000, "max_output_tokens": 128000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "supports_adaptive_thinking": true, "thinking_always_on": true, "supports_mid_conversation_system": true, @@ -4105,6 +4116,7 @@ "supports_vision": true, "supports_xhigh_reasoning_effort": true, "supports_max_reasoning_effort": false, + "supports_web_search": true, "prompt_cache_min_tokens": 512, "source": "https://learn.microsoft.com/en-us/azure/foundry/concepts/model-lifecycle-retirement" }, @@ -4229,6 +4241,11 @@ "max_input_tokens": 200000, "max_output_tokens": 64000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, "supports_computer_use": true, @@ -4239,6 +4256,7 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, + "supports_web_search": true, "prompt_cache_min_tokens": 1024, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/model-retirement-schedule" }, @@ -4290,6 +4308,11 @@ "max_input_tokens": 1000000, "max_output_tokens": 128000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": false, "supports_computer_use": true, @@ -4302,6 +4325,7 @@ "supports_tool_choice": true, "supports_vision": true, "supports_output_config": true, + "supports_web_search": true, "prompt_cache_min_tokens": 1024, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/claude-models" }, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index a4c16fbeaa3..ee3664d22d3 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3921,6 +3921,11 @@ "max_input_tokens": 200000, "max_output_tokens": 64000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "output_cost_per_token": 2.5e-05, "supports_assistant_prefill": true, "supports_computer_use": true, @@ -3932,6 +3937,7 @@ "supports_tool_choice": true, "supports_vision": true, "supports_output_config": true, + "supports_web_search": true, "prompt_cache_min_tokens": 4096, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/model-retirement-schedule" }, @@ -4088,6 +4094,11 @@ "max_input_tokens": 1000000, "max_output_tokens": 128000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "supports_adaptive_thinking": true, "thinking_always_on": true, "supports_mid_conversation_system": true, @@ -4105,6 +4116,7 @@ "supports_vision": true, "supports_xhigh_reasoning_effort": true, "supports_max_reasoning_effort": false, + "supports_web_search": true, "prompt_cache_min_tokens": 512, "source": "https://learn.microsoft.com/en-us/azure/foundry/concepts/model-lifecycle-retirement" }, @@ -4229,6 +4241,11 @@ "max_input_tokens": 200000, "max_output_tokens": 64000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": true, "supports_computer_use": true, @@ -4239,6 +4256,7 @@ "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true, + "supports_web_search": true, "prompt_cache_min_tokens": 1024, "source": "https://learn.microsoft.com/en-us/azure/foundry/openai/concepts/model-retirement-schedule" }, @@ -4290,6 +4308,11 @@ "max_input_tokens": 1000000, "max_output_tokens": 128000, "mode": "chat", + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, "output_cost_per_token": 1.5e-05, "supports_assistant_prefill": false, "supports_computer_use": true, @@ -4302,6 +4325,7 @@ "supports_tool_choice": true, "supports_vision": true, "supports_output_config": true, + "supports_web_search": true, "prompt_cache_min_tokens": 1024, "source": "https://learn.microsoft.com/en-us/azure/foundry/foundry-models/concepts/claude-models" }, diff --git a/tests/unit/test_model_prices_and_context_window.py b/tests/unit/test_model_prices_and_context_window.py index b270d385693..165f92e0e59 100644 --- a/tests/unit/test_model_prices_and_context_window.py +++ b/tests/unit/test_model_prices_and_context_window.py @@ -1050,6 +1050,29 @@ def test_bedrock_gpt_oss_vision_is_explicitly_disabled_for_the_router_gate() -> assert litellm.utils.is_vision_explicitly_disabled("bedrock_mantle/openai.gpt-oss-20b") is True +@pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup")) +def test_azure_ai_claude_rows_bill_web_search_like_their_anthropic_rows(path: Path) -> None: + prices: Final[dict[str, JsonValue]] = TypeAdapter(dict[str, JsonValue]).validate_json(path.read_bytes()) + pairs: Final = { + key: (row, prices[key.removeprefix("azure_ai/")]) + for key, row in prices.items() + if key.startswith("azure_ai/claude") and isinstance(row, dict) and key.removeprefix("azure_ai/") in prices + } + priced_pairs: Final = { + key: (row, bare) + for key, (row, bare) in pairs.items() + if isinstance(bare, dict) and bare.get("search_context_cost_per_query") + } + assert priced_pairs + drifted: Final = tuple( + key + for key, (row, bare) in priced_pairs.items() + if row.get("search_context_cost_per_query") != bare["search_context_cost_per_query"] + or row.get("supports_web_search") is not True + ) + assert drifted == (), drifted + + @pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup")) def test_azure_ai_claude_rows_priced_for_web_search_say_they_support_it(path: Path) -> None: prices: Final[dict[str, JsonValue]] = TypeAdapter(dict[str, JsonValue]).validate_json(path.read_bytes())