Protokoll #29369

ID29369
Zeitstempel2025-12-29 09:16:48.163031
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuspending
Tokens2,650 (Input: 2,650, Output: 0)
Dauer-
Request-Zeit2025-12-29 09:16:48.163031
Response-Zeit-

Request

{
    "event": "PostToolUse",
    "tool_name": "Read",
    "tool_response": {
        "type": "text",
        "file": {
            "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ChatController.php",
            "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Controller;\n\n\/\/ @responsibility: HTTP-Endpunkte für KI-Chat (Sessions, Nachrichten, Export)\n\nuse Domain\\Factory\\ChatSessionFactory;\nuse Domain\\Service\\ModelRegistryInterface;\nuse Framework\\Controller;\nuse Infrastructure\\Formatting\\ChatMessageFormatter;\nuse UseCases\\Chat\\CreateChatSessionUseCaseInterface;\nuse UseCases\\Chat\\DeleteChatSessionUseCaseInterface;\nuse UseCases\\Chat\\ExportChatSessionUseCase;\nuse UseCases\\Chat\\GetChatSessionUseCaseInterface;\nuse UseCases\\Chat\\SendChatMessageUseCase;\nuse UseCases\\Chat\\StreamingChatMessageUseCase;\nuse UseCases\\Chat\\UpdateChatSessionUseCaseInterface;\n\nclass ChatController extends Controller\n{\n    public function __construct(\n        private CreateChatSessionUseCaseInterface $createSessionUseCase,\n        private GetChatSessionUseCaseInterface $getSessionUseCase,\n        private UpdateChatSessionUseCaseInterface $updateSessionUseCase,\n        private DeleteChatSessionUseCaseInterface $deleteSessionUseCase,\n        private SendChatMessageUseCase $messageUseCase,\n        private StreamingChatMessageUseCase $streamingUseCase,\n        private ChatMessageFormatter $formatter,\n        private ExportChatSessionUseCase $exportUseCase,\n        private ModelRegistryInterface $modelRegistry\n    ) {\n    }\n\n    public function index(): void\n    {\n        $uuid = $this->createSessionUseCase->createSession();\n        header('Location: \/chat\/' . $uuid);\n        exit;\n    }\n\n    public function show(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session === null) {\n            header('Location: \/chat');\n            exit;\n        }\n\n        \/\/ Convert entities to arrays for views\n        $messages = $this->getSessionUseCase->getMessages($session->getId() ?? 0);\n        $messagesArray = array_map(fn ($m) => $m->toArray(), $messages);\n\n        $this->view('chat.index', [\n            'title' => $session->getTitle() ?? 'KI-Chat',\n            'session' => ChatSessionFactory::toArray($session),\n            'messages' => $messagesArray,\n            'sessions' => $this->getSessionUseCase->getAllSessionsWithStats(),\n            'authorProfiles' => $this->getSessionUseCase->getAuthorProfiles(),\n            'systemPrompts' => $this->getSessionUseCase->getSystemPrompts(),\n            'outputStructures' => $this->getSessionUseCase->getOutputStructures(),\n            'collections' => $this->getSessionUseCase->getAvailableCollections(),\n            'models' => $this->modelRegistry->getChatModels(),\n            'defaultModel' => $this->modelRegistry->getDefaultChatModel(),\n        ]);\n    }\n\n    public function sessionList(): void\n    {\n        $this->view('chat.partials.session-list', [\n            'sessions' => $this->getSessionUseCase->getAllSessionsWithStats(),\n            'currentUuid' => $this->getString('current') ?: null,\n        ]);\n    }\n\n    public function message(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session === null) {\n            $this->view('chat.partials.error', ['error' => 'Session nicht gefunden.']);\n\n            return;\n        }\n\n        $params = $this->extractChatParams($session);\n        $this->updateSessionIfChanged(\n            $session,\n            $params['model'],\n            $params['collections'],\n            $params['contextLimit'],\n            $params['authorProfileId'],\n            $params['temperature'],\n            $params['maxTokens']\n        );\n\n        if ($params['question'] === '') {\n            $this->view('chat.partials.error', ['error' => 'Bitte gib eine Frage ein.']);\n\n            return;\n        }\n\n        $validation = $this->validateCollections($params['collections']);\n        if (!$validation['valid']) {\n            $this->view('chat.partials.error', [\n                'error' => 'Collection-Fehler: ' . $validation['error'],\n                'details' => 'Bitte wähle nur Collections mit gleichem Embedding-Modell.',\n            ]);\n\n            return;\n        }\n\n        $response = $this->messageUseCase->execute(\n            sessionUuid: $uuid,\n            message: $params['question'],\n            model: $params['model'],\n            collections: $params['collections'],\n            contextLimit: $params['contextLimit'],\n            authorProfileId: $params['authorProfileId'],\n            systemPromptId: $params['systemPromptId'],\n            temperature: $params['temperature'],\n            maxTokens: $params['maxTokens'],\n            structureId: $params['structureId'],\n            qualityCheck: $params['qualityCheck']\n        );\n\n        if ($response->hasError()) {\n            $this->view('chat.partials.error', ['error' => $response->getError()]);\n\n            return;\n        }\n\n        $result = $response->toArray();\n        $this->view('chat.partials.response', [\n            'question' => $params['question'],\n            'result' => $result,\n            'model' => $params['model'],\n            'formattedAnswer' => $this->formatter->formatAnswer($result['answer'] ?? ''),\n        ]);\n    }\n\n    \/**\n     * Streaming message endpoint with SSE progress events\n     *\/\n    public function messageStream(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session === null) {\n            $this->sseError('Session nicht gefunden.');\n\n            return;\n        }\n\n        $params = $this->extractChatParams($session);\n        $this->updateSessionIfChanged(\n            $session,\n            $params['model'],\n            $params['collections'],\n            $params['contextLimit'],\n            $params['authorProfileId'],\n            $params['temperature'],\n            $params['maxTokens']\n        );\n\n        if ($params['question'] === '') {\n            $this->sseError('Bitte gib eine Frage ein.');\n\n            return;\n        }\n\n        $validation = $this->validateCollections($params['collections']);\n        if (!$validation['valid']) {\n            $this->sseError('Collection-Fehler: ' . $validation['error']);\n\n            return;\n        }\n\n        $this->setupSseStream();\n        $this->setupProgressCallback();\n\n        \/\/ Execute with streaming\n        $response = $this->streamingUseCase->execute(\n            sessionUuid: $uuid,\n            message: $params['question'],\n            model: $params['model'],\n            collections: $params['collections'],\n            contextLimit: $params['contextLimit'],\n            authorProfileId: $params['authorProfileId'],\n            systemPromptId: $params['systemPromptId'],\n            temperature: $params['temperature'],\n            maxTokens: $params['maxTokens'],\n            structureId: $params['structureId'],\n            qualityCheck: $params['qualityCheck'],\n            requestIp: $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'\n        );\n\n        \/\/ Send final result\n        $this->sendStreamResult($response, $params['question'], $params['model']);\n    }\n\n    private function sseError(string $message): void\n    {\n        header('Content-Type: text\/event-stream; charset=utf-8');\n        header('Cache-Control: no-cache, no-store, must-revalidate');\n        header('Content-Encoding: none');\n        while (ob_get_level()) {\n            ob_end_clean();\n        }\n        \/\/ Padding for browser buffering\n        echo ':' . str_repeat(' ', 4096) . \"\\n\\n\";\n        $errorData = ['error' => $message];\n        echo \"event: error\\n\";\n        echo 'data: ' . json_encode($errorData, JSON_UNESCAPED_UNICODE) . \"\\n\\n\";\n        flush();\n    }\n\n    public function updateTitle(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session === null) {\n            $this->notFound('Session nicht gefunden');\n        }\n\n        $title = $this->updateSessionUseCase->updateTitle($session->getId() ?? 0, $_POST['title'] ?? '');\n        $this->html(htmlspecialchars($title));\n    }\n\n    public function systemPrompt(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session === null) {\n            $this->notFound('Session nicht gefunden');\n        }\n\n        $result = $this->updateSessionUseCase->updateSystemPrompt($session->getId() ?? 0, $_POST['system_prompt'] ?? '');\n        $this->view('chat.partials.success', ['message' => $result->message]);\n    }\n\n    public function getSystemPrompt(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session === null) {\n            $this->notFound('Session nicht gefunden');\n        }\n\n        $this->view('chat.partials.system-prompt-modal', [\n            'session' => ChatSessionFactory::toArray($session),\n            'currentPrompt' => $this->getSessionUseCase->getSystemPromptById($session->getSystemPromptId()),\n            'defaultPrompt' => $this->getSessionUseCase->getDefaultSystemPrompt(),\n        ]);\n    }\n\n    public function delete(string $uuid): void\n    {\n        $session = $this->getSessionUseCase->getSession($uuid);\n\n        if ($session !== null) {\n            $this->deleteSessionUseCase->deleteSession($session->getId() ?? 0);\n        }\n\n        $this->htmxRedirect('\/chat');\n    }\n\n    public function deleteAll(): void\n    {\n        $this->deleteSessionUseCase->deleteAllSessions();\n        $this->htmxRedirect('\/chat');\n    }\n\n    public function export(string $uuid): void\n    {\n        $format = $this->getString('format') ?: 'markdown';\n        $filename = $this->exportUseCase->generateFilename($uuid, $format);\n\n        if ($format === 'json') {\n            $data = $this->exportUseCase->exportAsJson($uuid);\n\n            if ($data === null) {\n                $this->notFound('Session nicht gefunden');\n            }\n\n            $content = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);\n            $this->download($content, $filename, 'application\/json');\n        } else {\n            $content = $this->exportUseCase->exportAsMarkdow... [TRUNCATED-0e54172c93638ddc]",
            "numLines": 457,
            "startLine": 1,
            "totalLines": 457
        }
    }
}

Response

-
← Vorheriger Zur Liste Nächster →