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:
@@ -10,6 +10,22 @@ import (
|
||||
"gophergate/internal/models"
|
||||
)
|
||||
|
||||
func sanitizeFunctionName(name string) string {
|
||||
var sb strings.Builder
|
||||
for _, ch := range name {
|
||||
if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' {
|
||||
sb.WriteRune(ch)
|
||||
} else {
|
||||
sb.WriteRune('_')
|
||||
}
|
||||
}
|
||||
res := sb.String()
|
||||
if res == "" {
|
||||
return "function"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// MessagesToOpenAIJSON converts unified messages to OpenAI-compatible JSON, including tools and images.
|
||||
func MessagesToOpenAIJSON(messages []models.UnifiedMessage) ([]interface{}, error) {
|
||||
var result []interface{}
|
||||
@@ -35,7 +51,7 @@ func MessagesToOpenAIJSON(messages []models.UnifiedMessage) ([]interface{}, erro
|
||||
msg["tool_call_id"] = id
|
||||
|
||||
if m.Name != nil {
|
||||
msg["name"] = *m.Name
|
||||
msg["name"] = sanitizeFunctionName(*m.Name)
|
||||
}
|
||||
result = append(result, msg)
|
||||
continue
|
||||
@@ -91,6 +107,7 @@ func MessagesToOpenAIJSON(messages []models.UnifiedMessage) ([]interface{}, erro
|
||||
if sanitizedCalls[i].Type == "" {
|
||||
sanitizedCalls[i].Type = "function"
|
||||
}
|
||||
sanitizedCalls[i].Function.Name = sanitizeFunctionName(sanitizedCalls[i].Function.Name)
|
||||
}
|
||||
msg["tool_calls"] = sanitizedCalls
|
||||
msg["content"] = "" // OpenAI requirement: content must be string if tool_calls present
|
||||
@@ -124,11 +141,25 @@ func BuildOpenAIBody(request *models.UnifiedRequest, messagesJSON []interface{},
|
||||
body["max_tokens"] = *request.MaxTokens
|
||||
}
|
||||
if len(request.Tools) > 0 {
|
||||
body["tools"] = request.Tools
|
||||
sanitizedTools := make([]models.Tool, len(request.Tools))
|
||||
copy(sanitizedTools, request.Tools)
|
||||
for i := range sanitizedTools {
|
||||
if sanitizedTools[i].Type == "function" {
|
||||
sanitizedTools[i].Function.Name = sanitizeFunctionName(sanitizedTools[i].Function.Name)
|
||||
}
|
||||
}
|
||||
body["tools"] = sanitizedTools
|
||||
}
|
||||
if request.ToolChoice != nil {
|
||||
var toolChoice interface{}
|
||||
if err := json.Unmarshal(request.ToolChoice, &toolChoice); err == nil {
|
||||
if tcMap, ok := toolChoice.(map[string]interface{}); ok {
|
||||
if funcMap, ok := tcMap["function"].(map[string]interface{}); ok {
|
||||
if name, ok := funcMap["name"].(string); ok {
|
||||
funcMap["name"] = sanitizeFunctionName(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
body["tool_choice"] = toolChoice
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user