chatService = $config->createChatService(); $this->ollamaService = $config->createOllamaService(); $this->qdrantService = $config->createQdrantService(); } /** * POST /api/v1/chat * Handle chat message */ public function send(): void { $input = json_decode(file_get_contents('php://input'), true); $question = trim($input['message'] ?? ''); if ($question === '') { $this->json(['error' => 'Keine Frage angegeben'], 400); return; } try { $result = $this->askChat($question); $this->json($result); } catch (\Exception $e) { $this->json(['error' => $e->getMessage()], 500); } } /** * GET /api/v1/chat/search * Search for relevant chunks */ public function search(): void { $query = trim($_GET['q'] ?? ''); $limit = (int) ($_GET['limit'] ?? 5); if ($query === '') { $this->json(['error' => 'Keine Suchanfrage'], 400); return; } try { $results = $this->searchChunks($query, $limit); $this->json(['results' => $results]); } catch (\Exception $e) { $this->json(['error' => $e->getMessage()], 500); } } /** * GET /api/v1/chat/stats * Get chat/document statistics (Doc2Vector Pipeline) */ public function stats(): void { try { $pdo = $this->getDatabase(); $stats = [ 'dokumente' => (int) $pdo->query('SELECT COUNT(*) FROM dokumentation WHERE depth = 0')->fetchColumn(), 'seiten' => (int) $pdo->query('SELECT COUNT(*) FROM dokumentation WHERE depth > 0')->fetchColumn(), 'chunks' => (int) $pdo->query('SELECT COUNT(*) FROM dokumentation_chunks')->fetchColumn(), 'tokens' => (int) $pdo->query('SELECT COALESCE(SUM(token_count), 0) FROM dokumentation_chunks')->fetchColumn(), 'analyzed' => (int) $pdo->query("SELECT COUNT(*) FROM dokumentation_chunks WHERE analysis_status = 'completed'")->fetchColumn(), 'synced' => (int) $pdo->query('SELECT COUNT(*) FROM dokumentation_chunks WHERE qdrant_id IS NOT NULL')->fetchColumn(), ]; $this->json($stats); } catch (\Exception $e) { $this->json(['error' => $e->getMessage()], 500); } } /** * Ask chat question using ChatService * * @param string $question User's question * * @return array Chat response with answer and sources * * @throws \RuntimeException If chat service fails */ private function askChat(string $question): array { try { // Use dokumentation_chunks collection for RAG return $this->chatService->chat($question, 'claude-opus-4-5-20251101', 'dokumentation_chunks', 5); } catch (\RuntimeException $e) { throw new \RuntimeException('Chat-Service konnte nicht ausgeführt werden: ' . $e->getMessage(), 0, $e); } } /** * Search for similar chunks using vector search * * @param string $query Search query * @param int $limit Maximum number of results * * @return array}> Search results * * @throws \RuntimeException If search fails */ private function searchChunks(string $query, int $limit): array { try { $queryEmbedding = $this->ollamaService->getEmbedding($query); // Use dokumentation_chunks collection return $this->qdrantService->search($queryEmbedding, 'dokumentation_chunks', $limit); } catch (\RuntimeException $e) { throw new \RuntimeException('Suche konnte nicht ausgeführt werden: ' . $e->getMessage(), 0, $e); } } /** * Get database connection for dokumentation tables (ki_dev) */ private function getDatabase(): \PDO { return \Infrastructure\Config\DatabaseFactory::dev(); } }