From 68df60bd4a61833fe022c932af618f66cbc78601 Mon Sep 17 00:00:00 2001 From: Max <6111715+cmzz@users.noreply.github.com> Date: Fri, 10 Apr 2026 21:47:18 +0800 Subject: [PATCH] feat(provider): add TheRouter presets for OpenCode and OpenClaw (#1892) Co-authored-by: max --- src/config/openclawProviderPresets.ts | 66 +++++++++++++++++++ src/config/opencodeProviderPresets.ts | 31 +++++++++ .../therouterOpenCodeOpenClawPresets.test.ts | 50 ++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 tests/config/therouterOpenCodeOpenClawPresets.test.ts diff --git a/src/config/openclawProviderPresets.ts b/src/config/openclawProviderPresets.ts index cf9ca37c..965c4a6a 100644 --- a/src/config/openclawProviderPresets.ts +++ b/src/config/openclawProviderPresets.ts @@ -719,6 +719,72 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [ }, }, }, + { + name: "TheRouter", + websiteUrl: "https://therouter.ai", + apiKeyUrl: "https://dashboard.therouter.ai", + settingsConfig: { + baseUrl: "https://api.therouter.ai/v1", + apiKey: "", + api: "openai-completions", + models: [ + { + id: "anthropic/claude-sonnet-4.6", + name: "Claude Sonnet 4.6", + contextWindow: 1000000, + cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, + }, + { + id: "openai/gpt-5.3-codex", + name: "GPT-5.3 Codex", + contextWindow: 400000, + cost: { input: 5, output: 40, cacheRead: 0.5 }, + }, + { + id: "openai/gpt-5.2", + name: "GPT-5.2", + contextWindow: 400000, + cost: { input: 1.75, output: 14, cacheRead: 0.175 }, + }, + { + id: "google/gemini-3-flash-preview", + name: "Gemini 3 Flash Preview", + contextWindow: 1000000, + cost: { input: 0.5, output: 3, cacheRead: 0.05 }, + }, + { + id: "qwen/qwen3-coder-480b", + name: "Qwen3 Coder 480B", + contextWindow: 262144, + cost: { input: 0.6, output: 2.35 }, + }, + ], + }, + category: "aggregator", + templateValues: { + apiKey: { + label: "API Key", + placeholder: "sk-...", + editorValue: "", + }, + }, + suggestedDefaults: { + model: { + primary: "therouter/anthropic/claude-sonnet-4.6", + fallbacks: [ + "therouter/openai/gpt-5.2", + "therouter/google/gemini-3-flash-preview", + ], + }, + modelCatalog: { + "therouter/anthropic/claude-sonnet-4.6": { alias: "Sonnet" }, + "therouter/openai/gpt-5.2": { alias: "GPT-5.2" }, + "therouter/google/gemini-3-flash-preview": { alias: "Gemini Flash" }, + "therouter/openai/gpt-5.3-codex": { alias: "Codex" }, + "therouter/qwen/qwen3-coder-480b": { alias: "Qwen Coder" }, + }, + }, + }, { name: "ModelScope", websiteUrl: "https://modelscope.cn", diff --git a/src/config/opencodeProviderPresets.ts b/src/config/opencodeProviderPresets.ts index 18a830fc..9696ad08 100644 --- a/src/config/opencodeProviderPresets.ts +++ b/src/config/opencodeProviderPresets.ts @@ -879,6 +879,37 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [ }, }, }, + { + name: "TheRouter", + websiteUrl: "https://therouter.ai", + apiKeyUrl: "https://dashboard.therouter.ai", + settingsConfig: { + npm: "@ai-sdk/openai-compatible", + name: "TheRouter", + options: { + baseURL: "https://api.therouter.ai/v1", + apiKey: "", + setCacheKey: true, + }, + models: { + "anthropic/claude-sonnet-4.6": { name: "Claude Sonnet 4.6" }, + "openai/gpt-5.3-codex": { name: "GPT-5.3 Codex" }, + "openai/gpt-5.2": { name: "GPT-5.2" }, + "google/gemini-3-flash-preview": { + name: "Gemini 3 Flash Preview", + }, + "qwen/qwen3-coder-480b": { name: "Qwen3 Coder 480B" }, + }, + }, + category: "aggregator", + templateValues: { + apiKey: { + label: "API Key", + placeholder: "sk-...", + editorValue: "", + }, + }, + }, { name: "Novita AI", websiteUrl: "https://novita.ai", diff --git a/tests/config/therouterOpenCodeOpenClawPresets.test.ts b/tests/config/therouterOpenCodeOpenClawPresets.test.ts new file mode 100644 index 00000000..29279f9d --- /dev/null +++ b/tests/config/therouterOpenCodeOpenClawPresets.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import { opencodeProviderPresets } from "@/config/opencodeProviderPresets"; +import { openclawProviderPresets } from "@/config/openclawProviderPresets"; + +describe("TheRouter OpenCode and OpenClaw presets", () => { + it("uses OpenAI-compatible config for OpenCode", () => { + const preset = opencodeProviderPresets.find((item) => item.name === "TheRouter"); + const models = preset?.settingsConfig.models ?? {}; + + 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?.settingsConfig.npm).toBe("@ai-sdk/openai-compatible"); + expect(preset?.settingsConfig.options?.baseURL).toBe( + "https://api.therouter.ai/v1", + ); + expect(preset?.settingsConfig.options?.setCacheKey).toBe(true); + expect(models).toHaveProperty("openai/gpt-5.3-codex"); + expect(models).toHaveProperty("anthropic/claude-sonnet-4.6"); + expect(models).toHaveProperty("google/gemini-3-flash-preview"); + }); + + it("uses OpenAI completions config for OpenClaw", () => { + const preset = openclawProviderPresets.find((item) => item.name === "TheRouter"); + const modelIds = (preset?.settingsConfig.models ?? []).map((model) => model.id); + + 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?.settingsConfig.baseUrl).toBe("https://api.therouter.ai/v1"); + expect(preset?.settingsConfig.api).toBe("openai-completions"); + expect(modelIds).toEqual( + expect.arrayContaining([ + "anthropic/claude-sonnet-4.6", + "openai/gpt-5.3-codex", + "openai/gpt-5.2", + "google/gemini-3-flash-preview", + ]), + ); + expect(preset?.suggestedDefaults?.model).toEqual({ + primary: "therouter/anthropic/claude-sonnet-4.6", + fallbacks: [ + "therouter/openai/gpt-5.2", + "therouter/google/gemini-3-flash-preview", + ], + }); + }); +});