mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
Add SubRouter provider presets (#4522)
Co-authored-by: abingyyds <abingyyds@gmail.com>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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<string, string> = {
|
||||
relaxcode: _relaxcode,
|
||||
runapi: _runapi,
|
||||
shengsuanyun: _shengsuanyun,
|
||||
subrouter: _subrouter,
|
||||
sudocode: _sudocode,
|
||||
unity2: _unity2,
|
||||
};
|
||||
|
||||
@@ -140,6 +140,22 @@ export const iconMetadata: Record<string, IconMetadata> = {
|
||||
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",
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0d9488;stop-opacity:1" />
|
||||
<stop offset="50%" style="stop-color:#14b8a6;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#f59e0b;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="200" height="200" rx="40" fill="url(#grad1)"/>
|
||||
<g transform="translate(100,100)">
|
||||
<!-- Primary routing path: top-left to bottom-right -->
|
||||
<path d="M-50,-50 C-50,-10 -10,-10 0,0 C10,10 50,10 50,50"
|
||||
stroke="white" stroke-width="6" stroke-linecap="round" fill="none" opacity="0.95"/>
|
||||
<!-- Secondary routing path: top-right to bottom-left -->
|
||||
<path d="M50,-50 C50,-10 10,-10 0,0 C-10,10 -50,10 -50,50"
|
||||
stroke="white" stroke-width="6" stroke-linecap="round" fill="none" opacity="0.95"/>
|
||||
<!-- Central intersection node -->
|
||||
<circle cx="0" cy="0" r="10" fill="white" opacity="0.95"/>
|
||||
<!-- Endpoint dots -->
|
||||
<circle cx="-50" cy="-50" r="6" fill="white" opacity="0.7"/>
|
||||
<circle cx="50" cy="-50" r="6" fill="white" opacity="0.7"/>
|
||||
<circle cx="-50" cy="50" r="6" fill="white" opacity="0.7"/>
|
||||
<circle cx="50" cy="50" r="6" fill="white" opacity="0.7"/>
|
||||
<!-- Flow indicators -->
|
||||
<circle cx="-28" cy="-28" r="3.5" fill="white" opacity="0.5"/>
|
||||
<circle cx="28" cy="-28" r="3.5" fill="white" opacity="0.5"/>
|
||||
<circle cx="-28" cy="28" r="3.5" fill="white" opacity="0.5"/>
|
||||
<circle cx="28" cy="28" r="3.5" fill="white" opacity="0.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -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<string, string> }).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<string, string> }).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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user