Expose real train artifacts in dashboard

This commit is contained in:
2026-06-30 23:38:03 +08:00
parent fb96c96d8b
commit 3e9e8ba6f5
4 changed files with 70 additions and 4 deletions

View File

@@ -169,6 +169,13 @@ type AcceptancePayload = {
run_id?: string;
created_at?: string;
checks?: Array<{ name: string; passed: boolean }>;
artifacts?: {
train_root?: string;
best_weight?: string | null;
results_csv?: string | null;
predict_outputs?: string[];
heatmap_outputs?: string[];
};
model_family_readiness?: {
passed: boolean;
warnings: Array<{ name: string; passed: boolean }>;
@@ -367,6 +374,11 @@ function formatBytes(value?: number) {
return `${next.toFixed(unit > 1 ? 2 : 0)} ${units[unit]}`;
}
function fileName(path?: string | null) {
if (!path) return "";
return path.split("/").filter(Boolean).pop() ?? path;
}
function useData() {
const [catalog, setCatalog] = useState<Catalog | null>(null);
const [gpus, setGpus] = useState<GpuPayload | null>(null);
@@ -1195,6 +1207,7 @@ function App() {
<span>{acceptance?.created_at ?? "尚未运行"} {acceptance?.run_id ? `#${acceptance.run_id}` : ""}</span>
<span>{realAcceptance?.created_at ?? "尚未运行"} {realAcceptance?.run_id ? `#${realAcceptance.run_id}` : ""} {realAcceptance?.checks?.filter((item) => item.passed).length ?? 0}/{realAcceptance?.checks?.length ?? 0}</span>
<span>{realTrainAcceptance?.created_at ?? "尚未运行"} {realTrainAcceptance?.run_id ? `#${realTrainAcceptance.run_id}` : ""} {realTrainAcceptance?.checks?.filter((item) => item.passed).length ?? 0}/{realTrainAcceptance?.checks?.length ?? 0}</span>
<AcceptanceArtifactLinks artifacts={realTrainAcceptance?.artifacts} />
<span>{deepAcceptance?.created_at ?? "尚未运行"} {deepAcceptance?.run_id ? `#${deepAcceptance.run_id}` : ""} {deepAcceptance?.checks?.filter((item) => item.passed).length ?? 0}/{deepAcceptance?.checks?.length ?? 0}</span>
<span> readiness{acceptance?.model_family_readiness?.checks?.filter((item) => item.passed).length ?? 0}/{acceptance?.model_family_readiness?.checks?.length ?? 0}warnings {acceptance?.model_family_readiness?.warnings?.length ?? 0}</span>
</>
@@ -1595,6 +1608,27 @@ function ResultBrowser({
);
}
function AcceptanceArtifactLinks({ artifacts }: { artifacts?: AcceptancePayload["artifacts"] }) {
if (!artifacts) return null;
const links = [
artifacts.best_weight && { label: "best.pt", path: artifacts.best_weight, kind: "weight" },
artifacts.results_csv && { label: "loss csv", path: artifacts.results_csv, kind: "curve" },
...(artifacts.predict_outputs ?? []).slice(0, 2).map((path, index) => ({ label: `predict ${index + 1}`, path, kind: "segmentation" })),
...(artifacts.heatmap_outputs ?? []).slice(0, 4).map((path, index) => ({ label: `heatmap ${index + 1}`, path, kind: "heatmap" }))
].filter(Boolean) as Array<{ label: string; path: string; kind: string }>;
if (!links.length) return null;
return (
<div className="acceptanceArtifacts">
{links.map((item) => (
<a key={`${item.label}-${item.path}`} href={`${API_BASE}/api/artifacts/${item.path}`} target="_blank" rel="noreferrer" title={item.path}>
<span>{item.label}</span>
<small>{item.kind} · {fileName(item.path)}</small>
</a>
))}
</div>
);
}
function DatasetQuality({ validation }: { validation: DatasetValidation }) {
return (
<div className="qualityBox">

View File

@@ -681,6 +681,31 @@ textarea {
overflow-wrap: anywhere;
}
.acceptanceArtifacts {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.acceptanceArtifacts a {
min-width: 0;
display: grid;
gap: 3px;
padding: 8px;
border-radius: 6px;
border: 1px solid var(--line);
background: #101310;
color: var(--ink);
text-decoration: none;
}
.acceptanceArtifacts span,
.acceptanceArtifacts small {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.taskCheckList {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));