from __future__ import annotations import json from typing import Any import httpx from app.config import settings from app.db import execute, fetch_one from app.services import ollama AGENTS: dict[str, dict[str, str]] = { "marketing": {"name": "Marketing", "persona": "Social, brand voice, campaigns for Foodlinkk."}, "bizdev": {"name": "BizDev", "persona": "Pipeline, retail partnerships, deal structuring."}, "finance": {"name": "Finance", "persona": "Margins, cashflow, pricing for food brands."}, "sourcing": {"name": "Sourcing", "persona": "Suppliers, MOQ, lead times, procurement."}, "product": {"name": "Product", "persona": "SKU development, launch timelines, shelf readiness."}, "halal": {"name": "Halal", "persona": "Halal compliance, certification, ingredient vetting."}, "design": {"name": "Design", "persona": "Packaging, visual identity, retail presentation."}, "knowledge": {"name": "Knowledge", "persona": "Internal docs, RAG, policy answers."}, } IMAGE_KEYWORDS = ( "maak foto", "maak een foto", "genereer foto", "genereer afbeelding", "maak afbeelding", "productfoto", "genereer image", "generate image", "make image", "maak plaatje", "/genfoto", ) def _wants_image(raw: str) -> bool: t = (raw or "").strip().lower() return any(k in t for k in IMAGE_KEYWORDS) def _extract_image_prompt(raw: str) -> str: t = raw.strip() lower = t.lower() for k in IMAGE_KEYWORDS: if lower.startswith(k): rest = t[len(k):].strip(" :,-") if rest: return rest for k in IMAGE_KEYWORDS: if k in lower: idx = lower.index(k) + len(k) rest = t[idx:].strip(" :,-") if rest: return rest return t async def _log_event(agent_name: str, event_type: str, title: str, body: str, metadata: dict | None = None) -> None: payload = { "agent_name": agent_name, "agent_type": "herman_delegate", "event_type": event_type, "title": title[:255], "body": body, "metadata": metadata or {}, "status": "completed", "channel": "herman", } try: async with httpx.AsyncClient(timeout=15.0) as client: await client.post(f"{settings.TOOLS_API_URL.rstrip('/')}/events", json=payload) except Exception: try: execute( """INSERT INTO agent_events (agent_name, agent_type, event_type, title, body, status, channel, metadata) VALUES (%s, %s, %s, %s, %s, %s, %s, %s::jsonb)""", (agent_name, "herman_delegate", event_type, title[:255], body, "completed", "herman", json.dumps(metadata or {})), ) except Exception: pass def _pick_agent(raw: str) -> str: text = (raw or "").strip().lower() first = text.split()[0].replace(",", "").replace(".", "") if text else "knowledge" if first in AGENTS: return first for k in AGENTS: if k in text: return k return "knowledge" async def chat(message: str) -> dict[str, Any]: if _wants_image(message): prompt = _extract_image_prompt(message) try: async with httpx.AsyncClient(timeout=620.0) as client: r = await client.post( f"{settings.TOOLS_API_URL.rstrip('/')}/images/generate", json={"prompt": prompt, "width": 512, "height": 512, "steps": 15}, ) r.raise_for_status() data = r.json() filename = data.get("filename", "") subfolder = data.get("subfolder", "") img_type = data.get("type", "output") proxy = f"/api/ai/generated-image?filename={filename}&subfolder={subfolder}&type={img_type}" reply = f"Afbeelding gegenereerd voor: {prompt}" await _log_event("design", "image_generated", "ComfyUI via Herman", prompt[:500], {"filename": filename}) return { "agent": "design", "agent_label": "Design", "reply": reply, "image_url": proxy, "prompt": prompt, } except Exception as exc: return { "agent": "design", "agent_label": "Design", "reply": f"Kon geen afbeelding genereren: {exc}", } try: async with httpx.AsyncClient(timeout=620.0) as client: r = await client.post( f"{settings.HERMAN_ORCHESTRATOR_URL.rstrip('/')}/chat", json={"message": message, "agent": "default", "use_crm": True, "channel": "cockpit"}, ) r.raise_for_status() data = r.json() delegated = data.get("delegated_agents") or [data.get("agent", "herman")] await _log_event( "herman", "openswarm_delegation", f"Herman → {', '.join(delegated)}", message[:2000], {"delegated": delegated, "reason": data.get("routing_reason", "")}, ) return { "agent": data.get("agent", "herman"), "agent_label": data.get("agent_label", "Herman"), "reply": data.get("reply", ""), "delegated_agents": delegated, "routing_reason": data.get("routing_reason", ""), } except Exception as exc: return { "agent": "herman", "agent_label": "Herman", "reply": f"Herman orchestrator niet bereikbaar: {exc}", } async def generate_briefing() -> str: from app.services.briefing import generate_daily_briefing content, stats = await generate_daily_briefing() await _log_event("herman", "briefing", "CEO briefing generated", content[:1500], {"stats": stats}) return content