- 根因: 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绿+交叉编译
70 lines
2.9 KiB
Go
70 lines
2.9 KiB
Go
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)
|
|
}
|
|
}
|