{"repo": "chenqi92/pier-x", "files": {"src/lib/aiVendors.ts": {"153ab57b3a7f071b89a2389484dc27f3975c8c32": {"t": 1785674665.0623221, "n": 7059, "h": "633aad36e2462a38", "c": "// ── AI vendor preset registry (PRODUCT-SPEC §5.14.2) ───────────────\n// Pure data. Two wire protocols cover every vendor here: Anthropic's\n// Messages API and the OpenAI-compatible /chat/completions surface —\n// which is what virtually the whole market serves (NVIDIA NIM,\n// Xiaomi MiMo, DeepSeek, Kimi, GLM, DashScope, Ark, SiliconFlow,\n// OpenRouter, Groq, local Ollama/LM Studio/vLLM, …).\n//\n// Base URLs are DEFAULTS, not contracts: the settings UI always lets\n// the user edit them, so a vendor moving its endpoint never blocks\n// anyone. Each vendor gets its own keyring slot (`pier-x.ai.`),\n// so switching vendors never reuses another vendor's key.\n\nimport type { AiProviderKind } from \"./ai\";\n\nexport type AiVendorGroup = \"official\" | \"cn\" | \"intl\" | \"local\" | \"agent-cli\" | \"custom\";\n\nexport type AiVendorPreset = {\n /** Stable id — keyring slot suffix and persisted selection. */\n id: string;\n /** Display label (left untranslated — these are brand names). */\n label: string;\n group: AiVendorGroup;\n /** Wire protocol the endpoint speaks. */\n kind: AiProviderKind;\n /** Default endpoint base; \"\" = the user must fill it in. */\n baseUrl: string;\n /** Whether the endpoint normally requires an API key. */\n needsKey: boolean;\n /** Key format hint shown as the input placeholder. */\n keyHint?: string;\n /** Example model id for the model-input placeholder. */\n modelHint?: string;\n /** `kind === \"cli\"` only: which agent CLI this preset drives\n * (\"claude-code\" / \"codex\"); see PRODUCT-SPEC §5.14.8. */\n cliFlavor?: string;\n};\n\nexport const AI_VENDOR_GROUP_LABELS: Record = {\n official: \"Model vendors\",\n cn: \"China platforms\",\n intl: \"International platforms\",\n local: \"Local inference\",\n \"agent-cli\": \"Local agent CLI\",\n custom: \"Custom\",\n};\n\nexport const AI_VENDORS: AiVendorPreset[] = [\n // ── Model vendors ────────────────────────────────────────────\n {\n id: \"anthropic\",\n label: \"Anthropic\",\n group: \"official\",\n kind: \"anthropic\",\n baseUrl: \"https://api.anthropic.com\",\n needsKey: true,\n keyHint: \"sk-ant-…\",\n modelHint: \"claude-opus-4-8\",\n },\n {\n id: \"openai\",\n label: \"OpenAI\",\n group: \"official\",\n kind: \"openai\",\n baseUrl: \"https://api.openai.com/v1\",\n needsKey: true,\n keyHint: \"sk-…\",\n modelHint: \"gpt-5.5\",\n },\n // ── China platforms ──────────────────────────────────────────\n {\n id: \"deepseek\",\n label: \"DeepSeek\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://api.deepseek.com/v1\",\n needsKey: true,\n modelHint: \"deepseek-chat\",\n },\n {\n id: \"moonshot\",\n label: \"Kimi (Moonshot)\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://api.moonshot.cn/v1\",\n needsKey: true,\n },\n {\n id: \"zhipu\",\n label: \"智谱 GLM\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://open.bigmodel.cn/api/paas/v4\",\n needsKey: true,\n },\n {\n id: \"dashscope\",\n label: \"通义 Qwen (DashScope)\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n needsKey: true,\n modelHint: \"qwen-plus\",\n },\n {\n id: \"ark\",\n label: \"豆包 (火山方舟)\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://ark.cn-beijing.volces.com/api/v3\",\n needsKey: true,\n },\n {\n id: \"hunyuan\",\n label: \"腾讯混元\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://api.hunyuan.cloud.tencent.com/v1\",\n needsKey: true,\n },\n {\n id: \"qianfan\",\n label: \"百度千帆\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://qianfan.baidubce.com/v2\",\n needsKey: true,\n },\n {\n id: \"minimax\",\n label: \"MiniMax\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://api.minimaxi.com/v1\",\n needsKey: true,\n },\n {\n id: \"xiaomi\",\n label: \"小米 MiMo\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://api.xiaomimimo.com/v1\",\n needsKey: true,\n modelHint: \"mimo-v2.5-pro\",\n },\n {\n id: \"siliconflow\",\n label: \"SiliconFlow (硅基流动)\",\n group: \"cn\",\n kind: \"openai\",\n baseUrl: \"https://api.siliconflow.cn/v1\",\n needsKey: true,\n },\n // ── International platforms ──────────────────────────────────\n {\n id: \"nvidia\",\n label: \"NVIDIA NIM\",\n group: \"intl\",\n kind: \"openai\",\n baseUrl: \"https://integrate.api.nvidia.com/v1\",\n needsKey: true,\n keyHint: \"nvapi-…\",\n },\n {\n id: \"openrouter\",\n label: \"OpenRouter\",\n group: \"intl\",\n kind: \"openai\",\n baseUrl: \"https://openrouter.ai/api/v1\",\n needsKey: true,\n },\n {\n id: \"groq\",\n label: \"Groq\",\n group: \"intl\",\n kind: \"openai\",\n baseUrl: \"https://api.groq.com/openai/v1\",\n needsKey: true,\n },\n {\n id: \"mistral\",\n label: \"Mistral\",\n group: \"intl\",\n kind: \"openai\",\n baseUrl: \"https://api.mistral.ai/v1\",\n needsKey: true,\n },\n {\n id: \"xai\",\n label: \"xAI Grok\",\n group: \"intl\",\n kind: \"openai\",\n baseUrl: \"https://api.x.ai/v1\",\n needsKey: true,\n },\n {\n id: \"gemini\",\n label: \"Google Gemini\",\n group: \"intl\",\n kind: \"openai\",\n baseUrl: \"https://generativelanguage.googleapis.com/v1beta/openai\",\n needsKey: true,\n },\n // ── Local inference ──────────────────────────────────────────\n {\n id: \"ollama\",\n label: \"Ollama\",\n group: \"local\",\n kind: \"ollama\",\n baseUrl: \"http://localhost:11434/v1\",\n needsKey: false,\n },\n {\n id: \"lmstudio\",\n label: \"LM Studio\",\n group: \"local\",\n kind: \"openai\",\n baseUrl: \"http://localhost:1234/v1\",\n needsKey: false,\n },\n {\n id: \"vllm\",\n label: \"vLLM\",\n group: \"local\",\n kind: \"openai\",\n baseUrl: \"http://localhost:8000/v1\",\n needsKey: false,\n },\n // ── Local agent CLI (reuse your logged-in CLI, no API key; §5.14.8) ──\n {\n id: \"claude-code\",\n label: \"Claude Code\",\n group: \"agent-cli\",\n kind: \"cli\",\n baseUrl: \"\",\n needsKey: false,\n cliFlavor: \"claude-code\",\n modelHint: \"sonnet / claude-sonnet-4-6\",\n },\n {\n id: \"codex\",\n label: \"OpenAI Codex\",\n group: \"agent-cli\",\n kind: \"cli\",\n baseUrl: \"\",\n needsKey: false,\n cliFlavor: \"codex\",\n modelHint: \"gpt-5.5\",\n },\n // ── Custom ───────────────────────────────────────────────────\n {\n id: \"custom\",\n label: \"Custom (OpenAI-compatible)\",\n group: \"custom\",\n kind: \"openai\",\n baseUrl: \"\",\n needsKey: true,\n },\n {\n id: \"custom-anthropic\",\n label: \"Custom (Anthropic-compatible)\",\n group: \"custom\",\n kind: \"anthropic\",\n baseUrl: \"\",\n needsKey: true,\n },\n];\n\nconst FALLBACK = AI_VENDORS.find((v) => v.id === \"custom\")!;\n\nexport function aiVendorById(id: string): AiVendorPreset {\n return AI_VENDORS.find((v) => v.id === id) ?? FALLBACK;\n}\n\n/** Vendors in stable optgroup order for the settings