feat(catalog): add kimi-k2.7-code to kmca catalog + qwen-web models discovery (#3931 bug 3, #3737) (#4185)

Integrated into release/v3.8.29 — add moonshotai/kimi-k2.7-code to the kmca (kimi-coding-apikey) catalog (KIMI_CODING_SHARED.models), requested in discussion #3737. On resync: the PR's qwen-web PROVIDER_MODELS_CONFIG addition (issue #3931 bug #3) was already shipped by #4172 — dropped the duplicate, kept release's entry (which has the Array.isArray guard); kept #4183's KIMI_K27_MODELS spread + added the moonshotai-prefixed entry (union). Also reconciled the file-size baseline for openai-to-gemini.ts (#4180 merged without its +20 bump). Validated: 13/13 tests (kmca catalog + qwen-web parse + kimi registration) + file-size green.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-18 14:36:50 -03:00
committed by GitHub
parent d4e92db7a7
commit 75cf6baca4
3 changed files with 124 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
{
"_comment": "Catraca de tamanho (check-file-size.mjs). frozen so pode encolher; arquivos novos <= cap. --update ratcheta.",
"_rebaseline_2026_06_18_4180_gemini_default_thinking": "PR #4180 own growth: openai-to-gemini.ts 844->864 (+20 = default includeThoughts for modern Gemini 2.5+ at the existing openaiToGeminiBase chokepoint — when the client set no thinkingConfig, inject one (includeThoughts:true + a capped budget) so the model's reasoning is marked thought:true and routed to reasoning_content instead of leaking into visible content, #4170). Cohesive translator branch gated to gemini-2.5+/3 (excludes gemini-1.x and non-thinking 2.0); not extractable. Reconciled here because #4180 merged without the baseline bump.",
"_rebaseline_2026_06_18_4176_free_models": "PR #4176 own growth: AddApiKeyModal.tsx 845->866 (+21) and EditConnectionModal.tsx 1174->1204 (+30) = the 'import only free models' connection option — a free-models Toggle gated by providerHasFreeModels() plus its form-state wiring (importFreeModelsOnly field + providerSpecificData persistence, explicit-false on edit so the PUT merge doesn't keep a stale true) added to both connection modals, mirroring the prior per-modal toggle bumps #3879 (redact-thinking) and #2997 (disable-cooling). Detection lives in the new shared src/shared/utils/freeModels.ts (112 LOC, <cap); the duplicated Toggle is ~6 lines and the per-modal state wiring is intrinsic, so the remaining growth is cohesive UI, not an extractable block.",
"_rebaseline_2026_06_18_3931_qwen_web_models": "Issue #3931 (Problem #3) own growth: providers/[id]/models/route.ts 2512->2531 (+19 = one PROVIDER_MODELS_CONFIG entry for `qwen-web` + a 4-line comment). qwen-web was missing from the config map so its model-discovery page returned nothing (the OAuth fallback only fires for provider===qwen). Pure additive config entry pointing at the public chat.qwen.ai/api/v2/models endpoint; standard per-provider addition, not extractable.",
"_rebaseline_2026_06_18_4165_queue_timeout_msg": "Issue #4165 own growth: rateLimitManager.ts 1022->1035 (+13 at the existing withRateLimit catch chokepoint). Bottleneck's raw `This job timed out after <maxWaitMs> ms.` is rewritten into a clear OmniRoute-owned error (names resilienceSettings.requestQueue.maxWaitMs, disclaims upstream, keeps the original as `cause`, tags code=RATE_LIMIT_QUEUE_TIMEOUT) so queue-saturation 502s stop masquerading as provider outages. The branch already existed (it only logged); this adds the error construction at the same point. Not extractable — closes over provider/model/maxWaitMs locals of the single catch.",
@@ -75,7 +76,7 @@
"open-sse/services/rateLimitManager.ts": 1035,
"open-sse/services/tokenRefresh.ts": 1997,
"open-sse/services/usage.ts": 3408,
"open-sse/translator/request/openai-to-gemini.ts": 844,
"open-sse/translator/request/openai-to-gemini.ts": 864,
"open-sse/translator/response/openai-responses.ts": 903,
"open-sse/utils/cursorAgentProtobuf.ts": 1521,
"open-sse/utils/stream.ts": 2710,

View File

@@ -206,6 +206,12 @@ export const KIMI_CODING_SHARED = {
maxOutputTokens: 262144,
},
...KIMI_K27_MODELS,
{
id: "moonshotai/kimi-k2.7-code",
name: "Kimi K2.7 Code",
contextLength: 262144,
maxOutputTokens: 262144,
},
] as RegistryModel[],
} as const;

View File

@@ -0,0 +1,116 @@
// Regression guard for two catalog fixes shipped in v3.8.29:
//
// 1. Task 1 — moonshotai/kimi-k2.7-code added to the kimi-coding-apikey (kmca)
// provider catalog (KIMI_CODING_SHARED.models in open-sse/config/providers/shared.ts).
// Requested by @hana189 in discussion #3737.
//
// 2. Bug #3 (issue #3931) — qwen-web missing from PROVIDER_MODELS_CONFIG in
// src/app/api/providers/[id]/models/route.ts, so the model discovery page for
// the web-cookie provider returned nothing. Identified by @thezukiru in #3895.
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { getModelsByProviderId } from "../../open-sse/config/providerModels.ts";
// ── Task 1: kimi-k2.7-code in kmca (kimi-coding-apikey) ───────────────────────
test("kmca (kimi-coding-apikey) catalog includes moonshotai/kimi-k2.7-code", () => {
const models = getModelsByProviderId("kmca");
const ids = new Set(models.map((m) => m.id));
assert.ok(
ids.has("moonshotai/kimi-k2.7-code"),
"moonshotai/kimi-k2.7-code missing from kmca — add to KIMI_CODING_SHARED.models in shared.ts"
);
});
test("kmca kimi-k2.7-code has 262144 context length and maxOutputTokens", () => {
const models = getModelsByProviderId("kmca");
const model = models.find((m) => m.id === "moonshotai/kimi-k2.7-code");
assert.ok(model, "moonshotai/kimi-k2.7-code not found in kmca catalog");
assert.equal(model.contextLength, 262144, "kimi-k2.7-code contextLength must be 262144");
assert.equal(model.maxOutputTokens, 262144, "kimi-k2.7-code maxOutputTokens must be 262144");
});
test("kmca still exposes kimi-k2.6 and kimi-k2.6-thinking alongside the new model", () => {
const ids = new Set(getModelsByProviderId("kmca").map((m) => m.id));
assert.ok(ids.has("kimi-k2.6"), "kimi-k2.6 must still be present");
assert.ok(ids.has("kimi-k2.6-thinking"), "kimi-k2.6-thinking must still be present");
assert.ok(ids.has("moonshotai/kimi-k2.7-code"), "moonshotai/kimi-k2.7-code must be present");
});
// ── Bug #3 / issue #3931: qwen-web in PROVIDER_MODELS_CONFIG ──────────────────
const ROUTE_FILE = path.join("src", "app", "api", "providers", "[id]", "models", "route.ts");
test("PROVIDER_MODELS_CONFIG contains a qwen-web entry (issue #3931 bug #3)", () => {
const src = fs.readFileSync(ROUTE_FILE, "utf-8");
assert.match(
src,
/"qwen-web"\s*:/,
'"qwen-web" key missing from PROVIDER_MODELS_CONFIG in models/route.ts'
);
});
test("qwen-web PROVIDER_MODELS_CONFIG entry targets chat.qwen.ai/api/v2/models", () => {
const src = fs.readFileSync(ROUTE_FILE, "utf-8");
assert.match(
src,
/chat\.qwen\.ai\/api\/v2\/models/,
"qwen-web discovery URL must be https://chat.qwen.ai/api/v2/models"
);
});
test("qwen-web parseResponse handles Qwen nested data.data structure", () => {
const mockResponse = {
data: {
data: [
{ id: "qwen3.7-plus", name: "Qwen3.7-Plus", owned_by: "qwen" },
{ id: "qwen3-235b-a22b", name: "Qwen3-235B-A22B", owned_by: "qwen" },
{ id: "qwen3-coder-480b", name: "Qwen3-Coder-480B" },
],
},
};
// parseResponse logic matches PROVIDER_MODELS_CONFIG["qwen-web"].parseResponse
const innerData: Array<Record<string, unknown>> =
(mockResponse?.data?.data as Array<Record<string, unknown>>) ||
(mockResponse?.data as unknown as Array<Record<string, unknown>>) ||
[];
const models = innerData
.map((item) => ({
id: (item.id || item.name) as string,
name: (item.name || item.id) as string,
owned_by: (item.owned_by || "qwen") as string,
}))
.filter((m) => m.id);
assert.equal(models.length, 3);
assert.equal(models[0].id, "qwen3.7-plus");
assert.equal(models[0].name, "Qwen3.7-Plus");
assert.equal(models[0].owned_by, "qwen");
assert.equal(models[2].owned_by, "qwen", "owned_by defaults to 'qwen' when absent");
});
test("qwen-web parseResponse handles flat data array fallback", () => {
const mockResponse = {
data: [{ id: "qwen3.7-plus", name: "Qwen3.7-Plus" }],
};
const innerData: Array<Record<string, unknown>> =
(mockResponse?.data as unknown as { data?: Array<Record<string, unknown>> })?.data ||
(mockResponse?.data as unknown as Array<Record<string, unknown>>) ||
[];
const models = innerData
.map((item) => ({
id: (item.id || item.name) as string,
name: (item.name || item.id) as string,
owned_by: (item.owned_by || "qwen") as string,
}))
.filter((m) => m.id);
assert.equal(models.length, 1);
assert.equal(models[0].id, "qwen3.7-plus");
});