Build Laravel 13 ticket assistant with Docker, Livewire admin, and helpdesk scraper command

This commit is contained in:
SitiWeb
2026-04-29 13:11:39 +02:00
parent 141a1a3c9b
commit 3c4572bb12
58 changed files with 9377 additions and 455 deletions

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreArticleRequest;
use App\Models\Article;
use Illuminate\Http\JsonResponse;
class ArticleController extends Controller
{
public function index(): JsonResponse
{
return response()->json([
'data' => Article::query()->latest()->paginate(20),
]);
}
public function store(StoreArticleRequest $request): JsonResponse
{
$article = Article::query()->create($request->validated());
return response()->json([
'message' => 'Article created',
'data' => $article,
], 201);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Api;
use App\Exceptions\OllamaUnavailableException;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreTicketRequest;
use App\Models\Ticket;
use App\Services\EmbeddingService;
use App\Services\SemanticSearchService;
use Illuminate\Http\JsonResponse;
class TicketController extends Controller
{
public function __construct(
private readonly EmbeddingService $embeddingService,
private readonly SemanticSearchService $semanticSearchService,
) {}
public function store(StoreTicketRequest $request): JsonResponse
{
try {
$embedding = $this->embeddingService->embed($request->string('message')->toString());
} catch (OllamaUnavailableException $e) {
return response()->json([
'message' => 'Ollama is unavailable. Could not generate embedding.',
], 503);
}
$ticket = Ticket::query()->create([
'message' => $request->string('message')->toString(),
'embedding' => $embedding,
]);
try {
$result = $this->semanticSearchService->findBestArticle($ticket);
} catch (OllamaUnavailableException $e) {
return response()->json([
'message' => 'Ticket saved, but Ollama ranking is unavailable.',
'ticket_id' => $ticket->id,
], 202);
}
return response()->json([
'ticket_id' => $ticket->id,
'best_article' => $result['best_article'],
'confidence' => $result['confidence'],
'explanation' => $result['explanation'],
'top_3_candidates' => $result['top_3_candidates'],
], 201);
}
}