feat: implement advanced condition-based heuristic model routing
Upgrades the routing engine to support tag, token limit, multimodal, reasoning, and tool calling conditions. Adds unit tests for the new routing features.
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user