vercel_test.go

  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}