OPENENV_RL_01 / app /web /app.js
Siddharaj Shirke
deploy: fresh snapshot to Hugging Face Space
3eae4cc
const state = {
sessionId: null,
taskId: "district_backlog_easy",
agentPolicy: "backlog_clearance",
availableAgents: [],
trace: [],
running: false,
};
const AGENTS_FALLBACK = ["urgent_first", "oldest_first", "backlog_clearance"];
const els = {
taskSelect: document.getElementById("taskSelect"),
agentSelect: document.getElementById("agentSelect"),
stepsInput: document.getElementById("stepsInput"),
startRunBtn: document.getElementById("startRunBtn"),
resetSessionBtn: document.getElementById("resetSessionBtn"),
statusLine: document.getElementById("statusLine"),
stepTableBody: document.querySelector("#stepTable tbody"),
runChart: document.getElementById("runChart"),
benchTaskSelect: document.getElementById("benchTaskSelect"),
benchRunsInput: document.getElementById("benchRunsInput"),
benchStepsInput: document.getElementById("benchStepsInput"),
runBenchmarkBtn: document.getElementById("runBenchmarkBtn"),
benchChart: document.getElementById("benchChart"),
benchTableBody: document.querySelector("#benchTable tbody"),
kpiReward: document.getElementById("kpiReward"),
kpiBacklog: document.getElementById("kpiBacklog"),
kpiCompleted: document.getElementById("kpiCompleted"),
kpiSla: document.getElementById("kpiSla"),
kpiFairness: document.getElementById("kpiFairness"),
kpiScore: document.getElementById("kpiScore"),
};
function setStatus(msg) {
els.statusLine.textContent = msg;
}
async function api(path, options = {}) {
const response = await fetch(`/api${path}`, {
headers: { "Content-Type": "application/json" },
...options,
});
let payload = null;
try {
payload = await response.json();
} catch (e) {
payload = null;
}
if (!response.ok) {
const detail = payload && payload.detail ? payload.detail : `${response.status}`;
throw new Error(`API ${path} failed: ${detail}`);
}
return payload;
}
function setLoading(isLoading) {
state.running = isLoading;
els.startRunBtn.disabled = isLoading;
els.resetSessionBtn.disabled = isLoading;
els.runBenchmarkBtn.disabled = isLoading;
}
function formatFloat(v) {
return Number(v).toFixed(2);
}
function updateKpis(step) {
if (!step) return;
const totalReward = state.trace.reduce((sum, row) => sum + row.reward, 0);
els.kpiReward.textContent = formatFloat(totalReward);
els.kpiBacklog.textContent = `${step.backlog}`;
els.kpiCompleted.textContent = `${step.completed}`;
els.kpiSla.textContent = `${step.slaBreaches}`;
els.kpiFairness.textContent = formatFloat(step.fairnessGap);
}
function renderAction(actionObj) {
if (!actionObj || typeof actionObj !== "object") {
return "unknown";
}
const actionType = actionObj.action_type || "unknown";
const extras = [];
if (actionObj.service) extras.push(`service=${actionObj.service}`);
if (actionObj.target_service) extras.push(`target=${actionObj.target_service}`);
if (typeof actionObj.officer_delta === "number") extras.push(`delta=${actionObj.officer_delta}`);
if (actionObj.priority_mode) extras.push(`mode=${actionObj.priority_mode}`);
return extras.length ? `${actionType} (${extras.join(", ")})` : actionType;
}
function appendStepRow(row) {
const tr = document.createElement("tr");
const status = row.done ? "done" : "running";
tr.innerHTML = `
<td>${row.step}</td>
<td>${row.day}</td>
<td>${row.action}</td>
<td>${formatFloat(row.reward)}</td>
<td>${row.backlog}</td>
<td>${row.completed}</td>
<td>${row.slaBreaches}</td>
<td>${status}</td>
`;
els.stepTableBody.appendChild(tr);
}
function clearRunView() {
state.trace = [];
els.stepTableBody.innerHTML = "";
els.kpiReward.textContent = "0.00";
els.kpiBacklog.textContent = "0";
els.kpiCompleted.textContent = "0";
els.kpiSla.textContent = "0";
els.kpiFairness.textContent = "0.00";
els.kpiScore.textContent = "-";
drawRunChart([]);
}
function drawAxes(ctx, w, h, pad) {
ctx.strokeStyle = "#2f2f2f";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(pad, pad);
ctx.lineTo(pad, h - pad);
ctx.lineTo(w - pad, h - pad);
ctx.stroke();
}
function drawSeries(ctx, points, color, pad, w, h, yMax) {
if (!points.length) return;
const xStep = (w - pad * 2) / Math.max(points.length - 1, 1);
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.beginPath();
points.forEach((v, i) => {
const x = pad + i * xStep;
const y = h - pad - (v / Math.max(yMax, 1e-6)) * (h - pad * 2);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
}
function drawRunChart(trace) {
const canvas = els.runChart;
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const pad = 34;
ctx.clearRect(0, 0, w, h);
drawAxes(ctx, w, h, pad);
if (!trace.length) return;
const rewards = trace.map((x) => Math.max(0, x.reward));
const backlogs = trace.map((x) => x.backlog);
const yMax = Math.max(...rewards, ...backlogs, 1);
drawSeries(ctx, rewards, "#ffffff", pad, w, h, yMax);
drawSeries(ctx, backlogs, "#7a7a7a", pad, w, h, yMax);
ctx.fillStyle = "#d2d2d2";
ctx.font = "12px Segoe UI";
ctx.fillText("reward", pad + 6, pad + 8);
ctx.fillText("backlog", pad + 70, pad + 8);
}
function drawBenchmarkChart(agentResults) {
const canvas = els.benchChart;
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const pad = 34;
ctx.clearRect(0, 0, w, h);
drawAxes(ctx, w, h, pad);
if (!agentResults.length) return;
const barAreaW = w - pad * 2;
const slotW = barAreaW / agentResults.length;
agentResults.forEach((agent, idx) => {
const cx = pad + idx * slotW + slotW / 2;
const barW = Math.max(24, slotW * 0.48);
const barH = (h - pad * 2) * Math.min(1, Math.max(0, agent.average_score));
const topY = h - pad - barH;
ctx.fillStyle = "#ffffff";
ctx.fillRect(cx - barW / 2, topY, barW, barH);
ctx.fillStyle = "#9a9a9a";
agent.runs.forEach((run, runIdx) => {
const jitter = ((runIdx % 7) - 3) * 2.5;
const dotY = h - pad - (h - pad * 2) * Math.min(1, Math.max(0, run.score));
ctx.beginPath();
ctx.arc(cx + jitter, dotY, 3, 0, Math.PI * 2);
ctx.fill();
});
ctx.fillStyle = "#d0d0d0";
ctx.font = "11px Segoe UI";
ctx.textAlign = "center";
ctx.fillText(agent.agent_policy, cx, h - 10);
});
ctx.textAlign = "start";
}
async function resetSession() {
if (state.sessionId) {
try {
await api(`/sessions/${state.sessionId}`, { method: "DELETE" });
} catch (err) {
// Ignore stale session cleanup errors; reset will still create a fresh session.
}
}
state.taskId = els.taskSelect.value;
const payload = await api("/reset", {
method: "POST",
body: JSON.stringify({ task_id: state.taskId }),
});
state.sessionId = payload.session_id;
clearRunView();
setStatus(`Session ready: ${state.sessionId.slice(0, 8)}... (${state.taskId})`);
}
async function runSimulation() {
const requestedSteps = Number(els.stepsInput.value || 0);
if (!requestedSteps || requestedSteps < 1) {
setStatus("Enter a valid step count.");
return;
}
setLoading(true);
try {
if (!state.sessionId || state.taskId !== els.taskSelect.value) {
await resetSession();
}
state.agentPolicy = els.agentSelect.value;
setStatus(`Running ${requestedSteps} steps with ${state.agentPolicy}...`);
for (let i = 0; i < requestedSteps; i += 1) {
const stepRes = await api("/autostep", {
method: "POST",
body: JSON.stringify({
session_id: state.sessionId,
agent_policy: state.agentPolicy,
}),
});
const obs = stepRes.observation;
const row = {
step: state.trace.length + 1,
day: obs.day,
action: renderAction(stepRes.action),
reward: Number(stepRes.reward || 0),
backlog: obs.total_backlog,
completed: obs.total_completed,
slaBreaches: obs.total_sla_breaches,
fairnessGap: Number(obs.fairness_gap || 0),
done: !!stepRes.done,
};
state.trace.push(row);
appendStepRow(row);
updateKpis(row);
drawRunChart(state.trace);
if (stepRes.done) break;
}
const gradeRes = await api("/grade", {
method: "POST",
body: JSON.stringify({ session_id: state.sessionId }),
});
els.kpiScore.textContent = formatFloat(gradeRes.score);
setStatus(`Run finished. Score: ${formatFloat(gradeRes.score)} (${gradeRes.grader_name})`);
} catch (err) {
setStatus(err.message);
} finally {
setLoading(false);
}
}
async function runBenchmark() {
setLoading(true);
try {
const taskId = els.benchTaskSelect.value;
const runs = Number(els.benchRunsInput.value || 0);
const maxSteps = Number(els.benchStepsInput.value || 0);
if (!runs || !maxSteps) {
setStatus("Benchmark inputs are invalid.");
return;
}
const benchmarkAgents = state.availableAgents.length ? state.availableAgents : AGENTS_FALLBACK;
setStatus(`Running benchmark on ${taskId} with ${benchmarkAgents.length} agents...`);
const res = await api("/benchmark", {
method: "POST",
body: JSON.stringify({
task_id: taskId,
runs,
max_steps: maxSteps,
agent_policies: benchmarkAgents,
}),
});
els.benchTableBody.innerHTML = "";
res.agent_results.forEach((agent) => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${agent.agent_policy}</td>
<td>${formatFloat(agent.average_score)}</td>
<td>${formatFloat(agent.min_score)}</td>
<td>${formatFloat(agent.max_score)}</td>
`;
els.benchTableBody.appendChild(tr);
});
drawBenchmarkChart(res.agent_results);
setStatus("Benchmark completed.");
} catch (err) {
setStatus(err.message);
} finally {
setLoading(false);
}
}
async function init() {
setLoading(true);
try {
const health = await api("/health");
const tasksRes = await api("/tasks");
const agents = await api("/agents").catch(() => AGENTS_FALLBACK);
tasksRes.tasks.forEach((task) => {
const optA = new Option(task, task);
const optB = new Option(task, task);
els.taskSelect.add(optA);
els.benchTaskSelect.add(optB);
});
state.availableAgents = agents.length ? agents : AGENTS_FALLBACK;
state.availableAgents.forEach((agent) => {
els.agentSelect.add(new Option(agent, agent));
});
els.taskSelect.value = health.available_tasks.includes("district_backlog_easy")
? "district_backlog_easy"
: tasksRes.tasks[0];
els.benchTaskSelect.value = els.taskSelect.value;
els.agentSelect.value = state.availableAgents.includes("backlog_clearance")
? "backlog_clearance"
: state.availableAgents[0];
await resetSession();
} catch (err) {
setStatus(`Initialization failed: ${err.message}`);
} finally {
setLoading(false);
}
}
els.startRunBtn.addEventListener("click", runSimulation);
els.resetSessionBtn.addEventListener("click", async () => {
setLoading(true);
try {
await resetSession();
} catch (err) {
setStatus(err.message);
} finally {
setLoading(false);
}
});
els.runBenchmarkBtn.addEventListener("click", runBenchmark);
init();