| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package service
- import (
- "context"
- "fmt"
- "sort"
- "strings"
- )
- type CompositeRouteResolver struct {
- repo CompositeModelRouteRepository
- modelOwnershipResolver CompositeModelOwnershipResolver
- }
- func NewCompositeRouteResolver(repo CompositeModelRouteRepository) *CompositeRouteResolver {
- return &CompositeRouteResolver{repo: repo}
- }
- func (r *CompositeRouteResolver) SetModelOwnershipResolver(resolver CompositeModelOwnershipResolver) {
- if r != nil {
- r.modelOwnershipResolver = resolver
- }
- }
- func (r *CompositeRouteResolver) Resolve(ctx context.Context, groupID int64, model, endpoint string) (CompositeRouteDecision, error) {
- model = strings.TrimSpace(model)
- endpoint = normalizeCompositeRouteEndpoint(endpoint)
- decision := CompositeRouteDecision{
- GroupID: groupID,
- PublicModel: model,
- Endpoint: endpoint,
- }
- if model == "" {
- decision.Reason = "model is required"
- return decision, nil
- }
- if r != nil && r.repo != nil && groupID > 0 {
- routes, err := r.repo.ListByGroup(ctx, groupID, false)
- if err != nil {
- return decision, fmt.Errorf("list composite routes: %w", err)
- }
- if route, ok := matchCompositeRoute(routes, model, endpoint); ok {
- upstreamModel := strings.TrimSpace(route.UpstreamModel)
- if upstreamModel == "" {
- upstreamModel = model
- }
- return CompositeRouteDecision{
- Matched: true,
- Source: CompositeRouteSourceExplicit,
- GroupID: groupID,
- PublicModel: model,
- TargetPlatform: route.TargetPlatform,
- UpstreamModel: upstreamModel,
- Endpoint: endpoint,
- Route: &route,
- }, nil
- }
- }
- if r != nil && r.modelOwnershipResolver != nil && groupID > 0 {
- ownership, err := r.modelOwnershipResolver(ctx, groupID, model)
- if err != nil {
- // A recognizable model can still use the existing detector when the
- // account catalog is temporarily unavailable. Unknown aliases cannot.
- if _, detectable := DetectModelPlatform(model); !detectable {
- return decision, fmt.Errorf("resolve account model ownership: %w", err)
- }
- } else if ownership.Ambiguous {
- decision.Reason = "model is exposed by multiple provider platforms"
- return decision, nil
- } else if ownership.Matched {
- platform := strings.TrimSpace(ownership.TargetPlatform)
- if !isConcreteRequestPlatform(platform) {
- decision.Reason = "account model ownership has no concrete target platform"
- return decision, nil
- }
- return CompositeRouteDecision{
- Matched: true,
- Source: CompositeRouteSourceAccount,
- GroupID: groupID,
- PublicModel: model,
- TargetPlatform: platform,
- UpstreamModel: model,
- Endpoint: endpoint,
- }, nil
- }
- }
- if platform, ok := DetectModelPlatform(model); ok {
- return CompositeRouteDecision{
- Matched: true,
- Source: CompositeRouteSourceDetector,
- GroupID: groupID,
- PublicModel: model,
- TargetPlatform: platform,
- UpstreamModel: model,
- Endpoint: endpoint,
- }, nil
- }
- decision.Reason = "no explicit route or built-in detector match"
- return decision, nil
- }
- func matchCompositeRoute(routes []CompositeModelRoute, model, endpoint string) (CompositeModelRoute, bool) {
- if len(routes) == 0 {
- return CompositeModelRoute{}, false
- }
- type candidate struct {
- route CompositeModelRoute
- matchStrength int
- endpointWeight int
- prefixLen int
- }
- candidates := make([]candidate, 0, len(routes))
- for _, route := range routes {
- route.Endpoint = normalizeCompositeRouteEndpoint(route.Endpoint)
- if route.Endpoint != endpoint && route.Endpoint != CompositeRouteEndpointAny {
- continue
- }
- route.MatchType = normalizeCompositeRouteMatchType(route.MatchType)
- publicModel := strings.TrimSpace(route.PublicModel)
- if publicModel == "" {
- continue
- }
- matchStrength := 0
- prefixLen := len(publicModel)
- switch route.MatchType {
- case CompositeRouteMatchExact:
- if publicModel != model {
- continue
- }
- matchStrength = 2
- case CompositeRouteMatchPrefix:
- if !strings.HasPrefix(model, publicModel) {
- continue
- }
- matchStrength = 1
- default:
- continue
- }
- endpointWeight := 0
- if route.Endpoint == endpoint {
- endpointWeight = 1
- }
- candidates = append(candidates, candidate{
- route: route,
- matchStrength: matchStrength,
- endpointWeight: endpointWeight,
- prefixLen: prefixLen,
- })
- }
- if len(candidates) == 0 {
- return CompositeModelRoute{}, false
- }
- sort.SliceStable(candidates, func(i, j int) bool {
- a, b := candidates[i], candidates[j]
- if a.matchStrength != b.matchStrength {
- return a.matchStrength > b.matchStrength
- }
- if a.endpointWeight != b.endpointWeight {
- return a.endpointWeight > b.endpointWeight
- }
- if a.prefixLen != b.prefixLen {
- return a.prefixLen > b.prefixLen
- }
- if a.route.Priority != b.route.Priority {
- return a.route.Priority < b.route.Priority
- }
- return a.route.ID < b.route.ID
- })
- return candidates[0].route, true
- }
|