diff --git a/src/wetgit/ai/alerts.py b/src/wetgit/ai/alerts.py new file mode 100644 index 0000000..e32ee59 --- /dev/null +++ b/src/wetgit/ai/alerts.py @@ -0,0 +1,203 @@ +"""Change-alerts — stuur notificaties bij wetswijzigingen. + +Vergelijkt de huidige staat met de vorige en stuurt een e-mail +met een AI-gegenereerde change-summary via AgentMail. + +Usage: + python -m wetgit.ai.alerts --bwb-id BWBR0001840 --diff "..." + python -m wetgit.ai.alerts --test # Stuur test-alert +""" + +from __future__ import annotations + +import logging +import os +from datetime import date + +import httpx + +logger = logging.getLogger(__name__) + +MISTRAL_API_URL = "https://api.mistral.ai/v1/chat/completions" +AGENTMAIL_API_URL = "https://api.agentmail.to/v0" + +SUMMARY_PROMPT = """Je bent een juridisch communicatie-expert. Vat de volgende wetswijziging samen in begrijpelijk Nederlands (B1-niveau). + +Structuur: +1. **Wat is er veranderd?** (1-2 zinnen) +2. **Welke artikelen zijn geraakt?** (lijst) +3. **Waarom is dit relevant?** (1-2 zinnen, voor een niet-jurist) + +Wees kort en concreet. Max 150 woorden.""" + + +def send_change_alert( + bwb_id: str, + titel: str, + diff_text: str, + recipients: list[str] | None = None, + mistral_api_key: str | None = None, + agentmail_api_key: str | None = None, +) -> bool: + """Genereer een change-summary en stuur een e-mail alert. + + Args: + bwb_id: BWB identificatienummer van de gewijzigde regeling. + titel: Titel van de regeling. + diff_text: Git diff van de wijziging. + recipients: E-mailadressen (default: coornhert@wetgit.nl). + mistral_api_key: Mistral API key. + agentmail_api_key: AgentMail API key. + + Returns: + True als de alert succesvol verstuurd is. + """ + mistral_key = mistral_api_key or os.environ.get("MISTRAL_API_KEY", "") + agentmail_key = agentmail_api_key or os.environ.get("AGENTMAIL_API_KEY", "") + recipients = recipients or ["coornhert@wetgit.nl"] + + if not mistral_key or not agentmail_key: + logger.error("MISTRAL_API_KEY of AGENTMAIL_API_KEY ontbreekt") + return False + + # Stap 1: Analyseer de diff + added = sum(1 for l in diff_text.split("\n") if l.startswith("+") and not l.startswith("+++")) + removed = sum(1 for l in diff_text.split("\n") if l.startswith("-") and not l.startswith("---")) + + # Stap 2: Genereer AI-samenvatting van de wijziging + summary = _generate_change_summary(titel, diff_text, mistral_key) + if not summary: + summary = f"De {titel} is gewijzigd (+{added}/-{removed} regels). Bekijk de diff voor details." + + # Stap 3: Bouw de e-mail + subject = f"WetGit Alert: {titel} gewijzigd ({date.today().isoformat()})" + + body = f"""WetGit Change Alert +{'=' * 40} + +Regeling: {titel} +BWB-ID: {bwb_id} +Datum: {date.today().isoformat()} +Wijziging: +{added} / -{removed} regels + +Samenvatting +{'-' * 40} + +{summary} + +Details +{'-' * 40} + +Bekijk de volledige wijziging: +https://git.wetgit.nl/wetgit/rijk/commits/branch/main + +Officiele tekst: +https://wetten.overheid.nl/{bwb_id} + +--- +Dit is een automatisch bericht van WetGit (wetgit.nl). +Dit is geen juridisch advies. +""" + + # Stap 4: Verstuur via AgentMail + return _send_email( + from_address="coornhert@wetgit.nl", + to_addresses=recipients, + subject=subject, + body=body, + agentmail_key=agentmail_key, + ) + + +def _generate_change_summary(titel: str, diff_text: str, api_key: str) -> str | None: + """Genereer een AI-samenvatting van de wetswijziging.""" + # Beperk diff tot ~4000 chars + if len(diff_text) > 4000: + diff_text = diff_text[:4000] + "\n\n[...diff ingekort...]" + + try: + resp = httpx.post( + MISTRAL_API_URL, + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json={ + "model": "mistral-large-latest", + "messages": [ + {"role": "system", "content": SUMMARY_PROMPT}, + {"role": "user", "content": f"Wijziging in de {titel}:\n\n```diff\n{diff_text}\n```"}, + ], + "temperature": 0.3, + "max_tokens": 300, + }, + timeout=30, + ) + resp.raise_for_status() + return resp.json()["choices"][0]["message"]["content"].strip() + except Exception as e: + logger.error("Mistral API fout bij change summary: %s", e) + return None + + +def _send_email( + from_address: str, + to_addresses: list[str], + subject: str, + body: str, + agentmail_key: str, +) -> bool: + """Verstuur e-mail via AgentMail API.""" + try: + resp = httpx.post( + f"{AGENTMAIL_API_URL}/inboxes/{from_address}/messages/send", + headers={ + "Authorization": f"Bearer {agentmail_key}", + "Content-Type": "application/json", + }, + json={ + "to": to_addresses, + "subject": subject, + "text": body, + }, + timeout=15, + ) + resp.raise_for_status() + logger.info("Alert verstuurd naar %s: %s", to_addresses, subject) + return True + except httpx.HTTPError as e: + logger.error("AgentMail fout: %s — %s", e, getattr(e, 'response', {}).text if hasattr(e, 'response') else '') + return False + + +if __name__ == "__main__": + import argparse + + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") + + parser = argparse.ArgumentParser(description="WetGit change alerts") + parser.add_argument("--test", action="store_true", help="Stuur een test-alert") + parser.add_argument("--bwb-id", default="BWBR0001840") + parser.add_argument("--to", default="coornhert@wetgit.nl") + args = parser.parse_args() + + if args.test: + # Simuleer een wijziging in de Grondwet + test_diff = """--- a/wet/grondwet/BWBR0001840/README.md ++++ b/wet/grondwet/BWBR0001840/README.md +@@ -25,7 +25,7 @@ + ### Artikel 13 + +-**1.** Het briefgeheim is onschendbaar, behalve, in de gevallen bij de wet bepaald, op last van de rechter. ++**1.** Ieder heeft recht op eerbiediging van het brief- en telecommunicatiegeheim, behalve in de gevallen bij de wet bepaald, op last van de rechter. + +-**2.** Het telefoon- en telegraafgeheim is onschendbaar, behalve, in de gevallen bij de wet bepaald, door of met machtiging van hen die daartoe bij de wet zijn aangewezen. ++**2.** Het recht op eerbiediging van het brief- en telecommunicatiegeheim kan worden beperkt bij of krachtens de wet, met inachtneming van de voorwaarden die bij de wet zijn gesteld. +""" + success = send_change_alert( + bwb_id=args.bwb_id, + titel="Grondwet", + diff_text=test_diff, + recipients=[args.to], + ) + print(f"Test alert {'verstuurd' if success else 'MISLUKT'}")