anthropic_test.go

 1package providertests
 2
 3import (
 4	"net/http"
 5	"os"
 6	"testing"
 7
 8	"charm.land/fantasy"
 9	"charm.land/fantasy/anthropic"
10	"github.com/stretchr/testify/require"
11	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
12)
13
14var anthropicTestModels = []testModel{
15	{"claude-sonnet-4", "claude-sonnet-4-20250514", true},
16}
17
18func TestAnthropicCommon(t *testing.T) {
19	var pairs []builderPair
20	for _, m := range anthropicTestModels {
21		pairs = append(pairs, builderPair{m.name, anthropicBuilder(m.model), nil})
22	}
23	testCommon(t, pairs)
24}
25
26func TestAnthropicThinking(t *testing.T) {
27	opts := fantasy.ProviderOptions{
28		anthropic.Name: &anthropic.ProviderOptions{
29			Thinking: &anthropic.ThinkingProviderOption{
30				BudgetTokens: 4000,
31			},
32		},
33	}
34	var pairs []builderPair
35	for _, m := range anthropicTestModels {
36		if !m.reasoning {
37			continue
38		}
39		pairs = append(pairs, builderPair{m.name, anthropicBuilder(m.model), opts})
40	}
41	testThinking(t, pairs, testAnthropicThinking)
42}
43
44func testAnthropicThinking(t *testing.T, result *fantasy.AgentResult) {
45	reasoningContentCount := 0
46	signaturesCount := 0
47	// Test if we got the signature
48	for _, step := range result.Steps {
49		for _, msg := range step.Messages {
50			for _, content := range msg.Content {
51				if content.GetType() == fantasy.ContentTypeReasoning {
52					reasoningContentCount += 1
53					reasoningContent, ok := fantasy.AsContentType[fantasy.ReasoningPart](content)
54					if !ok {
55						continue
56					}
57					if len(reasoningContent.ProviderOptions) == 0 {
58						continue
59					}
60
61					anthropicReasoningMetadata, ok := reasoningContent.ProviderOptions[anthropic.Name]
62					if !ok {
63						continue
64					}
65					if reasoningContent.Text != "" {
66						if typed, ok := anthropicReasoningMetadata.(*anthropic.ReasoningOptionMetadata); ok {
67							require.NotEmpty(t, typed.Signature)
68							signaturesCount += 1
69						}
70					}
71				}
72			}
73		}
74	}
75	require.Greater(t, reasoningContentCount, 0)
76	require.Greater(t, signaturesCount, 0)
77	require.Equal(t, reasoningContentCount, signaturesCount)
78}
79
80func anthropicBuilder(model string) builderFunc {
81	return func(r *recorder.Recorder) (fantasy.LanguageModel, error) {
82		provider := anthropic.New(
83			anthropic.WithAPIKey(os.Getenv("FANTASY_ANTHROPIC_API_KEY")),
84			anthropic.WithHTTPClient(&http.Client{Transport: r}),
85		)
86		return provider.LanguageModel(model)
87	}
88}