openaicompat_test.go

  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}