[], 'model' => [], 'quant' => [], 'engine' => [], 'suite' => [], ]; /** @var array session_summary_id => expanded */ public array $expanded = []; public function updatedFilters(): void { $this->resetPage(); $this->expanded = []; } public function toggleExpand(int $sessionId): void { $this->expanded[$sessionId] = ! ($this->expanded[$sessionId] ?? false); } public function resetFilters(): void { $this->filters = [ 'gpu' => [], 'model' => [], 'quant' => [], 'engine' => [], 'suite' => [], ]; $this->resetPage(); } public function toggleFilter(string $field, string $value): void { if (! array_key_exists($field, $this->filters)) { return; } $this->filters[$field] = array_values(array_unique( in_array($value, $this->filters[$field], true) ? array_diff($this->filters[$field], [$value]) : array_merge($this->filters[$field], [$value]) )); $this->resetPage(); } public function filterLabel(string $field): string { $count = count($this->filters[$field] ?? []); return $count === 0 ? '' : ' ('.$count.')'; } #[Computed] public function sessions() { $ttl = (int) config('festbench.home_cache_ttl', 300); $user = auth()->user(); // Guest views are shared-cacheable; logged-in views may contain the // user's private bands, so they get a user-scoped key. $cacheKey = 'summary:'.($user?->id ?? 'guest').':'.md5(json_encode([$this->filters, $this->getPage()])); return Cache::remember($cacheKey, $ttl, function () use ($user) { $bands = $this->bandQuery($user)->paginate(10); $runIds = $bands->getCollection()->map(fn ($b) => $b->id)->all(); $suiteRows = $runIds === [] ? collect() : \Illuminate\Support\Facades\DB::table('session_suite_rows') ->whereIn('session_summary_id', $runIds) ->orderBy('sequence') ->get() ->groupBy('session_summary_id'); $bands->getCollection()->transform(fn ($band) => (object) [ 'band' => $band, 'suites' => $suiteRows->get($band->id, collect()), ]); return $bands; }); } private function bandQuery($user) { return \Illuminate\Support\Facades\DB::table('session_summaries') ->where(function ($q) use ($user) { $q->where('visibility', 'public'); if ($user !== null) { $q->orWhere(function ($own) use ($user) { $own->where('visibility', 'private')->where('user_id', $user->id); }); } }) ->when($this->filters['gpu'], fn ($q, array $gpus) => $q->whereIn('gpu', $gpus)) ->when($this->filters['model'], fn ($q, array $models) => $q->whereIn('model_id', $models)) ->when($this->filters['quant'], fn ($q, array $quants) => $q->whereIn('model_quant', $quants)) ->when($this->filters['engine'], fn ($q, array $engines) => $q->whereIn('engine', $engines)) ->when($this->filters['suite'], fn ($q, array $suites) => $q->whereExists(function ($sub) use ($suites) { $sub->select(\Illuminate\Support\Facades\DB::raw(1)) ->from('session_suite_rows') ->whereColumn('session_suite_rows.session_summary_id', 'session_summaries.id') ->whereIn('session_suite_rows.suite', $suites); })) ->orderByDesc('ran_at') ->orderBy('sequence'); } #[Computed] public function gpus(): array { return Cache::remember('summary:gpus', 300, function () { return \Illuminate\Support\Facades\DB::table('session_summaries') ->where('visibility', 'public') ->whereNotNull('gpu') ->distinct()->orderBy('gpu')->pluck('gpu')->all(); }); } #[Computed] public function models(): array { return Cache::remember('summary:models', 300, function () { return \Illuminate\Support\Facades\DB::table('session_summaries') ->where('visibility', 'public') ->whereNotNull('model_id') ->distinct()->orderBy('model_id')->pluck('model_id')->all(); }); } #[Computed] public function quants(): array { return Cache::remember('summary:quants', 300, function () { return \Illuminate\Support\Facades\DB::table('session_summaries') ->where('visibility', 'public') ->whereNotNull('model_quant') ->distinct()->orderBy('model_quant')->pluck('model_quant')->all(); }); } #[Computed] public function engines(): array { return Cache::remember('summary:engines', 300, function () { return \Illuminate\Support\Facades\DB::table('session_summaries') ->where('visibility', 'public') ->whereNotNull('engine') ->distinct()->orderBy('engine')->pluck('engine')->all(); }); } #[Computed] public function suites(): array { return Cache::remember('summary:suites', 300, function () { return \Illuminate\Support\Facades\DB::table('session_suite_rows') ->join('session_summaries', 'session_summaries.id', '=', 'session_suite_rows.session_summary_id') ->where('session_summaries.visibility', 'public') ->distinct()->orderBy('suite')->pluck('suite')->all(); }); } }; ?>
{{ __('FestBench - Benchmarks') }} {{ __('GPU, model, suite scores — click a total to expand benchmark rows.') }}
{{ __('All task rows') }}
@php $filterGroups = [ ['field' => 'gpu', 'label' => __('GPU'), 'options' => $this->gpus], ['field' => 'model', 'label' => __('Model'), 'options' => $this->models], ['field' => 'quant', 'label' => __('Quant'), 'options' => $this->quants], ['field' => 'engine', 'label' => __('Inference engine'), 'options' => $this->engines], ['field' => 'suite', 'label' => __('Suite'), 'options' => $this->suites], ]; @endphp @foreach ($filterGroups as $group) {{ $group['label'] }}{{ $this->filterLabel($group['field']) }} @foreach ($group['options'] as $option) @php $checked = in_array($option, $this->filters[$group['field']], true); @endphp @if ($checked) @else @endif {{ $option }} @endforeach @endforeach {{ __('Clear') }}
@if ($this->sessions->count() > 0)
@foreach ($this->sessions as $session) @php $isExpanded = $this->expanded[$session->band->id] ?? false; @endphp
{{ $session->band->gpu ?? 'unknown GPU' }} @if ($session->band->gpu_count) ×{{ $session->band->gpu_count }} @endif {{ $session->band->model_id ?? 'unknown model' }} @if ($session->band->label) {{ $session->band->label }} @endif @if ($session->band->model_quant) {{ $session->band->model_quant }} @endif @if ($session->band->engine) {{ $session->band->engine }} @endif {{ $session->band->runner_name ?? 'anonymous' }} {{ optional($session->band->ran_at)->format('M j Y') }}
@php $devlogicSuites = ['softbench', 'prosebench', 'psychbench', 'depthsweep']; $sysopsSuites = ['infrabench', 'scenariobench']; $suiteFilterActive = ! empty($this->filters['suite']); $visibleSuites = $suiteFilterActive ? $session->suites->filter(fn ($r) => in_array($r->suite, $this->filters['suite'], true)) : $session->suites; $devlogicRows = $visibleSuites->filter(fn ($r) => in_array($r->suite, $devlogicSuites)); $sysopsRows = $visibleSuites->filter(fn ($r) => in_array($r->suite, $sysopsSuites)); $hasDevlogic = $devlogicRows->isNotEmpty() && ($suiteFilterActive || $session->band->devlogic_tps !== null); $hasSysops = $sysopsRows->isNotEmpty() && ($suiteFilterActive || $session->band->sysops_tps !== null); @endphp {{-- Devlogic section: total strip (click to expand) + suite rows (when expanded) --}} @if ($hasDevlogic) @unless ($suiteFilterActive) @endunless @if ($isExpanded || $suiteFilterActive)
@foreach ($devlogicRows as $row) @php $detailUrl = $row->suite === 'scenariobench' ? route('sessions.scenarios', $session->band->id) : route('runs.show', $row->run_id) . ($row->level && $row->suite === 'depthsweep' ? '?level=' . $row->level : ''); @endphp {{ $row->suite }} @if ($row->level) {{ $row->level }} @endif ✔ {{ $row->passed }} ✘ {{ $row->failed }} @if ($row->weighted_user_tps !== null) {{ number_format($row->weighted_user_tps, 1) }} @elseif ($row->gen_tps !== null) {{ number_format($row->gen_tps, 1) }} {{ __('gen') }} @endif {{ number_format($row->total_tokens) }} {{ __('per-task detail') }} → @endforeach
@endif @endif {{-- Sysops section: total strip (click to expand) + suite rows (when expanded) --}} @if ($hasSysops) @unless ($suiteFilterActive) @endunless @if ($isExpanded || $suiteFilterActive)
@foreach ($sysopsRows as $row) @php $detailUrl = $row->suite === 'scenariobench' ? route('sessions.scenarios', $session->band->id) : route('runs.show', $row->run_id) . ($row->level && $row->suite === 'depthsweep' ? '?level=' . $row->level : ''); @endphp {{ $row->suite }} @if ($row->level) {{ $row->level }} @endif ✔ {{ $row->passed }} ✘ {{ $row->failed }} @if ($row->weighted_user_tps !== null) {{ number_format($row->weighted_user_tps, 1) }} @elseif ($row->gen_tps !== null) {{ number_format($row->gen_tps, 1) }} {{ __('gen') }} @endif {{ number_format($row->total_tokens) }} {{ __('per-task detail') }} → @endforeach
@endif @endif
@endforeach
{{ $this->sessions->links() }}
@else
{{ __('No sessions') }} {{ __('No benchmark sessions match the current filters.') }}
@endif