add per-agent and hive-wide skill invocation stats
This commit is contained in:
parent
2c11a437b4
commit
ef1554a1b2
4 changed files with 163 additions and 51 deletions
|
|
@ -44,6 +44,10 @@
|
|||
recorded data, so the section never shows an empty block. -->
|
||||
<h3 id="hive-stats-bash-h" hidden>◇ favorite tools (bash commands across the swarm)</h3>
|
||||
<div id="hive-stats-bash" hidden></div>
|
||||
<!-- most-triggered skills across the swarm. Header + list hidden until
|
||||
at least one agent has actually invoked a skill. -->
|
||||
<h3 id="hive-stats-skills-h" hidden>◇ skill mix (invocations across the swarm)</h3>
|
||||
<div id="hive-stats-skills" hidden></div>
|
||||
</main>
|
||||
|
||||
<script type="module" src="/static/stats.js" defer></script>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,39 @@ function hsMeta(parent, text) {
|
|||
p.textContent = text;
|
||||
parent.append(p);
|
||||
}
|
||||
// Shared bar-list renderer for any `KeyCount[]` mix (model/bash/skill).
|
||||
function renderKeyCountBars(container, mix) {
|
||||
container.replaceChildren();
|
||||
const max = mix[0].count || 1;
|
||||
for (const kc of mix) {
|
||||
const row = document.createElement('div'); row.className = 'hive-stats-bar';
|
||||
const lbl = document.createElement('span'); lbl.className = 'lbl'; lbl.textContent = kc.key;
|
||||
const track = document.createElement('span'); track.className = 'track';
|
||||
const fill = document.createElement('span'); fill.className = 'fill';
|
||||
fill.style.width = Math.max(2, Math.round(100 * kc.count / max)) + '%';
|
||||
track.append(fill);
|
||||
const cnt = document.createElement('span'); cnt.className = 'cnt'; cnt.textContent = hsFmtInt(kc.count);
|
||||
row.append(lbl, track, cnt);
|
||||
container.append(row);
|
||||
}
|
||||
}
|
||||
// A mix section that hides itself (+ its header) until the backing
|
||||
// capture has recorded data, so the section never shows an empty block
|
||||
// while data is pre-capture. Used by bash_mix and skill_mix.
|
||||
function renderOptionalMix(containerId, headerId, mix) {
|
||||
const el = $(containerId);
|
||||
const h = $(headerId);
|
||||
if (!el) return;
|
||||
if (!mix.length) {
|
||||
el.replaceChildren();
|
||||
el.hidden = true;
|
||||
if (h) h.hidden = true;
|
||||
return;
|
||||
}
|
||||
el.hidden = false;
|
||||
if (h) h.hidden = false;
|
||||
renderKeyCountBars(el, mix);
|
||||
}
|
||||
|
||||
function renderHiveStats(s) {
|
||||
const sum = $('hive-stats-summary');
|
||||
|
|
@ -93,54 +126,14 @@ function renderHiveStats(s) {
|
|||
const mm = $('hive-stats-models');
|
||||
if (mm) {
|
||||
const mix = s.model_mix || [];
|
||||
if (!mix.length) {
|
||||
hsMeta(mm, 'no turns in window');
|
||||
} else {
|
||||
mm.replaceChildren();
|
||||
const max = mix[0].count || 1;
|
||||
for (const kc of mix) {
|
||||
const row = document.createElement('div'); row.className = 'hive-stats-bar';
|
||||
const lbl = document.createElement('span'); lbl.className = 'lbl'; lbl.textContent = kc.key;
|
||||
const track = document.createElement('span'); track.className = 'track';
|
||||
const fill = document.createElement('span'); fill.className = 'fill';
|
||||
fill.style.width = Math.max(2, Math.round(100 * kc.count / max)) + '%';
|
||||
track.append(fill);
|
||||
const cnt = document.createElement('span'); cnt.className = 'cnt'; cnt.textContent = hsFmtInt(kc.count);
|
||||
row.append(lbl, track, cnt);
|
||||
mm.append(row);
|
||||
}
|
||||
}
|
||||
if (!mix.length) hsMeta(mm, 'no turns in window');
|
||||
else renderKeyCountBars(mm, mix);
|
||||
}
|
||||
|
||||
// "favorite tools": most-run bash commands across the swarm. Hidden
|
||||
// (header + list) until the capture has recorded data, so the
|
||||
// section never shows an empty block while capture is pre-data.
|
||||
const bh = $('hive-stats-bash');
|
||||
const bhH = $('hive-stats-bash-h');
|
||||
if (bh) {
|
||||
const bmix = s.bash_mix || [];
|
||||
if (!bmix.length) {
|
||||
bh.replaceChildren();
|
||||
bh.hidden = true;
|
||||
if (bhH) bhH.hidden = true;
|
||||
} else {
|
||||
bh.hidden = false;
|
||||
if (bhH) bhH.hidden = false;
|
||||
bh.replaceChildren();
|
||||
const max = bmix[0].count || 1;
|
||||
for (const kc of bmix) {
|
||||
const row = document.createElement('div'); row.className = 'hive-stats-bar';
|
||||
const lbl = document.createElement('span'); lbl.className = 'lbl'; lbl.textContent = kc.key;
|
||||
const track = document.createElement('span'); track.className = 'track';
|
||||
const fill = document.createElement('span'); fill.className = 'fill';
|
||||
fill.style.width = Math.max(2, Math.round(100 * kc.count / max)) + '%';
|
||||
track.append(fill);
|
||||
const cnt = document.createElement('span'); cnt.className = 'cnt'; cnt.textContent = hsFmtInt(kc.count);
|
||||
row.append(lbl, track, cnt);
|
||||
bh.append(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
// "favorite tools": most-run bash commands across the swarm.
|
||||
renderOptionalMix('hive-stats-bash', 'hive-stats-bash-h', s.bash_mix || []);
|
||||
// Most-triggered skills across the swarm.
|
||||
renderOptionalMix('hive-stats-skills', 'hive-stats-skills-h', s.skill_mix || []);
|
||||
}
|
||||
|
||||
async function refreshHiveStats() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue