Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/(main)/evaluations/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
hasSummaryScores,
isNewScoreObjectV2,
getScoreObject,
getOverallScore,
getAiSummary,
normalizeToIndividualScores,
isGroupedFormat,
} from "@/app/lib/utils/evaluation";
Expand All @@ -29,9 +31,11 @@
} from "@/app/lib/utils/evaluationExport";
import Sidebar from "@/app/components/Sidebar";
import {
AiSummaryNote,
CategoryMetricsTable,
DetailedResultsTable,
MetricsOverview,
OverallScoreCard,
RunModeBadge,
} from "@/app/components/evaluations";
import {
Expand All @@ -51,7 +55,7 @@
DownloadIcon,
} from "@/app/components/icons";

export default function EvaluationReport() {

Check warning on line 58 in app/(main)/evaluations/[id]/page.tsx

View workflow job for this annotation

GitHub Actions / lint-and-build

Function 'EvaluationReport' has too many statements (37). Maximum allowed is 20
const router = useRouter();
const params = useParams();
const toast = useToast();
Expand Down Expand Up @@ -266,6 +270,8 @@
scoreObject && isNewScoreObjectV2(scoreObject)
? (scoreObject.category_metrics ?? [])
: [];
const overallScore = getOverallScore(scoreObject);
const aiSummary = getAiSummary(scoreObject);

const isJobInProgress =
job.status.toLowerCase() !== "completed" &&
Expand Down Expand Up @@ -391,6 +397,7 @@
<div className="mx-auto space-y-6">
{hasScore && isNewFormat ? (
<>
{overallScore && <OverallScoreCard overall={overallScore} />}
<MetricsOverview
job={job}
summaryScores={summaryScores}
Expand All @@ -414,6 +421,11 @@

{hasScore && (
<div>
{aiSummary && !isFormatSwitching && (
<div className="mb-3">
<AiSummaryNote summary={aiSummary} />
</div>
)}
<div className="flex items-center gap-2 mb-3">
<h3 className="text-sm font-semibold text-text-secondary">
Detailed Results
Expand Down
16 changes: 16 additions & 0 deletions app/components/evaluations/AiSummaryNote.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use client";

import { InfoIcon } from "@/app/components/icons";

interface AiSummaryNoteProps {
summary: string;
}

export default function AiSummaryNote({ summary }: AiSummaryNoteProps) {
return (
<div className="flex items-start gap-2 rounded-lg px-4 py-3 bg-accent-primary/5 border border-accent-primary/20 text-sm text-text-primary">
<InfoIcon className="w-4 h-4 mt-0.5 shrink-0 text-accent-primary" />
<p>{summary}</p>
</div>
);
}
75 changes: 75 additions & 0 deletions app/components/evaluations/OverallScoreCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<span
className={`inline-flex items-center px-2.5 py-1 rounded-full text-[11px] font-semibold uppercase tracking-wide border ${color.bg} ${color.border} ${color.text}`}
>
{verdict}
</span>
);
}

export default function OverallScoreCard({ overall }: OverallScoreCardProps) {
return (
<div className="rounded-lg bg-bg-primary shadow-sm p-5">
<div className="flex items-center justify-between flex-wrap gap-3 mb-4">
<div>
<h3 className="text-sm font-semibold text-text-secondary">
Overall Score
</h3>
<div className="text-3xl font-bold text-text-primary mt-1 tabular-nums">
{overall.overall_score.toFixed(3)}
</div>
</div>
<VerdictBadge verdict={overall.verdict} />
</div>

{overall.breakdown.length > 0 && (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
{overall.breakdown.map((item) => (
<div
key={item.name}
className="rounded-lg px-4 py-3 bg-bg-secondary"
>
<div className="flex items-center justify-between gap-2 mb-1.5">
<span className="text-xs font-medium text-text-primary truncate">
{item.name}
</span>
<VerdictBadge verdict={item.verdict} />
</div>
<div className="flex items-baseline gap-2">
<span className="text-lg font-bold text-text-primary tabular-nums">
{item.score.toFixed(3)}
</span>
<span className="text-xs text-text-secondary">
weight {item.weight.toFixed(2)}
</span>
</div>
{typeof item.delta === "number" && (
<div
className={`text-xs mt-1 tabular-nums ${
item.delta >= 0
? "text-status-success-text"
: "text-status-error-text"
}`}
>
{item.delta >= 0 ? "▲" : "▼"}{" "}
{Math.abs(item.delta).toFixed(3)}
</div>
)}
</div>
))}
</div>
)}
</div>
);
}
2 changes: 2 additions & 0 deletions app/components/evaluations/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand Down
16 changes: 16 additions & 0 deletions app/lib/types/evaluation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,26 @@ export interface SummaryScore {
distribution?: Record<string, number>; // 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 {
Expand Down
64 changes: 64 additions & 0 deletions app/lib/utils/evaluation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
IndividualScore,
NewScoreObjectV2,
BasicScoreObject,
OverallScore,
ScoreObject,
TraceItem,
} from "@/app/lib/types/evaluation";
Expand Down Expand Up @@ -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<string, VerdictTone> = {
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[] {
Expand Down
Loading