1package providertests
2
3import (
4 "net/http"
5 "os"
6 "testing"
7
8 "charm.land/fantasy"
9 "charm.land/fantasy/providers/openai"
10 "charm.land/fantasy/providers/openaicompat"
11 "github.com/stretchr/testify/require"
12 "gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
13)
14
15func TestOpenAICompatibleCommon(t *testing.T) {
16 testCommon(t, []builderPair{
17 {"xai-grok-4-fast", builderXAIGrok4Fast, nil, nil},
18 {"xai-grok-code-fast", builderXAIGrokCodeFast, nil, nil},
19 {"groq-kimi-k2", builderGroq, nil, nil},
20 {"zai-glm-4.5", builderZAIGLM45, nil, nil},
21 {"huggingface-qwen3-coder", builderHuggingFace, nil, nil},
22 })
23}
24
25func TestOpenAICompatibleThinking(t *testing.T) {
26 opts := fantasy.ProviderOptions{
27 openaicompat.Name: &openaicompat.ProviderOptions{
28 ReasoningEffort: openai.ReasoningEffortOption(openai.ReasoningEffortHigh),
29 },
30 }
31 testThinking(t, []builderPair{
32 {"xai-grok-3-mini", builderXAIGrok3Mini, opts, nil},
33 {"zai-glm-4.5", builderZAIGLM45, opts, nil},
34 }, testOpenAICompatThinking)
35}
36
37func testOpenAICompatThinking(t *testing.T, result *fantasy.AgentResult) {
38 reasoningContentCount := 0
39 for _, step := range result.Steps {
40 for _, msg := range step.Messages {
41 for _, content := range msg.Content {
42 if content.GetType() == fantasy.ContentTypeReasoning {
43 reasoningContentCount += 1
44 }
45 }
46 }
47 }
48 require.Greater(t, reasoningContentCount, 0, "expected reasoning content, got none")
49}
50
51func builderXAIGrokCodeFast(r *recorder.Recorder) (fantasy.LanguageModel, error) {
52 provider := openaicompat.New(
53 openaicompat.WithBaseURL("https://api.x.ai/v1"),
54 openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
55 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
56 )
57 return provider.LanguageModel("grok-code-fast-1")
58}
59
60func builderXAIGrok4Fast(r *recorder.Recorder) (fantasy.LanguageModel, error) {
61 provider := openaicompat.New(
62 openaicompat.WithBaseURL("https://api.x.ai/v1"),
63 openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
64 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
65 )
66 return provider.LanguageModel("grok-4-fast")
67}
68
69func builderXAIGrok3Mini(r *recorder.Recorder) (fantasy.LanguageModel, error) {
70 provider := openaicompat.New(
71 openaicompat.WithBaseURL("https://api.x.ai/v1"),
72 openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
73 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
74 )
75 return provider.LanguageModel("grok-3-mini")
76}
77
78func builderZAIGLM45(r *recorder.Recorder) (fantasy.LanguageModel, error) {
79 provider := openaicompat.New(
80 openaicompat.WithBaseURL("https://api.z.ai/api/coding/paas/v4"),
81 openaicompat.WithAPIKey(os.Getenv("FANTASY_ZAI_API_KEY")),
82 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
83 )
84 return provider.LanguageModel("glm-4.5")
85}
86
87func builderGroq(r *recorder.Recorder) (fantasy.LanguageModel, error) {
88 provider := openaicompat.New(
89 openaicompat.WithBaseURL("https://api.groq.com/openai/v1"),
90 openaicompat.WithAPIKey(os.Getenv("FANTASY_GROQ_API_KEY")),
91 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
92 )
93 return provider.LanguageModel("moonshotai/kimi-k2-instruct-0905")
94}
95
96func builderHuggingFace(r *recorder.Recorder) (fantasy.LanguageModel, error) {
97 provider := openaicompat.New(
98 openaicompat.WithBaseURL("https://router.huggingface.co/v1"),
99 openaicompat.WithAPIKey(os.Getenv("FANTASY_HUGGINGFACE_API_KEY")),
100 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
101 )
102 return provider.LanguageModel("Qwen/Qwen3-Coder-480B-A35B-Instruct:cerebras")
103}