feat: Enhance Support Reply Service with tone instructions and article details

- Added tone instruction retrieval to SupportReplyService.
- Improved user feedback when no relevant article is found.
- Included article URL and tone instruction in LLM prompt.
- Updated response format to include source information.
- Enhanced article management UI with search functionality and editing capabilities.
- Introduced a new API endpoint for nearest articles based on vector search.
- Added confidence badge component to display article confidence levels.
- Implemented tests for article searching, editing, and nearest article API.
- Removed obsolete .htaccess file.
This commit is contained in:
your name
2026-05-13 22:25:45 +02:00
parent c94d3f85e8
commit 9244899f9b
22 changed files with 813 additions and 123 deletions

View File

@@ -5,9 +5,7 @@ namespace Tests\Unit;
use App\Models\Article;
use App\Models\Ticket;
use App\Services\AppSettingsService;
use App\Services\Llm\LlmClientInterface;
use App\Services\SupportReplyService;
use App\Services\TicketProcessingLoggerService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Fakes\FakeLlmClient;
use Tests\Fakes\FakeTicketProcessingLoggerService;
@@ -35,6 +33,7 @@ class SupportReplyServiceTest extends TestCase
$this->assertSame('1. Doe X', $reply);
$this->assertStringContainsString('Gebruikersvraag (genormaliseerd): vraag', $llm->generatedPrompts[0]['prompt']);
$this->assertStringContainsString('spreek de klant consequent informeel aan met je/jij/jouw', $llm->generatedPrompts[0]['prompt']);
}
public function test_it_falls_back_when_llm_returns_empty(): void
@@ -56,9 +55,41 @@ class SupportReplyServiceTest extends TestCase
$this->assertStringContainsString('Gebruik het kennisbankartikel', $reply);
}
private function fakeSettings(): AppSettingsService
public function test_it_includes_formal_addressing_instruction_when_configured(): void
{
return new class extends AppSettingsService {
$llm = new FakeLlmClient;
$llm->responses = ['1. Doe X'];
$service = new SupportReplyService(
$this->fakeSettings('u'),
$llm,
new FakeTicketProcessingLoggerService
);
$ticket = Ticket::query()->create(['message' => 'vraag', 'normalized_message' => 'vraag']);
$article = Article::query()->create(['title' => 'DNS', 'content' => 'steps']);
$service->build($ticket, $article, 'relevant');
$this->assertStringContainsString('spreek de klant consequent formeel aan met u/uw', $llm->generatedPrompts[0]['prompt']);
$this->assertStringContainsString('Gebruik geen je/jij/jouw', $llm->generatedPrompts[0]['prompt']);
}
private function fakeSettings(string $tone = 'je'): AppSettingsService
{
return new class($tone) extends AppSettingsService
{
public function __construct(private readonly string $tone) {}
public function get(string $key, ?string $default = null): ?string
{
if ($key === 'tone_addressing') {
return $this->tone;
}
return $default;
}
public function getPrompt(string $key, ?string $default = null): ?string
{
return 'Prompt';