feat(admin): AI quality preview showing per-(severity x provider) helpful-rate

v1.4.16 phase B5e: ship the `/admin/ai-quality` route that surfaces
the daily aggregator's helpful-rate per (severity x metricSourceType
x provider x prompt-version) bucket. Renders as a compact 7-column
table with severity tinted by clinical-importance colour and the
helpful-rate column tinted green/yellow/orange by 0.8/0.5
thresholds.

New endpoint `GET /api/admin/ai-quality` (requireAdmin-gated) returns
the raw `AppSettings.adminAiInsightsFeedbackSummary` blob so the
v1.4.17 ratchet that consumes the same shape for prompt-tuning can
read what the worker writes without an envelope-reshape step.

Empty state ("no summary yet" / "buckets[] is empty") rendered as a
quiet message rather than a fake zero row so the operator can tell
"no feedback" apart from "all-zeros". Loading + error banners follow
the existing host-metrics pattern.

Section wiring:
  - new `ai-quality` slug in ADMIN_SECTION_SLUGS (between
    integrations and feedback so the IA flows General → Integrations
    → AI Quality → Feedback Inbox)
  - new Sparkles icon entry in admin-shell
  - new switch case in [section]/renderer.tsx
  - EN+DE i18n keys: admin.section.ai-quality.{title,subtitle} +
    admin.aiQuality.{title,loading,loadError,empty,windowLabel,
    generatedAtLabel,col*}

Tests:
  - 5 new ai-quality-section tests (loading, empty-null, empty-buckets,
    table-render with multiple buckets, error)
  - 1 admin-sections smoke test ensures the empty branch renders +
    1 slug-list parity test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-10 01:44:47 +02:00
1 parent 5117266756
commit 887928211b
9 files changed
+437

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
import { apiHandler, requireAdmin } from "@/lib/api-handler";
import { apiSuccess } from "@/lib/api-response";
import { annotate } from "@/lib/logging/context";
import { prisma } from "@/lib/db";
/**
* v1.4.16 phase B5e — admin AI quality preview.
*
* Returns the latest aggregator-written summary from
* `AppSettings.adminAiInsightsFeedbackSummary`. The pg-boss
* `feedback-aggregator` queue writes this row daily at 04:00
* Europe/Berlin. requireAdmin() gates the endpoint — cross-user
* helpful-rate slices are admin-only telemetry per research §3.
*
* The response payload is intentionally the raw summary blob (no
* envelope reshaping) so the v1.4.17 ratchet that consumes it for
* prompt-tuning can read the same shape the worker writes.
*
* Empty state (`null` summary) means the aggregator hasn't run yet
* OR there's been no feedback in the rolling window. The admin UI
* surfaces a "no data yet" empty state rather than a fake zero row
* so the operator can tell "no feedback" apart from "all-zeros".
*/
export const GET = apiHandler(async () => {
await requireAdmin();
const settings = await prisma.appSettings.findUnique({
where: { id: "singleton" },
select: { adminAiInsightsFeedbackSummary: true },
});
const summary = settings?.adminAiInsightsFeedbackSummary ?? null;
annotate({
action: { name: "admin.ai_quality.read" },
meta: {
has_summary: summary !== null,
},
});
return apiSuccess({ summary });
});