1 line
14 KiB
JSON
1 line
14 KiB
JSON
{"repo": "ageniti/essentials", "files": {"src/essentials/api/registry.py": {"1b6aa0ca567837aec5ed9cb28e4b6242c2fc0a76": {"t": 1785674636.6411438, "n": 13242, "h": "af5c3bd3f6bb9f9f", "c": "\"\"\"\nLLM Provider Registry — single source of truth for provider metadata.\n\nAdding a new provider:\n 1. Add a ProviderSpec to PROVIDERS below.\n Done. Detection, display, and config all derive from here.\n\nOrder matters — it controls match priority. Gateways and cloud providers first,\nstandard providers by keyword, local/special providers last.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass\n\n\n@dataclass(frozen=True)\nclass ProviderSpec:\n \"\"\"One LLM provider's metadata.\n\n backend_type:\n \"anthropic\" — Anthropic SDK (default for claude-* models)\n \"openai_compat\" — OpenAI-compatible REST API\n \"\"\"\n\n # Identity\n name: str # canonical name, e.g. \"dashscope\"\n keywords: tuple[str, ...] # model-name substrings for detection (lowercase)\n env_key: str # primary API key environment variable\n display_name: str = \"\" # shown in status / diagnostics\n\n # Routing\n backend_type: str = \"openai_compat\" # \"anthropic\" | \"openai_compat\"\n default_base_url: str = \"\" # fallback base URL for this provider\n\n # Auto-detection signals\n detect_by_key_prefix: str = \"\" # match api_key prefix, e.g. \"sk-or-\"\n detect_by_base_keyword: str = \"\" # match substring in base_url\n\n # Classification flags\n is_gateway: bool = False # routes any model (OpenRouter, AiHubMix, …)\n is_local: bool = False # local deployment (vLLM, Ollama)\n is_oauth: bool = False # uses OAuth instead of API key\n\n @property\n def label(self) -> str:\n return self.display_name or self.name.title()\n\n\n# ---------------------------------------------------------------------------\n# PROVIDERS registry — order = detection priority.\n# ---------------------------------------------------------------------------\n\nPROVIDERS: tuple[ProviderSpec, ...] = (\n # === Gateways (detected by api_key prefix / base_url keyword) ============\n # OpenRouter: global gateway, keys start with \"sk-or-\"\n ProviderSpec(\n name=\"openrouter\",\n keywords=(\"openrouter\",),\n env_key=\"OPENROUTER_API_KEY\",\n display_name=\"OpenRouter\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://openrouter.ai/api/v1\",\n detect_by_key_prefix=\"sk-or-\",\n detect_by_base_keyword=\"openrouter\",\n is_gateway=True,\n is_local=False,\n is_oauth=False,\n ),\n # AiHubMix: OpenAI-compatible gateway\n ProviderSpec(\n name=\"aihubmix\",\n keywords=(\"aihubmix\",),\n env_key=\"OPENAI_API_KEY\",\n display_name=\"AiHubMix\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://aihubmix.com/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"aihubmix\",\n is_gateway=True,\n is_local=False,\n is_oauth=False,\n ),\n # SiliconFlow (硅基流动): OpenAI-compatible gateway\n ProviderSpec(\n name=\"siliconflow\",\n keywords=(\"siliconflow\",),\n env_key=\"OPENAI_API_KEY\",\n display_name=\"SiliconFlow\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.siliconflow.cn/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"siliconflow\",\n is_gateway=True,\n is_local=False,\n is_oauth=False,\n ),\n # VolcEngine (火山引擎 / Ark): OpenAI-compatible gateway\n ProviderSpec(\n name=\"volcengine\",\n keywords=(\"volcengine\", \"volces\", \"ark\"),\n env_key=\"OPENAI_API_KEY\",\n display_name=\"VolcEngine\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://ark.cn-beijing.volces.com/api/v3\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"volces\",\n is_gateway=True,\n is_local=False,\n is_oauth=False,\n ),\n # ModelScope: OpenAI-compatible inference API\n ProviderSpec(\n name=\"modelscope\",\n keywords=(\"modelscope\",),\n env_key=\"MODELSCOPE_API_KEY\",\n display_name=\"ModelScope\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api-inference.modelscope.cn/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"modelscope\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # === Standard cloud providers (matched by model-name keyword) ============\n # Anthropic: native SDK for claude-* models\n ProviderSpec(\n name=\"anthropic\",\n keywords=(\"anthropic\", \"claude\"),\n env_key=\"ANTHROPIC_API_KEY\",\n display_name=\"Anthropic\",\n backend_type=\"anthropic\",\n default_base_url=\"\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # OpenAI: gpt-* models\n ProviderSpec(\n name=\"openai\",\n keywords=(\"openai\", \"gpt\", \"o1\", \"o3\", \"o4\"),\n env_key=\"OPENAI_API_KEY\",\n display_name=\"OpenAI\",\n backend_type=\"openai_compat\",\n default_base_url=\"\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # DeepSeek\n ProviderSpec(\n name=\"deepseek\",\n keywords=(\"deepseek\",),\n env_key=\"DEEPSEEK_API_KEY\",\n display_name=\"DeepSeek\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.deepseek.com/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"deepseek\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Google Gemini\n ProviderSpec(\n name=\"gemini\",\n keywords=(\"gemini\",),\n env_key=\"GEMINI_API_KEY\",\n display_name=\"Gemini\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://generativelanguage.googleapis.com/v1beta/openai\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"googleapis\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # DashScope (Qwen / 阿里云)\n ProviderSpec(\n name=\"dashscope\",\n keywords=(\"qwen\", \"dashscope\"),\n env_key=\"DASHSCOPE_API_KEY\",\n display_name=\"DashScope\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"dashscope\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Moonshot / Kimi\n ProviderSpec(\n name=\"moonshot\",\n keywords=(\"moonshot\", \"kimi\"),\n env_key=\"MOONSHOT_API_KEY\",\n display_name=\"Moonshot\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.moonshot.ai/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"moonshot\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # MiniMax\n ProviderSpec(\n name=\"minimax\",\n keywords=(\"minimax\",),\n env_key=\"MINIMAX_API_KEY\",\n display_name=\"MiniMax\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.minimax.io/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"minimax\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Zhipu AI / GLM\n ProviderSpec(\n name=\"zhipu\",\n keywords=(\"zhipu\", \"glm\", \"chatglm\"),\n env_key=\"ZHIPUAI_API_KEY\",\n display_name=\"Zhipu AI\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://open.bigmodel.cn/api/paas/v4\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"bigmodel\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Groq\n ProviderSpec(\n name=\"groq\",\n keywords=(\"groq\",),\n env_key=\"GROQ_API_KEY\",\n display_name=\"Groq\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.groq.com/openai/v1\",\n detect_by_key_prefix=\"gsk_\",\n detect_by_base_keyword=\"groq\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Mistral\n ProviderSpec(\n name=\"mistral\",\n keywords=(\"mistral\", \"mixtral\", \"codestral\"),\n env_key=\"MISTRAL_API_KEY\",\n display_name=\"Mistral\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.mistral.ai/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"mistral\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # StepFun (阶跃星辰)\n ProviderSpec(\n name=\"stepfun\",\n keywords=(\"step-\", \"stepfun\"),\n env_key=\"STEPFUN_API_KEY\",\n display_name=\"StepFun\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://api.stepfun.com/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"stepfun\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Baidu / ERNIE\n ProviderSpec(\n name=\"baidu\",\n keywords=(\"ernie\", \"baidu\"),\n env_key=\"QIANFAN_ACCESS_KEY\",\n display_name=\"Baidu\",\n backend_type=\"openai_compat\",\n default_base_url=\"https://qianfan.baidubce.com/v2\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"baidubce\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # === Cloud platform providers (detected by base_url) ====================\n # AWS Bedrock\n ProviderSpec(\n name=\"bedrock\",\n keywords=(\"bedrock\",),\n env_key=\"AWS_ACCESS_KEY_ID\",\n display_name=\"AWS Bedrock\",\n backend_type=\"openai_compat\",\n default_base_url=\"\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"bedrock\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # Google Vertex AI\n ProviderSpec(\n name=\"vertex\",\n keywords=(\"vertex\",),\n env_key=\"GOOGLE_APPLICATION_CREDENTIALS\",\n display_name=\"Vertex AI\",\n backend_type=\"openai_compat\",\n default_base_url=\"\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"aiplatform\",\n is_gateway=False,\n is_local=False,\n is_oauth=False,\n ),\n # === Local deployments (matched by keyword or base_url) =================\n # Ollama\n ProviderSpec(\n name=\"ollama\",\n keywords=(\"ollama\",),\n env_key=\"\",\n display_name=\"Ollama\",\n backend_type=\"openai_compat\",\n default_base_url=\"http://localhost:11434/v1\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"localhost:11434\",\n is_gateway=False,\n is_local=True,\n is_oauth=False,\n ),\n # vLLM / any OpenAI-compatible local server\n ProviderSpec(\n name=\"vllm\",\n keywords=(\"vllm\",),\n env_key=\"\",\n display_name=\"vLLM/Local\",\n backend_type=\"openai_compat\",\n default_base_url=\"\",\n detect_by_key_prefix=\"\",\n detect_by_base_keyword=\"\",\n is_gateway=False,\n is_local=True,\n is_oauth=False,\n ),\n)\n\n\n# ---------------------------------------------------------------------------\n# Lookup helpers\n# ---------------------------------------------------------------------------\n\n\ndef find_by_name(name: str) -> ProviderSpec | None:\n \"\"\"Find a provider spec by canonical name, e.g. \"dashscope\".\"\"\"\n for spec in PROVIDERS:\n if spec.name == name:\n return spec\n return None\n\n\ndef _match_by_model(model: str) -> ProviderSpec | None:\n \"\"\"Match a standard/gateway provider by model-name keyword (case-insensitive).\"\"\"\n model_lower = model.lower()\n model_normalized = model_lower.replace(\"-\", \"_\")\n model_prefix = model_lower.split(\"/\", 1)[0] if \"/\" in model_lower else \"\"\n normalized_prefix = model_prefix.replace(\"-\", \"_\")\n\n std_specs = [s for s in PROVIDERS if not s.is_local and not s.is_oauth]\n\n # Prefer an explicit provider-prefix match (e.g. \"deepseek/...\" → deepseek spec)\n for spec in std_specs:\n if model_prefix and normalized_prefix == spec.name:\n return spec\n\n # Fall back to keyword scan\n for spec in std_specs:\n if any(\n kw in model_lower or kw.replace(\"-\", \"_\") in model_normalized\n for kw in spec.keywords\n ):\n return spec\n return None\n\n\ndef detect_provider_from_registry(\n model: str,\n api_key: str | None = None,\n base_url: str | None = None,\n) -> ProviderSpec | None:\n \"\"\"Detect the best-matching ProviderSpec for the given inputs.\n\n Detection priority:\n 1. api_key prefix (e.g. \"sk-or-\" → OpenRouter)\n 2. base_url keyword (e.g. \"aihubmix\" in URL → AiHubMix)\n 3. model name keyword (e.g. \"qwen\" → DashScope)\n \"\"\"\n # 1. api_key prefix\n if api_key:\n for spec in PROVIDERS:\n if spec.detect_by_key_prefix and api_key.startswith(spec.detect_by_key_prefix):\n return spec\n\n # 2. base_url keyword\n if base_url:\n base_lower = base_url.lower()\n for spec in PROVIDERS:\n if spec.detect_by_base_keyword and spec.detect_by_base_keyword in base_lower:\n return spec\n\n # 3. model keyword\n if model:\n return _match_by_model(model)\n\n return None\n"}}}} |