Spaces:
Sleeping
Sleeping
File size: 5,451 Bytes
d402333 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | """
Regression tests β one test per failure in the feedback.
Runs with the RULES backend (no model download) so it's fast and CI-able.
The LLM backends only improve on these results.
"""
import sys
import logging
logging.basicConfig(level=logging.WARNING)
from nlu import NLU
from orchestrator import Orchestrator, fmt_amount
PASS, FAIL = "\033[92mPASS\033[0m", "\033[91mFAIL\033[0m"
results = []
def check(name: str, cond: bool, detail: str = ""):
results.append((name, cond))
print(f" [{PASS if cond else FAIL}] {name}" + (f" β {detail}" if detail else ""))
def new_orch():
return Orchestrator(nlu=NLU(prefer="rules"))
def turn(orch, session, text):
resp, session, esc = orch.respond(text, text, session)
return resp, session, esc
print("\nββ P1: 'send money to abu now' must NOT re-ask for recipient ββ")
orch = new_orch()
s = orch.new_session()
resp, s, _ = turn(orch, s, "I want to send money to abu now")
check("recipient 'abu' prefilled (no 'who do you want to send to')",
"who" not in resp.lower() and "abu" not in resp.lower().replace("abu", "") or
("recipient" not in resp.lower() and "who would you like to send" not in resp.lower()),
f"agent said: {resp[:90]}")
check("asks for the AMOUNT instead (the actually-missing slot)",
"how much" in resp.lower(), f"agent said: {resp[:90]}")
print("\nββ P0: compound 'check my balance and also send 350000 to amina' ββ")
orch = new_orch()
s = orch.new_session()
resp, s, _ = turn(orch, s, "check my balance and also send 350000 to amina")
check("acknowledges BOTH tasks", "one at a time" in resp.lower() or
("balance" in resp.lower() and ("transfer" in resp.lower() or "amina" in resp.lower())),
f"agent said: {resp[:120]}")
# give account number β balance executes, transfer flow continues
resp, s, _ = turn(orch, s, "1234567890")
check("balance is delivered", "balance is" in resp.lower(), f"{resp[:90]}")
check("transfer to amina NOT dropped (asks to confirm or continues it)",
"amina" in resp.lower() or "send" in resp.lower() or "confirm" in resp.lower()
or "yes" in resp.lower(),
f"{resp[:140]}")
print("\nββ P0: 'where is your closest branch so I can get my ATM card' ββ")
orch = new_orch()
s = orch.new_session()
resp, s, _ = turn(orch, s, "where is your closest branch so I can get my ATM card")
check("NOT classified as block_card (no 'block' confirmation)",
"block" not in resp.lower(), f"{resp[:120]}")
check("gives branch info", "branch" in resp.lower() or "open" in resp.lower(),
f"{resp[:120]}")
print("\nββ P1: return reason 'too small' must not dead-end ββ")
orch = new_orch()
s = orch.new_session()
resp, s, _ = turn(orch, s, "I want to return my order")
resp, s, _ = turn(orch, s, "ORD-4521")
resp, s, _ = turn(orch, s, "too small")
check("'too small' accepted as return reason",
"too small" in resp.lower() or "return" in resp.lower(),
f"{resp[:120]}")
check("no 'did not understand' dead-end",
"didn't understand" not in resp.lower() and "did not understand" not in resp.lower(),
f"{resp[:90]}")
print("\nββ P1: unknown input β clarify once, then human WITH context ββ")
orch = new_orch()
s = orch.new_session()
resp, s, esc = turn(orch, s, "florble the wumbus")
check("first unknown β clarifying question, not menu dump",
"are you asking" in resp.lower() or "balance" in resp.lower(), f"{resp[:110]}")
check("not escalated yet", not esc)
resp, s, esc = turn(orch, s, "zorp zorp quux")
check("second unknown β human handoff", esc, f"{resp[:110]}")
check("handoff mentions context is preserved",
"won't have to repeat" in resp.lower() or "conversation" in resp.lower(),
f"{resp[:140]}")
print("\nββ P2: balance guard β 350,000 against smaller balance ββ")
orch = new_orch()
s = orch.new_session()
resp, s, _ = turn(orch, s, "check my balance")
resp, s, _ = turn(orch, s, "1111111") # deterministic mock balance
bal = s.balance
too_much = bal + 100_000
resp, s, _ = turn(orch, s, f"send {too_much} to musa")
check("over-balance transfer is refused, not confirmed",
"can't process" in resp.lower() or "balance is" in resp.lower(),
f"balance={fmt_amount(bal)}, asked={fmt_amount(too_much)} β {resp[:130]}")
print("\nββ P2: consistent β¦ formatting ββ")
check("fmt_amount('350000') == 'β¦350,000'", fmt_amount("350000") == "β¦350,000")
check("fmt_amount('134200') == 'β¦134,200'", fmt_amount("134200") == "β¦134,200")
check("fmt_amount(245000) == 'β¦245,000'", fmt_amount(245000) == "β¦245,000")
print("\nββ Destructive confirmation gate ββ")
orch = new_orch()
s = orch.new_session()
resp, s, _ = turn(orch, s, "send 5000 to fatima")
resp, s, _ = turn(orch, s, "1234567890") # account slot
check("transfer requires explicit yes/no before executing",
"yes" in resp.lower() and "confirm" in resp.lower(), f"{resp[:130]}")
resp, s, _ = turn(orch, s, "no")
check("'no' cancels cleanly", "cancelled" in resp.lower(), f"{resp[:90]}")
# ββ Summary ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
passed = sum(1 for _, ok in results if ok)
total = len(results)
print(f"\n{'='*60}\n {passed}/{total} checks passed\n{'='*60}")
sys.exit(0 if passed == total else 1)
|