Skip to content

Commit 4058f40

Browse files
author
Vikhyath Mondreti
committed
Retain benchmark run history with comparisons and human review
1 parent 72407ee commit 4058f40

22 files changed

Lines changed: 4217 additions & 1110 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { getBenchmarkRunContract, reviewBenchmarkRunContract } from '@/lib/api/contracts/benchmarks'
2+
import {
3+
defineInternalJsonRoute,
4+
internalOrchestrationErrorPolicy,
5+
internalRateLimits,
6+
internalSessionAuth,
7+
} from '@/lib/api/server/routes'
8+
import { benchmarkOperations } from '@/lib/benchmarks/application/operations'
9+
import { getBenchmarkRun, reviewBenchmarkRun } from '@/lib/benchmarks/application/runs'
10+
import { requireBenchmarkEnabled } from '@/lib/benchmarks/config'
11+
12+
export const GET = defineInternalJsonRoute({
13+
contract: getBenchmarkRunContract,
14+
auth: internalSessionAuth,
15+
operation: benchmarkOperations.readRun,
16+
rateLimit: internalRateLimits.none({ reason: 'Owner-only bounded benchmark run snapshot' }),
17+
errorPolicy: internalOrchestrationErrorPolicy,
18+
beforeParse: () => requireBenchmarkEnabled(),
19+
mapInput: ({ params }) => ({
20+
organizationId: params.id,
21+
benchmarkId: params.benchmarkId,
22+
runId: params.runId,
23+
}),
24+
useCase: getBenchmarkRun,
25+
})
26+
27+
export const PATCH = defineInternalJsonRoute({
28+
contract: reviewBenchmarkRunContract,
29+
auth: internalSessionAuth,
30+
operation: benchmarkOperations.reviewRun,
31+
rateLimit: internalRateLimits.user({ bucketName: 'benchmark-review' }),
32+
errorPolicy: internalOrchestrationErrorPolicy,
33+
beforeParse: () => requireBenchmarkEnabled(),
34+
mapInput: ({ params, body }) => ({
35+
organizationId: params.id,
36+
benchmarkId: params.benchmarkId,
37+
runId: params.runId,
38+
...body,
39+
}),
40+
useCase: reviewBenchmarkRun,
41+
})
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { listBenchmarkRunsContract } from '@/lib/api/contracts/benchmarks'
2+
import {
3+
defineInternalJsonRoute,
4+
internalOrchestrationErrorPolicy,
5+
internalRateLimits,
6+
internalSessionAuth,
7+
} from '@/lib/api/server/routes'
8+
import { benchmarkOperations } from '@/lib/benchmarks/application/operations'
9+
import { listBenchmarkRuns } from '@/lib/benchmarks/application/runs'
10+
import { requireBenchmarkEnabled } from '@/lib/benchmarks/config'
11+
12+
export const GET = defineInternalJsonRoute({
13+
contract: listBenchmarkRunsContract,
14+
auth: internalSessionAuth,
15+
operation: benchmarkOperations.listRuns,
16+
rateLimit: internalRateLimits.none({ reason: 'Owner-only paginated benchmark run summaries' }),
17+
errorPolicy: internalOrchestrationErrorPolicy,
18+
beforeParse: () => requireBenchmarkEnabled(),
19+
mapInput: ({ params, query }) => ({
20+
organizationId: params.id,
21+
benchmarkId: params.benchmarkId,
22+
...query,
23+
}),
24+
useCase: listBenchmarkRuns,
25+
})

‎apps/sim/app/o/[organizationId]/benchmark/benchmark.tsx‎

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,32 @@ export function Benchmark({ organizationId, canPlan }: BenchmarkProps) {
4040
className='min-w-[200px] flex-1'
4141
value={benchmarkId}
4242
options={records.map((record) => ({ value: record.id, label: record.name }))}
43-
onChange={(value) => setParams({ benchmarkId: value })}
43+
onChange={(value) =>
44+
setParams({
45+
benchmarkId: value,
46+
benchmarkView: null,
47+
runId: null,
48+
compareRunId: null,
49+
runsCursor: null,
50+
})
51+
}
4452
placeholder='Saved benchmarks'
4553
searchable
4654
isLoading={benchmarks.isLoading}
4755
error={benchmarks.error?.message}
4856
/>
49-
<Chip leftIcon={Plus} onClick={() => setParams({ benchmarkId: null })}>
57+
<Chip
58+
leftIcon={Plus}
59+
onClick={() =>
60+
setParams({
61+
benchmarkId: null,
62+
benchmarkView: null,
63+
runId: null,
64+
compareRunId: null,
65+
runsCursor: null,
66+
})
67+
}
68+
>
5069
New benchmark
5170
</Chip>
5271
{benchmarks.hasNextPage && (
@@ -64,7 +83,18 @@ export function Benchmark({ organizationId, canPlan }: BenchmarkProps) {
6483
organizationId={organizationId}
6584
benchmarkId={benchmarkId}
6685
canPlan={canPlan}
67-
onDeleted={() => setParams({ benchmarkId: null }, { history: 'replace' })}
86+
onDeleted={() =>
87+
setParams(
88+
{
89+
benchmarkId: null,
90+
benchmarkView: null,
91+
runId: null,
92+
compareRunId: null,
93+
runsCursor: null,
94+
},
95+
{ history: 'replace' }
96+
)
97+
}
6898
/>
6999
) : (
70100
<CreateBenchmark

‎apps/sim/app/o/[organizationId]/benchmark/components/benchmark-detail.tsx‎

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
'use client'
22

33
import { useState } from 'react'
4-
import { Chip, ChipConfirmModal, ChipModalError, ChipTextarea } from '@sim/emcn'
4+
import { Chip, ChipConfirmModal, ChipInput, ChipModalError, ChipTextarea } from '@sim/emcn'
55
import { Download, Trash } from '@sim/emcn/icons'
6+
import { useQueryStates } from 'nuqs'
67
import type {
78
BenchmarkCase,
89
RunBenchmarkStageBody,
910
UpdateBenchmarkBody,
1011
} from '@/lib/api/contracts/benchmarks'
1112
import { saveBlob } from '@/lib/uploads/client/download'
13+
import { BenchmarkHistory } from '@/app/o/[organizationId]/benchmark/components/benchmark-history'
1214
import { BenchmarkReference } from '@/app/o/[organizationId]/benchmark/components/benchmark-reference'
1315
import { BenchmarkResults } from '@/app/o/[organizationId]/benchmark/components/benchmark-results'
1416
import { BenchmarkStep } from '@/app/o/[organizationId]/benchmark/components/benchmark-step'
17+
import {
18+
benchmarkParams,
19+
benchmarkUrlOptions,
20+
} from '@/app/o/[organizationId]/benchmark/search-params'
1521
import {
1622
useBenchmark,
1723
useDeleteBenchmark,
@@ -34,7 +40,7 @@ interface BenchmarkEditorProps {
3440
saving: boolean
3541
stage: RunBenchmarkStageBody['stage'] | null
3642
onUpdate: (body: UpdateBenchmarkBody) => void
37-
onRun: (stage: RunBenchmarkStageBody['stage']) => void
43+
onRun: (stage: RunBenchmarkStageBody['stage'], runLabel?: string) => void
3844
}
3945

4046
function BenchmarkEditor({
@@ -47,6 +53,7 @@ function BenchmarkEditor({
4753
onRun,
4854
}: BenchmarkEditorProps) {
4955
const [draft, setDraft] = useState(benchmark.artifacts)
56+
const [runLabel, setRunLabel] = useState('')
5057
const { artifacts } = benchmark
5158
const referenceDirty =
5259
draft.taskBrief !== artifacts.taskBrief || draft.referenceSpec !== artifacts.referenceSpec
@@ -151,17 +158,30 @@ function BenchmarkEditor({
151158
<BenchmarkStep
152159
number={4}
153160
title='Grade the result'
154-
description='Check each recovered answer against the expected detail and its supporting passage.'
161+
description='Check each recovered answer and save the result to run history.'
155162
pending={stage === 'grade'}
156163
action={
157164
<Chip
158165
disabled={busy || dirty || !artifacts.reconstruction}
159-
onClick={() => onRun('grade')}
166+
onClick={() => onRun('grade', runLabel)}
160167
>
161168
{stage === 'grade' ? 'Grading…' : 'Grade'}
162169
</Chip>
163170
}
164171
>
172+
<div className='flex flex-col gap-2'>
173+
<label htmlFor='benchmark-run-label' className='text-[var(--text-body)] text-small'>
174+
Run label (optional)
175+
</label>
176+
<ChipInput
177+
id='benchmark-run-label'
178+
value={runLabel}
179+
maxLength={100}
180+
onChange={(event) => setRunLabel(event.target.value)}
181+
placeholder='e.g. Baseline or updated discovery'
182+
disabled={busy}
183+
/>
184+
</div>
165185
{artifacts.grade && (
166186
<>
167187
<p className='text-[var(--text-primary)] text-base'>
@@ -186,6 +206,7 @@ export function BenchmarkDetail({
186206
canPlan,
187207
onDeleted,
188208
}: BenchmarkDetailProps) {
209+
const [{ benchmarkView }, setParams] = useQueryStates(benchmarkParams, benchmarkUrlOptions)
189210
const benchmarkQuery = useBenchmark(organizationId, benchmarkId)
190211
const updateBenchmark = useUpdateBenchmark(organizationId, benchmarkId)
191212
const runStage = useRunBenchmarkStage(organizationId, benchmarkId)
@@ -259,22 +280,53 @@ export function BenchmarkDetail({
259280
The previous attempt expired. You can run that step again.
260281
</p>
261282
)}
262-
<BenchmarkEditor
263-
key={`${benchmark.id}:${benchmark.version}`}
264-
benchmark={benchmark}
265-
canPlan={canPlan}
266-
busy={busy}
267-
saving={updateBenchmark.isPending}
268-
stage={stage}
269-
onUpdate={(body) => {
270-
runStage.reset()
271-
updateBenchmark.mutate(body)
272-
}}
273-
onRun={(nextStage) => {
274-
updateBenchmark.reset()
275-
runStage.mutate({ version: benchmark.version, stage: nextStage })
276-
}}
277-
/>
283+
<div className='flex gap-1' aria-label='Benchmark views'>
284+
<Chip
285+
variant={benchmarkView === 'current' ? 'primary' : undefined}
286+
onClick={() => setParams({ benchmarkView: 'current' })}
287+
>
288+
Current run
289+
</Chip>
290+
<Chip
291+
variant={benchmarkView === 'history' ? 'primary' : undefined}
292+
onClick={() => setParams({ benchmarkView: 'history' })}
293+
>
294+
Run history
295+
</Chip>
296+
</div>
297+
{benchmarkView === 'history' ? (
298+
<BenchmarkHistory organizationId={organizationId} benchmarkId={benchmarkId} />
299+
) : (
300+
<BenchmarkEditor
301+
key={`${benchmark.id}:${benchmark.version}`}
302+
benchmark={benchmark}
303+
canPlan={canPlan}
304+
busy={busy}
305+
saving={updateBenchmark.isPending}
306+
stage={stage}
307+
onUpdate={(body) => {
308+
runStage.reset()
309+
updateBenchmark.mutate(body)
310+
}}
311+
onRun={(nextStage, runLabel) => {
312+
updateBenchmark.reset()
313+
runStage.mutate(
314+
{ version: benchmark.version, stage: nextStage, runLabel },
315+
{
316+
onSuccess: () => {
317+
if (nextStage === 'grade')
318+
setParams({
319+
benchmarkView: 'history',
320+
runId: null,
321+
compareRunId: null,
322+
runsCursor: null,
323+
})
324+
},
325+
}
326+
)
327+
}}
328+
/>
329+
)}
278330
<ChipConfirmModal
279331
open={deleteOpen}
280332
onOpenChange={(open) => {

0 commit comments

Comments
 (0)