"""Logique de l'assistant testée sans base : le seuil « sourcé ou silencieux » et la sélection des suggestions — la partie SQL est couverte par la recette réelle (convention R5.1 : pytest purs en CI).""" from siop_ai.assistant import ( CHAMPS_BILAN, SEUIL_CONFIANCE_FORTE, SEUIL_PERTINENCE, SEUIL_SUGGESTION, _cosinus, ) from siop_ai.embeddings import EmbeddeurDeterministe def test_les_seuils_sont_ordonnes(): # Pertinence et suggestion vivent dans des pipelines distincts (question → # passages vs description → libellés) : pas d'ordre imposé entre eux. # L'invariant : une suggestion tout juste retenue n'est jamais « forte ». assert 0 < SEUIL_PERTINENCE < 1 assert 0 < SEUIL_SUGGESTION < SEUIL_CONFIANCE_FORTE < 1 def test_champs_bilan_couvrent_les_six_champs_du_contrat(): assert set(CHAMPS_BILAN) == { "DOOR_STATE", "CABIN_POSITION", "ANOMALY", "EXTERNAL_CAUSE", "ACTION_TAKEN", "COMPONENT_CONCERNED", } def test_similarite_discrimine_le_bon_code(): """Le cœur de la suggestion : une description de panne de porte doit être plus proche du code « portes » que d'un code sans rapport.""" embeddeur = EmbeddeurDeterministe() description, porte, treuil = embeddeur.encoder( [ "la porte cabine rebondit, cellule encrassée, nettoyage barrière porte", "anomalie constatée : cellule ou barrière de porte encrassée", "élément concerné : treuil et moteur de traction", ] ) assert _cosinus(description, porte) > _cosinus(description, treuil) def test_cosinus_de_vecteurs_normes(): embeddeur = EmbeddeurDeterministe() [v] = embeddeur.encoder(["contrôle mensuel des portes palières"]) assert abs(_cosinus(v, v) - 1.0) < 1e-6