import { useEffect, useMemo, useRef, useState } from "react"; function resolveApiBase() { const explicitBase = import.meta.env.VITE_API_BASE; if (explicitBase) return explicitBase.replace(/\/$/, ""); const host = window.location.hostname; const isLocal = host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0"; // In local Vite dev, backend runs on :7860. In Spaces/prod, serve same-origin. if (isLocal && window.location.port === "5173") { return "http://localhost:7860"; } return window.location.origin.replace(/\/$/, ""); } const API_BASE = resolveApiBase(); const WS_URL = `${API_BASE.replace(/^http/, "ws")}/ws`; const TASKS = ["easy_screening", "budgeted_screening", "complex_tradeoff"]; async function apiPost(path, body) { const res = await fetch(`${API_BASE}${path}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { const msg = await res.text(); throw new Error(msg || `HTTP ${res.status}`); } return res.json(); } export default function App() { const [taskId, setTaskId] = useState("budgeted_screening"); const [obs, setObs] = useState(null); const [log, setLog] = useState([]); const [loading, setLoading] = useState(false); const [action, setAction] = useState({ action_type: "query_ddi", drug_id_1: "", drug_id_2: "", target_drug_id: "", intervention_type: "stop", proposed_new_drug_id: "", rationale: "", }); const medIds = useMemo( () => (obs?.current_medications || []).map((m) => m.drug_id), [obs] ); const hasValidEpisode = Boolean(obs?.episode_id) && (obs?.current_medications?.length || 0) > 0; const isDone = Boolean(obs?.done); const finalScore = typeof obs?.metadata?.grader_score === "number" ? obs.metadata.grader_score : null; const noBudgetsLeft = hasValidEpisode && (obs?.remaining_query_budget ?? 0) <= 0 && (obs?.remaining_intervention_budget ?? 0) <= 0; const wsRef = useRef(null); const pendingRef = useRef([]); const wsEnsure = async () => { if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) return wsRef.current; if (wsRef.current && wsRef.current.readyState === WebSocket.CONNECTING) { await new Promise((r) => setTimeout(r, 80)); return wsEnsure(); } const ws = new WebSocket(WS_URL); wsRef.current = ws; ws.onmessage = (evt) => { try { const msg = JSON.parse(evt.data); const pending = pendingRef.current.shift(); if (pending) pending.resolve(msg); } catch (e) { const pending = pendingRef.current.shift(); if (pending) pending.reject(e); } }; ws.onerror = (err) => { const pending = pendingRef.current.shift(); if (pending) pending.reject(err); }; ws.onclose = () => { wsRef.current = null; }; await new Promise((resolve, reject) => { const t = setTimeout(() => reject(new Error("WebSocket connect timeout")), 2500); ws.onopen = () => { clearTimeout(t); resolve(); }; }); return ws; }; const wsSend = async (type, data) => { const ws = await wsEnsure(); return await new Promise((resolve, reject) => { pendingRef.current.push({ resolve, reject }); ws.send(JSON.stringify({ type, data })); }); }; useEffect(() => { return () => { try { wsRef.current?.close(); } catch { // ignore } }; }, []); const appendLog = (text) => { setLog((prev) => [`${new Date().toLocaleTimeString()} ${text}`, ...prev].slice(0, 20)); }; const normalizeObsFromWs = (packetData) => { const observation = packetData?.observation || {}; const mergedMetadata = { ...(observation?.metadata || {}), ...(packetData?.info || {}), }; return { ...observation, done: Boolean(packetData?.done ?? observation?.done ?? false), reward: packetData?.reward ?? observation?.reward ?? null, metadata: mergedMetadata, }; }; const handleReset = async () => { setLoading(true); try { const msg = await wsSend("reset", { task_id: taskId }); const data = msg?.data || {}; const normalized = normalizeObsFromWs(data); setObs(normalized); const ids = (normalized?.current_medications || []).map((m) => m.drug_id); setAction((prev) => ({ ...prev, drug_id_1: ids[0] || "", drug_id_2: ids[1] || "", target_drug_id: ids[0] || "", })); appendLog(`Reset task=${taskId}`); } catch (err) { appendLog(`Reset failed: ${err.message}`); } finally { setLoading(false); } }; const buildActionPayload = () => { if (noBudgetsLeft) { return { action_type: "finish_review" }; } if (action.action_type === "query_ddi") { return { action_type: "query_ddi", drug_id_1: action.drug_id_1, drug_id_2: action.drug_id_2, }; } if (action.action_type === "propose_intervention") { return { action_type: "propose_intervention", target_drug_id: action.target_drug_id, intervention_type: action.intervention_type, proposed_new_drug_id: action.proposed_new_drug_id || undefined, rationale: action.rationale || undefined, }; } return { action_type: "finish_review" }; }; const isActionValid = () => { if (!hasValidEpisode) return false; if (isDone) return false; if (noBudgetsLeft) return true; if (action.action_type === "query_ddi") { return Boolean(action.drug_id_1 && action.drug_id_2); } if (action.action_type === "propose_intervention") { return Boolean(action.target_drug_id && action.intervention_type); } return true; }; const handleStep = async (overrideAction = null) => { if (!hasValidEpisode) { appendLog("Run Reset Episode before stepping."); return; } setLoading(true); try { const payload = overrideAction || buildActionPayload(); const msg = await wsSend("step", payload); const data = msg?.data || {}; const normalized = normalizeObsFromWs(data); setObs(normalized); appendLog(`Step: ${payload.action_type} -> reward=${data.reward ?? 0}`); } catch (err) { appendLog(`Step failed: ${err.message}`); } finally { setLoading(false); } }; const askAi = async () => { if (!hasValidEpisode) { appendLog("Run Reset Episode before asking AI."); return; } setLoading(true); try { const data = await apiPost("/agent/suggest", { observation: obs }); appendLog(`AI suggestion: ${data.action.action_type}`); await handleStep(data.action); } catch (err) { appendLog(`AI suggestion failed: ${err.message}`); } finally { setLoading(false); } }; return (

Polypharmacy Control Center

Metaverse Clinical Ops Console

{hasValidEpisode ? "Session Live" : "Waiting for reset"}

Episode

{hasValidEpisode ? (
Episode{obs.episode_id}
Task{obs.task_id}
Age / Sex{obs.age} / {obs.sex}
Step{obs.step_index}
Query budget{obs.remaining_query_budget}
Intervention budget{obs.remaining_intervention_budget}
) : (

Start with Reset Episode. Until then, step actions are blocked.

)} {noBudgetsLeft && (

Query and intervention budgets are exhausted. Finish review to get final score.

)} {isDone && (

Episode complete {finalScore !== null ? ` • final score: ${finalScore.toFixed(3)}` : ""}. Click Reset Episode to start a new case.

)}

Action Console

{action.action_type === "query_ddi" && (
setAction((a) => ({ ...a, drug_id_1: e.target.value }))} /> setAction((a) => ({ ...a, drug_id_2: e.target.value }))} />
)} {action.action_type === "propose_intervention" && (
setAction((a) => ({ ...a, proposed_new_drug_id: e.target.value })) } /> setAction((a) => ({ ...a, rationale: e.target.value }))} />
)}

Current Medications

{(obs?.current_medications || []).map((m) => (
{m.drug_id}

{m.generic_name}

{m.dose_mg} mg • {m.atc_class}
))}

Event Log

{log.map((line, idx) => (
{line}
))}
); }