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
+12 -1
View File
@@ -2,7 +2,7 @@
* `penguin config` — manages a Project's model credentials, default model, model list,
* Agent-level vault environment variables, and UI language.
*
* penguin config model add --model-id <upstream id> --provider <group> [--api-key <key>] [--context-window <n>] [--set-default] [--root <dir>]
* penguin config model add --model-id <upstream id> --provider <group> [--api-key <key>] [--context-window <n>] [--max-tokens <n>] [--set-default] [--root <dir>]
* penguin config model default --model-id <upstream id> --provider <group> [--root <dir>]
* penguin config model vision --model-id <upstream id> --provider <group> [--root <dir>]
* penguin config model list [--root <dir>]
@@ -120,6 +120,7 @@ export function registerConfigCommand(program: Command, t: Messages): void {
.option("--api-key <key>", t.config.addApiKey)
.option("--base-url <url>", t.config.addBaseUrl)
.option("--context-window <n>", t.config.addContextWindow, parseIntArg)
.option("--max-tokens <n>", t.config.addMaxTokens, parseIntArg)
.option("--client-type <type>", t.config.addClientType)
// Tri-state: --vision marks it supported / --no-vision marks it unsupported / neither given keeps the existing value (defaults to supported).
.option("--vision", t.config.addVision)
@@ -131,6 +132,15 @@ export function registerConfigCommand(program: Command, t: Messages): void {
.option("--set-default", t.config.addSetDefault, false)
.option("--root <dir>", t.common.root)
.action(async (opts) => {
// Output cap: parseIntArg already rejects non-numbers; 0/negative must not reach the config either.
const maxTokens: number | undefined = opts.maxTokens;
if (maxTokens !== undefined && maxTokens <= 0) {
process.stderr.write(
`${t.error(`--max-tokens must be a positive integer: got "${maxTokens}".`)}\n`,
);
process.exitCode = 1;
return;
}
const root = resolveRootOption(opts.root);
// --model-id takes the upstream id, paired with the required --provider as a
// reference; the group is never guessed, so --api-key can only ever land on the
@@ -164,6 +174,7 @@ export function registerConfigCommand(program: Command, t: Messages): void {
provider,
model_id: modelId,
...(opts.contextWindow !== undefined ? { context_window: opts.contextWindow } : {}),
...(maxTokens !== undefined ? { max_tokens: maxTokens } : {}),
...(clientType !== undefined ? { client_type: clientType } : {}),
...(opts.vision !== undefined ? { vision: opts.vision } : {}),
...(Object.keys(pricing).length > 0 ? { pricing } : {}),
+5
View File
@@ -39,6 +39,7 @@ export interface Messages {
addApiKey: string;
addBaseUrl: string;
addContextWindow: string;
addMaxTokens: string;
addClientType: string;
addVision: string;
addNoVision: string;
@@ -174,6 +175,8 @@ const en: Messages = {
addApiKey: "API key, stored inline in the Project's hidden .project_config.toml",
addBaseUrl: "Custom base URL",
addContextWindow: "Context window size (tokens)",
addMaxTokens:
"Per-model max output tokens (positive integer); when set it overrides the Agent's max_tokens, omit to inherit — lower it for small-context models",
addClientType: "AgentHub client type (e.g. openai); defaults by provider group when omitted",
addVision: "Mark the model as supporting image input (vision)",
addNoVision: "Mark the model as NOT supporting image input; omit both to keep current",
@@ -287,6 +290,8 @@ const zh: Messages = {
addApiKey: "API key,内联存入 Project 的隐藏文件 .project_config.toml",
addBaseUrl: "自定义 base url",
addContextWindow: "上下文窗口大小(token 数)",
addMaxTokens:
"该模型的最大输出长度(正整数);设置后覆盖 Agent 的 max_tokens,缺省沿用——小上下文模型建议调低",
addClientType: "AgentHub 客户端协议(如 openai);缺省按 provider 分组的语义取值",
addVision: "标注该模型支持图片输入(视觉)",
addNoVision: "标注该模型不支持图片输入;两者都不给则保留原值",