From 3ca3627ba2a7e1310fbbb6b7fe7fc1e91eb23ec1 Mon Sep 17 00:00:00 2001 From: "Kaapi-Agent [Bot]" <4937976+Kaapi-Agent [Bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:59:14 +0000 Subject: [PATCH] fix: display v2 overall score summary and AI diagnostic note on run detail page Co-Authored-By: Claude Sonnet 5 --- app/(main)/evaluations/[id]/page.tsx | 12 +++ app/components/evaluations/AiSummaryNote.tsx | 16 ++++ .../evaluations/OverallScoreCard.tsx | 75 +++++++++++++++++++ app/components/evaluations/index.ts | 2 + app/lib/types/evaluation.ts | 16 ++++ app/lib/utils/evaluation.ts | 64 ++++++++++++++++ 6 files changed, 185 insertions(+) create mode 100644 app/components/evaluations/AiSummaryNote.tsx create mode 100644 app/components/evaluations/OverallScoreCard.tsx diff --git a/app/(main)/evaluations/[id]/page.tsx b/app/(main)/evaluations/[id]/page.tsx index 535a88a1..15c5ec6f 100644 --- a/app/(main)/evaluations/[id]/page.tsx +++ b/app/(main)/evaluations/[id]/page.tsx @@ -20,6 +20,8 @@ import { hasSummaryScores, isNewScoreObjectV2, getScoreObject, + getOverallScore, + getAiSummary, normalizeToIndividualScores, isGroupedFormat, } from "@/app/lib/utils/evaluation"; @@ -29,9 +31,11 @@ import { } from "@/app/lib/utils/evaluationExport"; import Sidebar from "@/app/components/Sidebar"; import { + AiSummaryNote, CategoryMetricsTable, DetailedResultsTable, MetricsOverview, + OverallScoreCard, RunModeBadge, } from "@/app/components/evaluations"; import { @@ -266,6 +270,8 @@ export default function EvaluationReport() { scoreObject && isNewScoreObjectV2(scoreObject) ? (scoreObject.category_metrics ?? []) : []; + const overallScore = getOverallScore(scoreObject); + const aiSummary = getAiSummary(scoreObject); const isJobInProgress = job.status.toLowerCase() !== "completed" && @@ -391,6 +397,7 @@ export default function EvaluationReport() {
{hasScore && isNewFormat ? ( <> + {overallScore && } + {aiSummary && !isFormatSwitching && ( +
+ +
+ )}

Detailed Results diff --git a/app/components/evaluations/AiSummaryNote.tsx b/app/components/evaluations/AiSummaryNote.tsx new file mode 100644 index 00000000..a9489734 --- /dev/null +++ b/app/components/evaluations/AiSummaryNote.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { InfoIcon } from "@/app/components/icons"; + +interface AiSummaryNoteProps { + summary: string; +} + +export default function AiSummaryNote({ summary }: AiSummaryNoteProps) { + return ( +
+ +

{summary}

+
+ ); +} diff --git a/app/components/evaluations/OverallScoreCard.tsx b/app/components/evaluations/OverallScoreCard.tsx new file mode 100644 index 00000000..735e04c4 --- /dev/null +++ b/app/components/evaluations/OverallScoreCard.tsx @@ -0,0 +1,75 @@ +"use client"; + +import type { OverallScore } from "@/app/lib/types/evaluation"; +import { getVerdictColor } from "@/app/lib/utils/evaluation"; + +interface OverallScoreCardProps { + overall: OverallScore; +} + +function VerdictBadge({ verdict }: { verdict: string }) { + const color = getVerdictColor(verdict); + return ( + + {verdict} + + ); +} + +export default function OverallScoreCard({ overall }: OverallScoreCardProps) { + return ( +
+
+
+

+ Overall Score +

+
+ {overall.overall_score.toFixed(3)} +
+
+ +
+ + {overall.breakdown.length > 0 && ( +
+ {overall.breakdown.map((item) => ( +
+
+ + {item.name} + + +
+
+ + {item.score.toFixed(3)} + + + weight {item.weight.toFixed(2)} + +
+ {typeof item.delta === "number" && ( +
= 0 + ? "text-status-success-text" + : "text-status-error-text" + }`} + > + {item.delta >= 0 ? "▲" : "▼"}{" "} + {Math.abs(item.delta).toFixed(3)} +
+ )} +
+ ))} +
+ )} +
+ ); +} diff --git a/app/components/evaluations/index.ts b/app/components/evaluations/index.ts index 6f2fa03a..3ee40914 100644 --- a/app/components/evaluations/index.ts +++ b/app/components/evaluations/index.ts @@ -1,3 +1,4 @@ +export { default as AiSummaryNote } from "./AiSummaryNote"; export { default as CategoryMetricsTable } from "./CategoryMetricsTable"; export { default as CreateDatasetForm } from "./CreateDatasetForm"; export { default as DatasetCard } from "./DatasetCard"; @@ -10,6 +11,7 @@ export { default as EvalRunsList } from "./EvalRunsList"; export { default as EvaluationsTab } from "./EvaluationsTab"; export { default as GroupedResultsTable } from "./GroupedResultsTable"; export { default as MetricsOverview } from "./MetricsOverview"; +export { default as OverallScoreCard } from "./OverallScoreCard"; export { default as RunEvaluationForm } from "./RunEvaluationForm"; export { default as RunModeBadge } from "./RunModeBadge"; export { default as ScoreDisplay } from "./ScoreDisplay"; diff --git a/app/lib/types/evaluation.ts b/app/lib/types/evaluation.ts index 5b0eb7f2..dd6e858d 100644 --- a/app/lib/types/evaluation.ts +++ b/app/lib/types/evaluation.ts @@ -60,10 +60,26 @@ export interface SummaryScore { distribution?: Record; // For categorical data } +export interface ScoreBreakdownItem { + name: string; + score: number; + weight: number; + delta?: number | null; + verdict: string; +} + +export interface OverallScore { + overall_score: number; + verdict: string; + breakdown: ScoreBreakdownItem[]; +} + export interface NewScoreObjectV2 { summary_scores: SummaryScore[]; traces: TraceItem[] | GroupedTraceItem[]; category_metrics?: CategoryMetric[]; + overall?: OverallScore | null; + ai_summary?: string | null; } export interface PerItemScore { diff --git a/app/lib/utils/evaluation.ts b/app/lib/utils/evaluation.ts index 1403499b..20d5b96e 100644 --- a/app/lib/utils/evaluation.ts +++ b/app/lib/utils/evaluation.ts @@ -4,6 +4,7 @@ import type { IndividualScore, NewScoreObjectV2, BasicScoreObject, + OverallScore, ScoreObject, TraceItem, } from "@/app/lib/types/evaluation"; @@ -33,6 +34,69 @@ export function isGroupedFormat( return "llm_answers" in traces[0] && Array.isArray(traces[0].llm_answers); } +export function getOverallScore( + score: ScoreObject | null | undefined, +): OverallScore | null { + if (!score || !isNewScoreObjectV2(score)) return null; + return score.overall ?? null; +} + +export function getAiSummary( + score: ScoreObject | null | undefined, +): string | null { + if (!score || !isNewScoreObjectV2(score)) return null; + return score.ai_summary ?? null; +} + +type VerdictTone = "success" | "warning" | "error" | "default"; + +const VERDICT_TONE_MAP: Record = { + pass: "success", + passed: "success", + excellent: "success", + good: "success", + warning: "warning", + moderate: "warning", + fail: "error", + failed: "error", + poor: "error", + critical: "error", +}; + +export function getVerdictColor(verdict: string): { + bg: string; + border: string; + text: string; +} { + const tone = VERDICT_TONE_MAP[verdict.toLowerCase()] ?? "default"; + switch (tone) { + case "success": + return { + bg: "bg-status-success-bg", + border: "border-status-success-border", + text: "text-status-success-text", + }; + case "warning": + return { + bg: "bg-status-warning-bg", + border: "border-status-warning-border", + text: "text-status-warning-text", + }; + case "error": + return { + bg: "bg-status-error-bg", + border: "border-status-error-border", + text: "text-status-error-text", + }; + default: + return { + bg: "bg-status-default-bg", + border: "border-status-default-border", + text: "text-status-default-text", + }; + } +} + export function normalizeToIndividualScores( score: ScoreObject | null | undefined, ): IndividualScore[] {