{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "\/var\/www\/scripts\/pipeline\/generators\/config_loader.py",
"old_string": "def parse_structure(config: dict) -> tuple[str, str, list[str]]:\n \"\"\"Parse new-style structure profile into prompt text and format info.\"\"\"\n sections = []\n output_format = None\n erlaubte_tags = []\n\n # Ausgabe-Format\n ausgabe = config.get(\"ausgabe\", {})\n if ausgabe:\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\"erlaubte_tags\", [])\n verbotene_tags = ausgabe.get(\"verbotene_tags\", [])\n\n if output_format == \"body-html\":\n sections.append(f\"\"\"### Ausgabe-Format: HTML\n- **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}\n- **Verboten:** {\", \".join(verbotene_tags)}\n- {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")\n\n # Gesamtaufbau\n aufbau = config.get(\"gesamtaufbau\", {})\n if aufbau:\n sections.append(f\"\"\"### Gesamtaufbau:\n- Form: {aufbau.get(\"form\", \"\")}\n- Dramaturgie: {aufbau.get(\"dramaturgie\", \"\")}\n- Linearität: {aufbau.get(\"linearitaet\", \"\")}\n- Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")\n\n # Einstieg\n einstieg = config.get(\"einstieg\", {})\n if einstieg:\n sections.append(f\"\"\"### Einstieg:\n- Funktion: {einstieg.get(\"funktion\", \"\")}\n- Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}\n- Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")\n\n # Hauptteil-Blöcke\n hauptteil = config.get(\"hauptteil\", {})\n bloecke = hauptteil.get(\"bloecke\", [])\n if bloecke:\n bloecke_text = \"\\n\".join(\n [f\" {i + 1}. {b.get('fokus', '')} → {b.get('ziel', '')}\" for i, b in enumerate(bloecke)]\n )\n sections.append(f\"\"\"### Hauptteil-Struktur:\n{bloecke_text}\"\"\")\n\n # Schluss\n schluss = config.get(\"schluss\", {})\n if schluss:\n sections.append(f\"\"\"### Schluss:\n- Typ: {schluss.get(\"typ\", \"\")}\n- Funktion: {schluss.get(\"funktion\", \"\")}\n- Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")\n\n # Formatierung\n formatierung = config.get(\"formatierung\", {})\n if formatierung:\n ausschluss = formatierung.get(\"ausschluss\", [])\n if ausschluss:\n sections.append(f\"\"\"### Formatierung verboten:\n{\", \".join(ausschluss)}\"\"\")\n\n return \"\\n\\n\".join(sections), output_format, erlaubte_tags",
"new_string": "def parse_structure(config: dict) -> tuple[str, str, list[str]]:\n \"\"\"Parse structure profile into prompt text and format info.\n\n Supports both formats:\n - Blog\/HTML format: gesamtaufbau, einstieg, hauptteil.bloecke, schluss, formatierung.ausschluss\n - LinkedIn format: aufbau.hook, aufbau.hauptteil, aufbau.schluss, formatierung.*, verboten[]\n \"\"\"\n sections = []\n output_format = None\n erlaubte_tags = []\n\n # WICHTIG field (LinkedIn format)\n wichtig = config.get(\"WICHTIG\")\n if wichtig:\n sections.append(f\"### WICHTIG:\\n{wichtig}\")\n\n # Ausgabe-Format\n ausgabe = config.get(\"ausgabe\", {})\n if ausgabe:\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\"erlaubte_tags\", [])\n verbotene_tags = ausgabe.get(\"verbotene_tags\", [])\n\n if output_format == \"body-html\":\n sections.append(f\"\"\"### Ausgabe-Format: HTML\n- **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}\n- **Verboten:** {\", \".join(verbotene_tags)}\n- {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")\n elif output_format == \"reiner Text\":\n # LinkedIn plain text format\n zeichen_info = \"\"\n if ausgabe.get(\"zeichen_min\") and ausgabe.get(\"zeichen_max\"):\n zeichen_info = f\"\\n- Länge: {ausgabe.get('zeichen_min')}-{ausgabe.get('zeichen_max')} Zeichen\"\n if ausgabe.get(\"woerter\"):\n zeichen_info += f\" ({ausgabe.get('woerter')} Wörter)\"\n sections.append(f\"\"\"### Ausgabe-Format: Reiner Text\n- KEIN Markdown, KEINE Formatierung\n- Keine **Fettschrift**, keine *Kursivschrift*\n- Keine Überschriften mit # oder ##\n- Nur Fließtext und einfache Zeilenumbrüche{zeichen_info}\"\"\")\n\n # Try Blog format first: gesamtaufbau\n aufbau = config.get(\"gesamtaufbau\", {})\n if aufbau:\n sections.append(f\"\"\"### Gesamtaufbau:\n- Form: {aufbau.get(\"form\", \"\")}\n- Dramaturgie: {aufbau.get(\"dramaturgie\", \"\")}\n- Linearität: {aufbau.get(\"linearitaet\", \"\")}\n- Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")\n\n # Try LinkedIn format: aufbau (with nested hook, hauptteil, etc.)\n linkedin_aufbau = config.get(\"aufbau\", {})\n if linkedin_aufbau and not aufbau: # Only if gesamtaufbau not present\n aufbau_sections = []\n\n # Hook\n hook = linkedin_aufbau.get(\"hook\", {})\n if hook:\n hook_text = f\"### Hook ({hook.get('position', 'Anfang')}):\\n\"\n hook_text += f\"- Stil: {hook.get('stil', '')}\\n\"\n if hook.get(\"beispiele\"):\n hook_text += f\"- Beispiele: {'; '.join(hook['beispiele'][:2])}\\n\"\n if hook.get(\"verboten\"):\n hook_text += f\"- Verboten: {', '.join(hook['verboten'])}\"\n aufbau_sections.append(hook_text)\n\n # Praxisbezug\n praxis = linkedin_aufbau.get(\"praxisbezug\", {})\n if praxis:\n praxis_text = f\"### Praxisbezug ({praxis.get('position', '')}):\\n\"\n praxis_text += f\"- Stil: {praxis.get('stil', '')}\\n\"\n if praxis.get(\"muster\"):\n praxis_text += f\"- Muster: {'; '.join(praxis['muster'][:2])}\"\n aufbau_sections.append(praxis_text)\n\n # Hauptteil\n hauptteil = linkedin_aufbau.get(\"hauptteil\", {})\n if hauptteil:\n hauptteil_text = \"### Hauptteil:\\n\"\n if hauptteil.get(\"struktur\"):\n hauptteil_text += f\"- Struktur: {hauptteil.get('struktur')}\\n\"\n if hauptteil.get(\"theoretische_einordnung\"):\n hauptteil_text += f\"- Theorie: {hauptteil.get('theoretische_einordnung')}\\n\"\n if hauptteil.get(\"differenzierung\"):\n hauptteil_text += f\"- Differenzierung: {hauptteil.get('differenzierung')}\\n\"\n if hauptteil.get(\"dreier_struktur\"):\n hauptteil_text += f\"- Dreier-Struktur: {hauptteil.get('dreier_struktur')}\"\n aufbau_sections.append(hauptteil_text)\n\n # Handlungsempfehlung\n handlung = linkedin_aufbau.get(\"handlungsempfehlung\", {})\n if handlung:\n handlung_text = \"### Handlungsempfehlung:\\n\"\n handlung_text += f\"- Stil: {handlung.get('stil', '')}\\n\"\n if handlung.get(\"muster\"):\n handlung_text += f\"- Muster: {'; '.join(handlung['muster'][:2])}\"\n aufbau_sections.append(handlung_text)\n\n # Schluss (LinkedIn style)\n schluss = linkedin_aufbau.get(\"schluss\", {})\n if schluss:\n schluss_text = \"### Schluss:\\n\"\n if schluss.get(\"soft_cta\"):\n schluss_text += f\"- Stil: {schluss.get('soft_cta')}\\n\"\n if schluss.get(\"muster\"):\n schluss_text += f\"- Muster: {'; '.join(schluss['muster'][:2])}\\n\"\n if schluss.get(\"verboten\"):\n schluss_text += f\"- Verboten: {', '.join(schluss['verboten'])}\"\n aufbau_sections.append(schluss_text)\n\n sections.extend(aufbau_sections)\n\n # Blog format: Einstieg\n einstieg = config.get(\"einstieg\", {})\n if einstieg:\n sections.append(f\"\"\"### Einstieg:\n- Funktion: {einstieg.get(\"funktion\", \"\")}\n- Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}\n- Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")\n\n # Blog format: Hauptteil-Blöcke\n hauptteil = config.get(\"hauptteil\", {})\n bloecke = hauptteil.get(\"bloecke\", [])\n if bloecke:\n bloecke_text = \"\\n\".join(\n [f\" {i + 1}. {b.get('fokus', '')} → {b.get('ziel', '')}\" for i, b in enumerate(bloecke)]\n )\n sections.append(f\"\"\"### Hauptteil-Struktur:\n{bloecke_text}\"\"\")\n\n # Blog format: Schluss\n schluss = config.get(\"schluss\", {})\n if schluss:\n sections.append(f\"\"\"### Schluss:\n- Typ: {schluss.get(\"typ\", \"\")}\n- Funktion: {schluss.get(\"funktion\", \"\")}\n- Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")\n\n # Formatierung - handle both styles\n formatierung = config.get(\"formatierung\", {})\n if formatierung:\n # Blog format: ausschluss array\n ausschluss = formatierung.get(\"ausschluss\", [])\n if ausschluss:\n sections.append(f\"\"\"### Formatierung verboten:\n{\", \".join(ausschluss)}\"\"\")\n\n # LinkedIn format: individual fields\n verbote = []\n if formatierung.get(\"emojis\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"emojis\", \"\")):\n verbote.append(\"Emojis\")\n if formatierung.get(\"fettschrift\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"fettschrift\", \"\")):\n verbote.append(\"Fettschrift (**text**)\")\n if formatierung.get(\"markdown\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"markdown\", \"\")):\n verbote.append(\"Markdown-Formatierung\")\n if formatierung.get(\"hashtags\") == \"keine\":\n verbote.append(\"Hashtags\")\n\n if verbote and not ausschluss: # Only add if not already handled by ausschluss\n sections.append(f\"\"\"### Formatierung STRIKT verboten:\n{\", \".join(verbote)}\"\"\")\n\n # Top-level verboten array (LinkedIn format)\n verboten = config.get(\"verboten\", [])\n if verboten:\n sections.append(f\"\"\"### VERBOTEN (strikt einhalten!):\n- {chr(10).join(['- ' + v if not v.startswith('-') else v for v in verboten])}\"\"\")\n\n return \"\\n\\n\".join(sections), output_format, erlaubte_tags"
}
}
{
"tool_response": {
"filePath": "\/var\/www\/scripts\/pipeline\/generators\/config_loader.py",
"oldString": "def parse_structure(config: dict) -> tuple[str, str, list[str]]:\n \"\"\"Parse new-style structure profile into prompt text and format info.\"\"\"\n sections = []\n output_format = None\n erlaubte_tags = []\n\n # Ausgabe-Format\n ausgabe = config.get(\"ausgabe\", {})\n if ausgabe:\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\"erlaubte_tags\", [])\n verbotene_tags = ausgabe.get(\"verbotene_tags\", [])\n\n if output_format == \"body-html\":\n sections.append(f\"\"\"### Ausgabe-Format: HTML\n- **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}\n- **Verboten:** {\", \".join(verbotene_tags)}\n- {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")\n\n # Gesamtaufbau\n aufbau = config.get(\"gesamtaufbau\", {})\n if aufbau:\n sections.append(f\"\"\"### Gesamtaufbau:\n- Form: {aufbau.get(\"form\", \"\")}\n- Dramaturgie: {aufbau.get(\"dramaturgie\", \"\")}\n- Linearität: {aufbau.get(\"linearitaet\", \"\")}\n- Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")\n\n # Einstieg\n einstieg = config.get(\"einstieg\", {})\n if einstieg:\n sections.append(f\"\"\"### Einstieg:\n- Funktion: {einstieg.get(\"funktion\", \"\")}\n- Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}\n- Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")\n\n # Hauptteil-Blöcke\n hauptteil = config.get(\"hauptteil\", {})\n bloecke = hauptteil.get(\"bloecke\", [])\n if bloecke:\n bloecke_text = \"\\n\".join(\n [f\" {i + 1}. {b.get('fokus', '')} → {b.get('ziel', '')}\" for i, b in enumerate(bloecke)]\n )\n sections.append(f\"\"\"### Hauptteil-Struktur:\n{bloecke_text}\"\"\")\n\n # Schluss\n schluss = config.get(\"schluss\", {})\n if schluss:\n sections.append(f\"\"\"### Schluss:\n- Typ: {schluss.get(\"typ\", \"\")}\n- Funktion: {schluss.get(\"funktion\", \"\")}\n- Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")\n\n # Formatierung\n formatierung = config.get(\"formatierung\", {})\n if formatierung:\n ausschluss = formatierung.get(\"ausschluss\", [])\n if ausschluss:\n sections.append(f\"\"\"### Formatierung verboten:\n{\", \".join(ausschluss)}\"\"\")\n\n return \"\\n\\n\".join(sections), output_format, erlaubte_tags",
"newString": "def parse_structure(config: dict) -> tuple[str, str, list[str]]:\n \"\"\"Parse structure profile into prompt text and format info.\n\n Supports both formats:\n - Blog\/HTML format: gesamtaufbau, einstieg, hauptteil.bloecke, schluss, formatierung.ausschluss\n - LinkedIn format: aufbau.hook, aufbau.hauptteil, aufbau.schluss, formatierung.*, verboten[]\n \"\"\"\n sections = []\n output_format = None\n erlaubte_tags = []\n\n # WICHTIG field (LinkedIn format)\n wichtig = config.get(\"WICHTIG\")\n if wichtig:\n sections.append(f\"### WICHTIG:\\n{wichtig}\")\n\n # Ausgabe-Format\n ausgabe = config.get(\"ausgabe\", {})\n if ausgabe:\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\"erlaubte_tags\", [])\n verbotene_tags = ausgabe.get(\"verbotene_tags\", [])\n\n if output_format == \"body-html\":\n sections.append(f\"\"\"### Ausgabe-Format: HTML\n- **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}\n- **Verboten:** {\", \".join(verbotene_tags)}\n- {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")\n elif output_format == \"reiner Text\":\n # LinkedIn plain text format\n zeichen_info = \"\"\n if ausgabe.get(\"zeichen_min\") and ausgabe.get(\"zeichen_max\"):\n zeichen_info = f\"\\n- Länge: {ausgabe.get('zeichen_min')}-{ausgabe.get('zeichen_max')} Zeichen\"\n if ausgabe.get(\"woerter\"):\n zeichen_info += f\" ({ausgabe.get('woerter')} Wörter)\"\n sections.append(f\"\"\"### Ausgabe-Format: Reiner Text\n- KEIN Markdown, KEINE Formatierung\n- Keine **Fettschrift**, keine *Kursivschrift*\n- Keine Überschriften mit # oder ##\n- Nur Fließtext und einfache Zeilenumbrüche{zeichen_info}\"\"\")\n\n # Try Blog format first: gesamtaufbau\n aufbau = config.get(\"gesamtaufbau\", {})\n if aufbau:\n sections.append(f\"\"\"### Gesamtaufbau:\n- Form: {aufbau.get(\"form\", \"\")}\n- Dramaturgie: {aufbau.get(\"dramaturgie\", \"\")}\n- Linearität: {aufbau.get(\"linearitaet\", \"\")}\n- Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")\n\n # Try LinkedIn format: aufbau (with nested hook, hauptteil, etc.)\n linkedin_aufbau = config.get(\"aufbau\", {})\n if linkedin_aufbau and not aufbau: # Only if gesamtaufbau not present\n aufbau_sections = []\n\n # Hook\n hook = linkedin_aufbau.get(\"hook\", {})\n if hook:\n hook_text = f\"### Hook ({hook.get('position', 'Anfang')}):\\n\"\n hook_text += f\"- Stil: {hook.get('stil', '')}\\n\"\n if hook.get(\"beispiele\"):\n hook_text += f\"- Beispiele: {'; '.join(hook['beispiele'][:2])}\\n\"\n if hook.get(\"verboten\"):\n hook_text += f\"- Verboten: {', '.join(hook['verboten'])}\"\n aufbau_sections.append(hook_text)\n\n # Praxisbezug\n praxis = linkedin_aufbau.get(\"praxisbezug\", {})\n if praxis:\n praxis_text = f\"### Praxisbezug ({praxis.get('position', '')}):\\n\"\n praxis_text += f\"- Stil: {praxis.get('stil', '')}\\n\"\n if praxis.get(\"muster\"):\n praxis_text += f\"- Muster: {'; '.join(praxis['muster'][:2])}\"\n aufbau_sections.append(praxis_text)\n\n # Hauptteil\n hauptteil = linkedin_aufbau.get(\"hauptteil\", {})\n if hauptteil:\n hauptteil_text = \"### Hauptteil:\\n\"\n if hauptteil.get(\"struktur\"):\n hauptteil_text += f\"- Struktur: {hauptteil.get('struktur')}\\n\"\n if hauptteil.get(\"theoretische_einordnung\"):\n hauptteil_text += f\"- Theorie: {hauptteil.get('theoretische_einordnung')}\\n\"\n if hauptteil.get(\"differenzierung\"):\n hauptteil_text += f\"- Differenzierung: {hauptteil.get('differenzierung')}\\n\"\n if hauptteil.get(\"dreier_struktur\"):\n hauptteil_text += f\"- Dreier-Struktur: {hauptteil.get('dreier_struktur')}\"\n aufbau_sections.append(hauptteil_text)\n\n # Handlungsempfehlung\n handlung = linkedin_aufbau.get(\"handlungsempfehlung\", {})\n if handlung:\n handlung_text = \"### Handlungsempfehlung:\\n\"\n handlung_text += f\"- Stil: {handlung.get('stil', '')}\\n\"\n if handlung.get(\"muster\"):\n handlung_text += f\"- Muster: {'; '.join(handlung['muster'][:2])}\"\n aufbau_sections.append(handlung_text)\n\n # Schluss (LinkedIn style)\n schluss = linkedin_aufbau.get(\"schluss\", {})\n if schluss:\n schluss_text = \"### Schluss:\\n\"\n if schluss.get(\"soft_cta\"):\n schluss_text += f\"- Stil: {schluss.get('soft_cta')}\\n\"\n if schluss.get(\"muster\"):\n schluss_text += f\"- Muster: {'; '.join(schluss['muster'][:2])}\\n\"\n if schluss.get(\"verboten\"):\n schluss_text += f\"- Verboten: {', '.join(schluss['verboten'])}\"\n aufbau_sections.append(schluss_text)\n\n sections.extend(aufbau_sections)\n\n # Blog format: Einstieg\n einstieg = config.get(\"einstieg\", {})\n if einstieg:\n sections.append(f\"\"\"### Einstieg:\n- Funktion: {einstieg.get(\"funktion\", \"\")}\n- Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}\n- Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")\n\n # Blog format: Hauptteil-Blöcke\n hauptteil = config.get(\"hauptteil\", {})\n bloecke = hauptteil.get(\"bloecke\", [])\n if bloecke:\n bloecke_text = \"\\n\".join(\n [f\" {i + 1}. {b.get('fokus', '')} → {b.get('ziel', '')}\" for i, b in enumerate(bloecke)]\n )\n sections.append(f\"\"\"### Hauptteil-Struktur:\n{bloecke_text}\"\"\")\n\n # Blog format: Schluss\n schluss = config.get(\"schluss\", {})\n if schluss:\n sections.append(f\"\"\"### Schluss:\n- Typ: {schluss.get(\"typ\", \"\")}\n- Funktion: {schluss.get(\"funktion\", \"\")}\n- Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")\n\n # Formatierung - handle both styles\n formatierung = config.get(\"formatierung\", {})\n if formatierung:\n # Blog format: ausschluss array\n ausschluss = formatierung.get(\"ausschluss\", [])\n if ausschluss:\n sections.append(f\"\"\"### Formatierung verboten:\n{\", \".join(ausschluss)}\"\"\")\n\n # LinkedIn format: individual fields\n verbote = []\n if formatierung.get(\"emojis\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"emojis\", \"\")):\n verbote.append(\"Emojis\")\n if formatierung.get(\"fettschrift\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"fettschrift\", \"\")):\n verbote.append(\"Fettschrift (**text**)\")\n if formatierung.get(\"markdown\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"markdown\", \"\")):\n verbote.append(\"Markdown-Formatierung\")\n if formatierung.get(\"hashtags\") == \"keine\":\n verbote.append(\"Hashtags\")\n\n if verbote and not ausschluss: # Only add if not already handled by ausschluss\n sections.append(f\"\"\"### Formatierung STRIKT verboten:\n{\", \".join(verbote)}\"\"\")\n\n # Top-level verboten array (LinkedIn format)\n verboten = config.get(\"verboten\", [])\n if verboten:\n sections.append(f\"\"\"### VERBOTEN (strikt einhalten!):\n- {chr(10).join(['- ' + v if not v.startswith('-') else v for v in verboten])}\"\"\")\n\n return \"\\n\\n\".join(sections), output_format, erlaubte_tags",
"originalFile": "\"\"\"\nConfiguration Loading Functions - Load prompts, profiles, contracts, structures.\n\"\"\"\n\nimport json\nimport sys\n\nsys.path.insert(0, \"\/var\/www\/scripts\/pipeline\")\n\nfrom db import db\n\n\ndef get_prompt(name: str) -> str | None:\n \"\"\"Load prompt from database by name.\"\"\"\n cursor = db.execute(\n \"\"\"SELECT content FROM prompts WHERE name = %s AND is_active = 1 ORDER BY version DESC LIMIT 1\"\"\",\n (name,),\n )\n result = cursor.fetchone()\n cursor.close()\n return result[\"content\"] if result else None\n\n\ndef get_config_item(item_id: int, item_type: str) -> dict | None:\n \"\"\"Load configuration item from content_config table.\"\"\"\n if not item_id:\n return None\n\n cursor = db.execute(\n \"SELECT name, content FROM content_config WHERE id = %s AND type = %s AND status = 'active'\",\n (item_id, item_type),\n )\n result = cursor.fetchone()\n cursor.close()\n\n if result:\n config = json.loads(result[\"content\"]) if isinstance(result[\"content\"], str) else result[\"content\"]\n return {\"name\": result[\"name\"], \"config\": config}\n return None\n\n\ndef get_author_profile(profile_id: int) -> dict | None:\n \"\"\"Load author profile from database.\"\"\"\n return get_config_item(profile_id, \"author_profile\")\n\n\ndef get_contract(contract_id: int) -> dict | None:\n \"\"\"Load content contract from database.\"\"\"\n return get_config_item(contract_id, \"contract\")\n\n\ndef get_structure(structure_id: int) -> dict | None:\n \"\"\"Load content structure from database.\"\"\"\n result = get_config_item(structure_id, \"structure\")\n if result:\n # Structure has additional 'type' field in config\n result[\"type\"] = result[\"config\"].get(\"type\", \"article\")\n return result\n\n\ndef get_order(order_id: int) -> dict | None:\n \"\"\"Load content order with all related data.\"\"\"\n cursor = db.execute(\n \"\"\"SELECT co.*,\n ap.name as profile_name, ap.content as profile_config,\n cc.name as contract_name, cc.content as contract_config,\n cs.name as structure_name, cs.content as structure_config\n FROM content_orders co\n LEFT JOIN content_config ap ON co.author_profile_id = ap.id AND ap.type = 'author_profile'\n LEFT JOIN content_config cc ON co.contract_id = cc.id AND cc.type = 'contract'\n LEFT JOIN content_config cs ON co.structure_id = cs.id AND cs.type = 'structure'\n WHERE co.id = %s\"\"\",\n (order_id,),\n )\n result = cursor.fetchone()\n cursor.close()\n return result\n\n\ndef parse_author_profile(config: dict) -> str:\n \"\"\"Parse new-style author profile (Cary format) into prompt text.\"\"\"\n sections = []\n\n # Haltung\n haltung = config.get(\"haltung\", {})\n if haltung:\n sections.append(f\"\"\"### Haltung:\n- Grundhaltung: {haltung.get(\"grundhaltung\", \"\")}\n- Ausrichtung: {haltung.get(\"ausrichtung\", \"\")}\n- Spannungstoleranz: {haltung.get(\"spannungstoleranz\", \"\")}\n- Vereinfachung: {haltung.get(\"vereinfachung\", \"\")}\"\"\")\n\n # Tonalität\n tonalitaet = config.get(\"tonalitaet\", {})\n if tonalitaet:\n sections.append(f\"\"\"### Tonalität:\n- Charakter: {tonalitaet.get(\"charakter\", \"\")}\n- Stil: {tonalitaet.get(\"stil\", \"\")}\n- Wirkung: {tonalitaet.get(\"wirkung\", \"\")}\n- Abgrenzung: {tonalitaet.get(\"abgrenzung\", \"\")}\"\"\")\n\n # Sprachmodus\n sprachmodus = config.get(\"sprachmodus\", {})\n if sprachmodus:\n sections.append(f\"\"\"### Sprachmodus:\n- Denkstil: {sprachmodus.get(\"denkstil\", \"\")}\n- Aussagenform: {sprachmodus.get(\"aussagenform\", \"\")}\n- Fragenfunktion: {sprachmodus.get(\"fragenfunktion\", \"\")}\"\"\")\n\n # Grammatik und Satzbau - WICHTIG für Verbote\n grammatik = config.get(\"grammatik_und_satzbau\", {})\n if grammatik:\n verbote = []\n if grammatik.get(\"stakkato\") == \"ausgeschlossen\":\n verbote.append(\"Stakkato-Sätze\")\n if grammatik.get(\"einschuebe\") == \"keine\":\n verbote.append(\"Einschübe\")\n if grammatik.get(\"gedankenstriche\") == \"verboten\":\n verbote.append(\"Gedankenstriche (–)\")\n\n sections.append(f\"\"\"### Grammatik und Satzbau:\n- Sätze: {grammatik.get(\"saetze\", \"\")}\n- Rhythmus: {grammatik.get(\"rhythmus\", \"\")}\n- **VERBOTEN:** {\", \".join(verbote) if verbote else \"keine\"}\"\"\")\n\n # Wortwahl\n wortwahl = config.get(\"wortwahl\", {})\n if wortwahl:\n verboten = []\n if wortwahl.get(\"buzzwords\") == \"ausgeschlossen\":\n verboten.append(\"Buzzwords\")\n if wortwahl.get(\"methodennamen\") == \"ausgeschlossen\":\n verboten.append(\"Methodennamen\")\n\n sections.append(f\"\"\"### Wortwahl:\n- Niveau: {wortwahl.get(\"niveau\", \"\")}\n- Begriffe: {wortwahl.get(\"begriffe\", \"\")}\n- **VERBOTEN:** {\", \".join(verboten) if verboten else \"keine\"}\"\"\")\n\n # Adressierung\n adressierung = config.get(\"adressierung\", {})\n if adressierung:\n sections.append(f\"\"\"### Adressierung:\n- Form: {adressierung.get(\"form\", \"Sie\")}\n- Beziehung: {adressierung.get(\"beziehung\", \"\")}\n- Einladung: {adressierung.get(\"einladung\", \"\")}\"\"\")\n\n # Metaphern\n metaphern = config.get(\"metaphern\", {})\n if metaphern:\n sections.append(f\"\"\"### Metaphern:\n- Einsatz: {metaphern.get(\"einsatz\", \"\")}\n- Herkunft: {metaphern.get(\"herkunft\", \"\")}\n- Konsistenz: {metaphern.get(\"konsistenz\", \"\")}\"\"\")\n\n return \"\\n\\n\".join(sections)\n\n\ndef parse_structure(config: dict) -> tuple[str, str, list[str]]:\n \"\"\"Parse new-style structure profile into prompt text and format info.\"\"\"\n sections = []\n output_format = None\n erlaubte_tags = []\n\n # Ausgabe-Format\n ausgabe = config.get(\"ausgabe\", {})\n if ausgabe:\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\"erlaubte_tags\", [])\n verbotene_tags = ausgabe.get(\"verbotene_tags\", [])\n\n if output_format == \"body-html\":\n sections.append(f\"\"\"### Ausgabe-Format: HTML\n- **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}\n- **Verboten:** {\", \".join(verbotene_tags)}\n- {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")\n\n # Gesamtaufbau\n aufbau = config.get(\"gesamtaufbau\", {})\n if aufbau:\n sections.append(f\"\"\"### Gesamtaufbau:\n- Form: {aufbau.get(\"form\", \"\")}\n- Dramaturgie: {aufbau.get(\"dramaturgie\", \"\")}\n- Linearität: {aufbau.get(\"linearitaet\", \"\")}\n- Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")\n\n # Einstieg\n einstieg = config.get(\"einstieg\", {})\n if einstieg:\n sections.append(f\"\"\"### Einstieg:\n- Funktion: {einstieg.get(\"funktion\", \"\")}\n- Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}\n- Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")\n\n # Hauptteil-Blöcke\n hauptteil = config.get(\"hauptteil\", {})\n bloecke = hauptteil.get(\"bloecke\", [])\n if bloecke:\n bloecke_text = \"\\n\".join(\n [f\" {i + 1}. {b.get('fokus', '')} → {b.get('ziel', '')}\" for i, b in enumerate(bloecke)]\n )\n sections.append(f\"\"\"### Hauptteil-Struktur:\n{bloecke_text}\"\"\")\n\n # Schluss\n schluss = config.get(\"schluss\", {})\n if schluss:\n sections.append(f\"\"\"### Schluss:\n- Typ: {schluss.get(\"typ\", \"\")}\n- Funktion: {schluss.get(\"funktion\", \"\")}\n- Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")\n\n # Formatierung\n formatierung = config.get(\"formatierung\", {})\n if formatierung:\n ausschluss = formatierung.get(\"ausschluss\", [])\n if ausschluss:\n sections.append(f\"\"\"### Formatierung verboten:\n{\", \".join(ausschluss)}\"\"\")\n\n return \"\\n\\n\".join(sections), output_format, erlaubte_tags\n",
"structuredPatch": [
{
"oldStart": 157,
"oldLines": 11,
"newStart": 157,
"newLines": 21,
"lines": [
" ",
" ",
" def parse_structure(config: dict) -> tuple[str, str, list[str]]:",
"- \"\"\"Parse new-style structure profile into prompt text and format info.\"\"\"",
"+ \"\"\"Parse structure profile into prompt text and format info.",
"+",
"+ Supports both formats:",
"+ - Blog\/HTML format: gesamtaufbau, einstieg, hauptteil.bloecke, schluss, formatierung.ausschluss",
"+ - LinkedIn format: aufbau.hook, aufbau.hauptteil, aufbau.schluss, formatierung.*, verboten[]",
"+ \"\"\"",
" sections = []",
" output_format = None",
" erlaubte_tags = []",
" ",
"+ # WICHTIG field (LinkedIn format)",
"+ wichtig = config.get(\"WICHTIG\")",
"+ if wichtig:",
"+ sections.append(f\"### WICHTIG:\\n{wichtig}\")",
"+",
" # Ausgabe-Format",
" ausgabe = config.get(\"ausgabe\", {})",
" if ausgabe:"
]
},
{
"oldStart": 174,
"oldLines": 8,
"newStart": 184,
"newLines": 20,
"lines": [
" - **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}",
" - **Verboten:** {\", \".join(verbotene_tags)}",
" - {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")",
"+ elif output_format == \"reiner Text\":",
"+ # LinkedIn plain text format",
"+ zeichen_info = \"\"",
"+ if ausgabe.get(\"zeichen_min\") and ausgabe.get(\"zeichen_max\"):",
"+ zeichen_info = f\"\\n- Länge: {ausgabe.get('zeichen_min')}-{ausgabe.get('zeichen_max')} Zeichen\"",
"+ if ausgabe.get(\"woerter\"):",
"+ zeichen_info += f\" ({ausgabe.get('woerter')} Wörter)\"",
"+ sections.append(f\"\"\"### Ausgabe-Format: Reiner Text",
"+- KEIN Markdown, KEINE Formatierung",
"+- Keine **Fettschrift**, keine *Kursivschrift*",
"+- Keine Überschriften mit # oder ##",
"+- Nur Fließtext und einfache Zeilenumbrüche{zeichen_info}\"\"\")",
" ",
"- # Gesamtaufbau",
"+ # Try Blog format first: gesamtaufbau",
" aufbau = config.get(\"gesamtaufbau\", {})",
" if aufbau:",
" sections.append(f\"\"\"### Gesamtaufbau:"
]
},
{
"oldStart": 184,
"oldLines": 7,
"newStart": 206,
"newLines": 69,
"lines": [
" - Linearität: {aufbau.get(\"linearitaet\", \"\")}",
" - Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")",
" ",
"- # Einstieg",
"+ # Try LinkedIn format: aufbau (with nested hook, hauptteil, etc.)",
"+ linkedin_aufbau = config.get(\"aufbau\", {})",
"+ if linkedin_aufbau and not aufbau: # Only if gesamtaufbau not present",
"+ aufbau_sections = []",
"+",
"+ # Hook",
"+ hook = linkedin_aufbau.get(\"hook\", {})",
"+ if hook:",
"+ hook_text = f\"### Hook ({hook.get('position', 'Anfang')}):\\n\"",
"+ hook_text += f\"- Stil: {hook.get('stil', '')}\\n\"",
"+ if hook.get(\"beispiele\"):",
"+ hook_text += f\"- Beispiele: {'; '.join(hook['beispiele'][:2])}\\n\"",
"+ if hook.get(\"verboten\"):",
"+ hook_text += f\"- Verboten: {', '.join(hook['verboten'])}\"",
"+ aufbau_sections.append(hook_text)",
"+",
"+ # Praxisbezug",
"+ praxis = linkedin_aufbau.get(\"praxisbezug\", {})",
"+ if praxis:",
"+ praxis_text = f\"### Praxisbezug ({praxis.get('position', '')}):\\n\"",
"+ praxis_text += f\"- Stil: {praxis.get('stil', '')}\\n\"",
"+ if praxis.get(\"muster\"):",
"+ praxis_text += f\"- Muster: {'; '.join(praxis['muster'][:2])}\"",
"+ aufbau_sections.append(praxis_text)",
"+",
"+ # Hauptteil",
"+ hauptteil = linkedin_aufbau.get(\"hauptteil\", {})",
"+ if hauptteil:",
"+ hauptteil_text = \"### Hauptteil:\\n\"",
"+ if hauptteil.get(\"struktur\"):",
"+ hauptteil_text += f\"- Struktur: {hauptteil.get('struktur')}\\n\"",
"+ if hauptteil.get(\"theoretische_einordnung\"):",
"+ hauptteil_text += f\"- Theorie: {hauptteil.get('theoretische_einordnung')}\\n\"",
"+ if hauptteil.get(\"differenzierung\"):",
"+ hauptteil_text += f\"- Differenzierung: {hauptteil.get('differenzierung')}\\n\"",
"+ if hauptteil.get(\"dreier_struktur\"):",
"+ hauptteil_text += f\"- Dreier-Struktur: {hauptteil.get('dreier_struktur')}\"",
"+ aufbau_sections.append(hauptteil_text)",
"+",
"+ # Handlungsempfehlung",
"+ handlung = linkedin_aufbau.get(\"handlungsempfehlung\", {})",
"+ if handlung:",
"+ handlung_text = \"### Handlungsempfehlung:\\n\"",
"+ handlung_text += f\"- Stil: {handlung.get('stil', '')}\\n\"",
"+ if handlung.get(\"muster\"):",
"+ handlung_text += f\"- Muster: {'; '.join(handlung['muster'][:2])}\"",
"+ aufbau_sections.append(handlung_text)",
"+",
"+ # Schluss (LinkedIn style)",
"+ schluss = linkedin_aufbau.get(\"schluss\", {})",
"+ if schluss:",
"+ schluss_text = \"### Schluss:\\n\"",
"+ if schluss.get(\"soft_cta\"):",
"+ schluss_text += f\"- Stil: {schluss.get('soft_cta')}\\n\"",
"+ if schluss.get(\"muster\"):",
"+ schluss_text += f\"- Muster: {'; '.join(schluss['muster'][:2])}\\n\"",
"+ if schluss.get(\"verboten\"):",
"+ schluss_text += f\"- Verboten: {', '.join(schluss['verboten'])}\"",
"+ aufbau_sections.append(schluss_text)",
"+",
"+ sections.extend(aufbau_sections)",
"+",
"+ # Blog format: Einstieg",
" einstieg = config.get(\"einstieg\", {})",
" if einstieg:",
" sections.append(f\"\"\"### Einstieg:"
]
},
{
"oldStart": 192,
"oldLines": 7,
"newStart": 276,
"newLines": 7,
"lines": [
" - Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}",
" - Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")",
" ",
"- # Hauptteil-Blöcke",
"+ # Blog format: Hauptteil-Blöcke",
" hauptteil = config.get(\"hauptteil\", {})",
" bloecke = hauptteil.get(\"bloecke\", [])",
" if bloecke:"
]
},
{
"oldStart": 202,
"oldLines": 7,
"newStart": 286,
"newLines": 7,
"lines": [
" sections.append(f\"\"\"### Hauptteil-Struktur:",
" {bloecke_text}\"\"\")",
" ",
"- # Schluss",
"+ # Blog format: Schluss",
" schluss = config.get(\"schluss\", {})",
" if schluss:",
" sections.append(f\"\"\"### Schluss:"
]
},
{
"oldStart": 210,
"oldLines": 12,
"newStart": 294,
"newLines": 34,
"lines": [
" - Funktion: {schluss.get(\"funktion\", \"\")}",
" - Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")",
" ",
"- # Formatierung",
"+ # Formatierung - handle both styles",
" formatierung = config.get(\"formatierung\", {})",
" if formatierung:",
"+ # Blog format: ausschluss array",
" ausschluss = formatierung.get(\"ausschluss\", [])",
" if ausschluss:",
" sections.append(f\"\"\"### Formatierung verboten:",
" {\", \".join(ausschluss)}\"\"\")",
" ",
"+ # LinkedIn format: individual fields",
"+ verbote = []",
"+ if formatierung.get(\"emojis\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"emojis\", \"\")):",
"+ verbote.append(\"Emojis\")",
"+ if formatierung.get(\"fettschrift\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"fettschrift\", \"\")):",
"+ verbote.append(\"Fettschrift (**text**)\")",
"+ if formatierung.get(\"markdown\") == \"verboten\" or \"verboten\" in str(formatierung.get(\"markdown\", \"\")):",
"+ verbote.append(\"Markdown-Formatierung\")",
"+ if formatierung.get(\"hashtags\") == \"keine\":",
"+ verbote.append(\"Hashtags\")",
"+",
"+ if verbote and not ausschluss: # Only add if not already handled by ausschluss",
"+ sections.append(f\"\"\"### Formatierung STRIKT verboten:",
"+{\", \".join(verbote)}\"\"\")",
"+",
"+ # Top-level verboten array (LinkedIn format)",
"+ verboten = config.get(\"verboten\", [])",
"+ if verboten:",
"+ sections.append(f\"\"\"### VERBOTEN (strikt einhalten!):",
"+- {chr(10).join(['- ' + v if not v.startswith('-') else v for v in verboten])}\"\"\")",
"+",
" return \"\\n\\n\".join(sections), output_format, erlaubte_tags"
]
}
],
"userModified": false,
"replaceAll": false
}
}