{
"tool_response": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ChatController.php",
"oldString": " if ($question === '') {\n $this->redirect('\/chat');\n\n return;\n }",
"newString": " if ($question === '') {\n $this->redirect('\/chat');\n }",
"originalFile": "<?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\\Repository\\ContentRepositoryInterface;\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 private ContentRepositoryInterface $contentRepository\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 \/\/ Release session lock before long-running LLM call\n session_write_close();\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 \/\/ Release session lock before long-running LLM call\n \/\/ This allows other requests from same user to proceed\n session_write_close();\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->e... [TRUNCATED-95606820dd9374a0]",
"structuredPatch": [
{
"oldStart": 320,
"oldLines": 8,
"newStart": 320,
"newLines": 6,
"lines": [
" ",
" if ($question === '') {",
" $this->redirect('\/chat');",
"-",
"- return;",
" }",
" ",
" \/\/ Create title from question (max 100 chars)"
]
}
],
"userModified": false,
"replaceAll": false
}
}