mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-12 04:54:45 +00:00
feat(admin): rates + discovery flag in API
Extend GET /admin/api/overview's existing providers payload rather than adding a sibling endpoint: the route already returns one providers array per request on the dashboard's 5s poll, is already admin-gated and stats-excluded, and the new fields are one more batched limiter read sharing that same request — a second endpoint would duplicate all three properties for no benefit. adminProviderView gains AttemptsDay/FailuresDay/AttemptsMinute/ FailuresMinute (the provider's own current-window counters) and ModelRates (a per-model breakdown, keyed by upstream model id, via the new adminModelRateView) — Feature A. buildAdminOverview builds every provider's and every (provider, model) pair's limitScope in two passes and reads them all through limiter.providerUsage in ONE storeGetMulti call, then slices the flat result back by index. adminProviderView also gains DiscoveryEnabled, mirroring a new providerSnapshot.discoveryEnabled field (registry.go) read straight off providerState — it is set once at construction and never mutated afterward, so it needs neither providerState's mutex nor a place in its existing snapshot() method's mutable-field return shape. Feature B: this lets the webui tell "discovery is off" apart from "discovery is on but has not refreshed yet", which previously both showed the same zero lastRefresh as an identical "refreshed never".
This commit is contained in:
+24
-7
@@ -778,6 +778,22 @@ type providerSnapshot struct {
|
||||
lastErr string
|
||||
models []string
|
||||
modelCount int
|
||||
// discoveryEnabled mirrors providerState.discoveryEnabled (Feature B,
|
||||
// v0.22 last-refresh label honesty): read directly rather than through
|
||||
// providerState.snapshot(), because it is set once at construction
|
||||
// (newModelRegistry, from ProviderConfig.Discovery) and never
|
||||
// reassigned afterward — every other providerState field warmFill/
|
||||
// maybeRefresh/finishRefresh mutate only reads it, never writes it — so
|
||||
// it needs neither the mutex snapshot() takes for the fields that DO
|
||||
// mutate
|
||||
// (models/lastRefresh/lastErr) nor a place in that method's return
|
||||
// shape. The admin dashboard needs it to tell "discovery is off" apart
|
||||
// from "discovery is on but has not refreshed yet" — both currently
|
||||
// read identically as a zero LastRefresh, which GET /admin/api/overview
|
||||
// (admin.go) and the Providers tab (webui) previously rendered as the
|
||||
// misleading "refreshed never" for a provider that will never refresh
|
||||
// by design.
|
||||
discoveryEnabled bool
|
||||
}
|
||||
|
||||
// aliasSnapshotEntry is one configured alias's read-only view for the
|
||||
@@ -822,13 +838,14 @@ func (m *modelRegistry) snapshot() []providerSnapshot {
|
||||
// that class of trap entirely.
|
||||
models, lastRefresh, lastErr := m.states[name].snapshot()
|
||||
out = append(out, providerSnapshot{
|
||||
name: adapter.name(),
|
||||
typeName: adapter.typeName(),
|
||||
baseURL: adapter.base(),
|
||||
models: models,
|
||||
modelCount: len(models),
|
||||
lastRefresh: lastRefresh,
|
||||
lastErr: lastErr,
|
||||
name: adapter.name(),
|
||||
typeName: adapter.typeName(),
|
||||
baseURL: adapter.base(),
|
||||
models: models,
|
||||
modelCount: len(models),
|
||||
lastRefresh: lastRefresh,
|
||||
lastErr: lastErr,
|
||||
discoveryEnabled: m.states[name].discoveryEnabled,
|
||||
})
|
||||
}
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user