From 8a31571bc1a57bbed79883f677f973fa0bd34a5b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Fri, 29 May 2026 00:05:39 +0000 Subject: [PATCH] feat: route future Claude models to Anthropic provider via pattern matching Add pattern-based matching for Claude model names so that future models (e.g., claude-opus-4-9, claude-sonnet-5-0) are automatically routed to the Anthropic provider without requiring model_prices_and_context_window.json updates. The pattern matches: claude-{opus|sonnet|haiku}-{major}-{minor}[-YYYYMMDD] https://claude.ai/code/session_017asCVDN5jBFMBcZRjiQR6C --- .../get_llm_provider_logic.py | 26 ++++++ tests/local_testing/test_get_llm_provider.py | 92 +++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index ba6d438f16c..af4c080336c 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -71,6 +71,29 @@ def _is_azure_claude_model(model: str) -> bool: return False +import re + +_CLAUDE_PATTERN = re.compile( + r"^claude-(?:opus|sonnet|haiku)-\d+-\d+(?:-\d{8})?$", re.IGNORECASE +) + + +def _matches_claude_model_pattern(model: str) -> bool: + """ + Check if a model string matches the Claude model naming pattern. + + Matches patterns like: + - claude-opus-4-7 + - claude-sonnet-4-6 + - claude-haiku-4-5 + - claude-opus-5-1-20270101 (with optional date suffix) + + This allows future Claude models to be routed to the Anthropic provider + without requiring updates to model_prices_and_context_window.json. + """ + return _CLAUDE_PATTERN.match(model) is not None + + def handle_cohere_chat_model_custom_llm_provider( model: str, custom_llm_provider: Optional[str] = None ) -> Tuple[str, Optional[str]]: @@ -398,6 +421,9 @@ def get_llm_provider( # noqa: PLR0915 custom_llm_provider = "anthropic_text" else: custom_llm_provider = "anthropic" + ## anthropic - pattern-based matching for future Claude models + elif _matches_claude_model_pattern(model): + custom_llm_provider = "anthropic" ## cohere elif model in litellm.cohere_models or model in litellm.cohere_embedding_models: custom_llm_provider = "cohere" diff --git a/tests/local_testing/test_get_llm_provider.py b/tests/local_testing/test_get_llm_provider.py index 14b9e8cd136..b802e5b0de6 100644 --- a/tests/local_testing/test_get_llm_provider.py +++ b/tests/local_testing/test_get_llm_provider.py @@ -478,3 +478,95 @@ def test_get_llm_provider_use_proxy_arg_true_with_direct_args(): assert key == arg_api_key # Should use the argument key assert base == arg_api_base # Should use the argument base + +# -------- Tests for Claude model pattern matching --------- + + +class TestClaudeModelPatternMatching: + """ + Tests for _matches_claude_model_pattern which routes future Claude models + to the Anthropic provider without requiring model_prices_and_context_window.json updates. + """ + + def test_matches_claude_opus_pattern(self): + """Test claude-opus-X-Y pattern matching.""" + from litellm.litellm_core_utils.get_llm_provider_logic import ( + _matches_claude_model_pattern, + ) + + assert _matches_claude_model_pattern("claude-opus-4-7") is True + assert _matches_claude_model_pattern("claude-opus-4-9") is True + assert _matches_claude_model_pattern("claude-opus-5-1") is True + + def test_matches_claude_sonnet_pattern(self): + """Test claude-sonnet-X-Y pattern matching.""" + from litellm.litellm_core_utils.get_llm_provider_logic import ( + _matches_claude_model_pattern, + ) + + assert _matches_claude_model_pattern("claude-sonnet-4-6") is True + assert _matches_claude_model_pattern("claude-sonnet-5-0") is True + + def test_matches_claude_haiku_pattern(self): + """Test claude-haiku-X-Y pattern matching.""" + from litellm.litellm_core_utils.get_llm_provider_logic import ( + _matches_claude_model_pattern, + ) + + assert _matches_claude_model_pattern("claude-haiku-4-5") is True + assert _matches_claude_model_pattern("claude-haiku-5-0") is True + + def test_matches_claude_with_date_suffix(self): + """Test claude model pattern with date suffix.""" + from litellm.litellm_core_utils.get_llm_provider_logic import ( + _matches_claude_model_pattern, + ) + + assert _matches_claude_model_pattern("claude-opus-5-1-20270101") is True + assert _matches_claude_model_pattern("claude-sonnet-4-7-20260601") is True + assert _matches_claude_model_pattern("claude-haiku-4-6-20251201") is True + + def test_rejects_non_claude_models(self): + """Test that non-Claude models are not matched.""" + from litellm.litellm_core_utils.get_llm_provider_logic import ( + _matches_claude_model_pattern, + ) + + assert _matches_claude_model_pattern("gpt-4") is False + assert _matches_claude_model_pattern("mistral-large") is False + assert _matches_claude_model_pattern("llama-3") is False + + def test_rejects_invalid_claude_patterns(self): + """Test that invalid Claude model patterns are not matched.""" + from litellm.litellm_core_utils.get_llm_provider_logic import ( + _matches_claude_model_pattern, + ) + + # Wrong order (variant before name) + assert _matches_claude_model_pattern("claude-4-opus") is False + # Missing version numbers + assert _matches_claude_model_pattern("claude-opus") is False + # Old format (claude-3-opus instead of claude-opus-3) + assert _matches_claude_model_pattern("claude-3-opus-20240229") is False + # Invalid variant + assert _matches_claude_model_pattern("claude-mini-4-5") is False + + def test_get_llm_provider_future_claude_model(self): + """Test that get_llm_provider routes future Claude models to anthropic.""" + model, custom_llm_provider, dynamic_api_key, api_base = ( + litellm.get_llm_provider( + model="claude-opus-4-9", + ) + ) + assert custom_llm_provider == "anthropic" + assert model == "claude-opus-4-9" + + def test_get_llm_provider_future_claude_model_with_date(self): + """Test that get_llm_provider routes future Claude models with date suffix.""" + model, custom_llm_provider, dynamic_api_key, api_base = ( + litellm.get_llm_provider( + model="claude-opus-5-1-20270101", + ) + ) + assert custom_llm_provider == "anthropic" + assert model == "claude-opus-5-1-20270101"