diff --git a/src/config/claudeDesktopProviderPresets.ts b/src/config/claudeDesktopProviderPresets.ts index a9daa0ce3..d248d3ca3 100644 --- a/src/config/claudeDesktopProviderPresets.ts +++ b/src/config/claudeDesktopProviderPresets.ts @@ -257,6 +257,19 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [ partnerPromotionKey: "ccsub", icon: "ccsub", }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + category: "aggregator", + baseUrl: "https://subrouter.ai", + mode: "direct", + apiFormat: "anthropic", + modelRoutes: passthroughRoutes(true), + isPartner: true, + partnerPromotionKey: "subrouter", + icon: "subrouter", + }, { name: "Unity2.ai", websiteUrl: "https://unity2.ai", diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 4797a2ca7..c308385f4 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -209,6 +209,21 @@ export const providerPresets: ProviderPreset[] = [ partnerPromotionKey: "ccsub", icon: "ccsub", }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + settingsConfig: { + env: { + ANTHROPIC_BASE_URL: "https://subrouter.ai", + ANTHROPIC_AUTH_TOKEN: "", + }, + }, + category: "aggregator", + isPartner: true, + partnerPromotionKey: "subrouter", + icon: "subrouter", + }, { name: "Unity2.ai", websiteUrl: "https://unity2.ai", diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index 1766615ac..b85a9c46f 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -226,6 +226,22 @@ export const codexProviderPresets: CodexProviderPreset[] = [ partnerPromotionKey: "ccsub", icon: "ccsub", }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + category: "aggregator", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "subrouter", + "https://subrouter.ai/v1", + "gpt-5.5", + ), + endpointCandidates: ["https://subrouter.ai/v1"], + isPartner: true, + partnerPromotionKey: "subrouter", + icon: "subrouter", + }, { name: "Unity2.ai", websiteUrl: "https://unity2.ai", diff --git a/src/config/geminiProviderPresets.ts b/src/config/geminiProviderPresets.ts index f16a30f43..3a2b2e971 100644 --- a/src/config/geminiProviderPresets.ts +++ b/src/config/geminiProviderPresets.ts @@ -88,6 +88,25 @@ export const geminiProviderPresets: GeminiProviderPreset[] = [ partnerPromotionKey: "unity2", icon: "unity2", }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + settingsConfig: { + env: { + GOOGLE_GEMINI_BASE_URL: "https://subrouter.ai/v1beta", + GEMINI_MODEL: "gemini-3.5-flash", + }, + }, + baseURL: "https://subrouter.ai/v1beta", + model: "gemini-3.5-flash", + description: "SubRouter", + category: "aggregator", + isPartner: true, + partnerPromotionKey: "subrouter", + endpointCandidates: ["https://subrouter.ai/v1beta"], + icon: "subrouter", + }, { name: "PackyCode", websiteUrl: "https://www.packyapi.com", diff --git a/src/config/hermesProviderPresets.ts b/src/config/hermesProviderPresets.ts index 922245bd8..6fbb58ae2 100644 --- a/src/config/hermesProviderPresets.ts +++ b/src/config/hermesProviderPresets.ts @@ -264,6 +264,31 @@ export const hermesProviderPresets: HermesProviderPreset[] = [ model: { default: "gpt-5.5", provider: "ccsub" }, }, }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + settingsConfig: { + name: "subrouter", + base_url: "https://subrouter.ai/v1", + api_key: "", + api_mode: "chat_completions", + models: [ + { + id: "gpt-5.5", + name: "GPT-5.5", + context_length: 400000, + }, + ], + }, + category: "aggregator", + isPartner: true, + partnerPromotionKey: "subrouter", + icon: "subrouter", + suggestedDefaults: { + model: { default: "gpt-5.5", provider: "subrouter" }, + }, + }, { name: "Unity2.ai", websiteUrl: "https://unity2.ai", diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index a85bc2af0..44a015e45 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -294,6 +294,42 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, }, }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + settingsConfig: { + baseUrl: "https://subrouter.ai/v1", + apiKey: "", + api: "openai-completions", + models: [ + { + id: "gpt-5.5", + name: "GPT-5.5", + contextWindow: 400000, + }, + ], + }, + category: "aggregator", + isPartner: true, + partnerPromotionKey: "subrouter", + icon: "subrouter", + templateValues: { + apiKey: { + label: "API Key", + placeholder: "", + editorValue: "", + }, + }, + suggestedDefaults: { + model: { + primary: "subrouter/gpt-5.5", + }, + modelCatalog: { + "subrouter/gpt-5.5": { alias: "GPT-5.5" }, + }, + }, + }, { name: "Unity2.ai", websiteUrl: "https://unity2.ai", diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index bbf1aa516..6088c6a0d 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -436,6 +436,34 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, }, }, + { + name: "SubRouter", + websiteUrl: "https://subrouter.ai", + apiKeyUrl: "https://subrouter.ai/console/token", + settingsConfig: { + npm: "@ai-sdk/openai-compatible", + name: "SubRouter", + options: { + baseURL: "https://subrouter.ai/v1", + apiKey: "", + setCacheKey: true, + }, + models: { + "gpt-5.5": { name: "GPT-5.5" }, + }, + }, + category: "aggregator", + isPartner: true, + partnerPromotionKey: "subrouter", + icon: "subrouter", + templateValues: { + apiKey: { + label: "API Key", + placeholder: "", + editorValue: "", + }, + }, + }, { name: "Unity2.ai", websiteUrl: "https://unity2.ai", diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index ff3c4911c..5b52d2b7b 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1022,6 +1022,7 @@ "volcengine_agentplan": "Volcengine Ark Coding Plan offers exclusive benefits for CC Switch users: subscribe via this link and new customers get 75% off for the first two months, plus an extra 5% off with the exclusive invite code 6J6FV5N2 — as low as ¥9.4/month!", "byteplus": "Register via this link to get 500,000 tokens of free inference quota per model.", "ccsub": "CCSub is a stable, affordable AI API relay — a drop-in replacement for Claude.ai & OpenAI subscriptions, with one key for all models at ~30% of direct API cost.", + "subrouter": "SubRouter is an AI relay aggregator that lets one key access multiple models and providers.", "unity2": "Unity2.ai offers exclusive benefits for CC Switch users: register via this link to get $2 in credits, plus another $10 for joining the official group — up to $12 in free credits!" }, "presets": { diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 1287fe1a0..c129bc413 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1022,6 +1022,7 @@ "volcengine_agentplan": "火山方舟 Coding Plan は CC Switch ユーザーに専属特典を提供しています:このリンクから方舟 Coding Plan を購読すると、新規のお客様は最初の 2 か月が 75% オフ、さらに専用招待コード 6J6FV5N2 で 5% オフが加算され、月額 9.4 元から!", "byteplus": "このリンクから登録すると、各モデルごとに50万トークンの無料推論クォータがもらえます。", "ccsub": "CCSub は安定・低価格の AI API リレーサービスです。Claude Code の公式サブスクリプションの代替として、1つのキーで全モデルを公式比約1/3のコストで利用できます。", + "subrouter": "SubRouter は、1つのキーで複数のモデルとプロバイダーにアクセスできる AI 中継アグリゲーターです。", "unity2": "Unity2.ai は CC Switch ユーザーに専属特典を提供しています:このリンクから登録すると $2 分のクレジット、公式グループ参加でさらに $10、最大 $12 の無料クレジットがもらえます!" }, "presets": { diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index 2184fdd6c..60e722202 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -994,6 +994,7 @@ "volcengine_agentplan": "火山方舟 Coding Plan 為 CC Switch 的使用者提供了專屬福利:透過此連結訂閱方舟 Coding Plan,新客戶首兩個月享 2.5 折優惠,再用專屬邀請碼 6J6FV5N2 領取獎勵疊加 9.5 折,低至 9.4 元/月!", "byteplus": "透過此連結註冊即可取得每個模型 50 萬 tokens 的免費推理額度。", "ccsub": "CCSub 是穩定、實惠的 AI API 中轉平台,Claude Code 官方訂閱的超強平替,一個 Key 覆蓋全部模型,價格約為官方 1/3。", + "subrouter": "SubRouter 是中轉站聚合平台,一個 Key 可接入多個模型與供應商。", "unity2": "Unity2.ai 為 CC Switch 使用者提供專屬福利:透過此連結註冊可領取 $2 餘額,加入官方群再送 $10,最高可領 $12 免費額度!" }, "presets": { diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index caff6ea5b..3a903250b 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1022,6 +1022,7 @@ "volcengine_agentplan": "火山方舟 Coding Plan 为 CC Switch 的用户提供了专属福利:通过此链接订阅方舟 Coding Plan,新客户首两个月享 2.5 折优惠,再用专属邀请码 6J6FV5N2 领取奖励叠加 9.5 折,低至 9.4 元/月!", "byteplus": "通过此链接注册即可获取每个模型50万tokens的免费推理额度。", "ccsub": "CCSub 是稳定、实惠的 AI API 中转平台,Claude Code 官方订阅的超强平替,一个 Key 覆盖全部模型,价格约为官方 1/3。", + "subrouter": "SubRouter 是中转站聚合平台,一个 Key 可接入多个模型与供应商。", "unity2": "Unity2.ai 为 CC Switch 用户提供专属福利:通过此链接注册可领取 $2 余额,加入官方群再送 $10,最高可领 $12 免费额度!" }, "presets": { diff --git a/src/icons/extracted/index.ts b/src/icons/extracted/index.ts index 14c3e6ae9..352855b55 100644 --- a/src/icons/extracted/index.ts +++ b/src/icons/extracted/index.ts @@ -18,6 +18,7 @@ import _pipellm from "./pipellm.png"; import _relaxcode from "./relaxcode.png"; import _runapi from "./runapi.jpg"; import _shengsuanyun from "./shengsuanyun.svg?url"; +import _subrouter from "./subrouter.svg?url"; import _sudocode from "./sudocode.png"; import _unity2 from "./unity2.png"; @@ -111,6 +112,7 @@ export const iconUrls: Record = { relaxcode: _relaxcode, runapi: _runapi, shengsuanyun: _shengsuanyun, + subrouter: _subrouter, sudocode: _sudocode, unity2: _unity2, }; diff --git a/src/icons/extracted/metadata.ts b/src/icons/extracted/metadata.ts index 0f68025f2..75e004dab 100644 --- a/src/icons/extracted/metadata.ts +++ b/src/icons/extracted/metadata.ts @@ -140,6 +140,22 @@ export const iconMetadata: Record = { keywords: ["ccsub", "aggregator", "relay", "claude", "codex", "gateway"], defaultColor: "#1E88E5", }, + subrouter: { + name: "subrouter", + displayName: "SubRouter", + category: "ai-provider", + keywords: [ + "subrouter", + "subrouter.ai", + "aggregator", + "relay", + "claude", + "codex", + "gemini", + "gateway", + ], + defaultColor: "#0D9488", + }, chatglm: { name: "chatglm", displayName: "chatglm", diff --git a/src/icons/extracted/subrouter.svg b/src/icons/extracted/subrouter.svg new file mode 100644 index 000000000..2c4395c35 --- /dev/null +++ b/src/icons/extracted/subrouter.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/config/subrouterProviderPresets.test.ts b/tests/config/subrouterProviderPresets.test.ts new file mode 100644 index 000000000..18ace893d --- /dev/null +++ b/tests/config/subrouterProviderPresets.test.ts @@ -0,0 +1,130 @@ +import { describe, expect, it } from "vitest"; +import { claudeDesktopProviderPresets } from "@/config/claudeDesktopProviderPresets"; +import { providerPresets } from "@/config/claudeProviderPresets"; +import { codexProviderPresets } from "@/config/codexProviderPresets"; +import { geminiProviderPresets } from "@/config/geminiProviderPresets"; +import { hermesProviderPresets } from "@/config/hermesProviderPresets"; +import { openclawProviderPresets } from "@/config/openclawProviderPresets"; +import { opencodeProviderPresets } from "@/config/opencodeProviderPresets"; +import { hasIcon } from "@/icons/extracted"; + +const WEBSITE_URL = "https://subrouter.ai"; +const API_KEY_URL = "https://subrouter.ai/console/token"; + +describe("SubRouter provider presets", () => { + it("uses the Anthropic-compatible root endpoint for Claude", () => { + const preset = providerPresets.find((item) => item.name === "SubRouter"); + + expect(preset).toBeDefined(); + expect(preset?.websiteUrl).toBe(WEBSITE_URL); + expect(preset?.apiKeyUrl).toBe(API_KEY_URL); + expect(preset?.category).toBe("aggregator"); + expect(preset?.isPartner).toBe(true); + expect(preset?.partnerPromotionKey).toBe("subrouter"); + expect(preset?.icon).toBe("subrouter"); + + const env = (preset?.settingsConfig as { env: Record }).env; + expect(env.ANTHROPIC_BASE_URL).toBe("https://subrouter.ai"); + expect(env.ANTHROPIC_AUTH_TOKEN).toBe(""); + }); + + it("uses the OpenAI-compatible v1 endpoint for Codex", () => { + const preset = codexProviderPresets.find( + (item) => item.name === "SubRouter", + ); + + expect(preset).toBeDefined(); + expect(preset?.websiteUrl).toBe(WEBSITE_URL); + expect(preset?.apiKeyUrl).toBe(API_KEY_URL); + expect(preset?.category).toBe("aggregator"); + expect(preset?.endpointCandidates).toEqual(["https://subrouter.ai/v1"]); + expect(preset?.auth).toEqual({ OPENAI_API_KEY: "" }); + expect(preset?.config).toContain('name = "subrouter"'); + expect(preset?.config).toContain('model = "gpt-5.5"'); + expect(preset?.config).toContain('base_url = "https://subrouter.ai/v1"'); + expect(preset?.config).toContain('wire_api = "responses"'); + }); + + it("uses the Gemini-compatible v1beta endpoint for Gemini", () => { + const preset = geminiProviderPresets.find( + (item) => item.name === "SubRouter", + ); + + expect(preset).toBeDefined(); + expect(preset?.baseURL).toBe("https://subrouter.ai/v1beta"); + expect(preset?.endpointCandidates).toEqual(["https://subrouter.ai/v1beta"]); + expect(preset?.model).toBe("gemini-3.5-flash"); + + const env = (preset?.settingsConfig as { env: Record }).env; + expect(env.GOOGLE_GEMINI_BASE_URL).toBe("https://subrouter.ai/v1beta"); + expect(env.GEMINI_MODEL).toBe("gemini-3.5-flash"); + }); + + it("uses OpenAI-compatible config for OpenCode", () => { + const preset = opencodeProviderPresets.find( + (item) => item.name === "SubRouter", + ); + + expect(preset).toBeDefined(); + expect(preset?.settingsConfig.npm).toBe("@ai-sdk/openai-compatible"); + expect(preset?.settingsConfig.options?.baseURL).toBe( + "https://subrouter.ai/v1", + ); + expect(preset?.settingsConfig.options?.apiKey).toBe(""); + expect(preset?.settingsConfig.models).toHaveProperty("gpt-5.5"); + }); + + it("uses OpenAI completions config for OpenClaw without hardcoded pricing", () => { + const preset = openclawProviderPresets.find( + (item) => item.name === "SubRouter", + ); + const [model] = preset?.settingsConfig.models ?? []; + + expect(preset).toBeDefined(); + expect(preset?.settingsConfig.baseUrl).toBe("https://subrouter.ai/v1"); + expect(preset?.settingsConfig.api).toBe("openai-completions"); + expect(model).toMatchObject({ + id: "gpt-5.5", + name: "GPT-5.5", + contextWindow: 400000, + }); + expect(model).not.toHaveProperty("cost"); + expect(preset?.suggestedDefaults?.model).toEqual({ + primary: "subrouter/gpt-5.5", + }); + }); + + it("uses chat completions config for Hermes", () => { + const preset = hermesProviderPresets.find( + (item) => item.name === "SubRouter", + ); + + expect(preset).toBeDefined(); + expect(preset?.settingsConfig).toMatchObject({ + name: "subrouter", + base_url: "https://subrouter.ai/v1", + api_key: "", + api_mode: "chat_completions", + }); + expect(preset?.suggestedDefaults?.model).toEqual({ + default: "gpt-5.5", + provider: "subrouter", + }); + }); + + it("uses direct Anthropic routing for Claude Desktop", () => { + const preset = claudeDesktopProviderPresets.find( + (item) => item.name === "SubRouter", + ); + + expect(preset).toBeDefined(); + expect(preset?.baseUrl).toBe("https://subrouter.ai"); + expect(preset?.mode).toBe("direct"); + expect(preset?.apiFormat).toBe("anthropic"); + expect(preset?.modelRoutes?.length).toBeGreaterThan(0); + }); + + it("registers the SubRouter provider icon", () => { + expect(hasIcon("subrouter")).toBe(true); + }); +});