| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- package service
- import (
- "strconv"
- "testing"
- "github.com/stretchr/testify/require"
- "github.com/tidwall/gjson"
- )
- func TestOpenAIResponsesInputItemIDPrefixUsesObservedOutputContracts(t *testing.T) {
- tests := []struct {
- itemType string
- id string
- strip bool
- }{
- {itemType: "message", id: "msg_123", strip: false},
- {itemType: "message", id: "item_123", strip: true},
- {itemType: "reasoning", id: "rs_123", strip: false},
- {itemType: "reasoning", id: "item_123", strip: true},
- {itemType: "function_call", id: "fc_123", strip: false},
- {itemType: "function_call", id: "call_123", strip: true},
- {itemType: "tool_call", id: "fc_123", strip: false},
- {itemType: "local_shell_call", id: "fc_123", strip: false},
- {itemType: "mcp_tool_call", id: "fc_123", strip: false},
- {itemType: "custom_tool_call", id: "ctc_123", strip: false},
- {itemType: "custom_tool_call", id: "fc_123", strip: true},
- {itemType: "tool_search_call", id: "tsc_123", strip: false},
- {itemType: "tool_search_call", id: "fc_123", strip: true},
- {itemType: "web_search_call", id: "ws_123", strip: false},
- {itemType: "web_search_call", id: "item_123", strip: true},
- {itemType: "custom_tool_call_output", id: "fc_123", strip: false},
- {itemType: "custom_tool_call_output", id: "ctco_123", strip: true},
- // Do not impose an inferred contract on output types for which there is
- // no observed upstream prefix rejection.
- {itemType: "function_call_output", id: "fco_123", strip: false},
- {itemType: "tool_search_output", id: "tso_123", strip: false},
- {itemType: "mcp_tool_call_output", id: "mcpo_123", strip: false},
- {itemType: "future_item", id: "item_123", strip: false},
- }
- for _, tt := range tests {
- t.Run(tt.itemType+"/"+tt.id, func(t *testing.T) {
- require.Equal(t, tt.strip, shouldStripOpenAIResponsesInputItemID(tt.itemType, tt.id))
- })
- }
- }
- func TestSanitizeOpenAIResponsesInputItemIDsDoesNotCascadeAcrossIDNamespaces(t *testing.T) {
- body := []byte(`{"input":[
- {"type":"function_call","id":"item_bad_call","call_id":"call_valid","name":"lookup","arguments":"{}"},
- {"type":"function_call_output","call_id":"call_valid","output":"preserve paired output"},
- {"type":"function_call_output","call_id":"item_bad_call","output":"preserve opaque output"},
- {"type":"item_reference","id":"item_bad_call"},
- {"type":"item_reference","id":"remote_valid"},
- {"type":"custom_tool_call","id":"ctc_valid","call_id":"ctco_bad_output","name":"apply_patch","input":"patch"},
- {"type":"custom_tool_call_output","id":"ctco_bad_output","call_id":"ctco_bad_output","output":"preserve by call_id"}
- ]}`)
- sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
- require.NoError(t, err)
- require.True(t, changed)
- items := gjson.GetBytes(sanitized, "input").Array()
- require.Len(t, items, 7)
- require.False(t, items[0].Get("id").Exists())
- require.Equal(t, "call_valid", items[0].Get("call_id").String())
- require.Equal(t, "preserve paired output", items[1].Get("output").String())
- require.Equal(t, "preserve opaque output", items[2].Get("output").String())
- require.Equal(t, "item_bad_call", items[3].Get("id").String())
- require.Equal(t, "remote_valid", items[4].Get("id").String())
- require.Equal(t, "ctc_valid", items[5].Get("id").String())
- require.False(t, items[6].Get("id").Exists())
- require.Equal(t, "ctco_bad_output", items[6].Get("call_id").String())
- }
- func TestSanitizeOpenAIResponsesInputItemIDsLeavesUnrelatedReferencesUntouched(t *testing.T) {
- body := []byte(`{"previous_response_id":"resp_1","input":[{"type":"item_reference","id":"remote_item"}]}`)
- sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
- require.NoError(t, err)
- require.False(t, changed)
- require.Equal(t, body, sanitized)
- }
- func TestSanitizeOpenAIResponsesInputItemIDsPreservesReferenceToDuplicateRetainedID(t *testing.T) {
- body := []byte(`{"input":[{"type":"function_call","id":"ctc_shared","call_id":"call_1"},{"type":"custom_tool_call","id":"ctc_shared","call_id":"call_2"},{"type":"item_reference","id":"ctc_shared"}]}`)
- sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
- require.NoError(t, err)
- require.True(t, changed)
- require.False(t, gjson.GetBytes(sanitized, "input.0.id").Exists())
- require.Equal(t, "ctc_shared", gjson.GetBytes(sanitized, "input.1.id").String())
- require.Equal(t, "ctc_shared", gjson.GetBytes(sanitized, "input.2.id").String())
- }
- func TestSanitizeOpenAIResponsesInputItemIDsPreservesOpaqueOutputsAndReferences(t *testing.T) {
- body := []byte(`{"input":[
- {"type":"function_call","id":"item_shared","call_id":"call_real"},
- {"type":"function_call_output","id":"item_shared","call_id":"item_shared","output":"dangling"},
- {"type":"item_reference","id":"item_shared"},
- {"type":"function_call_output","id":"kept_output","call_id":"call_real","output":"kept"},
- {"type":"item_reference","id":"kept_output"}
- ]}`)
- sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
- require.NoError(t, err)
- require.True(t, changed)
- require.Len(t, gjson.GetBytes(sanitized, "input").Array(), 5)
- require.False(t, gjson.GetBytes(sanitized, "input.0.id").Exists())
- require.Equal(t, "dangling", gjson.GetBytes(sanitized, "input.1.output").String())
- require.Equal(t, "item_shared", gjson.GetBytes(sanitized, "input.2.id").String())
- require.Equal(t, "kept_output", gjson.GetBytes(sanitized, "input.3.id").String())
- require.Equal(t, "kept_output", gjson.GetBytes(sanitized, "input.4.id").String())
- second, changedAgain, err := sanitizeOpenAIResponsesInputItemIDs(sanitized)
- require.NoError(t, err)
- require.False(t, changedAgain)
- require.Equal(t, sanitized, second)
- }
- func TestSanitizeOpenAIResponsesInputItemIDsStripsEmptyKnownIDsOnly(t *testing.T) {
- body := []byte(`{"input":[{"type":"message","id":"","content":"hello"},{"type":"future_item","id":""}]}`)
- sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
- require.NoError(t, err)
- require.True(t, changed)
- require.False(t, gjson.GetBytes(sanitized, "input.0.id").Exists())
- require.True(t, gjson.GetBytes(sanitized, "input.1.id").Exists())
- }
- func TestSanitizeOpenAIResponsesInputItemIDsStripsOnlyNonPairCallIDs(t *testing.T) {
- body := []byte(`{"input":[
- {"type":"message","call_id":"remove_message","content":"hi"},
- {"type":"reasoning","call_id":"remove_reasoning","id":"rs_keep","encrypted_content":"cipher","summary":[]},
- {"type":"image_generation_call","call_id":"remove_image","id":"ig_keep","status":"completed"},
- {"type":"function_call","call_id":"keep_function","name":"lookup","arguments":"{}"},
- {"type":"function_call_output","call_id":"keep_function","output":"ok"},
- {"type":"custom_tool_call","call_id":"keep_custom","name":"patch","input":"x"},
- {"type":"custom_tool_call_output","call_id":"keep_custom","output":"ok"},
- {"type":"tool_search_call","call_id":"keep_search","arguments":"{}"},
- {"type":"tool_search_output","call_id":"keep_search","output":"ok"},
- {"type":"local_shell_call","call_id":"keep_shell","name":"shell","arguments":"{}"}
- ]}`)
- sanitized, changed, err := sanitizeOpenAIResponsesInputItemIDs(body)
- require.NoError(t, err)
- require.True(t, changed)
- for i := 0; i < 3; i++ {
- require.False(t, gjson.GetBytes(sanitized, "input."+strconv.Itoa(i)+".call_id").Exists())
- }
- for i := 3; i < 10; i++ {
- require.True(t, gjson.GetBytes(sanitized, "input."+strconv.Itoa(i)+".call_id").Exists())
- }
- }
|