diff --git a/open-sse/config/providers/registry/kimi/index.ts b/open-sse/config/providers/registry/kimi/index.ts index 3ec4bfcf83..ce0119af8b 100644 --- a/open-sse/config/providers/registry/kimi/index.ts +++ b/open-sse/config/providers/registry/kimi/index.ts @@ -1,4 +1,5 @@ import type { RegistryEntry } from "../../shared.ts"; +import { KIMI_K27_MODELS } from "../../shared.ts"; export const kimiProvider: RegistryEntry = { id: "kimi", @@ -11,5 +12,6 @@ export const kimiProvider: RegistryEntry = { models: [ { id: "kimi-k2.6", name: "Kimi K2.6" }, { id: "kimi-k2.5", name: "Kimi K2.5" }, + ...KIMI_K27_MODELS, ], }; diff --git a/open-sse/config/providers/shared.ts b/open-sse/config/providers/shared.ts index 1ab86bfe50..8dfe8719c6 100644 --- a/open-sse/config/providers/shared.ts +++ b/open-sse/config/providers/shared.ts @@ -145,6 +145,35 @@ export interface LegacyProvider { timeoutMs?: number; } +// Kimi K2.7 Code (released 2026-06-12): coding-focused successor to K2.6 — 1T +// MoE, 256K context, thinking-only (preserve_thinking forced) with a fixed +// sampling regime (temperature=1.0 / top_p=0.95). Two ids: `kimi-k2.7-code` and +// the high-speed variant `kimi-k2.7-code-highspeed`. `temperature`/`top_p` are +// stripped on every path: the OpenAI endpoint (api.moonshot.ai) treats them as +// non-modifiable, and the coding/Anthropic endpoint (api.kimi.com/coding) — the +// path validated live on the test VPS — tolerates them but fixes them anyway, so +// dropping them keeps the fixed regime and avoids an OpenAI-endpoint 400. +export const KIMI_K27_MODELS: RegistryModel[] = [ + { + id: "kimi-k2.7-code", + name: "Kimi K2.7 Code", + contextLength: 262144, + maxOutputTokens: 262144, + supportsVision: true, + supportsReasoning: true, + unsupportedParams: ["temperature", "top_p"], + }, + { + id: "kimi-k2.7-code-highspeed", + name: "Kimi K2.7 Code (High Speed)", + contextLength: 262144, + maxOutputTokens: 262144, + supportsVision: true, + supportsReasoning: true, + unsupportedParams: ["temperature", "top_p"], + }, +]; + export const KIMI_CODING_SHARED = { format: "claude", executor: "default", @@ -176,6 +205,7 @@ export const KIMI_CODING_SHARED = { contextLength: 262144, maxOutputTokens: 262144, }, + ...KIMI_K27_MODELS, ] as RegistryModel[], } as const; @@ -287,7 +317,7 @@ export const CHAT_OPENAI_COMPAT_MODELS: Record = { "aisingapore/Qwen-SEA-LION-v4-32B-IT", "allenai/Olmo-3-32B-Think", ]), - moonshot: buildModels(["kimi-k2.6", "kimi-k2.5"]), + moonshot: [...buildModels(["kimi-k2.6", "kimi-k2.5"]), ...KIMI_K27_MODELS], "meta-llama": buildModels([ "Llama-4-Maverick-17B-128E-Instruct-FP8", "Llama-4-Scout-17B-16E-Instruct-FP8", diff --git a/tests/unit/kimi-k2.7-code-registration.test.ts b/tests/unit/kimi-k2.7-code-registration.test.ts new file mode 100644 index 0000000000..fa3490d35e --- /dev/null +++ b/tests/unit/kimi-k2.7-code-registration.test.ts @@ -0,0 +1,66 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +// Kimi K2.7 Code (released 2026-06-12) is Moonshot's coding-focused successor to +// K2.6: 1T MoE, 256K context, thinking-only (preserve_thinking forced), with a +// fixed sampling regime (temperature=1.0 / top_p=0.95). It must be advertised on +// both the OAuth coding endpoint (api.kimi.com/coding, Anthropic format — the +// path validated live on the test VPS) and the OpenAI endpoint +// (api.moonshot.ai/v1). Two ids: `kimi-k2.7-code` and `kimi-k2.7-code-highspeed`. +const { getRegistryEntry, getUnsupportedParams } = await import( + "../../open-sse/config/providerRegistry.ts" +); +const { getResolvedModelCapabilities, supportsReasoning } = await import( + "../../src/lib/modelCapabilities.ts" +); + +const K27 = "kimi-k2.7-code"; +const K27_HS = "kimi-k2.7-code-highspeed"; + +function modelIds(provider: string): string[] { + const entry = getRegistryEntry(provider); + assert.ok(entry, `${provider} registry entry must exist`); + return (entry.models ?? []).map((m) => m.id); +} + +test("kimi-coding (OAuth) advertises kimi-k2.7-code + highspeed", () => { + const ids = modelIds("kimi-coding"); + assert.ok(ids.includes(K27), "kimi-coding must list kimi-k2.7-code"); + assert.ok(ids.includes(K27_HS), "kimi-coding must list kimi-k2.7-code-highspeed"); + assert.ok(ids.includes("kimi-k2.6"), "existing kimi-k2.6 stays listed"); +}); + +test("kimi-coding-apikey advertises kimi-k2.7-code (shares KIMI_CODING_SHARED)", () => { + const ids = modelIds("kimi-coding-apikey"); + assert.ok(ids.includes(K27), "kimi-coding-apikey must list kimi-k2.7-code"); + assert.ok(ids.includes(K27_HS), "kimi-coding-apikey must list kimi-k2.7-code-highspeed"); +}); + +test("moonshot (OpenAI endpoint) advertises kimi-k2.7-code + highspeed", () => { + const ids = modelIds("moonshot"); + assert.ok(ids.includes(K27), "moonshot must list kimi-k2.7-code"); + assert.ok(ids.includes(K27_HS), "moonshot must list kimi-k2.7-code-highspeed"); + assert.ok(ids.includes("kimi-k2.6"), "existing kimi-k2.6 stays listed"); +}); + +test("kimi (OpenAI endpoint) advertises kimi-k2.7-code + highspeed", () => { + const ids = modelIds("kimi"); + assert.ok(ids.includes(K27), "kimi must list kimi-k2.7-code"); + assert.ok(ids.includes(K27_HS), "kimi must list kimi-k2.7-code-highspeed"); +}); + +test("kimi-k2.7-code reports native 262144 context and is reasoning-capable", () => { + const caps = getResolvedModelCapabilities({ provider: "kimi-coding", model: K27 }); + assert.equal(caps.contextWindow, 262144, "context window must be the native 256K (262144)"); + // thinking-only model: the thinking budget pipeline must not strip its thinking + // config (applyThinkingBudget early-exits via supportsReasoning(model)). + assert.equal(supportsReasoning(K27), true, "kimi-k2.7-code must be reasoning-capable"); +}); + +test("kimi-k2.7-code strips client temperature/top_p (fixed sampling upstream)", () => { + for (const provider of ["kimi-coding", "kimi-coding-apikey", "moonshot", "kimi"]) { + const unsupported = getUnsupportedParams(provider, K27); + assert.ok(unsupported.includes("temperature"), `${provider}: temperature must be stripped`); + assert.ok(unsupported.includes("top_p"), `${provider}: top_p must be stripped`); + } +});