diff --git a/conf/custom_models.json b/conf/custom_models.json index 2b9f7c7..2a3bcf3 100644 --- a/conf/custom_models.json +++ b/conf/custom_models.json @@ -7,7 +7,7 @@ "Self-hosted APIs - Any OpenAI-compatible endpoint" ], "documentation": "https://github.com/BeehiveInnovations/zen-mcp-server/blob/main/docs/custom_models.md", - "usage": "Models can be accessed via aliases (e.g., 'opus', 'local-llama') or full names (e.g., 'anthropic/claude-3-opus', 'llama3.2')", + "usage": "Models can be accessed via aliases (e.g., 'opus', 'local-llama') or full names (e.g., 'anthropic/claude-opus-4', 'llama3.2')", "instructions": [ "Add new models by copying an existing entry and modifying it", "Aliases are case-insensitive and should be unique across all models", @@ -15,11 +15,11 @@ "Set supports_* flags based on the model's actual capabilities", "Set is_custom=true for models that should ONLY work with custom endpoints (Ollama, vLLM, etc.)", "Models not listed here will use generic defaults (32K context window, basic features)", - "For OpenRouter models: Use official OpenRouter model names (e.g., 'anthropic/claude-3-opus')", + "For OpenRouter models: Use official OpenRouter model names (e.g., 'anthropic/claude-opus-4')", "For local/custom models: Use model names as they appear in your API (e.g., 'llama3.2', 'gpt-3.5-turbo')" ], "field_descriptions": { - "model_name": "The model identifier - OpenRouter format (e.g., 'anthropic/claude-3-opus') or custom model name (e.g., 'llama3.2')", + "model_name": "The model identifier - OpenRouter format (e.g., 'anthropic/claude-opus-4') or custom model name (e.g., 'llama3.2')", "aliases": "Array of short names users can type instead of the full model name", "context_window": "Total number of tokens the model can process (input + output combined)", "supports_extended_thinking": "Whether the model supports extended reasoning tokens (currently none do via OpenRouter or custom APIs)", @@ -49,29 +49,29 @@ }, "models": [ { - "model_name": "anthropic/claude-3-opus", - "aliases": ["opus", "claude-opus", "claude3-opus", "claude-3-opus"], + "model_name": "anthropic/claude-opus-4", + "aliases": ["opus", "claude-opus", "claude4-opus", "claude-4-opus"], "context_window": 200000, "supports_extended_thinking": false, "supports_json_mode": false, "supports_function_calling": false, "supports_images": true, "max_image_size_mb": 5.0, - "description": "Claude 3 Opus - Most capable Claude model with vision" + "description": "Claude 4 Opus - Most capable Claude model with vision" }, { - "model_name": "anthropic/claude-3-sonnet", - "aliases": ["sonnet", "claude-sonnet", "claude3-sonnet", "claude-3-sonnet", "claude"], + "model_name": "anthropic/claude-sonnet-4", + "aliases": ["sonnet", "claude-sonnet", "claude4-sonnet", "claude-4-sonnet", "claude"], "context_window": 200000, "supports_extended_thinking": false, "supports_json_mode": false, "supports_function_calling": false, "supports_images": true, "max_image_size_mb": 5.0, - "description": "Claude 3 Sonnet - Balanced performance with vision" + "description": "Claude 4 Sonnet - Balanced performance with vision" }, { - "model_name": "anthropic/claude-3-haiku", + "model_name": "anthropic/claude-3.5-haiku", "aliases": ["haiku", "claude-haiku", "claude3-haiku", "claude-3-haiku"], "context_window": 200000, "supports_extended_thinking": false, diff --git a/docs/adding_providers.md b/docs/adding_providers.md index f3f3a4e..c93574f 100644 --- a/docs/adding_providers.md +++ b/docs/adding_providers.md @@ -690,7 +690,7 @@ When a user requests a model (e.g., "pro", "o3", "example-large-v1"), the system 2. OpenAI skips (Gemini already handled it) 3. OpenRouter never sees it -### Example: Model "claude-3-opus" +### Example: Model "claude-4-opus" 1. **Gemini provider** checks: NO, not my model → skip 2. **OpenAI provider** checks: NO, not my model → skip diff --git a/docs/custom_models.md b/docs/custom_models.md index 8094675..45f6967 100644 --- a/docs/custom_models.md +++ b/docs/custom_models.md @@ -41,9 +41,9 @@ The server uses `conf/custom_models.json` to map convenient aliases to both Open | Alias | Maps to OpenRouter Model | |-------|-------------------------| -| `opus` | `anthropic/claude-3-opus` | -| `sonnet`, `claude` | `anthropic/claude-3-sonnet` | -| `haiku` | `anthropic/claude-3-haiku` | +| `opus` | `anthropic/claude-opus-4` | +| `sonnet`, `claude` | `anthropic/claude-sonnet-4` | +| `haiku` | `anthropic/claude-3.5-haiku` | | `gpt4o`, `4o` | `openai/gpt-4o` | | `gpt4o-mini`, `4o-mini` | `openai/gpt-4o-mini` | | `pro`, `gemini` | `google/gemini-2.5-pro` | @@ -151,8 +151,8 @@ CUSTOM_MODEL_NAME=your-loaded-model **Using model aliases (from conf/custom_models.json):** ``` # OpenRouter models: -"Use opus for deep analysis" # → anthropic/claude-3-opus -"Use sonnet to review this code" # → anthropic/claude-3-sonnet +"Use opus for deep analysis" # → anthropic/claude-opus-4 +"Use sonnet to review this code" # → anthropic/claude-sonnet-4 "Use pro via zen to analyze this" # → google/gemini-2.5-pro "Use gpt4o via zen to analyze this" # → openai/gpt-4o "Use mistral via zen to optimize" # → mistral/mistral-large @@ -165,7 +165,7 @@ CUSTOM_MODEL_NAME=your-loaded-model **Using full model names:** ``` # OpenRouter models: -"Use anthropic/claude-3-opus via zen for deep analysis" +"Use anthropic/claude-opus-4 via zen for deep analysis" "Use openai/gpt-4o via zen to debug this" "Use deepseek/deepseek-coder via zen to generate code" @@ -249,7 +249,7 @@ Edit `conf/custom_models.json` to add new models. The configuration supports bot Popular models available through OpenRouter: - **GPT-4** - OpenAI's most capable model -- **Claude 3** - Anthropic's models (Opus, Sonnet, Haiku) +- **Claude 4** - Anthropic's models (Opus, Sonnet, Haiku) - **Mistral** - Including Mistral Large - **Llama 3** - Meta's open models - Many more at [openrouter.ai/models](https://openrouter.ai/models) @@ -258,4 +258,4 @@ Popular models available through OpenRouter: - **"Model not found"**: Check exact model name at openrouter.ai/models - **"Insufficient credits"**: Add credits to your OpenRouter account -- **"Model not available"**: Check your OpenRouter dashboard for model access permissions \ No newline at end of file +- **"Model not available"**: Check your OpenRouter dashboard for model access permissions diff --git a/providers/registry.py b/providers/registry.py index 8fa0478..a5efcf0 100644 --- a/providers/registry.py +++ b/providers/registry.py @@ -402,8 +402,8 @@ class ModelProviderRegistry: if openrouter_provider: # Prefer models known for deep reasoning preferred_models = [ - "anthropic/claude-3.5-sonnet", - "anthropic/claude-3-opus-20240229", + "anthropic/claude-sonnet-4", + "anthropic/claude-opus-4", "google/gemini-2.5-pro", "google/gemini-pro-1.5", "meta-llama/llama-3.1-70b-instruct", diff --git a/simulator_tests/test_openrouter_models.py b/simulator_tests/test_openrouter_models.py index 5a52efd..bd69806 100644 --- a/simulator_tests/test_openrouter_models.py +++ b/simulator_tests/test_openrouter_models.py @@ -117,7 +117,7 @@ class OpenRouterModelsTest(BaseSimulatorTest): self.logger.info(" ✅ Direct OpenRouter model call completed") # Test 5: OpenRouter alias from config - self.logger.info(" 5: Testing OpenRouter alias from config ('opus' -> anthropic/claude-3-opus)") + self.logger.info(" 5: Testing OpenRouter alias from config ('opus' -> anthropic/claude-opus-4)") response5, _ = self.call_mcp_tool( "chat", diff --git a/tests/test_auto_mode_comprehensive.py b/tests/test_auto_mode_comprehensive.py index d7e00ae..8539fdf 100644 --- a/tests/test_auto_mode_comprehensive.py +++ b/tests/test_auto_mode_comprehensive.py @@ -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): diff --git a/tests/test_listmodels_restrictions.py b/tests/test_listmodels_restrictions.py index 8d26902..5d9f06d 100644 --- a/tests/test_listmodels_restrictions.py +++ b/tests/test_listmodels_restrictions.py @@ -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 diff --git a/tests/test_model_restrictions.py b/tests/test_model_restrictions.py index b4b0e66..bd34a81 100644 --- a/tests/test_model_restrictions.py +++ b/tests/test_model_restrictions.py @@ -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") diff --git a/tests/test_openrouter_provider.py b/tests/test_openrouter_provider.py index 0dd2b78..da10678 100644 --- a/tests/test_openrouter_provider.py +++ b/tests/test_openrouter_provider.py @@ -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: diff --git a/tests/test_openrouter_registry.py b/tests/test_openrouter_registry.py index 1a5a2f1..4b8bbbf 100644 --- a/tests/test_openrouter_registry.py +++ b/tests/test_openrouter_registry.py @@ -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 diff --git a/tests/test_per_tool_model_defaults.py b/tests/test_per_tool_model_defaults.py index 92c904c..f2b9b5e 100644 --- a/tests/test_per_tool_model_defaults.py +++ b/tests/test_per_tool_model_defaults.py @@ -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.""" diff --git a/tests/test_provider_routing_bugs.py b/tests/test_provider_routing_bugs.py index 2ceda5a..9ed125b 100644 --- a/tests/test_provider_routing_bugs.py +++ b/tests/test_provider_routing_bugs.py @@ -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 } diff --git a/tools/shared/base_tool.py b/tools/shared/base_tool.py index 7bff37f..a98baf8 100644 --- a/tools/shared/base_tool.py +++ b/tools/shared/base_tool.py @@ -448,7 +448,7 @@ class BaseTool(ABC): except Exception: description += ( " OpenRouter: Any model available on openrouter.ai " - "(e.g., 'gpt-4', 'claude-3-opus', 'mistral-large')." + "(e.g., 'gpt-4', 'claude-4-opus', 'mistral-large')." ) description += f" Defaults to '{DEFAULT_MODEL}' if not specified."