Add runtime environment readiness checks
This commit is contained in:
@@ -157,6 +157,42 @@ type GpuPayload = {
|
||||
}>;
|
||||
};
|
||||
|
||||
type RuntimeCheck = {
|
||||
module: string;
|
||||
package?: string;
|
||||
passed: boolean;
|
||||
version?: string | null;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
type RuntimeEnv = {
|
||||
role: string;
|
||||
name: string;
|
||||
label: string;
|
||||
env_file: string;
|
||||
path?: string;
|
||||
exists: boolean;
|
||||
passed: boolean;
|
||||
checks: RuntimeCheck[];
|
||||
extra: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type RuntimeReadinessPayload = {
|
||||
available: boolean;
|
||||
passed: boolean;
|
||||
generated_at: string;
|
||||
cached: boolean;
|
||||
cache_seconds: number;
|
||||
envs: RuntimeEnv[];
|
||||
specs: {
|
||||
bootstrap_script: string;
|
||||
verify_script: string;
|
||||
env_files: string[];
|
||||
task_default: string;
|
||||
mmseg_default: string;
|
||||
};
|
||||
};
|
||||
|
||||
async function api<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -229,13 +265,15 @@ function useData() {
|
||||
const [coverage, setCoverage] = useState<CoveragePayload | null>(null);
|
||||
const [acceptance, setAcceptance] = useState<AcceptancePayload | null>(null);
|
||||
const [deepAcceptance, setDeepAcceptance] = useState<DeepAcceptancePayload | null>(null);
|
||||
const [runtimeReadiness, setRuntimeReadiness] = useState<RuntimeReadinessPayload | null>(null);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
const [catalogNext, gpusNext, jobsNext, resultsNext, curvesNext, datasetsNext, coverageNext, acceptanceNext, deepAcceptanceNext] = await Promise.all([
|
||||
const [catalogNext, gpusNext, readinessNext, jobsNext, resultsNext, curvesNext, datasetsNext, coverageNext, acceptanceNext, deepAcceptanceNext] = await Promise.all([
|
||||
api<Catalog>("/api/catalog"),
|
||||
api<GpuPayload>("/api/system/gpus"),
|
||||
api<RuntimeReadinessPayload>("/api/system/readiness"),
|
||||
api<Job[]>("/api/jobs"),
|
||||
api<ResultItem[]>("/api/results"),
|
||||
api<TrainingCurve[]>("/api/results/curves"),
|
||||
@@ -246,6 +284,7 @@ function useData() {
|
||||
]);
|
||||
setCatalog(catalogNext);
|
||||
setGpus(gpusNext);
|
||||
setRuntimeReadiness(readinessNext);
|
||||
setJobs(jobsNext);
|
||||
setResults(resultsNext.slice(0, 80));
|
||||
setCurves(curvesNext.slice(0, 12));
|
||||
@@ -277,7 +316,7 @@ function useData() {
|
||||
return () => window.clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
return { catalog, gpus, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh };
|
||||
return { catalog, gpus, runtimeReadiness, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh };
|
||||
}
|
||||
|
||||
function StatusPill({ status }: { status: string }) {
|
||||
@@ -300,7 +339,7 @@ function JobProgressBar({ progress }: { progress?: JobProgress }) {
|
||||
}
|
||||
|
||||
function App() {
|
||||
const { catalog, gpus, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh } = useData();
|
||||
const { catalog, gpus, runtimeReadiness, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh } = useData();
|
||||
const [taskType, setTaskType] = useState("mock.echo");
|
||||
const [params, setParams] = useState(JSON.stringify(defaultParams["mock.echo"], null, 2));
|
||||
const [selectedJob, setSelectedJob] = useState<Job | null>(null);
|
||||
@@ -492,6 +531,7 @@ function App() {
|
||||
<a href="#jobs"><Terminal size={18} />任务</a>
|
||||
<a href="#datasets"><Boxes size={18} />数据集</a>
|
||||
<a href="#gpu"><Cpu size={18} />GPU</a>
|
||||
<a href="#runtime"><ShieldCheck size={18} />环境</a>
|
||||
<a href="#coverage"><ClipboardCheck size={18} />覆盖</a>
|
||||
<a href="#weights"><HardDrive size={18} />权重</a>
|
||||
<a href="#results"><BarChart3 size={18} />结果</a>
|
||||
@@ -768,7 +808,7 @@ function App() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid three">
|
||||
<section className="grid four">
|
||||
<div className="panel" id="gpu">
|
||||
<div className="panelHead">
|
||||
<div>
|
||||
@@ -789,6 +829,37 @@ function App() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="panel" id="runtime">
|
||||
<div className="panelHead">
|
||||
<div>
|
||||
<p className="eyebrow">Runtime</p>
|
||||
<h2>环境就绪</h2>
|
||||
</div>
|
||||
<ShieldCheck size={22} />
|
||||
</div>
|
||||
<div className="envList">
|
||||
{(runtimeReadiness?.envs ?? []).map((env) => (
|
||||
<div key={env.role} className={`envCard ${env.passed ? "ok" : "bad"}`}>
|
||||
<div className="envHead">
|
||||
<div>
|
||||
<strong>{env.name}</strong>
|
||||
<small>{env.label}</small>
|
||||
</div>
|
||||
<span>{env.passed ? "READY" : env.exists ? "CHECK" : "MISSING"}</span>
|
||||
</div>
|
||||
<div className="envChecks">
|
||||
{env.checks.slice(0, 8).map((check) => (
|
||||
<span key={check.module} className={check.passed ? "ok" : "bad"} title={check.error ?? check.package}>
|
||||
{check.module}{check.version ? ` ${check.version}` : ""}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="muted">{runtimeReadiness?.passed ? "runtime imports ready" : "run scripts/bootstrap_conda_envs.sh"} · {runtimeReadiness?.generated_at ?? "not checked"}</p>
|
||||
</div>
|
||||
|
||||
<div className="panel" id="weights">
|
||||
<div className="panelHead">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user