73a82e6175
Upgrades the routing engine to support tag, token limit, multimodal, reasoning, and tool calling conditions. Adds unit tests for the new routing features.
128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
package providers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"gophergate/internal/models"
|
|
)
|
|
|
|
func TestSanitizeFunctionName(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
expected string
|
|
}{
|
|
{"google-search", "google-search"},
|
|
{"google.search", "google_search"},
|
|
{"google search", "google_search"},
|
|
{"web_search(query)", "web_search_query_"},
|
|
{"", "function"},
|
|
{"123_abc-XYZ", "123_abc-XYZ"},
|
|
{"invalid.name.with.dots", "invalid_name_with_dots"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
actual := sanitizeFunctionName(tc.input)
|
|
if actual != tc.expected {
|
|
t.Errorf("sanitizeFunctionName(%q) = %q; expected %q", tc.input, actual, tc.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMessagesToOpenAIJSON_SanitizeToolCalls(t *testing.T) {
|
|
messages := []models.UnifiedMessage{
|
|
{
|
|
Role: "assistant",
|
|
Content: []models.UnifiedContentPart{
|
|
{Type: "text", Text: "I will use search."},
|
|
},
|
|
ToolCalls: []models.ToolCall{
|
|
{
|
|
ID: "call_1",
|
|
Type: "function",
|
|
Function: models.FunctionCall{
|
|
Name: "google.search",
|
|
Arguments: `{"query": "hello"}`,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Role: "tool",
|
|
Content: []models.UnifiedContentPart{
|
|
{Type: "text", Text: `{"result": "success"}`},
|
|
},
|
|
ToolCallID: stringPtr("call_1"),
|
|
Name: stringPtr("google.search"),
|
|
},
|
|
}
|
|
|
|
res, err := MessagesToOpenAIJSON(messages)
|
|
if err != nil {
|
|
t.Fatalf("MessagesToOpenAIJSON failed: %v", err)
|
|
}
|
|
|
|
if len(res) != 2 {
|
|
t.Fatalf("expected 2 messages, got %d", len(res))
|
|
}
|
|
|
|
// Verify assistant message
|
|
msg1 := res[0].(map[string]interface{})
|
|
if msg1["role"] != "assistant" {
|
|
t.Errorf("expected role assistant, got %v", msg1["role"])
|
|
}
|
|
calls := msg1["tool_calls"].([]models.ToolCall)
|
|
if len(calls) != 1 {
|
|
t.Fatalf("expected 1 tool call, got %d", len(calls))
|
|
}
|
|
if calls[0].Function.Name != "google_search" {
|
|
t.Errorf("expected function name google_search, got %q", calls[0].Function.Name)
|
|
}
|
|
|
|
// Verify tool response message
|
|
msg2 := res[1].(map[string]interface{})
|
|
if msg2["role"] != "tool" {
|
|
t.Errorf("expected role tool, got %v", msg2["role"])
|
|
}
|
|
if msg2["name"] != "google_search" {
|
|
t.Errorf("expected tool name google_search, got %v", msg2["name"])
|
|
}
|
|
}
|
|
|
|
func TestBuildOpenAIBody_SanitizeToolsAndChoice(t *testing.T) {
|
|
req := &models.UnifiedRequest{
|
|
Model: "gpt-4o",
|
|
Tools: []models.Tool{
|
|
{
|
|
Type: "function",
|
|
Function: models.FunctionDef{
|
|
Name: "google.search",
|
|
},
|
|
},
|
|
},
|
|
ToolChoice: json.RawMessage(`{"type": "function", "function": {"name": "google.search"}}`),
|
|
}
|
|
|
|
body := BuildOpenAIBody(req, nil, false)
|
|
|
|
// Verify tools
|
|
tools := body["tools"].([]models.Tool)
|
|
if len(tools) != 1 {
|
|
t.Fatalf("expected 1 tool, got %d", len(tools))
|
|
}
|
|
if tools[0].Function.Name != "google_search" {
|
|
t.Errorf("expected tool function name google_search, got %q", tools[0].Function.Name)
|
|
}
|
|
|
|
// Verify tool_choice
|
|
toolChoice := body["tool_choice"].(map[string]interface{})
|
|
funcObj := toolChoice["function"].(map[string]interface{})
|
|
if funcObj["name"] != "google_search" {
|
|
t.Errorf("expected tool_choice function name google_search, got %q", funcObj["name"])
|
|
}
|
|
}
|
|
|
|
func stringPtr(s string) *string {
|
|
return &s
|
|
}
|