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) } }