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