Protokoll #15138

ID15138
Zeitstempel2025-12-25 13:58:13.673264
Clientroot
IP
Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13

Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens15,175 (Input: 0, Output: 0)
Dauer117 ms
Request-Zeit
Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18
Response-Zeit-

Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/scripts\/pipeline\/generate.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nContent Generation for KI-System - Wrapper Module.\n\nThis module provides backward compatibility by re-exporting all functions\nfrom the generators subpackage. New code should import directly from generators.\n\nRefactored for SRP compliance - see generators\/ for implementation.\n\"\"\"\n\nimport json\nimport sys\n\n# Re-export all public functions from generators package\nfrom generators import (\n    repair_json as _repair_json,\n    get_rag_context,\n    get_semantic_context,\n    get_taxonomy_context,\n    get_prompt,\n    get_config_item,\n    get_author_profile,\n    get_contract,\n    get_structure,\n    get_order,\n    build_generation_prompt,\n    call_llm,\n    generate_content,\n    save_version,\n    save_sources,\n    update_order_status,\n    get_critic,\n    run_critic,\n    run_critique_round,\n    revise_content,\n)\n\n# Also export config_loader parsers with original names for backward compatibility\nfrom generators.config_loader import parse_author_profile as _parse_new_author_profile\nfrom generators.config_loader import parse_structure as _parse_new_structure\n\n__all__ = [\n    \"_repair_json\",\n    \"get_rag_context\",\n    \"get_semantic_context\",\n    \"get_taxonomy_context\",\n    \"get_prompt\",\n    \"get_config_item\",\n    \"get_author_profile\",\n    \"get_contract\",\n    \"get_structure\",\n    \"get_order\",\n    \"_parse_new_author_profile\",\n    \"_parse_new_structure\",\n    \"build_generation_prompt\",\n    \"call_llm\",\n    \"generate_content\",\n    \"save_version\",\n    \"save_sources\",\n    \"update_order_status\",\n    \"get_critic\",\n    \"run_critic\",\n    \"run_critique_round\",\n    \"revise_content\",\n]\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) < 3:\n        print(\"Usage: python generate.py <command> <id> [options]\")\n        print(\"Commands: generate, critique, revise\")\n        sys.exit(1)\n\n    command = sys.argv[1]\n    entity_id = int(sys.argv[2])\n    model = sys.argv[3] if len(sys.argv) > 3 else \"anthropic\"\n\n    if command == \"generate\":\n        result = generate_content(entity_id, model=model)\n    elif command == \"critique\":\n        result = run_critique_round(entity_id, model=model)\n    elif command == \"revise\":\n        result = revise_content(entity_id, model=model)\n    else:\n        print(f\"Unknown command: {command}\")\n        sys.exit(1)\n\n    print(json.dumps(result, ensure_ascii=False, indent=2))\n"
    }
}

Response

{
    "tool_response": {
        "type": "update",
        "filePath": "\/var\/www\/scripts\/pipeline\/generate.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nContent Generation for KI-System - Wrapper Module.\n\nThis module provides backward compatibility by re-exporting all functions\nfrom the generators subpackage. New code should import directly from generators.\n\nRefactored for SRP compliance - see generators\/ for implementation.\n\"\"\"\n\nimport json\nimport sys\n\n# Re-export all public functions from generators package\nfrom generators import (\n    repair_json as _repair_json,\n    get_rag_context,\n    get_semantic_context,\n    get_taxonomy_context,\n    get_prompt,\n    get_config_item,\n    get_author_profile,\n    get_contract,\n    get_structure,\n    get_order,\n    build_generation_prompt,\n    call_llm,\n    generate_content,\n    save_version,\n    save_sources,\n    update_order_status,\n    get_critic,\n    run_critic,\n    run_critique_round,\n    revise_content,\n)\n\n# Also export config_loader parsers with original names for backward compatibility\nfrom generators.config_loader import parse_author_profile as _parse_new_author_profile\nfrom generators.config_loader import parse_structure as _parse_new_structure\n\n__all__ = [\n    \"_repair_json\",\n    \"get_rag_context\",\n    \"get_semantic_context\",\n    \"get_taxonomy_context\",\n    \"get_prompt\",\n    \"get_config_item\",\n    \"get_author_profile\",\n    \"get_contract\",\n    \"get_structure\",\n    \"get_order\",\n    \"_parse_new_author_profile\",\n    \"_parse_new_structure\",\n    \"build_generation_prompt\",\n    \"call_llm\",\n    \"generate_content\",\n    \"save_version\",\n    \"save_sources\",\n    \"update_order_status\",\n    \"get_critic\",\n    \"run_critic\",\n    \"run_critique_round\",\n    \"revise_content\",\n]\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) < 3:\n        print(\"Usage: python generate.py <command> <id> [options]\")\n        print(\"Commands: generate, critique, revise\")\n        sys.exit(1)\n\n    command = sys.argv[1]\n    entity_id = int(sys.argv[2])\n    model = sys.argv[3] if len(sys.argv) > 3 else \"anthropic\"\n\n    if command == \"generate\":\n        result = generate_content(entity_id, model=model)\n    elif command == \"critique\":\n        result = run_critique_round(entity_id, model=model)\n    elif command == \"revise\":\n        result = revise_content(entity_id, model=model)\n    else:\n        print(f\"Unknown command: {command}\")\n        sys.exit(1)\n\n    print(json.dumps(result, ensure_ascii=False, indent=2))\n",
        "structuredPatch": [
            {
                "oldStart": 1,
                "oldLines": 1115,
                "newStart": 1,
                "newLines": 71,
                "lines": [
                    " #!\/usr\/bin\/env python3",
                    " \"\"\"",
                    "-Content Generation for KI-System",
                    "-Generates content using RAG context, author profiles, and contracts.",
                    "+Content Generation for KI-System - Wrapper Module.",
                    "+",
                    "+This module provides backward compatibility by re-exporting all functions",
                    "+from the generators subpackage. New code should import directly from generators.",
                    "+",
                    "+Refactored for SRP compliance - see generators\/ for implementation.",
                    " \"\"\"",
                    " ",
                    " import json",
                    " import sys",
                    " ",
                    "-sys.path.insert(0, \"\/var\/www\/scripts\/pipeline\")",
                    "+# Re-export all public functions from generators package",
                    "+from generators import (",
                    "+    repair_json as _repair_json,",
                    "+    get_rag_context,",
                    "+    get_semantic_context,",
                    "+    get_taxonomy_context,",
                    "+    get_prompt,",
                    "+    get_config_item,",
                    "+    get_author_profile,",
                    "+    get_contract,",
                    "+    get_structure,",
                    "+    get_order,",
                    "+    build_generation_prompt,",
                    "+    call_llm,",
                    "+    generate_content,",
                    "+    save_version,",
                    "+    save_sources,",
                    "+    update_order_status,",
                    "+    get_critic,",
                    "+    run_critic,",
                    "+    run_critique_round,",
                    "+    revise_content,",
                    "+)",
                    " ",
                    "-from config import ANTHROPIC_API_KEY, ANTHROPIC_MODEL, OLLAMA_CHAT_MODEL, OLLAMA_HOST  # noqa: I001, E402",
                    "-from db import db  # noqa: E402",
                    "-from embed import search_similar  # noqa: E402",
                    "+# Also export config_loader parsers with original names for backward compatibility",
                    "+from generators.config_loader import parse_author_profile as _parse_new_author_profile",
                    "+from generators.config_loader import parse_structure as _parse_new_structure",
                    " ",
                    "+__all__ = [",
                    "+    \"_repair_json\",",
                    "+    \"get_rag_context\",",
                    "+    \"get_semantic_context\",",
                    "+    \"get_taxonomy_context\",",
                    "+    \"get_prompt\",",
                    "+    \"get_config_item\",",
                    "+    \"get_author_profile\",",
                    "+    \"get_contract\",",
                    "+    \"get_structure\",",
                    "+    \"get_order\",",
                    "+    \"_parse_new_author_profile\",",
                    "+    \"_parse_new_structure\",",
                    "+    \"build_generation_prompt\",",
                    "+    \"call_llm\",",
                    "+    \"generate_content\",",
                    "+    \"save_version\",",
                    "+    \"save_sources\",",
                    "+    \"update_order_status\",",
                    "+    \"get_critic\",",
                    "+    \"run_critic\",",
                    "+    \"run_critique_round\",",
                    "+    \"revise_content\",",
                    "+]",
                    " ",
                    "-def _repair_json(json_str):",
                    "-    \"\"\"",
                    "-    Attempt to repair common JSON issues from LLM output.",
                    " ",
                    "-    Fixes:",
                    "-    - Unescaped quotes in strings",
                    "-    - Missing commas between array elements",
                    "-    - Trailing commas",
                    "-    - Control characters in strings",
                    "-    \"\"\"",
                    "-    import re",
                    "-",
                    "-    # Remove control characters except newlines and tabs",
                    "-    json_str = re.sub(r\"[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]\", \"\", json_str)",
                    "-",
                    "-    # Fix common issue: missing comma before closing bracket in arrays",
                    "-    json_str = re.sub(r'\"\\s*\\n\\s*]', '\"\\n]', json_str)",
                    "-",
                    "-    # Fix trailing commas before closing brackets\/braces",
                    "-    json_str = re.sub(r\",\\s*}\", \"}\", json_str)",
                    "-    json_str = re.sub(r\",\\s*]\", \"]\", json_str)",
                    "-",
                    "-    # Fix missing commas between array elements (string followed by string)",
                    "-    json_str = re.sub(r'\"\\s*\\n\\s*\"', '\",\\n\"', json_str)",
                    "-",
                    "-    # Fix unescaped quotes within strings (heuristic: quotes not at boundaries)",
                    "-    # This is tricky, so we do a simple fix for common patterns",
                    "-    lines = json_str.split(\"\\n\")",
                    "-    fixed_lines = []",
                    "-    for line in lines:",
                    "-        # Count quotes - if odd number and line has content, try to fix",
                    "-        quote_count = line.count('\"') - line.count('\\\\\"')",
                    "-        if quote_count % 2 != 0 and \":\" in line:",
                    "-            # Try to escape internal quotes (very basic heuristic)",
                    "-            parts = line.split(\":\", 1)",
                    "-            if len(parts) == 2:",
                    "-                key_part = parts[0]",
                    "-                value_part = parts[1]",
                    "-                # If value has odd quotes, try to balance",
                    "-                if value_part.count('\"') % 2 != 0:",
                    "-                    # Add escaped quote or remove problematic char",
                    "-                    value_part = value_part.rstrip().rstrip(\",\")",
                    "-                    if not value_part.endswith('\"'):",
                    "-                        value_part += '\"'",
                    "-                    line = key_part + \":\" + value_part",
                    "-        fixed_lines.append(line)",
                    "-",
                    "-    return \"\\n\".join(fixed_lines)",
                    "-",
                    "-",
                    "-def get_prompt(name):",
                    "-    \"\"\"Load prompt from database by name.\"\"\"",
                    "-    cursor = db.execute(",
                    "-        \"\"\"SELECT content FROM prompts WHERE name = %s AND is_active = 1 ORDER BY version DESC LIMIT 1\"\"\",",
                    "-        (name,),",
                    "-    )",
                    "-    result = cursor.fetchone()",
                    "-    cursor.close()",
                    "-    return result[\"content\"] if result else None",
                    "-",
                    "-",
                    "-def get_rag_context(briefing, collection=\"documents\", limit=5):",
                    "-    \"\"\"",
                    "-    Get relevant context from Qdrant based on briefing.",
                    "-    Returns list of chunks with content and metadata.",
                    "-    \"\"\"",
                    "-    results = search_similar(briefing, collection=collection, limit=limit)",
                    "-",
                    "-    context_items = []",
                    "-    for result in results:",
                    "-        context_items.append(",
                    "-            {",
                    "-                \"content\": result[\"payload\"].get(\"content\", \"\"),",
                    "-                \"source\": result[\"payload\"].get(\"document_title\", \"Unknown\"),",
                    "-                \"score\": round(result[\"score\"], 4),",
                    "-                \"chunk_id\": result[\"payload\"].get(\"chunk_id\"),",
                    "-                \"document_id\": result[\"payload\"].get(\"document_id\"),",
                    "-            }",
                    "-        )",
                    "-",
                    "-    return context_items",
                    "-",
                    "-",
                    "-def get_config_item(item_id, item_type):",
                    "-    \"\"\"Load configuration item from content_config table.\"\"\"",
                    "-    if not item_id:",
                    "-        return None",
                    "-",
                    "-    cursor = db.execute(",
                    "-        \"SELECT name, content FROM content_config WHERE id = %s AND type = %s AND status = 'active'\",",
                    "-        (item_id, item_type),",
                    "-    )",
                    "-    result = cursor.fetchone()",
                    "-    cursor.close()",
                    "-",
                    "-    if result:",
                    "-        config = json.loads(result[\"content\"]) if isinstance(result[\"content\"], str) else result[\"content\"]",
                    "-        return {\"name\": result[\"name\"], \"config\": config}",
                    "-    return None",
                    "-",
                    "-",
                    "-def get_semantic_context(chunk_ids):",
                    "-    \"\"\"",
                    "-    Load entities and relations based on chunk_ids.",
                    "-",
                    "-    Uses the chunk_entities junction table to find relevant entities,",
                    "-    then loads relations between those entities.",
                    "-",
                    "-    Args:",
                    "-        chunk_ids: List of chunk IDs from RAG context",
                    "-",
                    "-    Returns:",
                    "-        dict with 'entities' and 'relations' lists",
                    "-    \"\"\"",
                    "-    if not chunk_ids:",
                    "-        return {\"entities\": [], \"relations\": []}",
                    "-",
                    "-    # Filter out None values",
                    "-    chunk_ids = [cid for cid in chunk_ids if cid is not None]",
                    "-    if not chunk_ids:",
                    "-        return {\"entities\": [], \"relations\": []}",
                    "-",
                    "-    placeholders = \", \".join([\"%s\"] * len(chunk_ids))",
                    "-",
                    "-    # Load entities via chunk_entities",
                    "-    cursor = db.execute(",
                    "-        f\"\"\"SELECT DISTINCT e.id, e.name, e.type, e.description,",
                    "-                  AVG(ce.relevance_score) as relevance",
                    "-           FROM chunk_entities ce",
                    "-           JOIN entities e ON ce.entity_id = e.id",
                    "-           WHERE ce.chunk_id IN ({placeholders})",
                    "-           GROUP BY e.id, e.name, e.type, e.description",
                    "-           ORDER BY relevance DESC",
                    "-           LIMIT 10\"\"\",",
                    "-        tuple(chunk_ids),",
                    "-    )",
                    "-    entities = cursor.fetchall()",
                    "-    cursor.close()",
                    "-",
                    "-    if not entities:",
                    "-        return {\"entities\": [], \"relations\": []}",
                    "-",
                    "-    # Get entity IDs for relation lookup",
                    "-    entity_ids = [e[\"id\"] for e in entities]",
                    "-    entity_placeholders = \", \".join([\"%s\"] * len(entity_ids))",
                    "-",
                    "-    # Load relations between found entities",
                    "-    cursor = db.execute(",
                    "-        f\"\"\"SELECT e1.name as source, er.relation_type, e2.name as target",
                    "-           FROM entity_relations er",
                    "-           JOIN entities e1 ON er.source_entity_id = e1.id",
                    "-           JOIN entities e2 ON er.target_entity_id = e2.id",
                    "-           WHERE e1.id IN ({entity_placeholders}) AND e2.id IN ({entity_placeholders})",
                    "-           LIMIT 15\"\"\",",
                    "-        tuple(entity_ids) + tuple(entity_ids),",
                    "-    )",
                    "-    relations = cursor.fetchall()",
                    "-    cursor.close()",
                    "-",
                    "-    return {\"entities\": entities, \"relations\": relations}",
                    "-",
                    "-",
                    "-def get_taxonomy_context(document_ids):",
                    "-    \"\"\"",
                    "-    Load taxonomy terms for documents.",
                    "-",
                    "-    Args:",
                    "-        document_ids: List of document IDs from RAG context",
                    "-",
                    "-    Returns:",
                    "-        List of taxonomy term dicts with name, slug, confidence",
                    "-    \"\"\"",
                    "-    if not document_ids:",
                    "-        return []",
                    "-",
                    "-    # Filter out None values",
                    "-    document_ids = [did for did in document_ids if did is not None]",
                    "-    if not document_ids:",
                    "-        return []",
                    "-",
                    "-    placeholders = \", \".join([\"%s\"] * len(document_ids))",
                    "-",
                    "-    cursor = db.execute(",
                    "-        f\"\"\"SELECT DISTINCT tt.name, tt.slug, MAX(dt.confidence) as confidence",
                    "-           FROM document_taxonomy dt",
                    "-           JOIN taxonomy_terms tt ON dt.taxonomy_term_id = tt.id",
                    "-           WHERE dt.document_id IN ({placeholders})",
                    "-           GROUP BY tt.id, tt.name, tt.slug",
                    "-           ORDER BY confidence DESC\"\"\",",
                    "-        tuple(document_ids),",
                    "-    )",
                    "-    taxonomy = cursor.fetchall()",
                    "-    cursor.close()",
                    "-",
                    "-    return taxonomy",
                    "-",
                    "-",
                    "-def get_author_profile(profile_id):",
                    "-    \"\"\"Load author profile from database.\"\"\"",
                    "-    return get_config_item(profile_id, \"author_profile\")",
                    "-",
                    "-",
                    "-def get_contract(contract_id):",
                    "-    \"\"\"Load content contract from database.\"\"\"",
                    "-    return get_config_item(contract_id, \"contract\")",
                    "-",
                    "-",
                    "-def get_structure(structure_id):",
                    "-    \"\"\"Load content structure from database.\"\"\"",
                    "-    result = get_config_item(structure_id, \"structure\")",
                    "-    if result:",
                    "-        # Structure has additional 'type' field in config",
                    "-        result[\"type\"] = result[\"config\"].get(\"type\", \"article\")",
                    "-    return result",
                    "-",
                    "-",
                    "-def get_order(order_id):",
                    "-    \"\"\"Load content order with all related data.\"\"\"",
                    "-    cursor = db.execute(",
                    "-        \"\"\"SELECT co.*,",
                    "-                  ap.name as profile_name, ap.content as profile_config,",
                    "-                  cc.name as contract_name, cc.content as contract_config,",
                    "-                  cs.name as structure_name, cs.content as structure_config",
                    "-           FROM content_orders co",
                    "-           LEFT JOIN content_config ap ON co.author_profile_id = ap.id AND ap.type = 'author_profile'",
                    "-           LEFT JOIN content_config cc ON co.contract_id = cc.id AND cc.type = 'contract'",
                    "-           LEFT JOIN content_config cs ON co.structure_id = cs.id AND cs.type = 'structure'",
                    "-           WHERE co.id = %s\"\"\",",
                    "-        (order_id,),",
                    "-    )",
                    "-    result = cursor.fetchone()",
                    "-    cursor.close()",
                    "-    return result",
                    "-",
                    "-",
                    "-def _parse_new_author_profile(config):",
                    "-    \"\"\"Parse new-style author profile (Cary format) into prompt text.\"\"\"",
                    "-    sections = []",
                    "-",
                    "-    # Haltung",
                    "-    haltung = config.get(\"haltung\", {})",
                    "-    if haltung:",
                    "-        sections.append(f\"\"\"### Haltung:",
                    "-- Grundhaltung: {haltung.get(\"grundhaltung\", \"\")}",
                    "-- Ausrichtung: {haltung.get(\"ausrichtung\", \"\")}",
                    "-- Spannungstoleranz: {haltung.get(\"spannungstoleranz\", \"\")}",
                    "-- Vereinfachung: {haltung.get(\"vereinfachung\", \"\")}\"\"\")",
                    "-",
                    "-    # Tonalität",
                    "-    tonalitaet = config.get(\"tonalitaet\", {})",
                    "-    if tonalitaet:",
                    "-        sections.append(f\"\"\"### Tonalität:",
                    "-- Charakter: {tonalitaet.get(\"charakter\", \"\")}",
                    "-- Stil: {tonalitaet.get(\"stil\", \"\")}",
                    "-- Wirkung: {tonalitaet.get(\"wirkung\", \"\")}",
                    "-- Abgrenzung: {tonalitaet.get(\"abgrenzung\", \"\")}\"\"\")",
                    "-",
                    "-    # Sprachmodus",
                    "-    sprachmodus = config.get(\"sprachmodus\", {})",
                    "-    if sprachmodus:",
                    "-        sections.append(f\"\"\"### Sprachmodus:",
                    "-- Denkstil: {sprachmodus.get(\"denkstil\", \"\")}",
                    "-- Aussagenform: {sprachmodus.get(\"aussagenform\", \"\")}",
                    "-- Fragenfunktion: {sprachmodus.get(\"fragenfunktion\", \"\")}\"\"\")",
                    "-",
                    "-    # Grammatik und Satzbau - WICHTIG für Verbote",
                    "-    grammatik = config.get(\"grammatik_und_satzbau\", {})",
                    "-    if grammatik:",
                    "-        verbote = []",
                    "-        if grammatik.get(\"stakkato\") == \"ausgeschlossen\":",
                    "-            verbote.append(\"Stakkato-Sätze\")",
                    "-        if grammatik.get(\"einschuebe\") == \"keine\":",
                    "-            verbote.append(\"Einschübe\")",
                    "-        if grammatik.get(\"gedankenstriche\") == \"verboten\":",
                    "-            verbote.append(\"Gedankenstriche (–)\")",
                    "-",
                    "-        sections.append(f\"\"\"### Grammatik und Satzbau:",
                    "-- Sätze: {grammatik.get(\"saetze\", \"\")}",
                    "-- Rhythmus: {grammatik.get(\"rhythmus\", \"\")}",
                    "-- **VERBOTEN:** {\", \".join(verbote) if verbote else \"keine\"}\"\"\")",
                    "-",
                    "-    # Wortwahl",
                    "-    wortwahl = config.get(\"wortwahl\", {})",
                    "-    if wortwahl:",
                    "-        verboten = []",
                    "-        if wortwahl.get(\"buzzwords\") == \"ausgeschlossen\":",
                    "-            verboten.append(\"Buzzwords\")",
                    "-        if wortwahl.get(\"methodennamen\") == \"ausgeschlossen\":",
                    "-            verboten.append(\"Methodennamen\")",
                    "-",
                    "-        sections.append(f\"\"\"### Wortwahl:",
                    "-- Niveau: {wortwahl.get(\"niveau\", \"\")}",
                    "-- Begriffe: {wortwahl.get(\"begriffe\", \"\")}",
                    "-- **VERBOTEN:** {\", \".join(verboten) if verboten else \"keine\"}\"\"\")",
                    "-",
                    "-    # Adressierung",
                    "-    adressierung = config.get(\"adressierung\", {})",
                    "-    if adressierung:",
                    "-        sections.append(f\"\"\"### Adressierung:",
                    "-- Form: {adressierung.get(\"form\", \"Sie\")}",
                    "-- Beziehung: {adressierung.get(\"beziehung\", \"\")}",
                    "-- Einladung: {adressierung.get(\"einladung\", \"\")}\"\"\")",
                    "-",
                    "-    # Metaphern",
                    "-    metaphern = config.get(\"metaphern\", {})",
                    "-    if metaphern:",
                    "-        sections.append(f\"\"\"### Metaphern:",
                    "-- Einsatz: {metaphern.get(\"einsatz\", \"\")}",
                    "-- Herkunft: {metaphern.get(\"herkunft\", \"\")}",
                    "-- Konsistenz: {metaphern.get(\"konsistenz\", \"\")}\"\"\")",
                    "-",
                    "-    return \"\\n\\n\".join(sections)",
                    "-",
                    "-",
                    "-def _parse_new_structure(config):",
                    "-    \"\"\"Parse new-style structure profile into prompt text and format info.\"\"\"",
                    "-    sections = []",
                    "-    output_format = None",
                    "-    erlaubte_tags = []",
                    "-",
                    "-    # Ausgabe-Format",
                    "-    ausgabe = config.get(\"ausgabe\", {})",
                    "-    if ausgabe:",
                    "-        output_format = ausgabe.get(\"format\", \"markdown\")",
                    "-        erlaubte_tags = ausgabe.get(\"erlaubte_tags\", [])",
                    "-        verbotene_tags = ausgabe.get(\"verbotene_tags\", [])",
                    "-",
                    "-        if output_format == \"body-html\":",
                    "-            sections.append(f\"\"\"### Ausgabe-Format: HTML",
                    "-- **Nur diese Tags verwenden:** {\", \".join(erlaubte_tags)}",
                    "-- **Verboten:** {\", \".join(verbotene_tags)}",
                    "-- {ausgabe.get(\"hinweis\", \"Sauberes semantisches HTML\")}\"\"\")",
                    "-",
                    "-    # Gesamtaufbau",
                    "-    aufbau = config.get(\"gesamtaufbau\", {})",
                    "-    if aufbau:",
                    "-        sections.append(f\"\"\"### Gesamtaufbau:",
                    "-- Form: {aufbau.get(\"form\", \"\")}",
                    "-- Dramaturgie: {aufbau.get(\"dramaturgie\", \"\")}",
                    "-- Linearität: {aufbau.get(\"linearitaet\", \"\")}",
                    "-- Themensprünge: {aufbau.get(\"themenspruenge\", \"\")}\"\"\")",
                    "-",
                    "-    # Einstieg",
                    "-    einstieg = config.get(\"einstieg\", {})",
                    "-    if einstieg:",
                    "-        sections.append(f\"\"\"### Einstieg:",
                    "-- Funktion: {einstieg.get(\"funktion\", \"\")}",
                    "-- Inhaltstyp: {einstieg.get(\"inhaltstyp\", \"\")}",
                    "-- Ausschluss: {einstieg.get(\"ausschluss\", \"\")}\"\"\")",
                    "-",
                    "-    # Hauptteil-Blöcke",
                    "-    hauptteil = config.get(\"hauptteil\", {})",
                    "-    bloecke = hauptteil.get(\"bloecke\", [])",
                    "-    if bloecke:",
                    "-        bloecke_text = \"\\n\".join(",
                    "-            [f\"  {i + 1}. {b.get('fokus', '')} → {b.get('ziel', '')}\" for i, b in enumerate(bloecke)]",
                    "-        )",
                    "-        sections.append(f\"\"\"### Hauptteil-Struktur:",
                    "-{bloecke_text}\"\"\")",
                    "-",
                    "-    # Schluss",
                    "-    schluss = config.get(\"schluss\", {})",
                    "-    if schluss:",
                    "-        sections.append(f\"\"\"### Schluss:",
                    "-- Typ: {schluss.get(\"typ\", \"\")}",
                    "-- Funktion: {schluss.get(\"funktion\", \"\")}",
                    "-- Abgrenzung: {schluss.get(\"abgrenzung\", \"\")}\"\"\")",
                    "-",
                    "-    # Formatierung",
                    "-    formatierung = config.get(\"formatierung\", {})",
                    "-    if formatierung:",
                    "-        ausschluss = formatierung.get(\"ausschluss\", [])",
                    "-        if ausschluss:",
                    "-            sections.append(f\"\"\"### Formatierung verboten:",
                    "-{\", \".join(ausschluss)}\"\"\")",
                    "-",
                    "-    return \"\\n\\n\".join(sections), output_format, erlaubte_tags",
                    "-",
                    "-",
                    "-def build_generation_prompt(briefing, context, profile, contract, structure=None, semantic=None, taxonomy=None):",
                    "-    \"\"\"Build the content generation prompt.\"\"\"",
                    "-",
                    "-    # Format context",
                    "-    context_text = \"\"",
                    "-    for i, ctx in enumerate(context, 1):",
                    "-        context_text += f\"\\n[Quelle {i}: {ctx['source']}]\\n{ctx['content']}\\n\"",
                    "-",
                    "-    # Build semantic context (entities and relations)",
                    "-    semantic_text = \"\"",
                    "-    if semantic:",
                    "-        if semantic.get(\"entities\"):",
                    "-            semantic_text += \"\\n## Relevante Konzepte\\n\"",
                    "-            for e in semantic[\"entities\"][:10]:",
                    "-                desc = e.get(\"description\") or \"\"",
                    "-                if desc:",
                    "-                    semantic_text += f\"- **{e['name']}** ({e['type']}): {desc[:100]}\\n\"",
                    "-                else:",
                    "-                    semantic_text += f\"- **{e['name']}** ({e['type']})\\n\"",
                    "-",
                    "-        if semantic.get(\"relations\"):",
                    "-            semantic_text += \"\\n## Konzept-Beziehungen\\n\"",
                    "-            for r in semantic[\"relations\"][:10]:",
                    "-                semantic_text += f\"- {r['source']} → {r['relation_type']} → {r['target']}\\n\"",
                    "-",
                    "-    # Build taxonomy context",
                    "-    taxonomy_text = \"\"",
                    "-    if taxonomy:",
                    "-        taxonomy_text = \"\\n## Thematische Einordnung\\n\"",
                    "-        taxonomy_text += \", \".join([t[\"name\"] for t in taxonomy])",
                    "-",
                    "-    # Build profile instructions - detect new vs old format",
                    "-    profile_text = \"\"",
                    "-    if profile:",
                    "-        config = profile.get(\"config\", {})",
                    "-",
                    "-        # Detect new format (has \"haltung\" or \"tonalitaet\" at top level)",
                    "-        if \"haltung\" in config or \"tonalitaet\" in config or \"grammatik_und_satzbau\" in config:",
                    "-            # New Cary-style profile",
                    "-            profile_text = f\"\"\"",
                    "-## Autorenprofil: {profile.get(\"name\", \"Standard\")}",
                    "-",
                    "-{_parse_new_author_profile(config)}",
                    "-\"\"\"",
                    "-        else:",
                    "-            # Old format - keep backwards compatibility",
                    "-            autorenprofil = config.get(\"autorenprofil\", config)",
                    "-",
                    "-            stimme = autorenprofil.get(\"stimme\", {})",
                    "-            stimme_text = \"\"",
                    "-            if stimme:",
                    "-                stimme_text = f\"\"\"",
                    "-### Stimme\/Tonalität:",
                    "-- Ton: {stimme.get(\"ton\", \"neutral\")}",
                    "-- Perspektive: {stimme.get(\"perspektive\", \"neutral\")}",
                    "-- Komplexität: {stimme.get(\"komplexitaet\", \"mittel\")}\"\"\"",
                    "-",
                    "-            stil = autorenprofil.get(\"stil\", {})",
                    "-            stil_text = \"\"",
                    "-            if stil:",
                    "-                stil_text = f\"\"\"",
                    "-### Stil:",
                    "-- Fachsprache: {\"Ja\" if stil.get(\"fachsprache\", False) else \"Nein\"}",
                    "-- Satzlänge: {stil.get(\"satzlaenge\", \"mittel\")}\"\"\"",
                    "-",
                    "-            tabus = autorenprofil.get(\"tabus\", [])",
                    "-            tabus_text = \"\"",
                    "-            if tabus:",
                    "-                tabus_text = f\"\"\"",
                    "-### Zu vermeiden:",
                    "-{\", \".join(tabus[:5])}\"\"\"",
                    "-",
                    "-            profile_text = f\"\"\"",
                    "-## Autorenprofil: {profile.get(\"name\", \"Standard\")}",
                    "-{stimme_text}",
                    "-{stil_text}",
                    "-{tabus_text}",
                    "-\"\"\"",
                    "-",
                    "-    # Build contract requirements",
                    "-    contract_text = \"\"",
                    "-    if contract:",
                    "-        config = contract.get(\"config\", {})",
                    "-        req = config.get(\"requirements\", {})",
                    "-        contract_text = f\"\"\"",
                    "-Contract: {contract.get(\"name\", \"Standard\")}",
                    "-- Wortanzahl: {req.get(\"min_words\", 500)} - {req.get(\"max_words\", 5000)} Wörter",
                    "-\"\"\"",
                    "-",
                    "-    # Build structure instructions - detect new vs old format",
                    "-    structure_text = \"\"",
                    "-    output_format = \"markdown\"",
                    "-    erlaubte_tags = []",
                    "-",
                    "-    if structure:",
                    "-        config = structure.get(\"config\", {})",
                    "-",
                    "-        # Detect new format (has \"ausgabe\" at top level)",
                    "-        if \"ausgabe\" in config or \"gesamtaufbau\" in config:",
                    "-            # New Blog-Struktur format",
                    "-            parsed_text, output_format, erlaubte_tags = _parse_new_structure(config)",
                    "-            structure_text = f\"\"\"",
                    "-## Struktur: {structure.get(\"name\", \"\")}",
                    "-",
                    "-{parsed_text}",
                    "-\"\"\"",
                    "-        else:",
                    "-            # Old format",
                    "-            structure_text = f\"\"\"",
                    "-Struktur-Template: {structure.get(\"name\", \"\")}",
                    "-- Abschnitte: {json.dumps(config.get(\"sections\", []), ensure_ascii=False)}",
                    "-\"\"\"",
                    "-",
                    "-    # Build format instruction based on structure's ausgabe",
                    "-    format_instruction = \"\"",
                    "-    if output_format == \"body-html\":",
                    "-        tags_str = \", \".join(erlaubte_tags) if erlaubte_tags else \"h1, h2, h3, h4, p, a, ol, ul, li, strong, table, hr\"",
                    "-        format_instruction = f\"\"\"7. **KRITISCH - Ausgabe als sauberes HTML:**",
                    "-   - NUR diese Tags: {tags_str}",
                    "-   - KEIN Markdown (keine ##, keine **, keine -)",
                    "-   - KEIN div, span, br, img, script, style",
                    "-   - Jeder Absatz in <p>-Tags",
                    "-   - Überschriften als <h2>, <h3>, <h4>",
                    "-   - Listen als <ul>\/<ol> mit <li>\"\"\"",
                    "-",
                    "-    # Load generate prompt template from database",
                    "-    # Note: Assumes db.connect() was called by the caller (generate_content)",
                    "-    prompt_template = get_prompt(\"content-generate\")",
                    "-",
                    "-    if prompt_template:",
                    "-        prompt = prompt_template.format(",
                    "-            profile_text=profile_text,",
                    "-            contract_text=contract_text,",
                    "-            structure_text=structure_text,",
                    "-            context=context_text,",
                    "-            briefing=briefing,",
                    "-            format_instruction=format_instruction,",
                    "-            semantic_text=semantic_text,",
                    "-            taxonomy_text=taxonomy_text,",
                    "-        )",
                    "-    else:",
                    "-        # Fallback if prompt not in DB",
                    "-        prompt = f\"\"\"Du bist ein professioneller Content-Autor. Erstelle basierend auf dem Briefing und dem bereitgestellten Kontext einen hochwertigen Text.",
                    "-",
                    "-{profile_text}",
                    "-{contract_text}",
                    "-{structure_text}",
                    "-{semantic_text}",
                    "-{taxonomy_text}",
                    "-",
                    "-## Kontext aus der Wissensbasis:",
                    "-{context_text}",
                    "-",
                    "-## Briefing:",
                    "-{briefing}",
                    "-",
                    "-## Anweisungen:",
                    "-1. Nutze die Informationen aus dem Kontext als Grundlage",
                    "-2. Halte dich an das Autorenprofil und den Schreibstil",
                    "-3. Beachte die Vorgaben aus dem Contract",
                    "-4. Strukturiere den Text gemäß dem Template (falls angegeben)",
                    "-5. Schreibe auf Deutsch",
                    "-6. Kennzeichne verwendete Quellen",
                    "-7. Berücksichtige die relevanten Konzepte und deren Beziehungen",
                    "-{format_instruction}",
                    "-",
                    "-Erstelle nun den Content:\"\"\"",
                    "-",
                    "-    return prompt",
                    "-",
                    "-",
                    "-def call_llm(prompt, model=\"anthropic\", client_name=\"content-studio\"):",
                    "-    \"\"\"",
                    "-    Call LLM to generate content with protokoll logging.",
                    "-",
                    "-    Args:",
                    "-        prompt: The prompt to send",
                    "-        model: 'anthropic' or 'ollama'",
                    "-        client_name: Identifier for protokoll logging",
                    "-",
                    "-    Returns:",
                    "-        Generated text content",
                    "-    \"\"\"",
                    "-    import time",
                    "-",
                    "-    start_time = time.time()",
                    "-    response_text = \"\"",
                    "-    tokens_input = 0",
                    "-    tokens_output = 0",
                    "-    model_name = \"\"",
                    "-    error_message = None",
                    "-    status = \"completed\"",
                    "-",
                    "-    try:",
                    "-        if model == \"anthropic\" and ANTHROPIC_API_KEY:",
                    "-            import anthropic",
                    "-",
                    "-            client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)",
                    "-            model_name = ANTHROPIC_MODEL",
                    "-",
                    "-            message = client.messages.create(",
                    "-                model=ANTHROPIC_MODEL, max_tokens=4000, messages=[{\"role\": \"user\", \"content\": prompt}]",
                    "-            )",
                    "-            response_text = message.content[0].text",
                    "-",
                    "-            # Extract token usage from Anthropic response",
                    "-            if hasattr(message, \"usage\"):",
                    "-                tokens_input = getattr(message.usage, \"input_tokens\", 0)",
                    "-                tokens_output = getattr(message.usage, \"output_tokens\", 0)",
                    "-        else:",
                    "-            # Fallback to Ollama",
                    "-            import requests",
                    "-",
                    "-            model_name = OLLAMA_CHAT_MODEL",
                    "-",
                    "-            response = requests.post(",
                    "-                f\"{OLLAMA_HOST}\/api\/generate\",",
                    "-                json={\"model\": OLLAMA_CHAT_MODEL, \"prompt\": prompt, \"stream\": False},",
                    "-                timeout=900,  # 15 min for large models",
                    "-            )",
                    "-            response.raise_for_status()",
                    "-            result = response.json()",
                    "-            response_text = result.get(\"response\", \"\")",
                    "-",
                    "-            # Extract token counts from Ollama response",
                    "-            tokens_input = result.get(\"prompt_eval_count\", 0)",
                    "-            tokens_output = result.get(\"eval_count\", 0)",
                    "-",
                    "-        # Clean up model artifacts (Gemma, Llama, etc.)",
                    "-        artifacts = [",
                    "-            \"<start_of_turn>\",",
                    "-            \"<\/start_of_turn>\",",
                    "-            \"<end_of_turn>\",",
                    "-            \"<\/end_of_turn>\",",
                    "-            \"<\/s>\",",
                    "-            \"<|eot_id|>\",",
                    "-            \"<|im_end|>\",",
                    "-        ]",
                    "-        for artifact in artifacts:",
                    "-            response_text = response_text.replace(artifact, \"\").strip()",
                    "-",
                    "-    except Exception as e:",
                    "-        status = \"error\"",
                    "-        error_message = str(e)",
                    "-        raise",
                    "-",
                    "-    finally:",
                    "-        # Calculate duration",
                    "-        duration_ms = int((time.time() - start_time) * 1000)",
                    "-",
                    "-        # Log to protokoll",
                    "-        db.log_to_protokoll(",
                    "-            client_name=client_name,",
                    "-            request=prompt,",
                    "-            response=response_text if status == \"completed\" else None,",
                    "-            model_name=model_name,",
                    "-            tokens_input=tokens_input,",
                    "-            tokens_output=tokens_output,",
                    "-            duration_ms=duration_ms,",
                    "-            status=status,",
                    "-            error_message=error_message,",
                    "-        )",
                    "-",
                    "-    return response_text",
                    "-",
                    "-",
                    "-def save_version(order_id, content, version_number=1, output_format=\"markdown\"):",
                    "-    \"\"\"Save content version to database.\"\"\"",
                    "-    content_json = json.dumps({\"text\": content, \"format\": output_format})",
                    "-",
                    "-    cursor = db.execute(",
                    "-        \"\"\"INSERT INTO content_versions (order_id, version_number, content)",
                    "-           VALUES (%s, %s, %s)\"\"\",",
                    "-        (order_id, version_number, content_json),",
                    "-    )",
                    "-    db.commit()",
                    "-    version_id = cursor.lastrowid",
                    "-    cursor.close()",
                    "-    return version_id",
                    "-",
                    "-",
                    "-def save_sources(order_id, context):",
                    "-    \"\"\"Save RAG sources to content_sources.\"\"\"",
                    "-    for ctx in context:",
                    "-        # Try to find chunk_id by content match",
                    "-        cursor = db.execute(\"SELECT id FROM chunks WHERE content LIKE %s LIMIT 1\", (ctx[\"content\"][:100] + \"%\",))",
                    "-        chunk = cursor.fetchone()",
                    "-        cursor.close()",
                    "-",
                    "-        if chunk:",
                    "-            cursor = db.execute(",
                    "-                \"\"\"INSERT IGNORE INTO content_sources (order_id, chunk_id, relevance_score)",
                    "-                   VALUES (%s, %s, %s)\"\"\",",
                    "-                (order_id, chunk[\"id\"], ctx[\"score\"]),",
                    "-            )",
                    "-            db.commit()",
                    "-            cursor.close()",
                    "-",
                    "-",
                    "-def update_order_status(order_id, status):",
                    "-    \"\"\"Update order status.\"\"\"",
                    "-    cursor = db.execute(\"UPDATE content_orders SET status = %s, updated_at = NOW() WHERE id = %s\", (status, order_id))",
                    "-    db.commit()",
                    "-    cursor.close()",
                    "-",
                    "-",
                    "-def generate_content(order_id, model=\"anthropic\", collection=\"documents\", context_limit=5):",
                    "-    \"\"\"",
                    "-    Main content generation function.",
                    "-",
                    "-    Args:",
                    "-        order_id: Content order ID",
                    "-        model: 'anthropic' or 'ollama'",
                    "-        collection: Qdrant collection to search",
                    "-        context_limit: Number of context chunks",
                    "-",
                    "-    Returns:",
                    "-        dict with version_id, content, sources",
                    "-    \"\"\"",
                    "-    db.connect()",
                    "-",
                    "-    try:",
                    "-        # Load order",
                    "-        order = get_order(order_id)",
                    "-        if not order:",
                    "-            return {\"error\": f\"Order {order_id} not found\"}",
                    "-",
                    "-        # Update status",
                    "-        update_order_status(order_id, \"generating\")",
                    "-",
                    "-        # Get RAG context",
                    "-        context = get_rag_context(order[\"briefing\"], collection, context_limit)",
                    "-",
                    "-        # Extract chunk_ids and document_ids for semantic context",
                    "-        chunk_ids = [c.get(\"chunk_id\") for c in context if c.get(\"chunk_id\")]",
                    "-        doc_ids = list({c.get(\"document_id\") for c in context if c.get(\"document_id\")})",
                    "-",
                    "-        # Load semantic context (entities and relations)",
                    "-        semantic = get_semantic_context(chunk_ids) if chunk_ids else None",
                    "-",
                    "-        # Load taxonomy context",
                    "-        taxonomy = get_taxonomy_context(doc_ids) if doc_ids else None",
                    "-",
                    "-        # Build profile\/contract\/structure",
                    "-        profile = None",
                    "-        if order.get(\"profile_config\"):",
                    "-            config = (",
                    "-                json.loads(order[\"profile_config\"])",
                    "-                if isinstance(order[\"profile_config\"], str)",
                    "-                else order[\"profile_config\"]",
                    "-            )",
                    "-            profile = {\"name\": order[\"profile_name\"], \"config\": config}",
                    "-",
                    "-        contract = None",
                    "-        if order.get(\"contract_config\"):",
                    "-            config = (",
                    "-                json.loads(order[\"contract_config\"])",
                    "-                if isinstance(order[\"contract_config\"], str)",
                    "-                else order[\"contract_config\"]",
                    "-            )",
                    "-            contract = {\"name\": order[\"contract_name\"], \"config\": config}",
                    "-",
                    "-        structure = None",
                    "-        output_format = \"markdown\"  # Default",
                    "-        if order.get(\"structure_config\"):",
                    "-            config = (",
                    "-                json.loads(order[\"structure_config\"])",
                    "-                if isinstance(order[\"structure_config\"], str)",
                    "-                else order[\"structure_config\"]",
                    "-            )",
                    "-            structure = {\"name\": order[\"structure_name\"], \"config\": config}",
                    "-            # Determine output format from structure",
                    "-            ausgabe = config.get(\"ausgabe\", {})",
                    "-            output_format = ausgabe.get(\"format\", \"markdown\")",
                    "-",
                    "-        # Build prompt",
                    "-        prompt = build_generation_prompt(",
                    "-            order[\"briefing\"], context, profile, contract, structure, semantic=semantic, taxonomy=taxonomy",
                    "-        )",
                    "-",
                    "-        # Generate content",
                    "-        content = call_llm(prompt, model, client_name=\"content-studio-generate\")",
                    "-",
                    "-        # Get current version number",
                    "-        cursor = db.execute(",
                    "-            \"SELECT MAX(version_number) as max_v FROM content_versions WHERE order_id = %s\", (order_id,)",
                    "-        )",
                    "-        result = cursor.fetchone()",
                    "-        cursor.close()",
                    "-        version_number = (result[\"max_v\"] or 0) + 1",
                    "-",
                    "-        # Save version with correct format",
                    "-        version_id = save_version(order_id, content, version_number, output_format)",
                    "-",
                    "-        # Save sources",
                    "-        save_sources(order_id, context)",
                    "-",
                    "-        # Update status",
                    "-        update_order_status(order_id, \"critique\")",
                    "-",
                    "-        return {",
                    "-            \"success\": True,",
                    "-            \"order_id\": order_id,",
                    "-            \"version_id\": version_id,",
                    "-            \"version_number\": version_number,",
                    "-            \"content\": content,",
                    "-            \"sources\": [{\"source\": c[\"source\"], \"score\": c[\"score\"]} for c in context],",
                    "-        }",
                    "-",
                    "-    except Exception as e:",
                    "-        update_order_status(order_id, \"draft\")",
                    "-        return {\"error\": str(e)}",
                    "-    finally:",
                    "-        db.disconnect()",
                    "-",
                    "-",
                    "-def get_critic(critic_id):",
                    "-    \"\"\"Load critic from database.\"\"\"",
                    "-    cursor = db.execute(",
                    "-        \"\"\"SELECT c.*, p.content as prompt_content",
                    "-           FROM critics c",
                    "-           LEFT JOIN prompts p ON c.prompt_id = p.id",
                    "-           WHERE c.id = %s AND c.is_active = 1\"\"\",",
                    "-        (critic_id,),",
                    "-    )",
                    "-    result = cursor.fetchone()",
                    "-    cursor.close()",
                    "-    return result",
                    "-",
                    "-",
                    "-def run_critic(content, critic_id, model=\"anthropic\"):",
                    "-    \"\"\"",
                    "-    Run a single critic on content.",
                    "-",
                    "-    Returns:",
                    "-        dict with feedback and rating",
                    "-    \"\"\"",
                    "-    db.connect()",
                    "-",
                    "-    try:",
                    "-        critic = get_critic(critic_id)",
                    "-        if not critic:",
                    "-            return {\"error\": f\"Critic {critic_id} not found\"}",
                    "-",
                    "-        fokus = json.loads(critic[\"fokus\"]) if isinstance(critic[\"fokus\"], str) else critic[\"fokus\"]",
                    "-        fokus_str = \", \".join(fokus)",
                    "-",
                    "-        # Load prompt from database (via critic.prompt_id or fallback to generic)",
                    "-        prompt_template = critic.get(\"prompt_content\")",
                    "-        if not prompt_template:",
                    "-            prompt_template = get_prompt(\"critic-generic\")",
                    "-        if not prompt_template:",
                    "-            # Ultimate fallback - should never happen if DB is properly set up",
                    "-            prompt_template = \"\"\"Du bist ein kritischer Lektor mit dem Fokus auf: {fokus}",
                    "-",
                    "-Analysiere den folgenden Text und gib strukturiertes Feedback:",
                    "-",
                    "-## Text:",
                    "-{content}",
                    "-",
                    "-## Deine Aufgabe:",
                    "-1. Prüfe den Text auf die Aspekte: {fokus}",
                    "-2. Identifiziere konkrete Verbesserungspunkte",
                    "-3. Bewerte die Qualität (1-10)",
                    "-",
                    "-Antworte im JSON-Format:",
                    "-{{",
                    "-  \"rating\": 8,",
                    "-  \"passed\": true,",
                    "-  \"issues\": [\"Issue 1\", \"Issue 2\"],",
                    "-  \"suggestions\": [\"Suggestion 1\"],",
                    "-  \"summary\": \"Kurze Zusammenfassung\"",
                    "-}}\"\"\"",
                    "-",
                    "-        # Format prompt with variables",
                    "-        prompt = prompt_template.format(fokus=fokus_str, content=content)",
                    "-",
                    "-        response = call_llm(prompt, model, client_name=\"content-studio-critique\")",
                    "-",
                    "-        # Parse JSON from response with robust error handling",
                    "-        import re",
                    "-",
                    "-        json_match = re.search(r\"\\{[\\s\\S]*\\}\", response)",
                    "-        if json_match:",
                    "-            json_str = json_match.group()",
                    "-            try:",
                    "-                feedback = json.loads(json_str)",
                    "-                feedback[\"critic_name\"] = critic[\"name\"]",
                    "-                return feedback",
                    "-            except json.JSONDecodeError:",
                    "-                # Try to repair common JSON issues",
                    "-                repaired = _repair_json(json_str)",
                    "-                try:",
                    "-                    feedback = json.loads(repaired)",
                    "-                    feedback[\"critic_name\"] = critic[\"name\"]",
                    "-                    return feedback",
                    "-                except json.JSONDecodeError:",
                    "-                    pass",
                    "-",
                    "-        return {",
                    "-            \"critic_name\": critic[\"name\"],",
                    "-            \"rating\": 5,",
                    "-            \"passed\": False,",
                    "-            \"issues\": [\"Konnte Feedback nicht parsen\"],",
                    "-            \"suggestions\": [],",
                    "-            \"summary\": response[:500],",
                    "-        }",
                    "-",
                    "-    except Exception as e:",
                    "-        return {\"error\": str(e)}",
                    "-    finally:",
                    "-        db.disconnect()",
                    "-",
                    "-",
                    "-def run_critique_round(version_id, model=\"anthropic\"):",
                    "-    \"\"\"",
                    "-    Run all active critics on a content version.",
                    "-",
                    "-    Returns:",
                    "-        dict with all critique results",
                    "-    \"\"\"",
                    "-    db.connect()",
                    "-",
                    "-    try:",
                    "-        # Get version content",
                    "-        cursor = db.execute(",
                    "-            \"SELECT cv.*, co.id as order_id, co.current_critique_round FROM content_versions cv JOIN content_orders co ON cv.order_id = co.id WHERE cv.id = %s\",",
                    "-            (version_id,),",
                    "-        )",
                    "-        version = cursor.fetchone()",
                    "-        cursor.close()",
                    "-",
                    "-        if not version:",
                    "-            return {\"error\": \"Version not found\"}",
                    "-",
                    "-        content_data = json.loads(version[\"content\"]) if isinstance(version[\"content\"], str) else version[\"content\"]",
                    "-        content_text = content_data.get(\"text\", \"\")",
                    "-",
                    "-        # Get all active critics",
                    "-        cursor = db.execute(\"SELECT id, name FROM critics WHERE is_active = 1 ORDER BY sort_order\")",
                    "-        critics = cursor.fetchall()",
                    "-        cursor.close()",
                    "-",
                    "-        # Increment critique round",
                    "-        new_round = (version[\"current_critique_round\"] or 0) + 1",
                    "-        cursor = db.execute(",
                    "-            \"UPDATE content_orders SET current_critique_round = %s WHERE id = %s\", (new_round, version[\"order_id\"])",
                    "-        )",
                    "-        db.commit()",
                    "-        cursor.close()",
                    "-",
                    "-        # Run each critic",
                    "-        results = []",
                    "-        all_passed = True",
                    "-",
                    "-        for critic in critics:",
                    "-            db.disconnect()  # Disconnect before calling run_critic",
                    "-            feedback = run_critic(content_text, critic[\"id\"], model)",
                    "-            db.connect()  # Reconnect",
                    "-",
                    "-            if \"error\" not in feedback:",
                    "-                # Save critique",
                    "-                cursor = db.execute(",
                    "-                    \"\"\"INSERT INTO content_critiques (version_id, critic_id, round, feedback)",
                    "-                       VALUES (%s, %s, %s, %s)\"\"\",",
                    "-                    (version_id, critic[\"id\"], new_round, json.dumps(feedback)),",
                    "-                )",
                    "-                db.commit()",
                    "-                cursor.close()",
                    "-",
                    "-                if not feedback.get(\"passed\", True):",
                    "-                    all_passed = False",
                    "-",
                    "-            results.append(feedback)",
                    "-",
                    "-        # Update order status based on results",
                    "-        if all_passed:",
                    "-            update_order_status(version[\"order_id\"], \"validate\")",
                    "-        else:",
                    "-            update_order_status(version[\"order_id\"], \"revision\")",
                    "-",
                    "-        return {\"success\": True, \"round\": new_round, \"critiques\": results, \"all_passed\": all_passed}",
                    "-",
                    "-    except Exception as e:",
                    "-        return {\"error\": str(e)}",
                    "-    finally:",
                    "-        db.disconnect()",
                    "-",
                    "-",
                    "-def revise_content(version_id, model=\"anthropic\"):",
                    "-    \"\"\"",
                    "-    Create a revision based on critique feedback.",
                    "-",
                    "-    Returns:",
                    "-        dict with new version info",
                    "-    \"\"\"",
                    "-    db.connect()",
                    "-",
                    "-    try:",
                    "-        # Get version and critiques",
                    "-        cursor = db.execute(",
                    "-            \"\"\"SELECT cv.*, co.id as order_id, co.briefing, co.current_critique_round,",
                    "-                      ap.content as profile_config,",
                    "-                      cs.content as structure_config",
                    "-               FROM content_versions cv",
                    "-               JOIN content_orders co ON cv.order_id = co.id",
                    "-               LEFT JOIN content_config ap ON co.author_profile_id = ap.id AND ap.type = 'author_profile'",
                    "-               LEFT JOIN content_config cs ON co.structure_id = cs.id AND cs.type = 'structure'",
                    "-               WHERE cv.id = %s\"\"\",",
                    "-            (version_id,),",
                    "-        )",
                    "-        version = cursor.fetchone()",
                    "-        cursor.close()",
                    "-",
                    "-        if not version:",
                    "-            return {\"error\": \"Version not found\"}",
                    "-",
                    "-        content_data = json.loads(version[\"content\"]) if isinstance(version[\"content\"], str) else version[\"content\"]",
                    "-        content_text = content_data.get(\"text\", \"\")",
                    "-",
                    "-        # Get latest critiques",
                    "-        cursor = db.execute(",
                    "-            \"\"\"SELECT c.name, cc.feedback",
                    "-               FROM content_critiques cc",
                    "-               JOIN critics c ON cc.critic_id = c.id",
                    "-               WHERE cc.version_id = %s AND cc.round = %s\"\"\",",
                    "-            (version_id, version[\"current_critique_round\"]),",
                    "-        )",
                    "-        critiques = cursor.fetchall()",
                    "-        cursor.close()",
                    "-",
                    "-        # Build revision prompt",
                    "-        feedback_text = \"\"",
                    "-        for critique in critiques:",
                    "-            fb = json.loads(critique[\"feedback\"]) if isinstance(critique[\"feedback\"], str) else critique[\"feedback\"]",
                    "-            feedback_text += f\"\\n### {critique['name']}:\\n\"",
                    "-            feedback_text += f\"- Bewertung: {fb.get('rating', 'N\/A')}\/10\\n\"",
                    "-            feedback_text += f\"- Probleme: {', '.join(fb.get('issues', []))}\\n\"",
                    "-            feedback_text += f\"- Vorschläge: {', '.join(fb.get('suggestions', []))}\\n\"",
                    "-",
                    "-        # Determine output format from structure",
                    "-        output_format = \"markdown\"  # Default",
                    "-        html_instruction = \"\"",
                    "-        if version.get(\"structure_config\"):",
                    "-            structure_config = (",
                    "-                json.loads(version[\"structure_config\"])",
                    "-                if isinstance(version[\"structure_config\"], str)",
                    "-                else version[\"structure_config\"]",
                    "-            )",
                    "-            ausgabe = structure_config.get(\"ausgabe\", {})",
                    "-            output_format = ausgabe.get(\"format\", \"markdown\")",
                    "-            erlaubte_tags = ausgabe.get(",
                    "-                \"erlaubte_tags\", [\"h1\", \"h2\", \"h3\", \"h4\", \"p\", \"ul\", \"ol\", \"li\", \"strong\", \"a\", \"table\", \"hr\"]",
                    "-            )",
                    "-",
                    "-            if output_format == \"body-html\":",
                    "-                tags_str = \", \".join(erlaubte_tags)",
                    "-                html_instruction = f\"\"\"",
                    "-5. **KRITISCH - Behalte das HTML-Format bei!**",
                    "-   - Nur diese Tags: {tags_str}",
                    "-   - KEIN Markdown, KEINE ## oder ** oder -",
                    "-   - KEIN div, span, br, img, script, style",
                    "-   - Fließtext immer in <p>-Tags\"\"\"",
                    "-",
                    "-        # Load revise prompt from database",
                    "-        prompt_template = get_prompt(\"content-revise\")",
                    "-        if prompt_template:",
                    "-            prompt = prompt_template.format(",
                    "-                content=content_text, feedback=feedback_text, html_instruction=html_instruction",
                    "-            )",
                    "-        else:",
                    "-            # Fallback if prompt not in DB",
                    "-            prompt = f\"\"\"Du bist ein professioneller Content-Editor. Überarbeite den folgenden Text basierend auf dem Feedback der Kritiker.",
                    "-",
                    "-## Originaler Text:",
                    "-{content_text}",
                    "-",
                    "-## Feedback der Kritiker:",
                    "-{feedback_text}",
                    "-",
                    "-## Anweisungen:",
                    "-1. Behebe alle genannten Probleme",
                    "-2. Setze die Verbesserungsvorschläge um",
                    "-3. Behalte den Grundton und Stil bei",
                    "-4. Achte auf eine kohärente Überarbeitung",
                    "-{html_instruction}",
                    "-",
                    "-Erstelle nun die überarbeitete Version:\"\"\"",
                    "-",
                    "-        # Generate revision",
                    "-        update_order_status(version[\"order_id\"], \"generating\")",
                    "-        revised_content = call_llm(prompt, model, client_name=\"content-studio-revise\")",
                    "-",
                    "-        # Save new version with correct format",
                    "-        new_version_number = version[\"version_number\"] + 1",
                    "-        new_version_id = save_version(version[\"order_id\"], revised_content, new_version_number, output_format)",
                    "-",
                    "-        # Update status",
                    "-        update_order_status(version[\"order_id\"], \"critique\")",
                    "-",
                    "-        return {",
                    "-            \"success\": True,",
                    "-            \"order_id\": version[\"order_id\"],",
                    "-            \"version_id\": new_version_id,",
                    "-            \"version_number\": new_version_number,",
                    "-            \"content\": revised_content,",
                    "-        }",
                    "-",
                    "-    except Exception as e:",
                    "-        return {\"error\": str(e)}",
                    "-    finally:",
                    "-        db.disconnect()",
                    "-",
                    "-",
                    " if __name__ == \"__main__\":",
                    "-    import sys",
                    "-",
                    "     if len(sys.argv) < 3:",
                    "         print(\"Usage: python generate.py <command> <id> [options]\")",
                    "         print(\"Commands: generate, critique, revise\")"
                ]
            }
        ],
        "originalFile": "#!\/usr\/bin\/env python3\n\"\"\"\nContent Generation for KI-System\nGenerates content using RAG context, author profiles, and contracts.\n\"\"\"\n\nimport json\nimport sys\n\nsys.path.insert(0, \"\/var\/www\/scripts\/pipeline\")\n\nfrom config import ANTHROPIC_API_KEY, ANTHROPIC_MODEL, OLLAMA_CHAT_MODEL, OLLAMA_HOST  # noqa: I001, E402\nfrom db import db  # noqa: E402\nfrom embed import search_similar  # noqa: E402\n\n\ndef _repair_json(json_str):\n    \"\"\"\n    Attempt to repair common JSON issues from LLM output.\n\n    Fixes:\n    - Unescaped quotes in strings\n    - Missing commas between array elements\n    - Trailing commas\n    - Control characters in strings\n    \"\"\"\n    import re\n\n    # Remove control characters except newlines and tabs\n    json_str = re.sub(r\"[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f]\", \"\", json_str)\n\n    # Fix common issue: missing comma before closing bracket in arrays\n    json_str = re.sub(r'\"\\s*\\n\\s*]', '\"\\n]', json_str)\n\n    # Fix trailing commas before closing brackets\/braces\n    json_str = re.sub(r\",\\s*}\", \"}\", json_str)\n    json_str = re.sub(r\",\\s*]\", \"]\", json_str)\n\n    # Fix missing commas between array elements (string followed by string)\n    json_str = re.sub(r'\"\\s*\\n\\s*\"', '\",\\n\"', json_str)\n\n    # Fix unescaped quotes within strings (heuristic: quotes not at boundaries)\n    # This is tricky, so we do a simple fix for common patterns\n    lines = json_str.split(\"\\n\")\n    fixed_lines = []\n    for line in lines:\n        # Count quotes - if odd number and line has content, try to fix\n        quote_count = line.count('\"') - line.count('\\\\\"')\n        if quote_count % 2 != 0 and \":\" in line:\n            # Try to escape internal quotes (very basic heuristic)\n            parts = line.split(\":\", 1)\n            if len(parts) == 2:\n                key_part = parts[0]\n                value_part = parts[1]\n                # If value has odd quotes, try to balance\n                if value_part.count('\"') % 2 != 0:\n                    # Add escaped quote or remove problematic char\n                    value_part = value_part.rstrip().rstrip(\",\")\n                    if not value_part.endswith('\"'):\n                        value_part += '\"'\n                    line = key_part + \":\" + value_part\n        fixed_lines.append(line)\n\n    return \"\\n\".join(fixed_lines)\n\n\ndef get_prompt(name):\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_rag_context(briefing, collection=\"documents\", limit=5):\n    \"\"\"\n    Get relevant context from Qdrant based on briefing.\n    Returns list of chunks with content and metadata.\n    \"\"\"\n    results = search_similar(briefing, collection=collection, limit=limit)\n\n    context_items = []\n    for result in results:\n        context_items.append(\n            {\n                \"content\": result[\"payload\"].get(\"content\", \"\"),\n                \"source\": result[\"payload\"].get(\"document_title\", \"Unknown\"),\n                \"score\": round(result[\"score\"], 4),\n                \"chunk_id\": result[\"payload\"].get(\"chunk_id\"),\n                \"document_id\": result[\"payload\"].get(\"document_id\"),\n            }\n        )\n\n    return context_items\n\n\ndef get_config_item(item_id, item_type):\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_semantic_context(chunk_ids):\n    \"\"\"\n    Load entities and relations based on chunk_ids.\n\n    Uses the chunk_entities junction table to find relevant entities,\n    then loads relations between those entities.\n\n    Args:\n        chunk_ids: List of chunk IDs from RAG context\n\n    Returns:\n        dict with 'entities' and 'relations' lists\n    \"\"\"\n    if not chunk_ids:\n        return {\"entities\": [], \"relations\": []}\n\n    # Filter out None values\n    chunk_ids = [cid for cid in chunk_ids if cid is not None]\n    if not chunk_ids:\n        return {\"entities\": [], \"relations\": []}\n\n    placeholders = \", \".join([\"%s\"] * len(chunk_ids))\n\n    # Load entities via chunk_entities\n    cursor = db.execute(\n        f\"\"\"SELECT DISTINCT e.id, e.name, e.type, e.description,\n                  AVG(ce.relevance_score) as relevance\n           FROM chunk_entities ce\n           JOIN entities e ON ce.entity_id = e.id\n           WHERE ce.chunk_id IN ({placeholders})\n           GROUP BY e.id, e.name, e.type, e.description\n           ORDER BY relevance DESC\n           LIMIT 10\"\"\",\n        tuple(chunk_ids),\n    )\n    entities = cursor.fetchall()\n    cursor.close()\n\n    if not entities:\n        return {\"entities\": [], \"relations\": []}\n\n    # Get entity IDs for relation lookup\n    entity_ids = [e[\"id\"] for e in entities]\n    entity_placeholders = \", \".join([\"%s\"] * len(entity_ids))\n\n    # Load relations between found entities\n    cursor = db.execute(\n        f\"\"\"SELECT e1.name as source, er.relation_type, e2.name as target\n           FROM entity_relations er\n           JOIN entities e1 ON er.source_entity_id = e1.id\n           JOIN entities e2 ON er.target_entity_id = e2.id\n           WHERE e1.id IN ({entity_placeholders}) AND e2.id IN ({entity_placeholders})\n           LIMIT 15\"\"\",\n        tuple(entity_ids) + tuple(entity_ids),\n    )\n    relations = cursor.fetchall()\n    cursor.close()\n\n    return {\"entities\": entities, \"relations\": relations}\n\n\ndef get_taxonomy_context(document_ids):\n    \"\"\"\n    Load taxonomy terms for documents.\n\n    Args:\n        document_ids: List of document IDs from RAG context\n\n    Returns:\n        List of taxonomy term dicts with name, slug, confidence\n    \"\"\"\n    if not document_ids:\n        return []\n\n    # Filter out None values\n    document_ids = [did for did in document_ids if did is not None]\n    if not document_ids:\n        return []\n\n    placeholders = \", \".join([\"%s\"] * len(document_ids))\n\n    cursor = db.execute(\n        f\"\"\"SELECT DISTINCT tt.name, tt.slug, MAX(dt.confidence) as confidence\n           FROM document_taxonomy dt\n           JOIN taxonomy_terms tt ON dt.taxonomy_term_id = tt.id\n           WHERE dt.document_id IN ({placeholders})\n           GROUP BY tt.id, tt.name, tt.slug\n           ORDER BY confidence DESC\"\"\",\n        tuple(document_ids),\n    )\n    taxonomy = cursor.fetchall()\n    cursor.close()\n\n    return taxonomy\n\n\ndef get_author_profile(profile_id):\n    \"\"\"Load author profile from database.\"\"\"\n    return get_config_item(profile_id, \"author_profile\")\n\n\ndef get_contract(contract_id):\n    \"\"\"Load content contract from database.\"\"\"\n    return get_config_item(contract_id, \"contract\")\n\n\ndef get_structure(structure_id):\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):\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_new_author_profile(config):\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\") == \"ausge... [TRUNCATED-2e4bdc5f812ef488]"
    }
}
← Vorheriger Zur Liste Nächster →