Files

1 line
16 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "artmeng/litecode", "files": {"agent/providers/registry.py": {"3e988e02ecfcbc878828ccd6f0241c6125fe3792": {"t": 1785674685.5435565, "n": 14114, "h": "b23a4de884f85106", "c": "\"\"\"\nProvider Registry — single source of truth for LLM provider metadata.\n\n设计原则(参考 nanobot):\n - ProviderSpec 只描述 *访问方式*,不内嵌 model 列表。\n - Model id 由用户在配置 / WebUI 自由填写。\n - 添加新 provider:在 PROVIDERS 元组里加一条,并在 webui ProviderOption 自动暴露。\n\n字段使用:\n - keywords: 用于 `provider=\"auto\"` 时按 model 名匹配。\n - default_api_base: 当用户未填 apiBase 时的兜底;OAuth/local/direct provider 也用。\n - is_gateway / is_local / is_oauth / is_direct: 决定是否要求 apiKey、UI 是否折叠 API Base 字段等。\n - thinking_style: 把\"启用思考\"开关注入 extra_body 的方式(不同厂家不同协议)。\n - model_overrides: 强制为某些 model 注入的固定参数(例如 Kimi K2 要求 temperature ≥ 1)。\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass, field\nfrom typing import Any\n\n\n@dataclass(frozen=True)\nclass ProviderSpec:\n \"\"\"一种 provider 的访问元数据。Model id 由用户填,spec 不存。\"\"\"\n\n # 身份\n name: str # registry id, 与 config providers.{name} 对齐\n display_name: str # UI 展示\n backend: str # openai_compat | anthropic | azure_openai | bedrock | openai_codex | github_copilot\n keywords: tuple[str, ...] = () # provider=\"auto\" 时用 model 名匹配\n\n # 网络\n default_api_base: str | None = None\n env_key: str = \"\" # 可选,从环境变量取 key\n env_extras: tuple[tuple[str, str], ...] = () # 额外注入的 env,e.g. ((\"ZHIPUAI_API_KEY\", \"{api_key}\"),)\n\n # 分类(仅 UI 分组与门禁逻辑用)\n region: str = \"other\" # foreign | aggregator | cloud | cn | local | other\n is_gateway: bool = False # OpenRouter / AiHubMix 等可路由任意模型\n is_local: bool = False # 本地部署(vLLM / Ollama / LM Studio)\n is_oauth: bool = False # OAuth-based,用户不填 apiKey\n is_direct: bool = False # 用户必须自己填完整 endpoint(custom / azure / bedrock / ovms)\n detect_by_key_prefix: str = \"\" # auto 路由:按 apiKey 前缀认人\n detect_by_base_keyword: str = \"\" # auto 路由:按 apiBase 关键字认人\n\n # OpenAI-compat 行为差异\n strip_model_prefix: bool = False # 发请求前去掉 \"vendor/\" 前缀(aihubmix 等)\n supports_max_completion_tokens: bool = False # OpenAI 系:用 max_completion_tokens 而非 max_tokens\n supports_prompt_caching: bool = False # Anthropic / OpenRouter 等\n\n # 思考开关 / 推理\n thinking_style: str = \"\" # thinking_type | enable_thinking | reasoning_split\n reasoning_as_content: bool = False # StepFun:把 reasoning 字段当正文\n\n # 强制覆写(按 model id 注入 extra_body / sampling)\n model_overrides: tuple[tuple[str, dict[str, Any]], ...] = field(default_factory=tuple)\n\n\nPROVIDERS: tuple[ProviderSpec, ...] = (\n # ─── 海外大厂 ─────────────────────────────────────────────\n ProviderSpec(\n name=\"openai\",\n display_name=\"OpenAI\",\n backend=\"openai_compat\",\n keywords=(\"openai\", \"gpt\", \"o1\", \"o3\", \"o4\"),\n default_api_base=\"https://api.openai.com/v1\",\n env_key=\"OPENAI_API_KEY\",\n region=\"foreign\",\n supports_max_completion_tokens=True,\n ),\n ProviderSpec(\n name=\"anthropic\",\n display_name=\"Anthropic\",\n backend=\"anthropic\",\n keywords=(\"anthropic\", \"claude\"),\n env_key=\"ANTHROPIC_API_KEY\",\n region=\"foreign\",\n supports_prompt_caching=True,\n ),\n ProviderSpec(\n name=\"gemini\",\n display_name=\"Google Gemini\",\n backend=\"openai_compat\",\n keywords=(\"gemini\", \"gemma\", \"google\"),\n default_api_base=\"https://generativelanguage.googleapis.com/v1beta/openai/\",\n env_key=\"GEMINI_API_KEY\",\n region=\"foreign\",\n ),\n ProviderSpec(\n name=\"xai\",\n display_name=\"xAI Grok\",\n backend=\"openai_compat\",\n keywords=(\"xai\", \"grok\"),\n default_api_base=\"https://api.x.ai/v1\",\n env_key=\"XAI_API_KEY\",\n region=\"foreign\",\n ),\n ProviderSpec(\n name=\"mistral\",\n display_name=\"Mistral AI\",\n backend=\"openai_compat\",\n keywords=(\"mistral\", \"codestral\"),\n default_api_base=\"https://api.mistral.ai/v1\",\n env_key=\"MISTRAL_API_KEY\",\n region=\"foreign\",\n ),\n ProviderSpec(\n name=\"groq\",\n display_name=\"Groq\",\n backend=\"openai_compat\",\n keywords=(\"groq\",),\n default_api_base=\"https://api.groq.com/openai/v1\",\n env_key=\"GROQ_API_KEY\",\n region=\"foreign\",\n ),\n\n # ─── 聚合 / 网关 ───────────────────────────────────────────\n ProviderSpec(\n name=\"openrouter\",\n display_name=\"OpenRouter\",\n backend=\"openai_compat\",\n keywords=(\"openrouter\",),\n default_api_base=\"https://openrouter.ai/api/v1\",\n env_key=\"OPENROUTER_API_KEY\",\n region=\"aggregator\",\n is_gateway=True,\n detect_by_key_prefix=\"sk-or-\",\n detect_by_base_keyword=\"openrouter\",\n supports_prompt_caching=True,\n ),\n ProviderSpec(\n name=\"huggingface\",\n display_name=\"Hugging Face\",\n backend=\"openai_compat\",\n keywords=(\"huggingface\", \"hugging-face\"),\n default_api_base=\"https://router.huggingface.co/v1\",\n env_key=\"HF_TOKEN\",\n region=\"aggregator\",\n is_gateway=True,\n detect_by_key_prefix=\"hf_\",\n detect_by_base_keyword=\"huggingface\",\n ),\n ProviderSpec(\n name=\"aihubmix\",\n display_name=\"AiHubMix\",\n backend=\"openai_compat\",\n keywords=(\"aihubmix\",),\n default_api_base=\"https://aihubmix.com/v1\",\n env_key=\"AIHUBMIX_API_KEY\",\n region=\"aggregator\",\n is_gateway=True,\n detect_by_base_keyword=\"aihubmix\",\n strip_model_prefix=True,\n ),\n ProviderSpec(\n name=\"siliconflow\",\n display_name=\"SiliconFlow (硅基流动)\",\n backend=\"openai_compat\",\n keywords=(\"siliconflow\",),\n default_api_base=\"https://api.siliconflow.cn/v1\",\n env_key=\"SILICONFLOW_API_KEY\",\n region=\"aggregator\",\n is_gateway=True,\n detect_by_base_keyword=\"siliconflow\",\n ),\n\n # ─── 云厂 ─────────────────────────────────────────────────\n ProviderSpec(\n name=\"azure_openai\",\n display_name=\"Azure OpenAI\",\n backend=\"azure_openai\",\n keywords=(\"azure\", \"azure-openai\"),\n region=\"cloud\",\n is_direct=True,\n ),\n ProviderSpec(\n name=\"bedrock\",\n display_name=\"AWS Bedrock\",\n backend=\"bedrock\",\n keywords=(\n \"bedrock\", \"anthropic.claude\", \"amazon.nova\", \"meta.\",\n \"mistral.\", \"cohere.\", \"deepseek.\", \"moonshot.\",\n ),\n env_key=\"AWS_BEARER_TOKEN_BEDROCK\",\n region=\"cloud\",\n is_direct=True,\n ),\n\n # ─── 国内 ─────────────────────────────────────────────────\n ProviderSpec(\n name=\"deepseek\",\n display_name=\"DeepSeek\",\n backend=\"openai_compat\",\n keywords=(\"deepseek\",),\n default_api_base=\"https://api.deepseek.com\",\n env_key=\"DEEPSEEK_API_KEY\",\n region=\"cn\",\n thinking_style=\"thinking_type\",\n ),\n ProviderSpec(\n name=\"dashscope\",\n display_name=\"Alibaba DashScope (Qwen)\",\n backend=\"openai_compat\",\n keywords=(\"dashscope\", \"qwen\"),\n default_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n env_key=\"DASHSCOPE_API_KEY\",\n region=\"cn\",\n thinking_style=\"enable_thinking\",\n ),\n ProviderSpec(\n name=\"moonshot\",\n display_name=\"Moonshot Kimi\",\n backend=\"openai_compat\",\n keywords=(\"moonshot\", \"kimi\"),\n default_api_base=\"https://api.moonshot.cn/v1\",\n env_key=\"MOONSHOT_API_KEY\",\n region=\"cn\",\n model_overrides=(\n (\"kimi-k2\", {\"temperature\": 1.0}),\n (\"kimi-k2.5\", {\"temperature\": 1.0}),\n (\"kimi-k2.6\", {\"temperature\": 1.0}),\n ),\n ),\n ProviderSpec(\n name=\"zhipu\",\n display_name=\"Zhipu GLM (智谱)\",\n backend=\"openai_compat\",\n keywords=(\"zhipu\", \"glm\", \"zai\"),\n default_api_base=\"https://open.bigmodel.cn/api/paas/v4/\",\n env_key=\"ZAI_API_KEY\",\n env_extras=((\"ZHIPUAI_API_KEY\", \"{api_key}\"),),\n region=\"cn\",\n ),\n ProviderSpec(\n name=\"volcengine\",\n display_name=\"VolcEngine 火山方舟 (含豆包)\",\n backend=\"openai_compat\",\n keywords=(\"volcengine\", \"volces\", \"ark\", \"doubao\"),\n default_api_base=\"https://ark.cn-beijing.volces.com/api/v3\",\n env_key=\"ARK_API_KEY\",\n region=\"cn\",\n is_gateway=True,\n detect_by_base_keyword=\"volces\",\n thinking_style=\"thinking_type\",\n ),\n ProviderSpec(\n name=\"volcengine_coding_plan\",\n display_name=\"VolcEngine Coding Plan\",\n backend=\"openai_compat\",\n keywords=(\"volcengine-plan\",),\n default_api_base=\"https://ark.cn-beijing.volces.com/api/coding/v3\",\n env_key=\"ARK_API_KEY\",\n region=\"cn\",\n is_gateway=True,\n strip_model_prefix=True,\n thinking_style=\"thinking_type\",\n ),\n ProviderSpec(\n name=\"byteplus\",\n display_name=\"BytePlus (海外火山)\",\n backend=\"openai_compat\",\n keywords=(\"byteplus\",),\n default_api_base=\"https://ark.ap-southeast.bytepluses.com/api/v3\",\n env_key=\"BYTEPLUS_API_KEY\",\n region=\"cn\",\n is_gateway=True,\n detect_by_base_keyword=\"bytepluses\",\n strip_model_prefix=True,\n thinking_style=\"thinking_type\",\n ),\n ProviderSpec(\n name=\"minimax\",\n display_name=\"MiniMax\",\n backend=\"openai_compat\",\n keywords=(\"minimax\",),\n default_api_base=\"https://api.minimax.io/v1\",\n env_key=\"MINIMAX_API_KEY\",\n region=\"cn\",\n thinking_style=\"reasoning_split\",\n ),\n ProviderSpec(\n name=\"stepfun\",\n display_name=\"Step Fun (阶跃星辰)\",\n backend=\"openai_compat\",\n keywords=(\"stepfun\", \"step\"),\n default_api_base=\"https://api.stepfun.com/v1\",\n env_key=\"STEPFUN_API_KEY\",\n region=\"cn\",\n reasoning_as_content=True,\n ),\n ProviderSpec(\n name=\"xiaomi_mimo\",\n display_name=\"Xiaomi MIMO (小米)\",\n backend=\"openai_compat\",\n keywords=(\"xiaomi\", \"mimo\"),\n default_api_base=\"https://api.xiaomimimo.com/v1\",\n env_key=\"XIAOMIMIMO_API_KEY\",\n region=\"cn\",\n ),\n ProviderSpec(\n name=\"longcat\",\n display_name=\"LongCat (美团)\",\n backend=\"openai_compat\",\n keywords=(\"longcat\",),\n default_api_base=\"https://api.longcat.chat/openai/v1\",\n env_key=\"LONGCAT_API_KEY\",\n region=\"cn\",\n ),\n ProviderSpec(\n name=\"qianfan\",\n display_name=\"Qianfan 千帆 (文心 ERNIE)\",\n backend=\"openai_compat\",\n keywords=(\"qianfan\", \"ernie\", \"wenxin\"),\n default_api_base=\"https://qianfan.baidubce.com/v2\",\n env_key=\"QIANFAN_API_KEY\",\n region=\"cn\",\n ),\n\n # ─── 本地部署 ──────────────────────────────────────────────\n ProviderSpec(\n name=\"ollama\",\n display_name=\"Ollama\",\n backend=\"openai_compat\",\n keywords=(\"ollama\", \"llama\", \"nemotron\"),\n default_api_base=\"http://localhost:11434/v1\",\n env_key=\"OLLAMA_API_KEY\",\n region=\"local\",\n is_local=True,\n detect_by_base_keyword=\"11434\",\n ),\n ProviderSpec(\n name=\"lm_studio\",\n display_name=\"LM Studio\",\n backend=\"openai_compat\",\n keywords=(\"lm-studio\", \"lmstudio\", \"lm_studio\"),\n default_api_base=\"http://localhost:1234/v1\",\n env_key=\"LM_STUDIO_API_KEY\",\n region=\"local\",\n is_local=True,\n detect_by_base_keyword=\"1234\",\n ),\n ProviderSpec(\n name=\"vllm\",\n display_name=\"vLLM\",\n backend=\"openai_compat\",\n keywords=(\"vllm\",),\n env_key=\"HOSTED_VLLM_API_KEY\",\n region=\"local\",\n is_local=True,\n ),\n ProviderSpec(\n name=\"ovms\",\n display_name=\"OpenVINO Model Server\",\n backend=\"openai_compat\",\n keywords=(\"openvino\", \"ovms\"),\n default_api_base=\"http://localhost:8000/v3\",\n region=\"local\",\n is_local=True,\n is_direct=True,\n ),\n\n # ─── OAuth-based ───────────────────────────────────────────\n ProviderSpec(\n name=\"openai_codex\",\n display_name=\"OpenAI Codex\",\n backend=\"openai_codex\",\n keywords=(\"openai-codex\", \"codex\"),\n default_api_base=\"https://chatgpt.com/backend-api\",\n region=\"other\",\n is_oauth=True,\n detect_by_base_keyword=\"codex\",\n strip_model_prefix=True,\n ),\n ProviderSpec(\n name=\"github_copilot\",\n display_name=\"GitHub Copilot\",\n backend=\"github_copilot\",\n keywords=(\"github_copilot\", \"copilot\"),\n default_api_base=\"https://api.githubcopilot.com\",\n region=\"other\",\n is_oauth=True,\n strip_model_prefix=True,\n supports_max_completion_tokens=True,\n ),\n\n # ─── 兜底 ─────────────────────────────────────────────────\n ProviderSpec(\n name=\"custom\",\n display_name=\"Custom\",\n backend=\"openai_compat\",\n keywords=(),\n region=\"other\",\n is_direct=True,\n ),\n)\n\n\ndef find_by_name(name: str | None) -> ProviderSpec | None:\n \"\"\"按 registry name 精确查找。容忍 - / _ 互换。\"\"\"\n if not name:\n return None\n normalized = name.replace(\"-\", \"_\").lower()\n for spec in PROVIDERS:\n if spec.name == normalized:\n return spec\n return None\n\n\ndef provider_options() -> list[dict[str, Any]]:\n \"\"\"供 WebUI ProviderOption 下拉用的元数据列表。\"\"\"\n return [\n {\n \"name\": spec.name,\n \"displayName\": spec.display_name,\n \"backend\": spec.backend,\n \"defaultApiBase\": spec.default_api_base or \"\",\n \"region\": spec.region,\n \"isGateway\": spec.is_gateway,\n \"isLocal\": spec.is_local,\n \"isOauth\": spec.is_oauth,\n \"isDirect\": spec.is_direct,\n \"thinkingStyle\": spec.thinking_style or None,\n }\n for spec in PROVIDERS\n ]\n"}}}}