- Cache persistant JSON thread-safe pour les résultats Ollama (invalidation par modèle) - Parallélisation des appels Ollama (ThreadPoolExecutor, 2 workers) - 6 nouvelles règles de filtrage DAS parasites (doublons, ponctuation, OCR, labo, fragments) - Client Ollama centralisé (mode JSON natif + retry) - Module GHM (estimation CMD/sévérité) - Module contrôle CPAM (parser + contre-argumentation RAG) - Export RUM (format RSS) - Viewer enrichi (détail dossier) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
81 lines
2.4 KiB
Python
81 lines
2.4 KiB
Python
"""Client Ollama partagé — appel LLM en mode JSON natif."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
|
|
import requests
|
|
|
|
from ..config import OLLAMA_URL, OLLAMA_MODEL, OLLAMA_TIMEOUT
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def parse_json_response(raw: str) -> dict | None:
|
|
"""Parse une réponse JSON d'Ollama, en gérant les blocs markdown."""
|
|
text = raw.strip()
|
|
if text.startswith("```"):
|
|
first_nl = text.find("\n")
|
|
if first_nl != -1:
|
|
text = text[first_nl + 1:]
|
|
if text.rstrip().endswith("```"):
|
|
text = text.rstrip()[:-3]
|
|
text = text.strip()
|
|
|
|
try:
|
|
return json.loads(text)
|
|
except json.JSONDecodeError:
|
|
logger.warning("Ollama : JSON invalide : %s", raw[:200])
|
|
return None
|
|
|
|
|
|
def call_ollama(
|
|
prompt: str,
|
|
temperature: float = 0.1,
|
|
max_tokens: int = 2500,
|
|
) -> dict | None:
|
|
"""Appelle Ollama en mode JSON natif avec retry.
|
|
|
|
Args:
|
|
prompt: Le prompt à envoyer.
|
|
temperature: Température de génération (défaut: 0.1).
|
|
max_tokens: Nombre max de tokens (défaut: 2500).
|
|
|
|
Returns:
|
|
Le dict JSON parsé, ou None en cas d'erreur.
|
|
"""
|
|
for attempt in range(2):
|
|
try:
|
|
response = requests.post(
|
|
f"{OLLAMA_URL}/api/generate",
|
|
json={
|
|
"model": OLLAMA_MODEL,
|
|
"prompt": prompt,
|
|
"stream": False,
|
|
"format": "json",
|
|
"options": {
|
|
"temperature": temperature,
|
|
"num_predict": max_tokens,
|
|
},
|
|
},
|
|
timeout=OLLAMA_TIMEOUT,
|
|
)
|
|
response.raise_for_status()
|
|
raw = response.json().get("response", "")
|
|
result = parse_json_response(raw)
|
|
if result is not None:
|
|
return result
|
|
if attempt == 0:
|
|
logger.info("Ollama : retry après échec de parsing")
|
|
except requests.ConnectionError:
|
|
logger.warning("Ollama non disponible (connexion refusée)")
|
|
return None
|
|
except requests.Timeout:
|
|
logger.warning("Ollama timeout après %ds", OLLAMA_TIMEOUT)
|
|
return None
|
|
except (requests.RequestException, json.JSONDecodeError) as e:
|
|
logger.warning("Ollama erreur : %s", e)
|
|
return None
|
|
return None
|