""" 大模型API中转系统配置 - 支持动态修改 v2.1.0 - 能力(Capability)体系: 每个模型标记能力,AUTO配置按能力绑定 能力类型: text 文本推理 vision 视觉能力 (多模态输入) audio_out 语音输出 (TTS) audio_in 语音输入 (ASR) image_gen 图片生成 video_gen 视频生成 """ import json 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://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-llm", "name": "SiliconFlow LLM", "priority": 2, "base_url": "https://api.siliconflow.cn/v1", "api_key": "sk-fhpoexpptvjghpnphtaxbkhjwulzovoqfffbckcfscjmwhcg", "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, }, ] # 默认模型别名 DEFAULT_MODEL_ALIASES = { "auto": "auto", "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配置固定绑定一个能力 # capability: 固定此auto的功能类型(取自 CAPABILITY_DEFS) # models: 有序的**具体模型**列表(按优先级,可拖动排序);为空时回退为按提供商能力选择 # providers: 候选提供商(* 表示所有启用的、且具备该能力模型的提供商) DEFAULT_AUTO_PROFILES = { "auto": { "name": "默认Auto", "description": "文本推理 - 按模型列表顺序自动选择", "capability": "text", "models": [ "unsloth/Qwen3.8-27B-Q6_K", "unsloth/Qwen3.8-27B-Q4_K_M", "deepseek-ai/DeepSeek-V4-Flash", "meituan-longcat/LongCat-2.0", "qwen3.6-plus", "GLM-5.3-flash", ], "providers": ["*"], "strategy": "priority", }, "auto-text": { "name": "文本推理", "description": "纯文本推理,按模型列表顺序自动选择", "capability": "text", "models": [ "unsloth/Qwen3.8-27B-Q6_K", "unsloth/Qwen3.8-27B-Q4_K_M", "deepseek-ai/DeepSeek-V4-Flash", "meituan-longcat/LongCat-2.0", "qwen3.6-plus", "GLM-5.3-flash", ], "providers": ["*"], "strategy": "priority", }, "auto-vision": { "name": "视觉能力", "description": "多模态视觉理解,按模型列表顺序自动选择", "capability": "vision", "models": [ "unsloth/Qwen3.8-27B-Q6_K", "unsloth/Qwen3.8-27B-Q4_K_M", "qwen3.6-plus", "GLM-5.3-flash", ], "providers": ["*"], "strategy": "priority", }, "auto-image": { "name": "图片生成", "description": "文生图,按模型列表顺序自动选择", "capability": "image_gen", "models": ["Qwen-Image"], "providers": ["*"], "strategy": "priority", }, "auto-voice-out": { "name": "语音输出", "description": "语音合成(TTS),按模型列表顺序自动选择", "capability": "audio_out", "models": [], "providers": ["*"], "strategy": "priority", }, "auto-voice-in": { "name": "语音输入", "description": "语音识别(ASR),按模型列表顺序自动选择", "capability": "audio_in", "models": [], "providers": ["*"], "strategy": "priority", }, "auto-video": { "name": "视频生成", "description": "文生视频,按模型列表顺序自动选择", "capability": "video_gen", "models": [], "providers": ["*"], "strategy": "priority", }, } # 路由缓存配置(历史上下文优先复用上次模型,命中前缀缓存节省成本) ROUTING_CONFIG = { "prefer_cache_model": True, # 开启后:带历史上下文的 auto 请求优先复用上次使用的模型 "cache_ttl_seconds": 3600, # 记忆有效期(秒) } # 邮件通知配置(auto 调用全部失败时发邮件通知) EMAIL_CONFIG = { "enabled": True, # 总开关 "notify_on_auto_failure": True, # auto 调用全部失败时发邮件 "smtp_host": "mail.tphai.com", "smtp_port": 587, "smtp_mode": "plain", # plain | starttls | ssl "smtp_user": "hz4th_coder@tphai.com", "smtp_password": "hz4th_coder@!", "from_name": "LLM Proxy", "from_addr": "hz4th_coder@tphai.com", "to_addrs": ["wlq@tphai.com"], "cooldown_seconds": 300, # 失败通知最小间隔(防轰炸) } # 接口 API Key 鉴权(OpenAI 兼容:Authorization: Bearer ) # 应用于 /v1/* 与 /mcp 接口;后台 /admin 不受此控制 API_AUTH = { "enabled": True, # 是否启用鉴权 "keys": [], # 允许的 key 列表(可多个) } def load_api_auth(): """加载 API 鉴权配置(未配置 key 时自动生成一个)""" import hashlib config = load_config() merged = {**API_AUTH, **config.get("api_auth", {})} if not merged.get("keys"): import time merged["keys"] = ["sk-hz4th-" + hashlib.md5((str(time.time()) + "llm-proxy").encode()).hexdigest()[:16]] return merged def save_api_auth(data): """保存 API 鉴权配置""" config = load_config() merged = {**load_api_auth(), **data} if not merged.get("keys"): merged["keys"] = API_AUTH["keys"] config["api_auth"] = merged save_config(config) return merged def load_email_config(): """加载邮件通知配置(运行时配置优先)""" config = load_config() return {**EMAIL_CONFIG, **config.get("email_config", {})} def save_email_config(data): """保存邮件通知配置""" config = load_config() merged = {**EMAIL_CONFIG, **data} config["email_config"] = merged save_config(config) return merged def load_routing_config(): """加载路由缓存配置(运行时配置优先)""" config = load_config() return {**ROUTING_CONFIG, **config.get("routing_config", {})} def save_routing_config(data): """保存路由缓存配置""" config = load_config() config["routing_config"] = {**ROUTING_CONFIG, **data} save_config(config) return config["routing_config"] def load_config(): """加载配置""" if CONFIG_FILE.exists(): try: data = json.loads(CONFIG_FILE.read_text(encoding='utf-8')) return data except: pass return { "providers": DEFAULT_PROVIDERS, "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() for p in providers: if p.get("id") == 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} config["providers"] = providers save_config(config) return providers[i] return None def delete_provider(provider_id): """删除提供商""" config = load_config() providers = config.get("providers", []) providers = [p for p in providers if p.get("id") != provider_id] config["providers"] = providers 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() aliases = config.get("model_aliases", {}) aliases[alias] = target config["model_aliases"] = aliases 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() profiles = config.get("auto_profiles", {}) profiles[profile_name] = profile_data config["auto_profiles"] = profiles save_config(config) return profiles def update_auto_profile(profile_name, profile_data): """更新Auto配置""" config = load_config() profiles = config.get("auto_profiles", {}) if profile_name in profiles: profiles[profile_name] = {**profiles[profile_name], **profile_data} config["auto_profiles"] = profiles save_config(config) return profiles[profile_name] return None def delete_auto_profile(profile_name): """删除Auto配置""" if profile_name == "auto": return False # 不能删除默认的auto config = load_config() profiles = config.get("auto_profiles", {}) if profile_name in profiles: del profiles[profile_name] config["auto_profiles"] = profiles save_config(config) return True return False # 初始化配置 config = load_config() UPSTREAM_PROVIDERS = config.get("providers", DEFAULT_PROVIDERS) MODEL_ALIASES = config.get("model_aliases", DEFAULT_MODEL_ALIASES) # 服务配置 SERVER_CONFIG = { "host": "0.0.0.0", "port": 16003, "debug": False, } # 日志配置 LOG_CONFIG = { "log_dir": "logs", "log_requests": True, "log_errors": True, } # 重试/熔断配置 RETRY_CONFIG = { "max_retries": 3, "retry_delay": 1, "cooldown_seconds": 60, # 熔断冷却期,到期自动恢复(半开) "retry_on_errors": [ "connection_error", "timeout", "rate_limit", "server_error", ], }