From 06908e252d768618265c31062a131f1ec2488908 Mon Sep 17 00:00:00 2001 From: PeterChrz Date: Sun, 20 Sep 2026 22:49:23 -0400 Subject: [PATCH] v2.4: per-band scenariobench aggregation, band scenario detail page, nickname badges, orderBy fix --- app/Console/Commands/SessionSummary.php | 57 +++++++ app/Models/BenchmarkRun.php | 4 +- resources/views/livewire/runs/show.blade.php | 9 +- .../livewire/sessions/scenarios.blade.php | 140 ++++++++++++++++++ .../views/livewire/summary/index.blade.php | 30 ++-- routes/web.php | 1 + 6 files changed, 225 insertions(+), 16 deletions(-) create mode 100644 resources/views/livewire/sessions/scenarios.blade.php diff --git a/app/Console/Commands/SessionSummary.php b/app/Console/Commands/SessionSummary.php index e9345ef..eb0148e 100644 --- a/app/Console/Commands/SessionSummary.php +++ b/app/Console/Commands/SessionSummary.php @@ -73,6 +73,15 @@ public function handle(): int $sessions[$key]['labels']->push($run->label); } + // Scenariobench is aggregated at band level (one row for all + // scenario runs in the band) rather than per-run, so collect the + // run rows and defer to a second pass below. + if ($run->suite === 'scenariobench') { + $sessions[$key]['scenario_runs'][] = $run; + + continue; + } + $aggs = $this->aggregateRun($run); $sessions[$key]['passed'] += $aggs->sum('passed'); @@ -81,6 +90,20 @@ public function handle(): int $sessions[$key]['runs'] = $sessions[$key]['runs']->merge($aggs); } + // Second pass: emit one aggregated scenariobench row per band, + // covering all scenario runs in that band. + foreach ($sessions as $key => $band) { + $scenarioRuns = $band['scenario_runs'] ?? []; + if (empty($scenarioRuns)) { + continue; + } + $agg = $this->aggregateScenarioBand($scenarioRuns); + $sessions[$key]['passed'] += $agg['passed']; + $sessions[$key]['failed'] += $agg['failed']; + $sessions[$key]['tokens'] += $agg['total_tokens']; + $sessions[$key]['runs'] = $band['runs']->merge([$agg]); + } + DB::transaction(function () use ($sessions) { DB::table('session_summaries')->truncate(); @@ -234,6 +257,40 @@ private function aggregateRun(object $run) ]]); } + /** + * Aggregate all scenariobench runs in a band into one summary row. + * Aggregates passed/failed/tokens and generated tok/s across every + * scenario run that belongs to the band. + * + * @param array $runs + */ + private function aggregateScenarioBand(array $runs): array + { + $runPks = array_map(fn ($r) => $r->id, $runs); + $first = $runs[0]; + + $agg = DB::table('scenario_runs') + ->whereIn('benchmark_run_id', $runPks) + ->selectRaw('count(*) filter (where ok) as passed') + ->selectRaw('count(*) filter (where not ok) as failed') + ->selectRaw('sum(gen_tokens) as total_tokens') + ->selectRaw('sum(gen_tokens) / nullif(sum(wall), 0) as gen_tps') + ->first(); + + return [ + 'run_pk' => $first->id, + 'run_id' => $first->run_id, + 'suite' => 'scenariobench', + 'level' => null, + 'passed' => (int) ($agg->passed ?? 0), + 'failed' => (int) ($agg->failed ?? 0), + 'avg_user_tps' => null, + 'weighted_user_tps' => null, + 'gen_tps' => $this->roundOrNull($agg->gen_tps), + 'total_tokens' => (int) ($agg->total_tokens ?? 0), + ]; + } + private function emptyAgg(object $run): array { return [ diff --git a/app/Models/BenchmarkRun.php b/app/Models/BenchmarkRun.php index 6210561..02b1ef4 100644 --- a/app/Models/BenchmarkRun.php +++ b/app/Models/BenchmarkRun.php @@ -126,9 +126,11 @@ public function suiteResults(?string $level = null): Collection $relation = Suites::runRelation($this->suite); + $orderCol = $this->suite === 'scenariobench' ? 'scenario' : 'task'; + return $this->{$relation}() ->when($level !== null && $level !== '', fn ($q) => $q->where('level', $level)) - ->orderBy('task') + ->orderBy($orderCol) ->orderBy('id') ->get(); } diff --git a/resources/views/livewire/runs/show.blade.php b/resources/views/livewire/runs/show.blade.php index 4fdcb4d..77c8e17 100644 --- a/resources/views/livewire/runs/show.blade.php +++ b/resources/views/livewire/runs/show.blade.php @@ -75,9 +75,12 @@ private function authorizeOwner(): void
{{ $this->run->suite }} · {{ $this->run->model_id }} - @if ($this->run->model_quant){{ $this->run->model_quant }} @endif - @if ($this->run->engine){{ $this->run->engine }} @endif - @if ($this->run->label){{ $this->run->label }} @endif + @if ($this->run->label) + {{ $this->run->label }} + @else + @if ($this->run->model_quant){{ $this->run->model_quant }} @endif + @if ($this->run->engine){{ $this->run->engine }} @endif + @endif @if ($this->run->visibility === 'private'){{ __('Private') }} @endif {{ optional($this->run->ran_at)->format('Y-m-d H:i') }} diff --git a/resources/views/livewire/sessions/scenarios.blade.php b/resources/views/livewire/sessions/scenarios.blade.php new file mode 100644 index 0000000..d4a6549 --- /dev/null +++ b/resources/views/livewire/sessions/scenarios.blade.php @@ -0,0 +1,140 @@ +sessionId = $session; + } + + #[Computed] + public function band(): object + { + return DB::table('session_summaries') + ->where('id', $this->sessionId) + ->where(function ($q) { + $q->where('visibility', 'public'); + if (auth()->user() !== null) { + $q->orWhere(function ($own) { + $own->where('visibility', 'private') + ->where('user_id', auth()->id()); + }); + } + }) + ->firstOrFail(); + } + + #[Computed] + public function runs() + { + $band = $this->band; + + return BenchmarkRun::where('suite', 'scenariobench') + ->where('model_id', $band->model_id) + ->where('model_quant', $band->model_quant) + ->where('gpu', $band->gpu) + ->where('gpu_count', $band->gpu_count) + ->where('user_id', $band->user_id) + ->where('visibility', $band->visibility) + ->where('engine', $band->engine) + ->where(function ($q) use ($band) { + $q->whereNull('config_id') + ->orWhere('config_id', $this->configIdFromName($band->config_name)); + }) + ->orderBy('ran_at') + ->get(); + } + + private function configIdFromName(?string $name): ?int + { + if ($name === null) { + return null; + } + + return DB::table('server_configs')->where('name', $name)->value('id'); + } + + #[Computed] + public function results() + { + $runIds = $this->runs->pluck('id'); + + return DB::table('scenario_runs') + ->whereIn('benchmark_run_id', $runIds) + ->orderBy('scenario') + ->orderBy('benchmark_run_id') + ->get(); + } +}; +?> + +
+
+
+ {{ __('Scenario results') }} + + @if ($this->band->label) + {{ $this->band->label }} + @endif + {{ $this->band->model_id ?? 'unknown model' }} + @if ($this->band->model_quant) + {{ $this->band->model_quant }} + @endif + {{ $this->band->gpu ?? 'unknown GPU' }} + +
+ + {{ __('Sessions') }} +
+ + + + {{ __('Scenario') }} + {{ __('Level') }} + {{ __('Pass') }} + {{ __('Outcome') }} + {{ __('Wall (s)') }} + {{ __('Turns') }} + {{ __('Commands') }} + {{ __('Submissions') }} + {{ __('Rejected') }} + {{ __('t first sub') }} + {{ __('t pass') }} + {{ __('Gen tokens') }} + + + + @foreach ($this->results as $result) + + {{ $result->scenario }} + {{ $result->level ?? '—' }} + + @if ($result->ok) + + @elseif ($result->ok === null) + — + @else + + @endif + + {{ $result->outcome ?? '—' }} + {{ number_format($result->wall, 1) }} + {{ number_format($result->turns) }} + {{ number_format($result->commands) }} + {{ number_format($result->submissions) }} + {{ number_format($result->rejected) }} + {{ number_format($result->t_first_sub, 1) }} + {{ $result->t_pass !== null ? number_format($result->t_pass, 1) : '—' }} + {{ number_format($result->gen_tokens) }} + + @endforeach + + +
\ No newline at end of file diff --git a/resources/views/livewire/summary/index.blade.php b/resources/views/livewire/summary/index.blade.php index 365cfa0..ee92c18 100644 --- a/resources/views/livewire/summary/index.blade.php +++ b/resources/views/livewire/summary/index.blade.php @@ -148,22 +148,23 @@ class="w-64" ×{{ $session->band->gpu_count }} @endif {{ $session->band->model_id ?? 'unknown model' }} - @if ($session->band->model_quant) - {{ $session->band->model_quant }} - @endif - @if ($session->band->engine) - {{ $session->band->engine }} - @endif - @if ($session->band->config_name) - {{ $session->band->config_name }} + @if ($session->band->label) + {{ $session->band->label }} + @else + @if ($session->band->model_quant) + {{ $session->band->model_quant }} + @endif + @if ($session->band->engine) + {{ $session->band->engine }} + @endif + @if ($session->band->config_name) + {{ $session->band->config_name }} + @endif @endif {{ $session->band->runner_name ?? 'anonymous' }} {{ optional($session->band->ran_at)->format('M j Y') }}
- @if ($session->band->label) -
{{ $session->band->label }}
- @endif {{-- Column titles aligned over the suite rows (md+ only) --}}