package web import ( "context" "html/template" "log/slog" "net/http" "slices" "strconv" "time" "gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi" "gitea.mynodes.xyz/ilker/llmgw/internal/control/fabrics" "gitea.mynodes.xyz/ilker/llmgw/internal/control/nvlink" "gitea.mynodes.xyz/ilker/llmgw/internal/store" ) // DashboardData is the whole page. type DashboardData struct { // CPU is the fleet's capacity when it has no GPUs, which is shown instead // of a GPU table that would only say there are none. CPU cpuCapacity // Menu is the shared top menu; Nav below is what the cluster page adds. Menu Nav // GPUMap is every card coloured by one figure, and IdleGPUs the replicas // holding cards that are doing nothing; both from the last heartbeats. GPUMap *gpuMap IdleGPUs []idleHolding // Efficiency is what each model gets from its cards, from the metrics // store; nil without one. Efficiency []modelEfficiency // Notice and Problem are what the last change made from this page did, // carried over the redirect back to it. Notice, Problem string Nodes []adminapi.NodeView Models []adminapi.ModelView Replicas []adminapi.ReplicaView Unplaced []adminapi.ExplainView Usage []store.KeyUsage Summary adminapi.Summary // Fabrics is the physical fabrics and what disagrees about them, from a // source that can say. Fabrics *fabrics.Report // NVLink is the multi-node NVLink domains, from a source that can say. NVLink *nvlink.Report Users []store.UserUsage Models2 []store.ModelUsage // Tiles are the headline figures, which are numbers rather than charts. Requests24h int64 Tokens24h int64 Errors24h int64 Spend24h int64 RequestSpark template.HTML TokenSpark template.HTML HasPeople bool // NodePrefix is what every node's name starts with, left off in tables // so a row fits on a line; the full name is on hover. NodePrefix string // Columns shown only when some row has something in them: a CPU-only // cluster's nodes table was three columns of dashes wide. AnyGPUs, AnyClasses, AnyFabrics, AnyFallbacks bool // Charts are pre-rendered rather than assembled in the template: building // SVG out of template fragments is how a chart ends up unescaped. RequestChart template.HTML TokenChart template.HTML LatencyChart template.HTML HasCharts bool NodeCount int ModelCount int ReadyReplicas int TotalReplicas int AuthEnabled bool // OnAdminListener decides which links the page offers. // // The same page is served twice, and the two have different neighbours: // metrics, pprof and the management API exist only on the admin listener, // while account administration and the profile exist only on the // client-facing one. A nav that assumes one of them sends every visitor to // the other into a 404 — or, on the public port, into "no api key was // provided", which is a far more confusing way to say the same thing. OnAdminListener bool // Fleet is the operational charts, nil when no metrics store is configured. Fleet *FleetCharts // Nav says which optional admin pages are mounted. Asked rather than // assumed: a link to a page that was never registered lands on whatever the // catch-all route serves, which on the client-facing port is the landing // page — an admin clicking "mirror" and getting the marketing copy has no // way to tell that from a bug. Nav NavExtras RefreshSec int // RefreshMeta is the reload for a browser without script. RefreshMeta template.HTML Version string Now string } // stateWords are every state a node, a replica, a shard or an engine's // installation can be in. Each is a CSS class and a word; the class stays as // it is and the word is looked up, as dash.state.. A state not listed // here shows its key, which the catalogue test is there to stop. var stateWords = []string{ "ready", "cordoned", "draining", "unreachable", "lost", "unknown", "placing", "pending", "starting", "loading", "stopped", "failed", "installing", "unsupported", "skipped", } func init() { for _, s := range stateWords { registerKeys("dash.state." + s) } // How bad a fabric's or an NVLink domain's problem is. registerKeys("fabric.level.fail", "fabric.level.warn", "fabric.level.info") } // refreshMeta is the meta element that reloads a page every sec seconds, // for the