Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/types/src/providers/kimi-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -16,6 +16,13 @@ export const kimiCodeDefaultModelInfo: ModelInfo = {
description: "Kimi Code's coding model for subscription and API-key access.",
}

export const kimiCodeModelDefaults: Record<string, { maxTokens: number }> = {
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<string, ModelInfo>
Expand Down
55 changes: 54 additions & 1 deletion src/api/providers/fetchers/__tests__/kimi-code.spec.ts
Original file line number Diff line number Diff line change
@@ -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())
Expand Down Expand Up @@ -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<string, number> = {
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)
})
})
6 changes: 5 additions & 1 deletion src/api/providers/fetchers/kimi-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -22,9 +24,11 @@ const KIMI_CODE_MODELS_TIMEOUT_MS = 10_000

export function mapKimiCodeModel(model: z.infer<typeof kimiCodeModelSchema>): 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,
Expand Down
Loading