140 lines
No EOL
5.7 KiB
PHP
140 lines
No EOL
5.7 KiB
PHP
<?php
|
|
|
|
use App\Models\ServerConfig;
|
|
use Flux\Flux;
|
|
use Illuminate\Validation\Rule;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Component;
|
|
|
|
new #[Layout('components.layouts.app')] class extends Component {
|
|
public ?int $editingId = null;
|
|
|
|
public string $name = '';
|
|
|
|
public string $engine = 'llama.cpp';
|
|
|
|
public string $config_yaml = '';
|
|
|
|
public function create(): void
|
|
{
|
|
$this->reset(['editingId', 'name', 'config_yaml']);
|
|
$this->engine = 'llama.cpp';
|
|
$this->modal('config-form')->show();
|
|
}
|
|
|
|
public function edit(int $id): void
|
|
{
|
|
$config = ServerConfig::where('user_id', auth()->id())->findOrFail($id);
|
|
|
|
$this->editingId = $config->id;
|
|
$this->name = $config->name;
|
|
$this->engine = $config->engine;
|
|
$this->config_yaml = $config->config_yaml;
|
|
|
|
$this->modal('config-form')->show();
|
|
}
|
|
|
|
public function save(): void
|
|
{
|
|
$validated = $this->validate([
|
|
'name' => ['required', 'string', 'max:128',
|
|
Rule::unique('server_configs', 'name')->where('user_id', auth()->id())->ignore($this->editingId)],
|
|
'engine' => ['required', 'string', 'max:64'],
|
|
'config_yaml' => ['nullable', 'string', 'max:65535'],
|
|
]);
|
|
|
|
ServerConfig::updateOrCreate(
|
|
['id' => $this->editingId, 'user_id' => auth()->id()],
|
|
$validated
|
|
);
|
|
|
|
$this->modal('config-form')->close();
|
|
Flux::toast(variant: 'success', text: __('Server config saved.'));
|
|
|
|
// Config identity is part of the session grouping key — re-summarize
|
|
// so linked runs regroup immediately.
|
|
Illuminate\Support\Facades\Artisan::call('festbench:summary');
|
|
}
|
|
|
|
public function delete(int $id): void
|
|
{
|
|
ServerConfig::where('user_id', auth()->id())->whereKey($id)->firstOrFail()->delete();
|
|
|
|
Flux::toast(variant: 'success', text: __('Server config deleted.'));
|
|
Illuminate\Support\Facades\Artisan::call('festbench:summary');
|
|
}
|
|
|
|
public function with(): array
|
|
{
|
|
return [
|
|
'configs' => ServerConfig::where('user_id', auth()->id())->orderBy('name')->get(),
|
|
];
|
|
}
|
|
};
|
|
?>
|
|
|
|
<div class="flex flex-col gap-6">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<flux:heading size="xl">{{ __('Server Configs') }}</flux:heading>
|
|
<flux:subheading>{{ __('Named harness configs. Runs link to them; the config identity keeps differently-tuned sessions in their own groups.') }}</flux:subheading>
|
|
</div>
|
|
<flux:button variant="primary" icon="plus" wire:click="create">{{ __('New config') }}</flux:button>
|
|
</div>
|
|
|
|
@if ($configs->isNotEmpty())
|
|
<flux:table>
|
|
<flux:table.columns>
|
|
<flux:table.column>{{ __('Name') }}</flux:table.column>
|
|
<flux:table.column>{{ __('Engine') }}</flux:table.column>
|
|
<flux:table.column class="text-right">{{ __('Linked runs') }}</flux:table.column>
|
|
<flux:table.column></flux:table.column>
|
|
</flux:table.columns>
|
|
|
|
<flux:table.rows>
|
|
@foreach ($configs as $config)
|
|
<flux:table.row :key="$config->id">
|
|
<flux:table.cell>{{ $config->name }}</flux:table.cell>
|
|
<flux:table.cell><flux:badge size="xs" color="sky">{{ $config->engine }}</flux:badge></flux:table.cell>
|
|
<flux:table.cell class="text-right font-mono text-sm">{{ $config->runs()->count() }}</flux:table.cell>
|
|
<flux:table.cell>
|
|
<div class="flex justify-end gap-1">
|
|
<flux:button size="xs" variant="ghost" icon="pencil-square" wire:click="edit({{ $config->id }})">{{ __('Edit') }}</flux:button>
|
|
<flux:button size="xs" variant="ghost" color="danger" icon="trash-2" wire:click="delete({{ $config->id }})">{{ __('Delete') }}</flux:button>
|
|
</div>
|
|
</flux:table.cell>
|
|
</flux:table.row>
|
|
@endforeach
|
|
</flux:table.rows>
|
|
</flux:table>
|
|
@else
|
|
<flux:callout variant="brand">
|
|
<flux:callout.heading>{{ __('No server configs yet') }}</flux:callout.heading>
|
|
<flux:callout.text>{{ __('Save your llama.cpp / vllm args as a named config so runs group by their tuning.') }}</flux:callout.text>
|
|
</flux:callout>
|
|
@endif
|
|
|
|
<flux:modal name="config-form" class="w-2xl">
|
|
<div class="flex flex-col gap-4">
|
|
<div>
|
|
<flux:heading size="lg">{{ $editingId ? __('Edit config') : __('New config') }}</flux:heading>
|
|
<flux:subheading>{{ __('Paste your harness args — the full server invocation (YAML or argv list).') }}</flux:subheading>
|
|
</div>
|
|
|
|
<flux:input wire:model="name" :label="__('Name')" placeholder="{{ __('llama.cpp DFlash spec-decode, 131k ctx') }}" />
|
|
|
|
<flux:select wire:model="engine" :label="__('Engine')">
|
|
@foreach (['llama.cpp', 'vllm', 'unknown', 'other'] as $engine)
|
|
<flux:select.option value="{{ $engine }}">{{ $engine }}</flux:select.option>
|
|
@endforeach
|
|
</flux:select>
|
|
|
|
<flux:textarea wire:model="config_yaml" :label="__('Harness args')" rows="12" class="font-mono text-xs" placeholder="--host 0.0.0.0 --port 8000 -hf model:Q5_K_M -ngl 99 ..." />
|
|
|
|
<div class="flex justify-end gap-2">
|
|
<flux:button variant="ghost" wire:click="$toggle('config-form')">{{ __('Cancel') }}</flux:button>
|
|
<flux:button variant="primary" wire:click="save">{{ __('Save config') }}</flux:button>
|
|
</div>
|
|
</div>
|
|
</flux:modal>
|
|
</div>
|