Spaces:
Running on Zero
Running on Zero
| import importlib.util | |
| from pathlib import Path | |
| from jawbreaker.analyzers import load_json_prediction, validate_prediction | |
| def load_run_eval_module(): | |
| spec = importlib.util.spec_from_file_location("run_eval", Path("eval/run_eval.py")) | |
| assert spec is not None | |
| assert spec.loader is not None | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| return module | |
| def test_validate_prediction_accepts_complete_prediction() -> None: | |
| prediction = { | |
| "risk_level": "dangerous", | |
| "scam_type": "credential_theft", | |
| "summary": "This is pretending to be a bank.", | |
| "tactics": ["fake authority", "credential request"], | |
| "safest_action": "Do not click links. Open the official app directly.", | |
| "trusted_person_message": "Can you check this for me?", | |
| "scam_dna": { | |
| "impersonates": "bank", | |
| "pressure": "account locked", | |
| "ask": "login", | |
| "risk": "credential theft", | |
| }, | |
| } | |
| assert validate_prediction(prediction) == [] | |
| def test_score_rows_tracks_dangerous_as_safe() -> None: | |
| run_eval = load_run_eval_module() | |
| rows = [ | |
| { | |
| "id": "case_1", | |
| "category": "bank_phishing", | |
| "input": "Bank login now", | |
| "expected_risk_level": "dangerous", | |
| "expected_scam_type": "credential_theft", | |
| "expected_tactics": ["credential request"], | |
| } | |
| ] | |
| predictions = { | |
| "case_1": { | |
| "risk_level": "safe", | |
| "scam_type": "none", | |
| "summary": "Looks fine.", | |
| "tactics": [], | |
| "safest_action": "No action needed.", | |
| "trusted_person_message": "Can you check this?", | |
| "scam_dna": {"impersonates": "", "pressure": "", "ask": "", "risk": ""}, | |
| } | |
| } | |
| metrics = run_eval.score_rows(rows, predictions, elapsed=0.01) | |
| assert metrics["risk_level_accuracy"] == 0 | |
| assert metrics["dangerous_as_safe"] == ["case_1"] | |
| assert metrics["dangerous_as_needs_check"] == [] | |
| assert metrics["suspicious_as_safe"] == [] | |
| def test_score_rows_tracks_dangerous_undercalls_and_suspicious_as_safe() -> None: | |
| run_eval = load_run_eval_module() | |
| rows = [ | |
| { | |
| "id": "danger_case", | |
| "category": "family_impersonation", | |
| "input": "Grandpa, I need money before midnight.", | |
| "expected_risk_level": "dangerous", | |
| "expected_scam_type": "family_impersonation", | |
| "expected_tactics": ["payment pressure"], | |
| }, | |
| { | |
| "id": "suspicious_case", | |
| "category": "suspicious", | |
| "input": "Open this marketplace escrow link.", | |
| "expected_risk_level": "suspicious", | |
| "expected_scam_type": "fake_escrow", | |
| "expected_tactics": ["suspicious link"], | |
| }, | |
| ] | |
| base_prediction = { | |
| "scam_type": "unknown", | |
| "summary": "Check this.", | |
| "tactics": [], | |
| "safest_action": "Verify through a trusted route.", | |
| "trusted_person_message": "Can you check this?", | |
| "scam_dna": {"impersonates": "", "pressure": "", "ask": "", "risk": ""}, | |
| } | |
| predictions = { | |
| "danger_case": {**base_prediction, "risk_level": "needs_check"}, | |
| "suspicious_case": {**base_prediction, "risk_level": "safe"}, | |
| } | |
| metrics = run_eval.score_rows(rows, predictions, elapsed=0.01) | |
| assert metrics["dangerous_as_needs_check"] == ["danger_case"] | |
| assert metrics["suspicious_as_safe"] == ["suspicious_case"] | |
| def test_score_rows_tracks_model_errors() -> None: | |
| run_eval = load_run_eval_module() | |
| rows = [ | |
| { | |
| "id": "case_1", | |
| "category": "safe_benign", | |
| "input": "Dentist appointment Tuesday.", | |
| "expected_risk_level": "safe", | |
| "expected_scam_type": "none", | |
| "expected_tactics": [], | |
| } | |
| ] | |
| predictions = { | |
| "case_1": { | |
| "risk_level": "safe", | |
| "scam_type": "none", | |
| "summary": "Looks fine.", | |
| "tactics": [], | |
| "safest_action": "No action needed.", | |
| "trusted_person_message": "Can you check this?", | |
| "scam_dna": {"impersonates": "", "pressure": "", "ask": "", "risk": ""}, | |
| "_jawbreaker_model_error": "JSONDecodeError('empty')", | |
| } | |
| } | |
| metrics = run_eval.score_rows(rows, predictions, elapsed=0.01) | |
| assert metrics["model_errors"] == [{"id": "case_1", "error": "JSONDecodeError('empty')"}] | |
| def test_has_unsafe_action_allows_do_not_send_money() -> None: | |
| run_eval = load_run_eval_module() | |
| assert not run_eval.has_unsafe_action("Do not send money. Call a known number.") | |
| assert run_eval.has_unsafe_action("Send money to verify the account.") | |
| def test_load_json_prediction_extracts_embedded_object() -> None: | |
| prediction = load_json_prediction( | |
| 'Here is the result: {"risk_level": "safe", "scam_type": "none", ' | |
| '"summary": "ok", "tactics": [], "safest_action": "No action.", ' | |
| '"trusted_person_message": "Please check.", ' | |
| '"scam_dna": {"impersonates": "", "pressure": "", "ask": "", "risk": ""}}' | |
| ) | |
| assert prediction["risk_level"] == "safe" | |
| def test_load_json_prediction_ignores_qwen_thinking_tokens() -> None: | |
| prediction = load_json_prediction( | |
| '<think>I should reason internally.</think>{"risk_level": "dangerous", ' | |
| '"scam_type": "family_impersonation", "summary": "scam", ' | |
| '"tactics": ["secrecy"], "safest_action": "Do not reply.", ' | |
| '"trusted_person_message": "Can you check this?", ' | |
| '"scam_dna": {"impersonates": "family", "pressure": "secret", "ask": "money", "risk": "payment"}}' | |
| ) | |
| assert prediction["risk_level"] == "dangerous" | |