Let length continuations and Anthropic output run unbounded #7
@@ -258,8 +258,10 @@ func BuildBody(protocol Protocol, model string, messages []Message, tools []Tool
|
||||
"max_tokens": opts.MaxTokens,
|
||||
"stream": true,
|
||||
}
|
||||
// Anthropic 要求 max_tokens。0 表示调用方不限制:用足够大的默认值,
|
||||
// 避免 4096 把长回复截断。真正的上限由调用方 StreamOptions.MaxTokens 决定。
|
||||
if body["max_tokens"] == 0 {
|
||||
body["max_tokens"] = 4096
|
||||
body["max_tokens"] = 128000
|
||||
}
|
||||
if sys != "" {
|
||||
body["system"] = sys
|
||||
|
||||
+10
-3
@@ -84,9 +84,12 @@ type Config struct {
|
||||
// RepeatGuard 为真时,同名且参数按键排序后相同的调用连续 3 次,注入一条提醒。
|
||||
// 每个 run 最多 3 条,不中断循环。
|
||||
RepeatGuard bool
|
||||
// ContinueOnLength 为真时,FinishReason=length 且没有工具调用就续写,最多 3 次。
|
||||
// 续写提示不写进 Session。第 4 次截断返回错误。
|
||||
// ContinueOnLength 为真时,FinishReason=length 且没有工具调用就续写。
|
||||
// 续写提示不写进 Session。次数由 MaxLengthContinuations 决定。
|
||||
ContinueOnLength bool
|
||||
// MaxLengthContinuations 是 length 续写的次数上限。
|
||||
// 0 表示默认 3 次;负数表示不限制。
|
||||
MaxLengthContinuations int
|
||||
}
|
||||
|
||||
// StopInfo 是 Stop 钩子看到的本 run 进度。
|
||||
@@ -219,7 +222,11 @@ func Run(ctx context.Context, cfg Config, user llm.Message) (Result, error) {
|
||||
}
|
||||
emit(cfg, Event{Type: "turn_end", Message: last})
|
||||
if cfg.ContinueOnLength && end.FinishReason == "length" && len(last.ToolCalls) == 0 {
|
||||
if lengthContinues >= maxLengthContinuations {
|
||||
lengthCap := cfg.MaxLengthContinuations
|
||||
if lengthCap == 0 {
|
||||
lengthCap = maxLengthContinuations
|
||||
}
|
||||
if lengthCap > 0 && lengthContinues >= lengthCap {
|
||||
errCap := errString("length continuation exceeded")
|
||||
cfg.logger().Error("runtime length continuation exceeded", errCap)
|
||||
return Result{Text: last.Content, Messages: messages, StopReason: llm.StopError, Usage: usage, Err: errCap}, nil
|
||||
|
||||
@@ -248,6 +248,46 @@ func TestLoopContinueOnLengthCaps(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoopContinueOnLengthUnlimited(t *testing.T) {
|
||||
var n int
|
||||
stream := func(ctx context.Context, model string, messages []llm.Message, decls []llm.ToolDecl, opts llm.StreamOptions) <-chan llm.Event {
|
||||
n++
|
||||
ch := make(chan llm.Event, 1)
|
||||
finish := "length"
|
||||
text := "part"
|
||||
if n >= 5 {
|
||||
finish = "stop"
|
||||
text = "done"
|
||||
}
|
||||
ch <- llm.Event{
|
||||
Kind: llm.KindMessageEnd,
|
||||
FinishReason: finish,
|
||||
Message: llm.Message{Role: llm.RoleAssistant, Content: text, StopReason: llm.StopEnd},
|
||||
}
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
res, err := Run(context.Background(), Config{
|
||||
Model: "fake",
|
||||
MaxTurns: 8,
|
||||
ContinueOnLength: true,
|
||||
MaxLengthContinuations: -1,
|
||||
Stream: stream,
|
||||
}, llm.Message{Role: llm.RoleUser, Content: "go"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.Err != nil {
|
||||
t.Fatalf("err = %v", res.Err)
|
||||
}
|
||||
if n != 5 {
|
||||
t.Fatalf("model calls = %d, want 5", n)
|
||||
}
|
||||
if res.Text != "done" {
|
||||
t.Fatalf("text = %q", res.Text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoopHistorySkipsGeneratedSystem(t *testing.T) {
|
||||
var first string
|
||||
_, err := Run(context.Background(), Config{
|
||||
|
||||
Reference in New Issue
Block a user