fix(kimi): use stable coding model id

This commit is contained in:
Peter Steinberger
2026-05-10 06:17:52 +01:00
parent e65017b0f9
commit 53cd3daed3
10 changed files with 70 additions and 25 deletions

View File

@@ -65,6 +65,7 @@ Docs: https://docs.openclaw.ai
- Agents/Anthropic: report 1M session context for Claude Opus/Sonnet 4 models even when local model config still advertises 200k, matching model discovery and preventing premature status/UI overflow. Fixes #66766.
- Models/OpenRouter: hide missing-auth direct provider rows in `/model status` when they are only duplicated by a nested OpenRouter model id such as `openrouter/google/...`, while preserving explicitly configured direct providers. Fixes #62317.
- Models: preserve an explicitly selected provider/model such as `opencode-go/deepseek-v4-pro` when another provider owns the same bare model alias. Fixes #79325.
- 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.
- 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.
- 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.

View File

@@ -301,7 +301,7 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
| Groq | `groq` | `GROQ_API_KEY` | - |
| Hugging Face Inference | `huggingface` | `HUGGINGFACE_HUB_TOKEN` or `HF_TOKEN` | `huggingface/deepseek-ai/DeepSeek-R1` |
| Kilo Gateway | `kilocode` | `KILOCODE_API_KEY` | `kilocode/kilo/auto` |
| Kimi Coding | `kimi` | `KIMI_API_KEY` or `KIMICODE_API_KEY` | `kimi/kimi-code` |
| Kimi Coding | `kimi` | `KIMI_API_KEY` or `KIMICODE_API_KEY` | `kimi/kimi-for-coding` |
| MiniMax | `minimax` / `minimax-portal` | `MINIMAX_API_KEY` / `MINIMAX_OAUTH_TOKEN` | `minimax/MiniMax-M2.7` |
| Mistral | `mistral` | `MISTRAL_API_KEY` | `mistral/mistral-large-latest` |
| Moonshot | `moonshot` | `MOONSHOT_API_KEY` | `moonshot/kimi-k2.6` |
@@ -394,18 +394,18 @@ Kimi Coding uses Moonshot AI's Anthropic-compatible endpoint:
- Provider: `kimi`
- Auth: `KIMI_API_KEY`
- Example model: `kimi/kimi-code`
- Example model: `kimi/kimi-for-coding`
```json5
{
env: { KIMI_API_KEY: "sk-..." },
agents: {
defaults: { model: { primary: "kimi/kimi-code" } },
defaults: { model: { primary: "kimi/kimi-for-coding" } },
},
}
```
Legacy `kimi/k2p5` remains accepted as a compatibility model id.
Legacy `kimi/kimi-code` and `kimi/k2p5` remain accepted as compatibility model ids and normalize to Kimi's stable API model id.
### Volcano Engine (Doubao)

View File

@@ -538,8 +538,8 @@ Interactive custom-provider onboarding infers image input for common vision mode
env: { KIMI_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "kimi/kimi-code" },
models: { "kimi/kimi-code": { alias: "Kimi Code" } },
model: { primary: "kimi/kimi-for-coding" },
models: { "kimi/kimi-for-coding": { alias: "Kimi Code" } },
},
},
}

View File

@@ -9,7 +9,7 @@ title: "Moonshot AI"
Moonshot provides the Kimi API with OpenAI-compatible endpoints. Configure the
provider and set the default model to `moonshot/kimi-k2.6`, or use
Kimi Coding with `kimi/kimi-code`.
Kimi Coding with `kimi/kimi-for-coding`.
<Warning>
Moonshot and Kimi Coding are **separate providers**. Keys are not interchangeable, endpoints differ, and model refs differ (`moonshot/...` vs `kimi/...`).
@@ -185,7 +185,7 @@ Choose your provider and follow the setup steps.
**Best for:** code-focused tasks via the Kimi Coding endpoint.
<Note>
Kimi Coding uses a different API key and provider prefix (`kimi/...`) than Moonshot (`moonshot/...`). Legacy model ref `kimi/k2p5` remains accepted as a compatibility id.
Kimi Coding uses a different API key and provider prefix (`kimi/...`) than Moonshot (`moonshot/...`). The stable API model ref is `kimi/kimi-for-coding`; legacy refs `kimi/kimi-code` and `kimi/k2p5` remain accepted and normalize to that API model id.
</Note>
<Steps>
@@ -199,7 +199,7 @@ Choose your provider and follow the setup steps.
{
agents: {
defaults: {
model: { primary: "kimi/kimi-code" },
model: { primary: "kimi/kimi-for-coding" },
},
},
}
@@ -219,9 +219,9 @@ Choose your provider and follow the setup steps.
env: { KIMI_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "kimi/kimi-code" },
model: { primary: "kimi/kimi-for-coding" },
models: {
"kimi/kimi-code": { alias: "Kimi" },
"kimi/kimi-for-coding": { alias: "Kimi" },
},
},
},

View File

@@ -2,5 +2,7 @@ export {
buildKimiCodingProvider,
KIMI_CODING_BASE_URL,
KIMI_CODING_DEFAULT_MODEL_ID,
KIMI_CODING_LEGACY_MODEL_IDS,
normalizeKimiCodingModelId,
} from "./provider-catalog.js";
export { KIMI_CODING_MODEL_REF, KIMI_MODEL_REF } from "./onboard.js";

View File

@@ -3,6 +3,23 @@ import { describe, expect, it } from "vitest";
import plugin from "./index.js";
describe("kimi provider plugin", () => {
it("normalizes legacy Kimi Code ids to the stable API model id", async () => {
const provider = await registerSingleProviderPlugin(plugin);
expect(
provider.normalizeResolvedModel?.({
provider: "kimi",
modelId: "kimi-code",
model: {
id: "kimi-code",
name: "Kimi Code",
provider: "kimi",
api: "anthropic-messages",
},
} as never),
).toMatchObject({ id: "kimi-for-coding" });
});
it("uses binary thinking with thinking off by default", async () => {
const provider = await registerSingleProviderPlugin(plugin);

View File

@@ -4,7 +4,7 @@ import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
import type { SecretInput } from "openclaw/plugin-sdk/secret-input";
import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import { applyKimiCodeConfig, KIMI_CODING_MODEL_REF } from "./onboard.js";
import { buildKimiCodingProvider } from "./provider-catalog.js";
import { buildKimiCodingProvider, normalizeKimiCodingModelId } from "./provider-catalog.js";
import { KIMI_REPLAY_POLICY } from "./replay-policy.js";
import { wrapKimiProviderStream } from "./stream.js";
@@ -96,6 +96,10 @@ export default definePluginEntry({
},
},
buildReplayPolicy: () => KIMI_REPLAY_POLICY,
normalizeResolvedModel: ({ model }) => {
const normalizedId = normalizeKimiCodingModelId(model.id);
return normalizedId === model.id ? undefined : { ...model, id: normalizedId };
},
resolveThinkingProfile: () => ({
levels: [
{ id: "off", label: "off" },

View File

@@ -9,7 +9,7 @@ import {
describe("kimi coding onboard", () => {
it("keeps the historical Kimi model ref alias pointed at the coding default", () => {
expect(KIMI_MODEL_REF).toBe("kimi/kimi-code");
expect(KIMI_MODEL_REF).toBe("kimi/kimi-for-coding");
expect(KIMI_CODING_MODEL_REF).toBe(KIMI_MODEL_REF);
});
@@ -21,7 +21,7 @@ describe("kimi coding onboard", () => {
api: "anthropic-messages",
baseUrl: "https://api.kimi.com/coding/",
});
expect(provider?.models?.map((model) => model.id)).toEqual(["kimi-code"]);
expect(provider?.models?.map((model) => model.id)).toEqual(["kimi-for-coding"]);
expect(cfg.agents?.defaults?.models?.[KIMI_MODEL_REF]?.alias).toBe("Kimi");
});

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { buildKimiCodingProvider } from "./provider-catalog.js";
import { buildKimiCodingProvider, normalizeKimiCodingModelId } from "./provider-catalog.js";
describe("kimi provider catalog", () => {
it("builds the bundled Kimi coding defaults", () => {
@@ -8,6 +8,16 @@ describe("kimi provider catalog", () => {
expect(provider.api).toBe("anthropic-messages");
expect(provider.baseUrl).toBe("https://api.kimi.com/coding/");
expect(provider.headers).toEqual({ "User-Agent": "claude-code/0.1.0" });
expect(provider.models.map((model) => model.id)).toEqual(["kimi-code", "k2p5"]);
expect(provider.models.map((model) => model.id)).toEqual([
"kimi-for-coding",
"kimi-code",
"k2p5",
]);
});
it("normalizes legacy Kimi coding model ids to the stable API model id", () => {
expect(normalizeKimiCodingModelId("kimi-code")).toBe("kimi-for-coding");
expect(normalizeKimiCodingModelId("k2p5")).toBe("kimi-for-coding");
expect(normalizeKimiCodingModelId("kimi-for-coding")).toBe("kimi-for-coding");
});
});

View File

@@ -1,9 +1,12 @@
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
import type {
ModelDefinitionConfig,
ModelProviderConfig,
} from "openclaw/plugin-sdk/provider-model-shared";
const KIMI_BASE_URL = "https://api.kimi.com/coding/";
const KIMI_CODING_USER_AGENT = "claude-code/0.1.0";
const KIMI_DEFAULT_MODEL_ID = "kimi-code";
const KIMI_LEGACY_MODEL_ID = "k2p5";
const KIMI_DEFAULT_MODEL_ID = "kimi-for-coding";
const KIMI_LEGACY_MODEL_IDS = ["kimi-code", "k2p5"] as const;
const KIMI_CODING_DEFAULT_CONTEXT_WINDOW = 262144;
const KIMI_CODING_DEFAULT_MAX_TOKENS = 32768;
const KIMI_CODING_DEFAULT_COST = {
@@ -12,6 +15,7 @@ const KIMI_CODING_DEFAULT_COST = {
cacheRead: 0,
cacheWrite: 0,
};
const KIMI_CODING_INPUT = ["text", "image"] satisfies NonNullable<ModelDefinitionConfig["input"]>;
export function buildKimiCodingProvider(): ModelProviderConfig {
return {
@@ -25,23 +29,30 @@ export function buildKimiCodingProvider(): ModelProviderConfig {
id: KIMI_DEFAULT_MODEL_ID,
name: "Kimi Code",
reasoning: true,
input: ["text", "image"],
input: [...KIMI_CODING_INPUT],
cost: KIMI_CODING_DEFAULT_COST,
contextWindow: KIMI_CODING_DEFAULT_CONTEXT_WINDOW,
maxTokens: KIMI_CODING_DEFAULT_MAX_TOKENS,
},
{
id: KIMI_LEGACY_MODEL_ID,
name: "Kimi Code (legacy model id)",
...KIMI_LEGACY_MODEL_IDS.map((id) => ({
id,
name: `Kimi Code (legacy ${id})`,
reasoning: true,
input: ["text", "image"],
input: [...KIMI_CODING_INPUT],
cost: KIMI_CODING_DEFAULT_COST,
contextWindow: KIMI_CODING_DEFAULT_CONTEXT_WINDOW,
maxTokens: KIMI_CODING_DEFAULT_MAX_TOKENS,
},
})),
],
};
}
export function normalizeKimiCodingModelId(modelId: string): string {
return KIMI_LEGACY_MODEL_IDS.includes(modelId as (typeof KIMI_LEGACY_MODEL_IDS)[number])
? KIMI_DEFAULT_MODEL_ID
: modelId;
}
export const KIMI_CODING_BASE_URL = KIMI_BASE_URL;
export const KIMI_CODING_DEFAULT_MODEL_ID = KIMI_DEFAULT_MODEL_ID;
export const KIMI_CODING_LEGACY_MODEL_IDS = KIMI_LEGACY_MODEL_IDS;