Compare commits

...

2 Commits

Author SHA1 Message Date
Devin AI
8b96ddefe3 Fix ruff formatting
Co-Authored-By: João <joao@crewai.com>
2026-05-21 17:13:40 +00:00
Devin AI
2c547f5f99 Fix #5893: Relax model naming prefix filtering for custom-deployed models
- Update _matches_provider_pattern for anthropic to use 'in' (contains)
  check instead of strict startswith for 'claude' keyword, and accept
  any model starting with 'anthropic' regardless of separator character
- Add pattern-matching fallback to _infer_provider_from_model so custom
  model names without provider prefix are correctly routed
- Update _is_anthropic_model to also detect 'anthropic.' substring
- Update ANTHROPIC_PREFIXES constant for consistency
- Add regression tests for all affected methods

Co-Authored-By: João <joao@crewai.com>
2026-05-21 17:10:49 +00:00
2 changed files with 97 additions and 6 deletions

View File

@@ -111,7 +111,12 @@ if LITELLM_AVAILABLE:
MIN_CONTEXT: Final[int] = 1024
MAX_CONTEXT: Final[int] = 2097152 # Current max from gemini-1.5-pro
ANTHROPIC_PREFIXES: Final[tuple[str, str, str]] = ("anthropic/", "claude-", "claude/")
ANTHROPIC_PREFIXES: Final[tuple[str, ...]] = (
"anthropic/",
"anthropic.",
"claude-",
"claude/",
)
LLM_CONTEXT_WINDOW_SIZES: Final[dict[str, int]] = {
# openai
@@ -465,9 +470,7 @@ class LLM(BaseLLM):
)
if provider == "anthropic" or provider == "claude":
return any(
model_lower.startswith(prefix) for prefix in ["claude-", "anthropic."]
)
return "claude" in model_lower or model_lower.startswith("anthropic")
if provider == "gemini" or provider == "google":
return any(
@@ -574,6 +577,19 @@ class LLM(BaseLLM):
if model in AZURE_MODELS:
return "azure"
# Fallback to pattern matching for models not in constants
provider_order = [
"bedrock",
"openai",
"anthropic",
"gemini",
"deepseek",
"dashscope",
]
for provider in provider_order:
if cls._matches_provider_pattern(model, provider):
return provider
return "openai"
@classmethod
@@ -653,8 +669,8 @@ class LLM(BaseLLM):
Returns:
bool: True if the model is from Anthropic, False otherwise.
"""
anthropic_prefixes = ("anthropic/", "claude-", "claude/")
return any(prefix in model.lower() for prefix in anthropic_prefixes)
anthropic_indicators = ("anthropic/", "anthropic.", "claude-", "claude/")
return any(indicator in model.lower() for indicator in anthropic_indicators)
def _prepare_completion_params(
self,

View File

@@ -996,6 +996,81 @@ def test_validate_model_in_constants():
is True
)
def test_matches_provider_pattern_custom_anthropic_names():
"""Test that _matches_provider_pattern handles custom-deployed Anthropic model names.
Regression test for https://github.com/crewAIInc/crewAI/issues/5893
Users with custom-deployed models (e.g. 'anthropic--claude-...') were filtered
out because pattern matching only supported ["claude-", "anthropic."] prefixes.
"""
# Standard patterns still work
assert LLM._matches_provider_pattern("claude-3-5-sonnet", "anthropic") is True
assert LLM._matches_provider_pattern("anthropic.claude-v2", "anthropic") is True
# Custom-deployed model names with non-standard separators (issue #5893)
assert LLM._matches_provider_pattern("anthropic--claude-3-5-sonnet", "anthropic") is True
assert LLM._matches_provider_pattern("anthropic--claude-opus-4", "anthropic") is True
# Models containing "claude" anywhere in the name
assert LLM._matches_provider_pattern("my-claude-model", "anthropic") is True
assert LLM._matches_provider_pattern("custom-claude-3-haiku", "anthropic") is True
# Models starting with "anthropic" regardless of separator
assert LLM._matches_provider_pattern("anthropic_custom_model", "anthropic") is True
assert LLM._matches_provider_pattern("anthropic-custom", "anthropic") is True
# Unrelated models should not match
assert LLM._matches_provider_pattern("gpt-4o", "anthropic") is False
assert LLM._matches_provider_pattern("gemini-2.0-flash", "anthropic") is False
assert LLM._matches_provider_pattern("llama-3-70b", "anthropic") is False
def test_validate_model_in_constants_custom_anthropic_names():
"""Test that _validate_model_in_constants accepts custom Anthropic model names.
Regression test for https://github.com/crewAIInc/crewAI/issues/5893
"""
# Custom naming conventions should be accepted via pattern matching fallback
assert LLM._validate_model_in_constants("anthropic--claude-3-5-sonnet", "anthropic") is True
assert LLM._validate_model_in_constants("anthropic--claude-3-5-sonnet", "claude") is True
assert LLM._validate_model_in_constants("custom-claude-model", "anthropic") is True
def test_infer_provider_custom_anthropic_names():
"""Test that _infer_provider_from_model infers anthropic for custom model names.
Regression test for https://github.com/crewAIInc/crewAI/issues/5893
Models like 'anthropic--claude-...' should be inferred as anthropic, not openai.
"""
assert LLM._infer_provider_from_model("anthropic--claude-3-5-sonnet") == "anthropic"
assert LLM._infer_provider_from_model("claude-3-5-sonnet") == "anthropic"
# Standard models should still be correctly inferred
assert LLM._infer_provider_from_model("gpt-4o") == "openai"
assert LLM._infer_provider_from_model("gemini-2.0-flash") == "gemini"
def test_is_anthropic_model_custom_names():
"""Test that _is_anthropic_model detects custom-deployed Anthropic models.
Regression test for https://github.com/crewAIInc/crewAI/issues/5893
"""
# Standard patterns
assert LLM._is_anthropic_model("anthropic/claude-3-5-sonnet") is True
assert LLM._is_anthropic_model("claude-3-5-sonnet") is True
# Custom naming with "claude-" substring
assert LLM._is_anthropic_model("anthropic--claude-3-5-sonnet") is True
# Bedrock-style naming with "anthropic."
assert LLM._is_anthropic_model("anthropic.claude-v2") is True
# Non-anthropic models
assert LLM._is_anthropic_model("gpt-4o") is False
assert LLM._is_anthropic_model("gemini-2.0-flash") is False
@pytest.mark.vcr(record_mode="once",decode_compressed_response=True)
def test_usage_info_non_streaming_with_call():
llm = LLM(model="gpt-4o-mini", is_litellm=True)