import assert from "node:assert/strict"; import { it } from "node:test"; import { sanitizeUserSettings, type ModelsSettings, type UserSettings } from "../src/core/settings.ts"; import { resolveConversationSpec, resolveModelSpec, resolveRoleSpec, resolveSettings } from "../src/core/settings-resolution.ts"; import { parseModelsSettingsBody } from "../src/server/settings-body.ts"; import { clampModelsLevels } from "../src/server/model-catalog.ts"; import { composeSlotFast, composeSlotModel } from "../components/settings/agent-section.tsx"; import { chatGPTPayload, chatGPTStreamError } from "../src/agent/chatgpt-stream.ts"; import { translate } from "../src/core/i18n.ts"; const chatgpt = { provider: "chatgpt", id: "gpt-5" }; const api = { provider: "openai", id: "gpt-5" }; it("validates and saves explicit per-tier Fast choices, dropping invalid and unsupported saved flags", () => { const models = { strong: { model: chatgpt, thinkingLevel: "high", fastMode: true }, cheap: { model: chatgpt, fastMode: false }, vision: { model: api } }; assert.deepEqual(parseModelsSettingsBody(models), { ok: true, settings: models }); assert.deepEqual(sanitizeUserSettings({ version: 1, models })?.models, models); for (const fastMode of ["true", 1, null]) assert.equal(parseModelsSettingsBody({ strong: { model: chatgpt, fastMode } }).ok, false); assert.equal(parseModelsSettingsBody({ strong: { model: api, fastMode: true } }).ok, false); assert.deepEqual(sanitizeUserSettings({ version: 1, models: { strong: { model: api, fastMode: true }, cheap: { model: chatgpt, fastMode: "true" } } })?.models, { strong: { model: api }, cheap: { model: chatgpt } }); }); it("toggles one tier without altering its model, reasoning, or siblings; a provider swap clears Fast", () => { const models: ModelsSettings = { strong: { model: chatgpt, thinkingLevel: "high" }, cheap: { model: api, thinkingLevel: "low" } }; const enabled = composeSlotFast(models, "strong", true); assert.deepEqual(enabled, { ...models, strong: { ...models.strong, fastMode: true } }); assert.equal(models.strong?.fastMode, undefined); assert.deepEqual(composeSlotFast(enabled, "cheap", true), enabled); assert.equal(composeSlotModel(enabled, "strong", "chatgpt", "another-model", "low").strong?.fastMode, true); assert.equal(composeSlotModel(enabled, "strong", "openai", "gpt-5", "high").strong?.fastMode, undefined); assert.equal(composeSlotFast(enabled, "strong", false).strong?.fastMode, false); }); it("updates existing conversations on the next resolution without replacing their saved model or reasoning", () => { const chat = { model: { provider: "chatgpt", id: "saved-chat-model" }, thinkingLevel: "low" as const }; const before = structuredClone(chat); const strong = { provider: "chatgpt", id: "new-session-default", thinkingLevel: "high" as const, fastMode: true }; assert.deepEqual(resolveConversationSpec(chat, strong), { provider: "chatgpt", id: "saved-chat-model", thinkingLevel: "low", fastMode: true }); assert.equal(resolveConversationSpec(chat, { ...strong, fastMode: false })?.fastMode, undefined); assert.equal(resolveConversationSpec({ ...chat, model: api }, strong)?.fastMode, undefined); assert.equal(resolveConversationSpec(chat, { ...strong, provider: "openai" })?.fastMode, undefined); assert.deepEqual(chat, before); }); it("background work uses only its mapped tier's Fast preference and never opts another provider in", () => { const settings: UserSettings = { version: 1, models: { strong: { model: chatgpt, fastMode: true }, cheap: { model: chatgpt, fastMode: false }, vision: { model: chatgpt, fastMode: true } } }; assert.equal(resolveRoleSpec("researcher", settings)?.fastMode, undefined); assert.equal(resolveRoleSpec("visualMaker", settings)?.fastMode, true); assert.equal(resolveRoleSpec("pdfReader", settings)?.fastMode, true); settings.models!.cheap!.fastMode = true; assert.equal(resolveRoleSpec("researcher", settings)?.fastMode, true); assert.equal(resolveModelSpec("researcher", { model: api }, settings.models)?.fastMode, undefined); assert.equal(resolveSettings({ version: 1, models: { strong: { model: api, fastMode: true } } }).models.strong?.fastMode, undefined); }); it("the server's reasoning clamp preserves Fast rather than silently dropping the opt-in on write", () => { const models: ModelsSettings = { strong: { model: chatgpt, thinkingLevel: "high", fastMode: true }, cheap: { model: api, thinkingLevel: "low" } }; assert.equal(clampModelsLevels(models).strong?.fastMode, true); assert.equal(clampModelsLevels(models).strong?.thinkingLevel, "high"); }); it("Fast is off for old records, request sanitization pins the chosen tier, and unsupported-mode errors are localized", () => { const settings = resolveSettings({ version: 1, models: { strong: { model: chatgpt } } }); assert.equal(settings.models.strong?.fastMode, undefined); assert.equal(chatGPTPayload({ input: [], service_tier: "priority" }, "gpt-5").service_tier, undefined); assert.equal(chatGPTPayload({ input: [], service_tier: "flex" }, "gpt-5", true).service_tier, "priority"); const error = chatGPTStreamError("400 invalid service_tier"); assert(error.includes("Turn off Fast mode")); assert(translate("ru", error).includes("Отключите быстрый режим")); assert(chatGPTStreamError("429 service_tier quota exceeded").includes("https://chatgpt.com/settings/usage")); });