Protokoll #15137
| ID | 15137 |
|---|---|
| Zeitstempel | 2025-12-25 13:57:57.056651 |
| Client | root |
| 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 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 5,387 (Input: 0, Output: 0) |
| Dauer | 103 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\/generators\/critic.py",
"content": "\"\"\"\nCritic Functions - Content critique and revision.\n\"\"\"\n\nimport json\nimport re\nimport sys\n\nsys.path.insert(0, \"\/var\/www\/scripts\/pipeline\")\n\nfrom db import db\n\nfrom .config_loader import get_prompt\nfrom .content_generator import call_llm\nfrom .persistence import save_version, update_order_status\nfrom .utils import repair_json\n\n\ndef get_critic(critic_id: int) -> dict | None:\n \"\"\"Load critic from database.\"\"\"\n cursor = db.execute(\n \"\"\"SELECT c.*, p.content as prompt_content\n FROM critics c\n LEFT JOIN prompts p ON c.prompt_id = p.id\n WHERE c.id = %s AND c.is_active = 1\"\"\",\n (critic_id,),\n )\n result = cursor.fetchone()\n cursor.close()\n return result\n\n\ndef run_critic(content: str, critic_id: int, model: str = \"anthropic\") -> dict:\n \"\"\"\n Run a single critic on content.\n\n Returns:\n dict with feedback and rating\n \"\"\"\n db.connect()\n\n try:\n critic = get_critic(critic_id)\n if not critic:\n return {\"error\": f\"Critic {critic_id} not found\"}\n\n fokus = json.loads(critic[\"fokus\"]) if isinstance(critic[\"fokus\"], str) else critic[\"fokus\"]\n fokus_str = \", \".join(fokus)\n\n # Load prompt from database (via critic.prompt_id or fallback to generic)\n prompt_template = critic.get(\"prompt_content\")\n if not prompt_template:\n prompt_template = get_prompt(\"critic-generic\")\n if not prompt_template:\n # Ultimate fallback - should never happen if DB is properly set up\n prompt_template = \"\"\"Du bist ein kritischer Lektor mit dem Fokus auf: {fokus}\n\nAnalysiere den folgenden Text und gib strukturiertes Feedback:\n\n## Text:\n{content}\n\n## Deine Aufgabe:\n1. Prüfe den Text auf die Aspekte: {fokus}\n2. Identifiziere konkrete Verbesserungspunkte\n3. Bewerte die Qualität (1-10)\n\nAntworte im JSON-Format:\n{{\n \"rating\": 8,\n \"passed\": true,\n \"issues\": [\"Issue 1\", \"Issue 2\"],\n \"suggestions\": [\"Suggestion 1\"],\n \"summary\": \"Kurze Zusammenfassung\"\n}}\"\"\"\n\n # Format prompt with variables\n prompt = prompt_template.format(fokus=fokus_str, content=content)\n\n response = call_llm(prompt, model, client_name=\"content-studio-critique\")\n\n # Parse JSON from response with robust error handling\n json_match = re.search(r\"\\{[\\s\\S]*\\}\", response)\n if json_match:\n json_str = json_match.group()\n try:\n feedback = json.loads(json_str)\n feedback[\"critic_name\"] = critic[\"name\"]\n return feedback\n except json.JSONDecodeError:\n # Try to repair common JSON issues\n repaired = repair_json(json_str)\n try:\n feedback = json.loads(repaired)\n feedback[\"critic_name\"] = critic[\"name\"]\n return feedback\n except json.JSONDecodeError:\n pass\n\n return {\n \"critic_name\": critic[\"name\"],\n \"rating\": 5,\n \"passed\": False,\n \"issues\": [\"Konnte Feedback nicht parsen\"],\n \"suggestions\": [],\n \"summary\": response[:500],\n }\n\n except Exception as e:\n return {\"error\": str(e)}\n finally:\n db.disconnect()\n\n\ndef run_critique_round(version_id: int, model: str = \"anthropic\") -> dict:\n \"\"\"\n Run all active critics on a content version.\n\n Returns:\n dict with all critique results\n \"\"\"\n db.connect()\n\n try:\n # Get version content\n cursor = db.execute(\n \"\"\"SELECT cv.*, co.id as order_id, co.current_critique_round\n FROM content_versions cv\n JOIN content_orders co ON cv.order_id = co.id\n WHERE cv.id = %s\"\"\",\n (version_id,),\n )\n version = cursor.fetchone()\n cursor.close()\n\n if not version:\n return {\"error\": \"Version not found\"}\n\n content_data = json.loads(version[\"content\"]) if isinstance(version[\"content\"], str) else version[\"content\"]\n content_text = content_data.get(\"text\", \"\")\n\n # Get all active critics\n cursor = db.execute(\"SELECT id, name FROM critics WHERE is_active = 1 ORDER BY sort_order\")\n critics = cursor.fetchall()\n cursor.close()\n\n # Increment critique round\n new_round = (version[\"current_critique_round\"] or 0) + 1\n cursor = db.execute(\n \"UPDATE content_orders SET current_critique_round = %s WHERE id = %s\", (new_round, version[\"order_id\"])\n )\n db.commit()\n cursor.close()\n\n # Run each critic\n results = []\n all_passed = True\n\n for critic in critics:\n db.disconnect() # Disconnect before calling run_critic\n feedback = run_critic(content_text, critic[\"id\"], model)\n db.connect() # Reconnect\n\n if \"error\" not in feedback:\n # Save critique\n cursor = db.execute(\n \"\"\"INSERT INTO content_critiques (version_id, critic_id, round, feedback)\n VALUES (%s, %s, %s, %s)\"\"\",\n (version_id, critic[\"id\"], new_round, json.dumps(feedback)),\n )\n db.commit()\n cursor.close()\n\n if not feedback.get(\"passed\", True):\n all_passed = False\n\n results.append(feedback)\n\n # Update order status based on results\n if all_passed:\n update_order_status(version[\"order_id\"], \"validate\")\n else:\n update_order_status(version[\"order_id\"], \"revision\")\n\n return {\"success\": True, \"round\": new_round, \"critiques\": results, \"all_passed\": all_passed}\n\n except Exception as e:\n return {\"error\": str(e)}\n finally:\n db.disconnect()\n\n\ndef revise_content(version_id: int, model: str = \"anthropic\") -> dict:\n \"\"\"\n Create a revision based on critique feedback.\n\n Returns:\n dict with new version info\n \"\"\"\n db.connect()\n\n try:\n # Get version and critiques\n cursor = db.execute(\n \"\"\"SELECT cv.*, co.id as order_id, co.briefing, co.current_critique_round,\n ap.content as profile_config,\n cs.content as structure_config\n FROM content_versions cv\n JOIN content_orders co ON cv.order_id = co.id\n LEFT JOIN content_config ap ON co.author_profile_id = ap.id AND ap.type = 'author_profile'\n LEFT JOIN content_config cs ON co.structure_id = cs.id AND cs.type = 'structure'\n WHERE cv.id = %s\"\"\",\n (version_id,),\n )\n version = cursor.fetchone()\n cursor.close()\n\n if not version:\n return {\"error\": \"Version not found\"}\n\n content_data = json.loads(version[\"content\"]) if isinstance(version[\"content\"], str) else version[\"content\"]\n content_text = content_data.get(\"text\", \"\")\n\n # Get latest critiques\n cursor = db.execute(\n \"\"\"SELECT c.name, cc.feedback\n FROM content_critiques cc\n JOIN critics c ON cc.critic_id = c.id\n WHERE cc.version_id = %s AND cc.round = %s\"\"\",\n (version_id, version[\"current_critique_round\"]),\n )\n critiques = cursor.fetchall()\n cursor.close()\n\n # Build revision prompt\n feedback_text = \"\"\n for critique in critiques:\n fb = json.loads(critique[\"feedback\"]) if isinstance(critique[\"feedback\"], str) else critique[\"feedback\"]\n feedback_text += f\"\\n### {critique['name']}:\\n\"\n feedback_text += f\"- Bewertung: {fb.get('rating', 'N\/A')}\/10\\n\"\n feedback_text += f\"- Probleme: {', '.join(fb.get('issues', []))}\\n\"\n feedback_text += f\"- Vorschläge: {', '.join(fb.get('suggestions', []))}\\n\"\n\n # Determine output format from structure\n output_format = \"markdown\" # Default\n html_instruction = \"\"\n if version.get(\"structure_config\"):\n structure_config = (\n json.loads(version[\"structure_config\"])\n if isinstance(version[\"structure_config\"], str)\n else version[\"structure_config\"]\n )\n ausgabe = structure_config.get(\"ausgabe\", {})\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\n \"erlaubte_tags\", [\"h1\", \"h2\", \"h3\", \"h4\", \"p\", \"ul\", \"ol\", \"li\", \"strong\", \"a\", \"table\", \"hr\"]\n )\n\n if output_format == \"body-html\":\n tags_str = \", \".join(erlaubte_tags)\n html_instruction = f\"\"\"\n5. **KRITISCH - Behalte das HTML-Format bei!**\n - Nur diese Tags: {tags_str}\n - KEIN Markdown, KEINE ## oder ** oder -\n - KEIN div, span, br, img, script, style\n - Fließtext immer in <p>-Tags\"\"\"\n\n # Load revise prompt from database\n prompt_template = get_prompt(\"content-revise\")\n if prompt_template:\n prompt = prompt_template.format(\n content=content_text, feedback=feedback_text, html_instruction=html_instruction\n )\n else:\n # Fallback if prompt not in DB\n prompt = f\"\"\"Du bist ein professioneller Content-Editor. Überarbeite den folgenden Text basierend auf dem Feedback der Kritiker.\n\n## Originaler Text:\n{content_text}\n\n## Feedback der Kritiker:\n{feedback_text}\n\n## Anweisungen:\n1. Behebe alle genannten Probleme\n2. Setze die Verbesserungsvorschläge um\n3. Behalte den Grundton und Stil bei\n4. Achte auf eine kohärente Überarbeitung\n{html_instruction}\n\nErstelle nun die überarbeitete Version:\"\"\"\n\n # Generate revision\n update_order_status(version[\"order_id\"], \"generating\")\n revised_content = call_llm(prompt, model, client_name=\"content-studio-revise\")\n\n # Save new version with correct format\n new_version_number = version[\"version_number\"] + 1\n ... [TRUNCATED-9454ca2763940847]"
}
}
Response
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/scripts\/pipeline\/generators\/critic.py",
"content": "\"\"\"\nCritic Functions - Content critique and revision.\n\"\"\"\n\nimport json\nimport re\nimport sys\n\nsys.path.insert(0, \"\/var\/www\/scripts\/pipeline\")\n\nfrom db import db\n\nfrom .config_loader import get_prompt\nfrom .content_generator import call_llm\nfrom .persistence import save_version, update_order_status\nfrom .utils import repair_json\n\n\ndef get_critic(critic_id: int) -> dict | None:\n \"\"\"Load critic from database.\"\"\"\n cursor = db.execute(\n \"\"\"SELECT c.*, p.content as prompt_content\n FROM critics c\n LEFT JOIN prompts p ON c.prompt_id = p.id\n WHERE c.id = %s AND c.is_active = 1\"\"\",\n (critic_id,),\n )\n result = cursor.fetchone()\n cursor.close()\n return result\n\n\ndef run_critic(content: str, critic_id: int, model: str = \"anthropic\") -> dict:\n \"\"\"\n Run a single critic on content.\n\n Returns:\n dict with feedback and rating\n \"\"\"\n db.connect()\n\n try:\n critic = get_critic(critic_id)\n if not critic:\n return {\"error\": f\"Critic {critic_id} not found\"}\n\n fokus = json.loads(critic[\"fokus\"]) if isinstance(critic[\"fokus\"], str) else critic[\"fokus\"]\n fokus_str = \", \".join(fokus)\n\n # Load prompt from database (via critic.prompt_id or fallback to generic)\n prompt_template = critic.get(\"prompt_content\")\n if not prompt_template:\n prompt_template = get_prompt(\"critic-generic\")\n if not prompt_template:\n # Ultimate fallback - should never happen if DB is properly set up\n prompt_template = \"\"\"Du bist ein kritischer Lektor mit dem Fokus auf: {fokus}\n\nAnalysiere den folgenden Text und gib strukturiertes Feedback:\n\n## Text:\n{content}\n\n## Deine Aufgabe:\n1. Prüfe den Text auf die Aspekte: {fokus}\n2. Identifiziere konkrete Verbesserungspunkte\n3. Bewerte die Qualität (1-10)\n\nAntworte im JSON-Format:\n{{\n \"rating\": 8,\n \"passed\": true,\n \"issues\": [\"Issue 1\", \"Issue 2\"],\n \"suggestions\": [\"Suggestion 1\"],\n \"summary\": \"Kurze Zusammenfassung\"\n}}\"\"\"\n\n # Format prompt with variables\n prompt = prompt_template.format(fokus=fokus_str, content=content)\n\n response = call_llm(prompt, model, client_name=\"content-studio-critique\")\n\n # Parse JSON from response with robust error handling\n json_match = re.search(r\"\\{[\\s\\S]*\\}\", response)\n if json_match:\n json_str = json_match.group()\n try:\n feedback = json.loads(json_str)\n feedback[\"critic_name\"] = critic[\"name\"]\n return feedback\n except json.JSONDecodeError:\n # Try to repair common JSON issues\n repaired = repair_json(json_str)\n try:\n feedback = json.loads(repaired)\n feedback[\"critic_name\"] = critic[\"name\"]\n return feedback\n except json.JSONDecodeError:\n pass\n\n return {\n \"critic_name\": critic[\"name\"],\n \"rating\": 5,\n \"passed\": False,\n \"issues\": [\"Konnte Feedback nicht parsen\"],\n \"suggestions\": [],\n \"summary\": response[:500],\n }\n\n except Exception as e:\n return {\"error\": str(e)}\n finally:\n db.disconnect()\n\n\ndef run_critique_round(version_id: int, model: str = \"anthropic\") -> dict:\n \"\"\"\n Run all active critics on a content version.\n\n Returns:\n dict with all critique results\n \"\"\"\n db.connect()\n\n try:\n # Get version content\n cursor = db.execute(\n \"\"\"SELECT cv.*, co.id as order_id, co.current_critique_round\n FROM content_versions cv\n JOIN content_orders co ON cv.order_id = co.id\n WHERE cv.id = %s\"\"\",\n (version_id,),\n )\n version = cursor.fetchone()\n cursor.close()\n\n if not version:\n return {\"error\": \"Version not found\"}\n\n content_data = json.loads(version[\"content\"]) if isinstance(version[\"content\"], str) else version[\"content\"]\n content_text = content_data.get(\"text\", \"\")\n\n # Get all active critics\n cursor = db.execute(\"SELECT id, name FROM critics WHERE is_active = 1 ORDER BY sort_order\")\n critics = cursor.fetchall()\n cursor.close()\n\n # Increment critique round\n new_round = (version[\"current_critique_round\"] or 0) + 1\n cursor = db.execute(\n \"UPDATE content_orders SET current_critique_round = %s WHERE id = %s\", (new_round, version[\"order_id\"])\n )\n db.commit()\n cursor.close()\n\n # Run each critic\n results = []\n all_passed = True\n\n for critic in critics:\n db.disconnect() # Disconnect before calling run_critic\n feedback = run_critic(content_text, critic[\"id\"], model)\n db.connect() # Reconnect\n\n if \"error\" not in feedback:\n # Save critique\n cursor = db.execute(\n \"\"\"INSERT INTO content_critiques (version_id, critic_id, round, feedback)\n VALUES (%s, %s, %s, %s)\"\"\",\n (version_id, critic[\"id\"], new_round, json.dumps(feedback)),\n )\n db.commit()\n cursor.close()\n\n if not feedback.get(\"passed\", True):\n all_passed = False\n\n results.append(feedback)\n\n # Update order status based on results\n if all_passed:\n update_order_status(version[\"order_id\"], \"validate\")\n else:\n update_order_status(version[\"order_id\"], \"revision\")\n\n return {\"success\": True, \"round\": new_round, \"critiques\": results, \"all_passed\": all_passed}\n\n except Exception as e:\n return {\"error\": str(e)}\n finally:\n db.disconnect()\n\n\ndef revise_content(version_id: int, model: str = \"anthropic\") -> dict:\n \"\"\"\n Create a revision based on critique feedback.\n\n Returns:\n dict with new version info\n \"\"\"\n db.connect()\n\n try:\n # Get version and critiques\n cursor = db.execute(\n \"\"\"SELECT cv.*, co.id as order_id, co.briefing, co.current_critique_round,\n ap.content as profile_config,\n cs.content as structure_config\n FROM content_versions cv\n JOIN content_orders co ON cv.order_id = co.id\n LEFT JOIN content_config ap ON co.author_profile_id = ap.id AND ap.type = 'author_profile'\n LEFT JOIN content_config cs ON co.structure_id = cs.id AND cs.type = 'structure'\n WHERE cv.id = %s\"\"\",\n (version_id,),\n )\n version = cursor.fetchone()\n cursor.close()\n\n if not version:\n return {\"error\": \"Version not found\"}\n\n content_data = json.loads(version[\"content\"]) if isinstance(version[\"content\"], str) else version[\"content\"]\n content_text = content_data.get(\"text\", \"\")\n\n # Get latest critiques\n cursor = db.execute(\n \"\"\"SELECT c.name, cc.feedback\n FROM content_critiques cc\n JOIN critics c ON cc.critic_id = c.id\n WHERE cc.version_id = %s AND cc.round = %s\"\"\",\n (version_id, version[\"current_critique_round\"]),\n )\n critiques = cursor.fetchall()\n cursor.close()\n\n # Build revision prompt\n feedback_text = \"\"\n for critique in critiques:\n fb = json.loads(critique[\"feedback\"]) if isinstance(critique[\"feedback\"], str) else critique[\"feedback\"]\n feedback_text += f\"\\n### {critique['name']}:\\n\"\n feedback_text += f\"- Bewertung: {fb.get('rating', 'N\/A')}\/10\\n\"\n feedback_text += f\"- Probleme: {', '.join(fb.get('issues', []))}\\n\"\n feedback_text += f\"- Vorschläge: {', '.join(fb.get('suggestions', []))}\\n\"\n\n # Determine output format from structure\n output_format = \"markdown\" # Default\n html_instruction = \"\"\n if version.get(\"structure_config\"):\n structure_config = (\n json.loads(version[\"structure_config\"])\n if isinstance(version[\"structure_config\"], str)\n else version[\"structure_config\"]\n )\n ausgabe = structure_config.get(\"ausgabe\", {})\n output_format = ausgabe.get(\"format\", \"markdown\")\n erlaubte_tags = ausgabe.get(\n \"erlaubte_tags\", [\"h1\", \"h2\", \"h3\", \"h4\", \"p\", \"ul\", \"ol\", \"li\", \"strong\", \"a\", \"table\", \"hr\"]\n )\n\n if output_format == \"body-html\":\n tags_str = \", \".join(erlaubte_tags)\n html_instruction = f\"\"\"\n5. **KRITISCH - Behalte das HTML-Format bei!**\n - Nur diese Tags: {tags_str}\n - KEIN Markdown, KEINE ## oder ** oder -\n - KEIN div, span, br, img, script, style\n - Fließtext immer in <p>-Tags\"\"\"\n\n # Load revise prompt from database\n prompt_template = get_prompt(\"content-revise\")\n if prompt_template:\n prompt = prompt_template.format(\n content=content_text, feedback=feedback_text, html_instruction=html_instruction\n )\n else:\n # Fallback if prompt not in DB\n prompt = f\"\"\"Du bist ein professioneller Content-Editor. Überarbeite den folgenden Text basierend auf dem Feedback der Kritiker.\n\n## Originaler Text:\n{content_text}\n\n## Feedback der Kritiker:\n{feedback_text}\n\n## Anweisungen:\n1. Behebe alle genannten Probleme\n2. Setze die Verbesserungsvorschläge um\n3. Behalte den Grundton und Stil bei\n4. Achte auf eine kohärente Überarbeitung\n{html_instruction}\n\nErstelle nun die überarbeitete Version:\"\"\"\n\n # Generate revision\n update_order_status(version[\"order_id\"], \"generating\")\n revised_content = call_llm(prompt, model, client_name=\"content-studio-revise\")\n\n # Save new version with correct format\n new_version_number = version[\"version_number\"] + 1\n ... [TRUNCATED-9454ca2763940847]",
"structuredPatch": [],
"originalFile": null
}
}