Files
ROMFASTSQL/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/tests/test_triaj.py
Claude Agent 47110d71bf fix(maria): mesajul si captura sunt acelasi lucru, iar cosinusul singur nu mai e dovada
Doua greseli din grupul "Maria Test", 2026-09-01, pe care 27409fb le-a atenuat dar
nu le-a rezolvat.

1. La o reclamatie vaga Maria cauta, in loc sa intrebe. "Buna . Am si eu o factura
   pe luna august cu eroare in spv la trimitere AUTO SULE" nu spune CE eroare e —
   n-are ce cauta in documente si n-are ce trimite la suport, fiindca programatorul
   ar pune exact aceeasi intrebare. Gardul (triaj.prea_vag) exista, dar cerea text
   sub 12 cuvinte; mesajul are 14. Gresea si invers: "nu pot incarca factura in SPV,
   imi da eroare de certificat" are 9 cuvinte si ARE raspuns in documente, si era
   oprita degeaba. Lungimea nu masoara cat de precis e mesajul.
   -> gardul nu mai numara cuvinte si se aplica DUPA cautare, doar cand nu exista
      acoperire. O singura data pe fir: daca nici intrebat omul nu spune mai mult,
      mesajul pleaca la suport.

2. Textul si captura, trimise una dupa alta, erau tratate ca doua probleme fara
   legatura. `este_continuare` rupea firul la ORICE imagine — dar cazul frecvent e
   tocmai omul care scrie problema si trimite captura imediat dupa, sau care
   raspunde la "trimite-mi o captura". Textul se pierdea, captura se cauta doar pe
   OCR-ul ei, se deschideau doua fire si se puteau deschide doua escaladari pentru
   aceeasi problema.
   -> o captura in primele FIR_IMAGINE_MIN (5) minute continua firul: textul citit
      din ea intra in ancora (cu tot cu coduri), cautarea se face pe mesaj +
      captura, iar pe o escaladare deschisa pleaca la aceeasi referinta — cu
      imaginea, nu doar cu textul citit din ea (_notifica_suport ia si media).

Si, ca urmare a lui (2): RANK_STRONG_COSINE dispare. Interogarea pe un fir e ancora
plus mesajul nou, deci cosinusul urca la fiecare replica fara sa apara vreo dovada
noua — aceeasi captura da 0,778 singura si 0,836 cu mesajul de dinainte, adica peste
0,80 pus ieri. Orice prag fix de sus e trecut de o discutie destul de lunga. Acoperirea
ramane pe dovada lexicala (termenii distinctivi ai intrebarii chiar in chunk-uri),
care e stabila la lungime. Niciun caz cu raspuns in documente nu avea nevoie de
scurtatura.

ops/calibrate-rank.py: 26/26, cu interogarea combinata adaugata la set. Teste: 102 pass.
Verificat end-to-end pe scenariul real (mesaj, apoi captura la 10 secunde): intrebare
de detalii, apoi o singura escaladare care poarta si textul si captura.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-09-01 19:44:24 +00:00

177 lines
6.8 KiB
Python

"""Triajul: cand se cer detalii, cand pleaca automat la programatori."""
from __future__ import annotations
import json
import time
import triaj
CHUNK_06550 = """[eroare ORA 06550]
keywords: ORA-06550 PLS-00201 eroare compilare
mesaj eroare: ORA-06550: eroare de compilare in program
ce inseamna: Programul nu e adus la zi peste tot.
rezolvare: Nu se rezolva din aplicatie si nu ajuta sa incerci din nou.
cand suni suportul: Intotdeauna. Trimite si o captura de ecran."""
CHUNK_04068 = """[eroare ORA 04068]
mesaj eroare: ORA-04068: existing state of packages has been discarded
ce inseamna: Programul a fost actualizat in timp ce aplicatia ta era deschisa.
rezolvare: Inchide complet aplicatia si redeschide-o.
cand suni suportul: Daca revine si dupa restartul aplicatiei."""
def test_nivelul_se_citeste_din_dictionar():
assert triaj.nivel_suport(CHUNK_06550) == "intotdeauna"
assert triaj.nivel_suport(CHUNK_04068) is None
assert triaj.nivel_suport("cand suni suportul: Imediat. Blocheaza tot.") == "imediat"
def test_raspunsul_nu_vorbeste_ca_pentru_programatori():
"""Cazul din 2026-09-01: modelul ii cerea unui contabil sa verifice schema."""
text = triaj.raspuns_escaladat(CHUNK_06550, "intotdeauna", "M-260901-A1B2",
True, cu_imagine=True)
jos = text.lower()
for cuvant in ("procedura", "schema", "pl/sql", "obiect", "variabila", "compilare"):
assert cuvant not in jos, cuvant
assert "M-260901-A1B2" in text
assert "captura" in jos
assert "blocheaza complet" in jos # intreaba cat e de urgent
def test_cand_notificarea_nu_a_plecat_nu_promitem_ca_a_plecat():
text = triaj.raspuns_escaladat(CHUNK_06550, "intotdeauna", "M-1", False)
assert "am inregistrat" in text.lower()
assert "te contacteaza cineva" not in text.lower()
def test_prea_vag_doar_pentru_acuzatii_fara_continut():
assert triaj.prea_vag("am o eroare", False)
assert triaj.prea_vag("nu merge nimic", False)
# cazul real din 2026-09-01: lung, dar tot nu spune CE eroare e. Plafonul de
# 12 cuvinte il lasa sa treaca, si a primit un raspuns inventat.
assert triaj.prea_vag(
"Buna . Am si eu o factura pe luna august cu eroare in spv la trimitere AUTO SULE",
False)
# o intrebare adevarata nu se opreste ca sa i se ceara detalii
assert not triaj.prea_vag("cum trimit declaratia D406 SAF-T", False)
# un cod inseamna ca avem deja dupa ce cauta
assert not triaj.prea_vag("imi da ORA-01722 la salvare", False)
# captura vorbeste de la sine
assert not triaj.prea_vag("am o eroare", True)
# --- completarea la o escaladare deschisa -----------------------------------
def _escaladare(ref: str) -> None:
import config
d = config.STATE_DIR / "escalations"
d.mkdir(parents=True, exist_ok=True)
(d / f"{int(time.time())}-{ref}.json").write_text(
json.dumps({"ref": ref, "text": "eroare initiala"}), encoding="utf-8")
def _fara_suport(monkeypatch):
import config
monkeypatch.setattr(config, "get", lambda k, d=None: "" if k == "SUPPORT_JID"
else config.DEFAULTS.get(k, d))
def test_completarea_intra_in_escaladarea_deschisa(monkeypatch):
import config
import consumer
_fara_suport(monkeypatch)
_escaladare("M-TEST-1")
raspuns = consumer.completeaza("M-TEST-1", {"id": "x"}, "eram la salvarea unei facturi")
assert "M-TEST-1" in raspuns
fisier = next((config.STATE_DIR / "escalations").glob("*-M-TEST-1.json"))
record = json.loads(fisier.read_text(encoding="utf-8"))
assert record["completari"][0]["text"] == "eram la salvarea unei facturi"
assert record["completari"][0]["fel"] == "detaliu"
def test_completarile_se_aduna_si_nu_repeta_acelasi_text(monkeypatch):
"""Trei confirmari identice una dupa alta suna a robot."""
import config
import consumer
_fara_suport(monkeypatch)
_escaladare("M-TEST-3")
raspunsuri = [consumer.completeaza("M-TEST-3", {"id": str(i)}, t) for i, t in enumerate(
["eram la facturi", "si la rapoarte", "si la import"])]
assert len(set(raspunsuri)) == 3
fisier = next((config.STATE_DIR / "escalations").glob("*-M-TEST-3.json"))
record = json.loads(fisier.read_text(encoding="utf-8"))
assert len(record["completari"]) == 3
def test_urgenta_marcheaza_escaladarea_si_anunta(monkeypatch):
import config
import consumer
trimise = []
monkeypatch.setattr(consumer, "_notifica_suport",
lambda text: trimise.append(text) or True)
_escaladare("M-TEST-U")
raspuns = consumer.completeaza("M-TEST-U", {"id": "u"}, "Este foarte urgent, ma blocheaza")
record = json.loads(next((config.STATE_DIR / "escalations").glob("*-M-TEST-U.json"))
.read_text(encoding="utf-8"))
assert record["urgenta"] is True
assert record["completari"][0]["fel"] == "urgenta"
assert trimise and trimise[0].startswith("[Maria] URGENT")
assert "urgenta" in raspuns.lower()
def test_intrebarea_de_stare_primeste_un_raspuns_nu_o_confirmare(monkeypatch):
"""„Nu m-a contactat nimeni. Cat mai dureaza?" primea „am adaugat si asta"."""
import config
import consumer
trimise = []
monkeypatch.setattr(consumer, "_notifica_suport",
lambda text: trimise.append(text) or True)
_escaladare("M-TEST-S")
raspuns = consumer.completeaza("M-TEST-S", {"id": "s"},
"Nu m-a contactat nimeni. Cat mai dureaza?")
assert "am adaugat" not in raspuns.lower()
assert "M-TEST-S" in raspuns
assert trimise and "intreaba" in trimise[0]
# a doua intrebare la un minut distanta nu mai suna telefonul echipei
trimise.clear()
consumer.completeaza("M-TEST-S", {"id": "s2"}, "tot nimic")
assert not trimise
def test_dupa_o_jumatate_de_ora_maria_o_spune_pe_fata():
import triaj
text = triaj.raspuns_stare("M-1", 42, False, telefon="0241 000 000")
assert "42" in text and "0241 000 000" in text
assert "nu mi-a raspuns nimeni" in text
def test_fel_completare():
import triaj
assert triaj.fel_completare("Este foarte urgent") == "urgenta"
assert triaj.fel_completare("ma blocheaza complet") == "urgenta"
assert triaj.fel_completare("Cat mai dureaza?") == "stare"
assert triaj.fel_completare("Tot nimic") == "stare"
assert triaj.fel_completare("eram la salvarea unei facturi") == "detaliu"
# ordinea conteaza: urgenta bate intrebarea de stare
assert triaj.fel_completare("urgent, cat mai dureaza?") == "urgenta"
def test_a_doua_intrebare_de_stare_nu_repeta_acelasi_text():
import triaj
intai = triaj.raspuns_stare("M-1", 10, False, a_cata=0)
apoi = triaj.raspuns_stare("M-1", 11, False, a_cata=1)
assert intai != apoi
# „acum 10 minute", nu „acum 10 de minute"
assert "10 minute" in intai and "10 de minute" not in intai
assert triaj.minute_text(25) == "25 de minute"