File size: 5,416 Bytes
8f03abe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
const state = { data: null, packet: null };
const byId = (id) => document.getElementById(id);
const esc = (value) => String(value ?? "").replace(/[&<>'"]/g, (char) => ({"&":"&amp;","<":"&lt;",">":"&gt;","'":"&#39;",'"':"&quot;"}[char]));
const percent = (part, total) => total ? `${Math.round(part * 100 / total)}%` : "—";

async function loadData() {
  const response = await fetch("benchmark.json");
  if (!response.ok) throw new Error(`data request failed (${response.status})`);
  return response.json();
}

function renderResults(runs) {
  byId("results-body").innerHTML = runs.map((run) => `
    <tr>
      <td><strong>${esc(run.domain_label)}</strong></td>
      <td>${run.packet_count}</td>
      <td>${run.trajectory_passed}/${run.packet_count} <span class="badge">${percent(run.trajectory_passed, run.packet_count)}</span></td>
      <td>${run.independent_end_task_passed}/${run.packet_count} <span class="badge">${percent(run.independent_end_task_passed, run.packet_count)}</span></td>
      <td>${run.primary_routing_decisions}</td>
      <td>${run.suppressed_by_budget}</td>
    </tr>`).join("");
}

function unique(values) { return [...new Set(values.filter(Boolean))].sort(); }
function fillSelect(select, values, label) {
  select.innerHTML = `<option value="">${label}</option>` + values.map((value) => `<option value="${esc(value)}">${esc(value)}</option>`).join("");
}

function availablePackets() {
  const domain = byId("domain-filter").value;
  return state.data.packets.filter((packet) => !domain || packet.domain === domain);
}

function updatePacketOptions(keepSelection = true) {
  const select = byId("packet-filter");
  const previous = keepSelection ? select.value : "";
  const packets = availablePackets();
  select.innerHTML = packets.map((packet) => `<option value="${esc(packet.packet_id)}">${esc(packet.packet_id)}</option>`).join("");
  if (packets.some((packet) => packet.packet_id === previous)) select.value = previous;
  renderPacket();
}

function renderPacket() {
  const id = byId("packet-filter").value;
  state.packet = state.data.packets.find((packet) => packet.packet_id === id) || availablePackets()[0];
  if (!state.packet) return;
  byId("packet-filter").value = state.packet.packet_id;
  const p = state.packet;
  byId("packet-summary").innerHTML = `
    <div class="packet-summary-head">
      <div><h3>${esc(p.packet_id)}</h3><div class="badges"><span class="badge">${esc(p.domain_label)}</span><span class="badge">${esc(p.executor_model)}</span><span class="badge">${p.primary_routing_decisions} primary decisions</span></div></div>
      <div class="badges"><span class="status ${p.trajectory_passed ? "success" : "failed"}">trajectory ${p.trajectory_passed ? "pass" : "fail"}</span><span class="status ${p.independent_end_task_passed ? "success" : "failed"}">trusted ${p.independent_end_task_passed ? "pass" : "fail"}</span></div>
    </div><p>${esc(p.task)}</p>`;
  renderSteps();
}

function renderSteps() {
  const role = byId("role-filter").value;
  const status = byId("status-filter").value;
  const rows = state.packet.steps.filter((step) => (!role || step.role === role) && (!status || step.event_status === status));
  byId("trajectory").innerHTML = rows.length ? rows.map((step) => `
    <article class="trajectory-step">
      <div class="step-number">#${String(step.index).padStart(2,"0")}</div>
      <div class="step-role"><strong>${esc(step.role)}</strong><small>budget before: ${esc(step.budget_before)} · confidence: ${step.confidence == null ? "—" : Number(step.confidence).toFixed(2)}</small><span class="event-pill">${esc(step.event_status)} · ${esc(step.semantic_delta)}</span></div>
      <div class="state-change"><small>state before → after</small><code>${esc(step.state_before || "not executed")}</code><code>→ ${esc(step.state_after || "not executed")}</code></div>
      <div class="choice-list">top choices<br>${step.top_choices.map((choice) => `${esc(choice.role)} ${(choice.probability * 100).toFixed(0)}%`).join(" · ") || "not recorded"}</div>
    </article>`).join("") : `<p class="error">No step matches the active role and status filters.</p>`;
}

async function init() {
  try {
    state.data = await loadData();
    renderResults(state.data.runs);
    const domains = state.data.runs.map((run) => run.domain);
    byId("domain-filter").innerHTML += domains.map((domain) => `<option value="${esc(domain)}">${esc(state.data.runs.find((run) => run.domain === domain).domain_label)}</option>`).join("");
    fillSelect(byId("role-filter"), unique(state.data.packets.flatMap((packet) => packet.steps.map((step) => step.role))), "All roles");
    fillSelect(byId("status-filter"), unique(state.data.packets.flatMap((packet) => packet.steps.map((step) => step.event_status))), "All statuses");
    updatePacketOptions(false);
    byId("domain-filter").addEventListener("change", () => updatePacketOptions(false));
    byId("packet-filter").addEventListener("change", renderPacket);
    byId("role-filter").addEventListener("change", renderSteps);
    byId("status-filter").addEventListener("change", renderSteps);
  } catch (error) {
    byId("results-body").innerHTML = `<tr><td colspan="6" class="error">Could not load explorer data: ${esc(error.message)}</td></tr>`;
    byId("trajectory").innerHTML = `<p class="error">Explorer data is unavailable.</p>`;
  }
}

document.addEventListener("DOMContentLoaded", init);