mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat: 模型获取支持 CLI 端点回退,同族优先 chat 后降级 cli
This commit is contained in:
@@ -20,8 +20,12 @@ from src.utils.ssl_utils import get_ssl_context
|
||||
# 并发请求限制
|
||||
MAX_CONCURRENT_REQUESTS = 5
|
||||
|
||||
# 只对这些基础 endpoint signature 获取模型列表,CLI 使用相同的上游 API
|
||||
MODEL_FETCH_FORMATS = ["openai:chat", "claude:chat", "gemini:chat"]
|
||||
# 模型获取格式优先级:同族内优先使用 chat 端点,若无则回退到 cli 端点
|
||||
MODEL_FETCH_FORMAT_PRIORITY: list[tuple[str, ...]] = [
|
||||
("openai:chat", "openai:cli"),
|
||||
("claude:chat", "claude:cli"),
|
||||
("gemini:chat", "gemini:cli"),
|
||||
]
|
||||
|
||||
# Return tuple signature:
|
||||
# (models, errors, has_success, upstream_metadata)
|
||||
@@ -163,17 +167,21 @@ def build_all_format_configs(
|
||||
if not format_to_endpoint:
|
||||
return []
|
||||
|
||||
# 只对基础 API 格式获取模型,CLI 格式使用相同的上游 API
|
||||
return [
|
||||
{
|
||||
"api_key": api_key_value,
|
||||
"base_url": ep.base_url,
|
||||
"api_format": fmt,
|
||||
"extra_headers": get_extra_headers_from_endpoint(ep),
|
||||
}
|
||||
for fmt in MODEL_FETCH_FORMATS
|
||||
if (ep := format_to_endpoint.get(fmt)) is not None
|
||||
]
|
||||
# 同族内优先使用 chat 端点,若无则回退到 cli 端点
|
||||
configs: list[dict] = []
|
||||
for candidates in MODEL_FETCH_FORMAT_PRIORITY:
|
||||
fmt = next((f for f in candidates if f in format_to_endpoint), None)
|
||||
if fmt is not None:
|
||||
ep = format_to_endpoint[fmt]
|
||||
configs.append(
|
||||
{
|
||||
"api_key": api_key_value,
|
||||
"base_url": ep.base_url,
|
||||
"api_format": fmt,
|
||||
"extra_headers": get_extra_headers_from_endpoint(ep),
|
||||
}
|
||||
)
|
||||
return configs
|
||||
|
||||
|
||||
async def fetch_models_from_endpoints(
|
||||
|
||||
@@ -47,15 +47,46 @@ def test_only_configured_formats_are_included() -> None:
|
||||
assert configs[0]["base_url"] == "https://betterclau.de/claude/api.freekey.site"
|
||||
|
||||
|
||||
def test_cli_format_only_is_skipped() -> None:
|
||||
"""如果只配置了 CLI 格式(不在 MODEL_FETCH_FORMATS 中),应返回空列表。"""
|
||||
def test_cli_format_only_fallback() -> None:
|
||||
"""只配置了 CLI 格式时,应回退使用 CLI 端点获取模型。"""
|
||||
format_to_endpoint = {
|
||||
"openai:cli": _make_endpoint("https://api.openai.example.com"),
|
||||
}
|
||||
|
||||
configs = build_all_format_configs("sk-test-key", format_to_endpoint) # type: ignore[arg-type]
|
||||
|
||||
assert configs == []
|
||||
assert len(configs) == 1
|
||||
assert configs[0]["api_format"] == "openai:cli"
|
||||
assert configs[0]["base_url"] == "https://api.openai.example.com"
|
||||
|
||||
|
||||
def test_chat_takes_priority_over_cli() -> None:
|
||||
"""同族同时存在 chat 和 cli 端点时,应优先使用 chat 端点。"""
|
||||
format_to_endpoint = {
|
||||
"openai:chat": _make_endpoint("https://chat.openai.example.com"),
|
||||
"openai:cli": _make_endpoint("https://cli.openai.example.com"),
|
||||
}
|
||||
|
||||
configs = build_all_format_configs("sk-test-key", format_to_endpoint) # type: ignore[arg-type]
|
||||
|
||||
assert len(configs) == 1
|
||||
assert configs[0]["api_format"] == "openai:chat"
|
||||
assert configs[0]["base_url"] == "https://chat.openai.example.com"
|
||||
|
||||
|
||||
def test_mixed_chat_and_cli_fallback() -> None:
|
||||
"""一个族有 chat、另一个族只有 cli,应各自正确选择。"""
|
||||
format_to_endpoint = {
|
||||
"openai:chat": _make_endpoint("https://chat.openai.example.com"),
|
||||
"claude:cli": _make_endpoint("https://cli.claude.example.com"),
|
||||
}
|
||||
|
||||
configs = build_all_format_configs("sk-test-key", format_to_endpoint) # type: ignore[arg-type]
|
||||
|
||||
assert len(configs) == 2
|
||||
by_fmt = {c["api_format"]: c for c in configs}
|
||||
assert by_fmt["openai:chat"]["base_url"] == "https://chat.openai.example.com"
|
||||
assert by_fmt["claude:cli"]["base_url"] == "https://cli.claude.example.com"
|
||||
|
||||
|
||||
def test_empty_format_to_endpoint() -> None:
|
||||
|
||||
Reference in New Issue
Block a user