diff --git a/packages/docs/content/models.en.md b/packages/docs/content/models.en.md index 4fb5e61..f065c3c 100644 --- a/packages/docs/content/models.en.md +++ b/packages/docs/content/models.en.md @@ -77,7 +77,7 @@ Some models in the preset catalog: deepseek-v4-pro / deepseek-v4-flash, gemini-3 ## Thinking levels -Five levels: `none | low | medium | high | xhigh`, configured per Agent as `model.thinking_level` in `system_config.yaml`, default medium. The chat draft view offers a quick picker next to the model selector: a picked level is written back to the selected Agent's setting immediately (the switched-to level becomes that Agent's new default and applies from the next session; a running session keeps the level it was created with). See [Configuration](/configuration). +Five levels: `none | low | medium | high | xhigh`, configured per Agent as `model.thinking_level` in `system_config.yaml`, default medium. The Web pickers offer `low` and above only (many models cannot disable thinking; `none` stays a valid stored value and still displays). The chat draft view offers a quick picker next to the model selector: a picked level is written back to the selected Agent's setting immediately (the switched-to level becomes that Agent's new default and applies from the next session; a running session keeps the level it was created with). See [Configuration](/configuration). ## Models decoupled from Agents diff --git a/packages/docs/content/models.zh.md b/packages/docs/content/models.zh.md index e296e56..c6578b6 100644 --- a/packages/docs/content/models.zh.md +++ b/packages/docs/content/models.zh.md @@ -77,7 +77,7 @@ api_key = "sk-..." ## 思考等级 -思考等级共五档:`none | low | medium | high | xhigh`,按 Agent 在 `system_config.yaml` 的 `model.thinking_level` 配置,默认 medium。对话草稿页在模型选择器旁提供快捷拾取器:选定档位立即写回所选 Agent 的该项配置(切换后的档位即成为该 Agent 的新默认,自下一个 Session 生效;进行中的 Session 沿用创建时的档位)。见 [配置参考](/configuration)。 +思考等级共五档:`none | low | medium | high | xhigh`,按 Agent 在 `system_config.yaml` 的 `model.thinking_level` 配置,默认 medium。Web 拾取器只提供 `low` 及以上档位(多数模型不支持关闭思考;`none` 仍是合法的已存值,能正常显示)。对话草稿页在模型选择器旁提供快捷拾取器:选定档位立即写回所选 Agent 的该项配置(切换后的档位即成为该 Agent 的新默认,自下一个 Session 生效;进行中的 Session 沿用创建时的档位)。见 [配置参考](/configuration)。 ## 模型与 Agent 解耦 diff --git a/packages/web/e2e/draft.spec.mjs b/packages/web/e2e/draft.spec.mjs index d127361..56b2fa7 100644 --- a/packages/web/e2e/draft.spec.mjs +++ b/packages/web/e2e/draft.spec.mjs @@ -87,14 +87,16 @@ test("draft: pick model/approval -> reload restores them -> send creates the ses await expect(page.getByRole("button", { name: "审批模式" })).toContainText("放行只读"); // Conversation-time thinking level (backed by the Agent settings): the picker shows the - // seeded default (medium, short name 中); the menu carries a title bar and exactly the five - // short-name rows (no descriptions, no default row); picking 高 writes straight through to - // the Agent config, so the session created on send runs with it and it becomes the Agent's - // new default. + // seeded default (medium, short name 中); the menu carries a title bar and the short-name + // rows 低/中/高/极高 only — no descriptions, no default row, and no 无 (many models cannot + // disable thinking); picking 高 writes straight through to the Agent config, so the session + // created on send runs with it and it becomes the Agent's new default. const thinkingBtn = page.getByRole("button", { name: "思考等级" }); await expect(thinkingBtn).toContainText("中"); await thinkingBtn.click(); await expect(page.getByText("思考等级", { exact: true })).toBeVisible(); // menu title bar + await expect(page.getByRole("button", { name: "低", exact: true })).toBeVisible(); + await expect(page.getByRole("button", { name: "无", exact: true })).toHaveCount(0); await page.getByRole("button", { name: "高", exact: true }).click(); await expect(thinkingBtn).toContainText("高"); await expect diff --git a/packages/web/src/features/agents/agent-settings-page.tsx b/packages/web/src/features/agents/agent-settings-page.tsx index bb68477..1d0702e 100644 --- a/packages/web/src/features/agents/agent-settings-page.tsx +++ b/packages/web/src/features/agents/agent-settings-page.tsx @@ -31,6 +31,7 @@ import { Modal } from "../../components/ui/modal"; import { Skeleton } from "../../components/ui/skeleton"; import { VaultTab } from "./vault-tab"; import { SchedulesTab } from "./schedules-tab"; +import { thinkingLevelOptionsFor } from "../chat/thinking-level"; type TabKey = "overview" | "prompt" | "runtime" | "tools" | "vault" | "schedules"; @@ -500,7 +501,16 @@ function RuntimeTab({ data, onSave }: { data: AgentConfigResponse; onSave: SaveF }; // S is reassigned on language switch (live binding), so read it during render rather than hoisting to a module-level constant. - const thinkingLevelOptions = withDefaultOption(S.agent.thinkingLevelOptions); + // "none" is no longer offered (many models cannot disable thinking) but stays a valid stored + // value: assembly lives in thinkingLevelOptionsFor, gated on the **persisted** config — a + // misclick onto another tier keeps the legacy row until the change is actually saved, so the + // stored value stays reachable (see thinking-level.ts). + const thinkingLevelOptions = thinkingLevelOptionsFor( + S.agent.thinkingLevelOptions, + S.agent.defaultValue, + S.agent.thinkingLevelNoneKept, + cfg.model?.thinkingLevel, + ); const compactionModeOptions = withDefaultOption(S.agent.compactionModeOptions); return ( diff --git a/packages/web/src/features/chat/chat-input.tsx b/packages/web/src/features/chat/chat-input.tsx index adb8ff6..6a4f0d4 100644 --- a/packages/web/src/features/chat/chat-input.tsx +++ b/packages/web/src/features/chat/chat-input.tsx @@ -64,7 +64,7 @@ import { ProviderLogo } from "../../components/ui/provider-logo"; import { hasConfiguredKey, sameModelRef, visibleChatModels } from "../models/model-grouping"; import { filterAgents, matchMention, splitLeadingMention } from "./agent-mentions"; import { matchSlash, removeSlashToken } from "./slash-token"; -import { THINKING_LEVELS, thinkingLevelLabel } from "./thinking-level"; +import { SELECTABLE_THINKING_LEVELS, thinkingLevelLabel } from "./thinking-level"; import { BOOK_ICON, buildSkillsMessage, @@ -393,8 +393,10 @@ const SPARK_ICON = "M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 * level straight through to the Agent settings — llmConfig is assembled once per session, so * the level applies to the session created on first send and becomes the Agent's new default * (switch-becomes-default). Per review: a title bar names the control, and the menu lists - * exactly the five levels with short names only (no descriptions, no "default" row) — an - * Agent without an explicit override shows an em dash until a level is picked. + * the selectable levels with short names only (no descriptions, no "default" row, and no + * "none" — many models cannot disable thinking); an Agent without an explicit override shows + * an em dash until a level is picked, and a stored legacy "none" still displays via the + * label table (just never offered). */ function ThinkingLevelSelect({ value, @@ -449,7 +451,7 @@ function ThinkingLevelSelect({
{S.chat.thinkingLevel}
- {THINKING_LEVELS.map((level) => ( + {SELECTABLE_THINKING_LEVELS.map((level) => (