From abaf732bb7a96848a7c6f5dd2ce485b86bff086d Mon Sep 17 00:00:00 2001 From: myk1yt Date: Tue, 11 Aug 2026 03:21:25 +0900 Subject: [PATCH] fix(kimi-code): correct maxTokens defaults and add server-side override (#1217) * fix(kimi-code): correct maxTokens defaults and add server-side override Fixes #1215 - Update kimiCodeDefaultModelInfo.maxTokens from 32768 to 131072 - Add kimiCodeModelDefaults for per-model fallback values - Add max_tokens to kimiCodeModelSchema for server response parsing - Override maxTokens in mapKimiCodeModel: server > model-default > global-default - Add 4 new tests for maxTokens override and fallback behavior * refactor(kimi-code): address PR review comments - Replace production constant imports in tests with hardcoded fixture values - Add .int() validation to max_tokens Zod schema - Add fractional max_tokens rejection test - Export kimiCodeModelSchema for test access --------- Co-authored-by: Zoo (VP) --- packages/types/src/providers/kimi-code.ts | 9 ++- .../fetchers/__tests__/kimi-code.spec.ts | 55 ++++++++++++++++++- src/api/providers/fetchers/kimi-code.ts | 6 +- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/packages/types/src/providers/kimi-code.ts b/packages/types/src/providers/kimi-code.ts index c9a458ed1f..0837e25832 100644 --- a/packages/types/src/providers/kimi-code.ts +++ b/packages/types/src/providers/kimi-code.ts @@ -7,7 +7,7 @@ export const kimiCodeReasoningEfforts = ["low", "high", "max"] as const export const kimiCodeDefaultModelInfo: ModelInfo = { contextWindow: 262_144, - maxTokens: 32_768, + maxTokens: 131_072, supportsImages: false, supportsPromptCache: false, supportsReasoningEffort: [...kimiCodeReasoningEfforts], @@ -16,6 +16,13 @@ export const kimiCodeDefaultModelInfo: ModelInfo = { description: "Kimi Code's coding model for subscription and API-key access.", } +export const kimiCodeModelDefaults: Record = { + k3: { maxTokens: 131_072 }, + "k3-256k": { maxTokens: 131_072 }, + "kimi-for-coding": { maxTokens: 131_072 }, + "kimi-for-coding-highspeed": { maxTokens: 131_072 }, +} + export const kimiCodeModels = { [kimiCodeDefaultModelId]: kimiCodeDefaultModelInfo, } as const satisfies Record diff --git a/src/api/providers/fetchers/__tests__/kimi-code.spec.ts b/src/api/providers/fetchers/__tests__/kimi-code.spec.ts index 3fbe995752..a96e760253 100644 --- a/src/api/providers/fetchers/__tests__/kimi-code.spec.ts +++ b/src/api/providers/fetchers/__tests__/kimi-code.spec.ts @@ -1,4 +1,4 @@ -import { getKimiCodeModels, mapKimiCodeModel } from "../kimi-code" +import { getKimiCodeModels, kimiCodeModelSchema, mapKimiCodeModel } from "../kimi-code" describe("Kimi Code model discovery", () => { beforeEach(() => vi.restoreAllMocks()) @@ -97,4 +97,57 @@ describe("Kimi Code model discovery", () => { expect(vi.mocked(fetch).mock.calls[0][1]?.signal?.aborted).toBe(true) expect(vi.getTimerCount()).toBe(0) }) + + it("overrides maxTokens from server max_tokens in mapKimiCodeModel", () => { + const mapped = mapKimiCodeModel({ + id: "kimi-for-coding", + max_tokens: 200_000, + }) + expect(mapped.maxTokens).toBe(200_000) + }) + + it("overrides maxTokens from server max_tokens at fetcher level in getKimiCodeModels", async () => { + vi.spyOn(globalThis, "fetch").mockResolvedValue( + new Response( + JSON.stringify({ + data: [{ id: "kimi-for-coding", context_length: 262144, max_tokens: 200_000 }], + }), + { status: 200 }, + ), + ) + const models = await getKimiCodeModels("token") + expect(models["kimi-for-coding"].maxTokens).toBe(200_000) + }) + + it("falls back to per-model defaults for known model ids", () => { + const expected: Record = { + k3: 131_072, + "k3-256k": 131_072, + "kimi-for-coding": 131_072, + "kimi-for-coding-highspeed": 131_072, + } + for (const [modelId, maxTokens] of Object.entries(expected)) { + const mapped = mapKimiCodeModel({ id: modelId }) + expect(mapped.maxTokens).toBe(maxTokens) + } + }) + + it("falls back to kimiCodeDefaultModelInfo.maxTokens for unknown model ids", () => { + const mapped = mapKimiCodeModel({ id: "unknown-model" }) + expect(mapped.maxTokens).toBe(131_072) + }) + + it("rejects fractional max_tokens from server response", () => { + const mapped = mapKimiCodeModel({ + id: "kimi-for-coding", + max_tokens: 131072.5, + }) + // Zod .int() would reject at schema level, but mapKimiCodeModel receives + // already-parsed data. Verify the schema rejects fractional values. + const result = kimiCodeModelSchema.safeParse({ + id: "kimi-for-coding", + max_tokens: 131072.5, + }) + expect(result.success).toBe(false) + }) }) diff --git a/src/api/providers/fetchers/kimi-code.ts b/src/api/providers/fetchers/kimi-code.ts index 10b93e26cf..02bf9180f7 100644 --- a/src/api/providers/fetchers/kimi-code.ts +++ b/src/api/providers/fetchers/kimi-code.ts @@ -3,14 +3,16 @@ import { z } from "zod" import { KIMI_CODE_BASE_URL, kimiCodeDefaultModelInfo, + kimiCodeModelDefaults, kimiCodeReasoningEfforts, type ModelInfo, type ModelRecord, } from "@roo-code/types" -const kimiCodeModelSchema = z.object({ +export const kimiCodeModelSchema = z.object({ id: z.string().min(1), context_length: z.number().positive().optional(), + max_tokens: z.number().int().positive().optional(), supports_reasoning: z.boolean().optional(), supports_image_in: z.boolean().optional(), display_name: z.string().optional(), @@ -22,9 +24,11 @@ const KIMI_CODE_MODELS_TIMEOUT_MS = 10_000 export function mapKimiCodeModel(model: z.infer): ModelInfo { const supportsReasoning = model.supports_reasoning ?? false + const defaults = kimiCodeModelDefaults[model.id] ?? {} return { ...kimiCodeDefaultModelInfo, contextWindow: model.context_length ?? kimiCodeDefaultModelInfo.contextWindow, + maxTokens: model.max_tokens ?? defaults.maxTokens ?? kimiCodeDefaultModelInfo.maxTokens, supportsReasoningEffort: supportsReasoning ? [...kimiCodeReasoningEfforts] : false, requiredReasoningEffort: supportsReasoning, reasoningEffort: supportsReasoning ? "max" : undefined,