diff --git a/src/config/claudeProviderPresets.ts b/src/config/claudeProviderPresets.ts index 94f9f60e..96353e62 100644 --- a/src/config/claudeProviderPresets.ts +++ b/src/config/claudeProviderPresets.ts @@ -671,6 +671,24 @@ export const providerPresets: ProviderPreset[] = [ icon: "openrouter", iconColor: "#6566F1", }, + { + name: "TheRouter", + websiteUrl: "https://therouter.ai", + apiKeyUrl: "https://dashboard.therouter.ai", + settingsConfig: { + env: { + ANTHROPIC_BASE_URL: "https://api.therouter.ai", + ANTHROPIC_AUTH_TOKEN: "", + ANTHROPIC_API_KEY: "", + ANTHROPIC_MODEL: "anthropic/claude-sonnet-4.6", + ANTHROPIC_DEFAULT_HAIKU_MODEL: "anthropic/claude-haiku-4.5", + ANTHROPIC_DEFAULT_SONNET_MODEL: "anthropic/claude-sonnet-4.6", + ANTHROPIC_DEFAULT_OPUS_MODEL: "anthropic/claude-opus-4.6", + }, + }, + category: "aggregator", + endpointCandidates: ["https://api.therouter.ai"], + }, { name: "Novita AI", websiteUrl: "https://novita.ai", diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index dd0a687f..97a0375b 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -365,4 +365,17 @@ requires_openai_auth = true`, icon: "openrouter", iconColor: "#6566F1", }, + { + name: "TheRouter", + websiteUrl: "https://therouter.ai", + apiKeyUrl: "https://dashboard.therouter.ai", + auth: generateThirdPartyAuth(""), + config: generateThirdPartyConfig( + "therouter", + "https://api.therouter.ai/v1", + "openai/gpt-5.3-codex", + ), + endpointCandidates: ["https://api.therouter.ai/v1"], + category: "aggregator", + }, ]; diff --git a/src/config/geminiProviderPresets.ts b/src/config/geminiProviderPresets.ts index aaff2e25..4b7339b5 100644 --- a/src/config/geminiProviderPresets.ts +++ b/src/config/geminiProviderPresets.ts @@ -241,6 +241,22 @@ export const geminiProviderPresets: GeminiProviderPreset[] = [ icon: "openrouter", iconColor: "#6566F1", }, + { + name: "TheRouter", + websiteUrl: "https://therouter.ai", + apiKeyUrl: "https://dashboard.therouter.ai", + settingsConfig: { + env: { + GOOGLE_GEMINI_BASE_URL: "https://api.therouter.ai", + GEMINI_MODEL: "gemini-2.5-pro", + }, + }, + baseURL: "https://api.therouter.ai", + model: "gemini-2.5-pro", + description: "TheRouter", + category: "aggregator", + endpointCandidates: ["https://api.therouter.ai"], + }, { name: "自定义", websiteUrl: "", diff --git a/tests/config/therouterProviderPresets.test.ts b/tests/config/therouterProviderPresets.test.ts new file mode 100644 index 00000000..97b77a24 --- /dev/null +++ b/tests/config/therouterProviderPresets.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from "vitest"; +import { providerPresets } from "@/config/claudeProviderPresets"; +import { codexProviderPresets } from "@/config/codexProviderPresets"; +import { geminiProviderPresets } from "@/config/geminiProviderPresets"; + +describe("TheRouter provider presets", () => { + it("uses the Anthropic-compatible root endpoint for Claude", () => { + const preset = providerPresets.find((item) => item.name === "TheRouter"); + + expect(preset).toBeDefined(); + expect(preset?.websiteUrl).toBe("https://therouter.ai"); + expect(preset?.apiKeyUrl).toBe("https://dashboard.therouter.ai"); + expect(preset?.category).toBe("aggregator"); + expect(preset?.endpointCandidates).toEqual(["https://api.therouter.ai"]); + + const env = (preset?.settingsConfig as { env: Record }).env; + expect(env.ANTHROPIC_BASE_URL).toBe("https://api.therouter.ai"); + expect(env.ANTHROPIC_AUTH_TOKEN).toBe(""); + expect(env.ANTHROPIC_API_KEY).toBe(""); + expect(env.ANTHROPIC_MODEL).toBe("anthropic/claude-sonnet-4.6"); + expect(env.ANTHROPIC_DEFAULT_HAIKU_MODEL).toBe( + "anthropic/claude-haiku-4.5", + ); + expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe( + "anthropic/claude-sonnet-4.6", + ); + expect(env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe( + "anthropic/claude-opus-4.6", + ); + }); + + it("uses the OpenAI-compatible v1 endpoint for Codex", () => { + const preset = codexProviderPresets.find((item) => item.name === "TheRouter"); + + expect(preset).toBeDefined(); + expect(preset?.websiteUrl).toBe("https://therouter.ai"); + expect(preset?.apiKeyUrl).toBe("https://dashboard.therouter.ai"); + expect(preset?.category).toBe("aggregator"); + expect(preset?.endpointCandidates).toEqual([ + "https://api.therouter.ai/v1", + ]); + expect(preset?.auth).toEqual({ OPENAI_API_KEY: "" }); + expect(preset?.config).toContain('model_provider = "therouter"'); + expect(preset?.config).toContain('model = "openai/gpt-5.3-codex"'); + expect(preset?.config).toContain( + 'base_url = "https://api.therouter.ai/v1"', + ); + expect(preset?.config).toContain('wire_api = "responses"'); + }); + + it("uses the Gemini-native root endpoint for Gemini", () => { + const preset = geminiProviderPresets.find((item) => item.name === "TheRouter"); + + expect(preset).toBeDefined(); + expect(preset?.websiteUrl).toBe("https://therouter.ai"); + expect(preset?.apiKeyUrl).toBe("https://dashboard.therouter.ai"); + expect(preset?.category).toBe("aggregator"); + expect(preset?.endpointCandidates).toEqual(["https://api.therouter.ai"]); + expect(preset?.baseURL).toBe("https://api.therouter.ai"); + expect(preset?.model).toBe("gemini-2.5-pro"); + + const env = (preset?.settingsConfig as { env: Record }).env; + expect(env.GOOGLE_GEMINI_BASE_URL).toBe("https://api.therouter.ai"); + expect(env.GEMINI_MODEL).toBe("gemini-2.5-pro"); + }); +});