feat: add hierarchical routing — groups can target other groups
CI / Lint (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (push) Has been cancelled

RouteToConcrete() recursively resolves group chains until a concrete
model is reached, with cycle detection and max depth (10) guard.

Example: all-purpose -> fast-flow -> deepseek-v4-flash
The dashboard log shows the full chain: 'deepseek-v4-flash (hierarchical:
fast-flow (default (first target)) -> deepseek-v4-flash (default (first target)))'
This commit is contained in:
2026-05-07 12:28:31 -04:00
parent 19517b0847
commit 7517307c11
2 changed files with 51 additions and 5 deletions
+7 -5
View File
@@ -541,18 +541,20 @@ func (s *Server) handleChatCompletions(c *gin.Context) {
}
}
// Check if model is a group and route to a concrete model
// Resolve model groups to concrete models (hierarchical — groups can target groups)
modelGroup := ""
if s.modelRouter != nil && s.modelRouter.IsGroup(modelID) {
modelGroup = modelID
if s.modelRouter != nil {
userMessage := extractUserMessage(req.Messages)
decision, err := s.modelRouter.Route(c.Request.Context(), modelID, userMessage)
decision, err := s.modelRouter.RouteToConcrete(c.Request.Context(), modelID, userMessage)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("model routing failed: %v", err)})
return
}
if decision.SelectedModel != modelID {
modelGroup = modelID
}
modelID = decision.SelectedModel
log.Printf("[ROUTER] %s -> %s (%s: %s)", modelGroup, modelID, decision.Strategy, decision.Reason)
log.Printf("[ROUTER] %s (%s: %s)", modelID, decision.Strategy, decision.Reason)
}
// Convert ChatCompletionRequest to UnifiedRequest