Percentiles are read from the histograms kept with the usage sums and shown per group on the usage tab and in the people page's panels. Requests refused by a limit are recorded as 429s against their owner, free of charge, so a team hitting its limits shows as such. Usage can be grouped by model. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
95 lines
4.5 KiB
HTML
95 lines
4.5 KiB
HTML
{{/* The usage tab of the people page. */}}
|
|
{{define "usage_body"}}
|
|
|
|
{{if .Error}}<p class="error">{{.Error}}</p>{{end}}
|
|
|
|
{{/* One row of controls above the charts, which is where a reader looks for
|
|
them. Links rather than a script: the choice is in the URL, so a view worth
|
|
coming back to can be bookmarked or pasted to somebody else. */}}
|
|
<div class="filters">
|
|
<span class="filter-label">Group by</span>
|
|
{{range .Groups}}<a class="chip{{if .Selected}} on{{end}}"
|
|
href="/admin/usage?by={{.Key}}&window={{$.Window}}">{{.Label}}</a>{{end}}
|
|
<span class="filter-label">Window</span>
|
|
{{range .Windows}}<a class="chip{{if .Selected}} on{{end}}"
|
|
href="/admin/usage?by={{$.By}}&window={{.Key}}">{{.Label}}</a>{{end}}
|
|
</div>
|
|
|
|
<div class="chart-row">
|
|
{{template "tschart" dict "t" .Requests "title" "Requests"
|
|
"note" "Requests served in each slice, counted where the key belongs now rather than where it was at the time."}}
|
|
{{template "tschart" dict "t" .Tokens "title" "Tokens"
|
|
"note" "Prompt and completion together, which is the figure a person recognises as what they consumed."}}
|
|
</div>
|
|
|
|
<div class="chart-row">
|
|
{{if .Priced}}
|
|
{{template "tschart" dict "t" .Spend "title" "Spend"
|
|
"note" "In whole units, the same ones a budget is written in."}}
|
|
{{else}}
|
|
{{/* Said rather than drawn. A spend chart flat along the bottom reads as a
|
|
quiet month, and this is not that. */}}
|
|
<div class="chart-wrap">
|
|
<h3 class="chart-title">Spend</h3>
|
|
<p class="empty">No model has a price, so every request costs zero and this
|
|
chart would be a flat line however busy the cluster is. Set prices in the
|
|
<code>pricing</code> block of the server configuration.</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
{{if .Rows}}
|
|
<table class="rows">
|
|
<tr><th>Group</th><th class="num">Requests</th><th class="num">Tokens</th><th class="num">Spend</th><th class="num">Errors</th>
|
|
{{if .HasStats}}<th class="num" title="Failed requests, refusals by a limit included">Error rate</th>
|
|
<th class="num" title="Refused by a rate, token or budget limit">Limited</th>
|
|
<th class="num" title="Time to first token, for streamed requests">First token p50 / p95</th>
|
|
<th class="num">Latency p95</th>{{end}}</tr>
|
|
{{range .Rows}}
|
|
<tr>
|
|
<td>{{.Name}}</td>
|
|
<td class="num">{{compact .Requests}}</td>
|
|
<td class="num">{{compact .Tokens}}</td>
|
|
<td class="num">{{.Spend}}</td>
|
|
<td class="num">{{if .Errors}}{{.Errors}}{{else}}—{{end}}</td>
|
|
{{if $.HasStats}}{{with .Stats}}
|
|
<td class="num{{if ge .ErrorRate 5.0}} q-warn{{end}}">{{if .Requests}}{{pct1 .ErrorRate}}%{{else}}—{{end}}</td>
|
|
<td class="num{{if .Limited}} q-warn{{end}}">{{if .Limited}}{{compact .Limited}}{{else}}—{{end}}</td>
|
|
<td class="num">{{pctile .TTFT 50}} / {{pctile .TTFT 95}}</td>
|
|
<td class="num">{{pctile .Latency 95}}</td>{{end}}{{end}}
|
|
</tr>
|
|
{{end}}
|
|
</table>
|
|
{{else}}
|
|
<p class="empty">Nothing recorded in this window.</p>
|
|
{{end}}
|
|
|
|
{{with .GPUHours}}
|
|
<h2>GPU time, by owner</h2>
|
|
{{/* What each owner's models held, whether or not anybody asked them
|
|
anything: a model kept loaded holds its cards all the same, and that is
|
|
the time a GPU quota is about. */}}
|
|
<div class="chart-row">
|
|
{{template "tschart" dict "t" $.GPUHeld "title" "GPUs held, by owner" "note" "The cards held by the replicas of each owner's models: models whose owner is a part of the quota tree."}}
|
|
</div>
|
|
<table class="rows">
|
|
<tr><th>Owner</th><th class="num">GPU-hours</th><th class="num">Held now</th></tr>
|
|
{{range .}}<tr><td>{{.Owner}}</td><td class="num">{{printf "%.1f" .Hours}}</td><td class="num">{{printf "%.0f" .Now}}</td></tr>{{end}}
|
|
</table>
|
|
<p class="note">From the metrics store, which keeps its history for as long as it is set to (fifteen days by default); a model with no owner is counted under “no owner”.</p>
|
|
{{end}}
|
|
|
|
{{/* The charts fold their tail; the table does not. Both are here because they
|
|
answer different questions, and the one that is easy to get wrong is
|
|
thinking the four coloured lines are all of it. */}}
|
|
<p class="note">The charts draw the four busiest groups by total and sum the
|
|
rest into “other”, so they always add up to the whole cluster. The table below
|
|
is every group, in the same order.</p>
|
|
|
|
<p class="note">A person is not a level of the quota tree — one person holds
|
|
keys in several projects — so grouping by person crosses the tree rather than
|
|
naming a level of it. Quotas are written on the other three, on the
|
|
<a href="/admin/quotas">Limits</a> tab.</p>
|
|
|
|
{{end}}
|