ae970b2a32
Replace the existing Node.js API server with a Python Flask application, implementing robust AI-driven content filtering based on international human rights law and enhancing security measures. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 30f4e946-427f-4b27-989d-531b9116d12f Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8af7d2ec-2cc3-4ece-8af3-9f071488d072/923ae0e3-a363-4db8-b04a-e8baca2a1330/AWHAa3Z Replit-Helium-Checkpoint-Created: true
119 lines
4.6 KiB
Python
119 lines
4.6 KiB
Python
"""
|
|
Agent IA pour le filtrage éthique et la synthèse démocratique.
|
|
Utilise les Intégrations IA de Replit (OpenAI proxy).
|
|
"""
|
|
import json
|
|
import os
|
|
import logging
|
|
from openai import OpenAI, BadRequestError
|
|
from legal_framework import LEGAL_FILTER_PROMPT, SYNTHESIS_PROMPT
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_client: OpenAI | None = None
|
|
|
|
|
|
def get_client() -> OpenAI:
|
|
global _client
|
|
if _client is None:
|
|
base_url = os.environ.get("AI_INTEGRATIONS_OPENAI_BASE_URL")
|
|
api_key = os.environ.get("AI_INTEGRATIONS_OPENAI_API_KEY")
|
|
if not base_url or not api_key:
|
|
raise RuntimeError(
|
|
"AI_INTEGRATIONS_OPENAI_BASE_URL et AI_INTEGRATIONS_OPENAI_API_KEY "
|
|
"sont requis. Configurez les intégrations Replit AI."
|
|
)
|
|
_client = OpenAI(base_url=base_url, api_key=api_key)
|
|
return _client
|
|
|
|
|
|
def filter_idea(content: str) -> dict:
|
|
"""
|
|
Filtre une idée selon le cadre légal international des droits humains.
|
|
Retourne : {"accepted": bool, "reason"?: str, "legal_basis"?: str}
|
|
"""
|
|
try:
|
|
client = get_client()
|
|
response = client.chat.completions.create(
|
|
model="gpt-5-mini",
|
|
max_completion_tokens=300,
|
|
response_format={"type": "json_object"},
|
|
messages=[
|
|
{"role": "system", "content": LEGAL_FILTER_PROMPT},
|
|
{"role": "user", "content": f'Idée soumise : "{content}"'},
|
|
],
|
|
)
|
|
raw = (
|
|
response.choices[0].message.content
|
|
or '{"accepted": false, "reason": "Contenu non conforme aux principes démocratiques."}'
|
|
)
|
|
raw = raw.strip()
|
|
if raw.startswith("```"):
|
|
raw = raw.split("```")[1]
|
|
if raw.startswith("json"):
|
|
raw = raw[4:]
|
|
raw = raw.strip()
|
|
start = raw.find("{")
|
|
end = raw.rfind("}") + 1
|
|
if start != -1 and end > start:
|
|
raw = raw[start:end]
|
|
result = json.loads(raw)
|
|
return result
|
|
except json.JSONDecodeError:
|
|
logger.warning("Impossible de parser la réponse JSON du filtre, raw=%r", raw if 'raw' in dir() else 'N/A')
|
|
return {"accepted": False, "reason": "Erreur interne de filtrage"}
|
|
except BadRequestError as e:
|
|
if "content_filter" in str(e) or "content management policy" in str(e):
|
|
logger.warning("Contenu bloqué par le filtre de sécurité du proxy IA — rejet automatique")
|
|
return {
|
|
"accepted": False,
|
|
"reason": (
|
|
"Ce contenu a été automatiquement bloqué car il contient des propos "
|
|
"haineux ou violents graves, contraires à la dignité humaine."
|
|
),
|
|
"legal_basis": (
|
|
"DUDH Art. 1 (dignité humaine), DUDH Art. 20 (interdiction de la haine), "
|
|
"PIDCP Art. 20, CEDH Art. 17 (abus de droit)"
|
|
),
|
|
}
|
|
logger.exception("Erreur API lors du filtrage")
|
|
return {"accepted": False, "reason": "Service temporairement indisponible"}
|
|
except Exception:
|
|
logger.exception("Erreur lors du filtrage de l'idée")
|
|
return {"accepted": False, "reason": "Service temporairement indisponible"}
|
|
|
|
|
|
def synthesize_ideas(ideas: list[str]) -> str:
|
|
"""
|
|
Synthétise une liste d'idées acceptées en un texte collectif
|
|
ancré dans les valeurs démocratiques et les droits humains.
|
|
"""
|
|
if not ideas:
|
|
return (
|
|
"Aucune idée n'a encore été soumise. "
|
|
"Soyez le premier à partager votre vision pour une société meilleure, "
|
|
"fondée sur la Déclaration universelle des droits de l'homme."
|
|
)
|
|
try:
|
|
client = get_client()
|
|
ideas_text = "\n".join(f"{i + 1}. {idea}" for i, idea in enumerate(ideas))
|
|
response = client.chat.completions.create(
|
|
model="gpt-5.2",
|
|
max_completion_tokens=1200,
|
|
messages=[
|
|
{"role": "system", "content": SYNTHESIS_PROMPT},
|
|
{
|
|
"role": "user",
|
|
"content": (
|
|
f"Voici les {len(ideas)} idée(s) citoyenne(s) validées :\n\n"
|
|
f"{ideas_text}\n\n"
|
|
"Rédige La Voix du Peuple."
|
|
),
|
|
},
|
|
],
|
|
)
|
|
return response.choices[0].message.content or "Synthèse en cours..."
|
|
except Exception:
|
|
logger.exception("Erreur lors de la synthèse des idées")
|
|
return "La synthèse est temporairement indisponible. Vos idées ont bien été enregistrées."
|