agents board: a busiest-agents table, joined by agent
Five instant table-format queries, one per column, joined on the agent label, sorted by turns. Checked in a real Grafana 13 against a mock Prometheus.
This commit is contained in:
parent
696c199621
commit
fec66af1c8
3 changed files with 235 additions and 1 deletions
|
|
@ -200,7 +200,7 @@ Grafana start — see [`credentials.md`](credentials.md).
|
||||||
`nix/host-modules/swarm-grafana/dashboards/`; edits made in the UI aren't
|
`nix/host-modules/swarm-grafana/dashboards/`; edits made in the UI aren't
|
||||||
kept.
|
kept.
|
||||||
|
|
||||||
- `agents`: per-agent resource usage and Claude Code activity.
|
- `agents`: per-agent resource usage and Claude Code activity. The **activity** tab ends with a **Busiest agents** table: turns, input, output and cache-read tokens, and cost for each agent in the range, busiest first.
|
||||||
- `authelia`, `bao`, `queue`: the identity provider, the secret store and the NATS queue.
|
- `authelia`, `bao`, `queue`: the identity provider, the secret store and the NATS queue.
|
||||||
- `claude-usage`: Claude use by model, effort, token type and source. Filter by hive, service (agents or the review classifier) and agent. The service filter separates the classifier's API-key spend from the agents.
|
- `claude-usage`: Claude use by model, effort, token type and source. Filter by hive, service (agents or the review classifier) and agent. The service filter separates the classifier's API-key spend from the agents.
|
||||||
- `egress`: what agents send through the egress proxy, in three tabs. **overview** has the totals (requests, agents active, refused, credential challenges, data transferred), requests over time by status, and the busiest agents and destinations. **refused** lists the hosts the proxy refused for each agent. **would-deny** lists what agents in `log` mode reached that restricted mode would refuse. The host lists are tables, one row per host (and agent), with a count column you can sort. Filter by agent.
|
- `egress`: what agents send through the egress proxy, in three tabs. **overview** has the totals (requests, agents active, refused, credential challenges, data transferred), requests over time by status, and the busiest agents and destinations. **refused** lists the hosts the proxy refused for each agent. **would-deny** lists what agents in `log` mode reached that restricted mode would refuse. The host lists are tables, one row per host (and agent), with a count column you can sort. Filter by agent.
|
||||||
|
|
|
||||||
|
|
@ -2353,6 +2353,202 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"panel-67": {
|
||||||
|
"kind": "Panel",
|
||||||
|
"spec": {
|
||||||
|
"id": 67,
|
||||||
|
"title": "Busiest agents",
|
||||||
|
"description": "Turns, tokens and cost per agent in the selected range, busiest first.",
|
||||||
|
"links": [],
|
||||||
|
"data": {
|
||||||
|
"kind": "QueryGroup",
|
||||||
|
"spec": {
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"query": {
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"group": "prometheus",
|
||||||
|
"version": "v0",
|
||||||
|
"datasource": {
|
||||||
|
"name": "@datasourceUid@"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"expr": "sum by (agent) (increase({__name__=\"hyperhive.agent.turn.count\", hive=~\"$hive\", agent=~\"$agent\"}[$__range]))",
|
||||||
|
"instant": true,
|
||||||
|
"format": "table"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refId": "A",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"query": {
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"group": "prometheus",
|
||||||
|
"version": "v0",
|
||||||
|
"datasource": {
|
||||||
|
"name": "@datasourceUid@"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"expr": "sum by (agent) (increase({__name__=\"claude_code.token.usage\", hive=~\"$hive\", agent=~\"$agent\", type=\"input\"}[$__range]))",
|
||||||
|
"instant": true,
|
||||||
|
"format": "table"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refId": "B",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"query": {
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"group": "prometheus",
|
||||||
|
"version": "v0",
|
||||||
|
"datasource": {
|
||||||
|
"name": "@datasourceUid@"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"expr": "sum by (agent) (increase({__name__=\"claude_code.token.usage\", hive=~\"$hive\", agent=~\"$agent\", type=\"output\"}[$__range]))",
|
||||||
|
"instant": true,
|
||||||
|
"format": "table"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refId": "C",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"query": {
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"group": "prometheus",
|
||||||
|
"version": "v0",
|
||||||
|
"datasource": {
|
||||||
|
"name": "@datasourceUid@"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"expr": "sum by (agent) (increase({__name__=\"claude_code.token.usage\", hive=~\"$hive\", agent=~\"$agent\", type=\"cacheRead\"}[$__range]))",
|
||||||
|
"instant": true,
|
||||||
|
"format": "table"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refId": "D",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"query": {
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"group": "prometheus",
|
||||||
|
"version": "v0",
|
||||||
|
"datasource": {
|
||||||
|
"name": "@datasourceUid@"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"expr": "sum by (agent) (increase({__name__=\"claude_code.cost.usage\", hive=~\"$hive\", agent=~\"$agent\"}[$__range]))",
|
||||||
|
"instant": true,
|
||||||
|
"format": "table"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"refId": "E",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryOptions": {},
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"kind": "joinByField",
|
||||||
|
"spec": {
|
||||||
|
"id": "joinByField",
|
||||||
|
"options": {
|
||||||
|
"byField": "agent",
|
||||||
|
"mode": "outer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "organize",
|
||||||
|
"spec": {
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {
|
||||||
|
"Time": true
|
||||||
|
},
|
||||||
|
"renameByName": {
|
||||||
|
"agent": "Agent",
|
||||||
|
"Value #A": "Turns",
|
||||||
|
"Value #B": "Input tokens",
|
||||||
|
"Value #C": "Output tokens",
|
||||||
|
"Value #D": "Cache read tokens",
|
||||||
|
"Value #E": "Cost"
|
||||||
|
},
|
||||||
|
"indexByName": {
|
||||||
|
"agent": 0,
|
||||||
|
"Value #A": 1,
|
||||||
|
"Value #B": 2,
|
||||||
|
"Value #C": 3,
|
||||||
|
"Value #D": 4,
|
||||||
|
"Value #E": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vizConfig": {
|
||||||
|
"kind": "VizConfig",
|
||||||
|
"group": "table",
|
||||||
|
"version": "",
|
||||||
|
"spec": {
|
||||||
|
"options": {
|
||||||
|
"showHeader": true,
|
||||||
|
"sortBy": [
|
||||||
|
{
|
||||||
|
"displayName": "Turns",
|
||||||
|
"desc": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "short",
|
||||||
|
"decimals": 0
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Cost"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "currencyUSD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decimals",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
|
|
@ -2444,6 +2640,19 @@
|
||||||
"name": "panel-60"
|
"name": "panel-60"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "GridLayoutItem",
|
||||||
|
"spec": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 23,
|
||||||
|
"width": 24,
|
||||||
|
"height": 8,
|
||||||
|
"element": {
|
||||||
|
"kind": "ElementReference",
|
||||||
|
"name": "panel-67"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,6 +453,31 @@ let
|
||||||
]
|
]
|
||||||
&& lib.all (m: lib.hasInfix "\"${m}\"" recorded) queried;
|
&& lib.all (m: lib.hasInfix "\"${m}\"" recorded) queried;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
# The agents board's busiest-agents table is five instant queries, one per
|
||||||
|
# column, in table format. Joining them by agent is what makes it one row per
|
||||||
|
# agent; without the join a table panel shows a frame per query.
|
||||||
|
name = "the agents dashboard's busiest-agents table joins its queries by agent";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
board = builtins.fromJSON (builtins.readFile ../host-modules/swarm-grafana/dashboards/agents.json);
|
||||||
|
tables = lib.filter (e: e.spec.vizConfig.group == "table") (lib.attrValues board.spec.elements);
|
||||||
|
table = lib.head tables;
|
||||||
|
xf = table.spec.data.spec.transformations;
|
||||||
|
in
|
||||||
|
lib.length tables == 1
|
||||||
|
&& table.spec.title == "Busiest agents"
|
||||||
|
&& lib.length table.spec.data.spec.queries == 5
|
||||||
|
&& lib.all (
|
||||||
|
q: q.spec.query.spec.format == "table" && q.spec.query.spec.instant
|
||||||
|
) table.spec.data.spec.queries
|
||||||
|
&&
|
||||||
|
map (t: t.spec.id) xf == [
|
||||||
|
"joinByField"
|
||||||
|
"organize"
|
||||||
|
]
|
||||||
|
&& (lib.head xf).spec.options.byField == "agent";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
# The egress board ships, and its queries read the two squid log lines as
|
# The egress board ships, and its queries read the two squid log lines as
|
||||||
# squid writes them: the would_deny line's field layout, and the default
|
# squid writes them: the would_deny line's field layout, and the default
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue