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 "charm.land/x/vcr"
12 "github.com/stretchr/testify/require"
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 {"llama-cpp-gpt-oss", builderLlamaCppGptOss, nil, nil},
23 })
24}
25
26func TestOpenAICompatObjectGeneration(t *testing.T) {
27 testObjectGeneration(t, []builderPair{
28 {"xai-grok-4-fast", builderXAIGrok4Fast, nil, nil},
29 {"xai-grok-code-fast", builderXAIGrokCodeFast, nil, nil},
30 {"zai-glm-4.5", builderZAIGLM45, nil, nil},
31 })
32}
33
34func TestOpenAICompatibleThinking(t *testing.T) {
35 opts := fantasy.ProviderOptions{
36 openaicompat.Name: &openaicompat.ProviderOptions{
37 ReasoningEffort: openai.ReasoningEffortOption(openai.ReasoningEffortHigh),
38 },
39 }
40 testThinking(t, []builderPair{
41 {"xai-grok-3-mini", builderXAIGrok3Mini, opts, nil},
42 {"zai-glm-4.5", builderZAIGLM45, opts, nil},
43 {"llama-cpp-gpt-oss", builderLlamaCppGptOss, opts, nil},
44 }, testOpenAICompatThinking)
45}
46
47func testOpenAICompatThinking(t *testing.T, result *fantasy.AgentResult) {
48 reasoningContentCount := 0
49 for _, step := range result.Steps {
50 for _, msg := range step.Messages {
51 for _, content := range msg.Content {
52 if content.GetType() == fantasy.ContentTypeReasoning {
53 reasoningContentCount += 1
54 }
55 }
56 }
57 }
58 require.Greater(t, reasoningContentCount, 0, "expected reasoning content, got none")
59}
60
61func builderXAIGrokCodeFast(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
62 provider, err := openaicompat.New(
63 openaicompat.WithBaseURL("https://api.x.ai/v1"),
64 openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
65 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
66 )
67 if err != nil {
68 return nil, err
69 }
70 return provider.LanguageModel(t.Context(), "grok-code-fast-1")
71}
72
73func builderXAIGrok4Fast(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
74 provider, err := openaicompat.New(
75 openaicompat.WithBaseURL("https://api.x.ai/v1"),
76 openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
77 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
78 )
79 if err != nil {
80 return nil, err
81 }
82 return provider.LanguageModel(t.Context(), "grok-4-fast")
83}
84
85func builderXAIGrok3Mini(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
86 provider, err := openaicompat.New(
87 openaicompat.WithBaseURL("https://api.x.ai/v1"),
88 openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
89 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
90 )
91 if err != nil {
92 return nil, err
93 }
94 return provider.LanguageModel(t.Context(), "grok-3-mini")
95}
96
97func builderZAIGLM45(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
98 provider, err := openaicompat.New(
99 openaicompat.WithBaseURL("https://api.z.ai/api/coding/paas/v4"),
100 openaicompat.WithAPIKey(os.Getenv("FANTASY_ZAI_API_KEY")),
101 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
102 )
103 if err != nil {
104 return nil, err
105 }
106 return provider.LanguageModel(t.Context(), "glm-4.5")
107}
108
109func builderGroq(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
110 provider, err := openaicompat.New(
111 openaicompat.WithBaseURL("https://api.groq.com/openai/v1"),
112 openaicompat.WithAPIKey(os.Getenv("FANTASY_GROQ_API_KEY")),
113 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
114 )
115 if err != nil {
116 return nil, err
117 }
118 return provider.LanguageModel(t.Context(), "moonshotai/kimi-k2-instruct-0905")
119}
120
121func builderHuggingFace(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
122 provider, err := openaicompat.New(
123 openaicompat.WithBaseURL("https://router.huggingface.co/v1"),
124 openaicompat.WithAPIKey(os.Getenv("FANTASY_HUGGINGFACE_API_KEY")),
125 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
126 )
127 if err != nil {
128 return nil, err
129 }
130 return provider.LanguageModel(t.Context(), "zai-org/GLM-4.6:cerebras")
131}
132
133func builderLlamaCppGptOss(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
134 provider, err := openaicompat.New(
135 openaicompat.WithBaseURL("http://localhost:8080/v1"),
136 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
137 )
138 if err != nil {
139 return nil, err
140 }
141 return provider.LanguageModel(t.Context(), "openai/gpt-oss-20b")
142}