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	"github.com/stretchr/testify/require"
11	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
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(r *recorder.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(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 testOpenAIResponsesThinkingWithSummaryThinking(t *testing.T, result *fantasy.AgentResult) {
57	reasoningContentCount := 0
58	encryptedData := 0
59	// Test if we got the signature
60	for _, step := range result.Steps {
61		for _, msg := range step.Messages {
62			for _, content := range msg.Content {
63				if content.GetType() == fantasy.ContentTypeReasoning {
64					reasoningContentCount += 1
65					reasoningContent, ok := fantasy.AsContentType[fantasy.ReasoningPart](content)
66					if !ok {
67						continue
68					}
69					if len(reasoningContent.ProviderOptions) == 0 {
70						continue
71					}
72
73					openaiReasoningMetadata, ok := reasoningContent.ProviderOptions[openai.Name]
74					if !ok {
75						continue
76					}
77					if typed, ok := openaiReasoningMetadata.(*openai.ResponsesReasoningMetadata); ok {
78						require.NotEmpty(t, typed.EncryptedContent)
79						encryptedData += 1
80					}
81				}
82			}
83		}
84	}
85	require.Greater(t, reasoningContentCount, 0)
86	require.Greater(t, encryptedData, 0)
87	require.Equal(t, reasoningContentCount, encryptedData)
88}