Skip to content

Commit 3f9ef41

Browse files
Siri-Raylefarcen
authored andcommitted
feat: refresh provider presets and models (#281)
* feat: refresh provider presets and models * fix: preserve provider base path during verification
1 parent 7252de7 commit 3f9ef41

17 files changed

Lines changed: 787 additions & 402 deletions

File tree

β€Žapps/api/src/lib/config-generator.tsβ€Ž

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,49 @@ const byokDefaultBaseUrls: Record<string, string> = {
9191
anthropic: "https://api.anthropic.com/v1",
9292
openai: "https://api.openai.com/v1",
9393
google: "https://generativelanguage.googleapis.com/v1beta/openai",
94+
siliconflow: "https://api.siliconflow.com/v1",
95+
ppio: "https://api.ppinfra.com/v3/openai",
96+
openrouter: "https://openrouter.ai/api/v1",
97+
minimax: "https://api.minimaxi.com/anthropic",
98+
kimi: "https://api.moonshot.cn/v1",
99+
glm: "https://open.bigmodel.cn/api/paas/v4",
100+
moonshot: "https://api.moonshot.cn/v1",
101+
zai: "https://open.bigmodel.cn/api/paas/v4",
94102
};
95103

104+
function resolveOpenClawProviderId(providerId: string): string {
105+
switch (providerId) {
106+
case "kimi":
107+
return "moonshot";
108+
case "glm":
109+
return "zai";
110+
default:
111+
return providerId;
112+
}
113+
}
114+
115+
function resolveOpenClawProviderApi(providerId: string): string {
116+
switch (resolveOpenClawProviderId(providerId)) {
117+
case "minimax":
118+
return "anthropic-messages";
119+
default:
120+
return "openai-completions";
121+
}
122+
}
123+
124+
function resolveOpenClawProviderAuthHeader(
125+
providerId: string,
126+
): boolean | undefined {
127+
return resolveOpenClawProviderId(providerId) === "minimax" ? true : undefined;
128+
}
129+
96130
function isByokProviderProxied(
97131
providerId: string,
98132
baseUrl: string | null,
99133
): boolean {
134+
const openclawProviderId = resolveOpenClawProviderId(providerId);
100135
const defaultBaseUrl = normalizeProviderBaseUrl(
101-
byokDefaultBaseUrls[providerId],
136+
byokDefaultBaseUrls[openclawProviderId],
102137
);
103138
const normalizedBaseUrl = normalizeProviderBaseUrl(baseUrl);
104139

@@ -112,22 +147,24 @@ function getByokProviderKey(input: {
112147
providerId: string;
113148
baseUrl: string | null;
114149
}): string {
150+
const openclawProviderId = resolveOpenClawProviderId(input.providerId);
115151
if (input.providerId === "custom") {
116152
return `custom_${input.id}`;
117153
}
118154

119155
return isByokProviderProxied(input.providerId, input.baseUrl)
120-
? `byok_${input.providerId}`
121-
: input.providerId;
156+
? `byok_${openclawProviderId}`
157+
: openclawProviderId;
122158
}
123159

124160
function getByokProviderModelId(
125161
providerKey: string,
126162
providerId: string,
127163
modelId: string,
128164
): string {
129-
return providerKey === `byok_${providerId}`
130-
? `${providerId}/${modelId}`
165+
const openclawProviderId = resolveOpenClawProviderId(providerId);
166+
return providerKey === `byok_${openclawProviderId}`
167+
? `${openclawProviderId}/${modelId}`
131168
: modelId;
132169
}
133170

@@ -221,13 +258,16 @@ export async function generatePoolConfig(
221258
// Map provider ID prefix (e.g. "anthropic") β†’ OpenClaw provider key
222259
// For proxied providers the key is "byok_anthropic", for direct it's "anthropic"
223260
const byokPrefixToKey = new Map<string, string>();
261+
const byokPrefixToProvider = new Map<string, string>();
224262
for (const bp of byokProviders) {
263+
const openclawProviderId = resolveOpenClawProviderId(bp.providerId);
225264
const key = getByokProviderKey({
226265
id: bp.id,
227266
providerId: bp.providerId,
228267
baseUrl: bp.baseUrl,
229268
});
230269
byokPrefixToKey.set(bp.providerId, key);
270+
byokPrefixToProvider.set(bp.providerId, openclawProviderId);
231271
}
232272

233273
// Prefix model ID with provider namespace for routing.
@@ -241,16 +281,19 @@ export async function generatePoolConfig(
241281
const slashIdx = rawModelId.indexOf("/");
242282
if (slashIdx > 0) {
243283
const prefix = rawModelId.substring(0, slashIdx);
284+
const suffix = rawModelId.substring(slashIdx + 1);
244285
const byokKey = byokPrefixToKey.get(prefix);
245-
if (byokKey) {
286+
const openclawProviderId = byokPrefixToProvider.get(prefix);
287+
if (byokKey && openclawProviderId) {
246288
if (prefix === "custom") {
247289
const upstreamModelId = rawModelId.substring("custom/".length);
248290
return `${byokKey}/${upstreamModelId}`;
249291
}
250292

251-
// Proxied: "anthropic/claude-sonnet-4" β†’ "byok_anthropic/anthropic/claude-sonnet-4"
252-
// Direct: "anthropic/claude-sonnet-4" β†’ "anthropic/claude-sonnet-4" (unchanged)
253-
return byokKey === prefix ? rawModelId : `${byokKey}/${rawModelId}`;
293+
const providerScopedModelId = `${openclawProviderId}/${suffix}`;
294+
return byokKey === openclawProviderId
295+
? providerScopedModelId
296+
: `${byokKey}/${providerScopedModelId}`;
254297
}
255298
}
256299
if (hasLitellm) return `litellm/${rawModelId}`;
@@ -614,8 +657,9 @@ export async function generatePoolConfig(
614657

615658
// Add BYOK (user-provided) providers (already loaded above for resolveModelId)
616659
for (const bp of byokProviders) {
660+
const openclawProviderId = resolveOpenClawProviderId(bp.providerId);
617661
const baseUrl = normalizeProviderBaseUrl(
618-
bp.baseUrl ?? byokDefaultBaseUrls[bp.providerId],
662+
bp.baseUrl ?? byokDefaultBaseUrls[openclawProviderId],
619663
);
620664
const providerKey = getByokProviderKey({
621665
id: bp.id,
@@ -628,7 +672,10 @@ export async function generatePoolConfig(
628672
const byokProvider = {
629673
baseUrl,
630674
apiKey: decrypt(bp.encryptedApiKey),
631-
api: "openai-completions",
675+
api: resolveOpenClawProviderApi(bp.providerId),
676+
...(resolveOpenClawProviderAuthHeader(bp.providerId)
677+
? { authHeader: true }
678+
: {}),
632679
models: modelIds.map((id) => ({
633680
id: getByokProviderModelId(providerKey, bp.providerId, id),
634681
name: id,

β€Žapps/api/src/lib/models.tsβ€Ž

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ export const PLATFORM_MODELS: Model[] = [
1515
description: "Most capable - ideal for complex tasks",
1616
},
1717
{
18-
id: "openai/gpt-4o",
19-
name: "GPT-4o",
18+
id: "openai/gpt-5.1",
19+
name: "GPT-5.1",
2020
provider: "openai",
21-
description: "OpenAI flagship model",
21+
description: "OpenAI flagship model for coding and agentic tasks",
2222
},
2323
{
24-
id: "openai/gpt-4o-mini",
25-
name: "GPT-4o Mini",
24+
id: "openai/gpt-5-mini",
25+
name: "GPT-5 mini",
2626
provider: "openai",
27-
description: "Lightweight and fast for simple tasks",
27+
description: "Faster, lower-cost GPT-5 model for well-defined tasks",
2828
},
2929
];
3030

β€Žapps/api/src/routes/model-routes.tsβ€Ž

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ const PROVIDER_BASE_URLS: Record<string, string> = {
214214
anthropic: "https://api.anthropic.com/v1",
215215
openai: "https://api.openai.com/v1",
216216
google: "https://generativelanguage.googleapis.com/v1beta/openai",
217+
siliconflow: "https://api.siliconflow.com/v1",
218+
ppio: "https://api.ppinfra.com/v3/openai",
219+
openrouter: "https://openrouter.ai/api/v1",
220+
minimax: "https://api.minimaxi.com/anthropic",
221+
kimi: "https://api.moonshot.cn/v1",
222+
glm: "https://open.bigmodel.cn/api/paas/v4",
223+
moonshot: "https://api.moonshot.cn/v1",
224+
zai: "https://open.bigmodel.cn/api/paas/v4",
217225
};
218226

219227
function getVerifyUrl(providerId: string, baseUrl?: string | null): string {
@@ -347,6 +355,14 @@ export function registerModelRoutes(app: OpenAPIHono<AppBindings>) {
347355
anthropic: "Anthropic",
348356
openai: "OpenAI",
349357
google: "Google AI",
358+
siliconflow: "SiliconFlow",
359+
ppio: "PPIO",
360+
openrouter: "OpenRouter",
361+
minimax: "MiniMax",
362+
kimi: "Kimi",
363+
glm: "GLM",
364+
moonshot: "Kimi",
365+
zai: "GLM",
350366
custom: "Custom",
351367
}[providerId] ??
352368
providerId;

β€Žapps/controller/src/lib/openclaw-config-compiler.tsβ€Ž

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,42 @@ const BYOK_DEFAULT_BASE_URLS: Record<string, string> = {
1212
anthropic: "https://api.anthropic.com/v1",
1313
openai: "https://api.openai.com/v1",
1414
google: "https://generativelanguage.googleapis.com/v1beta/openai",
15+
siliconflow: "https://api.siliconflow.com/v1",
16+
ppio: "https://api.ppinfra.com/v3/openai",
17+
openrouter: "https://openrouter.ai/api/v1",
18+
minimax: "https://api.minimaxi.com/anthropic",
19+
kimi: "https://api.moonshot.cn/v1",
20+
glm: "https://open.bigmodel.cn/api/paas/v4",
21+
moonshot: "https://api.moonshot.cn/v1",
22+
zai: "https://open.bigmodel.cn/api/paas/v4",
1523
};
1624

25+
function resolveOpenClawProviderId(providerId: string): string {
26+
switch (providerId) {
27+
case "kimi":
28+
return "moonshot";
29+
case "glm":
30+
return "zai";
31+
default:
32+
return providerId;
33+
}
34+
}
35+
36+
function resolveOpenClawProviderApi(providerId: string): string {
37+
switch (resolveOpenClawProviderId(providerId)) {
38+
case "minimax":
39+
return "anthropic-messages";
40+
default:
41+
return "openai-completions";
42+
}
43+
}
44+
45+
function resolveOpenClawProviderAuthHeader(
46+
providerId: string,
47+
): boolean | undefined {
48+
return resolveOpenClawProviderId(providerId) === "minimax" ? true : undefined;
49+
}
50+
1751
function isDesktopCloudConfig(value: unknown): value is {
1852
linkUrl: string;
1953
apiKey: string;
@@ -42,8 +76,9 @@ function isByokProviderProxied(
4276
providerId: string,
4377
baseUrl: string | null,
4478
): boolean {
79+
const openclawProviderId = resolveOpenClawProviderId(providerId);
4580
const defaultBaseUrl = normalizeProviderBaseUrl(
46-
BYOK_DEFAULT_BASE_URLS[providerId],
81+
BYOK_DEFAULT_BASE_URLS[openclawProviderId],
4782
);
4883
const normalizedBaseUrl = normalizeProviderBaseUrl(baseUrl);
4984

@@ -57,22 +92,24 @@ function getByokProviderKey(input: {
5792
providerId: string;
5893
baseUrl: string | null;
5994
}): string {
95+
const openclawProviderId = resolveOpenClawProviderId(input.providerId);
6096
if (input.providerId === "custom") {
6197
return `custom_${input.id}`;
6298
}
6399

64100
return isByokProviderProxied(input.providerId, input.baseUrl)
65-
? `byok_${input.providerId}`
66-
: input.providerId;
101+
? `byok_${openclawProviderId}`
102+
: openclawProviderId;
67103
}
68104

69105
function getByokProviderModelId(
70106
providerKey: string,
71107
providerId: string,
72108
modelId: string,
73109
): string {
74-
return providerKey === `byok_${providerId}`
75-
? `${providerId}/${modelId}`
110+
const openclawProviderId = resolveOpenClawProviderId(providerId);
111+
return providerKey === `byok_${openclawProviderId}`
112+
? `${openclawProviderId}/${modelId}`
76113
: modelId;
77114
}
78115

@@ -107,9 +144,10 @@ function compileModelsConfig(config: NexuConfig): OpenClawConfig["models"] {
107144
providerId: provider.providerId,
108145
baseUrl: provider.baseUrl,
109146
});
147+
const openclawProviderId = resolveOpenClawProviderId(provider.providerId);
110148
const baseUrl =
111149
normalizeProviderBaseUrl(
112-
provider.baseUrl ?? BYOK_DEFAULT_BASE_URLS[provider.providerId],
150+
provider.baseUrl ?? BYOK_DEFAULT_BASE_URLS[openclawProviderId],
113151
) ?? normalizeProviderBaseUrl(BYOK_DEFAULT_BASE_URLS.openai);
114152

115153
if (baseUrl === null) {
@@ -119,7 +157,10 @@ function compileModelsConfig(config: NexuConfig): OpenClawConfig["models"] {
119157
providers[providerKey] = {
120158
baseUrl,
121159
apiKey: provider.apiKey ?? "",
122-
api: "openai-completions",
160+
api: resolveOpenClawProviderApi(provider.providerId),
161+
...(resolveOpenClawProviderAuthHeader(provider.providerId)
162+
? { authHeader: true }
163+
: {}),
123164
models: provider.models.map((modelId) =>
124165
buildModelEntry(
125166
getByokProviderModelId(providerKey, provider.providerId, modelId),
@@ -157,7 +198,9 @@ function resolveModelId(config: NexuConfig, rawModelId: string): string {
157198
}
158199

159200
const byokPrefixToKey = new Map<string, string>();
201+
const byokPrefixToProvider = new Map<string, string>();
160202
for (const provider of config.providers.filter((item) => item.enabled)) {
203+
const openclawProviderId = resolveOpenClawProviderId(provider.providerId);
161204
byokPrefixToKey.set(
162205
provider.providerId,
163206
getByokProviderKey({
@@ -166,14 +209,20 @@ function resolveModelId(config: NexuConfig, rawModelId: string): string {
166209
baseUrl: provider.baseUrl,
167210
}),
168211
);
212+
byokPrefixToProvider.set(provider.providerId, openclawProviderId);
169213
}
170214

171215
const slashIndex = rawModelId.indexOf("/");
172216
if (slashIndex > 0) {
173217
const prefix = rawModelId.slice(0, slashIndex);
218+
const modelSuffix = rawModelId.slice(slashIndex + 1);
174219
const byokKey = byokPrefixToKey.get(prefix);
175-
if (byokKey) {
176-
return byokKey === prefix ? rawModelId : `${byokKey}/${rawModelId}`;
220+
const openclawProviderId = byokPrefixToProvider.get(prefix);
221+
if (byokKey && openclawProviderId) {
222+
const providerScopedModelId = `${openclawProviderId}/${modelSuffix}`;
223+
return byokKey === openclawProviderId
224+
? providerScopedModelId
225+
: `${byokKey}/${providerScopedModelId}`;
177226
}
178227
}
179228

β€Žapps/controller/src/services/model-provider-service.tsβ€Ž

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,27 @@ const PROVIDER_BASE_URLS: Record<string, string> = {
1010
anthropic: "https://api.anthropic.com/v1",
1111
openai: "https://api.openai.com/v1",
1212
google: "https://generativelanguage.googleapis.com/v1beta/openai",
13+
siliconflow: "https://api.siliconflow.com/v1",
14+
ppio: "https://api.ppinfra.com/v3/openai",
15+
openrouter: "https://openrouter.ai/api/v1",
16+
minimax: "https://api.minimaxi.com/anthropic",
17+
kimi: "https://api.moonshot.cn/v1",
18+
glm: "https://open.bigmodel.cn/api/paas/v4",
19+
moonshot: "https://api.moonshot.cn/v1",
20+
zai: "https://open.bigmodel.cn/api/paas/v4",
1321
};
1422

1523
function buildProviderUrl(
1624
baseUrl: string | null | undefined,
17-
pathname: string,
25+
path: string,
1826
): string | null {
1927
if (!baseUrl || baseUrl.trim().length === 0) {
2028
return null;
2129
}
2230

23-
return new URL(
24-
pathname,
25-
baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`,
26-
).toString();
31+
const normalizedBaseUrl = baseUrl.trim().replace(/\/+$/, "");
32+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
33+
return `${normalizedBaseUrl}${normalizedPath}`;
2734
}
2835

2936
type VerifyProviderBody = z.infer<typeof verifyProviderBodySchema>;
5.09 KB
Loading
27.5 KB
Loading
5.01 KB
Loading
3.91 KB
Loading
1.47 KB
Loading

0 commit comments

Comments
Β (0)