"""Vérifie le round-trip entraînement -> prédiction du `textcat` (la partie "comprendre le sens, pas juste les mots clés" du pipeline — voir le commentaire de `tech-step-matcher.ts` côté apps/api pour la motivation d'origine).""" from intent_service.locale_pipeline import LocalePipeline, TrainEntry _ENTRIES = [ TrainEntry( uid="melt", synonyms=["faire fondre"], utterances=[ "faire fondre le beurre à feu doux", "laisser fondre le beurre dans la poêle", "jusqu'à ce que le beurre ait disparu", "jusqu'à ce que le beurre ait complètement disparu dans la poêle", ], ), TrainEntry( uid="boil", synonyms=["bouillir"], utterances=[ "porter l'eau à ébullition", "faire bouillir l'eau salée", "laisser bouillir quelques minutes", "porter à ébullition puis baisser le feu", ], ), ] def test_train_returns_label_example_and_synonym_counts(): pipeline = LocalePipeline("fr") label_count, example_count, synonym_count = pipeline.train(_ENTRIES) assert label_count == 2 # `example_count` couvre les utterances *et* les synonyms (voir # LocalePipeline.train — les synonymes sont aussi des exemples # d'entraînement pour le textcat, pas seulement pour le PhraseMatcher). expected_examples = sum(len(entry.utterances) + len(entry.synonyms) for entry in _ENTRIES) assert example_count == expected_examples assert synonym_count == sum(len(entry.synonyms) for entry in _ENTRIES) assert pipeline.is_trained is True def test_classifies_a_paraphrase_never_using_the_techniques_own_verb(): # Le cas motivant tout le pipeline (voir tech-step-matcher.ts) : aucune # forme de "fondre" dans cette phrase, mais elle ne peut raisonnablement # signifier que `melt` une fois le textcat entraîné sur les paraphrases # ci-dessus. pipeline = LocalePipeline("fr") pipeline.train(_ENTRIES) result = pipeline.process("jusqu'à ce que le beurre ait disparu dans la poêle") assert result.intent == "melt" assert result.score > 0.5 def test_empty_entries_leaves_the_pipeline_untrained(): pipeline = LocalePipeline("fr") pipeline.train([]) assert pipeline.is_trained is False result = pipeline.process("faire fondre le beurre") assert result.intent is None assert result.entities == [] def test_retraining_replaces_the_previous_textcat_rather_than_accumulating(): # `textcat` (exclusive_classes) exige >= 2 labels (voir la note dans # LocalePipeline.train) — le second entraînement garde donc 2 entrées, # mais remplace "boil" par une technique différente ("chop"), pour # vérifier que "boil" ne peut plus jamais ressortir après coup (pas de # fusion incrémentale — voir la doc de `LocalePipeline.train`). pipeline = LocalePipeline("fr") pipeline.train(_ENTRIES) chop_entry = TrainEntry(uid="chop", synonyms=["couper"], utterances=["couper les légumes en dés"]) pipeline.train([_ENTRIES[0], chop_entry]) result = pipeline.process("porter l'eau à ébullition") assert result.intent != "boil"