sub2api/backend/internal/pkg/windsurf/nlu_extractor_test.go
win de048fad25 chore(wip): save Windsurf/Antigravity/ops customizations before upstream merge
WIP commit保存以下定制工作以便后续合并 upstream v0.1.124-125:
- Windsurf: tier access service, NLU extractor, cold threshold, Google login
- Antigravity: client/oauth 调整
- Ops: log stream handler/broadcaster/middleware, OpsLogStreamView
- Frontend: WindsurfLoginModal Google, GoogleIcon, AccountsView, sidebar/router/i18n
2026-05-09 00:41:19 +08:00

145 lines
4.0 KiB
Go

package windsurf
import (
"testing"
)
func TestExtractToolCallsNLU(t *testing.T) {
tools := []string{"edit_file", "read_file", "run_command"}
tests := []struct {
name string
text string
available []string
wantCount int
wantName string
}{
{
name: "marker form: function: edit_file with JSON",
text: `I'll use function: edit_file with {"path": "/tmp/x", "content": "abc"}`,
available: tools,
wantCount: 1,
wantName: "edit_file",
},
{
name: "marker form: tool_call read_file",
text: `tool_call: read_file arguments: {"path": "/etc/hosts"}`,
available: tools,
wantCount: 1,
wantName: "read_file",
},
{
name: "marker form: nested JSON object",
text: `function: run_command with {"cmd": "ls", "opts": {"long": true}}`,
available: tools,
wantCount: 1,
wantName: "run_command",
},
{
name: "bare name fallback when no marker",
text: `Sure, I'll edit_file {"path": "/tmp/y"} for you.`,
available: tools,
wantCount: 1,
wantName: "edit_file",
},
{
name: "unknown tool name rejected when available list is non-empty",
text: `function: delete_universe {"target": "all"}`,
available: tools,
wantCount: 0,
},
{
name: "no JSON after marker yields no call",
text: `function: edit_file but I'm not sure what arguments to use`,
available: tools,
wantCount: 0,
},
{
name: "empty text returns nil",
text: "",
available: tools,
wantCount: 0,
},
{
name: "duplicate names deduplicated",
text: `function: edit_file {"path": "/a"} then function: edit_file {"path": "/b"}`,
available: tools,
wantCount: 1,
wantName: "edit_file",
},
{
name: "name not in available list is rejected even when JSON valid",
text: `Calling foo with {"x": 1}`,
available: []string{"bar"},
wantCount: 0,
},
{
name: "marker with no available list still extracts",
text: `function: my_tool {"x": 1}`,
available: nil,
wantCount: 1,
wantName: "my_tool",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := ExtractToolCallsNLU(tc.text, tc.available)
if len(got) != tc.wantCount {
t.Fatalf("expected %d call(s), got %d: %+v", tc.wantCount, len(got), got)
}
if tc.wantCount > 0 && got[0].Name != tc.wantName {
t.Fatalf("expected name %q, got %q", tc.wantName, got[0].Name)
}
if tc.wantCount > 0 && got[0].ArgumentsJSON == "" {
t.Fatalf("expected non-empty ArgumentsJSON, got %q", got[0].ArgumentsJSON)
}
})
}
}
func TestHasNLUSignal(t *testing.T) {
tests := []struct {
text string
want bool
}{
{"function: edit_file {}", true},
{"I'll call edit_file", true},
{"calling tool edit_file", true},
{"调用工具 edit_file", true},
{"Hello, just a chat reply.", false},
{"", false},
{"<tool_use><name>foo</name></tool_use>", false},
}
for _, tc := range tests {
t.Run(tc.text, func(t *testing.T) {
if got := HasNLUSignal(tc.text); got != tc.want {
t.Fatalf("HasNLUSignal(%q) = %v, want %v", tc.text, got, tc.want)
}
})
}
}
func TestResolveEmulationFlavor(t *testing.T) {
tests := []struct {
name string
meta *ModelMeta
want string
}{
{"nil meta", nil, EmulationFlavorAuto},
{"explicit override wins", &ModelMeta{Provider: "anthropic", EmulationFlavor: "nlu"}, "nlu"},
{"anthropic default tool_use", &ModelMeta{Provider: "anthropic"}, EmulationFlavorToolUse},
{"zhipu default nlu", &ModelMeta{Provider: "zhipu"}, EmulationFlavorNLU},
{"moonshot default nlu", &ModelMeta{Provider: "moonshot"}, EmulationFlavorNLU},
{"openai default auto", &ModelMeta{Provider: "openai"}, EmulationFlavorAuto},
{"unknown provider auto", &ModelMeta{Provider: "xyz"}, EmulationFlavorAuto},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := ResolveEmulationFlavor(tc.meta); got != tc.want {
t.Fatalf("ResolveEmulationFlavor = %q, want %q", got, tc.want)
}
})
}
}