diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index 58fcf5ed79..5c7ef0b8c4 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -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, 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 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, diff --git a/open-sse/config/providers/shared.ts b/open-sse/config/providers/shared.ts index 8dfe8719c6..2d1525bb95 100644 --- a/open-sse/config/providers/shared.ts +++ b/open-sse/config/providers/shared.ts @@ -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; diff --git a/tests/unit/catalog-updates-v3829-kimi-qwen.test.ts b/tests/unit/catalog-updates-v3829-kimi-qwen.test.ts new file mode 100644 index 0000000000..21a04289f6 --- /dev/null +++ b/tests/unit/catalog-updates-v3829-kimi-qwen.test.ts @@ -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> = + (mockResponse?.data?.data as Array>) || + (mockResponse?.data as unknown as Array>) || + []; + 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> = + (mockResponse?.data as unknown as { data?: Array> })?.data || + (mockResponse?.data as unknown as Array>) || + []; + 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"); +});