Files
GopherGate/internal/router/heuristic.go
T

74 lines
2.1 KiB
Go

package router
import (
"context"
"encoding/json"
"strings"
"gophergate/internal/db"
)
// HeuristicRule defines a pattern-based routing rule.
type HeuristicRule struct {
Pattern string `json:"pattern"`
TargetIdx int `json:"target"`
CaseSensitive bool `json:"case_sensitive,omitempty"`
}
func routeHeuristic(group db.ModelGroup, targets []string, userMessage string) (*Decision, error) {
selected := targets[0]
reason := "default (first target)"
// If heuristic_rules is set, use them
if group.HeuristicRules != nil && *group.HeuristicRules != "" {
var rules []HeuristicRule
if err := json.Unmarshal([]byte(*group.HeuristicRules), &rules); err == nil {
searchMsg := userMessage
for _, rule := range rules {
pattern := rule.Pattern
msg := searchMsg
if !rule.CaseSensitive {
pattern = strings.ToLower(pattern)
msg = strings.ToLower(msg)
}
if strings.Contains(msg, pattern) {
if rule.TargetIdx >= 0 && rule.TargetIdx < len(targets) {
selected = targets[rule.TargetIdx]
reason = "matched heuristic rule: " + rule.Pattern
break
}
}
}
}
}
// Built-in fallback heuristics
if reason == "default (first target)" && len(targets) > 1 {
msgLower := strings.ToLower(userMessage)
complexIndicators := []string{
"step by step", "explain in detail", "reason through",
"think carefully", "analyze", "debug", "write code",
"implement", "refactor", "architecture",
}
for _, indicator := range complexIndicators {
if strings.Contains(msgLower, indicator) {
selected = targets[len(targets)-1]
reason = "complex task indicator: " + indicator
break
}
}
}
return &Decision{
SelectedModel: selected,
Strategy: "heuristic",
Reason: reason,
}, nil
}
// routeClassifier is a stub — real implementation in classifier.go (Task 3).
// Falls back to heuristic routing for now.
func routeClassifier(ctx context.Context, classify ClassifierFunc, group db.ModelGroup, targets []string, userMessage string) (*Decision, error) {
return routeHeuristic(group, targets, userMessage)
}