1package provider
2
3import (
4 "strings"
5 "testing"
6)
7
8// An image-only tool result must not serialize to an empty
9// function_call_output (the Responses API may reject it) and a
10// following user-message image must serialize as input_image so the
11// model actually receives the bytes.
12func TestCodexImageToolResultMirror(t *testing.T) {
13 c := NewOpenAICodex("token", "acct", "").(*codexClient)
14
15 wire, err := c.buildRequest(Request{
16 Model: "gpt-5.5",
17 Messages: []Message{
18 {Role: RoleUser, Content: []Content{TextBlock{Text: "look at this"}}},
19 {Role: RoleAssistant, Content: []Content{
20 ToolCallBlock{ID: "call_1", Name: "read", Arguments: []byte(`{"path":"x.png"}`)},
21 }},
22 {Role: RoleTool, Content: []Content{
23 ToolResultBlock{CallID: "call_1", Content: []Content{
24 ImageBlock{MimeType: "image/png", Data: []byte("png-bytes")},
25 }},
26 }},
27 // The agent loop appends this mirror after an image tool result.
28 {Role: RoleUser, Content: []Content{
29 TextBlock{Text: "Tool output included the following image content:"},
30 ImageBlock{MimeType: "image/png", Data: []byte("png-bytes")},
31 }},
32 },
33 })
34 if err != nil {
35 t.Fatal(err)
36 }
37
38 var sawFnOutput, sawInputImage bool
39 for _, item := range wire.Input {
40 switch v := item.(type) {
41 case codexFunctionCallOutput:
42 sawFnOutput = true
43 if strings.TrimSpace(v.Output) == "" {
44 t.Fatalf("image-only tool result produced empty function_call_output")
45 }
46 if !strings.Contains(strings.ToLower(v.Output), "image") {
47 t.Fatalf("placeholder should mention image, got %q", v.Output)
48 }
49 case codexInputMessage:
50 for _, ct := range v.Content {
51 if img, ok := ct.(codexInputImage); ok && img.Type == "input_image" {
52 sawInputImage = true
53 }
54 }
55 }
56 }
57 if !sawFnOutput {
58 t.Fatalf("no function_call_output emitted")
59 }
60 if !sawInputImage {
61 t.Fatalf("mirrored user image was not serialized as input_image")
62 }
63}