ChatController.php

Code Hygiene Score: 95

Keine Issues gefunden.

Dependencies 14

Klassen 1

Funktionen 6

Versionen 17

Code

<?php

declare(strict_types=1);

namespace Controller\Api;

// @responsibility: REST-API für KI-Chat und Vektor-Suche

use Domain\Repository\ChunkExplorerRepositoryInterface;
use Domain\Repository\DokumentExplorerRepositoryInterface;
use Domain\Repository\SeiteExplorerRepositoryInterface;
use Framework\Controller;
use Infrastructure\AI\ChatService;
use Infrastructure\AI\OllamaService;
use Infrastructure\AI\QdrantService;

class ChatController extends Controller
{
    private ChatService $chatService;
    private OllamaService $ollamaService;
    private QdrantService $qdrantService;
    private DokumentExplorerRepositoryInterface $dokumentRepository;
    private SeiteExplorerRepositoryInterface $seiteRepository;
    private ChunkExplorerRepositoryInterface $chunkRepository;

    public function __construct(
        ChatService $chatService,
        OllamaService $ollamaService,
        QdrantService $qdrantService,
        DokumentExplorerRepositoryInterface $dokumentRepository,
        SeiteExplorerRepositoryInterface $seiteRepository,
        ChunkExplorerRepositoryInterface $chunkRepository
    ) {
        $this->chatService = $chatService;
        $this->ollamaService = $ollamaService;
        $this->qdrantService = $qdrantService;
        $this->dokumentRepository = $dokumentRepository;
        $this->seiteRepository = $seiteRepository;
        $this->chunkRepository = $chunkRepository;
    }

    /**
     * POST /api/v1/chat
     * Handle chat message
     */
    public function send(): void
    {
        $input = $this->getJsonInput();
        $question = trim($input['message'] ?? '');

        if ($question === '') {
            $this->json(['error' => 'Keine Frage angegeben'], 400);

            return;
        }

        // Release session lock before long-running LLM call
        session_write_close();

        try {
            $result = $this->askChat($question);
            $this->json($result);
        } catch (\Exception $e) {
            error_log(sprintf('[API.Chat.send] %s: %s', get_class($e), $e->getMessage()));
            $this->json(['error' => $e->getMessage()], 500);
        }
    }

    /**
     * GET /api/v1/chat/search
     * Search for relevant chunks
     */
    public function search(): void
    {
        $query = $this->getString('q');
        $limit = $this->getInt('limit', 5);

        if ($query === '') {
            $this->json(['error' => 'Keine Suchanfrage'], 400);

            return;
        }

        // Release session lock before embedding generation
        session_write_close();

        try {
            $results = $this->searchChunks($query, $limit);
            $this->json(['results' => $results]);
        } catch (\Exception $e) {
            error_log(sprintf('[API.Chat.search] %s: %s', get_class($e), $e->getMessage()));
            $this->json(['error' => $e->getMessage()], 500);
        }
    }

    /**
     * GET /api/v1/chat/stats
     * Get chat/document statistics (Doc2Vector Pipeline)
     */
    public function stats(): void
    {
        try {
            $chunkStats = $this->chunkRepository->getChunkStats();

            $stats = [
                'dokumente' => $this->dokumentRepository->countDokumente(),
                'seiten' => $this->seiteRepository->countSeiten(),
                'chunks' => $chunkStats['total'],
                'tokens' => $chunkStats['tokens'],
                'analyzed' => $chunkStats['analyzed'],
                'synced' => $chunkStats['synced'],
            ];

            $this->json($stats);
        } catch (\Exception $e) {
            error_log(sprintf('[API.Chat.stats] %s: %s', get_class($e), $e->getMessage()));
            $this->json(['error' => $e->getMessage()], 500);
        }
    }

    /**
     * Ask chat question using ChatService
     *
     * @param string $question User's question
     *
     * @return array<string, mixed> 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<int, array{id: int|string, score: float, payload: array<string, mixed>}> 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);
        }
    }
}
← Übersicht Graph