- Created `quick-replies.blade.php` for managing quick replies. - Added `settings.blade.php` for admin settings management. - Implemented `ticket-show.blade.php` to display ticket details. - Introduced `timeline-card.blade.php` component for displaying timeline information. Enhance quick reply management functionality - Developed `quick-reply-manager.blade.php` for creating and editing quick replies. - Integrated Livewire for dynamic interaction and validation. Implement settings page for AI configuration - Created `settings-page.blade.php` for managing AI settings, including prompts and provider instances. - Added functionality for managing models and embeddings. Add ticket show functionality with real-time updates - Implemented ticket details view with processing status and tool call logs. - Added support for displaying article suggestions and error messages. Create unit tests for AI classifier and domain info tool - Added `AIClassifierServiceTest.php` to validate AI classifier functionality. - Implemented `DomainInfoToolTest.php` for domain parameter validation. - Created `OxxaClientTest.php` to test API interactions and password hashing.
200 lines
6.4 KiB
PHP
200 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Admin;
|
|
|
|
use App\Services\AppSettingsService;
|
|
use App\Services\ArticleEmbeddingMaintenanceService;
|
|
use App\Services\LlmModelCatalogService;
|
|
use Illuminate\Support\Str;
|
|
use Livewire\Component;
|
|
|
|
class SettingsPage extends Component
|
|
{
|
|
public string $activeTab = 'process';
|
|
|
|
public string $tone_addressing = 'je';
|
|
|
|
public string $activeProviderInstanceId = 'ollama_default';
|
|
|
|
public int $llm_timeout = 300;
|
|
|
|
public array $promptValues = [];
|
|
|
|
public array $providerInstances = [];
|
|
|
|
public array $modelValues = [];
|
|
|
|
public array $availableModels = [];
|
|
|
|
public array $embeddingStats = [];
|
|
|
|
public ?string $modelLoadError = null;
|
|
|
|
public array $processSteps = [];
|
|
|
|
public array $providerDefinitions = [];
|
|
|
|
public array $modelTasks = [];
|
|
|
|
public function mount(AppSettingsService $settings): void
|
|
{
|
|
$all = $settings->all();
|
|
$providerSettings = $settings->providerSettings();
|
|
$this->tone_addressing = (string) ($all['tone_addressing'] ?? 'je');
|
|
$this->activeProviderInstanceId = (string) ($providerSettings['active_instance_id'] ?? 'ollama_default');
|
|
$this->llm_timeout = (int) ($all['llm.timeout'] ?? 300);
|
|
$this->promptValues = $settings->promptValues();
|
|
$this->providerInstances = $providerSettings['instances'] ?? $settings->defaultProviderInstances();
|
|
$this->modelValues = $settings->modelSettings();
|
|
$this->processSteps = $settings->processSteps();
|
|
$this->providerDefinitions = $settings->providerDefinitions();
|
|
$this->modelTasks = $settings->modelTasks();
|
|
$this->refreshEmbeddingStats();
|
|
$this->loadModels();
|
|
}
|
|
|
|
public function setTab(string $tab): void
|
|
{
|
|
if (in_array($tab, ['process', 'providers', 'models', 'embeddings'], true)) {
|
|
$this->activeTab = $tab;
|
|
|
|
if ($tab === 'embeddings') {
|
|
$this->refreshEmbeddingStats();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function refreshEmbeddingStats(): void
|
|
{
|
|
$this->embeddingStats = app(ArticleEmbeddingMaintenanceService::class)->stats();
|
|
}
|
|
|
|
public function reindexMissingEmbeddings(): void
|
|
{
|
|
$count = app(ArticleEmbeddingMaintenanceService::class)->dispatchReindex(false);
|
|
$this->refreshEmbeddingStats();
|
|
session()->flash('saved', "{$count} artikelen zonder chunks zijn in de queue geplaatst.");
|
|
}
|
|
|
|
public function reindexAllEmbeddings(): void
|
|
{
|
|
$count = app(ArticleEmbeddingMaintenanceService::class)->dispatchReindex(true);
|
|
$this->refreshEmbeddingStats();
|
|
session()->flash('saved', "{$count} artikelen zijn in de queue geplaatst voor volledige herindex.");
|
|
}
|
|
|
|
public function addProviderInstance(): void
|
|
{
|
|
$id = 'provider_'.Str::uuid()->toString();
|
|
$this->providerInstances[] = [
|
|
'id' => $id,
|
|
'name' => 'Nieuwe provider',
|
|
'type' => 'lmstudio',
|
|
'base_url' => 'http://localhost:1234',
|
|
'chat_model' => '',
|
|
'embedding_model' => '',
|
|
];
|
|
$this->activeProviderInstanceId = $id;
|
|
$this->availableModels = [];
|
|
$this->modelLoadError = null;
|
|
}
|
|
|
|
public function removeProviderInstance(string $id): void
|
|
{
|
|
if (count($this->providerInstances) <= 1) {
|
|
return;
|
|
}
|
|
|
|
$this->providerInstances = array_values(array_filter(
|
|
$this->providerInstances,
|
|
fn (array $instance) => ($instance['id'] ?? null) !== $id
|
|
));
|
|
|
|
if ($this->activeProviderInstanceId === $id) {
|
|
$this->activeProviderInstanceId = (string) ($this->providerInstances[0]['id'] ?? '');
|
|
}
|
|
|
|
$this->loadModels();
|
|
}
|
|
|
|
public function setActiveProviderInstance(string $id): void
|
|
{
|
|
$ids = array_column($this->providerInstances, 'id');
|
|
if (in_array($id, $ids, true)) {
|
|
$this->activeProviderInstanceId = $id;
|
|
$this->loadModels();
|
|
}
|
|
}
|
|
|
|
public function loadModels(bool $refresh = false): void
|
|
{
|
|
$instance = $this->activeProviderInstance();
|
|
if ($instance === null) {
|
|
$this->availableModels = [];
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$this->availableModels = app(LlmModelCatalogService::class)->modelsFor($instance, $refresh);
|
|
$this->modelLoadError = null;
|
|
} catch (\Throwable $e) {
|
|
$this->availableModels = [];
|
|
$this->modelLoadError = $e->getMessage();
|
|
}
|
|
}
|
|
|
|
public function refreshModels(): void
|
|
{
|
|
$this->loadModels(true);
|
|
}
|
|
|
|
public function save(AppSettingsService $settings): void
|
|
{
|
|
$this->validate([
|
|
'tone_addressing' => ['required', 'in:je,u'],
|
|
'activeProviderInstanceId' => ['required', 'string'],
|
|
'llm_timeout' => ['required', 'integer', 'min:5', 'max:600'],
|
|
'promptValues' => ['array'],
|
|
'promptValues.*' => ['required', 'string', 'min:10'],
|
|
'providerInstances' => ['array', 'min:1'],
|
|
'providerInstances.*.id' => ['required', 'string'],
|
|
'providerInstances.*.name' => ['required', 'string', 'min:1'],
|
|
'providerInstances.*.type' => ['required', 'in:ollama,lmstudio'],
|
|
'providerInstances.*.base_url' => ['required', 'url'],
|
|
'providerInstances.*.chat_model' => ['nullable', 'string'],
|
|
'providerInstances.*.embedding_model' => ['nullable', 'string'],
|
|
'modelValues' => ['array'],
|
|
'modelValues.*' => ['required', 'string', 'min:1'],
|
|
]);
|
|
|
|
$settings->saveStructuredSettings(
|
|
promptValues: $this->promptValues,
|
|
providerInstances: $this->providerInstances,
|
|
activeProviderInstanceId: $this->activeProviderInstanceId,
|
|
modelValues: $this->modelValues,
|
|
timeout: $this->llm_timeout,
|
|
tone: $this->tone_addressing,
|
|
);
|
|
|
|
session()->flash('saved', 'Settings opgeslagen.');
|
|
$this->refreshEmbeddingStats();
|
|
}
|
|
|
|
private function activeProviderInstance(): ?array
|
|
{
|
|
foreach ($this->providerInstances as $instance) {
|
|
if (($instance['id'] ?? null) === $this->activeProviderInstanceId) {
|
|
return $instance;
|
|
}
|
|
}
|
|
|
|
return $this->providerInstances[0] ?? null;
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.admin.settings-page');
|
|
}
|
|
}
|