Files
ROMFASTSQL/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/tests/test_fir.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

113 lines
4.1 KiB
Python

"""Firul de discutie: continuare vs subiect nou, ancora, tacerea la preluare."""
from __future__ import annotations
import time
import fir
JID = "40712345678@s.whatsapp.net"
EROARE = ("[Oracle][ODBC][Ora]ORA-06550: line 1, column 7\n"
"PLS-00906: object VADECO PACK PARTENERI is invalid")
def test_firul_retine_codurile_ancorei():
f = fir.deschide(JID, EROARE)
assert "ora-06550" in f["coduri"] and "pls-00906" in f["coduri"]
def test_un_raspuns_scurt_e_continuare_nu_eroare_noua():
"""Cazul masurat: „da, ma blocheaza" cautat singur nimerea ordinul de plata."""
f = fir.deschide(JID, EROARE)
assert fir.este_continuare(f, "da, ma blocheaza complet", False)
assert fir.este_continuare(f, "eram la salvarea unei facturi", False)
# ancora intra in cautare, ca sa nu se caute patru cuvinte fara context
q = fir.interogare(f, "da, ma blocheaza complet")
assert "ORA-06550" in q and "ma blocheaza" in q
def test_alt_cod_rupe_firul():
f = fir.deschide(JID, EROARE)
assert not fir.este_continuare(f, "imi da acum ORA-12154", False)
# acelasi cod nu rupe firul
assert fir.este_continuare(f, "tot ORA-06550 imi da", False)
def test_captura_imediat_dupa_mesaj_continua_firul():
"""Cazul real: omul scrie problema si trimite captura la cateva secunde."""
f = fir.deschide(JID, "am o factura pe august cu eroare la trimitere")
assert fir.este_continuare(f, "", True)
def test_captura_mult_mai_tarziu_deschide_alt_fir():
f = fir.deschide(JID, EROARE)
f["at"] = time.time() - fir.imagine_fereastra_s() - 1
assert not fir.este_continuare(f, "", True)
def test_captura_pe_fir_aduce_textul_erorii_in_ancora():
"""Ancora era reclamatia vaga; captura e cea care spune ce eroare e."""
f = fir.deschide(JID, "am o factura cu eroare la trimitere")
fir.extinde_ancora(f, "ORA-06550: line 1, column 7")
assert "ora-06550" in f["coduri"]
assert "eroare la trimitere" in f["ancora"] and "ORA-06550" in f["ancora"]
# continuarile de dupa captura se cauta cu tot cu eroare, nu doar cu reclamatia
assert "ORA-06550" in fir.interogare(f, "da, ma blocheaza")
def test_fara_fir_nu_exista_continuare():
assert not fir.este_continuare(None, "da", False)
def test_firul_expira(monkeypatch):
f = fir.deschide(JID, EROARE)
fir.salveaza(f)
assert fir.incarca(JID) is not None
tarziu = time.time() + fir.ttl_s() + 1
monkeypatch.setattr(fir.time, "time", lambda: tarziu)
assert fir.incarca(JID) is None
def test_istoricul_se_opreste_la_max_schimburi():
f = fir.deschide(JID, EROARE)
for i in range(fir.MAX_SCHIMBURI + 4):
fir.adauga(f, "client", f"mesaj {i}")
assert len(f["schimburi"]) == fir.MAX_SCHIMBURI
assert f["schimburi"][-1]["text"] == f"mesaj {fir.MAX_SCHIMBURI + 3}"
assert fir.istoric(f)[-1]["role"] == "user"
def test_comenzile_de_oprire_si_revenire():
assert fir.comanda("Maria, stop") == "stop"
assert fir.comanda("preiau eu") == "stop"
assert fir.comanda("Maria, continua") == "start"
assert fir.comanda("cum trimit D406") is None
def test_tacerea_expira(monkeypatch):
f = fir.deschide(JID, EROARE)
fir.taci(f)
assert fir.tace(f)
tarziu = time.time() + fir.tacere_s() + 1
monkeypatch.setattr(fir.time, "time", lambda: tarziu)
assert not fir.tace(f)
def test_preluarea_doar_in_grupuri_cu_mai_multi_oameni(monkeypatch):
"""In self-chat si in grupul de test tot ce se scrie e fromMe."""
import consumer
monkeypatch.setattr(consumer, "grup_info",
lambda jid: {"participants": 1 if "test" in jid else 5})
assert not consumer.preluare_de_om({"fromMe": True, "isGroup": False, "from": JID})
assert not consumer.preluare_de_om(
{"fromMe": True, "isGroup": True, "from": "test@g.us", "text": "salut"})
assert consumer.preluare_de_om(
{"fromMe": True, "isGroup": True, "from": "client@g.us", "text": "preiau eu cazul"})
# propriul raspuns al Mariei nu e „un om care a preluat"
assert not consumer.preluare_de_om(
{"fromMe": True, "isGroup": True, "from": "client@g.us",
"text": consumer.REPLY_PREFIX + "buna"})