11'use client'
22
33import { useState } from 'react'
4- import { Chip , ChipConfirmModal , ChipModalError , ChipTextarea } from '@sim/emcn'
4+ import { Chip , ChipConfirmModal , ChipInput , ChipModalError , ChipTextarea } from '@sim/emcn'
55import { Download , Trash } from '@sim/emcn/icons'
6+ import { useQueryStates } from 'nuqs'
67import type {
78 BenchmarkCase ,
89 RunBenchmarkStageBody ,
910 UpdateBenchmarkBody ,
1011} from '@/lib/api/contracts/benchmarks'
1112import { saveBlob } from '@/lib/uploads/client/download'
13+ import { BenchmarkHistory } from '@/app/o/[organizationId]/benchmark/components/benchmark-history'
1214import { BenchmarkReference } from '@/app/o/[organizationId]/benchmark/components/benchmark-reference'
1315import { BenchmarkResults } from '@/app/o/[organizationId]/benchmark/components/benchmark-results'
1416import { BenchmarkStep } from '@/app/o/[organizationId]/benchmark/components/benchmark-step'
17+ import {
18+ benchmarkParams ,
19+ benchmarkUrlOptions ,
20+ } from '@/app/o/[organizationId]/benchmark/search-params'
1521import {
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
4046function 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