feat(llm): 模型配置新增输出上限,支持 max_completion_tokens 字段名

llm_profiles 加两列:max_tokens(单次回复输出上限,0=不发送该字段,由服务端
默认值决定)与 max_tokens_field(上限发哪个键)。后者仅对 format=openai 生效:
空(默认)= max_tokens;'max_completion_tokens' = 新键。OpenAI 推理模型
(o 系列/GPT-5)拒收 max_tokens、只认新键,而多数兼容网关反过来只认旧键,
故做成显式开关而非自动推断。anthropic 与 openai-responses 自带字段名,
存下来只会误导,保存时一并清空。

顺带补上一处从未接通的链路:planner/worker/chat/mainagent/goals 五个
agentcore.Options 都没设过 MaxTokens,恒为 0——openai 系因 omitempty 压根
不发上限,anthropic 走 norma 的 8192 兜底。光加配置项而不接这条线,用户在
后台填了数字也不会生效,故本次一并接通,取值与 streaming 一样按 profile
每轮解析,故障转移切换后下一轮即生效。

task_llm.go 里 nonStreaming() 的来源优先级(agent 绑定 → 任务链激活 profile
→ 全局)抽成共用的 activeCfg(),避免 maxTokens() 再抄一份同样的三级回退。

norma 升到 v0.3.3(该版本提供 Config.MaxTokensField)。go.mod 只动 norma 这
一行:仓库此前未 tidy 过,跑 go mod tidy 会顺带升 x/crypto、x/net 等一批无关
依赖,不混进本次改动。

迁移:新列走 ADD COLUMN IF NOT EXISTS + 默认 0/'',旧库升级后行为完全不变;
CHECK 约束限定取值,每次启动先删后建保持幂等。
This commit is contained in:
Autumn-27 committed 2026-08-31 12:20:31 -04:00
1 parent 338e77c064
commit 7cc467c159
20 files changed
+364 -33

No files matched your search

+21
View File
@@ -312,6 +312,15 @@ CREATE TABLE IF NOT EXISTS llm_profiles (
pool_exclude BOOLEAN NOT NULL DEFAULT false,
-- streaming=true(默认)走流式 SSE;false 走真·非流式(stream:false,一次性 JSON)。
streaming BOOLEAN NOT NULL DEFAULT true,
-- 单次回复的输出上限(token)。0=不发送该字段,由服务端默认值决定——保持既有行为。
-- 与 context_window_k(模型总容量,仅本地用于压缩阈值)是两回事:本值会随请求发出。
max_tokens INTEGER NOT NULL DEFAULT 0,
-- 输出上限用哪个请求字段名,仅对 format='openai' 生效:
-- '' = max_tokens(默认,兼容绝大多数网关)
-- 'max_completion_tokens' = 新字段;OpenAI 推理模型(o 系列/GPT-5)只认它,
-- 发 max_tokens 会被 unsupported_parameter 拒绝。
-- anthropic(max_tokens 必填)与 openai-responses(max_output_tokens)自带字段名,不受此值影响。
max_tokens_field TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
@@ -330,6 +339,18 @@ ALTER TABLE llm_profiles DROP CONSTRAINT IF EXISTS llm_profiles_format_check;
ALTER TABLE llm_profiles ADD CONSTRAINT llm_profiles_format_check
CHECK (format IN ('openai','anthropic','openai-responses'));
-- 输出上限及其字段名;补旧库。默认 0 / '' = 不发送上限、沿用 max_tokens 字段名,
-- 旧配置行为完全不变。
ALTER TABLE llm_profiles ADD COLUMN IF NOT EXISTS max_tokens INTEGER NOT NULL DEFAULT 0;
ALTER TABLE llm_profiles ADD COLUMN IF NOT EXISTS max_tokens_field TEXT NOT NULL DEFAULT '';
-- 同 format:先删再建,保证每次启动幂等。
ALTER TABLE llm_profiles DROP CONSTRAINT IF EXISTS llm_profiles_max_tokens_field_check;
ALTER TABLE llm_profiles ADD CONSTRAINT llm_profiles_max_tokens_field_check
CHECK (max_tokens_field IN ('','max_completion_tokens'));
ALTER TABLE llm_profiles DROP CONSTRAINT IF EXISTS llm_profiles_max_tokens_check;
ALTER TABLE llm_profiles ADD CONSTRAINT llm_profiles_max_tokens_check
CHECK (max_tokens >= 0);
-- 思考开关字段 thinking_type,从旧的单一 reasoning_effort 语义一次性拆分而来。
-- schema.sql 每次启动都执行,故迁移必须只跑一次:仅当该列尚不存在时才回填,
-- 否则每次启动都会把用户后来手动设的组合覆盖回去。旧 reasoning_effort 语义: