Watch
0
0
Fork
You've already forked hyperhive
0

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:
iris 2026-11-01 13:21:09 +01:00
commit fec66af1c8
3 changed files with 235 additions and 1 deletions

View file

@ -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.

View file

@ -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"
}
}
} }
] ]
} }

View file

@ -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