diff --git a/app/(routes)/evaluations/[id]/page.tsx b/app/(routes)/evaluations/[id]/page.tsx index a073dbf..a9a5843 100644 --- a/app/(routes)/evaluations/[id]/page.tsx +++ b/app/(routes)/evaluations/[id]/page.tsx @@ -1,6 +1,5 @@ /** - * EvaluationReport.tsx - Detailed evaluation report page - * + * Detailed evaluation report page * Shows metrics overview and per-item scores for a specific evaluation job */ @@ -8,6 +7,7 @@ import { useState, useEffect, useCallback } from "react"; import { useRouter, useParams } from "next/navigation"; +import { apiFetch } from "@/app/lib/apiClient"; import { useAuth } from "@/app/lib/context/AuthContext"; import { useApp } from "@/app/lib/context/AppContext"; import { @@ -27,6 +27,14 @@ import DetailedResultsTable from "@/app/components/DetailedResultsTable"; import { colors } from "@/app/lib/colors"; import { useToast } from "@/app/components/Toast"; import Loader from "@/app/components/Loader"; +import { + WarningTriangleIcon, + MenuIcon, + ChevronLeftIcon, + DatabaseIcon, + GroupIcon, + RefreshIcon, +} from "@/app/components/icons"; export default function EvaluationReport() { const router = useRouter(); @@ -65,7 +73,6 @@ export default function EvaluationReport() { return `"${sanitized}"`; }; - // Set initial selected key from context useEffect(() => { if (apiKeys.length > 0 && !selectedKeyId) { setSelectedKeyId(apiKeys[0].id); @@ -83,25 +90,12 @@ export default function EvaluationReport() { setError(null); try { - const response = await fetch( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const data = await apiFetch( `/api/evaluations/${jobId}?export_format=${exportFormat}`, - { - method: "GET", - headers: { "X-API-KEY": selectedKey.key }, - }, + selectedKey.key, ); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error || - errorData.message || - `Failed to fetch evaluation: ${response.status}`, - ); - } - - const data = await response.json(); - if (data.success === false && data.error) { toast.error(data.error); setExportFormat("row"); @@ -135,12 +129,10 @@ export default function EvaluationReport() { const fetchAssistantConfig = async (assistantId: string, apiKey: string) => { try { - const response = await fetch(`/api/assistant/${assistantId}`, { - method: "GET", - headers: { "X-API-KEY": apiKey }, - }); - if (!response.ok) return; - const result = await response.json(); + const result = await apiFetch<{ + success: boolean; + data?: AssistantConfig; + }>(`/api/assistant/${assistantId}`, apiKey); if (result.success && result.data) setAssistantConfig(result.data); } catch (err) { console.error( @@ -156,15 +148,11 @@ export default function EvaluationReport() { apiKey: string, ) => { try { - const configResponse = await fetch(`/api/configs/${configId}`, { - headers: { "X-API-KEY": apiKey }, - }); - if (!configResponse.ok) return; - const versionResponse = await fetch( + await apiFetch(`/api/configs/${configId}`, apiKey); + await apiFetch( `/api/configs/${configId}/versions/${configVersion}`, - { headers: { "X-API-KEY": apiKey } }, + apiKey, ); - if (!versionResponse.ok) return; } catch (error) { console.error("Error fetching config version info:", error); } @@ -321,22 +309,11 @@ export default function EvaluationReport() { setIsResyncing(true); try { - const response = await fetch( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const data = await apiFetch( `/api/evaluations/${jobId}?get_trace_info=true&resync_score=true&export_format=${exportFormat}`, - { - method: "GET", - headers: { "X-API-KEY": selectedKey.key }, - }, + selectedKey.key, ); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error( - errorData.error || - errorData.message || - `Failed to resync: ${response.status}`, - ); - } - const data = await response.json(); const foundJob = data.data || data; if (!foundJob) throw new Error("Evaluation job not found"); @@ -446,38 +423,14 @@ export default function EvaluationReport() { className="p-1.5 rounded-md flex-shrink-0" style={{ color: colors.text.secondary }} > - - - +

- - - + {job.dataset_name}

@@ -553,19 +494,7 @@ export default function EvaluationReport() { } }} > - - - + Individual Rows @@ -657,6 +574,17 @@ export default function EvaluationReport() { {/* Metrics */} {hasScore && isNewFormat ? (
+ {summaryScores.some( + (s) => job.total_items && s.total_pairs < job.total_items, + ) && ( +
+ + Some traces are still being scored. Scores shown are + partial and may change — click{" "} + Resync to + get the latest. +
+ )}

- - - + {isResyncing ? "Resyncing..." : "Resync"}

@@ -692,7 +610,7 @@ export default function EvaluationReport() { .map((summary) => (
{summary.std !== undefined && `±${summary.std.toFixed(3)} · `} - {summary.total_pairs} pairs + {job.total_items && + summary.total_pairs < job.total_items ? ( + + {summary.total_pairs}/{job.total_items} pairs + + + ) : ( + {summary.total_pairs} pairs + )}
))} @@ -727,16 +656,12 @@ export default function EvaluationReport() { .map((summary) => (
-
+
{summary.name}
@@ -745,15 +670,9 @@ export default function EvaluationReport() { ([key, value]) => (
- + {key} -
- {summary.total_pairs} pairs +
+ {job.total_items && + summary.total_pairs < job.total_items ? ( + + {summary.total_pairs}/{job.total_items} pairs + + + ) : ( + {summary.total_pairs} pairs + )}
))} @@ -876,10 +803,9 @@ export default function EvaluationReport() {
- {storedDatasets.length === 0 ? ( + {isDatasetsLoading ? ( +
+ +
+ ) : storedDatasets.length === 0 ? (
+ + + ); +} diff --git a/app/components/icons/evaluations/DatabaseIcon.tsx b/app/components/icons/evaluations/DatabaseIcon.tsx new file mode 100644 index 0000000..360b991 --- /dev/null +++ b/app/components/icons/evaluations/DatabaseIcon.tsx @@ -0,0 +1,23 @@ +interface IconProps { + className?: string; + style?: React.CSSProperties; +} + +export default function DatabaseIcon({ className, style }: IconProps) { + return ( + + + + ); +} diff --git a/app/components/icons/evaluations/GroupIcon.tsx b/app/components/icons/evaluations/GroupIcon.tsx new file mode 100644 index 0000000..8da3de4 --- /dev/null +++ b/app/components/icons/evaluations/GroupIcon.tsx @@ -0,0 +1,23 @@ +interface IconProps { + className?: string; + style?: React.CSSProperties; +} + +export default function GroupIcon({ className, style }: IconProps) { + return ( + + + + ); +} diff --git a/app/components/icons/evaluations/MenuIcon.tsx b/app/components/icons/evaluations/MenuIcon.tsx new file mode 100644 index 0000000..a53975f --- /dev/null +++ b/app/components/icons/evaluations/MenuIcon.tsx @@ -0,0 +1,23 @@ +interface IconProps { + className?: string; + style?: React.CSSProperties; +} + +export default function MenuIcon({ className, style }: IconProps) { + return ( + + + + ); +} diff --git a/app/components/icons/evaluations/RefreshIcon.tsx b/app/components/icons/evaluations/RefreshIcon.tsx new file mode 100644 index 0000000..fedb9e2 --- /dev/null +++ b/app/components/icons/evaluations/RefreshIcon.tsx @@ -0,0 +1,23 @@ +interface IconProps { + className?: string; + style?: React.CSSProperties; +} + +export default function RefreshIcon({ className, style }: IconProps) { + return ( + + + + ); +} diff --git a/app/components/icons/evaluations/WarningTriangleIcon.tsx b/app/components/icons/evaluations/WarningTriangleIcon.tsx new file mode 100644 index 0000000..737b9ce --- /dev/null +++ b/app/components/icons/evaluations/WarningTriangleIcon.tsx @@ -0,0 +1,23 @@ +interface IconProps { + className?: string; + style?: React.CSSProperties; +} + +export default function WarningTriangleIcon({ className, style }: IconProps) { + return ( + + + + ); +} diff --git a/app/components/icons/index.tsx b/app/components/icons/index.tsx index 9167c71..54feb0f 100644 --- a/app/components/icons/index.tsx +++ b/app/components/icons/index.tsx @@ -1,6 +1,12 @@ // Evaluations Icons export { default as ChevronUpIcon } from "./evaluations/ChevronUpIcon"; export { default as ChevronDownIcon } from "./evaluations/ChevronDownIcon"; +export { default as ChevronLeftIcon } from "./evaluations/ChevronLeftIcon"; export { default as EditIcon } from "./evaluations/EditIcon"; export { default as GearIcon } from "./evaluations/GearIcon"; export { default as CheckIcon } from "./evaluations/CheckIcon"; +export { default as WarningTriangleIcon } from "./evaluations/WarningTriangleIcon"; +export { default as MenuIcon } from "./evaluations/MenuIcon"; +export { default as DatabaseIcon } from "./evaluations/DatabaseIcon"; +export { default as GroupIcon } from "./evaluations/GroupIcon"; +export { default as RefreshIcon } from "./evaluations/RefreshIcon"; diff --git a/app/globals.css b/app/globals.css index 7de4e92..a29d7df 100644 --- a/app/globals.css +++ b/app/globals.css @@ -19,6 +19,36 @@ --font-mono: var(--font-geist-mono); } +/* Background colors */ +@theme inline { + --color-bg-primary: #ffffff; + --color-bg-secondary: #fafafa; +} + +/* Text colors */ +@theme inline { + --color-text-primary: #171717; + --color-text-secondary: #737373; +} + +/* Border colors */ +@theme inline { + --color-border: #e5e5e5; +} + +/* Accent colors */ +@theme inline { + --color-accent-primary: #171717; + --color-accent-hover: #404040; +} + +/* Status colors */ +@theme inline { + --color-status-success: #16a34a; + --color-status-error: #dc2626; + --color-status-warning: #f59e0b; +} + @media (prefers-color-scheme: dark) { :root { --background: #000000;