azure_responses_test.go

 1package providertests
 2
 3import (
 4	"cmp"
 5	"net/http"
 6	"os"
 7	"testing"
 8
 9	"charm.land/fantasy"
10	"charm.land/fantasy/providers/azure"
11	"charm.land/fantasy/providers/openai"
12	"github.com/stretchr/testify/require"
13	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
14)
15
16func TestAzureResponsesCommon(t *testing.T) {
17	var pairs []builderPair
18	models := []testModel{
19		{"azure-gpt-5-mini", "gpt-5-mini", true},
20		{"azure-o4-mini", "o4-mini", true},
21	}
22	for _, m := range models {
23		pairs = append(pairs, builderPair{m.name, azureReasoningBuilder(m.model), nil, nil})
24	}
25	testCommon(t, pairs)
26}
27
28func azureReasoningBuilder(model string) builderFunc {
29	return func(t *testing.T, r *recorder.Recorder) (fantasy.LanguageModel, error) {
30		provider, err := azure.New(
31			azure.WithBaseURL(cmp.Or(os.Getenv("FANTASY_AZURE_BASE_URL"), defaultBaseURL)),
32			azure.WithAPIKey(cmp.Or(os.Getenv("FANTASY_AZURE_API_KEY"), "(missing)")),
33			azure.WithHTTPClient(&http.Client{Transport: r}),
34			azure.WithUseResponsesAPI(),
35		)
36		if err != nil {
37			return nil, err
38		}
39		return provider.LanguageModel(t.Context(), model)
40	}
41}
42
43func TestAzureResponsesWithSummaryThinking(t *testing.T) {
44	opts := fantasy.ProviderOptions{
45		openai.Name: &openai.ResponsesProviderOptions{
46			Include: []openai.IncludeType{
47				openai.IncludeReasoningEncryptedContent,
48			},
49			ReasoningEffort:  openai.ReasoningEffortOption(openai.ReasoningEffortHigh),
50			ReasoningSummary: fantasy.Opt("auto"),
51		},
52	}
53	var pairs []builderPair
54	models := []testModel{
55		{"azure-gpt-5-mini", "gpt-5-mini", true},
56	}
57	for _, m := range models {
58		if !m.reasoning {
59			continue
60		}
61		pairs = append(pairs, builderPair{m.name, azureReasoningBuilder(m.model), opts, nil})
62	}
63	testThinking(t, pairs, testAzureResponsesThinkingWithSummaryThinking)
64}
65
66func testAzureResponsesThinkingWithSummaryThinking(t *testing.T, result *fantasy.AgentResult) {
67	reasoningContentCount := 0
68	encryptedData := 0
69	// Test if we got the signature
70	for _, step := range result.Steps {
71		for _, msg := range step.Messages {
72			for _, content := range msg.Content {
73				if content.GetType() == fantasy.ContentTypeReasoning {
74					reasoningContentCount += 1
75					reasoningContent, ok := fantasy.AsContentType[fantasy.ReasoningPart](content)
76					if !ok {
77						continue
78					}
79					if len(reasoningContent.ProviderOptions) == 0 {
80						continue
81					}
82
83					openaiReasoningMetadata, ok := reasoningContent.ProviderOptions[openai.Name]
84					if !ok {
85						continue
86					}
87					if typed, ok := openaiReasoningMetadata.(*openai.ResponsesReasoningMetadata); ok {
88						require.NotEmpty(t, typed.EncryptedContent)
89						encryptedData += 1
90					}
91				}
92			}
93		}
94	}
95	require.Greater(t, reasoningContentCount, 0)
96	require.Greater(t, encryptedData, 0)
97	require.Equal(t, reasoningContentCount, encryptedData)
98}