v2.1.0: 能力路由体系 + 模型管理 + 生图/语音/视频端点 + 大量修复
- 提供商重构: Local Qwen(文本+视觉) / SiliconFlow LLM(文本) / Autodl(文本+视觉) / Autodl Image(生图) - 能力标签: text/vision/audio_out/audio_in/image_gen/video_gen,模型级可配 - AUTO配置绑定能力: auto/auto-text/auto-vision/auto-image/auto-voice-out/auto-voice-in/auto-video - 新端点: /v1/images/generations /v1/audio/speech /v1/audio/transcriptions /v1/video/generations - 模型管理页: 能力切换/增删改/设默认/别名管理 - 修复: 失败切换不再替换用户模型; 熔断冷却后自动恢复; 重试不再白等; embeddings按模型路由 - 修复: 后台模板API路径(/api/admin); 删除v1旧admin目录与双端口混乱 - 修复: run.sh硬编码路径; 死配置RETRY/LOG接线; engine_completions空body防护 - 端口: 16003(16001-16100白名单区间); debug关闭; start.sh部署脚本
This commit is contained in:
+173
-30
@@ -1,5 +1,14 @@
|
||||
"""
|
||||
大模型API中转系统配置 - 支持动态修改
|
||||
v2.1.0 - 能力(Capability)体系: 每个模型标记能力,AUTO配置按能力绑定
|
||||
|
||||
能力类型:
|
||||
text 文本推理
|
||||
vision 视觉能力 (多模态输入)
|
||||
audio_out 语音输出 (TTS)
|
||||
audio_in 语音输入 (ASR)
|
||||
image_gen 图片生成
|
||||
video_gen 视频生成
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -8,28 +17,76 @@ from pathlib import Path
|
||||
# 配置文件路径
|
||||
CONFIG_FILE = Path(__file__).parent.parent / 'data' / 'config.json'
|
||||
|
||||
# 能力定义
|
||||
CAPABILITY_DEFS = {
|
||||
"text": "文本推理",
|
||||
"vision": "视觉能力",
|
||||
"audio_out": "语音输出",
|
||||
"audio_in": "语音输入",
|
||||
"image_gen": "图片生成",
|
||||
"video_gen": "视频生成",
|
||||
}
|
||||
|
||||
# 默认上游模型配置
|
||||
# capabilities: 该提供商下所有模型的默认能力(模型管理页可对单个模型覆盖)
|
||||
DEFAULT_PROVIDERS = [
|
||||
{
|
||||
"id": "local-qwen",
|
||||
"name": "Local Qwen",
|
||||
"priority": 1,
|
||||
"base_url": "http://192.168.2.5:1234/v1",
|
||||
"api_key": "sk-lm-fuP5tGU8:Hi7YU87jHyDP6Ay8Tl2j",
|
||||
"models": ["qwen3.5-4b", "qwen3.5", "qwen"],
|
||||
"default_model": "qwen3.5-4b",
|
||||
"timeout": 120,
|
||||
"base_url": "http://121.40.164.32:18003/v1",
|
||||
"api_key": "sk-xxxx",
|
||||
"models": [
|
||||
{"name": "unsloth/Qwen3.8-27B-Q6_K", "capabilities": ["text", "vision"]},
|
||||
{"name": "unsloth/Qwen3.8-27B-Q4_K_M", "capabilities": ["text", "vision"]},
|
||||
],
|
||||
"default_model": "unsloth/Qwen3.8-27B-Q6_K",
|
||||
"capabilities": ["text", "vision"],
|
||||
"timeout": 180,
|
||||
"enabled": True,
|
||||
},
|
||||
{
|
||||
"id": "siliconflow-deepseek",
|
||||
"name": "SiliconFlow DeepSeek",
|
||||
"id": "siliconflow-llm",
|
||||
"name": "SiliconFlow LLM",
|
||||
"priority": 2,
|
||||
"base_url": "https://api.siliconflow.cn/v1",
|
||||
"api_key": "sk-fhpoexpptvjghpnphtaxbkhjwulzovoqfffbckcfscjmwhcg",
|
||||
"models": ["Pro/deepseek-ai/DeepSeek-V3.2", "deepseek-v3", "deepseek"],
|
||||
"default_model": "Pro/deepseek-ai/DeepSeek-V3.2",
|
||||
"timeout": 120,
|
||||
"models": [
|
||||
{"name": "deepseek-ai/DeepSeek-V4-Flash", "capabilities": ["text"]},
|
||||
{"name": "meituan-longcat/LongCat-2.0", "capabilities": ["text"]},
|
||||
],
|
||||
"default_model": "deepseek-ai/DeepSeek-V4-Flash",
|
||||
"capabilities": ["text"],
|
||||
"timeout": 180,
|
||||
"enabled": True,
|
||||
},
|
||||
{
|
||||
"id": "autodl",
|
||||
"name": "Autodl",
|
||||
"priority": 3,
|
||||
"base_url": "https://www.autodl.art/api/v1",
|
||||
"api_key": "F9MBfolzuapqTsD4KmUf9qen720rXvUZ3Sp3IrWiCTukqonx",
|
||||
"models": [
|
||||
{"name": "qwen3.6-plus", "capabilities": ["text", "vision"]},
|
||||
{"name": "GLM-5.3-flash", "capabilities": ["text", "vision"]},
|
||||
],
|
||||
"default_model": "GLM-5.3-flash",
|
||||
"capabilities": ["text", "vision"],
|
||||
"timeout": 180,
|
||||
"enabled": True,
|
||||
},
|
||||
{
|
||||
"id": "autodl-image",
|
||||
"name": "Autodl Image",
|
||||
"priority": 4,
|
||||
"base_url": "https://www.autodl.art/api/v1",
|
||||
"api_key": "F9MBfolzuapqTsD4KmUf9qen720rXvUZ3Sp3IrWiCTukqonx",
|
||||
"models": [
|
||||
{"name": "Qwen-Image", "capabilities": ["image_gen"]},
|
||||
],
|
||||
"default_model": "Qwen-Image",
|
||||
"capabilities": ["image_gen"],
|
||||
"timeout": 180,
|
||||
"enabled": True,
|
||||
},
|
||||
]
|
||||
@@ -37,24 +94,80 @@ DEFAULT_PROVIDERS = [
|
||||
# 默认模型别名
|
||||
DEFAULT_MODEL_ALIASES = {
|
||||
"auto": "auto",
|
||||
"qwen": "qwen3.5-4b",
|
||||
"qwen3.5": "qwen3.5-4b",
|
||||
"qwen3.5-4b": "qwen3.5-4b",
|
||||
"deepseek": "Pro/deepseek-ai/DeepSeek-V3.2",
|
||||
"deepseek-v3": "Pro/deepseek-ai/DeepSeek-V3.2",
|
||||
"deepseek-v3.2": "Pro/deepseek-ai/DeepSeek-V3.2",
|
||||
"auto-text": "auto-text",
|
||||
"auto-vision": "auto-vision",
|
||||
"auto-image": "auto-image",
|
||||
"auto-voice-out": "auto-voice-out",
|
||||
"auto-voice-in": "auto-voice-in",
|
||||
"auto-video": "auto-video",
|
||||
"auto-vlm": "auto-vision", # 兼容旧配置
|
||||
"qwen": "unsloth/Qwen3.8-27B-Q6_K",
|
||||
"qwen3.8": "unsloth/Qwen3.8-27B-Q6_K",
|
||||
"local": "unsloth/Qwen3.8-27B-Q6_K",
|
||||
"deepseek": "deepseek-ai/DeepSeek-V4-Flash",
|
||||
"deepseek-v4-flash": "deepseek-ai/DeepSeek-V4-Flash",
|
||||
"longcat": "meituan-longcat/LongCat-2.0",
|
||||
"glm": "GLM-5.3-flash",
|
||||
"qwen3.6-plus": "qwen3.6-plus",
|
||||
"gpt-4": "GLM-5.3-flash",
|
||||
}
|
||||
|
||||
# 默认Auto配置(自定义候选模型和优先级)
|
||||
# 默认Auto配置:每个auto配置固定绑定一个能力
|
||||
# capability: 固定此auto的功能类型(取自 CAPABILITY_DEFS)
|
||||
# providers: 候选提供商(* 表示所有启用的、且具备该能力模型的提供商)
|
||||
DEFAULT_AUTO_PROFILES = {
|
||||
"auto": {
|
||||
"name": "默认Auto",
|
||||
"description": "所有启用的提供商按优先级自动选择",
|
||||
"providers": ["*"], # * 表示所有启用的提供商
|
||||
"strategy": "priority", # priority | random | round-robin
|
||||
}
|
||||
"description": "文本推理 - 自动选择可用提供商",
|
||||
"capability": "text",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
"auto-text": {
|
||||
"name": "文本推理",
|
||||
"description": "纯文本推理,自动选择文本模型",
|
||||
"capability": "text",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
"auto-vision": {
|
||||
"name": "视觉能力",
|
||||
"description": "多模态视觉理解,自动选择视觉模型",
|
||||
"capability": "vision",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
"auto-image": {
|
||||
"name": "图片生成",
|
||||
"description": "文生图,自动选择生图模型",
|
||||
"capability": "image_gen",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
"auto-voice-out": {
|
||||
"name": "语音输出",
|
||||
"description": "语音合成(TTS),自动选择语音输出模型",
|
||||
"capability": "audio_out",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
"auto-voice-in": {
|
||||
"name": "语音输入",
|
||||
"description": "语音识别(ASR),自动选择语音输入模型",
|
||||
"capability": "audio_in",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
"auto-video": {
|
||||
"name": "视频生成",
|
||||
"description": "文生视频,自动选择视频生成模型",
|
||||
"capability": "video_gen",
|
||||
"providers": ["*"],
|
||||
"strategy": "priority",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def load_config():
|
||||
"""加载配置"""
|
||||
if CONFIG_FILE.exists():
|
||||
@@ -68,16 +181,19 @@ def load_config():
|
||||
"model_aliases": DEFAULT_MODEL_ALIASES,
|
||||
}
|
||||
|
||||
|
||||
def save_config(config):
|
||||
"""保存配置"""
|
||||
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
CONFIG_FILE.write_text(json.dumps(config, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
|
||||
|
||||
def get_providers():
|
||||
"""获取提供商列表"""
|
||||
config = load_config()
|
||||
return config.get("providers", DEFAULT_PROVIDERS)
|
||||
|
||||
|
||||
def get_provider(provider_id):
|
||||
"""获取单个提供商"""
|
||||
providers = get_providers()
|
||||
@@ -86,25 +202,27 @@ def get_provider(provider_id):
|
||||
return p
|
||||
return None
|
||||
|
||||
|
||||
def add_provider(provider):
|
||||
"""添加提供商"""
|
||||
config = load_config()
|
||||
providers = config.get("providers", [])
|
||||
|
||||
|
||||
# 生成ID
|
||||
if not provider.get("id"):
|
||||
provider["id"] = provider["name"].lower().replace(" ", "-").replace(".", "-")
|
||||
|
||||
|
||||
providers.append(provider)
|
||||
config["providers"] = providers
|
||||
save_config(config)
|
||||
return provider
|
||||
|
||||
|
||||
def update_provider(provider_id, data):
|
||||
"""更新提供商"""
|
||||
config = load_config()
|
||||
providers = config.get("providers", [])
|
||||
|
||||
|
||||
for i, p in enumerate(providers):
|
||||
if p.get("id") == provider_id:
|
||||
providers[i] = {**p, **data}
|
||||
@@ -113,6 +231,7 @@ def update_provider(provider_id, data):
|
||||
return providers[i]
|
||||
return None
|
||||
|
||||
|
||||
def delete_provider(provider_id):
|
||||
"""删除提供商"""
|
||||
config = load_config()
|
||||
@@ -122,26 +241,29 @@ def delete_provider(provider_id):
|
||||
save_config(config)
|
||||
return True
|
||||
|
||||
|
||||
def update_priority(provider_ids):
|
||||
"""更新优先级顺序"""
|
||||
config = load_config()
|
||||
providers = config.get("providers", [])
|
||||
|
||||
|
||||
# 按新顺序设置优先级
|
||||
for i, pid in enumerate(provider_ids):
|
||||
for p in providers:
|
||||
if p.get("id") == pid:
|
||||
p["priority"] = i + 1
|
||||
|
||||
|
||||
config["providers"] = providers
|
||||
save_config(config)
|
||||
return providers
|
||||
|
||||
|
||||
def get_model_aliases():
|
||||
"""获取模型别名"""
|
||||
config = load_config()
|
||||
return config.get("model_aliases", DEFAULT_MODEL_ALIASES)
|
||||
|
||||
|
||||
def update_model_alias(alias, target):
|
||||
"""更新模型别名"""
|
||||
config = load_config()
|
||||
@@ -151,16 +273,33 @@ def update_model_alias(alias, target):
|
||||
save_config(config)
|
||||
return aliases
|
||||
|
||||
|
||||
def delete_model_alias(alias):
|
||||
"""删除模型别名"""
|
||||
if alias == "auto":
|
||||
return False # 不能删除默认的auto
|
||||
config = load_config()
|
||||
aliases = config.get("model_aliases", {})
|
||||
if alias in aliases:
|
||||
del aliases[alias]
|
||||
config["model_aliases"] = aliases
|
||||
save_config(config)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_auto_profiles():
|
||||
"""获取Auto配置列表"""
|
||||
config = load_config()
|
||||
return config.get("auto_profiles", DEFAULT_AUTO_PROFILES)
|
||||
|
||||
|
||||
def get_auto_profile(profile_name):
|
||||
"""获取单个Auto配置"""
|
||||
profiles = get_auto_profiles()
|
||||
return profiles.get(profile_name)
|
||||
|
||||
|
||||
def add_auto_profile(profile_name, profile_data):
|
||||
"""添加Auto配置"""
|
||||
config = load_config()
|
||||
@@ -170,6 +309,7 @@ def add_auto_profile(profile_name, profile_data):
|
||||
save_config(config)
|
||||
return profiles
|
||||
|
||||
|
||||
def update_auto_profile(profile_name, profile_data):
|
||||
"""更新Auto配置"""
|
||||
config = load_config()
|
||||
@@ -181,6 +321,7 @@ def update_auto_profile(profile_name, profile_data):
|
||||
return profiles[profile_name]
|
||||
return None
|
||||
|
||||
|
||||
def delete_auto_profile(profile_name):
|
||||
"""删除Auto配置"""
|
||||
if profile_name == "auto":
|
||||
@@ -194,6 +335,7 @@ def delete_auto_profile(profile_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# 初始化配置
|
||||
config = load_config()
|
||||
UPSTREAM_PROVIDERS = config.get("providers", DEFAULT_PROVIDERS)
|
||||
@@ -202,8 +344,8 @@ MODEL_ALIASES = config.get("model_aliases", DEFAULT_MODEL_ALIASES)
|
||||
# 服务配置
|
||||
SERVER_CONFIG = {
|
||||
"host": "0.0.0.0",
|
||||
"port": 19007,
|
||||
"debug": True,
|
||||
"port": 16003,
|
||||
"debug": False,
|
||||
}
|
||||
|
||||
# 日志配置
|
||||
@@ -213,14 +355,15 @@ LOG_CONFIG = {
|
||||
"log_errors": True,
|
||||
}
|
||||
|
||||
# 重试配置
|
||||
# 重试/熔断配置
|
||||
RETRY_CONFIG = {
|
||||
"max_retries": 3,
|
||||
"retry_delay": 1,
|
||||
"cooldown_seconds": 60, # 熔断冷却期,到期自动恢复(半开)
|
||||
"retry_on_errors": [
|
||||
"connection_error",
|
||||
"timeout",
|
||||
"rate_limit",
|
||||
"server_error",
|
||||
],
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user