first commit

This commit is contained in:
eeymoo
2026-09-19 12:54:45 +08:00
commit 6fc5b64077
126 changed files with 8601 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
import { useState } from "react"
import { useApi, apiFetch } from "@/hooks/useApi"
import { Button } from "@/components/ui/button"
type ExperimentSummary = {
id: string
name: string
status: string
run_count: number
}
type RunSummary = {
id: string
run_id: string
model_id: string
level: string
sample_id: string
repeat_index: number
status: string
retry_count: number
}
type RunDetail = {
id: string
run_id: string
model_id: string
level: string
sample_id: string
status: string
raw_output: string | null
latency_ms: number | null
}
export default function MonitorPage() {
const experimentsState = useApi<ExperimentSummary[]>("/experiments")
const [selectedExperimentId, setSelectedExperimentId] = useState<string | null>(null)
const runsState = useApi<RunSummary[]>(
selectedExperimentId ? `/experiments/${selectedExperimentId}/runs` : null,
[selectedExperimentId]
)
const [selectedRunId, setSelectedRunId] = useState<string | null>(null)
const runDetailState = useApi<RunDetail>(
selectedRunId ? `/experiments/${selectedExperimentId}/runs/${selectedRunId}` : null,
[selectedRunId, selectedExperimentId]
)
async function handleResume() {
if (!selectedExperimentId) return
await apiFetch(`/experiments/${selectedExperimentId}/run`, { method: "POST" })
alert("已触发断点续跑")
}
return (
<div className="space-y-6">
<h2 className="text-2xl font-semibold">实验监控</h2>
{experimentsState.status === "success" && (
<div className="flex gap-2 flex-wrap">
{experimentsState.data.map((e) => (
<Button
key={e.id}
variant={selectedExperimentId === e.id ? "default" : "outline"}
size="sm"
onClick={() => {
setSelectedExperimentId(e.id)
setSelectedRunId(null)
}}
>
{e.name} ({e.run_count})
</Button>
))}
</div>
)}
{selectedExperimentId && (
<div className="flex justify-end">
<Button onClick={handleResume}>断点续跑</Button>
</div>
)}
{runsState.status === "success" && (
<div className="border rounded-lg overflow-hidden">
<table className="w-full text-sm">
<thead className="bg-muted">
<tr>
<th className="px-4 py-2 text-left">模型</th>
<th className="px-4 py-2 text-left">级别</th>
<th className="px-4 py-2 text-left">重复</th>
<th className="px-4 py-2 text-left">状态</th>
<th className="px-4 py-2 text-left">重试</th>
</tr>
</thead>
<tbody>
{runsState.data.map((r) => (
<tr
key={r.id}
className={`cursor-pointer hover:bg-muted/50 ${selectedRunId === r.id ? "bg-muted" : ""}`}
onClick={() => setSelectedRunId(r.id)}
>
<td className="px-4 py-2">{r.model_id}</td>
<td className="px-4 py-2">{r.level}</td>
<td className="px-4 py-2">{r.repeat_index}</td>
<td className="px-4 py-2">{r.status}</td>
<td className="px-4 py-2">{r.retry_count}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{runDetailState.status === "success" && (
<div className="border rounded-lg p-4 space-y-2">
<h3 className="font-semibold">原始输出</h3>
<p className="text-sm text-muted-foreground">
Latency: {runDetailState.data.latency_ms ?? "-"} ms
</p>
<pre className="bg-muted p-2 rounded text-xs overflow-auto max-h-96">
{runDetailState.data.raw_output ?? "无输出"}
</pre>
</div>
)}
</div>
)
}