Every template and handler message in internal/web goes through a JSON
catalogue under internal/web/locales/<code>/, with English as the fallback.
The language is the person's choice (a cookie set by /_lang/{code}), else
Accept-Language, else landing.language. Tests refuse a key some language
lacks, a translation with other arguments than English, and template text
left in English.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
88 lines
3.7 KiB
HTML
88 lines
3.7 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">{{t "usage.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">{{t "usage.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" (t "usage.requests")
|
|
"note" (t "usage.requests_note")}}
|
|
{{template "tschart" dict "t" .Tokens "title" (t "usage.tokens")
|
|
"note" (t "usage.tokens_note")}}
|
|
</div>
|
|
|
|
<div class="chart-row">
|
|
{{if .Priced}}
|
|
{{template "tschart" dict "t" .Spend "title" (t "usage.spend")
|
|
"note" (t "usage.spend_note")}}
|
|
{{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">{{t "usage.spend"}}</h3>
|
|
<p class="empty">{{th "usage.unpriced"}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
{{if .Rows}}
|
|
<table class="rows">
|
|
<tr><th>{{t "usage.col.group"}}</th><th class="num">{{t "usage.requests"}}</th><th class="num">{{t "usage.tokens"}}</th><th class="num">{{t "usage.spend"}}</th><th class="num">{{t "usage.errors"}}</th>
|
|
{{if .HasStats}}<th class="num" title="{{t "usage.col.error_rate_title"}}">{{t "usage.col.error_rate"}}</th>
|
|
<th class="num" title="{{t "usage.col.limited_title"}}">{{t "usage.col.limited"}}</th>
|
|
<th class="num" title="{{t "usage.col.ttft_title"}}">{{t "usage.col.ttft"}}</th>
|
|
<th class="num">{{t "usage.col.latency"}}</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}}{{count .Errors}}{{else}}—{{end}}</td>
|
|
{{if $.HasStats}}{{with .Stats}}
|
|
<td class="num{{if ge .ErrorRate 5.0}} q-warn{{end}}">{{if .Requests}}{{t "usage.pct" (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">{{t "usage.empty"}}</p>
|
|
{{end}}
|
|
|
|
{{with .GPUHours}}
|
|
<h2>{{t "usage.gpu.title"}}</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" (t "usage.gpu.chart") "note" (t "usage.gpu.chart_note")}}
|
|
</div>
|
|
<table class="rows">
|
|
<tr><th>{{t "usage.col.owner"}}</th><th class="num">{{t "usage.col.gpu_hours"}}</th><th class="num">{{t "usage.col.held_now"}}</th></tr>
|
|
{{range .}}<tr><td>{{.Owner}}</td><td class="num">{{num .Hours 1}}</td><td class="num">{{num .Now 0}}</td></tr>{{end}}
|
|
</table>
|
|
<p class="note">{{t "usage.gpu.note"}}</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">{{t "usage.note.charts"}}</p>
|
|
|
|
<p class="note">{{th "usage.note.person"}}</p>
|
|
|
|
{{end}}
|