mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-22 01:48:01 +00:00
feat(auto): AUTO-only priority chain with tiered slots + live source probing; CSV export w/ key names; audit persistence; fix prompt token accounting & deepseek thinking
This commit is contained in:
@ -3,9 +3,11 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Registry holds all configured providers and routes model requests.
|
||||
@ -143,6 +145,36 @@ func (r *Registry) ProviderForModel(model string) *Provider {
|
||||
return p
|
||||
}
|
||||
|
||||
// ProviderForSlot returns the provider for a (model, source) slot. When
|
||||
// source is empty it behaves like ProviderForModel (owner of the model id);
|
||||
// when source is set it returns only that exact source (nil if the source
|
||||
// does not serve the model).
|
||||
func (r *Registry) ProviderForSlot(model, source string) *Provider {
|
||||
model = strings.ToLower(strings.TrimSpace(model))
|
||||
source = strings.TrimSpace(source)
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
if source == "" {
|
||||
p, ok := r.byModel[model]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
}
|
||||
for _, p := range r.providers {
|
||||
if !strings.EqualFold(p.Name(), source) {
|
||||
continue
|
||||
}
|
||||
for _, m := range p.cfg.Models {
|
||||
if strings.EqualFold(m.ID, model) {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Default returns the highest-priority available provider.
|
||||
func (r *Registry) Default() *Provider {
|
||||
chain := r.AUTOChain()
|
||||
@ -161,6 +193,27 @@ type SourceStatus struct {
|
||||
Available bool `json:"available"`
|
||||
Healthy bool `json:"healthy"`
|
||||
MaxConcurrent int `json:"max_concurrent"`
|
||||
LiveAvailable bool `json:"live_available"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
LastChecked int64 `json:"last_checked,omitempty"`
|
||||
}
|
||||
|
||||
// ProbeAll runs a live reachability check for every provider (in parallel).
|
||||
func (r *Registry) ProbeAll(ctx context.Context) {
|
||||
r.mu.RLock()
|
||||
providers := append([]*Provider(nil), r.providers...)
|
||||
r.mu.RUnlock()
|
||||
var wg sync.WaitGroup
|
||||
for _, p := range providers {
|
||||
wg.Add(1)
|
||||
go func(p *Provider) {
|
||||
defer wg.Done()
|
||||
probeCtx, cancel := context.WithTimeout(ctx, 6*time.Second)
|
||||
defer cancel()
|
||||
p.Probe(probeCtx)
|
||||
}(p)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (r *Registry) Status() []SourceStatus {
|
||||
@ -168,16 +221,20 @@ func (r *Registry) Status() []SourceStatus {
|
||||
defer r.mu.RUnlock()
|
||||
out := make([]SourceStatus, 0, len(r.providers))
|
||||
for _, p := range r.providers {
|
||||
s := SourceStatus{
|
||||
Name: p.Name(),
|
||||
Adapter: p.Adapter(),
|
||||
BaseURL: p.Config().BaseURL,
|
||||
Models: p.Models(),
|
||||
Available: p.Available(),
|
||||
Healthy: p.Available(),
|
||||
MaxConcurrent: p.MaxConcurrent(),
|
||||
}
|
||||
out = append(out, s)
|
||||
live, lastErr, lastAt := p.LastProbe()
|
||||
s := SourceStatus{
|
||||
Name: p.Name(),
|
||||
Adapter: p.Adapter(),
|
||||
BaseURL: p.Config().BaseURL,
|
||||
Models: p.Models(),
|
||||
Available: p.Available(),
|
||||
Healthy: p.Available(),
|
||||
MaxConcurrent: p.MaxConcurrent(),
|
||||
LiveAvailable: live,
|
||||
LastError: lastErr,
|
||||
LastChecked: lastAt,
|
||||
}
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user