Skip to content

Commit c919702

Browse files
authored
fix(moonshot): resolve moonshotai direct model refs
Summary: - accept `moonshotai` and `moonshot-ai` as direct Moonshot provider aliases at runtime - expose both aliases in the Moonshot manifest catalog - add resolver/catalog regression coverage for #73876 Verification: - `pnpm test src/agents/model-selection.test.ts src/agents/pi-embedded-runner/model.test.ts src/model-catalog/manifest-planner.test.ts` - `pnpm test src/plugins/contracts/plugin-registration.moonshot.contract.test.ts` - `git diff --check` - `OPENCLAW_TESTBOX=0 pnpm check:changed`
1 parent 9dc5738 commit c919702

6 files changed

Lines changed: 163 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai
7777
- Models/OpenRouter: repair stale session overrides that lost the outer `openrouter/` provider wrapper, so sessions return to the configured OpenRouter model instead of failing as an unknown direct-provider model. Fixes #78161. Thanks @hjamal7-bit.
7878
- Telegram: show full provider/model labels for nested OpenRouter model ids in the model picker, so `openrouter/openai/gpt-5.4-mini` no longer displays as `openai/gpt-5.4-mini`. Fixes #67792. (#72752) Thanks @iot2edge.
7979
- Models/OpenRouter: preserve live `supported_parameters` tool support metadata so non-tool Perplexity Sonar models no longer receive agent tool payloads and fall back unnecessarily. Fixes #64175. Thanks @Catfish-75.
80+
- Models/Moonshot: accept direct `moonshotai/...` and `moonshot-ai/...` refs as aliases for canonical `moonshot/...`, so copied OpenRouter Kimi ids no longer fail as unknown direct models. Fixes #73876. (#74946) Thanks @jeffrey701.
8081
- Kimi Code: use Kimi's stable `kimi-for-coding` API model id in bundled catalog, onboarding, and docs while normalizing legacy `kimi-code` and `k2p5` refs. Fixes #79965.
8182
- Volcengine/Kimi: strip provider-unsupported tool schema length and item constraint keywords for direct and coding-plan models so hosted Kimi runs do not reject message tools with `minLength`. Fixes #38817.
8283
- DeepSeek: backfill V4 `reasoning_content` replay fields for unowned OpenAI-compatible proxy providers, preventing follow-up request failures outside the bundled DeepSeek and OpenRouter routes. Fixes #79608.

extensions/moonshot/openclaw.plugin.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@
3333
}
3434
},
3535
"modelCatalog": {
36+
"aliases": {
37+
"moonshotai": {
38+
"provider": "moonshot"
39+
},
40+
"moonshot-ai": {
41+
"provider": "moonshot"
42+
}
43+
},
3644
"providers": {
3745
"moonshot": {
3846
"baseUrl": "https://api.moonshot.ai/v1",

src/agents/model-selection.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ describe("model-selection", () => {
224224
expect(normalizeProviderId("qwen")).toBe("qwen");
225225
expect(normalizeProviderId("kimi-code")).toBe("kimi");
226226
expect(normalizeProviderId("kimi-coding")).toBe("kimi");
227+
expect(normalizeProviderId("MoonshotAI")).toBe("moonshot");
228+
expect(normalizeProviderId("moonshot-ai")).toBe("moonshot");
227229
expect(normalizeProviderId("anthropic-cli")).toBe("claude-cli");
228230
expect(normalizeProviderId("bedrock")).toBe("amazon-bedrock");
229231
expect(normalizeProviderId("aws-bedrock")).toBe("amazon-bedrock");

src/agents/pi-embedded-runner/model.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,59 @@ describe("resolveModel", () => {
10421042
});
10431043
});
10441044

1045+
it("resolves direct moonshotai refs through the Moonshot provider alias", () => {
1046+
const cfg = {
1047+
models: {
1048+
providers: {
1049+
moonshot: {
1050+
baseUrl: "https://api.moonshot.ai/v1",
1051+
api: "openai-completions",
1052+
models: [
1053+
{
1054+
...makeModel("kimi-k2.6"),
1055+
name: "Kimi K2.6",
1056+
input: ["text", "image"],
1057+
},
1058+
],
1059+
},
1060+
},
1061+
},
1062+
} as unknown as OpenClawConfig;
1063+
1064+
const result = resolveModelForTest("moonshotai", "kimi-k2.6", "/tmp/agent", cfg);
1065+
1066+
expect(result.error).toBeUndefined();
1067+
expectRecordFields(result.model, {
1068+
provider: "moonshot",
1069+
id: "kimi-k2.6",
1070+
api: "openai-completions",
1071+
baseUrl: "https://api.moonshot.ai/v1",
1072+
input: ["text", "image"],
1073+
});
1074+
});
1075+
1076+
it("resolves direct moonshot-ai refs through the Moonshot provider alias", () => {
1077+
const cfg = {
1078+
models: {
1079+
providers: {
1080+
moonshot: {
1081+
baseUrl: "https://api.moonshot.ai/v1",
1082+
api: "openai-completions",
1083+
models: [makeModel("kimi-k2.6")],
1084+
},
1085+
},
1086+
},
1087+
} as unknown as OpenClawConfig;
1088+
1089+
const result = resolveModelForTest("moonshot-ai", "kimi-k2.6", "/tmp/agent", cfg);
1090+
1091+
expect(result.error).toBeUndefined();
1092+
expectRecordFields(result.model, {
1093+
provider: "moonshot",
1094+
id: "kimi-k2.6",
1095+
});
1096+
});
1097+
10451098
it("does not treat arbitrary namespaced model ids as provider prefixes", () => {
10461099
const cfg = {
10471100
models: {

src/agents/provider-id.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export function normalizeProviderId(provider: string): string {
2020
if (normalized === "kimi" || normalized === "kimi-code" || normalized === "kimi-coding") {
2121
return "kimi";
2222
}
23+
if (normalized === "moonshotai" || normalized === "moonshot-ai") {
24+
return "moonshot";
25+
}
2326
if (normalized === "bedrock" || normalized === "aws-bedrock") {
2427
return "amazon-bedrock";
2528
}

src/model-catalog/manifest-planner.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,102 @@ describe("manifest model catalog planner", () => {
137137
expect(plan.rows[0]?.baseUrl).toBe("https://example.openai.azure.com/openai/v1");
138138
});
139139

140+
// Regression for https://github.com/openclaw/openclaw/issues/73876.
141+
// The user-facing complaint is that copying a model id from OpenRouter
142+
// (which uses "moonshotai/kimi-k2.6" as the org slug) and dropping the
143+
// "openrouter/" prefix to hit the direct API failed with "Unknown
144+
// model: moonshotai/kimi-k2.6". The OpenAI plugin already shipped the
145+
// alias pattern (azure-openai-responses → openai); applying it to the
146+
// moonshot manifest lets the org-slug name resolve to moonshot's
147+
// existing catalog without renaming the canonical provider id (which
148+
// would break operators whose configs already say "moonshot/...").
149+
it("plans moonshotai alias rows from the moonshot provider catalog", () => {
150+
const plan = planManifestModelCatalogRows({
151+
providerFilter: "moonshotai",
152+
registry: {
153+
plugins: [
154+
{
155+
id: "moonshot",
156+
providers: ["moonshot"],
157+
modelCatalog: {
158+
aliases: {
159+
moonshotai: {
160+
provider: "moonshot",
161+
},
162+
"moonshot-ai": {
163+
provider: "moonshot",
164+
},
165+
},
166+
discovery: {
167+
moonshot: "static",
168+
},
169+
providers: {
170+
moonshot: {
171+
api: "openai-completions",
172+
baseUrl: "https://api.moonshot.ai/v1",
173+
models: [{ id: "kimi-k2.6", name: "Kimi K2.6" }],
174+
},
175+
},
176+
},
177+
},
178+
],
179+
},
180+
});
181+
182+
expect(plan.entries).toEqual([
183+
expect.objectContaining({
184+
pluginId: "moonshot",
185+
provider: "moonshotai",
186+
discovery: "static",
187+
}),
188+
]);
189+
expect(plan.rows).toEqual([
190+
expect.objectContaining({
191+
provider: "moonshotai",
192+
id: "kimi-k2.6",
193+
ref: "moonshotai/kimi-k2.6",
194+
api: "openai-completions",
195+
baseUrl: "https://api.moonshot.ai/v1",
196+
}),
197+
]);
198+
});
199+
200+
it("plans moonshot-ai alias rows from the moonshot provider catalog", () => {
201+
const plan = planManifestModelCatalogRows({
202+
providerFilter: "moonshot-ai",
203+
registry: {
204+
plugins: [
205+
{
206+
id: "moonshot",
207+
providers: ["moonshot"],
208+
modelCatalog: {
209+
aliases: {
210+
"moonshot-ai": {
211+
provider: "moonshot",
212+
},
213+
},
214+
providers: {
215+
moonshot: {
216+
api: "openai-completions",
217+
baseUrl: "https://api.moonshot.ai/v1",
218+
models: [{ id: "kimi-k2.6", name: "Kimi K2.6" }],
219+
},
220+
},
221+
},
222+
},
223+
],
224+
},
225+
});
226+
227+
expect(plan.rows).toEqual([
228+
expect.objectContaining({
229+
provider: "moonshot-ai",
230+
id: "kimi-k2.6",
231+
ref: "moonshot-ai/kimi-k2.6",
232+
}),
233+
]);
234+
});
235+
140236
it("keeps alias provider rows out of unfiltered broad planning", () => {
141237
const plan = planManifestModelCatalogRows({
142238
registry: {

0 commit comments

Comments
 (0)