chatService = $chatService ?? $this->createDefaultChatService(); $this->sessionRepo = $sessionRepo ?? new ChatSessionRepository(); $this->messageRepo = $messageRepo ?? new ChatMessageRepository(); $this->configRepo = $configRepo ?? new ContentConfigRepository(); } /** * Execute the use case * * @param string $sessionUuid Session UUID * @param string $message User message text * @param string $model AI model identifier * @param array $collections Qdrant collections to search * @param int $contextLimit Number of context chunks * @param int $authorProfileId Author profile ID (0 = none) * @param int $systemPromptId System prompt config ID * @param float $temperature AI temperature setting * @param int $maxTokens Max tokens for response * @return ChatResponse */ public function execute( string $sessionUuid, string $message, string $model, array $collections = ['documents'], int $contextLimit = 5, int $authorProfileId = 0, int $systemPromptId = 1, float $temperature = 0.7, int $maxTokens = 4096 ): ChatResponse { // 1. Validate session $session = $this->sessionRepo->findByUuid($sessionUuid); if ($session === null) { return ChatResponse::error('Session nicht gefunden.'); } // 2. Validate message $message = trim($message); if ($message === '') { return ChatResponse::error('Bitte gib eine Frage ein.'); } // 3. Save user message $this->messageRepo->save( sessionId: $session['id'], role: 'user', content: $message, model: $model ); // 4. Auto-set title from first message if ($session['title'] === null) { $title = mb_substr($message, 0, 50) . (mb_strlen($message) > 50 ? '...' : ''); $this->sessionRepo->updateTitle($session['id'], $title); } // 5. Get style prompt from author profile $stylePrompt = $this->getStylePromptFromProfile($authorProfileId); // 6. Get system prompt $systemPrompt = $this->getSystemPromptById($systemPromptId); // 7. Track timing and get AI response $startTime = microtime(true); try { $result = $this->chatService->chat( question: $message, model: $model, collections: $collections, limit: $contextLimit, stylePrompt: $stylePrompt, customSystemPrompt: $systemPrompt, temperature: $temperature, maxTokens: $maxTokens ); } catch (\Exception $e) { return ChatResponse::error('Chat-Service Fehler: ' . $e->getMessage()); } $endTime = microtime(true); // 8. Check for error in response if (isset($result['error'])) { return ChatResponse::error($result['error']); } // 9. Save assistant message with tracking $collectionsJson = json_encode($collections); $this->messageRepo->save( sessionId: $session['id'], role: 'assistant', content: $result['answer'] ?? '', model: $model, tokensInput: $result['usage']['input_tokens'] ?? null, tokensOutput: $result['usage']['output_tokens'] ?? null, sources: $result['sources'] ?? null, startMicrotime: $startTime, endMicrotime: $endTime, authorProfileId: $authorProfileId > 0 ? $authorProfileId : null, systemPromptId: $systemPromptId > 0 ? $systemPromptId : null, collectionsJson: $collectionsJson, contextLimit: $contextLimit ); // 10. Return response return ChatResponse::fromServiceResponse($result, $endTime - $startTime); } /** * Create default ChatService from credentials */ private function createDefaultChatService(): ChatService { $config = \Infrastructure\AI\AIConfig::fromCredentialsFile(); return $config->createChatService(); } /** * Get style prompt from author profile */ private function getStylePromptFromProfile(int $profileId): ?string { if ($profileId === 0) { return null; } $profile = $this->configRepo->findByIdAndType($profileId, 'author_profile'); if ($profile === null) { return null; } $config = json_decode($profile['content'] ?? '{}', true); if ($config === null) { return null; } $parts = []; if (isset($config['stimme']['ton'])) { $parts[] = 'Ton: ' . $config['stimme']['ton']; } if (isset($config['stimme']['perspektive'])) { $parts[] = 'Perspektive: ' . $config['stimme']['perspektive']; } if (isset($config['stil']['fachsprache']) && $config['stil']['fachsprache']) { $parts[] = 'Verwende Fachsprache'; } if (isset($config['stil']['beispiele']) && $config['stil']['beispiele'] === 'häufig') { $parts[] = 'Nutze häufig Beispiele'; } if (isset($config['stil']['listen']) && $config['stil']['listen'] === 'bevorzugt') { $parts[] = 'Bevorzuge Listen und Bullet-Points'; } if (isset($config['tabus']) && is_array($config['tabus'])) { $parts[] = 'Vermeide: ' . implode(', ', $config['tabus']); } if ($parts === []) { return null; } return 'Schreibstil (' . ($profile['name'] ?? 'Profil') . '): ' . implode('. ', $parts) . '.'; } /** * Get system prompt by ID */ private function getSystemPromptById(int $promptId): ?string { if ($promptId === 0) { return null; } $prompt = $this->configRepo->findByIdAndType($promptId, 'system_prompt'); if ($prompt === null) { return null; } $content = json_decode($prompt['content'] ?? '{}', true); return $content['prompt'] ?? null; } }