Labels now define distinct sessions — different power-cap configs (asus_210w, sapphire_210w, asus_uncapped) render as separate bands instead of merging into one. The label badge displays alongside quant/engine/config badges rather than replacing them. Scenario drilldown filters by label to prevent cross-band mixing. Data fix: runs 22-32 (asus_210w) and 43-46 (asus_uncapped) reassigned from Sapphire (gpu_id=11) to ASUS (gpu_id=12) — they were run on the ASUS card but imported with the Sapphire GPU identity.
169 lines
No EOL
8.7 KiB
PHP
169 lines
No EOL
8.7 KiB
PHP
<?php
|
|
|
|
use App\Livewire\IncludesSorting;
|
|
use App\Models\BenchmarkRun;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Livewire\Attributes\Computed;
|
|
use Livewire\Attributes\Layout;
|
|
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', 'gen_tps',
|
|
];
|
|
|
|
public ?int $sessionId = null;
|
|
|
|
public function mount(int $session): void
|
|
{
|
|
$this->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));
|
|
})
|
|
->where(function ($q) use ($band) {
|
|
if ($band->label !== null && $band->label !== '') {
|
|
$q->where('label', $band->label);
|
|
} else {
|
|
$q->where(function ($inner) {
|
|
$inner->whereNull('label')->orWhere('label', '');
|
|
});
|
|
}
|
|
})
|
|
->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');
|
|
|
|
$column = in_array($this->sortBy, self::SORTABLE, true) ? $this->sortBy : 'scenario';
|
|
$direction = $this->sortDirection === 'asc' ? 'asc' : 'desc';
|
|
|
|
$query = DB::table('scenario_runs')
|
|
->whereIn('benchmark_run_id', $runIds);
|
|
|
|
if ($column === 'gen_tps') {
|
|
$query->orderByRaw('gen_tokens / nullif(wall, 0) '.$direction);
|
|
} else {
|
|
$query->orderBy($column, $direction);
|
|
}
|
|
|
|
return $query->orderBy('id')->get();
|
|
}
|
|
};
|
|
?>
|
|
|
|
<div class="flex flex-col gap-6">
|
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
|
<div>
|
|
<flux:heading size="xl">{{ __('Scenario results') }}</flux:heading>
|
|
<flux:subheading>
|
|
@if ($this->band->label)
|
|
<flux:badge size="xs" color="amber">{{ $this->band->label }}</flux:badge>
|
|
@endif
|
|
<span class="text-sm">{{ $this->band->model_id ?? 'unknown model' }}</span>
|
|
@if ($this->band->model_quant)
|
|
<flux:badge size="xs" color="zinc">{{ $this->band->model_quant }}</flux:badge>
|
|
@endif
|
|
<span class="text-sm text-zinc-500">{{ $this->band->gpu ?? 'unknown GPU' }}</span>
|
|
</flux:subheading>
|
|
</div>
|
|
|
|
<flux:button variant="ghost" icon="arrow-left" href="{{ route('home') }}" wire:navigate>{{ __('Sessions') }}</flux:button>
|
|
</div>
|
|
|
|
<flux:table>
|
|
<flux:table.columns>
|
|
<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.column sortable :sorted="$sortBy === 'gen_tps'" :direction="$sortDirection" wire:click="sort('gen_tps')" class="text-center">{{ __('Gen tok/s') }}</flux:table.column>
|
|
</flux:table.columns>
|
|
|
|
<flux:table.rows>
|
|
@foreach ($this->results as $result)
|
|
<flux:table.row :key="$result->id">
|
|
<flux:table.cell>{{ $result->scenario }}</flux:table.cell>
|
|
<flux:table.cell>{{ $result->level ?? '—' }}</flux:table.cell>
|
|
<flux:table.cell>
|
|
@if ($result->ok)
|
|
<flux:icon name="check-circle" variant="solid" class="size-4 text-emerald-500" />
|
|
@elseif ($result->ok === null)
|
|
<span class="text-zinc-400">—</span>
|
|
@else
|
|
<flux:icon name="x-circle" variant="solid" class="size-4 text-red-500" />
|
|
@endif
|
|
</flux:table.cell>
|
|
<flux:table.cell><flux:badge size="xs" color="{{ $result->outcome === 'passed' ? 'emerald' : 'rose' }}">{{ $result->outcome ?? '—' }}</flux:badge></flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->wall, 1) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->turns) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->commands) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->submissions) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->rejected) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->t_first_sub, 1) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ $result->t_pass !== null ? number_format($result->t_pass, 1) : '—' }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ number_format($result->gen_tokens) }}</flux:table.cell>
|
|
<flux:table.cell class="text-center font-mono text-sm">{{ $result->wall > 0 && $result->gen_tokens > 0 ? number_format($result->gen_tokens / $result->wall, 1) : '—' }}</flux:table.cell>
|
|
</flux:table.row>
|
|
@endforeach
|
|
</flux:table.rows>
|
|
</flux:table>
|
|
</div>
|