Protokoll #29107

ID29107
Zeitstempel2025-12-29 00:09:53.639705
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens7,742 (Input: 1,680, Output: 6,062)
Dauer85 ms
Request-Zeit2025-12-29 00:09:53.639705
Response-Zeit2025-12-29 00:09:53.724367

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/UseCases\/Chat\/SendChatMessageUseCase.php",
        "old_string": "        \/\/ 12. Return response\n        return $response;\n    }\n\n    \/**\n     * Get structure name by ID\n     *\/\n    private function getStructureName(int $structureId): ?string\n    {\n        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');\n        return $structure['name'] ?? null;\n    }\n\n    \/**\n     * Create default ChatService from credentials\n     *\/\n    private function createDefaultChatService(): ChatService\n    {\n        $config = \\Infrastructure\\AI\\AIConfig::fromCredentialsFile();\n        return $config->createChatService();\n    }\n\n    \/**\n     * Get style prompt from author profile\n     *\/\n    private function getStylePromptFromProfile(int $profileId): ?string\n    {\n        if ($profileId === 0) {\n            return null;\n        }\n        $profile = $this->configRepo->findByIdAndType($profileId, 'author_profile');\n        if ($profile === null) {\n            return null;\n        }\n        $config = json_decode($profile['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\n        $parts = [];\n\n        if (isset($config['stimme']['ton'])) {\n            $parts[] = 'Ton: ' . $config['stimme']['ton'];\n        }\n        if (isset($config['stimme']['perspektive'])) {\n            $parts[] = 'Perspektive: ' . $config['stimme']['perspektive'];\n        }\n        if (isset($config['stil']['fachsprache']) && $config['stil']['fachsprache']) {\n            $parts[] = 'Verwende Fachsprache';\n        }\n        if (isset($config['stil']['beispiele']) && $config['stil']['beispiele'] === 'häufig') {\n            $parts[] = 'Nutze häufig Beispiele';\n        }\n        if (isset($config['stil']['listen']) && $config['stil']['listen'] === 'bevorzugt') {\n            $parts[] = 'Bevorzuge Listen und Bullet-Points';\n        }\n        if (isset($config['tabus']) && is_array($config['tabus'])) {\n            $parts[] = 'Vermeide: ' . implode(', ', $config['tabus']);\n        }\n\n        if ($parts === []) {\n            return null;\n        }\n\n        return 'Schreibstil (' . ($profile['name'] ?? 'Profil') . '): ' . implode('. ', $parts) . '.';\n    }\n\n    \/**\n     * Get system prompt by ID\n     *\/\n    private function getSystemPromptById(int $promptId): ?string\n    {\n        if ($promptId === 0) {\n            return null;\n        }\n\n        $prompt = $this->configRepo->findByIdAndType($promptId, 'system_prompt');\n        if ($prompt === null) {\n            return null;\n        }\n\n        $content = json_decode($prompt['content'] ?? '{}', true);\n\n        return $content['prompt'] ?? null;\n    }\n\n    \/**\n     * Get structure formatting prompt\n     *\/\n    private function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\n\n        if ($config === null) {\n            return null;\n        }\n\n        \/\/ Complex structure (Blog-Beitrag, LinkedIn-Beitrag etc.)\n        if (isset($config['ausgabe']) || isset($config['aufbau']) || isset($config['gesamtaufbau'])) {\n            return $this->buildComplexStructurePrompt($name, $config);\n        }\n\n        \/\/ Simple structure (legacy format)\n        $parts = [\"Formatiere deine Antwort als: {$name}\"];\n\n        if (isset($config['sections']) && is_array($config['sections'])) {\n            $parts[] = 'Struktur: ' . implode(' → ', $config['sections']);\n        }\n\n        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\n        }\n\n        if (isset($config['format']) && $config['format'] === 'qa') {\n            $min = $config['min_questions'] ?? 3;\n            $parts[] = \"Formatiere als FAQ mit mindestens {$min} Frage-Antwort-Paaren\";\n        }\n\n        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\n        }\n\n        if (isset($config['cta']) && $config['cta']) {\n            $parts[] = 'Schließe mit einem Call-to-Action ab';\n        }\n\n        return implode('. ', $parts) . '.';\n    }\n\n    \/**\n     * Build prompt from complex structure config\n     *\/\n    private function buildComplexStructurePrompt(string $name, array $config): string\n    {\n        $prompt = \"## Ausgabeformat: {$name}\\n\\n\";\n        $prompt .= \"Befolge diese Struktur-Vorgaben exakt:\\n\\n\";\n        $prompt .= $this->configToYaml($config, 0);\n\n        return $prompt;\n    }\n\n    \/**\n     * Convert config array to readable YAML-like format\n     *\/\n    private function configToYaml(array $config, int $indent): string\n    {\n        $result = '';\n        $prefix = str_repeat('  ', $indent);\n\n        foreach ($config as $key => $value) {\n            if (is_array($value)) {\n                if ($this->isIndexedArray($value)) {\n                    $result .= \"{$prefix}{$key}:\\n\";\n                    foreach ($value as $item) {\n                        if (is_array($item)) {\n                            $result .= \"{$prefix}- \" . trim($this->configToYaml($item, 0)) . \"\\n\";\n                        } else {\n                            $result .= \"{$prefix}- {$item}\\n\";\n                        }\n                    }\n                } else {\n                    $result .= \"{$prefix}{$key}:\\n\";\n                    $result .= $this->configToYaml($value, $indent + 1);\n                }\n            } else {\n                $result .= \"{$prefix}{$key}: {$value}\\n\";\n            }\n        }\n\n        return $result;\n    }\n\n    \/**\n     * Check if array is indexed (list) vs associative\n     *\/\n    private function isIndexedArray(array $arr): bool\n    {\n        if ($arr === []) {\n            return true;\n        }\n\n        return array_keys($arr) === range(0, count($arr) - 1);\n    }\n}",
        "new_string": "        \/\/ 12. Return response\n        return $response;\n    }\n}"
    }
}

Response

{
    "tool_response": {
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/UseCases\/Chat\/SendChatMessageUseCase.php",
        "oldString": "        \/\/ 12. Return response\n        return $response;\n    }\n\n    \/**\n     * Get structure name by ID\n     *\/\n    private function getStructureName(int $structureId): ?string\n    {\n        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');\n        return $structure['name'] ?? null;\n    }\n\n    \/**\n     * Create default ChatService from credentials\n     *\/\n    private function createDefaultChatService(): ChatService\n    {\n        $config = \\Infrastructure\\AI\\AIConfig::fromCredentialsFile();\n        return $config->createChatService();\n    }\n\n    \/**\n     * Get style prompt from author profile\n     *\/\n    private function getStylePromptFromProfile(int $profileId): ?string\n    {\n        if ($profileId === 0) {\n            return null;\n        }\n        $profile = $this->configRepo->findByIdAndType($profileId, 'author_profile');\n        if ($profile === null) {\n            return null;\n        }\n        $config = json_decode($profile['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\n        $parts = [];\n\n        if (isset($config['stimme']['ton'])) {\n            $parts[] = 'Ton: ' . $config['stimme']['ton'];\n        }\n        if (isset($config['stimme']['perspektive'])) {\n            $parts[] = 'Perspektive: ' . $config['stimme']['perspektive'];\n        }\n        if (isset($config['stil']['fachsprache']) && $config['stil']['fachsprache']) {\n            $parts[] = 'Verwende Fachsprache';\n        }\n        if (isset($config['stil']['beispiele']) && $config['stil']['beispiele'] === 'häufig') {\n            $parts[] = 'Nutze häufig Beispiele';\n        }\n        if (isset($config['stil']['listen']) && $config['stil']['listen'] === 'bevorzugt') {\n            $parts[] = 'Bevorzuge Listen und Bullet-Points';\n        }\n        if (isset($config['tabus']) && is_array($config['tabus'])) {\n            $parts[] = 'Vermeide: ' . implode(', ', $config['tabus']);\n        }\n\n        if ($parts === []) {\n            return null;\n        }\n\n        return 'Schreibstil (' . ($profile['name'] ?? 'Profil') . '): ' . implode('. ', $parts) . '.';\n    }\n\n    \/**\n     * Get system prompt by ID\n     *\/\n    private function getSystemPromptById(int $promptId): ?string\n    {\n        if ($promptId === 0) {\n            return null;\n        }\n\n        $prompt = $this->configRepo->findByIdAndType($promptId, 'system_prompt');\n        if ($prompt === null) {\n            return null;\n        }\n\n        $content = json_decode($prompt['content'] ?? '{}', true);\n\n        return $content['prompt'] ?? null;\n    }\n\n    \/**\n     * Get structure formatting prompt\n     *\/\n    private function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\n\n        if ($config === null) {\n            return null;\n        }\n\n        \/\/ Complex structure (Blog-Beitrag, LinkedIn-Beitrag etc.)\n        if (isset($config['ausgabe']) || isset($config['aufbau']) || isset($config['gesamtaufbau'])) {\n            return $this->buildComplexStructurePrompt($name, $config);\n        }\n\n        \/\/ Simple structure (legacy format)\n        $parts = [\"Formatiere deine Antwort als: {$name}\"];\n\n        if (isset($config['sections']) && is_array($config['sections'])) {\n            $parts[] = 'Struktur: ' . implode(' → ', $config['sections']);\n        }\n\n        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\n        }\n\n        if (isset($config['format']) && $config['format'] === 'qa') {\n            $min = $config['min_questions'] ?? 3;\n            $parts[] = \"Formatiere als FAQ mit mindestens {$min} Frage-Antwort-Paaren\";\n        }\n\n        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\n        }\n\n        if (isset($config['cta']) && $config['cta']) {\n            $parts[] = 'Schließe mit einem Call-to-Action ab';\n        }\n\n        return implode('. ', $parts) . '.';\n    }\n\n    \/**\n     * Build prompt from complex structure config\n     *\/\n    private function buildComplexStructurePrompt(string $name, array $config): string\n    {\n        $prompt = \"## Ausgabeformat: {$name}\\n\\n\";\n        $prompt .= \"Befolge diese Struktur-Vorgaben exakt:\\n\\n\";\n        $prompt .= $this->configToYaml($config, 0);\n\n        return $prompt;\n    }\n\n    \/**\n     * Convert config array to readable YAML-like format\n     *\/\n    private function configToYaml(array $config, int $indent): string\n    {\n        $result = '';\n        $prefix = str_repeat('  ', $indent);\n\n        foreach ($config as $key => $value) {\n            if (is_array($value)) {\n                if ($this->isIndexedArray($value)) {\n                    $result .= \"{$prefix}{$key}:\\n\";\n                    foreach ($value as $item) {\n                        if (is_array($item)) {\n                            $result .= \"{$prefix}- \" . trim($this->configToYaml($item, 0)) . \"\\n\";\n                        } else {\n                            $result .= \"{$prefix}- {$item}\\n\";\n                        }\n                    }\n                } else {\n                    $result .= \"{$prefix}{$key}:\\n\";\n                    $result .= $this->configToYaml($value, $indent + 1);\n                }\n            } else {\n                $result .= \"{$prefix}{$key}: {$value}\\n\";\n            }\n        }\n\n        return $result;\n    }\n\n    \/**\n     * Check if array is indexed (list) vs associative\n     *\/\n    private function isIndexedArray(array $arr): bool\n    {\n        if ($arr === []) {\n            return true;\n        }\n\n        return array_keys($arr) === range(0, count($arr) - 1);\n    }\n}",
        "newString": "        \/\/ 12. Return response\n        return $response;\n    }\n}",
        "originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace UseCases\\Chat;\n\n\/\/ @responsibility: Orchestriert Chat-Nachrichten mit AI-Service\n\nuse Domain\\Repository\\ChatMessageRepositoryInterface;\nuse Domain\\Repository\\ChatSessionRepositoryInterface;\nuse Infrastructure\\AI\\ChatService;\nuse Infrastructure\\AI\\ContentQualityValidator;\n\nclass SendChatMessageUseCase\n{\n    public function __construct(\n        private ChatService $chatService,\n        private ChatSessionRepositoryInterface $sessionRepo,\n        private ChatMessageRepositoryInterface $messageRepo,\n        private ChatPromptLoader $promptLoader,\n        private ContentQualityValidator $qualityValidator\n    ) {\n    }\n\n    \/**\n     * Execute the use case\n     *\n     * @param string $sessionUuid Session UUID\n     * @param string $message User message text\n     * @param string $model AI model identifier\n     * @param array<string> $collections Qdrant collections to search\n     * @param int $contextLimit Number of context chunks\n     * @param int $authorProfileId Author profile ID (0 = none)\n     * @param int $systemPromptId System prompt config ID\n     * @param float $temperature AI temperature setting\n     * @param int $maxTokens Max tokens for response\n     * @param int $structureId Output structure ID (0 = frei)\n     * @param bool $qualityCheck Run quality validation on response\n     * @return ChatResponse\n     *\/\n    public function execute(\n        string $sessionUuid,\n        string $message,\n        string $model,\n        array $collections = ['documents'],\n        int $contextLimit = 5,\n        int $authorProfileId = 0,\n        int $systemPromptId = 1,\n        float $temperature = 0.7,\n        int $maxTokens = 4096,\n        int $structureId = 0,\n        bool $qualityCheck = false\n    ): ChatResponse {\n        \/\/ 1. Validate session\n        $session = $this->sessionRepo->findByUuid($sessionUuid);\n        if ($session === null) {\n            return ChatResponse::error('Session nicht gefunden.');\n        }\n        $sessionId = $session->getId() ?? 0;\n\n        \/\/ 2. Validate message\n        $message = trim($message);\n        if ($message === '') {\n            return ChatResponse::error('Bitte gib eine Frage ein.');\n        }\n\n        \/\/ 3. Save user message\n        $this->messageRepo->save(\n            sessionId: $sessionId,\n            role: 'user',\n            content: $message,\n            model: $model\n        );\n\n        \/\/ 4. Auto-set title from first message\n        if ($session->getTitle() === null) {\n            $title = mb_substr($message, 0, 50) . (mb_strlen($message) > 50 ? '...' : '');\n            $this->sessionRepo->updateTitle($sessionId, $title);\n        }\n\n        \/\/ 5. Get prompts via ChatPromptLoader\n        $stylePrompt = $this->promptLoader->getStylePrompt($authorProfileId);\n        $systemPrompt = $this->promptLoader->getSystemPrompt($systemPromptId);\n        $structurePrompt = $this->promptLoader->getStructurePrompt($structureId);\n        if ($structurePrompt !== null) {\n            $systemPrompt = ($systemPrompt ?? '') . \"\\n\\n\" . $structurePrompt;\n        }\n\n        \/\/ 7. Track timing and get AI response\n        $startTime = microtime(true);\n\n        try {\n            $result = $this->chatService->chat(\n                question: $message,\n                model: $model,\n                collections: $collections,\n                limit: $contextLimit,\n                stylePrompt: $stylePrompt,\n                customSystemPrompt: $systemPrompt,\n                temperature: $temperature,\n                maxTokens: $maxTokens\n            );\n        } catch (\\Exception $e) {\n            return ChatResponse::error('Chat-Service Fehler: ' . $e->getMessage());\n        }\n\n        $endTime = microtime(true);\n\n        \/\/ 8. Prepare sources as JSON strings for storage\n        \/** @var array<int, non-empty-string> $sourcesForStorage *\/\n        $sourcesForStorage = array_map(\n            static fn (array $source): string => json_encode($source, JSON_THROW_ON_ERROR),\n            $result['sources']\n        );\n\n        \/\/ 9. Save assistant message with tracking\n        $collectionsJson = json_encode($collections);\n        $this->messageRepo->save(\n            sessionId: $sessionId,\n            role: 'assistant',\n            content: $result['answer'],\n            model: $model,\n            tokensInput: $result['usage']['input_tokens'] ?? null,\n            tokensOutput: $result['usage']['output_tokens'] ?? null,\n            sources: $sourcesForStorage,\n            startMicrotime: $startTime,\n            endMicrotime: $endTime,\n            authorProfileId: $authorProfileId > 0 ? $authorProfileId : null,\n            systemPromptId: $systemPromptId > 0 ? $systemPromptId : null,\n            collectionsJson: $collectionsJson,\n            contextLimit: $contextLimit\n        );\n\n        \/\/ 10. Create response\n        $response = ChatResponse::fromServiceResponse($result, $endTime - $startTime);\n        \/\/ 11. Run quality validation if enabled\n        if ($qualityCheck) {\n            $structureName = $this->promptLoader->getStructureName($structureId);\n            $validation = $this->qualityValidator->validate(\n                question: $message,\n                answer: $result['answer'],\n                sources: $result['sources'],\n                structureName: $structureName\n            );\n            $response = $response->withQualityValidation($validation);\n        }\n        \/\/ 12. Return response\n        return $response;\n    }\n\n    \/**\n     * Get structure name by ID\n     *\/\n    private function getStructureName(int $structureId): ?string\n    {\n        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');\n        return $structure['name'] ?? null;\n    }\n\n    \/**\n     * Create default ChatService from credentials\n     *\/\n    private function createDefaultChatService(): ChatService\n    {\n        $config = \\Infrastructure\\AI\\AIConfig::fromCredentialsFile();\n        return $config->createChatService();\n    }\n\n    \/**\n     * Get style prompt from author profile\n     *\/\n    private function getStylePromptFromProfile(int $profileId): ?string\n    {\n        if ($profileId === 0) {\n            return null;\n        }\n        $profile = $this->configRepo->findByIdAndType($profileId, 'author_profile');\n        if ($profile === null) {\n            return null;\n        }\n        $config = json_decode($profile['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\n        $parts = [];\n\n        if (isset($config['stimme']['ton'])) {\n            $parts[] = 'Ton: ' . $config['stimme']['ton'];\n        }\n        if (isset($config['stimme']['perspektive'])) {\n            $parts[] = 'Perspektive: ' . $config['stimme']['perspektive'];\n        }\n        if (isset($config['stil']['fachsprache']) && $config['stil']['fachsprache']) {\n            $parts[] = 'Verwende Fachsprache';\n        }\n        if (isset($config['stil']['beispiele']) && $config['stil']['beispiele'] === 'häufig') {\n            $parts[] = 'Nutze häufig Beispiele';\n        }\n        if (isset($config['stil']['listen']) && $config['stil']['listen'] === 'bevorzugt') {\n            $parts[] = 'Bevorzuge Listen und Bullet-Points';\n        }\n        if (isset($config['tabus']) && is_array($config['tabus'])) {\n            $parts[] = 'Vermeide: ' . implode(', ', $config['tabus']);\n        }\n\n        if ($parts === []) {\n            return null;\n        }\n\n        return 'Schreibstil (' . ($profile['name'] ?? 'Profil') . '): ' . implode('. ', $parts) . '.';\n    }\n\n    \/**\n     * Get system prompt by ID\n     *\/\n    private function getSystemPromptById(int $promptId): ?string\n    {\n        if ($promptId === 0) {\n            return null;\n        }\n\n        $prompt = $this->configRepo->findByIdAndType($promptId, 'system_prompt');\n        if ($prompt === null) {\n            return null;\n        }\n\n        $content = json_decode($prompt['content'] ?? '{}', true);\n\n        return $content['prompt'] ?? null;\n    }\n\n    \/**\n     * Get structure formatting prompt\n     *\/\n    private function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\n\n        if ($config === null) {\n            return null;\n        }\n\n        \/\/ Complex structure (Blog-Beitrag, LinkedIn-Beitrag etc.)\n        if (isset($config['ausgabe']) || isset($config['aufbau']) || isset($config['gesamtaufbau'])) {\n            return $this->buildComplexStructurePrompt($name, $config);\n        }\n\n        \/\/ Simple structure (legacy format)\n        $parts = [\"Formatiere deine Antwort als: {$name}\"];\n\n        if (isset($config['sections']) && is_array($config['sections'])) {\n            $parts[] = 'Struktur: ' . implode(' → ', $config['sections']);\n        }\n\n        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\n        }\n\n        if (isset($config['format']) && $config['format'] === 'qa') {\n            $min = $config['min_questions'] ?? 3;\n            $parts[] = \"Formatiere als FAQ mit mindestens {$min} Frage-Antwort-Paaren\";\n        }\n\n        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\n        }\n\n        if (isset($config['cta']) && $config['cta']) {\n            $parts[] = 'Schließe mit einem Call-to-Action ab';\n        }\n\n        return implode('. ', $parts) . '.';\n    }\n\n    \/**\n     * Build prompt from co... [TRUNCATED-5bec052a1e1c4075]",
        "structuredPatch": [
            {
                "oldStart": 147,
                "oldLines": 201,
                "newStart": 147,
                "newLines": 4,
                "lines": [
                    "         \/\/ 12. Return response",
                    "         return $response;",
                    "     }",
                    "-",
                    "-    \/**",
                    "-     * Get structure name by ID",
                    "-     *\/",
                    "-    private function getStructureName(int $structureId): ?string",
                    "-    {",
                    "-        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');",
                    "-        return $structure['name'] ?? null;",
                    "-    }",
                    "-",
                    "-    \/**",
                    "-     * 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;",
                    "-    }",
                    "-",
                    "-    \/**",
                    "-     * Get structure formatting prompt",
                    "-     *\/",
                    "-    private function getStructurePrompt(int $structureId): ?string",
                    "-    {",
                    "-        if ($structureId === 0) {",
                    "-            return null;",
                    "-        }",
                    "-",
                    "-        $structure = $this->configRepo->findByIdAndType($structureId, 'structure');",
                    "-        if ($structure === null) {",
                    "-            return null;",
                    "-        }",
                    "-",
                    "-        $name = $structure['name'] ?? 'Struktur';",
                    "-        $config = json_decode($structure['content'] ?? '{}', true);",
                    "-",
                    "-        if ($config === null) {",
                    "-            return null;",
                    "-        }",
                    "-",
                    "-        \/\/ Complex structure (Blog-Beitrag, LinkedIn-Beitrag etc.)",
                    "-        if (isset($config['ausgabe']) || isset($config['aufbau']) || isset($config['gesamtaufbau'])) {",
                    "-            return $this->buildComplexStructurePrompt($name, $config);",
                    "-        }",
                    "-",
                    "-        \/\/ Simple structure (legacy format)",
                    "-        $parts = [\"Formatiere deine Antwort als: {$name}\"];",
                    "-",
                    "-        if (isset($config['sections']) && is_array($config['sections'])) {",
                    "-            $parts[] = 'Struktur: ' . implode(' → ', $config['sections']);",
                    "-        }",
                    "-",
                    "-        if (isset($config['max_chars'])) {",
                    "-            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';",
                    "-        }",
                    "-",
                    "-        if (isset($config['min_words'])) {",
                    "-            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';",
                    "-        }",
                    "-",
                    "-        if (isset($config['max_words'])) {",
                    "-            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';",
                    "-        }",
                    "-",
                    "-        if (isset($config['format']) && $config['format'] === 'qa') {",
                    "-            $min = $config['min_questions'] ?? 3;",
                    "-            $parts[] = \"Formatiere als FAQ mit mindestens {$min} Frage-Antwort-Paaren\";",
                    "-        }",
                    "-",
                    "-        if (isset($config['hashtags']) && $config['hashtags']) {",
                    "-            $parts[] = 'Füge passende Hashtags hinzu';",
                    "-        }",
                    "-",
                    "-        if (isset($config['cta']) && $config['cta']) {",
                    "-            $parts[] = 'Schließe mit einem Call-to-Action ab';",
                    "-        }",
                    "-",
                    "-        return implode('. ', $parts) . '.';",
                    "-    }",
                    "-",
                    "-    \/**",
                    "-     * Build prompt from complex structure config",
                    "-     *\/",
                    "-    private function buildComplexStructurePrompt(string $name, array $config): string",
                    "-    {",
                    "-        $prompt = \"## Ausgabeformat: {$name}\\n\\n\";",
                    "-        $prompt .= \"Befolge diese Struktur-Vorgaben exakt:\\n\\n\";",
                    "-        $prompt .= $this->configToYaml($config, 0);",
                    "-",
                    "-        return $prompt;",
                    "-    }",
                    "-",
                    "-    \/**",
                    "-     * Convert config array to readable YAML-like format",
                    "-     *\/",
                    "-    private function configToYaml(array $config, int $indent): string",
                    "-    {",
                    "-        $result = '';",
                    "-        $prefix = str_repeat('  ', $indent);",
                    "-",
                    "-        foreach ($config as $key => $value) {",
                    "-            if (is_array($value)) {",
                    "-                if ($this->isIndexedArray($value)) {",
                    "-                    $result .= \"{$prefix}{$key}:\\n\";",
                    "-                    foreach ($value as $item) {",
                    "-                        if (is_array($item)) {",
                    "-                            $result .= \"{$prefix}- \" . trim($this->configToYaml($item, 0)) . \"\\n\";",
                    "-                        } else {",
                    "-                            $result .= \"{$prefix}- {$item}\\n\";",
                    "-                        }",
                    "-                    }",
                    "-                } else {",
                    "-                    $result .= \"{$prefix}{$key}:\\n\";",
                    "-                    $result .= $this->configToYaml($value, $indent + 1);",
                    "-                }",
                    "-            } else {",
                    "-                $result .= \"{$prefix}{$key}: {$value}\\n\";",
                    "-            }",
                    "-        }",
                    "-",
                    "-        return $result;",
                    "-    }",
                    "-",
                    "-    \/**",
                    "-     * Check if array is indexed (list) vs associative",
                    "-     *\/",
                    "-    private function isIndexedArray(array $arr): bool",
                    "-    {",
                    "-        if ($arr === []) {",
                    "-            return true;",
                    "-        }",
                    "-",
                    "-        return array_keys($arr) === range(0, count($arr) - 1);",
                    "-    }",
                    " }"
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →