Add admin views for quick replies, settings, and ticket details
- 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.
This commit is contained in:
199
app/Livewire/Admin/SettingsPage.php
Normal file
199
app/Livewire/Admin/SettingsPage.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user