Spaces:
Running
Running
Merge remote main with local project
Browse files
ui/app/components/AgentTrustMonitor.tsx
CHANGED
|
@@ -30,7 +30,7 @@ function trustColor(t: number) {
|
|
| 30 |
export default function AgentTrustMonitor({
|
| 31 |
observation, trustDeltas, activeSpec, events, running, totalReward,
|
| 32 |
}: Props) {
|
| 33 |
-
const agents = observation?.available_specialists ?
|
| 34 |
const trust = observation?.trust_snapshot ?? {};
|
| 35 |
const lastReward = observation?.last_reward ?? 0;
|
| 36 |
|
|
@@ -108,4 +108,4 @@ export default function AgentTrustMonitor({
|
|
| 108 |
</div>
|
| 109 |
</div>
|
| 110 |
);
|
| 111 |
-
}
|
|
|
|
| 30 |
export default function AgentTrustMonitor({
|
| 31 |
observation, trustDeltas, activeSpec, events, running, totalReward,
|
| 32 |
}: Props) {
|
| 33 |
+
const agents = observation?.available_specialists || observation?.available_workers || ["S0", "S1", "S2", "S3", "S4"];
|
| 34 |
const trust = observation?.trust_snapshot ?? {};
|
| 35 |
const lastReward = observation?.last_reward ?? 0;
|
| 36 |
|
|
|
|
| 108 |
</div>
|
| 109 |
</div>
|
| 110 |
);
|
| 111 |
+
}
|
ui/app/components/GPUClusterPanel.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
-
import { useState, useEffect
|
| 4 |
-
import { motion
|
| 5 |
|
| 6 |
type NodeStatus = "ACTIVE" | "IDLE" | "OVERLOADED" | "FAILED";
|
| 7 |
|
|
@@ -13,79 +13,82 @@ interface GPUNode {
|
|
| 13 |
status: NodeStatus;
|
| 14 |
}
|
| 15 |
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
const [mounted, setMounted] = useState(false);
|
| 18 |
const [nodes, setNodes] = useState<GPUNode[]>([
|
| 19 |
-
{ id: "GPU-1", utilization:
|
| 20 |
-
{ id: "GPU-2", utilization:
|
| 21 |
-
{ id: "GPU-3", utilization:
|
| 22 |
{ id: "GPU-4", utilization: 0, memory: 0, load: 0, status: "IDLE" },
|
| 23 |
]);
|
| 24 |
|
| 25 |
const [avgLoad, setAvgLoad] = useState(0);
|
| 26 |
const [jitter, setJitter] = useState(0.45);
|
| 27 |
|
|
|
|
|
|
|
|
|
|
| 28 |
useEffect(() => {
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
| 55 |
|
| 56 |
useEffect(() => {
|
| 57 |
const total = nodes.reduce((acc, n) => acc + n.utilization, 0);
|
| 58 |
setAvgLoad(total / nodes.length);
|
| 59 |
}, [nodes]);
|
| 60 |
|
| 61 |
-
if (!mounted)
|
| 62 |
-
return (
|
| 63 |
-
<section className="section-block" id="gpu-cluster" style={{ opacity: 0 }}>
|
| 64 |
-
<div className="section-label">03 // COMPUTE RESOURCES</div>
|
| 65 |
-
<h2 className="section-title">GPU Compute Clusters</h2>
|
| 66 |
-
</section>
|
| 67 |
-
);
|
| 68 |
-
}
|
| 69 |
|
| 70 |
return (
|
| 71 |
<section className="section-block" id="gpu-cluster">
|
| 72 |
<div className="section-label">03 // COMPUTE RESOURCES</div>
|
| 73 |
<h2 className="section-title">GPU Compute Clusters</h2>
|
| 74 |
<p className="section-desc">
|
| 75 |
-
Real-time telemetry from the underlying inference hardware.
|
| 76 |
-
|
| 77 |
</p>
|
| 78 |
|
| 79 |
<div className="cluster-grid">
|
| 80 |
{nodes.map((node) => (
|
| 81 |
<div key={node.id} className={`card node-card ${node.status.toLowerCase()}`}>
|
| 82 |
-
<div className="card-id">{node.id} //
|
| 83 |
-
|
| 84 |
<div className="node-status-badge">
|
| 85 |
-
<div className="status-dot" style={{
|
| 86 |
-
background: node.status === "
|
| 87 |
-
|
| 88 |
-
|
| 89 |
}} />
|
| 90 |
{node.status}
|
| 91 |
</div>
|
|
@@ -96,9 +99,10 @@ export default function GPUClusterPanel() {
|
|
| 96 |
<span style={{ color: "var(--cyan)" }}>{Math.round(node.utilization)}%</span>
|
| 97 |
</div>
|
| 98 |
<div className="metric-bar-bg">
|
| 99 |
-
<motion.div
|
| 100 |
-
className="metric-bar-fill"
|
| 101 |
animate={{ width: `${node.utilization}%` }}
|
|
|
|
| 102 |
style={{ background: node.utilization > 90 ? "var(--red)" : "var(--cyan)" } as any}
|
| 103 |
/>
|
| 104 |
</div>
|
|
@@ -110,9 +114,10 @@ export default function GPUClusterPanel() {
|
|
| 110 |
<span style={{ color: "var(--green)" }}>{Math.round(node.memory)}%</span>
|
| 111 |
</div>
|
| 112 |
<div className="metric-bar-bg">
|
| 113 |
-
<motion.div
|
| 114 |
-
className="metric-bar-fill"
|
| 115 |
animate={{ width: `${node.memory}%` }}
|
|
|
|
| 116 |
style={{ background: "var(--green)" } as any}
|
| 117 |
/>
|
| 118 |
</div>
|
|
@@ -136,7 +141,7 @@ export default function GPUClusterPanel() {
|
|
| 136 |
<div className="cluster-total-load">
|
| 137 |
<span className="label">TOTAL CLUSTER LOAD</span>
|
| 138 |
<div className="load-meter-bg">
|
| 139 |
-
<motion.div
|
| 140 |
className="load-meter-fill"
|
| 141 |
animate={{ width: `${avgLoad}%` }}
|
| 142 |
style={{ background: avgLoad > 80 ? "var(--red)" : "var(--cyan)", color: avgLoad > 80 ? "var(--red)" : "var(--cyan)" } as any}
|
|
@@ -152,4 +157,4 @@ export default function GPUClusterPanel() {
|
|
| 152 |
</div>
|
| 153 |
</section>
|
| 154 |
);
|
| 155 |
-
}
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
+
import { useState, useEffect } from "react";
|
| 4 |
+
import { motion } from "framer-motion";
|
| 5 |
|
| 6 |
type NodeStatus = "ACTIVE" | "IDLE" | "OVERLOADED" | "FAILED";
|
| 7 |
|
|
|
|
| 13 |
status: NodeStatus;
|
| 14 |
}
|
| 15 |
|
| 16 |
+
interface GPUClusterPanelProps {
|
| 17 |
+
sessionId?: string;
|
| 18 |
+
mode?: string;
|
| 19 |
+
gpuPool?: any[]; // Live data from observation
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
export default function GPUClusterPanel({ sessionId, mode, gpuPool }: GPUClusterPanelProps) {
|
| 23 |
const [mounted, setMounted] = useState(false);
|
| 24 |
const [nodes, setNodes] = useState<GPUNode[]>([
|
| 25 |
+
{ id: "GPU-1", utilization: 0, memory: 0, load: 0, status: "IDLE" },
|
| 26 |
+
{ id: "GPU-2", utilization: 0, memory: 0, load: 0, status: "IDLE" },
|
| 27 |
+
{ id: "GPU-3", utilization: 0, memory: 0, load: 0, status: "IDLE" },
|
| 28 |
{ id: "GPU-4", utilization: 0, memory: 0, load: 0, status: "IDLE" },
|
| 29 |
]);
|
| 30 |
|
| 31 |
const [avgLoad, setAvgLoad] = useState(0);
|
| 32 |
const [jitter, setJitter] = useState(0.45);
|
| 33 |
|
| 34 |
+
useEffect(() => { setMounted(true); }, []);
|
| 35 |
+
|
| 36 |
+
// ── LIVE SYNC FROM OBSERVATION ────────────────────────────
|
| 37 |
useEffect(() => {
|
| 38 |
+
if (gpuPool && Array.isArray(gpuPool)) {
|
| 39 |
+
setNodes(gpuPool.slice(0, 4).map((g: any) => {
|
| 40 |
+
const util = (g.memory_used / g.memory_total) * 100;
|
| 41 |
+
let status = g.state.toUpperCase();
|
| 42 |
+
if (status === "ALLOCATED") status = "ACTIVE";
|
| 43 |
+
|
| 44 |
+
return {
|
| 45 |
+
id: g.id,
|
| 46 |
+
utilization: util,
|
| 47 |
+
memory: util,
|
| 48 |
+
load: (util / 100) * 4.2,
|
| 49 |
+
status: status as NodeStatus
|
| 50 |
+
};
|
| 51 |
+
}));
|
| 52 |
+
} else if (!sessionId || mode !== "cluster") {
|
| 53 |
+
// Fallback to subtle idle simulation if no live data
|
| 54 |
+
const timer = setInterval(() => {
|
| 55 |
+
setJitter(Math.random() * 0.5);
|
| 56 |
+
setNodes(prev => prev.map(n => ({
|
| 57 |
+
...n,
|
| 58 |
+
utilization: Math.max(0, n.utilization + (Math.random() - 0.5) * 2),
|
| 59 |
+
load: n.utilization * 0.04
|
| 60 |
+
})));
|
| 61 |
+
}, 2000);
|
| 62 |
+
return () => clearInterval(timer);
|
| 63 |
+
}
|
| 64 |
+
}, [gpuPool, sessionId, mode]);
|
| 65 |
|
| 66 |
useEffect(() => {
|
| 67 |
const total = nodes.reduce((acc, n) => acc + n.utilization, 0);
|
| 68 |
setAvgLoad(total / nodes.length);
|
| 69 |
}, [nodes]);
|
| 70 |
|
| 71 |
+
if (!mounted) return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
return (
|
| 74 |
<section className="section-block" id="gpu-cluster">
|
| 75 |
<div className="section-label">03 // COMPUTE RESOURCES</div>
|
| 76 |
<h2 className="section-title">GPU Compute Clusters</h2>
|
| 77 |
<p className="section-desc">
|
| 78 |
+
Real-time telemetry from the underlying inference hardware.
|
| 79 |
+
Note how cluster utilization spikes as the RL model allocates worker jobs.
|
| 80 |
</p>
|
| 81 |
|
| 82 |
<div className="cluster-grid">
|
| 83 |
{nodes.map((node) => (
|
| 84 |
<div key={node.id} className={`card node-card ${node.status.toLowerCase()}`}>
|
| 85 |
+
<div className="card-id">{node.id} // CORE-AX-{node.id.split("-")[1] || "0X"}</div>
|
| 86 |
+
|
| 87 |
<div className="node-status-badge">
|
| 88 |
+
<div className="status-dot" style={{
|
| 89 |
+
background: node.status === "ACTIVE" ? "var(--green)" :
|
| 90 |
+
node.status === "OVERLOADED" ? "var(--red)" :
|
| 91 |
+
node.status === "FAILED" ? "#555" : "var(--muted)"
|
| 92 |
}} />
|
| 93 |
{node.status}
|
| 94 |
</div>
|
|
|
|
| 99 |
<span style={{ color: "var(--cyan)" }}>{Math.round(node.utilization)}%</span>
|
| 100 |
</div>
|
| 101 |
<div className="metric-bar-bg">
|
| 102 |
+
<motion.div
|
| 103 |
+
className="metric-bar-fill"
|
| 104 |
animate={{ width: `${node.utilization}%` }}
|
| 105 |
+
transition={{ type: "spring", stiffness: 100, damping: 20 }}
|
| 106 |
style={{ background: node.utilization > 90 ? "var(--red)" : "var(--cyan)" } as any}
|
| 107 |
/>
|
| 108 |
</div>
|
|
|
|
| 114 |
<span style={{ color: "var(--green)" }}>{Math.round(node.memory)}%</span>
|
| 115 |
</div>
|
| 116 |
<div className="metric-bar-bg">
|
| 117 |
+
<motion.div
|
| 118 |
+
className="metric-bar-fill"
|
| 119 |
animate={{ width: `${node.memory}%` }}
|
| 120 |
+
transition={{ type: "spring", stiffness: 100, damping: 20 }}
|
| 121 |
style={{ background: "var(--green)" } as any}
|
| 122 |
/>
|
| 123 |
</div>
|
|
|
|
| 141 |
<div className="cluster-total-load">
|
| 142 |
<span className="label">TOTAL CLUSTER LOAD</span>
|
| 143 |
<div className="load-meter-bg">
|
| 144 |
+
<motion.div
|
| 145 |
className="load-meter-fill"
|
| 146 |
animate={{ width: `${avgLoad}%` }}
|
| 147 |
style={{ background: avgLoad > 80 ? "var(--red)" : "var(--cyan)", color: avgLoad > 80 ? "var(--red)" : "var(--cyan)" } as any}
|
|
|
|
| 157 |
</div>
|
| 158 |
</section>
|
| 159 |
);
|
| 160 |
+
}
|
ui/app/components/SpecialistNetwork.tsx
CHANGED
|
@@ -17,7 +17,7 @@ export default function SpecialistNetwork({
|
|
| 17 |
trustDeltas: Record<string, number>;
|
| 18 |
activeSpec: string | null;
|
| 19 |
}) {
|
| 20 |
-
const ids = observation?.available_specialists ?
|
| 21 |
return (
|
| 22 |
<div className="net">
|
| 23 |
<svg className="net-svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
|
@@ -79,4 +79,4 @@ export default function SpecialistNetwork({
|
|
| 79 |
})}
|
| 80 |
</div>
|
| 81 |
);
|
| 82 |
-
}
|
|
|
|
| 17 |
trustDeltas: Record<string, number>;
|
| 18 |
activeSpec: string | null;
|
| 19 |
}) {
|
| 20 |
+
const ids = observation?.available_specialists || observation?.available_workers || ["S0", "S1", "S2", "S3", "S4"];
|
| 21 |
return (
|
| 22 |
<div className="net">
|
| 23 |
<svg className="net-svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
|
|
|
| 79 |
})}
|
| 80 |
</div>
|
| 81 |
);
|
| 82 |
+
}
|
ui/app/components/TrustTimeline.tsx
CHANGED
|
@@ -10,7 +10,7 @@ export default function TrustTimeline({
|
|
| 10 |
observation: Observation | null;
|
| 11 |
trustDeltas: Record<string, number>;
|
| 12 |
}) {
|
| 13 |
-
const ids = observation?.available_specialists ?? ["S0", "S1", "S2", "S3", "S4"];
|
| 14 |
return (
|
| 15 |
<div className="tl">
|
| 16 |
{ids.map((id) => {
|
|
@@ -38,4 +38,4 @@ export default function TrustTimeline({
|
|
| 38 |
})}
|
| 39 |
</div>
|
| 40 |
);
|
| 41 |
-
}
|
|
|
|
| 10 |
observation: Observation | null;
|
| 11 |
trustDeltas: Record<string, number>;
|
| 12 |
}) {
|
| 13 |
+
const ids = observation?.available_specialists ?? observation?.available_workers ?? ["S0", "S1", "S2", "S3", "S4"];
|
| 14 |
return (
|
| 15 |
<div className="tl">
|
| 16 |
{ids.map((id) => {
|
|
|
|
| 38 |
})}
|
| 39 |
</div>
|
| 40 |
);
|
| 41 |
+
}
|
ui/app/hooks/useSentinel.ts
CHANGED
|
@@ -9,13 +9,16 @@ import type {
|
|
| 9 |
|
| 10 |
/* ── helpers ──────────────────────────────────────────── */
|
| 11 |
|
| 12 |
-
const API_BASE =
|
|
|
|
|
|
|
| 13 |
|
| 14 |
function bestSpec(obs: Observation | null): string {
|
| 15 |
if (!obs) return "S0";
|
| 16 |
-
|
|
|
|
| 17 |
(a, b) => (obs.trust_snapshot[b] ?? 0.5) - (obs.trust_snapshot[a] ?? 0.5),
|
| 18 |
-
)[0];
|
| 19 |
}
|
| 20 |
|
| 21 |
function heuristicMove(obs: Observation | null) {
|
|
@@ -29,9 +32,8 @@ function heuristicMove(obs: Observation | null) {
|
|
| 29 |
|
| 30 |
function randomMove(obs: Observation | null) {
|
| 31 |
if (!obs) return { action: "delegate" as ActionType, specialist: "S0", trust: 0.5 };
|
| 32 |
-
const
|
| 33 |
-
|
| 34 |
-
] || "S0";
|
| 35 |
return { action: "delegate" as ActionType, specialist: sp, trust: obs.trust_snapshot[sp] ?? 0.5 };
|
| 36 |
}
|
| 37 |
|
|
@@ -122,7 +124,8 @@ export function useSentinel() {
|
|
| 122 |
const trustDeltas = useMemo(() => {
|
| 123 |
if (!observation) return {};
|
| 124 |
const d: Record<string, number> = {};
|
| 125 |
-
|
|
|
|
| 126 |
d[id] = (observation.trust_snapshot[id] ?? 0.5) - (prevTrust[id] ?? 0.5);
|
| 127 |
}
|
| 128 |
return d;
|
|
@@ -150,10 +153,10 @@ export function useSentinel() {
|
|
| 150 |
const s = nextSeed ?? seed;
|
| 151 |
setRunning(true);
|
| 152 |
abortRef.current = false;
|
| 153 |
-
const payload = { task_type: t, seed: s };
|
| 154 |
setLastReq({ method: "POST", path: "/reset", body: payload });
|
| 155 |
try {
|
| 156 |
-
const res = await fetch(`${
|
| 157 |
method: "POST",
|
| 158 |
headers: { "Content-Type": "application/json" },
|
| 159 |
body: JSON.stringify(payload),
|
|
@@ -195,17 +198,20 @@ export function useSentinel() {
|
|
| 195 |
|
| 196 |
setActiveSpec(specialist);
|
| 197 |
|
|
|
|
|
|
|
|
|
|
| 198 |
const payload = {
|
| 199 |
session_id: sid,
|
| 200 |
task_type: obs.task_type,
|
| 201 |
-
action_type:
|
| 202 |
specialist_id: specialist,
|
| 203 |
subtask_response: action === "solve_independently" ? "SELF_SOLVED" : null,
|
| 204 |
reasoning: `ui-${action}${specialist ? `-${specialist}` : ""}`,
|
| 205 |
};
|
| 206 |
setLastReq({ method: "POST", path: `/step?session_id=${sid}`, body: payload });
|
| 207 |
try {
|
| 208 |
-
const res = await fetch(`${
|
| 209 |
method: "POST",
|
| 210 |
headers: { "Content-Type": "application/json" },
|
| 211 |
body: JSON.stringify(payload),
|
|
@@ -276,4 +282,4 @@ export function useSentinel() {
|
|
| 276 |
prevTrust, trustDeltas, recommended, activeSpec,
|
| 277 |
resetEpisode, stepEpisode, autoRun, stopAutoRun, swapProfiles,
|
| 278 |
};
|
| 279 |
-
}
|
|
|
|
| 9 |
|
| 10 |
/* ── helpers ──────────────────────────────────────────── */
|
| 11 |
|
| 12 |
+
const API_BASE = typeof window !== "undefined"
|
| 13 |
+
? (process.env.NEXT_PUBLIC_API_URL || (window.location.port === "3000" || window.location.port === "3458" ? "http://127.0.0.1:7860" : ""))
|
| 14 |
+
: "";
|
| 15 |
|
| 16 |
function bestSpec(obs: Observation | null): string {
|
| 17 |
if (!obs) return "S0";
|
| 18 |
+
const ids = obs.available_specialists || obs.available_workers || [];
|
| 19 |
+
return [...ids].sort(
|
| 20 |
(a, b) => (obs.trust_snapshot[b] ?? 0.5) - (obs.trust_snapshot[a] ?? 0.5),
|
| 21 |
+
)[0] || "S0";
|
| 22 |
}
|
| 23 |
|
| 24 |
function heuristicMove(obs: Observation | null) {
|
|
|
|
| 32 |
|
| 33 |
function randomMove(obs: Observation | null) {
|
| 34 |
if (!obs) return { action: "delegate" as ActionType, specialist: "S0", trust: 0.5 };
|
| 35 |
+
const ids = obs.available_specialists || obs.available_workers || [];
|
| 36 |
+
const sp = ids[Math.floor(Math.random() * ids.length)] || "S0";
|
|
|
|
| 37 |
return { action: "delegate" as ActionType, specialist: sp, trust: obs.trust_snapshot[sp] ?? 0.5 };
|
| 38 |
}
|
| 39 |
|
|
|
|
| 124 |
const trustDeltas = useMemo(() => {
|
| 125 |
if (!observation) return {};
|
| 126 |
const d: Record<string, number> = {};
|
| 127 |
+
const ids = observation.available_specialists || observation.available_workers || [];
|
| 128 |
+
for (const id of ids) {
|
| 129 |
d[id] = (observation.trust_snapshot[id] ?? 0.5) - (prevTrust[id] ?? 0.5);
|
| 130 |
}
|
| 131 |
return d;
|
|
|
|
| 153 |
const s = nextSeed ?? seed;
|
| 154 |
setRunning(true);
|
| 155 |
abortRef.current = false;
|
| 156 |
+
const payload = { task_type: t, seed: s, mode: "cluster" };
|
| 157 |
setLastReq({ method: "POST", path: "/reset", body: payload });
|
| 158 |
try {
|
| 159 |
+
const res = await fetch(`${API_BASE}/reset`, {
|
| 160 |
method: "POST",
|
| 161 |
headers: { "Content-Type": "application/json" },
|
| 162 |
body: JSON.stringify(payload),
|
|
|
|
| 198 |
|
| 199 |
setActiveSpec(specialist);
|
| 200 |
|
| 201 |
+
const isCluster = active?.info?.environment_mode === "cluster" || sessionId === "cluster";
|
| 202 |
+
const mappedAction = (isCluster && action === "delegate") ? "allocate" : action;
|
| 203 |
+
|
| 204 |
const payload = {
|
| 205 |
session_id: sid,
|
| 206 |
task_type: obs.task_type,
|
| 207 |
+
action_type: mappedAction,
|
| 208 |
specialist_id: specialist,
|
| 209 |
subtask_response: action === "solve_independently" ? "SELF_SOLVED" : null,
|
| 210 |
reasoning: `ui-${action}${specialist ? `-${specialist}` : ""}`,
|
| 211 |
};
|
| 212 |
setLastReq({ method: "POST", path: `/step?session_id=${sid}`, body: payload });
|
| 213 |
try {
|
| 214 |
+
const res = await fetch(`${API_BASE}/step?session_id=${encodeURIComponent(sid)}`, {
|
| 215 |
method: "POST",
|
| 216 |
headers: { "Content-Type": "application/json" },
|
| 217 |
body: JSON.stringify(payload),
|
|
|
|
| 282 |
prevTrust, trustDeltas, recommended, activeSpec,
|
| 283 |
resetEpisode, stepEpisode, autoRun, stopAutoRun, swapProfiles,
|
| 284 |
};
|
| 285 |
+
}
|
ui/app/lib/types.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type Observation = {
|
|
| 14 |
subtasks_total: number;
|
| 15 |
subtasks_remaining: number;
|
| 16 |
available_specialists: string[];
|
|
|
|
| 17 |
trust_snapshot: Record<string, number>;
|
| 18 |
stakes_level: number;
|
| 19 |
step_count: number;
|
|
@@ -21,6 +22,7 @@ export type Observation = {
|
|
| 21 |
last_action_summary: string | null;
|
| 22 |
last_reward: number;
|
| 23 |
episode_status: string;
|
|
|
|
| 24 |
};
|
| 25 |
|
| 26 |
export type Reward = {
|
|
@@ -41,6 +43,7 @@ export type StepResult = {
|
|
| 41 |
score: number;
|
| 42 |
adversarial_detections?: number;
|
| 43 |
adversarial_poisonings?: number;
|
|
|
|
| 44 |
};
|
| 45 |
};
|
| 46 |
|
|
@@ -73,4 +76,4 @@ export type EventItem = {
|
|
| 73 |
summary: string;
|
| 74 |
reward: number;
|
| 75 |
outcome: "success" | "blocked" | "poisoned" | "skipped" | "reset";
|
| 76 |
-
};
|
|
|
|
| 14 |
subtasks_total: number;
|
| 15 |
subtasks_remaining: number;
|
| 16 |
available_specialists: string[];
|
| 17 |
+
available_workers?: string[];
|
| 18 |
trust_snapshot: Record<string, number>;
|
| 19 |
stakes_level: number;
|
| 20 |
step_count: number;
|
|
|
|
| 22 |
last_action_summary: string | null;
|
| 23 |
last_reward: number;
|
| 24 |
episode_status: string;
|
| 25 |
+
gpu_pool?: any[];
|
| 26 |
};
|
| 27 |
|
| 28 |
export type Reward = {
|
|
|
|
| 43 |
score: number;
|
| 44 |
adversarial_detections?: number;
|
| 45 |
adversarial_poisonings?: number;
|
| 46 |
+
environment_mode?: string;
|
| 47 |
};
|
| 48 |
};
|
| 49 |
|
|
|
|
| 76 |
summary: string;
|
| 77 |
reward: number;
|
| 78 |
outcome: "success" | "blocked" | "poisoned" | "skipped" | "reset";
|
| 79 |
+
};
|
ui/app/page.tsx
CHANGED
|
@@ -78,7 +78,7 @@ export default function Page() {
|
|
| 78 |
}}
|
| 79 |
disabled={s.running}
|
| 80 |
>
|
| 81 |
-
▶ Launch Simulation
|
| 82 |
</button>
|
| 83 |
<button
|
| 84 |
className="btn-secondary"
|
|
@@ -191,7 +191,7 @@ export default function Page() {
|
|
| 191 |
<div className="sim-controls-row">
|
| 192 |
<span className="ctrl-label">POLICY:</span>
|
| 193 |
<button className="btn-sm-ctrl" onClick={() => void s.autoRun("heuristic" as AutoPolicy)} disabled={s.running || s.done}>
|
| 194 |
-
▶ HEURISTIC
|
| 195 |
</button>
|
| 196 |
<button className="btn-sm-ctrl" onClick={() => void s.autoRun("random" as AutoPolicy)} disabled={s.running || s.done}>
|
| 197 |
⚄ RANDOM
|
|
@@ -256,7 +256,11 @@ export default function Page() {
|
|
| 256 |
<div className="divider" />
|
| 257 |
|
| 258 |
{/* GPU CLUSTER */}
|
| 259 |
-
<GPUClusterPanel
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
<div className="divider" />
|
| 262 |
|
|
@@ -293,9 +297,9 @@ export default function Page() {
|
|
| 293 |
</div>
|
| 294 |
<div className="footer-right">
|
| 295 |
BUILD 2.4.1 // MARL-FRAMEWORK // MIT LICENSE<br />
|
| 296 |
-
© 2025 THE_BOYS. ALL RIGHTS RESERVED.
|
| 297 |
</div>
|
| 298 |
</footer>
|
| 299 |
</>
|
| 300 |
);
|
| 301 |
-
}
|
|
|
|
| 78 |
}}
|
| 79 |
disabled={s.running}
|
| 80 |
>
|
| 81 |
+
▶️ Launch Simulation
|
| 82 |
</button>
|
| 83 |
<button
|
| 84 |
className="btn-secondary"
|
|
|
|
| 191 |
<div className="sim-controls-row">
|
| 192 |
<span className="ctrl-label">POLICY:</span>
|
| 193 |
<button className="btn-sm-ctrl" onClick={() => void s.autoRun("heuristic" as AutoPolicy)} disabled={s.running || s.done}>
|
| 194 |
+
▶️ HEURISTIC
|
| 195 |
</button>
|
| 196 |
<button className="btn-sm-ctrl" onClick={() => void s.autoRun("random" as AutoPolicy)} disabled={s.running || s.done}>
|
| 197 |
⚄ RANDOM
|
|
|
|
| 256 |
<div className="divider" />
|
| 257 |
|
| 258 |
{/* GPU CLUSTER */}
|
| 259 |
+
<GPUClusterPanel
|
| 260 |
+
sessionId={s.info?.session_id}
|
| 261 |
+
mode={s.info?.environment_mode}
|
| 262 |
+
gpuPool={s.observation?.gpu_pool}
|
| 263 |
+
/>
|
| 264 |
|
| 265 |
<div className="divider" />
|
| 266 |
|
|
|
|
| 297 |
</div>
|
| 298 |
<div className="footer-right">
|
| 299 |
BUILD 2.4.1 // MARL-FRAMEWORK // MIT LICENSE<br />
|
| 300 |
+
©️ 2025 THE_BOYS. ALL RIGHTS RESERVED.
|
| 301 |
</div>
|
| 302 |
</footer>
|
| 303 |
</>
|
| 304 |
);
|
| 305 |
+
}
|