Add sortable columns to all bench result tables (scenario detail + run detail)

This commit is contained in:
PeterChrz 2026-09-20 23:26:29 -04:00
parent 504b9e1322
commit 6f6f43c5ec
Signed by untrusted user who does not match committer: pch
GPG key ID: 8F0826ECF7302C63
3 changed files with 99 additions and 53 deletions

View file

@ -116,7 +116,7 @@ public function depthsweepResults(): HasMany
* an empty collection for suites this run does not have. An optional
* level filter narrows to one thinking level (depthsweep drill-in).
*/
public function suiteResults(?string $level = null): Collection
public function suiteResults(?string $level = null, ?string $sortBy = null, ?string $sortDir = null): Collection
{
$model = Suites::modelFor($this->suite);
@ -126,11 +126,13 @@ public function suiteResults(?string $level = null): Collection
$relation = Suites::runRelation($this->suite);
$orderCol = $this->suite === 'scenariobench' ? 'scenario' : 'task';
$defaultOrder = $this->suite === 'scenariobench' ? 'scenario' : 'task';
$orderCol = $sortBy ?? $defaultOrder;
$direction = $sortDir === 'asc' ? 'asc' : 'desc';
return $this->{$relation}()
->when($level !== null && $level !== '', fn ($q) => $q->where('level', $level))
->orderBy($orderCol)
->orderBy($orderCol, $direction)
->orderBy('id')
->get();
}

View file

@ -1,5 +1,6 @@
<?php
use App\Livewire\IncludesSorting;
use App\Models\BenchmarkRun;
use Flux\Flux;
use Illuminate\Auth\Access\AuthorizationException;
@ -8,6 +9,24 @@
use Livewire\Component;
new #[Layout('components.layouts.app')] class extends Component {
use IncludesSorting;
private const SORTABLE_SCENARIO = [
'scenario', 'level', 'ok', 'outcome',
'wall', 'turns', 'commands', 'submissions', 'rejected',
't_first_sub', 't_pass', 'gen_tokens',
];
private const SORTABLE_DEPTHSWEEP = [
'section', 'task', 'level', 'ok', 'ttft', 'wall', 'visible',
];
private const SORTABLE_METRIC = [
'section', 'task', 'level', 'ok', 'checks_passed',
'kind', 'user_tps', 'decode', 'ttft', 't_answer',
'wall', 'reasoning', 'visible', 'finish',
];
public ?string $notes = null;
public ?string $runId = null;
@ -67,6 +86,16 @@ private function authorizeOwner(): void
throw new AuthorizationException;
}
}
private function sortColumn(array $allowed): string
{
return in_array($this->sortBy, $allowed, true) ? $this->sortBy : $allowed[0];
}
private function sortDir(): string
{
return $this->sortDirection === 'asc' ? 'asc' : 'desc';
}
};
?>
@ -137,24 +166,25 @@ private function authorizeOwner(): void
<flux:heading size="md">{{ $this->run->suite === 'scenariobench' ? __('Scenario results') : ($this->run->suite === 'depthsweep' ? __('Depth sweep results') : __('Task results')) }}</flux:heading>
<div class="mt-3">
@if ($this->run->suite === 'scenariobench')
@php($sortCol = $this->sortColumn(self::SORTABLE_SCENARIO))
<flux:table>
<flux:table.columns>
<flux:table.column>{{ __('Scenario') }}</flux:table.column>
<flux:table.column>{{ __('Level') }}</flux:table.column>
<flux:table.column>{{ __('Pass') }}</flux:table.column>
<flux:table.column>{{ __('Outcome') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Wall (s)') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Turns') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Commands') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Submissions') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Rejected') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('t first sub') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('t pass') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Gen tokens') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'scenario'" :direction="$sortDirection" wire:click="sort('scenario')">{{ __('Scenario') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'level'" :direction="$sortDirection" wire:click="sort('level')">{{ __('Level') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'ok'" :direction="$sortDirection" wire:click="sort('ok')">{{ __('Pass') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'outcome'" :direction="$sortDirection" wire:click="sort('outcome')">{{ __('Outcome') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'wall'" :direction="$sortDirection" wire:click="sort('wall')" class="text-center">{{ __('Wall (s)') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'turns'" :direction="$sortDirection" wire:click="sort('turns')" class="text-center">{{ __('Turns') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'commands'" :direction="$sortDirection" wire:click="sort('commands')" class="text-center">{{ __('Commands') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'submissions'" :direction="$sortDirection" wire:click="sort('submissions')" class="text-center">{{ __('Submissions') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'rejected'" :direction="$sortDirection" wire:click="sort('rejected')" class="text-center">{{ __('Rejected') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 't_first_sub'" :direction="$sortDirection" wire:click="sort('t_first_sub')" class="text-center">{{ __('t first sub') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 't_pass'" :direction="$sortDirection" wire:click="sort('t_pass')" class="text-center">{{ __('t pass') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'gen_tokens'" :direction="$sortDirection" wire:click="sort('gen_tokens')" class="text-center">{{ __('Gen tokens') }}</flux:table.column>
</flux:table.columns>
<flux:table.rows>
@foreach ($this->run->suiteResults() as $result)
@foreach ($this->run->suiteResults($this->level, $sortCol, $this->sortDir()) as $result)
<flux:table.row :key="$result->id">
<flux:table.cell>{{ $result->scenario }}</flux:table.cell>
<flux:table.cell>{{ $result->level ?? '—' }}</flux:table.cell>
@ -188,20 +218,21 @@ private function authorizeOwner(): void
<flux:button size="xs" variant="ghost" icon="x-mark" href="{{ route('runs.show', $this->run->run_id) }}" wire:navigate>{{ __('show all levels') }}</flux:button>
</div>
@endif
@php($sortCol = $this->sortColumn(self::SORTABLE_DEPTHSWEEP))
<flux:table>
<flux:table.columns>
<flux:table.column>{{ __('Depth') }}</flux:table.column>
<flux:table.column>{{ __('Task') }}</flux:table.column>
<flux:table.column>{{ __('Level') }}</flux:table.column>
<flux:table.column>{{ __('Found') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'section'" :direction="$sortDirection" wire:click="sort('section')">{{ __('Depth') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'task'" :direction="$sortDirection" wire:click="sort('task')">{{ __('Task') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'level'" :direction="$sortDirection" wire:click="sort('level')">{{ __('Level') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'ok'" :direction="$sortDirection" wire:click="sort('ok')">{{ __('Found') }}</flux:table.column>
<flux:table.column>{{ __('Needle') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('TTFT') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Wall') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Visible') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'ttft'" :direction="$sortDirection" wire:click="sort('ttft')" class="text-center">{{ __('TTFT') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'wall'" :direction="$sortDirection" wire:click="sort('wall')" class="text-center">{{ __('Wall') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'visible'" :direction="$sortDirection" wire:click="sort('visible')" class="text-center">{{ __('Visible') }}</flux:table.column>
</flux:table.columns>
<flux:table.rows>
@foreach ($this->run->suiteResults($this->level) as $result)
@foreach ($this->run->suiteResults($this->level, $sortCol, $this->sortDir()) as $result)
<flux:table.row :key="$result->id">
<flux:table.cell><flux:badge size="xs" color="sky">{{ $result->section }}</flux:badge></flux:table.cell>
<flux:table.cell>{{ $result->task }}</flux:table.cell>
@ -222,32 +253,33 @@ private function authorizeOwner(): void
</flux:table.rows>
</flux:table>
@else
@php($sortCol = $this->sortColumn(self::SORTABLE_METRIC))
<flux:table>
<flux:table.columns>
@if (in_array($this->run->suite, ['softbench', 'prosebench', 'psychbench']))
<flux:table.column>{{ __('Section') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'section'" :direction="$sortDirection" wire:click="sort('section')">{{ __('Section') }}</flux:table.column>
@endif
<flux:table.column>{{ __('Task') }}</flux:table.column>
<flux:table.column>{{ __('Level') }}</flux:table.column>
<flux:table.column>{{ __('Pass') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'task'" :direction="$sortDirection" wire:click="sort('task')">{{ __('Task') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'level'" :direction="$sortDirection" wire:click="sort('level')">{{ __('Level') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'ok'" :direction="$sortDirection" wire:click="sort('ok')">{{ __('Pass') }}</flux:table.column>
@if (in_array($this->run->suite, ['prosebench', 'psychbench']))
<flux:table.column class="text-center">{{ __('Checks') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'checks_passed'" :direction="$sortDirection" wire:click="sort('checks_passed')" class="text-center">{{ __('Checks') }}</flux:table.column>
@endif
@if ($this->run->suite === 'infrabench')
<flux:table.column>{{ __('Kind') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'kind'" :direction="$sortDirection" wire:click="sort('kind')">{{ __('Kind') }}</flux:table.column>
@endif
<flux:table.column class="text-center">{{ __('User tok/s') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Decode') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('TTFT') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('t_answer') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Wall') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Reasoning') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Visible') }}</flux:table.column>
<flux:table.column>{{ __('Finish') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'user_tps'" :direction="$sortDirection" wire:click="sort('user_tps')" class="text-center">{{ __('User tok/s') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'decode'" :direction="$sortDirection" wire:click="sort('decode')" class="text-center">{{ __('Decode') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'ttft'" :direction="$sortDirection" wire:click="sort('ttft')" class="text-center">{{ __('TTFT') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 't_answer'" :direction="$sortDirection" wire:click="sort('t_answer')" class="text-center">{{ __('t_answer') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'wall'" :direction="$sortDirection" wire:click="sort('wall')" class="text-center">{{ __('Wall') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'reasoning'" :direction="$sortDirection" wire:click="sort('reasoning')" class="text-center">{{ __('Reasoning') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'visible'" :direction="$sortDirection" wire:click="sort('visible')" class="text-center">{{ __('Visible') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'finish'" :direction="$sortDirection" wire:click="sort('finish')">{{ __('Finish') }}</flux:table.column>
</flux:table.columns>
<flux:table.rows>
@foreach ($this->run->suiteResults() as $result)
@foreach ($this->run->suiteResults(null, $sortCol, $this->sortDir()) as $result)
<flux:table.row :key="$result->id">
@if (in_array($this->run->suite, ['softbench', 'prosebench', 'psychbench']))
<flux:table.cell>{{ $result->section ?? '—' }}</flux:table.cell>

View file

@ -1,5 +1,6 @@
<?php
use App\Livewire\IncludesSorting;
use App\Models\BenchmarkRun;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Computed;
@ -7,6 +8,14 @@
use Livewire\Component;
new #[Layout('components.layouts.app')] class extends Component {
use IncludesSorting;
private const SORTABLE = [
'scenario', 'level', 'ok', 'outcome',
'wall', 'turns', 'commands', 'submissions', 'rejected',
't_first_sub', 't_pass', 'gen_tokens',
];
public ?int $sessionId = null;
public function mount(int $session): void
@ -66,10 +75,13 @@ public function results()
{
$runIds = $this->runs->pluck('id');
$column = in_array($this->sortBy, self::SORTABLE, true) ? $this->sortBy : 'scenario';
$direction = $this->sortDirection === 'asc' ? 'asc' : 'desc';
return DB::table('scenario_runs')
->whereIn('benchmark_run_id', $runIds)
->orderBy('scenario')
->orderBy('benchmark_run_id')
->orderBy($column, $direction)
->orderBy('id')
->get();
}
};
@ -96,18 +108,18 @@ public function results()
<flux:table>
<flux:table.columns>
<flux:table.column>{{ __('Scenario') }}</flux:table.column>
<flux:table.column>{{ __('Level') }}</flux:table.column>
<flux:table.column>{{ __('Pass') }}</flux:table.column>
<flux:table.column>{{ __('Outcome') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Wall (s)') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Turns') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Commands') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Submissions') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Rejected') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('t first sub') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('t pass') }}</flux:table.column>
<flux:table.column class="text-center">{{ __('Gen tokens') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'scenario'" :direction="$sortDirection" wire:click="sort('scenario')">{{ __('Scenario') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'level'" :direction="$sortDirection" wire:click="sort('level')">{{ __('Level') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'ok'" :direction="$sortDirection" wire:click="sort('ok')">{{ __('Pass') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'outcome'" :direction="$sortDirection" wire:click="sort('outcome')">{{ __('Outcome') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'wall'" :direction="$sortDirection" wire:click="sort('wall')" class="text-center">{{ __('Wall (s)') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'turns'" :direction="$sortDirection" wire:click="sort('turns')" class="text-center">{{ __('Turns') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'commands'" :direction="$sortDirection" wire:click="sort('commands')" class="text-center">{{ __('Commands') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'submissions'" :direction="$sortDirection" wire:click="sort('submissions')" class="text-center">{{ __('Submissions') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'rejected'" :direction="$sortDirection" wire:click="sort('rejected')" class="text-center">{{ __('Rejected') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 't_first_sub'" :direction="$sortDirection" wire:click="sort('t_first_sub')" class="text-center">{{ __('t first sub') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 't_pass'" :direction="$sortDirection" wire:click="sort('t_pass')" class="text-center">{{ __('t pass') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy === 'gen_tokens'" :direction="$sortDirection" wire:click="sort('gen_tokens')" class="text-center">{{ __('Gen tokens') }}</flux:table.column>
</flux:table.columns>
<flux:table.rows>