fix: cover kimi tool choice normalization paths (#39854) (thanks @GeekCheyun)

This commit is contained in:
Peter Steinberger
2026-03-08 14:13:53 +00:00
parent 577fe57d37
commit c7bca64bf5
2 changed files with 36 additions and 27 deletions

View File

@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
- Mattermost replies: keep `root_id` pinned to the existing thread root when an agent replies inside a thread, while still using reply-target threading for top-level posts. (#27744) thanks @hnykda.
- Agents/failover: detect Amazon Bedrock `Too many tokens per day` quota errors as rate limits across fallback, cron retry, and memory embeddings while keeping context-window `too many tokens per request` errors out of the rate-limit lane. (#39377) Thanks @gambletan.
- Android/Play distribution: remove self-update, background location, `screen.record`, and background mic capture from the Android app, narrow the foreground service to `dataSync` only, and clean up the legacy `location.enabledMode=always` preference migration. (#39660) Thanks @obviyus.
- Kimi coding tool calls: normalize Anthropic `tool_choice` modes like `auto`, `none`, and `required` before sending Kimi coding anthropic-messages requests so tool calls keep working on `.7`. (#39854) Thanks @GeekCheyun.
## 2026.3.7

View File

@@ -804,37 +804,45 @@ describe("applyExtraParamsToAgent", () => {
});
it("normalizes anthropic tool_choice modes for kimi-coding endpoints", () => {
const payloads: Record<string, unknown>[] = [];
const baseStreamFn: StreamFn = (_model, _context, options) => {
const payload: Record<string, unknown> = {
tools: [
{
name: "read",
description: "Read file",
input_schema: { type: "object", properties: {} },
},
],
tool_choice: { type: "auto" },
const cases = [
{ input: { type: "auto" }, expected: "auto" },
{ input: { type: "none" }, expected: "none" },
{ input: { type: "required" }, expected: "required" },
] as const;
for (const testCase of cases) {
const payloads: Record<string, unknown>[] = [];
const baseStreamFn: StreamFn = (_model, _context, options) => {
const payload: Record<string, unknown> = {
tools: [
{
name: "read",
description: "Read file",
input_schema: { type: "object", properties: {} },
},
],
tool_choice: testCase.input,
};
options?.onPayload?.(payload);
payloads.push(payload);
return {} as ReturnType<StreamFn>;
};
options?.onPayload?.(payload);
payloads.push(payload);
return {} as ReturnType<StreamFn>;
};
const agent = { streamFn: baseStreamFn };
const agent = { streamFn: baseStreamFn };
applyExtraParamsToAgent(agent, undefined, "kimi-coding", "k2p5", undefined, "low");
applyExtraParamsToAgent(agent, undefined, "kimi-coding", "k2p5", undefined, "low");
const model = {
api: "anthropic-messages",
provider: "kimi-coding",
id: "k2p5",
baseUrl: "https://api.kimi.com/coding/",
} as Model<"anthropic-messages">;
const context: Context = { messages: [] };
void agent.streamFn?.(model, context, {});
const model = {
api: "anthropic-messages",
provider: "kimi-coding",
id: "k2p5",
baseUrl: "https://api.kimi.com/coding/",
} as Model<"anthropic-messages">;
const context: Context = { messages: [] };
void agent.streamFn?.(model, context, {});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.tool_choice).toBe("auto");
expect(payloads).toHaveLength(1);
expect(payloads[0]?.tool_choice).toBe(testCase.expected);
}
});
it("does not rewrite anthropic tool schema for non-kimi endpoints", () => {