Files
Luminary/internal/gateway/routing.go
T
renjue 76ba500417
CI / docker (push) Successful in 2m4s
Initial commit: Luminary AI Gateway
OpenAI-compatible AI gateway with Vue admin UI, multi-provider egress,
ingress key governance, monitoring, and security controls.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 21:46:16 +08:00

105 lines
2.8 KiB
Go

package gateway
import (
"strings"
"github.com/rose_cat707/luminary/internal/model"
"github.com/rose_cat707/luminary/internal/store/cache"
)
type routeTarget struct {
ProviderID uint
ProviderKeyID uint // 0 = auto load balance
}
func (s *Service) resolveRoute(ingressID uint, modelName string) *routeTarget {
rules := s.cache.ListRoutingRules(ingressID)
var best *model.RoutingRule
for i := range rules {
r := rules[i]
if !r.Enabled {
continue
}
if !matchPattern(r.ModelPattern, modelName) {
continue
}
if best == nil || r.Priority > best.Priority {
best = &r
}
}
if best == nil {
return nil
}
return &routeTarget{ProviderID: best.ProviderID, ProviderKeyID: best.ProviderKeyID}
}
func matchPattern(pattern, model string) bool {
if pattern == "" || pattern == "*" {
return true
}
if strings.HasSuffix(pattern, "*") {
return strings.HasPrefix(model, strings.TrimSuffix(pattern, "*"))
}
return pattern == model
}
func (s *Service) findProvidersForModel(modelName string, allowed []string, category model.ProviderCategory, route *routeTarget) ([]model.Provider, error) {
if route != nil {
if p, ok := s.cache.GetProvider(route.ProviderID); ok && p.Category == category && p.Enabled {
if cache.MatchesList(allowed, p.Name) || cache.MatchesList(allowed, string(p.Type)) || cache.MatchesList(allowed, "*") {
return []model.Provider{p}, nil
}
}
}
var matched []model.Provider
for _, p := range s.cache.ListProviders() {
if p.Category != category || !model.IsCategorySupported(p.Category) || !p.Enabled {
continue
}
if !cache.MatchesList(allowed, p.Name) && !cache.MatchesList(allowed, string(p.Type)) {
continue
}
if s.providerSupportsModel(&p, modelName) {
matched = append(matched, p)
}
}
if len(matched) == 0 {
if parts := strings.SplitN(modelName, "/", 2); len(parts) == 2 {
if p, ok := s.cache.GetProviderByName(parts[0]); ok && p.Category == category && p.Enabled {
if s.providerSupportsModel(&p, parts[1]) {
return []model.Provider{p}, nil
}
}
}
return nil, errNoProvider(modelName)
}
return matched, nil
}
func (s *Service) providerSupportsModel(p *model.Provider, modelName string) bool {
models := s.cache.GetModels(p.ID)
if len(models) == 0 {
for _, k := range s.cache.ListProviderKeys(p.ID) {
allowed := cache.ParseJSONList(k.ModelsJSON)
if cache.MatchesList(allowed, modelName) || cache.MatchesList(allowed, "*") {
return true
}
}
return false
}
for _, m := range models {
if m.Enabled && modelEntryMatches(m, modelName) {
return true
}
}
return false
}
func errNoProvider(model string) error {
return &gatewayError{msg: "no provider found for model " + model}
}
type gatewayError struct{ msg string }
func (e *gatewayError) Error() string { return e.msg }