package model import ( "context" "fmt" "net/http" "strings" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/config" ) // TestAnthropicChat_Chat_DefaultMaxTokensLargeEnoughForThinking 守护思考 // 模型的修复:其推理与可见答案共享 max_tokens 预算时, // 零值默认必须给两者都留出空间。它还断言显式的 // Request.MaxTokens 绝不会被该默认覆盖 // (anthropic_test.go 里既有的 64-token 用例就是兼容性证明)。 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 断言思考块 // 远长于文本块的响应仍会浮出文本,且 // 绝不把思考泄漏进 Content(思考不是答案的 // 红线)。这就是预算足够大时思考模型产出的形状, // 与旧 1024 上限造成的截断形成对照。 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) } }