Add full capability readiness matrix
This commit is contained in:
@@ -193,6 +193,44 @@ type RuntimeReadinessPayload = {
|
||||
};
|
||||
};
|
||||
|
||||
type CapabilityDomain = {
|
||||
id: string;
|
||||
label: string;
|
||||
ready: boolean;
|
||||
tasks: {
|
||||
total: number;
|
||||
required: number;
|
||||
required_ready: number;
|
||||
examples: string[];
|
||||
missing_required: string[];
|
||||
unbuildable_required: string[];
|
||||
};
|
||||
runtime: Array<{ role: string; name: string; passed: boolean }>;
|
||||
evidence: {
|
||||
count: number;
|
||||
artifacts: Array<{ name: string; relative_path: string; role?: string; family?: string }>;
|
||||
curves: Array<{ name: string; relative_path: string; family?: string; row_count?: number }>;
|
||||
};
|
||||
gaps: string[];
|
||||
};
|
||||
|
||||
type CapabilityPayload = {
|
||||
passed: boolean;
|
||||
generated_at: string;
|
||||
summary: {
|
||||
ready_domains: number;
|
||||
total_domains: number;
|
||||
mapped_user_scripts: number;
|
||||
user_scripts_total: number;
|
||||
uploaded_datasets: number;
|
||||
artifacts: number;
|
||||
curves: number;
|
||||
weights: number;
|
||||
};
|
||||
requirements: Array<{ id: string; label: string; passed: boolean; detail: string }>;
|
||||
domains: CapabilityDomain[];
|
||||
};
|
||||
|
||||
async function api<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -266,14 +304,16 @@ function useData() {
|
||||
const [acceptance, setAcceptance] = useState<AcceptancePayload | null>(null);
|
||||
const [deepAcceptance, setDeepAcceptance] = useState<DeepAcceptancePayload | null>(null);
|
||||
const [runtimeReadiness, setRuntimeReadiness] = useState<RuntimeReadinessPayload | null>(null);
|
||||
const [capabilities, setCapabilities] = useState<CapabilityPayload | null>(null);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
const [catalogNext, gpusNext, readinessNext, jobsNext, resultsNext, curvesNext, datasetsNext, coverageNext, acceptanceNext, deepAcceptanceNext] = await Promise.all([
|
||||
const [catalogNext, gpusNext, readinessNext, capabilitiesNext, 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<CapabilityPayload>("/api/capabilities"),
|
||||
api<Job[]>("/api/jobs"),
|
||||
api<ResultItem[]>("/api/results"),
|
||||
api<TrainingCurve[]>("/api/results/curves"),
|
||||
@@ -285,6 +325,7 @@ function useData() {
|
||||
setCatalog(catalogNext);
|
||||
setGpus(gpusNext);
|
||||
setRuntimeReadiness(readinessNext);
|
||||
setCapabilities(capabilitiesNext);
|
||||
setJobs(jobsNext);
|
||||
setResults(resultsNext.slice(0, 80));
|
||||
setCurves(curvesNext.slice(0, 12));
|
||||
@@ -316,7 +357,7 @@ function useData() {
|
||||
return () => window.clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
return { catalog, gpus, runtimeReadiness, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh };
|
||||
return { catalog, gpus, runtimeReadiness, capabilities, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh };
|
||||
}
|
||||
|
||||
function StatusPill({ status }: { status: string }) {
|
||||
@@ -339,7 +380,7 @@ function JobProgressBar({ progress }: { progress?: JobProgress }) {
|
||||
}
|
||||
|
||||
function App() {
|
||||
const { catalog, gpus, runtimeReadiness, jobs, results, curves, datasets, datasetValidations, coverage, acceptance, deepAcceptance, error, refresh } = useData();
|
||||
const { catalog, gpus, runtimeReadiness, capabilities, 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);
|
||||
@@ -529,6 +570,7 @@ function App() {
|
||||
</div>
|
||||
<nav>
|
||||
<a href="#jobs"><Terminal size={18} />任务</a>
|
||||
<a href="#capabilities"><Gauge 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>
|
||||
@@ -579,6 +621,48 @@ function App() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="panel" id="capabilities">
|
||||
<div className="panelHead">
|
||||
<div>
|
||||
<p className="eyebrow">Capability Matrix</p>
|
||||
<h2>全功能矩阵</h2>
|
||||
</div>
|
||||
<StatusPill status={capabilities?.passed ? "success" : "queued"} />
|
||||
</div>
|
||||
<div className="capSummary">
|
||||
<div><span>域</span><strong>{capabilities?.summary.ready_domains ?? 0}/{capabilities?.summary.total_domains ?? 0}</strong></div>
|
||||
<div><span>脚本</span><strong>{capabilities?.summary.mapped_user_scripts ?? 0}/{capabilities?.summary.user_scripts_total ?? 0}</strong></div>
|
||||
<div><span>数据集</span><strong>{capabilities?.summary.uploaded_datasets ?? 0}</strong></div>
|
||||
<div><span>结果</span><strong>{capabilities?.summary.artifacts ?? 0}</strong></div>
|
||||
<div><span>曲线</span><strong>{capabilities?.summary.curves ?? 0}</strong></div>
|
||||
<div><span>权重</span><strong>{capabilities?.summary.weights ?? 0}</strong></div>
|
||||
</div>
|
||||
<div className="capabilityGrid">
|
||||
{(capabilities?.domains ?? []).map((domain) => (
|
||||
<div key={domain.id} className={`capCard ${domain.ready ? "ok" : "bad"}`}>
|
||||
<div className="capHead">
|
||||
<strong>{domain.label}</strong>
|
||||
<span>{domain.ready ? "READY" : "CHECK"}</span>
|
||||
</div>
|
||||
<div className="capNumbers">
|
||||
<span>{domain.tasks.required_ready}/{domain.tasks.required} required</span>
|
||||
<span>{domain.evidence.count} evidence</span>
|
||||
</div>
|
||||
<div className="capTags">
|
||||
{domain.tasks.examples.slice(0, 4).map((task) => <small key={task}>{task}</small>)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="requirementStrip">
|
||||
{(capabilities?.requirements ?? []).map((item) => (
|
||||
<span key={item.id} className={item.passed ? "ok" : "bad"} title={item.detail}>
|
||||
{item.label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid two" id="jobs">
|
||||
<div className="panel taskPanel">
|
||||
<div className="panelHead">
|
||||
|
||||
@@ -186,6 +186,131 @@ h2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.capSummary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.capSummary div {
|
||||
min-width: 0;
|
||||
padding: 10px;
|
||||
border-radius: 7px;
|
||||
border: 1px solid var(--line);
|
||||
background: #101310;
|
||||
}
|
||||
|
||||
.capSummary span,
|
||||
.capSummary strong {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.capSummary span {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.capSummary strong {
|
||||
margin-top: 3px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.capabilityGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.capCard {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
border-radius: 7px;
|
||||
border: 1px solid var(--line);
|
||||
background: #101310;
|
||||
}
|
||||
|
||||
.capCard.ok {
|
||||
border-color: rgba(157, 226, 111, 0.32);
|
||||
}
|
||||
|
||||
.capCard.bad {
|
||||
border-color: rgba(240, 113, 103, 0.55);
|
||||
}
|
||||
|
||||
.capHead {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.capHead strong {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.capHead span {
|
||||
color: var(--green);
|
||||
font-size: 11px;
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
.capCard.bad .capHead span {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
.capNumbers {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.capTags {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.capTags small {
|
||||
min-width: 0;
|
||||
padding: 4px 5px;
|
||||
border-radius: 4px;
|
||||
background: #080a08;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.requirementStrip {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.requirementStrip span {
|
||||
padding: 5px 7px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
color: var(--muted);
|
||||
background: #101310;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.requirementStrip span.ok {
|
||||
color: var(--green);
|
||||
border-color: rgba(157, 226, 111, 0.32);
|
||||
}
|
||||
|
||||
.requirementStrip span.bad {
|
||||
color: var(--red);
|
||||
border-color: rgba(240, 113, 103, 0.55);
|
||||
}
|
||||
|
||||
.metric, .panel {
|
||||
border: 1px solid var(--line);
|
||||
background: rgba(21, 24, 21, 0.94);
|
||||
@@ -958,7 +1083,7 @@ meter {
|
||||
}
|
||||
|
||||
nav {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -985,8 +1110,11 @@ meter {
|
||||
}
|
||||
|
||||
.metrics,
|
||||
.capSummary,
|
||||
.capabilityGrid,
|
||||
.grid.two,
|
||||
.grid.three,
|
||||
.grid.four,
|
||||
.taskColumns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user