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	})
 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, err := 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	if err != nil {
 58		return nil, err
 59	}
 60	return provider.LanguageModel("grok-code-fast-1")
 61}
 62
 63func builderXAIGrok4Fast(r *recorder.Recorder) (fantasy.LanguageModel, error) {
 64	provider, err := openaicompat.New(
 65		openaicompat.WithBaseURL("https://api.x.ai/v1"),
 66		openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
 67		openaicompat.WithHTTPClient(&http.Client{Transport: r}),
 68	)
 69	if err != nil {
 70		return nil, err
 71	}
 72	return provider.LanguageModel("grok-4-fast")
 73}
 74
 75func builderXAIGrok3Mini(r *recorder.Recorder) (fantasy.LanguageModel, error) {
 76	provider, err := openaicompat.New(
 77		openaicompat.WithBaseURL("https://api.x.ai/v1"),
 78		openaicompat.WithAPIKey(os.Getenv("FANTASY_XAI_API_KEY")),
 79		openaicompat.WithHTTPClient(&http.Client{Transport: r}),
 80	)
 81	if err != nil {
 82		return nil, err
 83	}
 84	return provider.LanguageModel("grok-3-mini")
 85}
 86
 87func builderZAIGLM45(r *recorder.Recorder) (fantasy.LanguageModel, error) {
 88	provider, err := openaicompat.New(
 89		openaicompat.WithBaseURL("https://api.z.ai/api/coding/paas/v4"),
 90		openaicompat.WithAPIKey(os.Getenv("FANTASY_ZAI_API_KEY")),
 91		openaicompat.WithHTTPClient(&http.Client{Transport: r}),
 92	)
 93	if err != nil {
 94		return nil, err
 95	}
 96	return provider.LanguageModel("glm-4.5")
 97}
 98
 99func builderGroq(r *recorder.Recorder) (fantasy.LanguageModel, error) {
100	provider, err := openaicompat.New(
101		openaicompat.WithBaseURL("https://api.groq.com/openai/v1"),
102		openaicompat.WithAPIKey(os.Getenv("FANTASY_GROQ_API_KEY")),
103		openaicompat.WithHTTPClient(&http.Client{Transport: r}),
104	)
105	if err != nil {
106		return nil, err
107	}
108	return provider.LanguageModel("moonshotai/kimi-k2-instruct-0905")
109}
110
111func builderHuggingFace(r *recorder.Recorder) (fantasy.LanguageModel, error) {
112	provider, err := openaicompat.New(
113		openaicompat.WithBaseURL("https://router.huggingface.co/v1"),
114		openaicompat.WithAPIKey(os.Getenv("FANTASY_HUGGINGFACE_API_KEY")),
115		openaicompat.WithHTTPClient(&http.Client{Transport: r}),
116	)
117	if err != nil {
118		return nil, err
119	}
120	return provider.LanguageModel("Qwen/Qwen3-Coder-480B-A35B-Instruct:cerebras")
121}