{{define "dashboard"}}
{{.NodeCount}} nodes · {{.ReadyReplicas}} of {{.TotalReplicas}} replicas ready · {{.ModelCount}} models
{{if .OnAdminListener}}{{else}}{{template "nav" .Menu}}{{end}}| Class | In use | Free | Faulty | Usage |
|---|---|---|---|---|
{{.Class}} |
{{.Used}}/{{.Total}} | {{.Free}} | {{if .Unhealthy}}{{.Unhealthy}}{{else}}—{{end}} | {{pct .Used .Total}}% |
No nodes have registered.
{{end}}{{.}}
{{end}}{{with .Problem}}{{.}}
{{end}} {{if .Nodes}}| Node | State | CPU | RAM | {{if .AnyGPUs}}GPUs | {{end}}Disk | Engines | {{if .AnyGPUs}}vLLM build | {{end}}{{if .AnyClasses}}Classes | {{end}}{{if .AnyFabrics}}Fabrics | {{end}}Models | Replicas | Seen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
{{shortNode .ID $.NodePrefix}}{{if .MgmtAddr}} {{.MgmtAddr}}{{end}}
logs |
{{.State}} | {{/* A dash, not an empty bar, when the agent has not measured it lately: unknown is not idle. */}}{{with .Load}}{{template "minibar" pctbar .CPUPercent}}{{printf "%.0f" .CPUPercent}}%{{if .CPUs}} of {{.CPUs}}{{end}}{{else}}—{{end}} | {{with .Load}}{{template "minibar" pctbar .MemPercent}}{{bytes .MemUsed}} of {{bytes .MemTotal}}{{else}}{{if .MemoryGB}}{{.MemoryGB}}G{{else}}—{{end}}{{end}} | {{if $.AnyGPUs}}{{if .GPUTotal}}{{.GPUFree}}/{{.GPUTotal}}{{else}}—{{end}} | {{end}} {{/* Free out of total, because the first is meaningless without the second, and an em dash where nothing was reported: an agent that was never asked about its disk must not read as a full one. */}}{{if .DiskTotalGB}}{{.DiskFreeGB}}G/{{.DiskTotalGB}}G{{else}}—{{end}} | {{if .Engines}}{{join .Engines}}{{else}}none yet{{end}} | {{/* Which vLLM build the node installs: left to its hardware, or chosen. What is installed beside it, so a choice and its outcome are read together. Only a GPU node has one; changing it is for an administrator, on the page reached through a session. */}} {{if $.AnyGPUs}}{{if .GPUTotal}}{{$node := .}} {{range .Provision}}{{if eq .Engine "vllm"}}{{with .Variant}}{{buildLabel .}} {{end}}{{.State}}{{end}}{{end}} {{if and $.Menu.CSRF .EngineBuilds}} {{end}} {{else}}—{{end}} | {{end}} {{if $.AnyClasses}}{{join .Classes}} | {{end}} {{if $.AnyFabrics}}{{join .Fabrics}} | {{end}}{{if .Models}}{{len .Models}}{{else}}—{{end}} | {{.Replicas}} | {{or .LastSeenAgo "—"}} |
| {{join .Unhealthy}} | ||||||||||||
| {{.Engine}} {{.Version}}{{if .Variant}} ({{.Variant}}){{end}} {{.State}}: {{.Detail}} | ||||||||||||
| {{.}} | ||||||||||||
No agents have registered.
{{end}}| Model | Aliases | Ready | Wanted | Class | Reserves | Profiles | {{if .AnyFallbacks}}Fallbacks | {{end}}What it is for |
|---|---|---|---|---|---|---|---|---|
{{.Name}} |
{{join .Aliases}} | {{.Ready}} | {{.Min}}–{{.Max}} | {{.Class}}{{if .Preempted}} −{{.Preempted}}{{end}} | {{if or .MemoryGB .CPUCores}}{{.MemoryGB}}G{{if .CPUCores}} · {{.CPUCores}}c{{end}}{{else}}—{{end}} {{- if .MeasuredMemoryGB}} {{printf "%.1f" .MeasuredMemoryGB}}G used{{end}}{{if .Warnings}} ⚠{{end}} | {{if .External}}external {{.External}}{{else}}{{join .Profiles}}{{end}} | {{if $.AnyFallbacks}}{{join .Fallbacks}} | {{end}} {{/* Cut to the column, the whole sentence on hover: a row of its own for each description made the table twice as long as the catalog. */}}{{or .Description "—"}} |
The catalog is empty.
{{end}} {{if .Unplaced}}{{.Model}}
{{range .Rejections}}{{.Profile}} @ {{or .Pool "-"}}{{if .Node}}/{{.Node}}{{end}}: {{.Reason}}
{{end}}
{{end}}
{{end}}
| Replica | Model | State | Pool | Endpoint | Age | Nodes |
|---|---|---|---|---|---|---|
{{.ID}} |
{{.Model}} | {{.State}} | {{or .Pool "—"}} | {{if .Endpoint}}{{.Endpoint}}{{else}}—{{end}} |
{{duration .AgeSec}} | {{range .Shards}}{{shortNode .Node $.NodePrefix}} {{end}} |
| {{.Err}} | ||||||
No replicas.
{{end}} {{if .Summary.IdleReplicas}}| Replica | Model | GPUs | Unused for |
|---|---|---|---|
{{.ID}} |
{{.Model}} | {{.GPUs}} | {{duration .IdleSec}} |
| Fabric | Is physically | Nodes | Fully free | Weights |
|---|---|---|---|---|
{{.Fabric}} |
{{with $.Fabrics}}{{range .Physical}}{{if has .Declared $name}} {{.Label}}{{if .RateGbps}} · {{.RateGbps}} Gb/s{{end}} {{end}}{{end}}{{end}} |
{{.Nodes}} | {{.LargestFreeBlock}} | {{if .NodesWithWeights}}{{.Models}} model(s) on {{.NodesWithWeights}}/{{.Nodes}} nodes{{else}}not reported{{end}} |
| Fabric | Is physically | Master | Standby | Nodes | Last change of master |
|---|---|---|---|---|---|
{{if .Declared}}{{range $i, $d := .Declared}}{{if $i}}, {{end}}{{$d}}{{end}}{{else}}undeclared{{end}} |
{{.Label}}{{if .RateGbps}} · {{.RateGbps}} Gb/s{{end}} | {{if eq .Kind "roce"}}none: RoCE{{else}}{{with .SM}}{{.GUID}}{{if .LID}} LID {{.LID}}, priority {{.Priority}}{{end}}{{else}}—{{end}}{{end}} |
{{if eq .Kind "roce"}}—{{else if .SMs}}{{if .Standbys}}{{.Standbys}}{{else}}none{{end}}{{else}}not listed{{end}} | {{len .Members}} | {{with .Changes}}{{with index . 0}}{{ago .At}}: {{.From}} → {{.To}}{{end}}{{if gt (len .) 1}} ({{len .}} in a day){{end}}{{else}}none in a day{{end}} |
A fabric's name is what the nodes were told; what it is physically is the subnet manager its InfiniBand ports see, or the network its RoCE ports are on. A replica spanning nodes is only ever formed on one of those. Fully free counts whole unused nodes, which is what decides whether such a replica can land.
{{end}} {{with .NVLink}}{{len .Domains}} multi-node domain{{if ne (len .Domains) 1}}s{{end}}{{range .Domains}} · {{shortDomain .Key}} {{.Usable}}/{{len .Members}} trays usable{{end}}
{{if .Problems}} · {{len .Problems}} problem{{if ne (len .Problems) 1}}s{{end}}{{end}}
· see the racks →
No history: {{.Err}}
{{else}}Requests
{{.RequestChart}}Tokens
{{.TokenChart}}Mean latency
{{.LatencyChart}}No traffic recorded yet.
{{end}} {{if .HasPeople}}| Person | Keys | Requests | Tokens | Errors | Spend | Last seen |
|---|---|---|---|---|---|---|
| {{if .Email}}{{.Email}}{{else}}operator-issued keys{{end}} | {{.Keys}} | {{.Requests}} | {{compact .Tokens}} | {{if .Errors}}{{.Errors}}{{else}}—{{end}} | {{units .CostMicros}} | {{ago .LastSeen}} |
| Model | Requests | Tokens | Errors | Mean latency |
|---|---|---|---|---|
{{.Model}} |
{{.Requests}} | {{compact .Tokens}} | {{if .Errors}}{{.Errors}}{{else}}—{{end}} | {{latency .AvgLatency}} |
Counted by the model that actually answered, which differs from the one requested whenever a fallback served it.
{{end}}| Person | Through | Spends against | Requests | Errors | Prompt | Completion | Spend | Last |
|---|---|---|---|---|---|---|---|---|
| {{or .Email "—"}} | {{if .Chat}}chat page{{else if .KeyName}}{{.KeyName}} {{.KeyPrefix}}{{else}}{{.KeyID}}{{end}} |
{{if .ProjectPath}}{{.ProjectPath}}{{else if .ProjectID}}{{.ProjectID}}{{else}}personal{{end}} |
{{.Requests}} | {{if .Errors}}{{.Errors}}{{else}}0{{end}} | {{compact .Prompt}} | {{compact .Completion}} | {{units .CostMicros}} | {{ago .LastAt}} |
Spend is in units, whatever a unit was configured to mean. Usage is written in batches, so the last few seconds may be missing.
{{else}}No requests recorded in the last 24 hours.
{{end}} {{else}}Authentication is off, so there is no identity to account against.
{{end}} {{if not .OnAdminListener}}Metrics, pprof and the management API are on the admin listener,
which is bound to loopback and not reachable from here. pprof can dump the
process heap, where API keys sit in plaintext, which is why that port is not
exposed:
ssh -L 9090:127.0.0.1:9090 <control-plane>
Nothing recorded yet.
{{else}} {{.t.SVG}} peak {{.t.Peak}} {{if gt (len .t.Legend) 1}}{{.note}}