// app.js — Autonomy Calibration Environment UI
// Communicates with FastAPI backend at /api/reset and /api/step
const API = '/api';
let currentScenario = null;
let sessionStats = { episodes: 0, correct: 0, totalReward: 0 };
// ─── DOM REFERENCES ──────────────────────────────────────────
const elCategory = document.getElementById('category-tag');
const elContext = document.getElementById('context-text');
const elTask = document.getElementById('task-text');
const elAction = document.getElementById('action-text');
const elHistory = document.getElementById('history-list');
const elScenarioId = document.getElementById('scenario-id');
const elLoading = document.getElementById('loading');
const elContent = document.getElementById('content');
const elDoneBanner = document.getElementById('done-banner');
const elButtons = document.querySelectorAll('.decision-btn');
const elStatEpisodes = document.getElementById('stat-episodes');
const elStatCorrect = document.getElementById('stat-correct');
const elStatReward = document.getElementById('stat-reward');
const modal = document.getElementById('modal-overlay');
const elVerdict = document.getElementById('modal-verdict');
const elModalSub = document.getElementById('modal-subtitle');
const elModalTotal = document.getElementById('modal-total');
const elRewardRows = document.getElementById('reward-rows');
const elBestDecision = document.getElementById('best-decision');
// ─── GPU TRAINING ──────────────────────────────────────────────
async function startTraining() {
const btn = document.getElementById('train-btn');
const status = document.getElementById('train-status');
btn.disabled = true;
btn.innerHTML = "⏳ Initializing...";
status.innerHTML = "Requesting GPU compute resources...";
status.style.display = 'block';
status.style.color = '#9fa8da';
try {
const response = await fetch('/api/train', { method: 'POST' });
const data = await response.json();
if (data.status === 'started') {
btn.innerHTML = "⚙️ Training in Progress";
status.innerHTML = `Success: Training started on ${data.device}.
Check logs for progress.`;
status.style.color = '#81c784';
} else {
throw new Error(data.detail || "Failed to start training");
}
} catch (err) {
btn.disabled = false;
btn.innerHTML = "🚀 Start GPU Training";
status.innerHTML = `Error: ${err.message}`;
status.style.color = '#e57373';
}
}
async function uploadModel() {
const btn = document.getElementById('upload-btn');
const status = document.getElementById('train-status');
const repoId = "JOY0021/autonomy-agent-v2";
btn.disabled = true;
btn.innerHTML = "📡 Uploading...";
status.style.display = 'block';
status.innerHTML = `Pushing data to ${repoId}...`;
status.style.color = '#9fa8da';
try {
const response = await fetch('/api/upload', { method: 'POST' });
const data = await response.json();
if (data.status === 'success') {
btn.innerHTML = "✅ Published!";
status.innerHTML = `Success! View your model: huggingface.co/${repoId}`;
status.style.color = '#81c784';
} else {
throw new Error(data.message || "Upload failed");
}
} catch (err) {
btn.disabled = false;
btn.innerHTML = "📡 Publish to Hub";
status.innerHTML = `Upload Error: ${err.message}. Make sure the model page exists!`;
status.style.color = '#e57373';
}
}
// ─── STARTUP ─────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', startNewEpisode);
// ─── EPISODE MANAGEMENT ──────────────────────────────────────
async function startNewEpisode() {
elLoading.style.display = 'block';
elContent.style.display = 'none';
elDoneBanner.classList.remove('visible');
setButtonsEnabled(true);
try {
const res = await fetch(`${API}/reset`, { method: 'POST' });
if (!res.ok) throw new Error(`Reset failed: ${res.status}`);
const data = await res.json();
currentScenario = data;
renderScenario(data);
elLoading.style.display = 'none';
elContent.style.display = 'block';
sessionStats.episodes++;
updateStats();
} catch (err) {
elLoading.textContent = `Error loading scenario: ${err.message}`;
console.error(err);
}
}
// ─── RENDER SCENARIO ─────────────────────────────────────────
function renderScenario(data) {
// Support both wrapped {observation: ...} and flat Observation structures
const obs = data.observation || data;
const cat = data.category || obs.task_id || 'general';
elCategory.textContent = cat.toUpperCase();
elCategory.className = `category-tag category-${cat}`;
elScenarioId.textContent = data.scenario_id || `seed:${data.seed || 'none'}`;
// Map OpenEnv fields to UI
elContext.textContent = obs.prompt || obs.context || 'No context provided.';
elTask.textContent = obs.task || `Step ${obs.step}`;
elAction.textContent = obs.action_to_evaluate || 'Select an action to proceed.';
// History
elHistory.innerHTML = '';
const history = obs.history || [];
if (history.length === 0) {
elHistory.innerHTML = '