feat: Update Claude model references from v3 to v4 (fixes issue #118) (#119)

* feat: Update Claude model references from v3 to v4

- Update model configurations from claude-3-opus to claude-4-opus
- Update model configurations from claude-3-sonnet to claude-4-sonnet
- Maintain backward compatibility through existing aliases (opus, sonnet, claude)
- Update provider registry preferred models list
- Update all test cases and assertions to reflect new model names
- Update documentation and examples consistently across all files
- Add Claude 4 model support while preserving existing functionality

Files modified: 15 (config, docs, providers, tests, tools)
Pattern: Systematic claude-3-* → claude-4-* model reference migration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* PR feedback: changed anthropic/claude-4-opus -> anthropic/claude-opus-4 and anthropic/claude-4-haiku -> anthropic/claude-3.5-haiku

* changed anthropic/claude-4-sonnet -> anthropic/claude-sonnet-4

* PR feedback removed specific model from test mock

* PR feedback removed base.py

---------

Co-authored-by: Omry Nachman <omry@wix.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
omryn-vera
2025-06-23 11:57:13 +02:00
committed by GitHub
parent 8262d47c1e
commit 4ae0344b14
13 changed files with 63 additions and 63 deletions

View File

@@ -527,7 +527,7 @@ class TestAutoModeComprehensive:
"google/gemini-2.5-pro",
"openai/o3",
"openai/o4-mini",
"anthropic/claude-3-opus",
"anthropic/claude-opus-4",
]
with patch.object(OpenRouterProvider, "_registry", mock_registry):

View File

@@ -53,8 +53,8 @@ class TestListModelsRestrictions(unittest.TestCase):
# Set up mock to return only allowed models when restrictions are respected
# Include both aliased models and full model names without aliases
self.mock_openrouter.list_models.return_value = [
"anthropic/claude-3-opus-20240229", # Has alias "opus"
"anthropic/claude-3-sonnet-20240229", # Has alias "sonnet"
"anthropic/claude-opus-4", # Has alias "opus"
"anthropic/claude-sonnet-4", # Has alias "sonnet"
"deepseek/deepseek-r1-0528:free", # No alias, full name
"qwen/qwen3-235b-a22b-04-28:free", # No alias, full name
]
@@ -67,12 +67,12 @@ class TestListModelsRestrictions(unittest.TestCase):
def resolve_side_effect(model_name):
if "opus" in model_name.lower():
config = MagicMock()
config.model_name = "anthropic/claude-3-opus-20240229"
config.model_name = "anthropic/claude-opus-4-20240229"
config.context_window = 200000
return config
elif "sonnet" in model_name.lower():
config = MagicMock()
config.model_name = "anthropic/claude-3-sonnet-20240229"
config.model_name = "anthropic/claude-sonnet-4-20240229"
config.context_window = 200000
return config
return None # No config for models without aliases
@@ -93,8 +93,8 @@ class TestListModelsRestrictions(unittest.TestCase):
mock_get_models.return_value = {
"gemini-2.5-flash": ProviderType.GOOGLE,
"gemini-2.5-pro": ProviderType.GOOGLE,
"anthropic/claude-3-opus-20240229": ProviderType.OPENROUTER,
"anthropic/claude-3-sonnet-20240229": ProviderType.OPENROUTER,
"anthropic/claude-opus-4-20240229": ProviderType.OPENROUTER,
"anthropic/claude-sonnet-4-20240229": ProviderType.OPENROUTER,
"deepseek/deepseek-r1-0528:free": ProviderType.OPENROUTER,
"qwen/qwen3-235b-a22b-04-28:free": ProviderType.OPENROUTER,
}
@@ -172,7 +172,7 @@ class TestListModelsRestrictions(unittest.TestCase):
utils.model_restrictions._restriction_service = None
# Set up mock to return many models when no restrictions
all_models = [f"provider{i//10}/model-{i}" for i in range(50)] # Simulate 50 models from different providers
all_models = [f"provider{i // 10}/model-{i}" for i in range(50)] # Simulate 50 models from different providers
self.mock_openrouter.list_models.return_value = all_models
# Mock registry instance

View File

@@ -24,7 +24,7 @@ class TestModelRestrictionService:
assert service.is_allowed(ProviderType.OPENAI, "o3-mini")
assert service.is_allowed(ProviderType.GOOGLE, "gemini-2.5-pro")
assert service.is_allowed(ProviderType.GOOGLE, "gemini-2.5-flash")
assert service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-3-opus")
assert service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-opus-4")
assert service.is_allowed(ProviderType.OPENROUTER, "openai/o3")
# Should have no restrictions
@@ -44,7 +44,7 @@ class TestModelRestrictionService:
# Google and OpenRouter should have no restrictions
assert service.is_allowed(ProviderType.GOOGLE, "gemini-2.5-pro")
assert service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-3-opus")
assert service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-opus-4")
def test_load_multiple_models_restriction(self):
"""Test loading multiple allowed models."""
@@ -159,7 +159,7 @@ class TestModelRestrictionService:
# Should only allow specified OpenRouter models
assert service.is_allowed(ProviderType.OPENROUTER, "opus")
assert service.is_allowed(ProviderType.OPENROUTER, "sonnet")
assert service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-3-opus", "opus") # With original name
assert service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-opus-4", "opus") # With original name
assert not service.is_allowed(ProviderType.OPENROUTER, "haiku")
assert not service.is_allowed(ProviderType.OPENROUTER, "anthropic/claude-3-haiku")
assert not service.is_allowed(ProviderType.OPENROUTER, "mistral-large")

View File

@@ -44,7 +44,7 @@ class TestOpenRouterProvider:
# Should accept any model - OpenRouter handles validation
assert provider.validate_model_name("gpt-4") is True
assert provider.validate_model_name("claude-3-opus") is True
assert provider.validate_model_name("claude-4-opus") is True
assert provider.validate_model_name("any-model-name") is True
assert provider.validate_model_name("GPT-4") is True
assert provider.validate_model_name("unknown-model") is True
@@ -71,26 +71,26 @@ class TestOpenRouterProvider:
provider = OpenRouterProvider(api_key="test-key")
# Test alias resolution
assert provider._resolve_model_name("opus") == "anthropic/claude-3-opus"
assert provider._resolve_model_name("sonnet") == "anthropic/claude-3-sonnet"
assert provider._resolve_model_name("opus") == "anthropic/claude-opus-4"
assert provider._resolve_model_name("sonnet") == "anthropic/claude-sonnet-4"
assert provider._resolve_model_name("o3") == "openai/o3"
assert provider._resolve_model_name("o3-mini") == "openai/o3-mini"
assert provider._resolve_model_name("o3mini") == "openai/o3-mini"
assert provider._resolve_model_name("o4-mini") == "openai/o4-mini"
assert provider._resolve_model_name("o4-mini-high") == "openai/o4-mini-high"
assert provider._resolve_model_name("claude") == "anthropic/claude-3-sonnet"
assert provider._resolve_model_name("claude") == "anthropic/claude-sonnet-4"
assert provider._resolve_model_name("mistral") == "mistralai/mistral-large-2411"
assert provider._resolve_model_name("deepseek") == "deepseek/deepseek-r1-0528"
assert provider._resolve_model_name("r1") == "deepseek/deepseek-r1-0528"
# Test case-insensitive
assert provider._resolve_model_name("OPUS") == "anthropic/claude-3-opus"
assert provider._resolve_model_name("OPUS") == "anthropic/claude-opus-4"
assert provider._resolve_model_name("O3") == "openai/o3"
assert provider._resolve_model_name("Mistral") == "mistralai/mistral-large-2411"
assert provider._resolve_model_name("CLAUDE") == "anthropic/claude-3-sonnet"
assert provider._resolve_model_name("CLAUDE") == "anthropic/claude-sonnet-4"
# Test direct model names (should pass through unchanged)
assert provider._resolve_model_name("anthropic/claude-3-opus") == "anthropic/claude-3-opus"
assert provider._resolve_model_name("anthropic/claude-opus-4") == "anthropic/claude-opus-4"
assert provider._resolve_model_name("openai/o3") == "openai/o3"
# Test unknown models pass through
@@ -155,8 +155,8 @@ class TestOpenRouterAutoMode:
"google/gemini-2.5-pro",
"openai/o3",
"openai/o3-mini",
"anthropic/claude-3-opus",
"anthropic/claude-3-sonnet",
"anthropic/claude-opus-4",
"anthropic/claude-sonnet-4",
]
ModelProviderRegistry.register_provider(ProviderType.OPENROUTER, OpenRouterProvider)
@@ -181,7 +181,7 @@ class TestOpenRouterAutoMode:
os.environ.pop("OPENAI_API_KEY", None)
os.environ["OPENROUTER_API_KEY"] = "test-openrouter-key"
os.environ.pop("OPENROUTER_ALLOWED_MODELS", None)
os.environ["OPENROUTER_ALLOWED_MODELS"] = "anthropic/claude-3-opus,google/gemini-2.5-flash"
os.environ["OPENROUTER_ALLOWED_MODELS"] = "anthropic/claude-opus-4,google/gemini-2.5-flash"
os.environ["DEFAULT_MODEL"] = "auto"
# Force reload to pick up new environment variable
@@ -193,8 +193,8 @@ class TestOpenRouterAutoMode:
mock_models = [
"google/gemini-2.5-flash",
"google/gemini-2.5-pro",
"anthropic/claude-3-opus",
"anthropic/claude-3-sonnet",
"anthropic/claude-opus-4",
"anthropic/claude-sonnet-4",
]
mock_registry.list_models.return_value = mock_models
@@ -212,7 +212,7 @@ class TestOpenRouterAutoMode:
assert len(available_models) > 0, "Should have some allowed models"
expected_allowed = {"google/gemini-2.5-flash", "anthropic/claude-3-opus"}
expected_allowed = {"google/gemini-2.5-flash", "anthropic/claude-opus-4"}
assert (
set(available_models.keys()) == expected_allowed
@@ -263,7 +263,7 @@ class TestOpenRouterRegistry:
# Should have loaded models
models = registry.list_models()
assert len(models) > 0
assert "anthropic/claude-3-opus" in models
assert "anthropic/claude-opus-4" in models
assert "openai/o3" in models
# Should have loaded aliases
@@ -282,13 +282,13 @@ class TestOpenRouterRegistry:
# Test known model
caps = registry.get_capabilities("opus")
assert caps is not None
assert caps.model_name == "anthropic/claude-3-opus"
assert caps.model_name == "anthropic/claude-opus-4"
assert caps.context_window == 200000 # Claude's context window
# Test using full model name
caps = registry.get_capabilities("anthropic/claude-3-opus")
caps = registry.get_capabilities("anthropic/claude-opus-4")
assert caps is not None
assert caps.model_name == "anthropic/claude-3-opus"
assert caps.model_name == "anthropic/claude-opus-4"
# Test unknown model
caps = registry.get_capabilities("non-existent-model")
@@ -301,11 +301,11 @@ class TestOpenRouterRegistry:
registry = OpenRouterModelRegistry()
# All these should resolve to Claude Sonnet
sonnet_aliases = ["sonnet", "claude", "claude-sonnet", "claude3-sonnet"]
sonnet_aliases = ["sonnet", "claude", "claude-sonnet", "claude4-sonnet"]
for alias in sonnet_aliases:
config = registry.resolve(alias)
assert config is not None
assert config.model_name == "anthropic/claude-3-sonnet"
assert config.model_name == "anthropic/claude-sonnet-4"
class TestOpenRouterFunctionality:

View File

@@ -74,9 +74,9 @@ class TestOpenRouterModelRegistry:
# Test various aliases
test_cases = [
("opus", "anthropic/claude-3-opus"),
("OPUS", "anthropic/claude-3-opus"), # Case insensitive
("claude", "anthropic/claude-3-sonnet"),
("opus", "anthropic/claude-opus-4"),
("OPUS", "anthropic/claude-opus-4"), # Case insensitive
("claude", "anthropic/claude-sonnet-4"),
("o3", "openai/o3"),
("deepseek", "deepseek/deepseek-r1-0528"),
("mistral", "mistralai/mistral-large-2411"),
@@ -92,9 +92,9 @@ class TestOpenRouterModelRegistry:
registry = OpenRouterModelRegistry()
# Should be able to look up by full model name
config = registry.resolve("anthropic/claude-3-opus")
config = registry.resolve("anthropic/claude-opus-4")
assert config is not None
assert config.model_name == "anthropic/claude-3-opus"
assert config.model_name == "anthropic/claude-opus-4"
config = registry.resolve("openai/o3")
assert config is not None
@@ -118,7 +118,7 @@ class TestOpenRouterModelRegistry:
caps = config.to_capabilities()
assert caps.provider == ProviderType.OPENROUTER
assert caps.model_name == "anthropic/claude-3-opus"
assert caps.model_name == "anthropic/claude-opus-4"
assert caps.friendly_name == "OpenRouter"
assert caps.context_window == 200000
assert not caps.supports_extended_thinking

View File

@@ -288,11 +288,11 @@ class TestProviderHelperMethods:
with patch.object(ModelProviderRegistry, "get_provider") as mock_get_provider:
# Mock openrouter provider
mock_openrouter = MagicMock()
mock_openrouter.validate_model_name.side_effect = lambda m: m == "anthropic/claude-3.5-sonnet"
mock_openrouter.validate_model_name.side_effect = lambda m: m == "anthropic/claude-sonnet-4"
mock_get_provider.side_effect = lambda ptype: mock_openrouter if ptype == ProviderType.OPENROUTER else None
model = ModelProviderRegistry._find_extended_thinking_model()
assert model == "anthropic/claude-3.5-sonnet"
assert model == "anthropic/claude-sonnet-4"
def test_find_extended_thinking_model_none_found(self):
"""Test when no thinking model is found."""

View File

@@ -318,7 +318,7 @@ class TestOpenRouterAliasRestrictions:
os.environ.pop("OPENAI_API_KEY", None)
os.environ.pop("XAI_API_KEY", None)
os.environ["OPENROUTER_API_KEY"] = "test-key"
os.environ["OPENROUTER_ALLOWED_MODELS"] = "o3-mini,anthropic/claude-3-opus,flash"
os.environ["OPENROUTER_ALLOWED_MODELS"] = "o3-mini,anthropic/claude-opus-4,flash"
# Register OpenRouter provider
from providers.openrouter import OpenRouterProvider
@@ -330,7 +330,7 @@ class TestOpenRouterAliasRestrictions:
expected_models = {
"openai/o3-mini", # from alias
"anthropic/claude-3-opus", # full name
"anthropic/claude-opus-4", # full name
"google/gemini-2.5-flash", # from alias
}