Spaces:
Build error
Build error
| const state = { | |
| tasks: [], | |
| selectedTask: "easy", | |
| sessionId: "default", | |
| observation: null, | |
| done: false, | |
| grade: null, | |
| logs: [], | |
| latest: null, | |
| showRawLatest: false, | |
| }; | |
| const refs = { | |
| taskSelect: document.getElementById("taskSelect"), | |
| resetBtn: document.getElementById("resetBtn"), | |
| autoBtn: document.getElementById("autoBtn"), | |
| aiBtn: document.getElementById("aiBtn"), | |
| finalizeBtn: document.getElementById("finalizeBtn"), | |
| suggestBtn: document.getElementById("suggestBtn"), | |
| runStepBtn: document.getElementById("runStepBtn"), | |
| clearLogBtn: document.getElementById("clearLogBtn"), | |
| actionForm: document.getElementById("actionForm"), | |
| actionType: document.getElementById("actionType"), | |
| passengerId: document.getElementById("passengerId"), | |
| flightId: document.getElementById("flightId"), | |
| sessionBadge: document.getElementById("sessionBadge"), | |
| phaseBadge: document.getElementById("phaseBadge"), | |
| scoreBadge: document.getElementById("scoreBadge"), | |
| taskMeta: document.getElementById("taskMeta"), | |
| budgetRemaining: document.getElementById("budgetRemaining"), | |
| budgetSpent: document.getElementById("budgetSpent"), | |
| progressValue: document.getElementById("progressValue"), | |
| invalidValue: document.getElementById("invalidValue"), | |
| stepCountBadge: document.getElementById("stepCountBadge"), | |
| pendingList: document.getElementById("pendingList"), | |
| flightsList: document.getElementById("flightsList"), | |
| latestResult: document.getElementById("latestResult"), | |
| latestRaw: document.getElementById("latestRaw"), | |
| toggleRawBtn: document.getElementById("toggleRawBtn"), | |
| logList: document.getElementById("logList"), | |
| }; | |
| function money(value) { | |
| return `$${Number(value || 0).toFixed(2)}`; | |
| } | |
| function safeJson(data) { | |
| return JSON.stringify(data, null, 2); | |
| } | |
| function escapeHtml(value) { | |
| return String(value) | |
| .replaceAll("&", "&") | |
| .replaceAll("<", "<") | |
| .replaceAll(">", ">") | |
| .replaceAll('"', """) | |
| .replaceAll("'", "'"); | |
| } | |
| function latestRow(label, value) { | |
| return ` | |
| <div class="latest-row"> | |
| <span class="latest-label">${escapeHtml(label)}</span> | |
| <span class="latest-value">${escapeHtml(value)}</span> | |
| </div> | |
| `; | |
| } | |
| function buildLatestSummary() { | |
| const data = state.latest; | |
| if (!data) return '<div class="latest-empty">No actions yet.</div>'; | |
| if (data.error) return `<div class="latest-status latest-status-error">Error</div><div class="latest-note">${escapeHtml(data.error)}</div>`; | |
| if (data.event === "reset") { | |
| return `<div class="latest-status latest-status-reset">Session reset</div>${latestRow("Task", data.task)}${latestRow("Session", data.session_id)}`; | |
| } | |
| const summary = []; | |
| summary.push(`<div class="latest-status ${data.done ? "latest-status-done" : "latest-status-active"}">${data.done ? "Complete" : "Step Applied"}</div>`); | |
| const obs = data.observation || {}; | |
| summary.push(latestRow("Reward", Number(data.reward?.value || 0).toFixed(4))); | |
| summary.push(latestRow("Budget Spent", money(obs.budget_spent))); | |
| summary.push(latestRow("Remaining", money(obs.budget_remaining))); | |
| if (data.final_score !== undefined) { | |
| summary.push(latestRow("Final Score", Number(data.final_score).toFixed(4))); | |
| } | |
| return summary.join(""); | |
| } | |
| async function api(path, options = {}) { | |
| const response = await fetch(path, { | |
| headers: { "Content-Type": "application/json" }, | |
| ...options, | |
| }); | |
| const payload = await response.json(); | |
| if (!response.ok) throw new Error(payload.detail || "API Error"); | |
| return payload; | |
| } | |
| function renderAll() { | |
| const obs = state.observation; | |
| if (!obs) return; | |
| refs.sessionBadge.textContent = `Session: ${state.sessionId}`; | |
| refs.phaseBadge.textContent = `State: ${state.done ? "Done" : "Active"}`; | |
| refs.scoreBadge.textContent = `Grade: ${state.grade == null ? "-" : Number(state.grade).toFixed(4)}`; | |
| refs.budgetRemaining.textContent = money(obs.budget_remaining); | |
| refs.budgetSpent.textContent = money(obs.budget_spent); | |
| refs.progressValue.textContent = `${obs.processed_count}/${obs.total_passengers}`; | |
| refs.invalidValue.textContent = obs.invalid_actions || 0; | |
| refs.stepCountBadge.textContent = `step ${obs.step_count || 0}`; | |
| // Render Lists | |
| refs.pendingList.innerHTML = (obs.pending_passengers || []).map(p => ` | |
| <div class="card card-passenger"> | |
| <strong>${p.id}</strong> - ${p.priority_tier}<br> | |
| <small>${p.cabin_class} | ${p.original_flight}</small> | |
| </div> | |
| `).join(""); | |
| refs.flightsList.innerHTML = (obs.available_flights || []).map(f => ` | |
| <div class="card card-flight"> | |
| <strong>${f.id}</strong> ${f.is_partner ? "(Partner)" : ""}<br> | |
| <small>T+${f.departure_hrs}h | E:${f.economy_seats} B:${f.business_seats}</small> | |
| </div> | |
| `).join(""); | |
| refs.latestResult.innerHTML = buildLatestSummary(); | |
| // Update Dropdowns | |
| const passOpts = obs.pending_passengers.map(p => `<option value="${p.id}">${p.id}</option>`).join(""); | |
| refs.passengerId.innerHTML = '<option value="">Select Passenger</option>' + passOpts; | |
| const flightOpts = obs.available_flights.map(f => `<option value="${f.id}">${f.id}</option>`).join(""); | |
| refs.flightId.innerHTML = '<option value="">Select Flight</option>' + flightOpts; | |
| } | |
| async function resetSession() { | |
| const result = await api("/reset", { | |
| method: "POST", | |
| body: JSON.stringify({ task: refs.taskSelect.value }), | |
| }); | |
| state.sessionId = result.session_id; | |
| state.observation = result.observation; | |
| state.done = false; | |
| state.grade = null; | |
| state.logs = []; | |
| state.latest = { event: "reset", task: result.task_key, session_id: result.session_id }; | |
| renderAll(); | |
| } | |
| async function runStep(action) { | |
| const result = await api("/step", { | |
| method: "POST", | |
| body: JSON.stringify({ session_id: state.sessionId, action }), | |
| }); | |
| processResult(result, action); | |
| } | |
| async function runAIStep(recursive = false) { | |
| if (state.done) return; | |
| refs.aiBtn.disabled = true; | |
| refs.aiBtn.textContent = "AI Thinking..."; | |
| try { | |
| const result = await api(`/auto_step?session_id=${encodeURIComponent(state.sessionId)}`, { method: "POST" }); | |
| processResult(result, { action_type: "AI_INFERENCE" }); | |
| if (recursive && !state.done) { | |
| setTimeout(() => runAIStep(true), 500); | |
| } | |
| } catch (err) { | |
| showError(err); | |
| } finally { | |
| if (!recursive || state.done) { | |
| refs.aiBtn.disabled = false; | |
| refs.aiBtn.textContent = "AI Auto-Play"; | |
| } | |
| } | |
| } | |
| function processResult(result, action) { | |
| state.observation = result.observation; | |
| state.done = !!result.done; | |
| state.latest = result; | |
| state.logs.push({ action, reward: result.reward?.value || 0 }); | |
| if (result.final_score !== undefined) state.grade = result.final_score; | |
| renderAll(); | |
| } | |
| function showError(err) { | |
| state.latest = { error: err.message }; | |
| renderAll(); | |
| } | |
| async function loadTasks() { | |
| const result = await api("/tasks"); | |
| state.tasks = result.tasks; | |
| refs.taskSelect.innerHTML = state.tasks.map(t => `<option value="${t.task_key}">${t.task_key.toUpperCase()}</option>`).join(""); | |
| } | |
| function bindEvents() { | |
| refs.resetBtn.onclick = resetSession; | |
| refs.aiBtn.onclick = () => runAIStep(true); | |
| refs.runStepBtn.onclick = (e) => { | |
| e.preventDefault(); | |
| const action = { | |
| action_type: refs.actionType.value, | |
| passenger_id: refs.passengerId.value, | |
| flight_id: refs.flightId.value | |
| }; | |
| runStep(action); | |
| }; | |
| } | |
| async function init() { | |
| bindEvents(); | |
| await loadTasks(); | |
| await resetSession(); | |
| } | |
| init(); | |