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:
1 parent
338e77c064
commit
7cc467c159
20 files changed
+364
-33
No files matched your search
+29
-14
@@ -139,34 +139,46 @@ func (r *taskLLMRuntime) current() (*Task, int64, int64, llm.Provider, error) {
|
||||
return t, 0, pt.LLMChainRevision, prov, nil
|
||||
}
|
||||
|
||||
// nonStreaming reports whether the task's currently-active LLM source is set to
|
||||
// non-streaming. It mirrors current()'s source precedence (agent binding →
|
||||
// active chain profile → global) but reads only the streaming flag. Read-only
|
||||
// and best-effort: any resolution error falls back to streaming (false), the
|
||||
// safe default. If failover switches to a profile with a different streaming
|
||||
// setting, the change takes effect on the next agent run (a fresh Session is
|
||||
// built per run in captureRun).
|
||||
func (r *taskLLMRuntime) nonStreaming() bool {
|
||||
// activeCfg resolves the task's currently-active LLM config, mirroring current()'s
|
||||
// source precedence (agent binding → active chain profile → global). Read-only and
|
||||
// best-effort: ok=false when nothing resolves, leaving the per-setting fallback to
|
||||
// the caller. If failover switches profiles, the change takes effect on the next
|
||||
// agent run (a fresh Session is built per run in captureRun).
|
||||
func (r *taskLLMRuntime) activeCfg() (agent.Config, bool) {
|
||||
taskNum, err := parseTaskID(r.taskID)
|
||||
if err != nil {
|
||||
return false
|
||||
return agent.Config{}, false
|
||||
}
|
||||
pt, err := r.s.m.pg.GetTask(taskNum)
|
||||
if err != nil || pt == nil {
|
||||
return false
|
||||
return agent.Config{}, false
|
||||
}
|
||||
if _, cfg, ok := r.s.agentBindingProvider(r.agentKey); ok {
|
||||
return !cfg.Stream
|
||||
return cfg, true
|
||||
}
|
||||
if len(pt.LLMProfileIDs) > 0 && pt.ActiveLLMProfileID != nil {
|
||||
if _, cfg, ok := r.s.providerForProfile(*pt.ActiveLLMProfileID); ok {
|
||||
return !cfg.Stream
|
||||
return cfg, true
|
||||
}
|
||||
}
|
||||
if _, cfg, ok := r.s.globalProvider(); ok {
|
||||
return !cfg.Stream
|
||||
return cfg, true
|
||||
}
|
||||
return false
|
||||
return agent.Config{}, false
|
||||
}
|
||||
|
||||
// nonStreaming reports whether the task's currently-active LLM source is set to
|
||||
// non-streaming. Unresolvable → streaming (false), the safe default.
|
||||
func (r *taskLLMRuntime) nonStreaming() bool {
|
||||
cfg, ok := r.activeCfg()
|
||||
return ok && !cfg.Stream
|
||||
}
|
||||
|
||||
// maxTokens returns the currently-active source's per-reply output cap.
|
||||
// Unresolvable → 0, i.e. send no cap, matching the pre-setting behaviour.
|
||||
func (r *taskLLMRuntime) maxTokens() int {
|
||||
cfg, _ := r.activeCfg() // 未解析出配置时是零值 0
|
||||
return cfg.MaxTokens
|
||||
}
|
||||
|
||||
func parseTaskID(id string) (int64, error) {
|
||||
@@ -531,6 +543,7 @@ func (s *Server) agentsForTask(t *Task) *taskAgentBundle {
|
||||
wk := agent.NewWorker(workerRuntime, "task-router", s.m.dir, tx, window, s.agentMaxTurns("worker"))
|
||||
wk.SetCompactionWindowResolver(workerRuntime.CompactionWindow)
|
||||
wk.SetNonStreaming(workerRuntime.nonStreaming) // 按任务当前激活 profile 的流式开关(每轮读)
|
||||
wk.SetMaxTokens(workerRuntime.maxTokens) // 同上,输出上限也跟随当前激活 profile
|
||||
wk.SetRunTimeout(time.Duration(s.agentRunSeconds("worker")) * time.Second)
|
||||
wk.SetProxy(s.m.ProxyAddr(), s.m.ProxyCACert())
|
||||
wk.SetMemory(memory.NewStore(filepath.Join(s.m.dir, "memory")))
|
||||
@@ -539,6 +552,7 @@ func (s *Server) agentsForTask(t *Task) *taskAgentBundle {
|
||||
pl := agent.NewPlanner(plannerRuntime, "task-router", s.m.dir, tx, plannerRuntime.CompactionWindow(), s.agentMaxTurns("planner"))
|
||||
pl.SetCompactionWindowResolver(plannerRuntime.CompactionWindow)
|
||||
pl.SetNonStreaming(plannerRuntime.nonStreaming)
|
||||
pl.SetMaxTokens(plannerRuntime.maxTokens)
|
||||
pl.SetKillWork(s.engine.KillWork)
|
||||
pl.SetSteerWork(s.engine.SteerWork)
|
||||
pl.SetProxy(s.m.ProxyAddr(), s.m.ProxyCACert())
|
||||
@@ -547,6 +561,7 @@ func (s *Server) agentsForTask(t *Task) *taskAgentBundle {
|
||||
main := agent.NewMainAgent(mainRuntime, "task-router", s.m.dir, tx, mainRuntime.CompactionWindow(), s.agentMaxTurns("mainagent"))
|
||||
main.SetCompactionWindowResolver(mainRuntime.CompactionWindow)
|
||||
main.SetNonStreaming(mainRuntime.nonStreaming)
|
||||
main.SetMaxTokens(mainRuntime.maxTokens)
|
||||
main.SetProxy(s.m.ProxyAddr(), s.m.ProxyCACert())
|
||||
main.SetWebSearch(s.webSearchFor("mainagent"))
|
||||
main.SetSteerWork(s.engine.SteerWork) // steer_work:人对运行中 work 实时纠偏
|
||||
|
||||
Reference in new issue
Block a user