Protokoll #29103

ID29103
Zeitstempel2025-12-29 00:08:12.271627
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens5,224 (Input: 1,465, Output: 3,759)
Dauer97 ms
Request-Zeit2025-12-29 00:08:12.271627
Response-Zeit2025-12-29 00:08:12.368856

Request

{
    "event": "PreToolUse",
    "tool_name": "Edit",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/UseCases\/Chat\/ChatPromptLoader.php",
        "old_string": "    \/**\n     * Load structure prompt.\n     *\n     * @throws \\RuntimeException When structure cannot be parsed\n     *\/\n    public function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->getStructure($structureId);\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\n\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        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\n        }\n        if (isset($config['format']) && $config['format'] === 'qa') {\n            $parts[] = 'FAQ mit ' . ($config['min_questions'] ?? 3) . ' Paaren';\n        }\n        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\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    }",
        "new_string": "    \/**\n     * Load structure prompt.\n     *\n     * @throws \\RuntimeException When structure cannot be parsed\n     *\/\n    public function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->getStructure($structureId);\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\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        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\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        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\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     * @param array<string, mixed> $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     * @param array<string, mixed> $config\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     * @param array<mixed> $arr\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    }"
    }
}

Response

{
    "tool_response": {
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/UseCases\/Chat\/ChatPromptLoader.php",
        "oldString": "    \/**\n     * Load structure prompt.\n     *\n     * @throws \\RuntimeException When structure cannot be parsed\n     *\/\n    public function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->getStructure($structureId);\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\n\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        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\n        }\n        if (isset($config['format']) && $config['format'] === 'qa') {\n            $parts[] = 'FAQ mit ' . ($config['min_questions'] ?? 3) . ' Paaren';\n        }\n        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\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    }",
        "newString": "    \/**\n     * Load structure prompt.\n     *\n     * @throws \\RuntimeException When structure cannot be parsed\n     *\/\n    public function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->getStructure($structureId);\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\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        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\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        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\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     * @param array<string, mixed> $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     * @param array<string, mixed> $config\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     * @param array<mixed> $arr\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    }",
        "originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace UseCases\\Chat;\n\n\/\/ @responsibility: Lädt Chat-Prompts aus ContentConfig-Repository\n\nuse Domain\\Repository\\ContentConfigRepositoryInterface;\n\nfinal class ChatPromptLoader\n{\n    public function __construct(\n        private ContentConfigRepositoryInterface $configRepo\n    ) {\n    }\n\n    \/**\n     * Load style prompt from author profile.\n     *\n     * @throws \\RuntimeException When profile cannot be parsed\n     *\/\n    public function getStylePrompt(int $profileId): ?string\n    {\n        if ($profileId === 0) {\n            return null;\n        }\n\n        $profile = $this->configRepo->getAuthorProfile($profileId);\n        if ($profile === null) {\n            return null;\n        }\n\n        $config = json_decode($profile['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\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     * Load system prompt by ID.\n     *\n     * @throws \\RuntimeException When prompt cannot be parsed\n     *\/\n    public function getSystemPrompt(int $promptId): ?string\n    {\n        if ($promptId === 0) {\n            return null;\n        }\n\n        $prompt = $this->configRepo->getSystemPrompt($promptId);\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     * Load structure prompt.\n     *\n     * @throws \\RuntimeException When structure cannot be parsed\n     *\/\n    public function getStructurePrompt(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->getStructure($structureId);\n        if ($structure === null) {\n            return null;\n        }\n\n        $name = $structure['name'] ?? 'Struktur';\n        $config = json_decode($structure['content'] ?? '{}', true);\n        if ($config === null) {\n            return null;\n        }\n\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        if (isset($config['max_chars'])) {\n            $parts[] = 'Maximale Länge: ' . $config['max_chars'] . ' Zeichen';\n        }\n        if (isset($config['min_words'])) {\n            $parts[] = 'Mindestens ' . $config['min_words'] . ' Wörter';\n        }\n        if (isset($config['max_words'])) {\n            $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';\n        }\n        if (isset($config['format']) && $config['format'] === 'qa') {\n            $parts[] = 'FAQ mit ' . ($config['min_questions'] ?? 3) . ' Paaren';\n        }\n        if (isset($config['hashtags']) && $config['hashtags']) {\n            $parts[] = 'Füge passende Hashtags hinzu';\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     * Get structure name by ID.\n     *\/\n    public function getStructureName(int $structureId): ?string\n    {\n        if ($structureId === 0) {\n            return null;\n        }\n\n        $structure = $this->configRepo->getStructure($structureId);\n\n        return $structure['name'] ?? null;\n    }\n\n    \/**\n     * Compose effective system prompt from components.\n     *\/\n    public function composeSystemPrompt(\n        int $systemPromptId,\n        int $structureId,\n        int $authorProfileId,\n        bool $hasContext,\n        string $defaultRagPrompt,\n        string $defaultNoContextPrompt\n    ): string {\n        $systemPrompt = $this->getSystemPrompt($systemPromptId);\n        $structurePrompt = $this->getStructurePrompt($structureId);\n        $stylePrompt = $this->getStylePrompt($authorProfileId);\n\n        \/\/ Add structure to system prompt\n        if ($structurePrompt !== null) {\n            $systemPrompt = ($systemPrompt ?? '') . \"\\n\\n\" . $structurePrompt;\n        }\n\n        \/\/ Use default if no system prompt\n        $effectivePrompt = $systemPrompt ?? ($hasContext ? $defaultRagPrompt : $defaultNoContextPrompt);\n\n        \/\/ Add style prompt\n        if ($stylePrompt !== null && $stylePrompt !== '') {\n            $effectivePrompt .= \"\\n\\n\" . $stylePrompt;\n        }\n\n        return $effectivePrompt;\n    }\n}\n",
        "structuredPatch": [
            {
                "oldStart": 107,
                "oldLines": 6,
                "newStart": 107,
                "newLines": 12,
                "lines": [
                    "             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'])) {"
                ]
            },
            {
                "oldStart": 122,
                "oldLines": 7,
                "newStart": 128,
                "newLines": 8,
                "lines": [
                    "             $parts[] = 'Maximal ' . $config['max_words'] . ' Wörter';",
                    "         }",
                    "         if (isset($config['format']) && $config['format'] === 'qa') {",
                    "-            $parts[] = 'FAQ mit ' . ($config['min_questions'] ?? 3) . ' Paaren';",
                    "+            $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';"
                ]
            },
            {
                "oldStart": 135,
                "oldLines": 6,
                "newStart": 142,
                "newLines": 67,
                "lines": [
                    "     }",
                    " ",
                    "     \/**",
                    "+     * Build prompt from complex structure config.",
                    "+     *",
                    "+     * @param array<string, mixed> $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.",
                    "+     *",
                    "+     * @param array<string, mixed> $config",
                    "+     *\/",
                    "+    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.",
                    "+     *",
                    "+     * @param array<mixed> $arr",
                    "+     *\/",
                    "+    private function isIndexedArray(array $arr): bool",
                    "+    {",
                    "+        if ($arr === []) {",
                    "+            return true;",
                    "+        }",
                    "+",
                    "+        return array_keys($arr) === range(0, count($arr) - 1);",
                    "+    }",
                    "+",
                    "+    \/**",
                    "      * Get structure name by ID.",
                    "      *\/",
                    "     public function getStructureName(int $structureId): ?string"
                ]
            }
        ],
        "userModified": false,
        "replaceAll": false
    }
}
← Vorheriger Zur Liste Nächster →