feat: add cv benchmark workflow and admin visibility
This commit is contained in:
@@ -26,6 +26,14 @@ type AiServiceMetrics = {
|
||||
gpuName?: string | null;
|
||||
ocrAvailable?: boolean | null;
|
||||
ocrLanguages?: string | null;
|
||||
ollamaConfigured?: boolean | null;
|
||||
ollamaReachable?: boolean | null;
|
||||
ollamaModel?: string | null;
|
||||
ollamaModelAvailable?: boolean | null;
|
||||
ollamaVersion?: string | null;
|
||||
ollamaInstalledModels?: string[] | null;
|
||||
ollamaLoadedModels?: string[] | null;
|
||||
ollamaLoadedCount?: number | null;
|
||||
healthLatencyMs?: number | null;
|
||||
probeLatencyMs?: number | null;
|
||||
lastProbeAt?: string | null;
|
||||
@@ -60,6 +68,13 @@ type EditableEmailSettings = {
|
||||
hasPassword: boolean;
|
||||
};
|
||||
|
||||
type CvBenchmarkStatus = {
|
||||
indexJson?: string | null;
|
||||
reportMarkdown?: string | null;
|
||||
rootPath: string;
|
||||
lastUpdatedAtUtc?: string | null;
|
||||
};
|
||||
|
||||
type SystemStatus = {
|
||||
environment: string;
|
||||
contentRoot: string;
|
||||
@@ -141,6 +156,7 @@ export default function AdminSystemPage() {
|
||||
const [tab, setTab] = useState(0);
|
||||
const [status, setStatus] = useState<SystemStatus | null>(null);
|
||||
const [emailSettings, setEmailSettings] = useState<EditableEmailSettings | null>(null);
|
||||
const [benchmarkStatus, setBenchmarkStatus] = useState<CvBenchmarkStatus | null>(null);
|
||||
const [smtpPassword, setSmtpPassword] = useState("");
|
||||
const [clearPassword, setClearPassword] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -156,18 +172,21 @@ export default function AdminSystemPage() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const [statusRes, emailRes] = await Promise.all([
|
||||
const [statusRes, emailRes, benchmarkRes] = await Promise.all([
|
||||
api.get<SystemStatus>("/admin/system"),
|
||||
api.get<EditableEmailSettings>("/admin/system/email-settings"),
|
||||
api.get<CvBenchmarkStatus>("/admin/system/cv-benchmark").catch(() => ({ data: null } as any)),
|
||||
]);
|
||||
setStatus(statusRes.data);
|
||||
setEmailSettings(emailRes.data);
|
||||
setBenchmarkStatus(benchmarkRes.data ?? null);
|
||||
setSmtpPassword("");
|
||||
setClearPassword(false);
|
||||
} catch (e: any) {
|
||||
setError(getApiErrorMessage(e, "Failed to load system status."));
|
||||
setStatus(null);
|
||||
setEmailSettings(null);
|
||||
setBenchmarkStatus(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -367,6 +386,8 @@ export default function AdminSystemPage() {
|
||||
<DetailRow label={t("adminSystemOllamaReachable")} value={status?.ai.ollamaReachable ? t("yes") : t("noWord")} />
|
||||
<DetailRow label={t("adminSystemOllamaModel")} value={status?.ai.ollamaModel || "-"} />
|
||||
<DetailRow label={t("adminSystemOllamaModelAvailable")} value={status?.ai.ollamaModelAvailable ? t("yes") : t("noWord")} />
|
||||
<DetailRow label="Ollama version" value={status?.ai.ollamaVersion || "-"} />
|
||||
<DetailRow label="Loaded models" value={status?.ai.ollamaLoadedCount ?? 0} />
|
||||
<DetailRow label={t("adminSystemHealthLatency")} value={status?.ai.healthLatencyMs != null ? `${status.ai.healthLatencyMs} ms` : "-"} />
|
||||
<DetailRow label={t("adminSystemProbeLatency")} value={status?.ai.probeLatencyMs != null ? `${status.ai.probeLatencyMs} ms` : "-"} />
|
||||
<DetailRow label={t("adminSystemLastProbe")} value={formatDate(status?.ai.lastProbeAt)} />
|
||||
@@ -395,6 +416,25 @@ export default function AdminSystemPage() {
|
||||
<Chip label={status?.auth.gmailConfigured ? t("adminSystemGmailReady") : t("adminSystemGmailIncomplete")} variant="outlined" size="small" />
|
||||
<Chip label={status?.ai.gpuAvailable ? t("adminSystemGpuVisible") : t("adminSystemCpuMode")} color={status?.ai.gpuAvailable ? "success" : "default"} size="small" />
|
||||
<Chip label={status?.ai.ocrAvailable ? `OCR ${status.ai.ocrLanguages || "enabled"}` : t("adminSystemOcrUnavailable")} variant="outlined" size="small" />
|
||||
{(status?.ai.ollamaInstalledModels ?? []).slice(0, 4).map((model) => (
|
||||
<Chip key={model} label={`Model · ${model}`} variant="outlined" size="small" />
|
||||
))}
|
||||
{(status?.ai.ollamaLoadedModels ?? []).slice(0, 3).map((model) => (
|
||||
<Chip key={`loaded-${model}`} label={`Loaded · ${model}`} color="primary" variant="outlined" size="small" />
|
||||
))}
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Paper sx={{ p: 2, borderRadius: 3 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 900, mb: 1 }}>CV benchmark review</Typography>
|
||||
<Stack spacing={0.75}>
|
||||
<DetailRow label="Benchmark root" value={benchmarkStatus?.rootPath || "-"} />
|
||||
<DetailRow label="Last benchmark update" value={formatDate(benchmarkStatus?.lastUpdatedAtUtc)} />
|
||||
</Stack>
|
||||
<Box sx={{ mt: 1.5, p: 1.5, borderRadius: 2, backgroundColor: "background.default", border: "1px solid", borderColor: "divider", maxHeight: 260, overflow: "auto" }}>
|
||||
<Typography variant="body2" sx={{ whiteSpace: "pre-wrap", fontFamily: "ui-monospace, SFMono-Regular, monospace" }}>
|
||||
{benchmarkStatus?.reportMarkdown || "Run scripts/run-cv-benchmark.sh to generate the latest corpus report and fixture candidates."}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user