Spaces:
Running
Running
| import React, { useEffect, useMemo, useRef, useState } from "https://esm.sh/react@18.3.1"; | |
| import { createRoot } from "https://esm.sh/react-dom@18.3.1/client"; | |
| async function api(path, options = {}) { | |
| const res = await fetch(`/api${path}`, { | |
| headers: { "Content-Type": "application/json" }, | |
| ...options, | |
| }); | |
| let payload = null; | |
| try { | |
| payload = await res.json(); | |
| } catch (err) { | |
| payload = null; | |
| } | |
| if (!res.ok) { | |
| const detail = payload && payload.detail ? payload.detail : `${res.status}`; | |
| throw new Error(`API ${path} failed: ${detail}`); | |
| } | |
| return payload; | |
| } | |
| function drawAxes(ctx, w, h, pad) { | |
| ctx.clearRect(0, 0, w, h); | |
| 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 LineCanvas({ pointsA, pointsB, labelA, labelB }) { | |
| const ref = useRef(null); | |
| useEffect(() => { | |
| const canvas = ref.current; | |
| if (!canvas) return; | |
| const ctx = canvas.getContext("2d"); | |
| const w = canvas.width; | |
| const h = canvas.height; | |
| const pad = 34; | |
| drawAxes(ctx, w, h, pad); | |
| const all = [...pointsA, ...pointsB]; | |
| if (!all.length) return; | |
| const yMax = Math.max(...all, 1); | |
| const draw = (arr, color) => { | |
| if (!arr.length) return; | |
| const stepX = (w - pad * 2) / Math.max(arr.length - 1, 1); | |
| ctx.strokeStyle = color; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| arr.forEach((v, i) => { | |
| const x = pad + i * stepX; | |
| const y = h - pad - (v / yMax) * (h - pad * 2); | |
| if (i === 0) ctx.moveTo(x, y); | |
| else ctx.lineTo(x, y); | |
| }); | |
| ctx.stroke(); | |
| }; | |
| draw(pointsA, "#ffffff"); | |
| draw(pointsB, "#808080"); | |
| ctx.fillStyle = "#d5d5d5"; | |
| ctx.font = "12px Segoe UI"; | |
| ctx.fillText(labelA, pad + 6, pad + 8); | |
| ctx.fillText(labelB, pad + 92, pad + 8); | |
| }, [pointsA, pointsB, labelA, labelB]); | |
| return React.createElement("canvas", { ref, width: 1200, height: 320 }); | |
| } | |
| function CompareCanvas({ baselineScore, rlScore }) { | |
| const ref = useRef(null); | |
| useEffect(() => { | |
| const canvas = ref.current; | |
| if (!canvas) return; | |
| const ctx = canvas.getContext("2d"); | |
| const w = canvas.width; | |
| const h = canvas.height; | |
| const pad = 36; | |
| drawAxes(ctx, w, h, pad); | |
| if (baselineScore == null || rlScore == null) return; | |
| const bars = [ | |
| { name: "baseline", score: baselineScore, color: "#9a9a9a", x: w * 0.35 }, | |
| { name: "phase2", score: rlScore, color: "#ffffff", x: w * 0.65 }, | |
| ]; | |
| bars.forEach((bar) => { | |
| const barW = 120; | |
| const barH = (h - pad * 2) * Math.max(0, Math.min(1, bar.score)); | |
| const y = h - pad - barH; | |
| ctx.fillStyle = bar.color; | |
| ctx.fillRect(bar.x - barW / 2, y, barW, barH); | |
| ctx.fillStyle = "#dddddd"; | |
| ctx.font = "13px Segoe UI"; | |
| ctx.textAlign = "center"; | |
| ctx.fillText(`${bar.name}: ${bar.score.toFixed(3)}`, bar.x, h - 10); | |
| }); | |
| ctx.textAlign = "start"; | |
| }, [baselineScore, rlScore]); | |
| return React.createElement("canvas", { ref, width: 1200, height: 300 }); | |
| } | |
| function formatNumber(value, digits = 2) { | |
| if (value == null || Number.isNaN(Number(value))) return "-"; | |
| return Number(value).toFixed(digits); | |
| } | |
| function App() { | |
| const [loading, setLoading] = useState(false); | |
| const [status, setStatus] = useState("Initializing..."); | |
| const [tasks, setTasks] = useState([]); | |
| const [agents, setAgents] = useState([]); | |
| const [components, setComponents] = useState([]); | |
| const [models, setModels] = useState([]); | |
| const [taskId, setTaskId] = useState("district_backlog_easy"); | |
| const [agentPolicy, setAgentPolicy] = useState("backlog_clearance"); | |
| const [steps, setSteps] = useState(40); | |
| const [sessionId, setSessionId] = useState(""); | |
| const [manualSeed, setManualSeed] = useState(""); | |
| const [manualActionJson, setManualActionJson] = useState('{\n "action_type": "advance_time"\n}'); | |
| const [manualOutput, setManualOutput] = useState("{}"); | |
| const [baselineTrace, setBaselineTrace] = useState([]); | |
| const [graderScore, setGraderScore] = useState(null); | |
| const [benchmarkRows, setBenchmarkRows] = useState([]); | |
| const [modelPath, setModelPath] = useState("results/best_model/phase2_final.zip"); | |
| const [modelType, setModelType] = useState("maskable"); | |
| const [rlMaxSteps, setRlMaxSteps] = useState(80); | |
| const [rlRun, setRlRun] = useState(null); | |
| const [rlEval, setRlEval] = useState([]); | |
| const [compareData, setCompareData] = useState({ baseline: null, rl: null }); | |
| const [workflowOutput, setWorkflowOutput] = useState(""); | |
| const [workflowMeta, setWorkflowMeta] = useState(null); | |
| useEffect(() => { | |
| const init = async () => { | |
| setLoading(true); | |
| try { | |
| const [health, tasksRes, agentsRes, componentsRes, modelsRes] = await Promise.all([ | |
| api("/health"), | |
| api("/tasks"), | |
| api("/agents"), | |
| api("/workflows/components"), | |
| api("/rl/models"), | |
| ]); | |
| const taskList = tasksRes.tasks || []; | |
| const agentList = agentsRes || []; | |
| const modelList = (modelsRes.models || []).filter((m) => m.exists); | |
| setTasks(taskList); | |
| setAgents(agentList); | |
| setComponents(componentsRes.components || []); | |
| setModels(modelsRes.models || []); | |
| const defaultTask = taskList.includes("district_backlog_easy") ? "district_backlog_easy" : taskList[0]; | |
| setTaskId(defaultTask || "district_backlog_easy"); | |
| const defaultAgent = agentList.includes("backlog_clearance") ? "backlog_clearance" : (agentList[0] || "backlog_clearance"); | |
| setAgentPolicy(defaultAgent); | |
| const phase2 = modelList.find((m) => m.path.toLowerCase().includes("phase2_final")) || modelList[0]; | |
| if (phase2) { | |
| setModelPath(phase2.path); | |
| setModelType(phase2.model_type); | |
| } | |
| setStatus(`API ready (v${health.version}).`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| init(); | |
| }, []); | |
| const baselineRewards = useMemo(() => baselineTrace.map((x) => Math.max(0, Number(x.reward || 0))), [baselineTrace]); | |
| const baselineBacklog = useMemo(() => baselineTrace.map((x) => Number(x.backlog || 0)), [baselineTrace]); | |
| const baselineKpi = useMemo(() => { | |
| const totalReward = baselineTrace.reduce((sum, row) => sum + Number(row.reward || 0), 0); | |
| const last = baselineTrace.length ? baselineTrace[baselineTrace.length - 1] : null; | |
| return { | |
| reward: totalReward, | |
| backlog: last ? last.backlog : 0, | |
| completed: last ? last.completed : 0, | |
| sla: last ? last.sla_breaches : 0, | |
| fairness: last ? last.fairness_gap : 0, | |
| }; | |
| }, [baselineTrace]); | |
| const activeModel = useMemo(() => models.find((m) => m.path === modelPath), [models, modelPath]); | |
| const manualReset = async () => { | |
| setLoading(true); | |
| try { | |
| const payload = { | |
| task_id: taskId, | |
| }; | |
| if (manualSeed.trim()) { | |
| payload.seed = Number(manualSeed.trim()); | |
| } | |
| const res = await api("/reset", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| setSessionId(res.session_id); | |
| setManualOutput(JSON.stringify(res, null, 2)); | |
| setStatus(`Session created: ${res.session_id}`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const manualStep = async () => { | |
| if (!sessionId) { | |
| setStatus("Create a session first with Reset."); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const action = JSON.parse(manualActionJson); | |
| const res = await api("/step", { | |
| method: "POST", | |
| body: JSON.stringify({ session_id: sessionId, action }), | |
| }); | |
| setManualOutput(JSON.stringify(res, null, 2)); | |
| setStatus(`Manual step done. reward=${formatNumber(res.reward)}`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const manualState = async () => { | |
| if (!sessionId) { | |
| setStatus("Create a session first with Reset."); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const res = await api("/state", { | |
| method: "POST", | |
| body: JSON.stringify({ session_id: sessionId, include_action_history: true }), | |
| }); | |
| setManualOutput(JSON.stringify(res, null, 2)); | |
| setStatus("State fetched."); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const manualGrade = async () => { | |
| if (!sessionId) { | |
| setStatus("Create a session first with Reset."); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const res = await api("/grade", { | |
| method: "POST", | |
| body: JSON.stringify({ session_id: sessionId }), | |
| }); | |
| setManualOutput(JSON.stringify(res, null, 2)); | |
| setStatus(`Grade score=${formatNumber(res.score, 3)} (${res.grader_name})`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const resetBaselineSession = async () => { | |
| const res = await api("/reset", { | |
| method: "POST", | |
| body: JSON.stringify({ task_id: taskId }), | |
| }); | |
| setSessionId(res.session_id); | |
| setBaselineTrace([]); | |
| setGraderScore(null); | |
| return res.session_id; | |
| }; | |
| const runBaseline = async () => { | |
| setLoading(true); | |
| try { | |
| let sid = sessionId; | |
| if (!sid) { | |
| sid = await resetBaselineSession(); | |
| } | |
| const rows = []; | |
| for (let i = 0; i < Number(steps); i += 1) { | |
| const stepRes = await api("/autostep", { | |
| method: "POST", | |
| body: JSON.stringify({ session_id: sid, agent_policy: agentPolicy }), | |
| }); | |
| rows.push({ | |
| step: rows.length + 1, | |
| day: stepRes.observation.day, | |
| action: stepRes.action.action_type, | |
| reward: Number(stepRes.reward || 0), | |
| backlog: stepRes.observation.total_backlog, | |
| completed: stepRes.observation.total_completed, | |
| sla_breaches: stepRes.observation.total_sla_breaches, | |
| fairness_gap: Number(stepRes.observation.fairness_gap || 0), | |
| done: stepRes.done, | |
| }); | |
| if (stepRes.done) break; | |
| } | |
| setBaselineTrace(rows); | |
| const gradeRes = await api("/grade", { | |
| method: "POST", | |
| body: JSON.stringify({ session_id: sid }), | |
| }); | |
| setGraderScore(Number(gradeRes.score)); | |
| setStatus(`Baseline run done. score=${formatNumber(gradeRes.score, 3)}`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const runBenchmark = async () => { | |
| if (!agents.length) { | |
| setStatus("No baseline agents available."); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const res = await api("/benchmark", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| task_id: taskId, | |
| runs: 3, | |
| max_steps: Number(steps), | |
| agent_policies: agents, | |
| }), | |
| }); | |
| setBenchmarkRows(res.agent_results || []); | |
| setStatus("Baseline benchmark done."); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const runTrainedEpisode = async () => { | |
| setLoading(true); | |
| try { | |
| const res = await api("/rl/run", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| task_id: taskId, | |
| model_path: modelPath, | |
| model_type: modelType, | |
| max_steps: Number(rlMaxSteps), | |
| }), | |
| }); | |
| setRlRun(res); | |
| setStatus(`Trained run done. score=${formatNumber(res.grader_score, 3)} (${res.grader_name})`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const evaluateTrainedModel = async () => { | |
| setLoading(true); | |
| try { | |
| const res = await api("/rl/evaluate", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| model_path: modelPath, | |
| model_type: modelType, | |
| episodes: 3, | |
| task_ids: tasks, | |
| }), | |
| }); | |
| setRlEval(res.results || []); | |
| setStatus(`Trained evaluation done. avg=${formatNumber(res.average_grader_score, 3)}`); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const compareBaselineVsPhase2 = async () => { | |
| setLoading(true); | |
| try { | |
| const [base, rl] = await Promise.all([ | |
| api("/benchmark", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| task_id: taskId, | |
| runs: 3, | |
| max_steps: Number(steps), | |
| agent_policies: [agentPolicy], | |
| }), | |
| }), | |
| api("/rl/evaluate", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| model_path: modelPath, | |
| model_type: modelType, | |
| episodes: 3, | |
| task_ids: [taskId], | |
| }), | |
| }), | |
| ]); | |
| const baselineScore = base.agent_results && base.agent_results.length | |
| ? Number(base.agent_results[0].average_score) | |
| : null; | |
| const rlScore = rl.results && rl.results.length | |
| ? Number(rl.results[0].grader_score) | |
| : null; | |
| setCompareData({ baseline: baselineScore, rl: rlScore }); | |
| setStatus("Comparison done."); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const workflowIdForComponent = (componentName) => { | |
| if (componentName === "baseline_openai.py") return "baseline_openai"; | |
| if (componentName === "inference.py") return "inference"; | |
| if (componentName === "phase2_final.zip") return "phase2_eval"; | |
| return null; | |
| }; | |
| const runWorkflowFromUi = async (workflowId) => { | |
| setLoading(true); | |
| try { | |
| const payload = { | |
| workflow_id: workflowId, | |
| max_steps: Number(steps), | |
| episodes: 3, | |
| model_path: modelPath, | |
| model_type: modelType, | |
| timeout_seconds: 240, | |
| }; | |
| const res = await api("/workflows/run", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| setWorkflowMeta({ | |
| workflow_id: res.workflow_id, | |
| exit_code: res.exit_code, | |
| duration_seconds: res.duration_seconds, | |
| timed_out: res.timed_out, | |
| command: res.command, | |
| }); | |
| const out = [ | |
| "$ " + (res.command || []).join(" "), | |
| "", | |
| "STDOUT:", | |
| res.stdout || "", | |
| "", | |
| "STDERR:", | |
| res.stderr || "", | |
| ].join("\n"); | |
| setWorkflowOutput(out); | |
| setStatus( | |
| `Workflow ${res.workflow_id} finished. exit_code=${res.exit_code}, duration=${formatNumber(res.duration_seconds, 2)}s` | |
| ); | |
| } catch (err) { | |
| setStatus(err.message); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return React.createElement( | |
| "div", | |
| { className: "shell" }, | |
| React.createElement( | |
| "header", | |
| { className: "hero" }, | |
| React.createElement("h1", null, "Gov Workflow OpenEnv - React Console"), | |
| React.createElement( | |
| "p", | |
| null, | |
| "Shows OpenEnv API execution, baseline/inference workflow visibility, and trained Phase 2 RL model behavior from one screen." | |
| ) | |
| ), | |
| React.createElement("div", { className: "status" }, status), | |
| React.createElement( | |
| "section", | |
| { className: "panel" }, | |
| React.createElement("h2", null, "Workflow Components Visibility"), | |
| React.createElement( | |
| "div", | |
| { className: "grid cols-2" }, | |
| ...components.map((c) => | |
| React.createElement( | |
| "article", | |
| { key: c.component, className: "panel" }, | |
| React.createElement("h3", null, c.component), | |
| React.createElement("div", { className: `badge ${c.available ? "ok" : ""}` }, c.available ? "available" : "missing"), | |
| React.createElement("p", { className: "small" }, c.description), | |
| c.command ? React.createElement("pre", null, c.command) : null, | |
| workflowIdForComponent(c.component) | |
| ? React.createElement( | |
| "div", | |
| { className: "btn-row", style: { marginTop: "8px" } }, | |
| React.createElement( | |
| "button", | |
| { | |
| className: "secondary", | |
| onClick: () => runWorkflowFromUi(workflowIdForComponent(c.component)), | |
| disabled: loading, | |
| }, | |
| "Run In Frontend" | |
| ) | |
| ) | |
| : null, | |
| c.notes ? React.createElement("p", { className: "small" }, c.notes) : null | |
| ) | |
| ) | |
| ), | |
| workflowMeta | |
| ? React.createElement( | |
| "div", | |
| { className: "small", style: { marginTop: "10px" } }, | |
| `Last run: ${workflowMeta.workflow_id} | exit=${workflowMeta.exit_code} | timeout=${workflowMeta.timed_out ? "true" : "false"} | duration=${formatNumber(workflowMeta.duration_seconds, 2)}s` | |
| ) | |
| : null, | |
| workflowOutput ? React.createElement("pre", { style: { marginTop: "10px" } }, workflowOutput) : null | |
| ), | |
| React.createElement( | |
| "section", | |
| { className: "panel" }, | |
| React.createElement("h2", null, "OpenEnv API Runner (step/reset/state/grade)"), | |
| React.createElement( | |
| "div", | |
| { className: "form-row" }, | |
| React.createElement( | |
| "label", | |
| null, | |
| "Task", | |
| React.createElement( | |
| "select", | |
| { value: taskId, onChange: (e) => setTaskId(e.target.value) }, | |
| ...tasks.map((t) => React.createElement("option", { key: t, value: t }, t)) | |
| ) | |
| ), | |
| React.createElement( | |
| "label", | |
| null, | |
| "Seed (optional)", | |
| React.createElement("input", { | |
| value: manualSeed, | |
| onChange: (e) => setManualSeed(e.target.value), | |
| placeholder: "11", | |
| }) | |
| ), | |
| React.createElement( | |
| "label", | |
| null, | |
| "Session ID", | |
| React.createElement("input", { | |
| value: sessionId, | |
| onChange: (e) => setSessionId(e.target.value), | |
| placeholder: "auto after reset", | |
| }) | |
| ) | |
| ), | |
| React.createElement( | |
| "label", | |
| { style: { marginTop: "10px" } }, | |
| "Action JSON for /step", | |
| React.createElement("textarea", { | |
| value: manualActionJson, | |
| onChange: (e) => setManualActionJson(e.target.value), | |
| }) | |
| ), | |
| React.createElement( | |
| "div", | |
| { className: "btn-row", style: { marginTop: "10px" } }, | |
| React.createElement("button", { onClick: manualReset, disabled: loading }, "Reset"), | |
| React.createElement("button", { onClick: manualStep, disabled: loading }, "Step"), | |
| React.createElement("button", { onClick: manualState, disabled: loading }, "State"), | |
| React.createElement("button", { onClick: manualGrade, disabled: loading }, "Grade"), | |
| ), | |
| React.createElement("pre", { style: { marginTop: "10px" } }, manualOutput) | |
| ), | |
| React.createElement( | |
| "section", | |
| { className: "panel" }, | |
| React.createElement("h2", null, "Baseline Agent Runner (backend policy)"), | |
| React.createElement( | |
| "div", | |
| { className: "form-row" }, | |
| React.createElement( | |
| "label", | |
| null, | |
| "Baseline Agent", | |
| React.createElement( | |
| "select", | |
| { value: agentPolicy, onChange: (e) => setAgentPolicy(e.target.value) }, | |
| ...agents.map((a) => React.createElement("option", { key: a, value: a }, a)) | |
| ) | |
| ), | |
| React.createElement( | |
| "label", | |
| null, | |
| "Steps", | |
| React.createElement("input", { | |
| type: "number", | |
| min: 1, | |
| max: 500, | |
| value: steps, | |
| onChange: (e) => setSteps(e.target.value), | |
| }) | |
| ) | |
| ), | |
| React.createElement( | |
| "div", | |
| { className: "btn-row", style: { marginTop: "10px" } }, | |
| React.createElement("button", { onClick: runBaseline, disabled: loading }, "Run Baseline"), | |
| React.createElement("button", { className: "secondary", onClick: resetBaselineSession, disabled: loading }, "Reset Session"), | |
| React.createElement("button", { className: "secondary", onClick: runBenchmark, disabled: loading }, "Run Benchmark"), | |
| ), | |
| React.createElement( | |
| "div", | |
| { className: "kpis", style: { marginTop: "10px" } }, | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Total Reward"), React.createElement("div", { className: "v" }, formatNumber(baselineKpi.reward))), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Backlog"), React.createElement("div", { className: "v" }, baselineKpi.backlog)), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Completed"), React.createElement("div", { className: "v" }, baselineKpi.completed)), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "SLA Breaches"), React.createElement("div", { className: "v" }, baselineKpi.sla)), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Fairness Gap"), React.createElement("div", { className: "v" }, formatNumber(baselineKpi.fairness))), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Grader Score"), React.createElement("div", { className: "v" }, graderScore == null ? "-" : formatNumber(graderScore, 3))), | |
| ), | |
| React.createElement("div", { style: { marginTop: "10px" } }, React.createElement(LineCanvas, { | |
| pointsA: baselineRewards, | |
| pointsB: baselineBacklog, | |
| labelA: "reward", | |
| labelB: "backlog", | |
| })), | |
| React.createElement( | |
| "div", | |
| { className: "table-wrap", style: { marginTop: "10px" } }, | |
| React.createElement( | |
| "table", | |
| null, | |
| React.createElement( | |
| "thead", | |
| null, | |
| React.createElement( | |
| "tr", | |
| null, | |
| React.createElement("th", null, "Step"), | |
| React.createElement("th", null, "Day"), | |
| React.createElement("th", null, "Action"), | |
| React.createElement("th", null, "Reward"), | |
| React.createElement("th", null, "Backlog"), | |
| React.createElement("th", null, "Completed"), | |
| React.createElement("th", null, "SLA"), | |
| React.createElement("th", null, "Done") | |
| ) | |
| ), | |
| React.createElement( | |
| "tbody", | |
| null, | |
| ...baselineTrace.map((r) => | |
| React.createElement( | |
| "tr", | |
| { key: `b-${r.step}` }, | |
| React.createElement("td", null, r.step), | |
| React.createElement("td", null, r.day), | |
| React.createElement("td", null, r.action), | |
| React.createElement("td", null, formatNumber(r.reward)), | |
| React.createElement("td", null, r.backlog), | |
| React.createElement("td", null, r.completed), | |
| React.createElement("td", null, r.sla_breaches), | |
| React.createElement("td", null, r.done ? "true" : "false") | |
| ) | |
| ) | |
| ) | |
| ) | |
| ), | |
| benchmarkRows.length | |
| ? React.createElement( | |
| "div", | |
| { className: "table-wrap", style: { marginTop: "10px" } }, | |
| React.createElement( | |
| "table", | |
| null, | |
| React.createElement( | |
| "thead", | |
| null, | |
| React.createElement( | |
| "tr", | |
| null, | |
| React.createElement("th", null, "Agent"), | |
| React.createElement("th", null, "Avg Score"), | |
| React.createElement("th", null, "Min"), | |
| React.createElement("th", null, "Max") | |
| ) | |
| ), | |
| React.createElement( | |
| "tbody", | |
| null, | |
| ...benchmarkRows.map((r) => | |
| React.createElement( | |
| "tr", | |
| { key: `bench-${r.agent_policy}` }, | |
| React.createElement("td", null, r.agent_policy), | |
| React.createElement("td", null, formatNumber(r.average_score, 3)), | |
| React.createElement("td", null, formatNumber(r.min_score, 3)), | |
| React.createElement("td", null, formatNumber(r.max_score, 3)) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| : null | |
| ), | |
| React.createElement( | |
| "section", | |
| { className: "panel" }, | |
| React.createElement("h2", null, "Trained RL Model (Phase 2 / Phase 3)"), | |
| React.createElement( | |
| "div", | |
| { className: "form-row" }, | |
| React.createElement( | |
| "label", | |
| null, | |
| "Model", | |
| React.createElement( | |
| "select", | |
| { | |
| value: modelPath, | |
| onChange: (e) => { | |
| const p = e.target.value; | |
| setModelPath(p); | |
| const hit = models.find((m) => m.path === p); | |
| if (hit) setModelType(hit.model_type); | |
| }, | |
| }, | |
| ...models.filter((m) => m.exists).map((m) => | |
| React.createElement("option", { key: m.path, value: m.path }, `${m.label}`) | |
| ) | |
| ) | |
| ), | |
| React.createElement( | |
| "label", | |
| null, | |
| "Model Type", | |
| React.createElement( | |
| "select", | |
| { value: modelType, onChange: (e) => setModelType(e.target.value) }, | |
| React.createElement("option", { value: "maskable" }, "maskable"), | |
| React.createElement("option", { value: "recurrent" }, "recurrent") | |
| ) | |
| ), | |
| React.createElement( | |
| "label", | |
| null, | |
| "Max Steps", | |
| React.createElement("input", { | |
| type: "number", | |
| min: 1, | |
| max: 1000, | |
| value: rlMaxSteps, | |
| onChange: (e) => setRlMaxSteps(e.target.value), | |
| }) | |
| ) | |
| ), | |
| React.createElement( | |
| "div", | |
| { className: "btn-row", style: { marginTop: "10px" } }, | |
| React.createElement("button", { onClick: runTrainedEpisode, disabled: loading }, "Run Trained Episode"), | |
| React.createElement("button", { className: "secondary", onClick: evaluateTrainedModel, disabled: loading }, "Evaluate Model"), | |
| React.createElement("button", { className: "secondary", onClick: compareBaselineVsPhase2, disabled: loading }, "Compare vs Baseline"), | |
| ), | |
| activeModel | |
| ? React.createElement("p", { className: "small", style: { marginTop: "10px" } }, `Using: ${activeModel.path}`) | |
| : null, | |
| rlRun | |
| ? React.createElement( | |
| "div", | |
| { style: { marginTop: "10px" } }, | |
| React.createElement( | |
| "div", | |
| { className: "kpis" }, | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Task"), React.createElement("div", { className: "v" }, rlRun.task_id)), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Seed"), React.createElement("div", { className: "v" }, rlRun.seed)), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Total Reward"), React.createElement("div", { className: "v" }, formatNumber(rlRun.total_reward))), | |
| React.createElement("div", { className: "kpi" }, React.createElement("div", { className: "k" }, "Grader Score"), React.createElement("div", { className: "v" }, formatNumber(rlRun.grader_score, 3))), | |
| ), | |
| React.createElement("div", { style: { marginTop: "10px" } }, React.createElement(LineCanvas, { | |
| pointsA: (rlRun.trace || []).map((x) => Math.max(0, Number(x.reward || 0))), | |
| pointsB: (rlRun.trace || []).map((x) => Number(x.backlog || 0)), | |
| labelA: "rl reward", | |
| labelB: "rl backlog", | |
| })), | |
| React.createElement( | |
| "div", | |
| { className: "table-wrap", style: { marginTop: "10px" } }, | |
| React.createElement( | |
| "table", | |
| null, | |
| React.createElement( | |
| "thead", | |
| null, | |
| React.createElement( | |
| "tr", | |
| null, | |
| React.createElement("th", null, "Step"), | |
| React.createElement("th", null, "Action Index"), | |
| React.createElement("th", null, "Action"), | |
| React.createElement("th", null, "Reward"), | |
| React.createElement("th", null, "Backlog"), | |
| React.createElement("th", null, "Completed"), | |
| React.createElement("th", null, "SLA") | |
| ) | |
| ), | |
| React.createElement( | |
| "tbody", | |
| null, | |
| ...(rlRun.trace || []).map((r) => | |
| React.createElement( | |
| "tr", | |
| { key: `rl-${r.step}` }, | |
| React.createElement("td", null, r.step), | |
| React.createElement("td", null, r.action_index), | |
| React.createElement("td", null, r.action_label), | |
| React.createElement("td", null, formatNumber(r.reward)), | |
| React.createElement("td", null, r.backlog), | |
| React.createElement("td", null, r.completed), | |
| React.createElement("td", null, r.sla_breaches) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| : null, | |
| rlEval.length | |
| ? React.createElement( | |
| "div", | |
| { className: "table-wrap", style: { marginTop: "10px" } }, | |
| React.createElement( | |
| "table", | |
| null, | |
| React.createElement( | |
| "thead", | |
| null, | |
| React.createElement( | |
| "tr", | |
| null, | |
| React.createElement("th", null, "Task"), | |
| React.createElement("th", null, "Score"), | |
| React.createElement("th", null, "Reward"), | |
| React.createElement("th", null, "Completed"), | |
| React.createElement("th", null, "SLA Breaches") | |
| ) | |
| ), | |
| React.createElement( | |
| "tbody", | |
| null, | |
| ...rlEval.map((r) => | |
| React.createElement( | |
| "tr", | |
| { key: `eval-${r.task_id}` }, | |
| React.createElement("td", null, r.task_id), | |
| React.createElement("td", null, formatNumber(r.grader_score, 3)), | |
| React.createElement("td", null, formatNumber(r.total_reward, 2)), | |
| React.createElement("td", null, r.total_completed), | |
| React.createElement("td", null, r.total_sla_breaches) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| : null, | |
| React.createElement("div", { style: { marginTop: "12px" } }, React.createElement(CompareCanvas, { | |
| baselineScore: compareData.baseline, | |
| rlScore: compareData.rl, | |
| })) | |
| ) | |
| ); | |
| } | |
| const rootEl = document.getElementById("app-root"); | |
| const root = createRoot(rootEl); | |
| root.render(React.createElement(App)); | |
| window.__APP_MOUNTED__ = true; | |