festbench-web/resources/views/livewire/settings/server-configs.blade.php
PeterChrz 10d4e0aef8
Rename Server Config to Inference Args in the UI
The stored blob is the inference server invocation, and it is no longer only
a local server's: a hosted provider is addressed rather than configured.
Display text only -- the table, routes and relations keep their names, so
there is no migration and no broken links.
2026-09-21 08:02:34 -04:00

140 lines
No EOL
5.8 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: __('Inference args 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: __('Inference args 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">{{ __('Inference Args') }}</flux:heading>
<flux:subheading>{{ __('Named inference server invocations. Runs link to them; the identity keeps differently-tuned sessions in their own groups.') }}</flux:subheading>
</div>
<flux:button variant="primary" icon="plus" wire:click="create">{{ __('New inference args') }}</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 inference args yet') }}</flux:callout.heading>
<flux:callout.text>{{ __('Save your llama.cpp / vllm / hosted-provider invocation as a named entry 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 inference args') }}</flux:heading>
<flux:subheading>{{ __('Paste the full inference server invocation (YAML or argv list), or how a hosted model was addressed.') }}</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="__('Inference 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>