完善 function_call_output 续链校验与引用匹配 续链场景强制 store=true,过滤 input 时避免副作用 补充续链判断与过滤相关单元测试 测试: go test ./...
99 lines
4.0 KiB
Go
99 lines
4.0 KiB
Go
package service
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
func TestNeedsToolContinuationSignals(t *testing.T) {
|
||
// 覆盖所有触发续链的信号来源,确保判定逻辑完整。
|
||
cases := []struct {
|
||
name string
|
||
body map[string]any
|
||
want bool
|
||
}{
|
||
{name: "nil", body: nil, want: false},
|
||
{name: "previous_response_id", body: map[string]any{"previous_response_id": "resp_1"}, want: true},
|
||
{name: "previous_response_id_blank", body: map[string]any{"previous_response_id": " "}, want: false},
|
||
{name: "function_call_output", body: map[string]any{"input": []any{map[string]any{"type": "function_call_output"}}}, want: true},
|
||
{name: "item_reference", body: map[string]any{"input": []any{map[string]any{"type": "item_reference"}}}, want: true},
|
||
{name: "tools", body: map[string]any{"tools": []any{map[string]any{"type": "function"}}}, want: true},
|
||
{name: "tools_empty", body: map[string]any{"tools": []any{}}, want: false},
|
||
{name: "tools_invalid", body: map[string]any{"tools": "bad"}, want: false},
|
||
{name: "tool_choice", body: map[string]any{"tool_choice": "auto"}, want: true},
|
||
{name: "tool_choice_object", body: map[string]any{"tool_choice": map[string]any{"type": "function"}}, want: true},
|
||
{name: "tool_choice_empty_object", body: map[string]any{"tool_choice": map[string]any{}}, want: false},
|
||
{name: "none", body: map[string]any{"input": []any{map[string]any{"type": "text", "text": "hi"}}}, want: false},
|
||
}
|
||
|
||
for _, tt := range cases {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
require.Equal(t, tt.want, NeedsToolContinuation(tt.body))
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestHasFunctionCallOutput(t *testing.T) {
|
||
// 仅当 input 中存在 function_call_output 才视为续链输出。
|
||
require.False(t, HasFunctionCallOutput(nil))
|
||
require.True(t, HasFunctionCallOutput(map[string]any{
|
||
"input": []any{map[string]any{"type": "function_call_output"}},
|
||
}))
|
||
require.False(t, HasFunctionCallOutput(map[string]any{
|
||
"input": "text",
|
||
}))
|
||
}
|
||
|
||
func TestHasToolCallContext(t *testing.T) {
|
||
// tool_call/function_call 必须包含 call_id,才能作为可关联上下文。
|
||
require.False(t, HasToolCallContext(nil))
|
||
require.True(t, HasToolCallContext(map[string]any{
|
||
"input": []any{map[string]any{"type": "tool_call", "call_id": "call_1"}},
|
||
}))
|
||
require.True(t, HasToolCallContext(map[string]any{
|
||
"input": []any{map[string]any{"type": "function_call", "call_id": "call_2"}},
|
||
}))
|
||
require.False(t, HasToolCallContext(map[string]any{
|
||
"input": []any{map[string]any{"type": "tool_call"}},
|
||
}))
|
||
}
|
||
|
||
func TestFunctionCallOutputCallIDs(t *testing.T) {
|
||
// 仅提取非空 call_id,去重后返回。
|
||
require.Empty(t, FunctionCallOutputCallIDs(nil))
|
||
callIDs := FunctionCallOutputCallIDs(map[string]any{
|
||
"input": []any{
|
||
map[string]any{"type": "function_call_output", "call_id": "call_1"},
|
||
map[string]any{"type": "function_call_output", "call_id": ""},
|
||
map[string]any{"type": "function_call_output", "call_id": "call_1"},
|
||
},
|
||
})
|
||
require.ElementsMatch(t, []string{"call_1"}, callIDs)
|
||
}
|
||
|
||
func TestHasFunctionCallOutputMissingCallID(t *testing.T) {
|
||
require.False(t, HasFunctionCallOutputMissingCallID(nil))
|
||
require.True(t, HasFunctionCallOutputMissingCallID(map[string]any{
|
||
"input": []any{map[string]any{"type": "function_call_output"}},
|
||
}))
|
||
require.False(t, HasFunctionCallOutputMissingCallID(map[string]any{
|
||
"input": []any{map[string]any{"type": "function_call_output", "call_id": "call_1"}},
|
||
}))
|
||
}
|
||
|
||
func TestHasItemReferenceForCallIDs(t *testing.T) {
|
||
// item_reference 需要覆盖所有 call_id 才视为可关联上下文。
|
||
require.False(t, HasItemReferenceForCallIDs(nil, []string{"call_1"}))
|
||
require.False(t, HasItemReferenceForCallIDs(map[string]any{}, []string{"call_1"}))
|
||
req := map[string]any{
|
||
"input": []any{
|
||
map[string]any{"type": "item_reference", "id": "call_1"},
|
||
map[string]any{"type": "item_reference", "id": "call_2"},
|
||
},
|
||
}
|
||
require.True(t, HasItemReferenceForCallIDs(req, []string{"call_1"}))
|
||
require.True(t, HasItemReferenceForCallIDs(req, []string{"call_1", "call_2"}))
|
||
require.False(t, HasItemReferenceForCallIDs(req, []string{"call_1", "call_3"}))
|
||
}
|