Files
Aissa 910ba465d7 Add War Room voice meeting, fix chat overlay, and ship 5 cyan UI themes.
Voice Room lets humans talk with agents via Whisper/TTS; chat typing no longer blocked by a full-screen overlay, attachments/avatars are larger, and Settings exposes five vivid mid-dark cyan/blue themes for the whole cockpit.
2026-08-16 15:49:29 +00:00

181 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Daily Herman War Room digest → Telegram (Aïssa + Mo)."""
from __future__ import annotations
import logging
from datetime import datetime, timezone
from typing import Any
import httpx
from app.config import settings
from app.db import execute, fetch_all, fetch_one
from app.services import llm_router
log = logging.getLogger("cockpit.warroom_digest")
def _recipients() -> list[tuple[str, str]]:
"""(chat_id, display_name)"""
return [
(str(getattr(settings, "HERMES_CEO_CHAT_ID", None) or "8859782446"), "Aïssa"),
(str(getattr(settings, "HERMES_CTO_CHAT_ID", None) or "789036463"), "Mo"),
]
def collect_warroom_day(*, hours: int = 24, limit: int = 120) -> list[dict[str, Any]]:
hours = max(1, min(int(hours), 72))
rows = fetch_all(
"""
SELECT m.id, m.sender_type, m.agent_key, m.username, m.content, m.created_at, r.kind, r.title
FROM warroom_messages m
JOIN warroom_rooms r ON r.id = m.room_id
WHERE m.created_at >= NOW() - make_interval(hours => %s)
AND r.kind = 'warroom'
ORDER BY m.created_at ASC
LIMIT %s
""",
(hours, limit),
)
out = []
for r in rows or []:
item = dict(r)
if item.get("created_at") and hasattr(item["created_at"], "isoformat"):
item["created_at"] = item["created_at"].isoformat()
out.append(item)
return out
def _transcript(messages: list[dict[str, Any]], *, max_chars: int = 6000) -> str:
lines: list[str] = []
for m in messages:
st = m.get("sender_type")
if st == "user":
who = m.get("username") or "user"
elif st == "agent":
who = m.get("agent_key") or "agent"
else:
who = "system"
content = (m.get("content") or "").strip().replace("\n", " ")
if not content:
continue
lines.append(f"- {who}: {content[:280]}")
text = "\n".join(lines)
return text[-max_chars:]
async def generate_digest_text(messages: list[dict[str, Any]]) -> str:
day = datetime.now(timezone.utc).strftime("%Y-%m-%d")
if not messages:
return (
f"📋 War Room dagrapport {day}\n\n"
"Geen activiteit in #war-room vandaag. Agents stonden klaar; geen overleg gelogd."
)
transcript = _transcript(messages)
system = (
"Je bent Herman, Co-CEO van Foodlinkk. Schrijf een beknopte Nederlandse "
"Telegram-samenvatting van het War Room-overleg voor Aïssa (CEO) en Mo (CTO).\n"
"Structuur:\n"
f"1) Kop: 📋 War Room dagrapport {day}\n"
"2) 3–6 bullets: wat agents bespraken (pipeline, deals, acties, blockers)\n"
"3) Sectie 'Acties / open punten' met concrete next steps\n"
"4) 1 zin afsluiting\n"
"Max ~350 woorden. Geen @zelf-prefix. Geen 'agents staan uit'."
)
user = f"War Room transcript (nieuwste onderaan):\n{transcript}"
reply, _meta = await llm_router.chat_messages(
[{"role": "system", "content": system}, {"role": "user", "content": user}],
timeout=90.0,
user=str(getattr(settings, "HERMES_CEO_CHAT_ID", None) or "8859782446"),
max_tokens=900,
temperature=0.3,
)
text = (reply or "").strip()
if not text.startswith("📋"):
text = f"📋 War Room dagrapport {day}\n\n{text}"
return text[:3500]
async def _deliver_telegram(chat_id: str, name: str, digest: str) -> dict[str, Any]:
"""Ask Hermes agent to deliver the digest into that Telegram chat."""
system = (
f"Je bent Herman. Bezorg het volgende War Room-dagrapport NU via Telegram "
f"aan {name} (chat_id={chat_id}). Stuur de tekst zo letterlijk mogelijk. "
"Bevestig kort of verzonden."
)
try:
reply, meta = await llm_router.chat_messages(
[
{"role": "system", "content": system},
{"role": "user", "content": digest},
],
timeout=120.0,
user=str(chat_id),
max_tokens=200,
temperature=0.1,
)
return {
"ok": True,
"chat_id": chat_id,
"name": name,
"confirm": (reply or "")[:240],
"provider": (meta or {}).get("provider_type"),
}
except Exception as exc:
log.exception("warroom digest deliver failed %s", chat_id)
return {"ok": False, "chat_id": chat_id, "name": name, "error": str(exc)}
def _persist(digest: str, message_count: int, deliveries: list[dict[str, Any]]) -> None:
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)
""",
(
"herman",
"warroom_digest",
"warroom_daily_digest",
"War Room dagrapport verstuurd",
digest[:4000],
"completed",
"telegram",
__import__("json").dumps(
{
"message_count": message_count,
"deliveries": deliveries,
}
),
),
)
except Exception:
log.exception("persist digest event failed")
try:
from app.services import agent_chat
agent_chat.post_system_line(
"📤 Dagrapport verstuurd naar Aïssa + Mo via Telegram.\n\n" + digest[:1500],
agent_key="herman",
metadata={"kind": "warroom_digest", "deliveries": deliveries},
)
except Exception:
log.exception("post digest to warroom failed")
async def run_daily_digest(*, hours: int = 24, deliver: bool = True) -> dict[str, Any]:
messages = collect_warroom_day(hours=hours)
digest = await generate_digest_text(messages)
deliveries: list[dict[str, Any]] = []
if deliver:
for chat_id, name in _recipients():
deliveries.append(await _deliver_telegram(chat_id, name, digest))
_persist(digest, len(messages), deliveries)
return {
"ok": True,
"message_count": len(messages),
"digest": digest,
"deliveries": deliveries,
"generated_at": datetime.now(timezone.utc).isoformat(),
}