Initial commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Chatbot;
|
||||
use App\Models\ChatbotHistory;
|
||||
use App\Services\DocumentIngestionService;
|
||||
use App\Services\WeaviateService;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
|
||||
class AdminDashboard extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public Chatbot $chatbot;
|
||||
public string $url = '';
|
||||
public $pdfFile;
|
||||
public string $statusMessage = '';
|
||||
public string $statusType = ''; // 'success', 'error', 'info'
|
||||
public string $systemPrompt = '';
|
||||
public string $chatbotName = '';
|
||||
public array $history = [];
|
||||
public bool $enableVision = true;
|
||||
public bool $crawlEntireSite = false;
|
||||
public int $maxPages = 30;
|
||||
|
||||
public function mount(Chatbot $chatbot)
|
||||
{
|
||||
$this->chatbot = $chatbot;
|
||||
$this->systemPrompt = $chatbot->system_prompt ?? '';
|
||||
$this->chatbotName = $chatbot->name;
|
||||
$this->loadHistory();
|
||||
}
|
||||
|
||||
protected function loadHistory()
|
||||
{
|
||||
$this->history = $this->chatbot->histories()
|
||||
->latest()
|
||||
->limit(15)
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
return [
|
||||
'action' => $item->action,
|
||||
'target' => $item->target,
|
||||
'status' => $item->status,
|
||||
'timestamp' => $item->created_at->format('d/m/Y H:i')
|
||||
];
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
public function saveSystemPrompt()
|
||||
{
|
||||
$this->validate([
|
||||
'systemPrompt' => 'required|string|max:5000',
|
||||
'chatbotName' => 'required|string|max:100',
|
||||
]);
|
||||
|
||||
$this->chatbot->update([
|
||||
'name' => $this->chatbotName,
|
||||
'system_prompt' => $this->systemPrompt
|
||||
]);
|
||||
|
||||
$this->logAction("Configuration Chatbot", "Nom modifié en '{$this->chatbotName}'", "success");
|
||||
|
||||
$this->statusMessage = "Les paramètres du chatbot ont été mis à jour avec succès !";
|
||||
$this->statusType = 'success';
|
||||
|
||||
$this->dispatch('settings-updated');
|
||||
}
|
||||
|
||||
protected function logAction(string $action, string $target, string $status)
|
||||
{
|
||||
$this->chatbot->histories()->create([
|
||||
'action' => $action,
|
||||
'target' => $target,
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
$this->loadHistory();
|
||||
}
|
||||
|
||||
public function ingestUrl(DocumentIngestionService $ingestionService)
|
||||
{
|
||||
$this->validate([
|
||||
'url' => 'required|url',
|
||||
'maxPages' => 'required|integer|min:1|max:100',
|
||||
]);
|
||||
|
||||
if ($this->crawlEntireSite) {
|
||||
$this->statusMessage = "Exploration et indexation de l'ensemble du site en cours (cela peut prendre quelques minutes)...";
|
||||
$this->statusType = 'info';
|
||||
|
||||
// Increase time limit for crawler execution
|
||||
@set_time_limit(300);
|
||||
|
||||
$indexedCount = $ingestionService->crawlAndIngestSite($this->url, (string) $this->chatbot->id, $this->maxPages);
|
||||
|
||||
if ($indexedCount > 0) {
|
||||
$this->logAction("Crawler Site Web", "{$this->url} ({$indexedCount} pages indexées)", "success");
|
||||
$this->statusMessage = "Le site a été exploré et {$indexedCount} pages ont été indexées avec succès !";
|
||||
$this->statusType = 'success';
|
||||
$this->url = '';
|
||||
} else {
|
||||
$this->logAction("Crawler Site Web", $this->url, "failed");
|
||||
$this->statusMessage = "L'exploration du site a échoué. Veuillez vérifier les logs et l'URL.";
|
||||
$this->statusType = 'error';
|
||||
}
|
||||
} else {
|
||||
$this->statusMessage = "Indexation de l'URL en cours...";
|
||||
$this->statusType = 'info';
|
||||
|
||||
$success = $ingestionService->ingestUrl($this->url, (string) $this->chatbot->id);
|
||||
|
||||
if ($success) {
|
||||
$this->logAction("Indexation URL", $this->url, "success");
|
||||
$this->statusMessage = "L'URL a été indexée avec succès dans Weaviate !";
|
||||
$this->statusType = 'success';
|
||||
$this->url = '';
|
||||
} else {
|
||||
$this->logAction("Indexation URL", $this->url, "failed");
|
||||
$this->statusMessage = "Impossible d'indexer l'URL. Veuillez vérifier les logs et l'état de Weaviate.";
|
||||
$this->statusType = 'error';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function ingestPdf(DocumentIngestionService $ingestionService)
|
||||
{
|
||||
$this->validate([
|
||||
'pdfFile' => 'required|file|mimes:pdf|max:10240', // Max 10MB
|
||||
]);
|
||||
|
||||
$this->statusMessage = "Indexation du fichier PDF en cours...";
|
||||
$this->statusType = 'info';
|
||||
|
||||
$originalName = $this->pdfFile->getClientOriginalName();
|
||||
$storedPath = $this->pdfFile->storeAs('temp', $originalName);
|
||||
$absolutePath = storage_path('app/private/' . $storedPath);
|
||||
|
||||
if (!file_exists($absolutePath)) {
|
||||
$absolutePath = storage_path('app/' . $storedPath);
|
||||
}
|
||||
|
||||
$success = $ingestionService->ingestPdf($absolutePath, $originalName, (string) $this->chatbot->id, $this->enableVision);
|
||||
|
||||
// Cleanup
|
||||
if (file_exists($absolutePath)) {
|
||||
@unlink($absolutePath);
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
$this->logAction("Indexation PDF", $originalName, "success");
|
||||
$this->statusMessage = "Le fichier PDF '{$originalName}' a été indexé avec succès !";
|
||||
$this->statusType = 'success';
|
||||
$this->pdfFile = null;
|
||||
} else {
|
||||
$this->logAction("Indexation PDF", $originalName, "failed");
|
||||
$this->statusMessage = "Une erreur s'est produite lors de l'indexation du PDF.";
|
||||
$this->statusType = 'error';
|
||||
}
|
||||
}
|
||||
|
||||
public function clearDatabase(WeaviateService $weaviateService)
|
||||
{
|
||||
$success = $weaviateService->clearChatbotData((string) $this->chatbot->id);
|
||||
|
||||
if ($success) {
|
||||
$this->logAction("Réinitialisation Index", "Suppression de toutes les données vectorielles", "success");
|
||||
$this->statusMessage = "La base de données vectorielle du chatbot a été réinitialisée avec succès.";
|
||||
$this->statusType = 'success';
|
||||
} else {
|
||||
$this->logAction("Réinitialisation Index", "Suppression de toutes les données vectorielles", "failed");
|
||||
$this->statusMessage = "Impossible de vider les données Weaviate.";
|
||||
$this->statusType = 'error';
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.admin-dashboard');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Chatbot as ChatbotModel;
|
||||
use App\Services\MistralService;
|
||||
use App\Services\WeaviateService;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\On;
|
||||
|
||||
class Chatbot extends Component
|
||||
{
|
||||
public ChatbotModel $chatbot;
|
||||
public string $userMessage = '';
|
||||
public array $messages = [];
|
||||
public bool $isSending = false;
|
||||
public string $chatbotName = '';
|
||||
|
||||
public function mount(ChatbotModel $chatbot)
|
||||
{
|
||||
$this->chatbot = $chatbot;
|
||||
$this->loadSettings();
|
||||
|
||||
// Load messages from session specific to this chatbot's slug
|
||||
$this->messages = session()->get("chat_messages_{$chatbot->slug}", [
|
||||
[
|
||||
'role' => 'assistant',
|
||||
'content' => "Bonjour ! Je suis votre assistant virtuel RAG. Je peux répondre à vos questions en me basant sur le contenu de ce site. Que puis-je faire pour vous aujourd'hui ?"
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[On('settings-updated')]
|
||||
public function loadSettings()
|
||||
{
|
||||
// Reload chatbot model from database to get fresh settings
|
||||
$this->chatbot->refresh();
|
||||
$this->chatbotName = $this->chatbot->name;
|
||||
}
|
||||
|
||||
public function sendMessage(WeaviateService $weaviate, MistralService $mistral)
|
||||
{
|
||||
$this->validate([
|
||||
'userMessage' => 'required|string|max:1000',
|
||||
]);
|
||||
|
||||
$query = trim($this->userMessage);
|
||||
|
||||
// 1. Add user message
|
||||
$this->messages[] = [
|
||||
'role' => 'user',
|
||||
'content' => $query
|
||||
];
|
||||
|
||||
$this->userMessage = '';
|
||||
$this->isSending = true;
|
||||
|
||||
// Save to session immediately for visual update
|
||||
session()->put("chat_messages_{$this->chatbot->slug}", $this->messages);
|
||||
|
||||
$this->dispatch('message-sent');
|
||||
}
|
||||
|
||||
public function getResponse(WeaviateService $weaviate, MistralService $mistral)
|
||||
{
|
||||
if (!$this->isSending) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lastMessage = end($this->messages);
|
||||
if (!$lastMessage || $lastMessage['role'] !== 'user') {
|
||||
$this->isSending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$query = $lastMessage['content'];
|
||||
|
||||
// 2. Search Weaviate for context using the chatbot ID
|
||||
$chunks = $weaviate->search($query, (string) $this->chatbot->id, 4);
|
||||
|
||||
// 3. Generate response via Mistral AI
|
||||
$history = array_slice($this->messages, 0, -1);
|
||||
$response = $mistral->generateResponse($query, $chunks, $history, $this->chatbot->system_prompt);
|
||||
|
||||
// Extract sources
|
||||
$sources = [];
|
||||
if (!empty($chunks)) {
|
||||
foreach ($chunks as $chunk) {
|
||||
if (isset($chunk['source'])) {
|
||||
$sources[] = $chunk['source'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sources = array_unique($sources);
|
||||
|
||||
// 4. Add assistant response
|
||||
$this->messages[] = [
|
||||
'role' => 'assistant',
|
||||
'content' => $response ?? "Désolé, je n'ai pas pu obtenir de réponse.",
|
||||
'sources' => $sources
|
||||
];
|
||||
|
||||
$this->isSending = false;
|
||||
|
||||
// Save to session
|
||||
session()->put("chat_messages_{$this->chatbot->slug}", $this->messages);
|
||||
}
|
||||
|
||||
public function clearChat()
|
||||
{
|
||||
session()->forget("chat_messages_{$this->chatbot->slug}");
|
||||
$this->mount($this->chatbot);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.chatbot');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\Chatbot;
|
||||
use App\Services\WeaviateService;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
|
||||
class ChatbotManager extends Component
|
||||
{
|
||||
public string $name = '';
|
||||
public string $slug = '';
|
||||
public string $statusMessage = '';
|
||||
public string $statusType = '';
|
||||
|
||||
public function createChatbot()
|
||||
{
|
||||
// Auto-generate slug if empty
|
||||
if (empty($this->slug)) {
|
||||
$this->slug = Str::slug($this->name);
|
||||
} else {
|
||||
$this->slug = Str::slug($this->slug);
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'name' => 'required|string|max:100',
|
||||
'slug' => 'required|string|unique:chatbots,slug|max:100',
|
||||
]);
|
||||
|
||||
$chatbot = Chatbot::create([
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
]);
|
||||
|
||||
$this->name = '';
|
||||
$this->slug = '';
|
||||
$this->statusMessage = "Le chatbot '{$chatbot->name}' a été créé avec succès !";
|
||||
$this->statusType = 'success';
|
||||
}
|
||||
|
||||
public function deleteChatbot(int $id, WeaviateService $weaviateService)
|
||||
{
|
||||
$chatbot = Chatbot::find($id);
|
||||
if ($chatbot) {
|
||||
$name = $chatbot->name;
|
||||
|
||||
// Delete data in Weaviate for this chatbot
|
||||
$weaviateService->clearChatbotData((string) $chatbot->id);
|
||||
|
||||
// Delete database records
|
||||
$chatbot->delete();
|
||||
|
||||
$this->statusMessage = "Le chatbot '{$name}' et toutes ses données vectorielles ont été supprimés.";
|
||||
$this->statusType = 'success';
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$chatbots = Chatbot::latest()->get();
|
||||
return view('livewire.chatbot-manager', compact('chatbots'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user