chore: add models (#30)
Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user