{
"tool_response": {
"filePath": "\/opt\/scripts\/pipeline\/generate.py",
"oldString": "def 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.config as profile_config,\n cc.name as contract_name, cc.config as contract_config,\n cs.name as structure_name, cs.config as structure_config\n FROM content_orders co\n LEFT JOIN author_profiles ap ON co.author_profile_id = ap.id\n LEFT JOIN content_contracts cc ON co.contract_id = cc.id\n LEFT JOIN content_structures cs ON co.structure_id = cs.id\n WHERE co.id = %s\"\"\",\n (order_id,),\n )\n result = cursor.fetchone()\n cursor.close()\n return result",
"newString": "def 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.config as profile_config,\n cc.name as contract_name, cc.config as contract_config,\n cs.name as structure_name, cs.config 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",
"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, \"\/opt\/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 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 }\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, config FROM content_config WHERE id = %s AND type = %s AND is_active = 1\",\n (item_id, item_type),\n )\n result = cursor.fetchone()\n cursor.close()\n\n if result:\n config = json.loads(result[\"config\"]) if isinstance(result[\"config\"], str) else result[\"config\"]\n return {\"name\": result[\"name\"], \"config\": config}\n return None\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.config as profile_config,\n cc.name as contract_name, cc.config as contract_config,\n cs.name as structure_name, cs.config as structure_config\n FROM content_orders co\n LEFT JOIN author_profiles ap ON co.author_profile_id = ap.id\n LEFT JOIN content_contracts cc ON co.contract_id = cc.id\n LEFT JOIN content_structures cs ON co.structure_id = cs.id\n WHERE co.id = %s\"\"\",\n (order_id,),\n )\n result = cursor.fetchone()\n cursor.close()\n return result\n\n\ndef build_generation_prompt(briefing, context, profile, contract, structure=None):\n \"\"\"Build the content generation prompt.\"\"\"\n\n # Format context\n context_text = \"\"\n for i, ctx in enumerate(context, 1):\n context_text += f\"\\n[Quelle {i}: {ctx['source']}]\\n{ctx['content']}\\n\"\n\n # Build profile instructions\n profile_text = \"\"\n if profile:\n config = profile.get(\"config\", {})\n profile_text = f\"\"\"\nAutorenprofil: {profile.get(\"name\", \"Standard\")}\n- Schreibstil: {config.get(\"voice\", \"neutral\")}\n- Tonalität: {config.get(\"style\", \"professionell\")}\n- Konventionen: {config.get(\"conventions\", \"Standard-Deutsch\")}\n\"\"\"\n\n # Build contract requirements\n contract_text = \"\"\n if contract:\n config = contract.get(\"config\", {})\n req = config.get(\"requirements\", {})\n contract_text = f\"\"\"\nContract: {contract.get(\"name\", \"Standard\")}\n- Wortanzahl: {req.get(\"min_words\", 500)} - {req.get(\"max_words\", 5000)} Wörter\n- Struktur-Validierung: {req.get(\"structure_validation\", True)}\n\"\"\"\n\n # Build structure instructions\n structure_text = \"\"\n if structure:\n config = structure.get(\"config\", {})\n structure_text = f\"\"\"\nStruktur-Template: {structure.get(\"name\", \"\")}\n- Typ: {structure.get(\"type\", \"\")}\n- Abschnitte: {json.dumps(config.get(\"sections\", []), ensure_ascii=False)}\n\"\"\"\n\n prompt = f\"\"\"Du bist ein professioneller Content-Autor. Erstelle basierend auf dem Briefing und dem bereitgestellten Kontext einen hochwertigen Text.\n\n{profile_text}\n{contract_text}\n{structure_text}\n\n## Kontext aus der Wissensbasis:\n{context_text}\n\n## Briefing:\n{briefing}\n\n## Anweisungen:\n1. Nutze die Informationen aus dem Kontext als Grundlage\n2. Halte dich an das Autorenprofil und den Schreibstil\n3. Beachte die Vorgaben aus dem Contract\n4. Strukturiere den Text gemäß dem Template (falls angegeben)\n5. Schreibe auf Deutsch\n6. Kennzeichne verwendete Quellen\n\nErstelle nun den Content:\"\"\"\n\n return prompt\n\n\ndef call_llm(prompt, model=\"anthropic\"):\n \"\"\"Call LLM to generate content.\"\"\"\n if model == \"anthropic\" and ANTHROPIC_API_KEY:\n import anthropic\n\n client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY)\n\n message = client.messages.create(\n model=ANTHROPIC_MODEL, max_tokens=4000, messages=[{\"role\": \"user\", \"content\": prompt}]\n )\n return message.content[0].text\n else:\n # Fallback to Ollama\n import requests\n\n response = requests.post(\n f\"{OLLAMA_HOST}\/api\/generate\",\n json={\"model\": OLLAMA_CHAT_MODEL, \"prompt\": prompt, \"stream\": False},\n timeout=300,\n )\n response.raise_for_status()\n return response.json().get(\"response\", \"\")\n\n\ndef save_version(order_id, content, version_number=1):\n \"\"\"Save content version to database.\"\"\"\n content_json = json.dumps({\"text\": content, \"format\": \"markdown\"})\n\n cursor = db.execute(\n \"\"\"INSERT INTO content_versions (order_id, version_number, content)\n VALUES (%s, %s, %s)\"\"\",\n (order_id, version_number, content_json),\n )\n db.commit()\n version_id = cursor.lastrowid\n cursor.close()\n return version_id\n\n\ndef save_sources(order_id, context):\n \"\"\"Save RAG sources to content_sources.\"\"\"\n for ctx in context:\n # Try to find chunk_id by content match\n cursor = db.execute(\"SELECT id FROM chunks WHERE content LIKE %s LIMIT 1\", (ctx[\"content\"][:100] + \"%\",))\n chunk = cursor.fetchone()\n cursor.close()\n\n if chunk:\n cursor = db.execute(\n \"\"\"INSERT IGNORE INTO content_sources (order_id, chunk_id, relevance_score)\n VALUES (%s, %s, %s)\"\"\",\n (order_id, chunk[\"id\"], ctx[\"score\"]),\n )\n db.commit()\n cursor.close()\n\n\ndef update_order_status(order_id, status):\n \"\"\"Update order status.\"\"\"\n cursor = db.execute(\"UPDATE content_orders SET status = %s, updated_at = NOW() WHERE id = %s\", (status, order_id))\n db.commit()\n cursor.close()\n\n\ndef generate_content(order_id, model=\"anthropic\", collection=\"documents\", context_limit=5):\n \"\"\"\n Main content generation function.\n\n Args:\n order_id: Content order ID\n model: 'anthropic' or 'ollama'\n collection: Qdrant collection to search\n context_limit: Number of context chunks\n\n Returns:\n dict with version_id, content, sources\n \"\"\"\n db.connect()\n\n try:\n # Load order\n order = get_order(order_id)\n if not order:\n return {\"error\": f\"Order {order_id} not found\"}\n\n # Update status\n update_order_status(order_id, \"generating\")\n\n # Get RAG context\n context = get_rag_context(order[\"briefing\"], collection, context_limit)\n\n # Build profile\/contract\/structure\n profile = None\n if order.get(\"profile_config\"):\n config = (\n json.loads(order[\"profile_config\"])\n if isinstance(order[\"profile_config\"], str)\n else order[\"profile_config\"]\n )\n profile = {\"name\": order[\"profile_name\"], \"config\": config}\n\n contract = None\n if order.get(\"contract_config\"):\n config = (\n json.loads(order[\"contract_config\"])\n if isinstance(order[\"contract_config\"], str)\n else order[\"contract_config\"]\n )\n contract = {\"name\": order[\"contract_name\"], \"config\": config}\n\n structure = None\n if order.get(\"structure_config\"):\n config = (\n json.loads(order[\"structure_config\"])\n if isinstance(order[\"structure_config\"], str)\n else order[\"structure_config\"]\n )\n structure = {\"name\": order[\"structure_name\"], \"config\": config}\n\n # Build prompt\n prompt = build_generation_prompt(order[\"briefing\"], context, profile, contract, structure)\n\n # Generate content\n content = call_llm(prompt, model)\n\n # Get current version number\n cursor = db.execute(\n \"SELECT MAX(version_number) as max_v FROM content_versions WHERE order_id = %s\", (order_id,)\n )\n result = cursor.fetchone()\n cursor.close()\n version_number = (result[\"max_v\"] or 0) + 1\n\n # Save version\n version_id = save_version(order_id, content, version_number)\n\n # Save sources\n save_sources(order_id, context)\n\n # Update status\n update_order_status(order_id, \"critique\")\n\n return {\n \"success\": True,\n \"order_id\": order_id,\n \"version_id\": version_id,\n \"version_number\": version_number,\n \"content\": content,\n \"sources\": [{\"source\": c[\"source\"], \"score\": c[\"score\"]} for c in context],\n }\n\n except Exception as e:\n update_order_status(order_id, \"draft\")\n return {\"error\": str(e)}\n finally:\n db.disconnect()\n\n\ndef get_critic(critic_id):\n \"\"\"Load critic from database.\"\"\"\n cursor = db.execute(\n \"\"\"SELECT c.*, p.content as prompt_content\n FROM critic... [TRUNCATED-d25cef7af92cfc8d]",
"structuredPatch": [
{
"oldStart": 79,
"oldLines": 9,
"newStart": 79,
"newLines": 9,
"lines": [
" cc.name as contract_name, cc.config as contract_config,",
" cs.name as structure_name, cs.config as structure_config",
" FROM content_orders co",
"- LEFT JOIN author_profiles ap ON co.author_profile_id = ap.id",
"- LEFT JOIN content_contracts cc ON co.contract_id = cc.id",
"- LEFT JOIN content_structures cs ON co.structure_id = cs.id",
"+ 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,),",
" )"
]
}
],
"userModified": false,
"replaceAll": false
}
}