Skip to content

Commit 3217048

Browse files
committed
feat(evaluation): UI API client for the evaluation endpoints
Typed wrappers over the seven `/evaluation` routes, with interfaces mirroring the response schemas one-for-one, and two things the two eval views would otherwise each define: - `isActiveStatus` / `ACTIVE_RUN_STATUSES`, so "can this run still change?" has one definition rather than a repeated status list; - `EVAL_POLL_MS`, the refetch cadence while a run is in flight. `status-badge` learns EVALUATING so a run in its promptfoo phase renders like every other in-flight status instead of falling through to the default.
1 parent a229934 commit 3217048

2 files changed

Lines changed: 147 additions & 0 deletions

File tree

ui/src/components/shared/status-badge.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const statusStyles: Record<string, string> = {
55
PROCESSING: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200",
66
RUNNING: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200",
77
INDEXING: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200",
8+
EVALUATING: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200",
89
// OpenRag indexing-task states (TaskStateManager)
910
SERIALIZING: "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200",
1011
CANCELLED: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200",

ui/src/lib/api/evaluation.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { request } from "./client";
2+
3+
// Admin evaluation endpoints:
4+
// GET /evaluation/datasets → EvalDataset[]
5+
// POST /evaluation/datasets → EvalDataset (multipart)
6+
// DELETE /evaluation/datasets/{id} → 204
7+
// GET /evaluation/runs → EvalRunSummary[]
8+
// POST /evaluation/runs → EvalRun (202, 409 when one is in flight)
9+
// GET /evaluation/runs/{id} → EvalRun
10+
// POST /evaluation/runs/{id}/cancel → EvalRun
11+
12+
export interface EvalDataset {
13+
id: string;
14+
name: string;
15+
corpus_file_count: number;
16+
testset_row_count: number;
17+
created_at: string | null;
18+
created_by: number | null;
19+
}
20+
21+
export type EvalRunStatus =
22+
| "QUEUED"
23+
| "INDEXING"
24+
| "EVALUATING"
25+
| "COMPLETED"
26+
| "FAILED"
27+
| "CANCELLED";
28+
29+
export const ACTIVE_RUN_STATUSES: EvalRunStatus[] = ["QUEUED", "INDEXING", "EVALUATING"];
30+
31+
export function isActiveStatus(status: EvalRunStatus): boolean {
32+
return ACTIVE_RUN_STATUSES.includes(status);
33+
}
34+
35+
/** Refetch cadence while a run can still change, shared by both eval views. */
36+
export const EVAL_POLL_MS = 3000;
37+
38+
export interface FileIndexingSample {
39+
filename: string;
40+
size_bytes: number;
41+
duration_seconds: number;
42+
failed: boolean;
43+
}
44+
45+
export interface IndexingMetrics {
46+
files_total: number;
47+
files_failed: number;
48+
bytes_total: number;
49+
wall_seconds: number;
50+
files_per_minute: number;
51+
megabytes_per_second: number;
52+
p50_seconds: number;
53+
p95_seconds: number;
54+
by_extension: Record<string, Record<string, number>>;
55+
samples: FileIndexingSample[];
56+
}
57+
58+
export interface RetrievalMetrics {
59+
scored_cases: number;
60+
skipped_cases: number;
61+
hit_rate: number;
62+
mrr: number;
63+
recall: number;
64+
context_relevance: number | null;
65+
}
66+
67+
export interface AnswerMetrics {
68+
scored_cases: number;
69+
pass_rate: number;
70+
factuality: number | null;
71+
rubric_score: number | null;
72+
}
73+
74+
export interface EvalCase {
75+
query: string;
76+
retrieved_file_ids: string[];
77+
expected_file_ids: string[];
78+
hit: boolean | null;
79+
reciprocal_rank: number | null;
80+
answer: string | null;
81+
answer_passed: boolean | null;
82+
grader_reason: string | null;
83+
}
84+
85+
export interface EvalRun {
86+
id: string;
87+
dataset_id: string;
88+
status: EvalRunStatus;
89+
started_at: string | null;
90+
finished_at: string | null;
91+
indexing: IndexingMetrics | null;
92+
retrieval: RetrievalMetrics | null;
93+
answer: AnswerMetrics | null;
94+
cases: EvalCase[];
95+
error: string | null;
96+
created_by: number | null;
97+
}
98+
99+
export interface EvalRunSummary {
100+
id: string;
101+
dataset_id: string;
102+
status: EvalRunStatus;
103+
started_at: string | null;
104+
finished_at: string | null;
105+
hit_rate: number | null;
106+
mrr: number | null;
107+
answer_pass_rate: number | null;
108+
files_per_minute: number | null;
109+
error: string | null;
110+
}
111+
112+
export function listEvalDatasets() {
113+
return request<EvalDataset[]>("/evaluation/datasets");
114+
}
115+
116+
export function createEvalDataset(name: string, testset: File, corpus: File[]) {
117+
const body = new FormData();
118+
body.append("name", name);
119+
body.append("testset", testset);
120+
// FastAPI reads repeated parts as `list[UploadFile]`.
121+
corpus.forEach((file) => body.append("corpus", file));
122+
return request<EvalDataset>("/evaluation/datasets", { method: "POST", body });
123+
}
124+
125+
export function deleteEvalDataset(id: string) {
126+
return request<void>(`/evaluation/datasets/${encodeURIComponent(id)}`, { method: "DELETE" });
127+
}
128+
129+
export function listEvalRuns(limit = 50) {
130+
return request<EvalRunSummary[]>(`/evaluation/runs?limit=${limit}`);
131+
}
132+
133+
export function startEvalRun(datasetId: string) {
134+
return request<EvalRun>("/evaluation/runs", {
135+
method: "POST",
136+
body: JSON.stringify({ dataset_id: datasetId }),
137+
});
138+
}
139+
140+
export function getEvalRun(id: string) {
141+
return request<EvalRun>(`/evaluation/runs/${encodeURIComponent(id)}`);
142+
}
143+
144+
export function cancelEvalRun(id: string) {
145+
return request<EvalRun>(`/evaluation/runs/${encodeURIComponent(id)}/cancel`, { method: "POST" });
146+
}

0 commit comments

Comments
 (0)