From 5b58886ebfd49ed2767c3c6b6f6b87ad62f04b27 Mon Sep 17 00:00:00 2001 From: dom Date: Sun, 8 Mar 2026 11:47:44 +0100 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20versioning=20s=C3=A9mantique=20(sin?= =?UTF-8?q?gle=20source=20of=20truth)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Crée src/__version__.py comme source unique de version (2.1.0) - pyproject.toml utilise dynamic version via setuptools attr - Affiche la version dans le footer de la sidebar (base.html) - Ajoute endpoint /health avec version et status Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 5 ++++- src/__version__.py | 3 +++ src/viewer/app.py | 8 +++++++- src/viewer/templates/base.html | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/__version__.py diff --git a/pyproject.toml b/pyproject.toml index 3e06aae..76c962e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend" [project] name = "t2a" -version = "2.0.0" +dynamic = ["version"] description = "Pipeline de codage CIM-10/CCAM automatise pour le PMSI hospitalier" readme = "README.md" requires-python = ">=3.11" @@ -43,6 +43,9 @@ dev = [ [project.scripts] t2a = "src.main:main" +[tool.setuptools.dynamic] +version = {attr = "src.__version__.__version__"} + [tool.setuptools.packages.find] include = ["src*"] diff --git a/src/__version__.py b/src/__version__.py new file mode 100644 index 0000000..fb8cbb7 --- /dev/null +++ b/src/__version__.py @@ -0,0 +1,3 @@ +"""Version unique du projet T2A v2 (single source of truth).""" + +__version__ = "2.1.0" diff --git a/src/viewer/app.py b/src/viewer/app.py index a78d99d..101bb0f 100644 --- a/src/viewer/app.py +++ b/src/viewer/app.py @@ -22,6 +22,7 @@ from ..config import ( ALLOWED_EXTENSIONS, UPLOAD_MAX_SIZE_MB, ) from .. import config as cfg +from ..__version__ import __version__ as APP_VERSION from ..control.cpam_context import _assess_dossier_strength from ..medical.bio_normals import BIO_NORMALS from .referentiels import ReferentielManager @@ -102,7 +103,12 @@ def create_app() -> Flask: rep = item break dossier_list.append({"name": format_dossier_name(group_name), "path": rep["path_rel"]}) - return {"dossier_list": dossier_list} + return {"dossier_list": dossier_list, "app_version": APP_VERSION} + + # --- Health check --- + @app.route("/health") + def health(): + return jsonify({"status": "ok", "version": APP_VERSION}) # =================================================================== # Routes principales diff --git a/src/viewer/templates/base.html b/src/viewer/templates/base.html index a9320af..9b7da04 100644 --- a/src/viewer/templates/base.html +++ b/src/viewer/templates/base.html @@ -421,6 +421,9 @@ +
+ v{{ app_version }} +
From 768bb9419393f376c25aef93b102452077265fe0 Mon Sep 17 00:00:00 2001 From: dom Date: Sun, 8 Mar 2026 11:49:12 +0100 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20validation=20config=20au=20d=C3=A9m?= =?UTF-8?q?arrage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajoute validate_config() dans src/config.py : * Vérifie accessibilité Ollama (warning si injoignable) * Warning si ANTHROPIC_API_KEY absente (fallback cloud indispo) * Warning si modèles CPAM et validation identiques * Vérifie qu'au moins un modèle LLM est configuré - Appel automatique au démarrage du viewer Flask - Ne fait jamais crasher l'app (warnings uniquement) Co-Authored-By: Claude Opus 4.6 --- src/config.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++ src/viewer/app.py | 7 +++++++ 2 files changed, 58 insertions(+) diff --git a/src/config.py b/src/config.py index e8e0a32..738e581 100644 --- a/src/config.py +++ b/src/config.py @@ -123,6 +123,57 @@ def check_adversarial_model_config() -> tuple[bool, str]: return False, "" +def validate_config() -> list[str]: + """Valide la configuration au démarrage et retourne la liste des warnings émis. + + Vérifie : + - OLLAMA_URL défini et accessible (warning si injoignable, pas de crash) + - ANTHROPIC_API_KEY présente (warning si absente — fallback cloud indisponible) + - Modèles CPAM et validation distincts (validation adversariale) + - Au moins un modèle LLM configuré + + Ne fait jamais crasher l'application. + """ + warnings_emitted: list[str] = [] + + # 1. Vérifier OLLAMA_URL + if not OLLAMA_URL: + msg = "OLLAMA_URL non défini — aucun LLM local disponible" + _cfg_logger.warning(msg) + warnings_emitted.append(msg) + else: + try: + import requests as _req + resp = _req.get(f"{OLLAMA_URL}/api/tags", timeout=5) + resp.raise_for_status() + _cfg_logger.info("Ollama accessible sur %s", OLLAMA_URL) + except Exception as e: + msg = f"Ollama injoignable sur {OLLAMA_URL} — {e}" + _cfg_logger.warning(msg) + warnings_emitted.append(msg) + + # 2. Vérifier ANTHROPIC_API_KEY + if not os.environ.get("ANTHROPIC_API_KEY"): + msg = "ANTHROPIC_API_KEY absente — fallback cloud (Haiku) indisponible" + _cfg_logger.warning(msg) + warnings_emitted.append(msg) + + # 3. Vérifier modèles CPAM vs validation (adversarial) + same, adv_msg = check_adversarial_model_config() + if same: + _cfg_logger.warning(adv_msg) + warnings_emitted.append(adv_msg) + + # 4. Vérifier qu'au moins un modèle LLM est configuré + has_model = bool(OLLAMA_MODEL) or any(OLLAMA_MODELS.values()) + if not has_model: + msg = "Aucun modèle LLM configuré (OLLAMA_MODEL et OLLAMA_MODELS vides)" + _cfg_logger.warning(msg) + warnings_emitted.append(msg) + + return warnings_emitted + + # --- Configuration RUM / établissement --- FINESS = os.environ.get("T2A_FINESS", "000000000") diff --git a/src/viewer/app.py b/src/viewer/app.py index 101bb0f..e577496 100644 --- a/src/viewer/app.py +++ b/src/viewer/app.py @@ -85,6 +85,13 @@ def create_app() -> Flask: except Exception as e: logger.error("Verification FAISS echouee : %s", e) + # Validation config au démarrage + try: + from ..config import validate_config + validate_config() + except Exception as e: + logger.error("Validation config échouée : %s", e) + ref_manager = ReferentielManager() val_manager = ValidationManager()