Add Légifrance API integration for live legal text injection
Implements a two-layer approach to ensure legal texts are accurate and current: Option A — runtime injection (legifrance_client.py): - LegifranceClient: OAuth2 via PISTE (oauth.piste.gouv.fr), fetches consolidated article text via api.piste.gouv.fr/dila/legifrance/lf-engine-app - Redis cache with 24h TTL (in-memory fallback if no Redis) - build_filter_prompt() injects live texts into the prompt with explicit priority over static descriptions — model uses official Légifrance text, not training memory - Graceful fallback to static prompt if API is unavailable — filtering never blocks Option B — weekly sync script (scripts/check_legal_refs.py): - Reads legal_refs.yaml (11 tracked articles, priority critical/high/medium) - Fetches current text from Légifrance, diffs against stored baseline - --init: resolves LEGIARTI IDs on first run - --check: report only; --update: saves new texts to YAML - Exit code 2 when changes detected (CI/n8n-compatible) Supporting changes: - legal_refs.yaml: tracked articles with code LEGITEXT IDs, priorities, rationale - requirements.txt: add pyyaml>=6.0.2 and requests>=2.32.0 - .env.example: document PISTE_CLIENT_ID, PISTE_CLIENT_SECRET, LEGIFRANCE_CACHE_TTL - transparence.tsx: document the Légifrance injection for public transparency - Fixed portal URL: piste.gouv.fr (not piste.api.gouv.fr — verified June 2026) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,15 +9,33 @@ import json
|
||||
import os
|
||||
import logging
|
||||
from openai import OpenAI, BadRequestError
|
||||
from legal_framework import LEGAL_FILTER_PROMPT, SYNTHESIS_PROMPT, LAW_CHECK_PROMPT
|
||||
from legal_framework import LEGAL_FILTER_PROMPT, SYNTHESIS_PROMPT, LAW_CHECK_PROMPT, build_filter_prompt
|
||||
from legifrance_client import get_legifrance_client
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MISTRAL_BASE_URL = "https://api.mistral.ai/v1"
|
||||
_LEGAL_REFS_PATH = os.path.join(os.path.dirname(__file__), "legal_refs.yaml")
|
||||
|
||||
_client: OpenAI | None = None
|
||||
|
||||
|
||||
def _get_live_legal_texts() -> dict[str, str]:
|
||||
"""
|
||||
Récupère les textes consolidés des articles critiques depuis Légifrance (PISTE).
|
||||
Retourne {} si PISTE n'est pas configuré ou en cas d'erreur — sans bloquer le filtrage.
|
||||
Les résultats sont mis en cache dans Redis (24h).
|
||||
"""
|
||||
try:
|
||||
lf = get_legifrance_client()
|
||||
if not lf.available:
|
||||
return {}
|
||||
return lf.fetch_tracked_articles(_LEGAL_REFS_PATH)
|
||||
except Exception:
|
||||
logger.warning("Légifrance: impossible de récupérer les textes live — mode statique")
|
||||
return {}
|
||||
|
||||
|
||||
def get_client() -> OpenAI:
|
||||
"""
|
||||
Supporte deux modes (par ordre de priorité) :
|
||||
@@ -58,12 +76,16 @@ def filter_idea(content: str) -> dict:
|
||||
try:
|
||||
client = get_client()
|
||||
filter_model = os.environ.get("FILTER_MODEL", os.environ.get("OPENAI_FILTER_MODEL", "mistral-small-latest"))
|
||||
live_texts = _get_live_legal_texts()
|
||||
system_prompt = build_filter_prompt(live_texts)
|
||||
if live_texts:
|
||||
logger.debug("Légifrance: %d article(s) injecté(s) dans le prompt", len(live_texts))
|
||||
response = client.chat.completions.create(
|
||||
model=filter_model,
|
||||
max_tokens=300,
|
||||
response_format={"type": "json_object"},
|
||||
messages=[
|
||||
{"role": "system", "content": LEGAL_FILTER_PROMPT},
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": f'Idée soumise : "{content}"'},
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user