"""ADR-004 §3 — la génération est OPT-IN et configurable : clé, modèle, mode. Sans clé, le mode extractif est le contrat ; en mode api sans clé, le boot refuse (config validée au démarrage, comme l'API NestJS).""" import pytest from siop_ai.config import charger_reglages from siop_ai.generation import GenerateurExtractif, construire_generateur, construire_invite from siop_ai.recherche import ExtraitTrouve def _extrait(titre: str, locator: str, content: str) -> ExtraitTrouve: return ExtraitTrouve( source_type="DOCUMENT", document_id="d-1", work_order_id=None, titre=titre, locator=locator, content=content, score=0.9, ) def test_defaut_extractif_sans_cle(): generateur = construire_generateur("off", "", "claude-opus-4-8") assert isinstance(generateur, GenerateurExtractif) assert generateur.rediger("couple de serrage ?", [_extrait("n", "p. 1", "x")]) is None def test_mode_api_sans_cle_refuse_au_boot(monkeypatch): monkeypatch.setenv("AI_GENERATION", "api") monkeypatch.delenv("AI_API_KEY", raising=False) with pytest.raises(ValueError, match="AI_API_KEY"): charger_reglages() def test_mode_inconnu_refuse_au_boot(monkeypatch): monkeypatch.setenv("AI_GENERATION", "toujours") with pytest.raises(ValueError, match="AI_GENERATION"): charger_reglages() def test_cle_et_modele_configurables(monkeypatch): monkeypatch.setenv("AI_GENERATION", "api") monkeypatch.setenv("AI_API_KEY", "sk-test-123") monkeypatch.setenv("AI_MODEL", "claude-opus-4-8") reglages = charger_reglages() assert reglages.ai_api_key == "sk-test-123" assert reglages.ai_model == "claude-opus-4-8" def test_invite_numerote_les_extraits_et_rien_d_autre(): invite = construire_invite( "quel couple de serrage pour les guides ?", [ _extrait("Notice Gen2.pdf", "p. 42", "Serrer à 25 N·m."), _extrait("OT-2026-0341", "bilan du 2026-07-17", "Coulisseaux remplacés."), ], ) assert "[1] Notice Gen2.pdf · p. 42" in invite assert "[2] OT-2026-0341 · bilan du 2026-07-17" in invite assert invite.endswith("Question : quel couple de serrage pour les guides ?")