Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
165 lines
6.7 KiB
Go
165 lines
6.7 KiB
Go
package apicompat
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestFlattenResponsesNamespaces_RewritesDeclarationHistoryAndChoice(t *testing.T) {
|
|
req := map[string]any{
|
|
"model": "gpt-5.5",
|
|
"tools": []any{
|
|
map[string]any{"type": "function", "name": "plain", "description": "keep"},
|
|
map[string]any{
|
|
"type": "namespace",
|
|
"name": "collaboration",
|
|
"tools": []any{
|
|
map[string]any{"type": "function", "name": "spawn_agent", "description": "spawn", "parameters": map[string]any{"type": "object"}},
|
|
},
|
|
},
|
|
},
|
|
"tool_choice": map[string]any{"type": "function", "name": "spawn_agent", "namespace": "collaboration"},
|
|
"input": []any{
|
|
map[string]any{"type": "function_call", "call_id": "call_1", "name": "spawn_agent", "namespace": "collaboration", "arguments": "{}"},
|
|
map[string]any{"type": "message", "role": "user", "content": "hi", "name": "spawn_agent", "namespace": "collaboration"},
|
|
},
|
|
}
|
|
|
|
names, changed, err := FlattenResponsesNamespaces(req)
|
|
require.NoError(t, err)
|
|
require.True(t, changed)
|
|
require.Equal(t, ResponsesNamespaceName{Namespace: "collaboration", Name: "spawn_agent"}, names["collaboration__spawn_agent"])
|
|
|
|
tools, ok := req["tools"].([]any)
|
|
require.True(t, ok)
|
|
require.Len(t, tools, 2)
|
|
plainTool, ok := tools[0].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "plain", plainTool["name"])
|
|
flatTool, ok := tools[1].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "collaboration__spawn_agent", flatTool["name"])
|
|
require.Equal(t, "spawn", flatTool["description"])
|
|
|
|
choice, ok := req["tool_choice"].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "collaboration__spawn_agent", choice["name"])
|
|
require.NotContains(t, choice, "namespace")
|
|
|
|
input, ok := req["input"].([]any)
|
|
require.True(t, ok)
|
|
require.Len(t, input, 2)
|
|
call, ok := input[0].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "collaboration__spawn_agent", call["name"])
|
|
require.NotContains(t, call, "namespace")
|
|
message, ok := input[1].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "spawn_agent", message["name"])
|
|
require.Equal(t, "collaboration", message["namespace"])
|
|
require.Equal(t, "gpt-5.5", req["model"])
|
|
}
|
|
|
|
func TestFlattenResponsesNamespaces_RejectsFlatNameCollision(t *testing.T) {
|
|
req := map[string]any{"tools": []any{
|
|
map[string]any{"type": "function", "name": "collaboration__spawn_agent"},
|
|
map[string]any{"type": "namespace", "name": "collaboration", "tools": []any{
|
|
map[string]any{"type": "function", "name": "spawn_agent"},
|
|
}},
|
|
}}
|
|
|
|
_, _, err := FlattenResponsesNamespaces(req)
|
|
require.ErrorContains(t, err, "conflicts with a top-level tool")
|
|
}
|
|
|
|
func TestFlattenResponsesNamespaces_NamespaceGroupChoiceFallsBackToAuto(t *testing.T) {
|
|
req := map[string]any{
|
|
"tools": []any{map[string]any{
|
|
"type": "namespace", "name": "collaboration", "tools": []any{
|
|
map[string]any{"type": "function", "name": "spawn_agent"},
|
|
map[string]any{"type": "function", "name": "send_message"},
|
|
},
|
|
}},
|
|
"tool_choice": map[string]any{"type": "namespace", "name": "collaboration"},
|
|
}
|
|
|
|
_, changed, err := FlattenResponsesNamespaces(req)
|
|
require.NoError(t, err)
|
|
require.True(t, changed)
|
|
require.Equal(t, "auto", req["tool_choice"])
|
|
}
|
|
|
|
func TestFlattenResponsesNamespacesExcept_PreservesBuiltInNamespaceAndChoice(t *testing.T) {
|
|
req := map[string]any{
|
|
"tools": []any{
|
|
map[string]any{"type": "namespace", "name": "image_gen", "tools": []any{
|
|
map[string]any{"type": "function", "name": "imagegen"},
|
|
}},
|
|
map[string]any{"type": "namespace", "name": "collaboration", "tools": []any{
|
|
map[string]any{"type": "function", "name": "spawn_agent"},
|
|
}},
|
|
},
|
|
"tool_choice": map[string]any{"type": "namespace", "name": "image_gen"},
|
|
}
|
|
|
|
names, changed, err := FlattenResponsesNamespacesExcept(req, map[string]bool{"image_gen": true})
|
|
require.NoError(t, err)
|
|
require.True(t, changed)
|
|
require.Contains(t, names, "collaboration__spawn_agent")
|
|
tools, ok := req["tools"].([]any)
|
|
require.True(t, ok)
|
|
require.Len(t, tools, 2)
|
|
preservedTool, ok := tools[0].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "namespace", preservedTool["type"])
|
|
require.Equal(t, "image_gen", preservedTool["name"])
|
|
flatTool, ok := tools[1].(map[string]any)
|
|
require.True(t, ok)
|
|
require.Equal(t, "function", flatTool["type"])
|
|
require.Equal(t, "collaboration__spawn_agent", flatTool["name"])
|
|
require.Equal(t, map[string]any{"type": "namespace", "name": "image_gen"}, req["tool_choice"])
|
|
}
|
|
|
|
func TestFlattenResponsesNamespaces_RejectsNamespaceCollision(t *testing.T) {
|
|
req := map[string]any{"tools": []any{
|
|
map[string]any{"type": "namespace", "name": "a", "tools": []any{
|
|
map[string]any{"type": "function", "name": "b__c"},
|
|
}},
|
|
map[string]any{"type": "namespace", "name": "a__b", "tools": []any{
|
|
map[string]any{"type": "function", "name": "c"},
|
|
}},
|
|
}}
|
|
|
|
_, _, err := FlattenResponsesNamespaces(req)
|
|
require.ErrorContains(t, err, "both flatten")
|
|
}
|
|
|
|
func TestRestoreResponsesNamespaceCalls_RewritesOnlyFunctionCalls(t *testing.T) {
|
|
payload := []byte(`{"type":"response.completed","response":{"output":[{"type":"function_call","name":"collaboration__spawn_agent","call_id":"call_1","arguments":"{}","extra":"keep"},{"type":"function_call","name":"plain","arguments":"{}"},{"type":"message","name":"collaboration__spawn_agent","content":"<tag>&value</tag>"}]}}`)
|
|
names := map[string]ResponsesNamespaceName{
|
|
"collaboration__spawn_agent": {Namespace: "collaboration", Name: "spawn_agent"},
|
|
}
|
|
|
|
got, changed, err := RestoreResponsesNamespaceCalls(payload, names)
|
|
require.NoError(t, err)
|
|
require.True(t, changed)
|
|
require.JSONEq(t, `{"type":"response.completed","response":{"output":[{"type":"function_call","name":"spawn_agent","namespace":"collaboration","call_id":"call_1","arguments":"{}","extra":"keep"},{"type":"function_call","name":"plain","arguments":"{}"},{"type":"message","name":"collaboration__spawn_agent","content":"<tag>&value</tag>"}]}}`, string(got))
|
|
require.Contains(t, string(got), "<tag>&value</tag>")
|
|
require.NotContains(t, string(got), `\u003c`)
|
|
}
|
|
|
|
func TestRestoreResponsesNamespaceCalls_RewritesLifecycleItems(t *testing.T) {
|
|
for _, eventType := range []string{"response.output_item.added", "response.output_item.done"} {
|
|
t.Run(eventType, func(t *testing.T) {
|
|
payload := []byte(`{"type":"` + eventType + `","item":{"type":"function_call","name":"collaboration__spawn_agent","arguments":"{}"}}`)
|
|
got, changed, err := RestoreResponsesNamespaceCalls(payload, map[string]ResponsesNamespaceName{
|
|
"collaboration__spawn_agent": {Namespace: "collaboration", Name: "spawn_agent"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, changed)
|
|
require.JSONEq(t, `{"type":"`+eventType+`","item":{"type":"function_call","name":"spawn_agent","namespace":"collaboration","arguments":"{}"}}`, string(got))
|
|
})
|
|
}
|
|
}
|