Add Command Center v2: DQ/RAG integration, S3 browser, Jupyter, GPU matrix.

Mirror mo/atc-GPU layout with config/, docs/, scripts/ for Gitea deploy.
This commit is contained in:
mo
2026-06-25 00:28:23 +00:00
parent fb9cc21c9a
commit a11621b21f
110 changed files with 14622 additions and 529 deletions
+10
View File
@@ -0,0 +1,10 @@
import { useEffect, useState } from 'react'
export function useClock() {
const [now, setNow] = useState(new Date())
useEffect(() => {
const t = setInterval(() => setNow(new Date()), 1000)
return () => clearInterval(t)
}, [])
return now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
}
+331
View File
@@ -0,0 +1,331 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import {
askNode as apiAskNode,
decideApproval,
fetchAgents,
fetchApprovals,
fetchFeed,
fetchGpu,
fetchNodeDetail,
fetchStatus,
fetchTerminals,
fetchWorkload,
probeNode as apiProbeNode,
sendPrompt as apiSendPrompt,
} from '../lib/api'
import { AGENT_NODE, NODE_ALIASES, wsUrl } from '../lib/constants'
import { resolveInfraNode } from '../lib/infraCatalog'
import type {
Agent,
AgentAnim,
Approval,
ChatMessage,
FeedEntry,
GpuStatus,
NodeDetail,
StatusData,
TerminalLine,
TopologyNode,
WorkloadData,
} from '../types'
function resolveProbeId(nodeId: string) {
const aliased = NODE_ALIASES[nodeId] || nodeId
const infra = resolveInfraNode(nodeId) || resolveInfraNode(aliased)
return infra?.id || aliased
}
export function useCommandCenter() {
const [agents, setAgents] = useState<Agent[]>([])
const [agentsLoading, setAgentsLoading] = useState(true)
const [status, setStatus] = useState<StatusData | null>(null)
const [workload, setWorkload] = useState<WorkloadData | null>(null)
const [gpu, setGpu] = useState<GpuStatus | null>(null)
const [feed, setFeed] = useState<FeedEntry[]>([])
const [approvals, setApprovals] = useState<Approval[]>([])
const [chat, setChat] = useState<ChatMessage[]>([])
const [anims, setAnims] = useState<Record<string, AgentAnim>>({})
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null)
const [promptBusy, setPromptBusy] = useState(false)
const [terminals, setTerminals] = useState<Record<string, TerminalLine[]>>({})
const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null)
const [selectedNode, setSelectedNode] = useState<TopologyNode | null>(null)
const [nodeDetail, setNodeDetail] = useState<NodeDetail | null>(null)
const [nodeBusy, setNodeBusy] = useState(false)
const [mainView, setMainView] = useState<'platform' | 'approvals' | 'presentation' | 'dataquality' | 'knowledge' | 'storage'>('platform')
const [approvalHighlight, setApprovalHighlight] = useState(false)
const [chatExpanded, setChatExpanded] = useState(false)
const promptTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const [terminalExpanded, setTerminalExpanded] = useState(true)
const appendTerminal = useCallback((line: TerminalLine) => {
setTerminals((prev) => {
const cur = prev[line.agent_id] || []
return { ...prev, [line.agent_id]: [...cur, line].slice(-300) }
})
}, [])
const selectedAgent = useMemo(
() => agents.find((a) => a.id === selectedAgentId) || null,
[agents, selectedAgentId],
)
const reloadFast = useCallback(async () => {
const [a, s, f, ap, g, t] = await Promise.all([
fetchAgents(),
fetchStatus(),
fetchFeed(),
fetchApprovals(),
fetchGpu(),
fetchTerminals(),
])
setAgents(a)
setAgentsLoading(false)
setStatus(s)
setGpu(g || s?.gpu || null)
setFeed(f)
setApprovals(ap)
setTerminals(t)
}, [])
const reloadWorkload = useCallback(async () => {
const w = await fetchWorkload()
if (w?.zones) setWorkload(w)
}, [])
const reload = useCallback(async () => {
await reloadFast()
reloadWorkload()
}, [reloadFast, reloadWorkload])
useEffect(() => {
reloadFast().then(() => reloadWorkload())
const ws = new WebSocket(wsUrl())
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data)
if (msg.type === 'status') {
setStatus(msg.data)
if (msg.data.gpu) setGpu(msg.data.gpu)
}
if (msg.type === 'workload') setWorkload(msg.data)
if (msg.type === 'terminal') appendTerminal(msg.line)
if (msg.type === 'terminal_history' && msg.terminals) setTerminals(msg.terminals)
if (msg.type === 'feed') setFeed((prev) => [msg.entry, ...prev].slice(0, 100))
if (msg.type === 'agent_dispatch') {
setSelectedAgentId(msg.agent_id)
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'walk', zone: msg.zone } }))
}
if (msg.type === 'agent_fetch') {
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'fetch', zone: msg.zone } }))
}
if (msg.type === 'agent_return') {
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'return', zone: msg.zone } }))
setTimeout(() => {
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'idle' } }))
}, 1200)
}
if (msg.type === 'prompt_result') {
setChat((c) => [...c, { role: 'agent', text: msg.answer, agent: msg.agent_id, ts: new Date().toISOString() }])
setPromptBusy(false)
reloadFast()
}
if (msg.type === 'approval_new') {
setApprovals((prev) => {
if (prev.some((a) => a.id === msg.approval?.id)) return prev
return [msg.approval, ...prev]
})
if (msg.approval?.status === 'pending') setApprovalHighlight(true)
}
if (msg.type === 'approval_update' && msg.approval) {
setApprovals((prev) => prev.filter((a) => a.id !== msg.approval.id))
}
if (msg.type === 'node_ask_result') {
setNodeBusy(false)
if (msg.agent_id) setSelectedAgentId(msg.agent_id)
}
}
const iv = setInterval(reloadFast, 15000)
const wv = setInterval(reloadWorkload, 45000)
return () => { ws.close(); clearInterval(iv); clearInterval(wv) }
}, [reloadFast, reloadWorkload, appendTerminal])
const findNodeStub = useCallback((nodeId: string): TopologyNode | null => {
const resolved = NODE_ALIASES[nodeId] || nodeId
const allNodes = [
...(workload?.topology?.nodes || []),
...Object.values(workload?.topologies || {}).flatMap((v) => v.nodes),
]
const wn = allNodes.find((n) => n.id === nodeId) || allNodes.find((n) => n.id === resolved)
if (wn) return wn
const infra = resolveInfraNode(nodeId) || resolveInfraNode(resolved)
if (infra) {
return {
id: infra.id,
label: infra.label,
vm: infra.vm,
ip: infra.ip,
x: 50,
y: 50,
color: infra.accent,
level: 'ok',
role: infra.description,
apps: infra.apps.map((a) => ({ name: a.label, state: 'link', image: a.url, ports: a.port ? [a.port] : [] })),
running: 1,
total: 1,
}
}
const agent = agents.find((a) => a.id === nodeId)
if (agent) {
return {
id: agent.id, label: agent.name, vm: 'agent', ip: '10.0.21.33',
x: 50, y: 50, color: agent.color, level: 'ok', role: agent.role, apps: [], running: 1, total: 1,
agent_id: agent.id,
}
}
const zone = workload?.zones.find((z) => z.id === nodeId || z.id === resolved)
if (zone) {
return {
id: nodeId, label: zone.label, vm: zone.vm || zone.id, ip: zone.ip || '',
x: 50, y: 50, color: zone.color, level: zone.level, role: 'zone', apps: zone.apps,
running: zone.running, total: zone.total,
}
}
return null
}, [workload, agents])
const probeNodeId = useCallback((nodeId: string) => {
setNodeBusy(true)
setTerminalExpanded(true)
apiProbeNode(resolveProbeId(nodeId)).finally(() => setNodeBusy(false))
}, [])
const openTerminal = useCallback((nodeId: string) => {
setTerminalExpanded(true)
setSelectedNodeId(nodeId)
}, [])
const selectNode = useCallback(async (nodeId: string) => {
const stub = findNodeStub(nodeId)
if (!stub) return
const probeId = resolveProbeId(nodeId)
setSelectedNodeId(probeId)
setSelectedNode({ ...stub, id: probeId })
setNodeDetail(null)
setTerminalExpanded(true)
const infra = resolveInfraNode(nodeId)
const linked = agents.find(
(a) => a.id === nodeId || AGENT_NODE[a.id] === probeId || a.id === infra?.agentId,
)
if (linked) setSelectedAgentId(linked.id)
try {
const detail = await fetchNodeDetail(probeId)
if (!detail.error) setNodeDetail(detail as NodeDetail)
} catch { /* ok */ }
probeNodeId(nodeId)
}, [findNodeStub, agents, probeNodeId])
const selectAgent = useCallback((id: string) => {
setSelectedAgentId(id)
setTerminalExpanded(true)
const agent = agents.find((a) => a.id === id)
if (!agent) return
const nodeId = agent.supervisor ? id : (AGENT_NODE[id] || agent.zone)
if (nodeId) {
const stub = findNodeStub(nodeId)
if (stub) {
selectNode(nodeId)
return
}
}
setSelectedNodeId(null)
setSelectedNode(null)
setNodeDetail(null)
}, [agents, findNodeStub, selectNode])
const clearSelection = useCallback(() => {
setSelectedNodeId(null)
setSelectedNode(null)
setNodeDetail(null)
setSelectedAgentId(null)
}, [])
const probeNode = useCallback(() => {
if (selectedNodeId) probeNodeId(selectedNodeId)
}, [selectedNodeId, probeNodeId])
const askNode = useCallback(async (message: string) => {
if (!selectedNodeId) return
setNodeBusy(true)
setTerminalExpanded(true)
await apiAskNode(resolveProbeId(selectedNodeId), message)
}, [selectedNodeId])
const sendPrompt = useCallback(async (message: string, agentId?: string) => {
setPromptBusy(true)
setChatExpanded(true)
setChat((c) => [...c, { role: 'user', text: message, ts: new Date().toISOString() }])
if (agentId) setSelectedAgentId(agentId)
await apiSendPrompt(message, agentId)
}, [])
const decide = useCallback(async (id: string, approved: boolean, decidedBy = 'mo-commander', note = '') => {
setApprovals((prev) => prev.filter((a) => a.id !== id))
await decideApproval(id, approved, decidedBy, note)
reloadFast()
}, [reloadFast])
const terminalSubjectId = selectedNodeId || selectedAgentId
const inspectorLines = useMemo(() => {
if (!terminalSubjectId) return []
const probeId = resolveProbeId(terminalSubjectId)
return terminals[probeId] || terminals[terminalSubjectId] || terminals[selectedAgentId || ''] || []
}, [terminalSubjectId, terminals, selectedAgentId])
const focusApprovals = useCallback(() => {
setApprovalHighlight(true)
setMainView('approvals')
}, [])
return {
agents,
agentsLoading,
status,
workload,
gpu,
feed,
approvals,
chat,
anims,
selectedAgentId,
selectedAgent,
promptBusy,
selectedNodeId,
selectedNode,
nodeDetail,
nodeBusy,
mainView,
setMainView,
approvalHighlight,
setApprovalHighlight,
inspectorLines,
terminalSubjectId,
terminalExpanded,
setTerminalExpanded,
selectNode,
selectAgent,
clearSelection,
probeNode,
probeNodeId,
openTerminal,
askNode,
sendPrompt,
decide,
focusApprovals,
reload,
chatExpanded,
setChatExpanded,
}
}
+120
View File
@@ -0,0 +1,120 @@
import { useEffect, useRef, useState } from 'react'
import type { Agent, AgentAnim, GpuStatus } from '../types'
import { pseudoAgentLoad } from '../lib/agentMeta'
export type AgentLoad = { cpu: number; mem: number }
export type GpuLiveMetrics = {
tokenThroughput: number
avgUtil: number
avgVram: number
deviceUtils: number[]
}
function clamp(n: number, min: number, max: number) {
return Math.min(max, Math.max(min, n))
}
function memPct(used: number, total: number) {
if (!total) return 0
return Math.round((used / total) * 100)
}
export function useLiveMetrics(
agents: Agent[],
gpu: GpuStatus | null,
animations: Record<string, AgentAnim>,
boost = false,
) {
const [agentLoads, setAgentLoads] = useState<Record<string, AgentLoad>>({})
const [gpuLive, setGpuLive] = useState<GpuLiveMetrics>({
tokenThroughput: 0,
avgUtil: 0,
avgVram: 0,
deviceUtils: [],
})
const loadsRef = useRef(agentLoads)
loadsRef.current = agentLoads
useEffect(() => {
const seed: Record<string, AgentLoad> = {}
for (const a of agents) {
seed[a.id] = pseudoAgentLoad(a.stats)
}
setAgentLoads(seed)
const gpus = gpu?.gpus || []
const baseUtil = gpus.length ? gpus.reduce((s, g) => s + g.util_gpu, 0) / gpus.length : 0
const baseVram = gpus.length
? gpus.reduce((s, g) => s + memPct(g.memory_used_mib, g.memory_total_mib), 0) / gpus.length
: 0
const inferenceOn = gpu?.ok && gpu.inference_active
setGpuLive({
tokenThroughput: inferenceOn ? Math.round(baseUtil * 42 + 120) : 0,
avgUtil: baseUtil,
avgVram: baseVram,
deviceUtils: gpus.map((g) => g.util_gpu),
})
}, [agents, gpu])
useEffect(() => {
const tick = () => {
setAgentLoads((prev) => {
const next: Record<string, AgentLoad> = {}
for (const a of agents) {
const busy = (animations[a.id]?.state || 'idle') !== 'idle'
const base = pseudoAgentLoad(a.stats)
const cur = prev[a.id] || base
const drift = (Math.random() - 0.5) * (busy ? 7 : 2.5)
const driftMem = (Math.random() - 0.5) * (busy ? 5 : 2)
const targetCpu = busy ? Math.max(base.cpu, cur.cpu) : base.cpu
const targetMem = busy ? Math.max(base.mem, cur.mem) : base.mem
next[a.id] = {
cpu: clamp(Math.round(cur.cpu + drift + (busy ? 1.2 : -0.3)), 4, 96),
mem: clamp(Math.round(cur.mem + driftMem + (busy ? 0.8 : -0.2)), 6, 92),
}
if (!busy) {
next[a.id].cpu = clamp(Math.round(next[a.id].cpu * 0.85 + targetCpu * 0.15), 4, 96)
next[a.id].mem = clamp(Math.round(next[a.id].mem * 0.85 + targetMem * 0.15), 6, 92)
}
}
return next
})
if (gpu?.ok) {
const gpus = gpu.gpus || []
const inferenceOn = gpu.inference_active
setGpuLive((prev) => {
const baseUtil = gpus.length
? gpus.reduce((s, g) => s + g.util_gpu, 0) / gpus.length
: prev.avgUtil
const baseVram = gpus.length
? gpus.reduce((s, g) => s + memPct(g.memory_used_mib, g.memory_total_mib), 0) / gpus.length
: prev.avgVram
const jitterScale = boost ? 12 : 6
const utilJitter = (Math.random() - 0.5) * (inferenceOn ? jitterScale : 2)
const avgUtil = clamp(baseUtil + utilJitter, 0, 100)
const avgVram = clamp(baseVram + (Math.random() - 0.5) * 3, 0, 100)
const deviceUtils = gpus.map((g, i) => {
const real = g.util_gpu
if (boost && inferenceOn) {
return clamp(real + (Math.random() - 0.5) * 8, 0, 100)
}
return clamp((prev.deviceUtils[i] ?? real) + (Math.random() - 0.5) * 5, 0, 100)
})
const tokenBase = boost ? Math.max(180, baseUtil * 55 + 140) : baseUtil * 42 + 120
const tokenThroughput = inferenceOn
? clamp(Math.round(prev.tokenThroughput * 0.4 + tokenBase * 0.6 + (Math.random() - 0.5) * (boost ? 45 : 28)), boost ? 120 : 80, boost ? 520 : 420)
: 0
return { tokenThroughput, avgUtil, avgVram, deviceUtils }
})
}
}
tick()
const id = setInterval(tick, boost ? 1000 : 5000)
return () => clearInterval(id)
}, [agents, animations, gpu, boost])
return { agentLoads, gpuLive }
}