fix(model): anthropic默认max_tokens 1024→8192 — thinking+text共享预算,长思考不再截断正文为空
- 根因: deepseek-v4-flash-vision-exp 为 thinking 模型, content=[thinking,text] 共享 max_tokens,默认1024被长思考吃光→仅thinking块无text块, stop_reason=max_tokens → resp.Content="" → no reply produced - 仅当 req.MaxTokens==0 套用默认(显式值不受影响); loop→agent→anthropic 传递链无截断 - 新增 anthropic_budget_test(默认发8192 + 长思考短text断言Content=text+thinking不泄漏) - build/vet/test绿+交叉编译
This commit is contained in:
1 parent
f43751fd07
commit
57d5d22d87
2 files changed
+71
-1
No files matched your search
@@ -20,7 +20,8 @@ const (
|
||||
// anthropicDefaultMaxTokens is the default max_tokens sent when
|
||||
// Request.MaxTokens is zero. Anthropic requires max_tokens to be present and
|
||||
// positive, so a sensible default is applied rather than omitting it.
|
||||
anthropicDefaultMaxTokens = 1024
|
||||
// max_tokens is shared by thinking + text, so it must leave room for both.
|
||||
anthropicDefaultMaxTokens = 8192
|
||||
// anthropicDataPrefix marks a payload embedded as a data: URI.
|
||||
anthropicDataPrefix = "data:"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/config"
|
||||
)
|
||||
|
||||
// TestAnthropicChat_Chat_DefaultMaxTokensLargeEnoughForThinking guards the fix
|
||||
// for thinking models whose reasoning shares the max_tokens budget with the
|
||||
// visible answer: the zero-value default must leave room for both. It also
|
||||
// asserts an explicit Request.MaxTokens is never overridden by that default
|
||||
// (the existing 64-token case in anthropic_test.go is the compatibility proof).
|
||||
func TestAnthropicChat_Chat_DefaultMaxTokensLargeEnoughForThinking(t *testing.T) {
|
||||
t.Parallel()
|
||||
ch := make(chan anthropicCaptured, 1)
|
||||
srv := newAnthropicServer(t, http.StatusOK, "application/json", anthropicTextResponse, ch)
|
||||
ad := NewAnthropicChat(config.LLMConfig{AnthropicBaseURL: srv.URL, AnthropicModel: "claude-test"})
|
||||
|
||||
if _, err := ad.Chat(context.Background(),
|
||||
Request{Messages: []Message{TextMessage(RoleUser, "hi")}}, Options{}); err != nil {
|
||||
t.Fatalf("Chat error: %v", err)
|
||||
}
|
||||
if got := (<-ch).body["max_tokens"]; got != float64(anthropicDefaultMaxTokens) {
|
||||
t.Errorf("default body.max_tokens = %v, want %d", got, anthropicDefaultMaxTokens)
|
||||
}
|
||||
if anthropicDefaultMaxTokens < 4096 {
|
||||
t.Errorf("anthropicDefaultMaxTokens = %d, too small for a long thinking turn plus its answer", anthropicDefaultMaxTokens)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnthropicChat_Chat_LongThinkingThenShortText asserts a response whose
|
||||
// thinking block is far longer than its text block still surfaces the text and
|
||||
// never leaks the thinking into Content (the thinking-is-not-the-answer red
|
||||
// line). This is the shape a thinking model produces when the budget is large
|
||||
// enough, versus the truncation the old 1024 cap caused.
|
||||
func TestAnthropicChat_Chat_LongThinkingThenShortText(t *testing.T) {
|
||||
t.Parallel()
|
||||
thinking := strings.Repeat("推理", 1500) // 3000 runes, far longer than the answer
|
||||
body := fmt.Sprintf(`{
|
||||
"id":"msg_think","model":"claude-test",
|
||||
"content":[
|
||||
{"type":"thinking","thinking":%q},
|
||||
{"type":"text","text":"现在是下午三点。"}
|
||||
],
|
||||
"usage":{"input_tokens":20,"output_tokens":900}
|
||||
}`, thinking)
|
||||
srv := newAnthropicServer(t, http.StatusOK, "application/json", body, nil)
|
||||
ad := NewAnthropicChat(config.LLMConfig{AnthropicBaseURL: srv.URL, AnthropicModel: "claude-test"})
|
||||
|
||||
out, err := ad.Chat(context.Background(),
|
||||
Request{Messages: []Message{TextMessage(RoleUser, "几点了?")}}, Options{})
|
||||
if err != nil {
|
||||
t.Fatalf("Chat error: %v", err)
|
||||
}
|
||||
if out.Content != "现在是下午三点。" {
|
||||
t.Errorf("Content = %q, want the text block", out.Content)
|
||||
}
|
||||
if strings.Contains(out.Content, "推理") {
|
||||
t.Errorf("Content leaked thinking: %q", out.Content)
|
||||
}
|
||||
if out.Message.Role != RoleAssistant || len(out.Message.Content) != 1 || out.Message.Content[0].Text != "现在是下午三点。" {
|
||||
t.Errorf("Message = %+v, want a single assistant text part", out.Message)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user