dhrumilparikh's picture
Restore previously deleted files per user request
4e3cf69
Raw
History Blame Contribute Delete
29.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Reference — Fleet AI Oversight</title>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--sidebar-bg: #0e0f11;
--sidebar-text: #a1a1aa;
--sidebar-accent: #6366f1;
--canvas: #f4f4f5;
--surface: #ffffff;
--border: #e4e4e7;
--text-primary: #111112;
--text-secondary: #71717a;
--text-mono: #3d3d3a;
--indigo: #6366f1;
--indigo-soft: #eef2ff;
--indigo-border: #c7d2fe;
--emerald: #059669;
--emerald-soft: #ecfdf5;
--amber: #d97706;
--amber-soft: #fffbeb;
--rose: #e11d48;
--rose-soft: #fff1f2;
--gray-100: #f4f4f5;
--gray-200: #e4e4e7;
--gray-300: #d4d4d8;
--gray-400: #a1a1aa;
--radius: 8px;
--shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
}
body { font-family: 'Plus Jakarta Sans', sans-serif; background: var(--canvas); color: var(--text-primary); font-size: 13px; line-height: 1.5; min-height: 100vh; }
.app-shell { display: flex; min-height: 100vh; }
.sidebar { width: 220px; flex-shrink: 0; background: var(--sidebar-bg); display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
.sidebar-logo { padding: 18px 16px 14px; border-bottom: 1px solid rgba(255,255,255,0.06); display: flex; align-items: center; gap: 9px; }
.logo-mark { width: 28px; height: 28px; background: var(--sidebar-accent); border-radius: 7px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 14px; }
.logo-text { font-size: 13px; font-weight: 600; color: #fff; line-height: 1.2; }
.logo-text span { display: block; font-size: 10px; color: var(--sidebar-text); font-weight: 400; }
.nav-section { padding: 20px 12px 6px; font-size: 10px; font-weight: 600; color: rgba(161,161,170,0.5); text-transform: uppercase; letter-spacing: 0.1em; }
.nav-item { display: flex; align-items: center; gap: 9px; padding: 7px 12px; margin: 1px 4px; border-radius: 6px; font-size: 13px; color: var(--sidebar-text); text-decoration: none; transition: all 0.15s; cursor: pointer; }
.nav-item:hover { background: rgba(255,255,255,0.06); color: #fff; }
.nav-item.active { background: rgba(99,102,241,0.18); color: #fff; }
.nav-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: 0.5; flex-shrink: 0; }
.nav-item.active .nav-dot { opacity: 1; background: var(--sidebar-accent); }
.main { flex: 1; padding: 40px 48px; max-width: 960px; }
.page-header { margin-bottom: 32px; }
.page-header h1 { font-size: 24px; font-weight: 700; margin-bottom: 6px; }
.page-header p { font-size: 13px; color: var(--text-secondary); }
.base-url-bar { display: flex; align-items: center; gap: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; margin-bottom: 32px; box-shadow: var(--shadow); }
.base-url-label { font-size: 10px; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; }
.base-url-value { font-family: 'DM Mono', monospace; font-size: 13px; color: var(--indigo); font-weight: 500; }
.section-header { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-secondary); margin-bottom: 10px; margin-top: 28px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
.endpoint-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 8px; box-shadow: var(--shadow); overflow: hidden; }
.endpoint-header { display: flex; align-items: center; gap: 12px; padding: 12px 16px; cursor: pointer; user-select: none; }
.endpoint-header:hover { background: var(--gray-100); }
.method-badge { font-family: 'DM Mono', monospace; font-size: 10px; font-weight: 500; padding: 2px 8px; border-radius: 4px; min-width: 44px; text-align: center; text-transform: uppercase; letter-spacing: 0.04em; }
.method-get { background: var(--emerald-soft); color: var(--emerald); border: 1px solid #a7f3d0; }
.method-post { background: var(--indigo-soft); color: var(--indigo); border: 1px solid var(--indigo-border); }
.endpoint-path { font-family: 'DM Mono', monospace; font-size: 13px; color: var(--text-mono); font-weight: 500; }
.endpoint-summary { font-size: 12px; color: var(--text-secondary); margin-left: auto; }
.expand-icon { font-size: 10px; color: var(--gray-400); transition: transform 0.2s; }
.endpoint-card.open .expand-icon { transform: rotate(180deg); }
.endpoint-body { border-top: 1px solid var(--border); padding: 16px; display: none; }
.endpoint-card.open .endpoint-body { display: block; }
.endpoint-desc { font-size: 12px; color: var(--text-secondary); margin-bottom: 14px; line-height: 1.6; }
.schema-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-secondary); margin-bottom: 6px; margin-top: 12px; }
.schema-block { font-family: 'DM Mono', monospace; font-size: 11.5px; background: var(--gray-100); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; color: var(--text-mono); white-space: pre-wrap; line-height: 1.6; }
.schema-key { color: var(--indigo); }
.schema-type { color: var(--amber); }
.schema-comment { color: var(--gray-400); }
.schema-required { color: var(--rose); font-size: 10px; font-weight: 600; text-transform: uppercase; }
.try-it-section { margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--border); }
.try-it-title { font-size: 11px; font-weight: 700; color: var(--text-primary); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 10px; }
.try-row { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
.try-row label { font-size: 11px; font-weight: 600; color: var(--text-secondary); }
.try-row input, .try-row select, .try-row textarea {
font-family: 'DM Mono', monospace; font-size: 12px; padding: 7px 10px;
border: 1px solid var(--border); border-radius: 6px; background: var(--surface);
color: var(--text-primary); outline: none; transition: border-color 0.15s;
}
.try-row input:focus, .try-row select:focus, .try-row textarea:focus { border-color: var(--indigo); }
.try-row textarea { resize: vertical; min-height: 60px; }
.btn-try { background: var(--indigo); color: #fff; padding: 8px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; border: none; font-family: 'Plus Jakarta Sans', sans-serif; transition: background 0.15s; }
.btn-try:hover { background: #4f46e5; }
.btn-try:disabled { opacity: 0.5; cursor: not-allowed; }
.response-block { margin-top: 10px; font-family: 'DM Mono', monospace; font-size: 11.5px; background: #0e0f11; border-radius: 6px; padding: 12px 14px; color: #a5f3fc; white-space: pre-wrap; max-height: 280px; overflow-y: auto; display: none; line-height: 1.6; }
.response-block.visible { display: block; }
.response-block.err { color: #fca5a5; }
.response-status { font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 4px; margin-bottom: 6px; display: inline-block; }
.status-ok { background: var(--emerald-soft); color: var(--emerald); }
.status-err { background: var(--rose-soft); color: var(--rose); }
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.35; } }
</style>
</head>
<body>
<div class="app-shell">
<nav class="sidebar">
<div class="sidebar-logo">
<div class="logo-mark"></div>
<div class="logo-text">Fleet Oversight<span>v2.0 · Hackathon Finals</span></div>
</div>
<div class="nav-section">Workspace</div>
<a href="/ui/" class="nav-item"><span class="nav-dot"></span>Dashboard</a>
<a href="/ui/fleet" class="nav-item"><span class="nav-dot"></span>Fleet Runner</a>
<a href="/ui/chat" class="nav-item"><span class="nav-dot"></span>RAG Chatbot</a>
<div class="nav-section">Evaluation</div>
<a href="/ui/results" class="nav-item"><span class="nav-dot"></span>Training Results</a>
<div class="nav-section">API</div>
<a href="/ui/api" class="nav-item active"><span class="nav-dot"></span>API Docs</a>
<a href="/ui/health" class="nav-item"><span class="nav-dot"></span>Health Check</a>
</nav>
<main class="main">
<div class="page-header">
<h1>API Reference</h1>
<p>All endpoints for the Fleet AI Oversight environment. Interactive — send real requests from this page.</p>
</div>
<div class="base-url-bar">
<span class="base-url-label">Base URL</span>
<span class="base-url-value" id="base-url">http://localhost:8000</span>
</div>
<!-- SYSTEM -->
<div class="section-header">System</div>
<div class="endpoint-card" id="ep-health">
<div class="endpoint-header" onclick="toggle('ep-health')">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/health</span>
<span class="endpoint-summary">Liveness probe</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Returns the current health status of the environment. No auth required. Use this to confirm the server is up before starting an episode.</p>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"status"</span>: <span class="schema-type">string</span> <span class="schema-comment">// "healthy"</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<button class="btn-try" onclick="sendRequest('GET','/health',null,'resp-health')">Send Request</button>
<div class="response-block" id="resp-health"></div>
</div>
</div>
</div>
<div class="endpoint-card" id="ep-metadata">
<div class="endpoint-header" onclick="toggle('ep-metadata')">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/metadata</span>
<span class="endpoint-summary">Project metadata</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Returns project name, version, author, and description.</p>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"project"</span>: <span class="schema-type">string</span>
<span class="schema-key">"version"</span>: <span class="schema-type">string</span>
<span class="schema-key">"author"</span>: <span class="schema-type">string</span>
<span class="schema-key">"description"</span>: <span class="schema-type">string</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<button class="btn-try" onclick="sendRequest('GET','/metadata',null,'resp-metadata')">Send Request</button>
<div class="response-block" id="resp-metadata"></div>
</div>
</div>
</div>
<!-- FLEET -->
<div class="section-header">Fleet</div>
<div class="endpoint-card" id="ep-reset">
<div class="endpoint-header" onclick="toggle('ep-reset')">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/fleet/reset</span>
<span class="endpoint-summary">Start a new episode</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Initialises a new FleetOversightEnv episode. Choose from easy_fleet (8 steps), medium_fleet (12 steps), or hard_fleet (16 steps). Returns the initial observation.</p>
<div class="schema-label">Request Body</div>
<div class="schema-block"><span class="schema-key">"task_id"</span>: <span class="schema-type">string</span> <span class="schema-comment">// "easy_fleet" | "medium_fleet" | "hard_fleet"</span>
<span class="schema-key">"seed"</span>: <span class="schema-type">integer</span> <span class="schema-comment">// random seed (default 42)</span></div>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"status"</span>: <span class="schema-type">string</span> <span class="schema-comment">// "ok"</span>
<span class="schema-key">"task_id"</span>: <span class="schema-type">string</span>
<span class="schema-key">"observation"</span>: <span class="schema-type">object</span> <span class="schema-comment">// FleetObservation</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<div class="try-row">
<label>task_id</label>
<select id="reset-task">
<option value="easy_fleet">easy_fleet</option>
<option value="medium_fleet">medium_fleet</option>
<option value="hard_fleet">hard_fleet</option>
</select>
</div>
<div class="try-row">
<label>seed</label>
<input id="reset-seed" type="number" value="42" />
</div>
<button class="btn-try" onclick="sendReset()">Send Request</button>
<div class="response-block" id="resp-reset"></div>
</div>
</div>
</div>
<div class="endpoint-card" id="ep-step">
<div class="endpoint-header" onclick="toggle('ep-step')">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/fleet/step</span>
<span class="endpoint-summary">Take an oversight action</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Executes one oversight action on the active episode. Returns the next observation, decomposed reward, done flag, and info dict. An episode must be active (call /fleet/reset first).</p>
<div class="schema-label">Request Body</div>
<div class="schema-block"><span class="schema-key">"action_type"</span>: <span class="schema-type">string</span> <span class="schema-required">required</span>
<span class="schema-comment">// monitor | intervene | approve | escalate | pause | resume | submit_audit</span>
<span class="schema-key">"worker_id"</span>: <span class="schema-type">string</span> <span class="schema-required">required</span>
<span class="schema-comment">// worker_0 | worker_1 | worker_2 | worker_3 | worker_4</span>
<span class="schema-key">"reason"</span>: <span class="schema-type">string</span> <span class="schema-comment">// optional justification</span></div>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"observation"</span>: <span class="schema-type">object</span>
<span class="schema-key">"reward"</span>: <span class="schema-type">object</span> <span class="schema-comment">// decomposed reward fields</span>
<span class="schema-key">"reward_total"</span>: <span class="schema-type">number</span> <span class="schema-comment">// clipped to [-1, 1]</span>
<span class="schema-key">"done"</span>: <span class="schema-type">boolean</span>
<span class="schema-key">"info"</span>: <span class="schema-type">object</span></div>
<div class="schema-label">Reward Decomposition</div>
<div class="schema-block"><span class="schema-key">detection</span>: +0.40 <span class="schema-comment">// correct anomaly identification</span>
<span class="schema-key">approval</span>: +0.10 <span class="schema-comment">// correct healthy approval</span>
<span class="schema-key">escalation</span>: +0.15 <span class="schema-comment">// correct escalation</span>
<span class="schema-key">false_pos</span>: −0.45 <span class="schema-comment">// intervening on healthy worker</span>
<span class="schema-key">missed</span>: −0.65 <span class="schema-comment">// missing a real violation</span>
<span class="schema-key">repeat_mon</span>: −0.10 <span class="schema-comment">// repeat monitor same worker</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<div class="try-row">
<label>action_type</label>
<select id="step-action">
<option value="monitor">monitor</option>
<option value="intervene">intervene</option>
<option value="approve">approve</option>
<option value="escalate">escalate</option>
<option value="pause">pause</option>
<option value="resume">resume</option>
<option value="submit_audit">submit_audit</option>
</select>
</div>
<div class="try-row">
<label>worker_id</label>
<select id="step-worker">
<option value="worker_0">worker_0 (Clean)</option>
<option value="worker_1">worker_1 (Chunk)</option>
<option value="worker_2">worker_2 (Embed)</option>
<option value="worker_3">worker_3 (Retrieve)</option>
<option value="worker_4">worker_4 (Eval)</option>
</select>
</div>
<div class="try-row">
<label>reason (optional)</label>
<input id="step-reason" type="text" placeholder="Anomaly flag detected…" />
</div>
<button class="btn-try" onclick="sendStep()">Send Request</button>
<div class="response-block" id="resp-step"></div>
</div>
</div>
</div>
<div class="endpoint-card" id="ep-state">
<div class="endpoint-header" onclick="toggle('ep-state')">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/fleet/state</span>
<span class="endpoint-summary">Current episode state</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Returns the full mutable episode state: step count, total reward, oversight budget, task ID, and done flag.</p>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"status"</span>: <span class="schema-type">string</span>
<span class="schema-key">"state"</span>: {
<span class="schema-key">"task_id"</span>: <span class="schema-type">string</span>
<span class="schema-key">"step_count"</span>: <span class="schema-type">integer</span>
<span class="schema-key">"total_reward"</span>: <span class="schema-type">number</span>
<span class="schema-key">"oversight_budget_remaining"</span>: <span class="schema-type">integer</span>
<span class="schema-key">"is_done"</span>: <span class="schema-type">boolean</span>
}</div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<button class="btn-try" onclick="sendRequest('GET','/fleet/state',null,'resp-state')">Send Request</button>
<div class="response-block" id="resp-state"></div>
</div>
</div>
</div>
<div class="endpoint-card" id="ep-workers">
<div class="endpoint-header" onclick="toggle('ep-workers')">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/fleet/workers</span>
<span class="endpoint-summary">Worker partial observations</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Returns partial observations for all 5 workers: last action, status, anomaly flag, budget remaining, risk score, and ground-truth anomaly status. The oversight agent only sees the partial obs — risk_score and is_anomalous are for debugging only.</p>
<div class="schema-label">Response (per worker)</div>
<div class="schema-block"><span class="schema-key">"last_action_name"</span>: <span class="schema-type">string</span>
<span class="schema-key">"budget_remaining"</span>: <span class="schema-type">integer</span>
<span class="schema-key">"anomaly_flag"</span>: <span class="schema-type">integer</span> <span class="schema-comment">// 0 or 1 (20% noisy)</span>
<span class="schema-key">"status"</span>: <span class="schema-type">string</span>
<span class="schema-key">"step_count"</span>: <span class="schema-type">integer</span>
<span class="schema-key">"risk_score"</span>: <span class="schema-type">number</span> <span class="schema-comment">// debug only</span>
<span class="schema-key">"is_anomalous"</span>: <span class="schema-type">boolean</span> <span class="schema-comment">// debug only</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<button class="btn-try" onclick="sendRequest('GET','/fleet/workers',null,'resp-workers')">Send Request</button>
<div class="response-block" id="resp-workers"></div>
</div>
</div>
</div>
<div class="endpoint-card" id="ep-report">
<div class="endpoint-header" onclick="toggle('ep-report')">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/fleet/report</span>
<span class="endpoint-summary">Run summary report</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Generates a narrative run report with action history, detected anomalies, total reward, and governance quality assessment.</p>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<button class="btn-try" onclick="sendRequest('GET','/fleet/report',null,'resp-report')">Send Request</button>
<div class="response-block" id="resp-report"></div>
</div>
</div>
</div>
<div class="endpoint-card" id="ep-evaluate">
<div class="endpoint-header" onclick="toggle('ep-evaluate')">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/fleet/evaluate</span>
<span class="endpoint-summary">Gate evaluation</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Runs the 5-gate OpenEnv evaluation on the completed episode. All gates must pass for the episode to be considered a success.</p>
<div class="schema-label">Gates (all must pass)</div>
<div class="schema-block"><span class="schema-key">min_oversight_score</span> ≥ 0.55
<span class="schema-key">max_invalid_actions</span> ≤ 3
<span class="schema-key">max_governance_risk</span> ≤ 0.60
<span class="schema-key">min_detection_rate</span> ≥ 0.50
<span class="schema-key">min_pipeline_quality</span> ≥ 0.60</div>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"status"</span>: <span class="schema-type">string</span>
<span class="schema-key">"evaluation"</span>: {
<span class="schema-key">"gates"</span>: <span class="schema-type">object</span> <span class="schema-comment">// gate_name → {passed, value, threshold}</span>
<span class="schema-key">"passed"</span>: <span class="schema-type">boolean</span>
<span class="schema-key">"score"</span>: <span class="schema-type">number</span>
}</div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<button class="btn-try" onclick="sendRequest('POST','/fleet/evaluate',{},'resp-evaluate')">Send Request</button>
<div class="response-block" id="resp-evaluate"></div>
</div>
</div>
</div>
<!-- RAG -->
<div class="section-header">RAG</div>
<div class="endpoint-card" id="ep-rag">
<div class="endpoint-header" onclick="toggle('ep-rag')">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/rag/query</span>
<span class="endpoint-summary">Query the RAG pipeline</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Queries the vector index built by worker_3 (Retrieval). Requires a completed fleet episode where the embedding worker ran successfully. Returns the top answer, source chunks, and a confidence score.</p>
<div class="schema-label">Request Body</div>
<div class="schema-block"><span class="schema-key">"question"</span>: <span class="schema-type">string</span> <span class="schema-required">required</span></div>
<div class="schema-label">Response</div>
<div class="schema-block"><span class="schema-key">"error"</span>: <span class="schema-type">string | null</span> <span class="schema-comment">// non-null if pipeline not built</span>
<span class="schema-key">"answer"</span>: <span class="schema-type">string | null</span>
<span class="schema-key">"source_chunks"</span>: <span class="schema-type">array</span> <span class="schema-comment">// [{chunk_id, text, score}]</span>
<span class="schema-key">"confidence"</span>: <span class="schema-type">number</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<div class="try-row">
<label>question</label>
<input id="rag-q" type="text" placeholder="What is the retrieval pipeline?" value="What is the retrieval pipeline?" />
</div>
<button class="btn-try" onclick="sendRAG()">Send Request</button>
<div class="response-block" id="resp-rag"></div>
</div>
</div>
</div>
<!-- PLOTS -->
<div class="section-header">Plots &amp; Training Data</div>
<div class="endpoint-card" id="ep-plots">
<div class="endpoint-header" onclick="toggle('ep-plots')">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/plots/{filename}</span>
<span class="endpoint-summary">Serve training plot files</span>
<span class="expand-icon"></span>
</div>
<div class="endpoint-body">
<p class="endpoint-desc">Serves files from the <code>/plots/</code> directory. The training results page fetches <code>training_metrics.json</code> for live chart data. Run <code>python fleet_train.py --simulate</code> to generate plot files.</p>
<div class="schema-label">Common files</div>
<div class="schema-block">training_metrics.json <span class="schema-comment">// reward + detection curves</span>
reward_curve.png <span class="schema-comment">// matplotlib chart</span>
detection_rate.png <span class="schema-comment">// detection improvement</span></div>
<div class="try-it-section">
<div class="try-it-title">Try It</div>
<div class="try-row">
<label>filename</label>
<input id="plot-file" type="text" value="training_metrics.json" />
</div>
<button class="btn-try" onclick="sendPlot()">Send Request</button>
<div class="response-block" id="resp-plots"></div>
</div>
</div>
</div>
</main>
</div>
<script>
// Detect base URL
document.getElementById('base-url').textContent = window.location.origin;
function toggle(id) {
const card = document.getElementById(id);
card.classList.toggle('open');
}
async function sendRequest(method, path, body, respId) {
const el = document.getElementById(respId);
el.className = 'response-block visible';
el.textContent = 'Sending…';
try {
const opts = { method, headers: { 'Content-Type': 'application/json' } };
if (body !== null) opts.body = JSON.stringify(body);
const resp = await fetch(path, opts);
const text = await resp.text();
let parsed;
try { parsed = JSON.parse(text); } catch { parsed = text; }
const statusEl = `<span class="response-status ${resp.ok ? 'status-ok' : 'status-err'}">${resp.status} ${resp.statusText}</span>\n`;
el.innerHTML = statusEl + (typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : parsed);
el.className = `response-block visible ${resp.ok ? '' : 'err'}`;
} catch (e) {
el.textContent = 'Error: ' + e.message;
el.className = 'response-block visible err';
}
}
async function sendReset() {
const task = document.getElementById('reset-task').value;
const seed = parseInt(document.getElementById('reset-seed').value) || 42;
await sendRequest('POST', '/fleet/reset', { task_id: task, seed }, 'resp-reset');
}
async function sendStep() {
const action_type = document.getElementById('step-action').value;
const worker_id = document.getElementById('step-worker').value;
const reason = document.getElementById('step-reason').value || null;
await sendRequest('POST', '/fleet/step', { action_type, worker_id, reason }, 'resp-step');
}
async function sendRAG() {
const question = document.getElementById('rag-q').value;
await sendRequest('POST', '/rag/query', { question }, 'resp-rag');
}
async function sendPlot() {
const filename = document.getElementById('plot-file').value;
await sendRequest('GET', `/plots/${encodeURIComponent(filename)}`, null, 'resp-plots');
}
</script>
</body>
</html>