116 lines
6.7 KiB
Python
116 lines
6.7 KiB
Python
"""ZP Wiki + CRZP in one answer: order, labels, clarification, follow-ups, CRZP timeout."""
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from tools.answers import combined as C
|
|
|
|
WIKI = {"status": "ok", "reason": None, "accepted_for_delivery": True, "claims": [], "index_version": {"version": "v"},
|
|
"complete": True, "truncated": False, "truncation_reasons": [],
|
|
"answer": "**Ján Pták — Diplomová práca 2027**\n\nVedúci práce: Daniel Hladek.\n\nZdroje: https://zp.test/jan",
|
|
"source_urls": ["https://zp.test/jan"],
|
|
"delivery_check": {"layer": "delivery", "code": "complete", "request_fingerprint": "x"}}
|
|
|
|
|
|
@pytest.fixture
|
|
def combo(monkeypatch):
|
|
monkeypatch.setenv("SEARCH_API_KEY", "combined-test-key")
|
|
monkeypatch.setenv("ANSWER_WITH_CRZP", "1")
|
|
state = {"part": None, "match": {"name": "Ján Pták", "student": True}}
|
|
monkeypatch.setattr("tools.crzp.answering.wiki_bridge.wiki_match", lambda q, db_file=None: state["match"])
|
|
monkeypatch.setattr(C, "crzp_part", lambda q, person, complete=None, student=True: state["part"])
|
|
state["deliver"] = lambda q, **kw: dict(WIKI)
|
|
return state
|
|
|
|
|
|
def ask(combo, question, token=None):
|
|
return C.answer(question, engine="rag", context_token=token, deliver=lambda q, **kw: combo["deliver"](q, **kw))
|
|
|
|
|
|
def test_wiki_first_then_the_marked_crzp_part(combo):
|
|
combo["part"] = {"mode": "person", "text": "- bakalárska práca 2025 „IoT“ — vedúci Pavol Galajda; oponent Daniel Hládek",
|
|
"rows": [], "urls": ["https://opac.test/1"], "people": [], "works": []}
|
|
result = ask(combo, "Kto vedie Jána Ptáka?")
|
|
text = result["answer"]
|
|
assert text.index("Vedúci práce: Daniel Hladek") < text.index("**V CRZP**") < text.index("Pavol Galajda")
|
|
assert result["source_urls"] == ["https://zp.test/jan", "https://opac.test/1"] and result["status"] == "ok"
|
|
import packages.claude.client as client
|
|
client.validate_metadata(result)
|
|
assert client.validated_delivery_check(result, "Kto vedie Jána Ptáka?", "rag")
|
|
|
|
|
|
def test_without_crzp_the_wiki_answer_is_unchanged_but_for_wording(combo):
|
|
result = ask(combo, "Kedy mal Ján Pták posledné stretnutie?")
|
|
assert "V CRZP" not in result["answer"] and result["answer"].startswith("**Ján Pták")
|
|
|
|
|
|
def test_a_work_only_crzp_has_is_asked_for_and_the_choice_continues(combo, monkeypatch):
|
|
combo["part"] = {"mode": "person", "text": "x", "rows": [], "urls": [], "people": [],
|
|
"works": [{"source": "crzp", "label": "bakalárska práca 2025 „IoT“", "type": "bachelor_thesis", "key": "k"}]}
|
|
monkeypatch.setattr(C, "wiki_works", lambda person: {"Diplomová práca 2027": {"type": "diploma_thesis", "topic": "Agent"}})
|
|
asked = ask(combo, "Aké technológie využíva Ján Pták vo svojej práci?")
|
|
assert asked["status"] == "ambiguous" and "1. Diplomová práca 2027 „Agent“ (ZP Wiki)" in asked["answer"]
|
|
assert "2. bakalárska práca 2025 „IoT“ (CRZP)" in asked["answer"]
|
|
seen = []
|
|
combo["deliver"] = lambda q, **kw: (seen.append(q), dict(WIKI))[1]
|
|
ask(combo, "1", asked["conversation_context"]["token"])
|
|
assert seen == ["Aké technológie využíva Ján Pták vo svojej diplomovej práci?"]
|
|
chosen = []
|
|
monkeypatch.setattr(C, "_answer_choice", lambda q, c, **kw: chosen.append(c) or dict(WIKI))
|
|
ask(combo, "tú bakalársku", asked["conversation_context"]["token"])
|
|
assert chosen[0]["source"] == "crzp"
|
|
|
|
|
|
def test_schools_of_the_people_of_the_previous_answer(combo):
|
|
combo["part"] = {"mode": "similar", "text": "- Maksym Zatirka — Technická univerzita v Košiciach: „X“", "rows": [],
|
|
"urls": [], "people": [("Eva Malá", "Slovenská technická univerzita v Bratislave")]}
|
|
first = ask(combo, "Kto pracoval na podobnej téme ako Ján Pták?")
|
|
combo["match"] = None
|
|
schools = ask(combo, "a z akých škôl boli tí študenti?", first["conversation_context"]["token"])
|
|
assert "- Ján Pták — Technická univerzita v Košiciach (KEMT FEI)" in schools["answer"]
|
|
assert "- Eva Malá — Slovenská technická univerzita v Bratislave" in schools["answer"]
|
|
|
|
|
|
def test_a_slow_crzp_does_not_hold_the_wiki_answer(combo, monkeypatch):
|
|
monkeypatch.setattr(C, "CRZP_SECONDS", 0.5)
|
|
monkeypatch.setattr(C, "crzp_part", lambda *a, **k: (time.sleep(4), None)[1])
|
|
started = time.monotonic()
|
|
result = ask(combo, "Kto vedie Jána Ptáka?")
|
|
assert result["status"] == "ok" and time.monotonic() - started < 2.0
|
|
|
|
|
|
def test_a_forged_combined_token_is_refused(combo):
|
|
first = ask(combo, "Kto vedie Jána Ptáka?")
|
|
token = first["conversation_context"]["token"]
|
|
assert ask(combo, "a z akých škôl?", token[:-3] + "abc")["reason"] == "invalid_context"
|
|
|
|
|
|
def test_crzp_people_and_keywords_link_to_the_wiki_graph_by_code():
|
|
from tools.crzp.storage.graph import link_pairs
|
|
rows = [{"school": "Technická univerzita v Košiciach", "authors": ["Pták, Ján"], "supervisors": ["Galajda, Pavol"],
|
|
"opponents": [], "keywords": ["Speech Recognition", "iot"]},
|
|
{"school": "Iná univerzita", "authors": ["Pták, Jan"], "supervisors": [], "opponents": [], "keywords": []}]
|
|
people, keywords = link_pairs(rows, ["Ján Pták", "Daniel Hladek"], ["rozpoznávanie reči", "Dialógový systém"])
|
|
assert people == [{"crzp": "Pták, Ján", "wiki": "Ján Pták"}] # only at TUKE, the namesake elsewhere is not linked
|
|
assert keywords == [{"key": "speech recognition", "wiki": "rozpoznávanie reči"}] # through the reviewed synonyms
|
|
|
|
|
|
def test_the_wiki_question_which_person_wins_over_crzp(combo):
|
|
combo["match"] = None
|
|
combo["part"] = {"mode": "explicit", "status": "ambiguous", "text": "Menu „Ján“ zodpovedá v CRZP…", "rows": [],
|
|
"urls": [], "people": []}
|
|
combo["deliver"] = lambda q, **kw: {**WIKI, "status": "ambiguous", "reason": "ambiguous_person",
|
|
"answer": "Ktorého Jána myslíš? Ján Holp, Ján Pták."}
|
|
result = ask(combo, "Na čom pracoval Ján?")
|
|
assert result["answer"] == "Ktorého Jána myslíš? Ján Holp, Ján Pták."
|
|
combo["deliver"] = lambda q, **kw: {**WIKI, "status": "ambiguous", "reason": "missing_subject",
|
|
"answer": "Uveď meno študenta."}
|
|
combo["part"] = {**combo["part"], "status": "ok", "text": "Školitelia prác …"}
|
|
assert "Školitelia prác" in ask(combo, "Kto viedol najviac prác o strojovom učení?")["answer"]
|
|
|
|
|
|
def test_an_unknown_person_goes_to_crzp_only_for_a_thesis_question():
|
|
assert C.THESIS.search(C.normalized("O čom je bakalárska práca Jána Nováka?"))
|
|
assert not C.THESIS.search(C.normalized("Na čom pracuje Peter Novák?"))
|
|
assert not C.ABOUT.search(C.normalized("Čomu sa venuje Jakub Schwarc?"))
|