feat: per-model max output tokens and a conversation-time thinking level backed by agent settings (#28)

Co-authored-by: Alice <alice@prismshadow.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yaowei Zheng
2026-07-22 22:22:41 +08:00
committed by GitHub
parent 99f391f379
commit cabbab1c16
36 changed files with 797 additions and 42 deletions
+15 -1
View File
@@ -241,6 +241,13 @@ export interface StreamModel {
items: ChatItem[];
/** A nested sub-session model (produces no stats row; its stats count toward the parent). */
nested: boolean;
/**
* The session's thinking level, captured from the main session's `session_meta` on history
* replay ("default" when the Agent config leaves it unset; null until a session_meta has been
* seen). llmConfig is assembled once per session, so this is fixed for the session's lifetime —
* shown read-only in the input area next to the locked model.
*/
thinkingLevel: string | null;
stats: TaskStatsTracker;
/** The currently open text/thinking fragment (opened by start, closed by stop). */
openText: AssistantTextItem | null;
@@ -318,6 +325,7 @@ function newModel(nested: boolean, localDecisions: Set<string>): StreamModel {
return {
items: [],
nested,
thinkingLevel: null,
stats: createTaskStatsTracker(),
openText: null,
openThinking: null,
@@ -392,7 +400,13 @@ export function pushMessage(
advanceLastTs(model, msg.timestamp);
return;
}
// session_meta (main session): not rendered.
// session_meta (main session): not rendered as an item, but its thinking level is captured
// for the input area's read-only display (fixed per session: llmConfig is assembled once at
// session creation, so a mid-conversation Agent-config change doesn't affect this session).
if (msg.type === "session_meta") {
const level = (msg.payload as { thinking_level?: unknown }).thinking_level;
if (typeof level === "string" && level) model.thinkingLevel = level;
}
}
/** ISO timestamp → milliseconds (returns undefined if invalid). */
+12
View File
@@ -312,6 +312,10 @@ export const en: Strings = {
contextWindow: "Context window",
contextWindowUnit: "tokens",
contextWindowHint: "Leave empty if unknown",
maxTokens: "Max output tokens",
maxTokensHint: "Leave empty to inherit the agent setting",
maxTokensCapHint: "Caps output tokens per request; lower it for small-context local models",
maxTokensInvalid: "Must be a positive integer",
clientTypeLocked: (t: string): string => `Protocol: ${t} (kept as configured; not editable)`,
vision: "Supports image input (vision / multimodal)",
visionHint:
@@ -480,6 +484,14 @@ export const en: Strings = {
newSessionMenu: "New chat",
chooseAgent: "Choose agent",
chooseModel: "Choose model",
thinkingLevel: "Thinking level",
thinkingLevelNames: {
none: "None",
low: "Low",
medium: "Medium",
high: "High",
xhigh: "Extreme High",
},
workspaceUseThis: "Use this dir",
workspaceUp: "Parent dir",
workspaceNoSubdirs: "No subdirectories",
+13
View File
@@ -287,6 +287,10 @@ export const zh = {
contextWindow: "上下文窗口",
contextWindowUnit: "tokens",
contextWindowHint: "留空表示未知",
maxTokens: "最大输出长度(Token)",
maxTokensHint: "留空沿用 Agent 设置",
maxTokensCapHint: "限制单次请求的输出 Token 数;小上下文的本地模型建议调低",
maxTokensInvalid: "必须为正整数",
clientTypeLocked: (t: string): string => `协议:${t}(沿用原配置,不可修改)`,
vision: "支持图片输入(视觉/多模态)",
visionHint:
@@ -458,6 +462,15 @@ export const zh = {
newSessionMenu: "新建对话",
chooseAgent: "选择 Agent",
chooseModel: "选择模型",
thinkingLevel: "思考等级",
/** 会话前拾取器的档位短名(评审要求:只写短名、不带说明、无“缺省”项)。 */
thinkingLevelNames: {
none: "无",
low: "低",
medium: "中",
high: "高",
xhigh: "极高",
} as Readonly<Record<string, string>>,
workspaceUseThis: "使用此目录",
workspaceUp: "上级目录",
workspaceNoSubdirs: "无子目录",