From 5b54bf2328c53244d9509256defbc9f06f54d97a Mon Sep 17 00:00:00 2001 From: kerry Date: Tue, 15 Sep 2026 23:36:22 +0000 Subject: [PATCH] refactor(model_info): restore provider-neutral Gemini chat baseline Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ...odel_prices_and_context_window_backup.json | 4 +-- model_prices_and_context_window.json | 4 +-- .../test_fallback_generalizations.py | 26 +++++++++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9569f29aa0e..cc4c7feffad 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -58152,8 +58152,8 @@ }, { "name": "gemini-chat-baseline", - "pattern": "^(?:gemini|vertex_ai)/gemini-(?!.*(?:-tts|-image|-live|-audio|-embedding|-computer-use|-robotics|-transcribe|-translate))(?:2\\.[5-9]|[3-9](?:\\.\\d{1,2})?)-(?:pro|flash)(?:-lite)?(?![a-z])", - "description": "A first-party Gemini text-chat id at 2.5 or higher, as gemini/ or vertex_ai/ (get_model_info tries the provider-prefixed name first, so a bare id under those providers matches too): gemini-[.minor]-(pro|flash)[-lite] with any trailing preview, date or variant tag. Scoped to Google's own namespaces because resellers wrap the same models differently (one exposes them as mode responses without reasoning), so nothing below is certain there. The lookahead excludes the tts, image, live, audio, embedding, computer-use, robotics, transcribe and translate lines, which are different modes with different capabilities. 2.5 is the floor: every first-party text-chat Gemini from 2.5 onward in this map carries all of these fields, 1.5 and 2.0 do not and are fully mapped. Carries no token limits or pricing, so those stay on the standard unmapped behavior rather than a guessed number.", + "pattern": "gemini-(?!.*(?:-tts|-image|-live|-audio|-embedding|-computer-use|-robotics|-transcribe|-translate))(?:2[.-][5-9]|[3-9](?:[.-]\\d{1,2})?)-(?:pro|flash)(?:-lite)?(?![a-z])", + "description": "Any Gemini text-chat id at 2.5 or higher under any namespace, including bare ids, gemini/, vertex_ai/, openrouter/google/, deepinfra/google/, vercel_ai_gateway/google/, oci/google., and databricks-gemini--: gemini-[.minor]-(pro|flash)[-lite] with any trailing preview, date or variant tag. The capability flags were verified against each of those providers' own catalogs and docs. The lookahead excludes the tts, image, live, audio, embedding, computer-use, robotics, transcribe and translate lines, which are different modes with different capabilities. Provider-specific deviations, such as Perplexity's Agent API serving these as mode responses, are carried by their exact map entries, which always win over this rule. Carries no token limits or pricing, so those stay on the standard unmapped behavior rather than a guessed number.", "model_info": { "mode": "chat", "supports_reasoning": true, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9569f29aa0e..cc4c7feffad 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -58152,8 +58152,8 @@ }, { "name": "gemini-chat-baseline", - "pattern": "^(?:gemini|vertex_ai)/gemini-(?!.*(?:-tts|-image|-live|-audio|-embedding|-computer-use|-robotics|-transcribe|-translate))(?:2\\.[5-9]|[3-9](?:\\.\\d{1,2})?)-(?:pro|flash)(?:-lite)?(?![a-z])", - "description": "A first-party Gemini text-chat id at 2.5 or higher, as gemini/ or vertex_ai/ (get_model_info tries the provider-prefixed name first, so a bare id under those providers matches too): gemini-[.minor]-(pro|flash)[-lite] with any trailing preview, date or variant tag. Scoped to Google's own namespaces because resellers wrap the same models differently (one exposes them as mode responses without reasoning), so nothing below is certain there. The lookahead excludes the tts, image, live, audio, embedding, computer-use, robotics, transcribe and translate lines, which are different modes with different capabilities. 2.5 is the floor: every first-party text-chat Gemini from 2.5 onward in this map carries all of these fields, 1.5 and 2.0 do not and are fully mapped. Carries no token limits or pricing, so those stay on the standard unmapped behavior rather than a guessed number.", + "pattern": "gemini-(?!.*(?:-tts|-image|-live|-audio|-embedding|-computer-use|-robotics|-transcribe|-translate))(?:2[.-][5-9]|[3-9](?:[.-]\\d{1,2})?)-(?:pro|flash)(?:-lite)?(?![a-z])", + "description": "Any Gemini text-chat id at 2.5 or higher under any namespace, including bare ids, gemini/, vertex_ai/, openrouter/google/, deepinfra/google/, vercel_ai_gateway/google/, oci/google., and databricks-gemini--: gemini-[.minor]-(pro|flash)[-lite] with any trailing preview, date or variant tag. The capability flags were verified against each of those providers' own catalogs and docs. The lookahead excludes the tts, image, live, audio, embedding, computer-use, robotics, transcribe and translate lines, which are different modes with different capabilities. Provider-specific deviations, such as Perplexity's Agent API serving these as mode responses, are carried by their exact map entries, which always win over this rule. Carries no token limits or pricing, so those stay on the standard unmapped behavior rather than a guessed number.", "model_info": { "mode": "chat", "supports_reasoning": true, diff --git a/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py b/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py index a4fbd79187e..71e6e20b1a4 100644 --- a/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py +++ b/tests/test_litellm/litellm_core_utils/test_fallback_generalizations.py @@ -457,12 +457,19 @@ def shipped_cost_map(monkeypatch): ("gemini-3.9-flash-lite-preview-09-2026", "vertex_ai"), ("vertex_ai/gemini-4-pro", None), ("gemini-4-pro-preview-customtools", "gemini"), + ("google/gemini-4-pro", "openrouter"), + ("google/gemini-4-pro", "deepinfra"), + ("google/gemini-4-pro", "vercel_ai_gateway"), + ("google.gemini-4-pro", "oci"), + ("databricks-gemini-4-1-pro", "databricks"), ], ) -def test_shipped_gemini_chat_baseline_resolves_first_party_unmapped_ids(shipped_cost_map, model, provider): +def test_shipped_gemini_chat_baseline_resolves_unmapped_ids(shipped_cost_map, model, provider): assert model not in litellm.model_cost if provider == "gemini": assert f"gemini/{model}" not in litellm.model_cost + elif provider in {"openrouter", "deepinfra", "vercel_ai_gateway", "oci", "databricks"}: + assert f"{provider}/{model}" not in litellm.model_cost info = litellm.get_model_info(model, custom_llm_provider=provider) assert info["litellm_provider"] == (provider or model.split("/")[0]) @@ -481,16 +488,11 @@ def test_shipped_gemini_chat_baseline_resolves_first_party_unmapped_ids(shipped_ assert not info.get("output_cost_per_token") -def test_shipped_gemini_chat_baseline_skips_reseller_namespaces(shipped_cost_map): - for model, provider in ( - ("google/gemini-4-pro", "perplexity"), - ("google/gemini-4-pro", "openrouter"), - ("databricks-gemini-4-1-pro", "databricks"), - ): - with pytest.raises(Exception, match="isn't mapped yet"): - litellm.get_model_info(model, custom_llm_provider=provider) - - assert match_capability_generalizations("google/gemini-4-pro") is None +def test_shipped_gemini_chat_baseline_loses_to_perplexity_exact_entries(shipped_cost_map): + info = litellm.get_model_info("google/gemini-2.5-pro", custom_llm_provider="perplexity") + entry = litellm.model_cost["perplexity/google/gemini-2.5-pro"] + assert info["mode"] == "responses" + assert entry["supports_reasoning"] is False def test_shipped_gemini_chat_baseline_skips_non_chat_and_pre_2_5_ids(shipped_cost_map): @@ -506,6 +508,8 @@ def test_shipped_gemini_chat_baseline_skips_non_chat_and_pre_2_5_ids(shipped_cos "gemini/gemini-4-flashy", "gemini/gemini-4-flash-transcribe", "gemini/gemini-4-flash-live-translate-preview", + "databricks-gemini-3-1-flash-image", + "openrouter/google/gemini-2.0-flash-001", ): assert match_capability_generalizations(model) is None, model