1package providertests
2
3import (
4 "net/http"
5 "os"
6 "testing"
7
8 "charm.land/fantasy"
9 "charm.land/fantasy/providers/anthropic"
10 "charm.land/fantasy/providers/vercel"
11 "charm.land/x/vcr"
12 "github.com/stretchr/testify/require"
13)
14
15var vercelTestModels = []testModel{
16 {"claude-sonnet-4", "anthropic/claude-sonnet-4", true},
17 {"gemini-2.5-flash", "google/gemini-2.5-flash", false},
18 {"gpt-5", "openai/gpt-5", true},
19 {"gemini-3-pro-preview", "google/gemini-3-pro-preview", true},
20}
21
22func TestVercelCommon(t *testing.T) {
23 var pairs []builderPair
24 for _, m := range vercelTestModels {
25 pairs = append(pairs, builderPair{m.name, vercelBuilder(m.model), nil, nil})
26 }
27 testCommon(t, pairs)
28}
29
30func TestVercelCommonWithAnthropicCache(t *testing.T) {
31 testCommon(t, []builderPair{
32 {"claude-sonnet-4", vercelBuilder("anthropic/claude-sonnet-4"), nil, addAnthropicCaching},
33 })
34}
35
36func TestVercelThinking(t *testing.T) {
37 enabled := true
38 opts := fantasy.ProviderOptions{
39 vercel.Name: &vercel.ProviderOptions{
40 Reasoning: &vercel.ReasoningOptions{
41 Enabled: &enabled,
42 },
43 },
44 }
45
46 var pairs []builderPair
47 for _, m := range vercelTestModels {
48 if !m.reasoning {
49 continue
50 }
51 pairs = append(pairs, builderPair{m.name, vercelBuilder(m.model), opts, nil})
52 }
53 testThinking(t, pairs, testVercelThinking)
54
55 // test anthropic signature
56 testThinking(t, []builderPair{
57 {"claude-sonnet-4-sig", vercelBuilder("anthropic/claude-sonnet-4"), opts, nil},
58 }, testVercelThinkingWithSignature)
59}
60
61func testVercelThinkingWithSignature(t *testing.T, result *fantasy.AgentResult) {
62 reasoningContentCount := 0
63 signaturesCount := 0
64 // Test if we got the signature
65 for _, step := range result.Steps {
66 for _, msg := range step.Messages {
67 for _, content := range msg.Content {
68 if content.GetType() == fantasy.ContentTypeReasoning {
69 reasoningContentCount += 1
70 reasoningContent, ok := fantasy.AsContentType[fantasy.ReasoningPart](content)
71 if !ok {
72 continue
73 }
74 if len(reasoningContent.ProviderOptions) == 0 {
75 continue
76 }
77
78 anthropicReasoningMetadata, ok := reasoningContent.ProviderOptions[anthropic.Name]
79 if !ok {
80 continue
81 }
82 if reasoningContent.Text != "" {
83 if typed, ok := anthropicReasoningMetadata.(*anthropic.ReasoningOptionMetadata); ok {
84 require.NotEmpty(t, typed.Signature)
85 signaturesCount += 1
86 }
87 }
88 }
89 }
90 }
91 }
92 require.Greater(t, reasoningContentCount, 0)
93 require.Greater(t, signaturesCount, 0)
94 require.Equal(t, reasoningContentCount, signaturesCount)
95 // we also add the anthropic metadata so test that
96 testAnthropicThinking(t, result)
97}
98
99func testVercelThinking(t *testing.T, result *fantasy.AgentResult) {
100 reasoningContentCount := 0
101 for _, step := range result.Steps {
102 for _, msg := range step.Messages {
103 for _, content := range msg.Content {
104 if content.GetType() == fantasy.ContentTypeReasoning {
105 reasoningContentCount += 1
106 }
107 }
108 }
109 }
110 require.Greater(t, reasoningContentCount, 0)
111}
112
113func vercelBuilder(model string) builderFunc {
114 return func(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
115 provider, err := vercel.New(
116 vercel.WithAPIKey(os.Getenv("FANTASY_VERCEL_API_KEY")),
117 vercel.WithHTTPClient(&http.Client{Transport: r}),
118 )
119 if err != nil {
120 return nil, err
121 }
122 return provider.LanguageModel(t.Context(), model)
123 }
124}