Identity-only senses no longer lock a new cue. Learning stages, journal-generate review, and additive docs follow the closed section 6. Co-authored-by: Cursor <cursoragent@cursor.com>
69 lines
2.9 KiB
Python
69 lines
2.9 KiB
Python
"""Unit tests for student/teacher comparison. No database."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
os.environ.setdefault("KANSHO_PROVIDER_KEY", "")
|
|
os.environ.setdefault("KANSHO_DETECT_PROVIDER_KEY", "")
|
|
|
|
from unittest.mock import patch
|
|
|
|
from detect_learning import canonicalize_detect_mode, decision_for_mention
|
|
from detect_pipeline import (
|
|
MentionSpan,
|
|
compact_diff_counters,
|
|
map_entity_type,
|
|
protects,
|
|
same_mention,
|
|
types_equivalent,
|
|
)
|
|
|
|
|
|
def expect(ok: bool, message: str) -> None:
|
|
if not ok:
|
|
raise SystemExit(f"FAIL: {message}")
|
|
print(f"OK {message}")
|
|
|
|
|
|
def main() -> None:
|
|
expect(canonicalize_detect_mode("learning") == "learning_full", "learning aliases to learning_full")
|
|
expect(canonicalize_detect_mode("semantic") == "bypass", "semantic aliases to bypass")
|
|
expect(canonicalize_detect_mode("wordlist") is None, "unknown mode is rejected")
|
|
expect(map_entity_type("person") == "PERSON", "person maps to PERSON")
|
|
expect(map_entity_type("place") == "PLACE", "place maps to PLACE")
|
|
expect(protects("PERSON") and not protects("FOOD"), "PERSON protects, FOOD does not")
|
|
expect(types_equivalent("PERSON", "PLACE"), "similar protectable types are equivalent")
|
|
expect(not types_equivalent("FOOD", "PERSON"), "FOOD versus PERSON is never equivalent")
|
|
inner = MentionSpan("Sushi", 20, 25, "PERSON", "identity", "cue", True, "frau")
|
|
outer = MentionSpan("Frau Sushi", 15, 25, "PERSON", "identity", "teacher", False, "frau")
|
|
expect(same_mention(inner, outer), "contained span is the same mention")
|
|
food = MentionSpan("Sushi", 20, 25, "FOOD", "not_identity", "teacher", False, "aß")
|
|
expect(same_mention(inner, food), "same offsets stay one mention even when types differ")
|
|
compact = compact_diff_counters({"diff_agree": 2, "diff_type_conflict": 1, "label": "Sushi"})
|
|
expect(compact["diff_agree"] == 2 and compact["diff_type_conflict"] == 1, "known counters are kept")
|
|
expect("label" not in compact and "Sushi" not in str(compact), "compact counters drop plaintext")
|
|
with patch("detect_learning.get_cue_decision", return_value=None), patch(
|
|
"detect_learning.get_sense",
|
|
return_value={"identity_hits": 3, "non_identity_hits": 0, "ambiguous": False},
|
|
), patch("detect_learning.cue_for_mention", return_value="aß"):
|
|
expect(
|
|
decision_for_mention("p", "Sushi", "Ich aß Sushi.", 7, 12) == "ask",
|
|
"Tag-21: identity-only sense does not lock a new cue",
|
|
)
|
|
with patch("detect_learning.get_cue_decision", return_value="identity"), patch(
|
|
"detect_learning.cue_for_mention", return_value="frau"
|
|
):
|
|
expect(
|
|
decision_for_mention("p", "Sushi", "meine Frau Sushi", 11, 16) == "identity",
|
|
"stored cue remains binding",
|
|
)
|
|
print("detect pipeline unit ok")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|