openai_responses_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/x/vcr"
11	"github.com/stretchr/testify/require"
12)
13
14func TestOpenAIResponsesCommon(t *testing.T) {
15	var pairs []builderPair
16	for _, m := range openaiTestModels {
17		pairs = append(pairs, builderPair{m.name, openAIReasoningBuilder(m.model), nil, nil})
18	}
19	testCommon(t, pairs)
20}
21
22func openAIReasoningBuilder(model string) builderFunc {
23	return func(t *testing.T, r *vcr.Recorder) (fantasy.LanguageModel, error) {
24		provider, err := openai.New(
25			openai.WithAPIKey(os.Getenv("FANTASY_OPENAI_API_KEY")),
26			openai.WithHTTPClient(&http.Client{Transport: r}),
27			openai.WithUseResponsesAPI(),
28		)
29		if err != nil {
30			return nil, err
31		}
32		return provider.LanguageModel(t.Context(), model)
33	}
34}
35
36func TestOpenAIResponsesWithSummaryThinking(t *testing.T) {
37	opts := fantasy.ProviderOptions{
38		openai.Name: &openai.ResponsesProviderOptions{
39			Include: []openai.IncludeType{
40				openai.IncludeReasoningEncryptedContent,
41			},
42			ReasoningEffort:  openai.ReasoningEffortOption(openai.ReasoningEffortHigh),
43			ReasoningSummary: fantasy.Opt("auto"),
44		},
45	}
46	var pairs []builderPair
47	for _, m := range openaiTestModels {
48		if !m.reasoning {
49			continue
50		}
51		pairs = append(pairs, builderPair{m.name, openAIReasoningBuilder(m.model), opts, nil})
52	}
53	testThinking(t, pairs, testOpenAIResponsesThinkingWithSummaryThinking)
54}
55
56func TestOpenAIResponsesObjectGeneration(t *testing.T) {
57	var pairs []builderPair
58	for _, m := range openaiTestModels {
59		pairs = append(pairs, builderPair{m.name, openAIReasoningBuilder(m.model), nil, nil})
60	}
61	testObjectGeneration(t, pairs)
62}
63
64func testOpenAIResponsesThinkingWithSummaryThinking(t *testing.T, result *fantasy.AgentResult) {
65	reasoningContentCount := 0
66	encryptedData := 0
67	// Test if we got the signature
68	for _, step := range result.Steps {
69		for _, msg := range step.Messages {
70			for _, content := range msg.Content {
71				if content.GetType() == fantasy.ContentTypeReasoning {
72					reasoningContentCount += 1
73					reasoningContent, ok := fantasy.AsContentType[fantasy.ReasoningPart](content)
74					if !ok {
75						continue
76					}
77					if len(reasoningContent.ProviderOptions) == 0 {
78						continue
79					}
80
81					openaiReasoningMetadata, ok := reasoningContent.ProviderOptions[openai.Name]
82					if !ok {
83						continue
84					}
85					if typed, ok := openaiReasoningMetadata.(*openai.ResponsesReasoningMetadata); ok {
86						require.NotEmpty(t, typed.EncryptedContent)
87						encryptedData += 1
88					}
89				}
90			}
91		}
92	}
93	require.Greater(t, reasoningContentCount, 0)
94	require.Greater(t, encryptedData, 0)
95	require.Equal(t, reasoningContentCount, encryptedData)
96}