"""Couvre `LocalePipeline`'s second `PhraseMatcher` (ustensiles, `utensil_vocabulary.py`) — même style que `test_locale_pipeline_entities.py` (offsets exacts, insensibilité accents/casse), mais contre le vocabulaire *réel* (`UTENSIL_VOCABULARY`, statique, construit par `preload()` — pas besoin d'un jeu de test dédié comme pour les techniques, voir `LocalePipeline.preload`'s own comment).""" from intent_service.locale_pipeline import LocalePipeline, TrainEntry # Un `train()` minimal suffit — le `PhraseMatcher` d'ustensiles est # construit par `preload()` (appelé par `train()`), indépendamment du # `TrainEntry` de techniques passé ici (voir `preload()`'s own comment sur # pourquoi les deux ne sont pas couplés). _MINIMAL_ENTRIES = [ TrainEntry(uid="melt", synonyms=["fondre"], utterances=["faire fondre le beurre"]), TrainEntry(uid="simmer", synonyms=["mijoter"], utterances=["faire mijoter à feu doux"]), ] def _fr_pipeline() -> LocalePipeline: pipeline = LocalePipeline("fr") pipeline.train(_MINIMAL_ENTRIES) return pipeline def test_matches_a_real_utensil_with_exact_span(): pipeline = _fr_pipeline() text = "Dans une poêle chaude, faire fondre le beurre" result = pipeline.process(text) pan_entities = [e for e in result.entities if e.uid == "pan"] assert len(pan_entities) == 1 entity = pan_entities[0] assert entity.kind == "utensil" assert text[entity.start : entity.end] == "poêle" def test_is_case_and_accent_insensitive(): pipeline = _fr_pipeline() result = pipeline.process("Verser dans la POÊLE") utensil_uids = [e.uid for e in result.entities if e.kind == "utensil"] assert utensil_uids == ["pan"] def test_matches_a_multi_word_synonym(): pipeline = _fr_pipeline() text = "Découper les légumes sur la planche à découper" result = pipeline.process(text) board_entities = [e for e in result.entities if e.uid == "cuttingBoard"] assert len(board_entities) == 1 assert text[board_entities[0].start : board_entities[0].end] == "planche à découper" def test_technique_and_utensil_are_both_returned_without_interfering(): pipeline = _fr_pipeline() text = "Dans une casserole, faire mijoter à feu doux" result = pipeline.process(text) kinds_by_uid = {e.uid: e.kind for e in result.entities} assert kinds_by_uid.get("simmer") == "technique" assert kinds_by_uid.get("saucepan") == "utensil" def test_returns_no_utensil_entities_when_none_are_mentioned(): pipeline = _fr_pipeline() result = pipeline.process("Laisser reposer la pâte une heure") assert [e for e in result.entities if e.kind == "utensil"] == [] def test_matches_english_utensils_too(): pipeline = LocalePipeline("en") pipeline.train([TrainEntry(uid="chop", synonyms=["chop"], utterances=["chop the onions finely"])]) text = "Heat the pan before adding the onions" result = pipeline.process(text) pan_entities = [e for e in result.entities if e.uid == "pan"] assert len(pan_entities) == 1 assert pan_entities[0].kind == "utensil" assert text[pan_entities[0].start : pan_entities[0].end] == "pan"