fix(models): publish effort_tiers on Kimi K3 base models only (#12299) (#12371)

Validado em lote numa worktree combinada com os 6 PRs destas duas levas sobre o tip de `release/v3.8.51`: os seis boardaram **sem um único conflito**, `typecheck:core` limpo e **54/54** nos 7 arquivos de teste que trazem.

O drift de `i18n:check` (`docs/security/GUARDRAILS.md`, `STEALTH_GUIDE.md` — source-changed) foi medido também no tip puro e é idêntico: base-red pré-existente, não desta leva.
This commit is contained in:
Goni Sulaiman
2026-09-04 03:27:55 +01:00
committed by GitHub
parent 9271a34ec1
commit 9cbc4f118e
4 changed files with 136 additions and 1 deletions

View File

@@ -0,0 +1 @@
- **fix(models):** publish `effort_tiers` on Kimi K3's synced base-model entries (`kmca/k3`, `kmca/k3-256k`) so catalog-only clients (OpenCode, plain SDK pickers) can see and select the reasoning tiers (`low`/`high`/`max`) the synced metadata already carried — the `isSkippedEffortProvider` gate no longer suppresses tier visibility on those base entries, while synthetic `<id>-<tier>` variant generation stays prevented and Codex/GLM base models remain excluded unchanged ([#12299](https://github.com/diegosouzapw/OmniRoute/issues/12299))

View File

@@ -21,6 +21,12 @@
* `<id>-<tier>` catalog entries (open-sse/utils/syncedEffortVariants.ts) — it
* never runs over the base entry's `capabilities`, so it cannot substitute
* for this check. Required (not optional) so no call site can silently skip it.
*
* #12299 carve-out: Kimi K3's synced base entries (`k3`, `k3-256k` — the kmca
* catalog's `low`/`high`/`max` vocabulary) are exempted from the exclusion so
* catalog-only clients (OpenCode, plain SDK pickers) can see and select their
* tiers. Model-scoped, never provider-wide: Codex, GLM, and non-K3 kimi models
* keep the full exclusion exactly as before this carve-out.
*/
// Use the same canonical alias as catalogModelPolicy.ts (l.1) — a relative path from
// src/app/api/v1/models/ to open-sse/ would need 5 `../` and silently breaks under
@@ -39,8 +45,30 @@ interface SyncedCapabilityFlags {
supportedThinkingEfforts?: string[];
}
// Model-id pattern for the Kimi K3 family (#12299): the kmca catalog syncs
// `k3`/`k3-256k` (and prefixed forms such as `kmca/k3`). Same shape the
// executor/translator layers use to recognize K3 elsewhere
// (reasoningContentInjector.ts::K3_AUTHENTIC_REASONING_PATTERN).
const KIMI_K3_MODEL_ID_PATTERN = /(?:^|\/)(?:kimi-)?k3(?:$|-)/i;
/**
* #12299: only Kimi K3's synced BASE entries are exempt from the
* `isSkippedEffortProvider` exclusion. Model-scoped, never provider-wide —
* the exemption requires a kimi-owned provider AND a K3 model id, so Codex,
* GLM, and non-K3 kimi models keep the exclusion contract from #7694.
*/
function isExemptKimiK3BaseModel(sm: SyncedCapabilityFlags, ownedBy: string): boolean {
return (
ownedBy.startsWith("kimi") && typeof sm.id === "string" && KIMI_K3_MODEL_ID_PATTERN.test(sm.id)
);
}
function effectiveEffortTiers(sm: SyncedCapabilityFlags, ownedBy: string): string[] | undefined {
if (isSkippedEffortProvider(ownedBy)) return undefined;
// Exclusion gate (#7694): codex/glm/kimi own a conflicting `-{effort}` suffix
// mechanism — the blind opencode-plugin mapping must never see effort_tiers
// for them, or it double-handles the suffix. #12299 narrows only the kimi K3
// base-model entries out of that gate; everything else stays excluded.
if (isSkippedEffortProvider(ownedBy) && !isExemptKimiK3BaseModel(sm, ownedBy)) return undefined;
const learned = sm.id ? getLearnedReasoningEffortForModel(sm.id) : null;
const synced =
Array.isArray(sm.supportedThinkingEfforts) && sm.supportedThinkingEfforts.length > 0

View File

@@ -0,0 +1,103 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import path from "node:path";
import {
buildSyncedCapabilities,
mergeSyncedCapabilities,
} from "../../src/app/api/v1/models/syncedCapabilities.ts";
import {
shouldExposeSyncedEffortVariants,
appendSyncedEffortVariants,
} from "../../open-sse/utils/syncedEffortVariants.ts";
// #12299: Kimi K3's supportedThinkingEfforts (["low", "high", "max"]) were
// suppressed on the BASE model by isSkippedEffortProvider in
// effectiveEffortTiers(), leaving catalog-only clients with no tiers to copy.
// Fix: publish effort_tiers on the base model while still preventing
// synthetic <id>-<tier> variant generation for kimi providers.
const KIMI_K3_TIERS = ["low", "high", "max"];
test("Kimi K3 base model publishes effort_tiers via buildSyncedCapabilities (#12299)", () => {
const caps = buildSyncedCapabilities(
{ id: "k3", supportsThinking: true, supportedThinkingEfforts: KIMI_K3_TIERS },
"kimi-coding-apikey"
);
assert.ok(caps, "capabilities must be defined for kimi K3");
assert.deepEqual(
caps.effort_tiers,
KIMI_K3_TIERS,
"kimi K3 base model must publish effort_tiers low/high/max"
);
});
test("Kimi K3-256k base model publishes effort_tiers via buildSyncedCapabilities (#12299)", () => {
const caps = buildSyncedCapabilities(
{ id: "k3-256k", supportsThinking: true, supportedThinkingEfforts: KIMI_K3_TIERS },
"kimi-coding-apikey"
);
assert.ok(caps, "capabilities must be defined for kimi K3-256k");
assert.deepEqual(
caps.effort_tiers,
KIMI_K3_TIERS,
"kimi K3-256k base model must publish effort_tiers low/high/max"
);
});
test("Kimi K3 merge path also publishes effort_tiers (#12299)", () => {
const merged = mergeSyncedCapabilities(
{ tool_calling: true },
{ id: "k3", supportsThinking: true, supportedThinkingEfforts: KIMI_K3_TIERS },
"kimi-coding-apikey"
);
assert.ok(merged, "merged capabilities must be defined");
assert.deepEqual(
merged.effort_tiers,
KIMI_K3_TIERS,
"merge path must publish kimi K3 effort_tiers"
);
assert.equal(merged.tool_calling, true, "existing tool_calling must be preserved");
});
test("shouldExposeSyncedEffortVariants still prevents synthetic kimi variants", () => {
// The base model should NOT generate synthetic <id>-<tier> entries
assert.equal(
shouldExposeSyncedEffortVariants({
id: "kimi/k3",
owned_by: "kimi-coding-apikey",
capabilities: { effort_tiers: KIMI_K3_TIERS },
}),
false,
"must not generate synthetic kimi/k3-low, kimi/k3-high, etc."
);
});
test("appendSyncedEffortVariants does not create kimi variant entries", () => {
const models = [
{
id: "kimi-coding-apikey/k3",
owned_by: "kimi-coding-apikey",
capabilities: { effort_tiers: KIMI_K3_TIERS },
},
];
const result = appendSyncedEffortVariants(models);
assert.equal(result.length, 1, "must not add synthetic variant entries for kimi");
assert.equal(result[0].id, "kimi-coding-apikey/k3", "original entry must be unchanged");
});
test("kimi K3 static registry tiers match synced metadata", () => {
// Verify the static registry in runtime.ts has the correct tiers
const runtimePath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
"../../open-sse/config/providers/registry/kimi/coding/runtime.ts"
);
const content = readFileSync(runtimePath, "utf8");
// Verify the static thinking policies declare the same tiers
assert.ok(
content.includes('"low", "high", "max"'),
"KIMI_CODE_STATIC_THINKING_POLICIES.k3 must declare low/high/max"
);
});

View File

@@ -62,6 +62,9 @@ test("merge path keeps vision AND applies the learned override", () => {
// Exclusion gate (#7694): codex/glm/kimi already own a conflicting
// `-{effort}` suffix mechanism — the blind opencode-plugin mapping must never
// see effort_tiers for them, learned or synced, or it double-handles the suffix.
// #12299 exempts only Kimi K3's BASE model entries (asserted in
// tests/unit/kimi-k3-effort-tiers-12299.test.ts) — non-K3 kimi models such as
// "excluded-model" below stay excluded alongside codex/glm.
for (const ownedBy of ["codex", "glm", "glm-cn", "glmt", "kimi", "kimi-coding-apikey"]) {
test(`build: excluded provider "${ownedBy}" never gets effort_tiers (synced)`, () => {
const caps = buildSyncedCapabilities(