Resolve runs the full classify → match → load pipeline for a router model config. It is transport-agnostic: callers pass a built classifier, a candidate loader, and a probe; Resolve returns a ResolveResult or an error if the resolved config violates invariants or the fallback can't be loaded. Error
(ctx context.Context, routerCfg *config.ModelConfig, classifier Classifier, loader CandidateLoader, probe Probe)
| 73 | // pushes resolution straight to the fallback path (mirrors the |
| 74 | // classifier-build-error branch in the historical RouteModel middleware). |
| 75 | func Resolve(ctx context.Context, routerCfg *config.ModelConfig, classifier Classifier, loader CandidateLoader, probe Probe) (*ResolveResult, error) { |
| 76 | if routerCfg == nil || !routerCfg.HasRouter() { |
| 77 | return nil, fmt.Errorf("router.Resolve: config has no router block") |
| 78 | } |
| 79 | |
| 80 | if classifier == nil { |
| 81 | return resolveFallback(routerCfg, loader, Decision{}, LabelFallback, "classifier unavailable") |
| 82 | } |
| 83 | |
| 84 | start := time.Now() |
| 85 | decision, err := classifier.Classify(ctx, probe) |
| 86 | if err != nil { |
| 87 | return resolveFallback(routerCfg, loader, Decision{Latency: time.Since(start)}, classifier.Name(), "classifier error: "+err.Error()) |
| 88 | } |
| 89 | |
| 90 | candidate := MatchCandidate(routerCfg.Router.Candidates, decision.Labels) |
| 91 | if candidate == "" { |
| 92 | return resolveFallback(routerCfg, loader, decision, classifier.Name(), "no candidate covers labels: "+strings.Join(decision.Labels, ",")) |
| 93 | } |
| 94 | |
| 95 | candidateCfg, err := loader(candidate) |
| 96 | if err != nil || candidateCfg == nil { |
| 97 | return nil, fmt.Errorf("router candidate %q not loadable: %w", candidate, err) |
| 98 | } |
| 99 | if candidateCfg.HasRouter() { |
| 100 | return nil, fmt.Errorf("router candidate %q is itself a router (depth-1 invariant)", candidate) |
| 101 | } |
| 102 | |
| 103 | return &ResolveResult{ |
| 104 | RouterModel: routerCfg.Name, |
| 105 | ChosenModel: candidate, |
| 106 | ChosenConfig: candidateCfg, |
| 107 | Decision: decision, |
| 108 | Labels: decision.Labels, |
| 109 | ClassifierName: classifier.Name(), |
| 110 | UsedFallback: false, |
| 111 | }, nil |
| 112 | } |
| 113 | |
| 114 | // resolveFallback handles the three failure modes that fall through to |
| 115 | // cfg.Router.Fallback: classifier build failed, Classify returned an |
no test coverage detected