chore: add models (#30)

Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
This commit is contained in:
Ziheng Zhang
2026-03-17 01:40:03 +08:00
committed by GitHub
parent 6408ccb6db
commit 4b648b3413
5 changed files with 135 additions and 2 deletions
+2
View File
@@ -10,6 +10,8 @@ NVIDIA_API_KEY= # build.nvidia.com
SILICONFLOW_API_KEY= # siliconflow.cn
OPENROUTER_API_KEY= # openrouter.ai
ZHIPU_API_KEY= # open.bigmodel.cn
VOLCENGINE_API_KEY= # volcengine.com (火山引擎)
DASHSCOPE_API_KEY= # dashscope.aliyuncs.com (阿里云)
CUSTOM_OPENAI_API_KEY= # Third-party OpenAI-compatible endpoint
CUSTOM_OPENAI_BASE_URL= # OpenAI-compatible base URL (optional)
CUSTOM_ANTHROPIC_API_KEY= # Third-party Anthropic-compatible endpoint
+80
View File
@@ -365,6 +365,66 @@ def validate_zhipu_key(api_key: str) -> tuple[bool, str]:
return False, f"Error: {e}"
def validate_volcengine_key(api_key: str) -> tuple[bool, str]:
"""Validate a Volcengine API key by making a test request.
Returns:
Tuple of (is_valid, message).
"""
if not api_key:
return True, "Skipped (no key provided)"
try:
import openai
client = openai.OpenAI(
api_key=api_key,
base_url="https://ark.cn-beijing.volces.com/api/v3",
)
client.models.list()
return True, "Valid"
except Exception as e:
error_str = str(e).lower()
if (
"401" in error_str
or "unauthorized" in error_str
or "invalid" in error_str
or "authentication" in error_str
):
return False, "Invalid API key"
return False, f"Error: {e}"
def validate_dashscope_key(api_key: str) -> tuple[bool, str]:
"""Validate a DashScope API key by making a test request.
Returns:
Tuple of (is_valid, message).
"""
if not api_key:
return True, "Skipped (no key provided)"
try:
import openai
client = openai.OpenAI(
api_key=api_key,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
client.models.list()
return True, "Valid"
except Exception as e:
error_str = str(e).lower()
if (
"401" in error_str
or "unauthorized" in error_str
or "invalid" in error_str
or "authentication" in error_str
):
return False, "Invalid API key"
return False, f"Error: {e}"
def validate_tavily_key(api_key: str) -> tuple[bool, str]:
"""Validate a Tavily API key by making a test request.
@@ -531,6 +591,14 @@ def _step_provider(config: EvoScientistConfig) -> str:
title="ZhipuAI CodePlan (智谱代码计划 — GLM models for coding)",
value="zhipu-code",
),
Choice(
title="Volcengine (火山引擎 — Doubao models)",
value="volcengine",
),
Choice(
title="DashScope (阿里云 — Qwen models)",
value="dashscope",
),
Choice(title="Ollama (local models)", value="ollama"),
Choice(
title="OpenAI-compatible (third-party OpenAI endpoint)",
@@ -599,6 +667,16 @@ def _provider_key_info(config: EvoScientistConfig, provider: str):
config.zhipu_api_key or os.environ.get("ZHIPU_API_KEY", ""),
validate_zhipu_key,
),
"volcengine": (
"Volcengine",
config.volcengine_api_key or os.environ.get("VOLCENGINE_API_KEY", ""),
validate_volcengine_key,
),
"dashscope": (
"DashScope",
config.dashscope_api_key or os.environ.get("DASHSCOPE_API_KEY", ""),
validate_dashscope_key,
),
"custom-openai": (
"OpenAI-compatible",
config.custom_openai_api_key or os.environ.get("CUSTOM_OPENAI_API_KEY", ""),
@@ -2286,6 +2364,8 @@ def run_onboard(skip_validation: bool = False) -> bool:
"openrouter": "openrouter_api_key",
"zhipu": "zhipu_api_key",
"zhipu-code": "zhipu_api_key",
"volcengine": "volcengine_api_key",
"dashscope": "dashscope_api_key",
"custom-openai": "custom_openai_api_key",
"custom-anthropic": "custom_anthropic_api_key",
}
+8
View File
@@ -69,6 +69,8 @@ class EvoScientistConfig:
siliconflow_api_key: str = ""
openrouter_api_key: str = ""
zhipu_api_key: str = ""
volcengine_api_key: str = ""
dashscope_api_key: str = ""
custom_openai_api_key: str = ""
custom_openai_base_url: str = ""
custom_anthropic_api_key: str = ""
@@ -343,6 +345,8 @@ _ENV_MAPPINGS = {
"siliconflow_api_key": "SILICONFLOW_API_KEY",
"openrouter_api_key": "OPENROUTER_API_KEY",
"zhipu_api_key": "ZHIPU_API_KEY",
"volcengine_api_key": "VOLCENGINE_API_KEY",
"dashscope_api_key": "DASHSCOPE_API_KEY",
"custom_openai_api_key": "CUSTOM_OPENAI_API_KEY",
"custom_openai_base_url": "CUSTOM_OPENAI_BASE_URL",
"custom_anthropic_api_key": "CUSTOM_ANTHROPIC_API_KEY",
@@ -422,6 +426,10 @@ def apply_config_to_env(config: EvoScientistConfig) -> None:
os.environ["OPENROUTER_API_KEY"] = config.openrouter_api_key
if config.zhipu_api_key and not os.environ.get("ZHIPU_API_KEY"):
os.environ["ZHIPU_API_KEY"] = config.zhipu_api_key
if config.volcengine_api_key and not os.environ.get("VOLCENGINE_API_KEY"):
os.environ["VOLCENGINE_API_KEY"] = config.volcengine_api_key
if config.dashscope_api_key and not os.environ.get("DASHSCOPE_API_KEY"):
os.environ["DASHSCOPE_API_KEY"] = config.dashscope_api_key
if config.custom_openai_api_key and not os.environ.get("CUSTOM_OPENAI_API_KEY"):
os.environ["CUSTOM_OPENAI_API_KEY"] = config.custom_openai_api_key
if config.custom_openai_base_url and not os.environ.get("CUSTOM_OPENAI_BASE_URL"):
+15 -2
View File
@@ -2,8 +2,8 @@
This module provides a unified interface for creating chat model instances
with support for multiple providers (Anthropic, OpenAI, Google GenAI, NVIDIA,
SiliconFlow, OpenRouter, ZhipuAI, Ollama, and custom OpenAI-compatible endpoints) and
convenient short names for common models.
SiliconFlow, OpenRouter, ZhipuAI, Volcengine, DashScope, Ollama, and custom
OpenAI-compatible endpoints) and convenient short names for common models.
"""
from __future__ import annotations
@@ -67,6 +67,8 @@ _SILICONFLOW_BASE_URL = "https://api.siliconflow.cn/v1"
_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
_ZHIPU_BASE_URL = "https://open.bigmodel.cn/api/paas/v4"
_ZHIPU_CODE_BASE_URL = "https://open.bigmodel.cn/api/coding/paas/v4"
_VOLCENGINE_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3"
_DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
# Third-party providers routed through the OpenAI provider with a custom base_url.
# Maps provider name → (base_url or None, env var for API key).
@@ -75,6 +77,8 @@ _THIRD_PARTY_PROVIDERS: dict[str, tuple[str | None, str]] = {
"openrouter": (_OPENROUTER_BASE_URL, "OPENROUTER_API_KEY"),
"zhipu": (_ZHIPU_BASE_URL, "ZHIPU_API_KEY"),
"zhipu-code": (_ZHIPU_CODE_BASE_URL, "ZHIPU_API_KEY"),
"volcengine": (_VOLCENGINE_BASE_URL, "VOLCENGINE_API_KEY"),
"dashscope": (_DASHSCOPE_BASE_URL, "DASHSCOPE_API_KEY"),
"custom-openai": (
None,
"CUSTOM_OPENAI_API_KEY",
@@ -154,6 +158,15 @@ _MODEL_ENTRIES: list[tuple[str, str, str]] = [
("glm-5", "glm-5", "zhipu"),
("glm-5-turbo", "glm-5-turbo", "zhipu"),
("glm-4.7", "glm-4.7", "zhipu"),
# Volcengine (火山引擎 — Doubao models)
("doubao-seed-1.6", "doubao-seed-1.6", "volcengine"),
("doubao-1.5-pro", "doubao-1.5-pro-256k", "volcengine"),
("doubao-1.5-thinking-pro", "doubao-1.5-thinking-pro", "volcengine"),
# DashScope (阿里云 — Qwen models)
("qwen3-coder", "qwen3-coder-plus", "dashscope"),
("qwen3-235b", "qwen3-235b-a22b", "dashscope"),
("qwen-max", "qwen-max", "dashscope"),
("qwq-plus", "qwq-plus", "dashscope"),
]
# Public dict for simple lookups (last entry wins for duplicate names).
+30
View File
@@ -34,6 +34,8 @@ class TestModelsRegistry:
assert "openrouter" in providers
assert "zhipu" in providers
assert "zhipu-code" in providers
assert "volcengine" in providers
assert "dashscope" in providers
def test_entries_are_valid_tuples(self):
"""Test that _MODEL_ENTRIES contains valid (name, model_id, provider) tuples."""
@@ -46,6 +48,8 @@ class TestModelsRegistry:
"openrouter",
"zhipu",
"zhipu-code",
"volcengine",
"dashscope",
"custom-openai",
"custom-anthropic",
}
@@ -445,6 +449,32 @@ class TestThirdPartyRouting:
call_kwargs = mock_init.call_args[1]
assert "reasoning" not in call_kwargs
@patch("EvoScientist.llm.models.init_chat_model")
def test_volcengine_routes_through_openai(self, mock_init, monkeypatch):
"""Volcengine provider should route through OpenAI with correct base_url."""
mock_init.return_value = "mock_model"
monkeypatch.setenv("VOLCENGINE_API_KEY", "ve-key-123")
get_chat_model("doubao-seed-1.6", provider="volcengine")
call_kwargs = mock_init.call_args[1]
assert call_kwargs["model_provider"] == "openai"
assert call_kwargs["base_url"] == "https://ark.cn-beijing.volces.com/api/v3"
assert call_kwargs["api_key"] == "ve-key-123"
@patch("EvoScientist.llm.models.init_chat_model")
def test_dashscope_routes_through_openai(self, mock_init, monkeypatch):
"""DashScope provider should route through OpenAI with correct base_url."""
mock_init.return_value = "mock_model"
monkeypatch.setenv("DASHSCOPE_API_KEY", "ds-key-456")
get_chat_model("qwen-max", provider="dashscope")
call_kwargs = mock_init.call_args[1]
assert call_kwargs["model_provider"] == "openai"
assert call_kwargs["base_url"] == "https://dashscope.aliyuncs.com/compatible-mode/v1"
assert call_kwargs["api_key"] == "ds-key-456"
# =============================================================================
# Test _apply_auto_config