| from __future__ import annotations |
|
|
| import argparse |
| import sys |
| from pathlib import Path |
|
|
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| sys.path.insert(0, str(ROOT)) |
|
|
| from training.generate_jawbreaker_data import ( |
| eval_row, |
| generated_cases, |
| sft_row, |
| write_jsonl, |
| ) |
| from training.generate_v2_data import hard_cases, hard_prediction |
| from training.generate_v3_data import boundary_cases, split_special_cases |
| from training.generate_v4_data import v4_cases |
| from training.generate_v5_data import calibration_cases as v5_calibration_cases |
|
|
|
|
| V6_CALIBRATION_CASES = [ |
| { |
| "category": "safe_scary_no_action_v6", |
| "risk": "safe", |
| "scam_type": "none", |
| "tactics": [], |
| "impersonates": "known sender", |
| "pressure": "no sensitive pressure", |
| "ask": "no sensitive ask", |
| "risk_text": "none identified", |
| "safe_action": ( |
| "If this came from someone you know and asks for nothing sensitive, it is probably safe. " |
| "Still avoid unexpected links." |
| ), |
| "messages": [ |
| "Clinic notice: lab results were posted to the usual patient portal. No urgent action is required.", |
| "School safety update: the drill is complete, students are safe, and the normal schedule has resumed.", |
| "Building notice: fire alarm testing is scheduled today. No evacuation or reply is needed.", |
| "Weather update: buses are delayed by fifteen minutes. Pickup is still at the normal stop.", |
| "Library notice: your requested book is ready at the front desk. No fee is due.", |
| "HR reminder: open enrollment closes Friday. Use the company portal if you want to make changes.", |
| "Court reminder: jury check-in begins Monday. Bring the summons you received in the mail.", |
| "Pharmacy update: your refill is ready at your usual store.", |
| "Dentist reminder: your appointment is tomorrow. Call the office number from your calendar if needed.", |
| "Neighborhood alert: roadwork starts Monday. Follow the posted detour signs.", |
| "Utility notice: meter reading happens this week. No appointment or payment is required.", |
| "Office security update: badge checks will happen at the front desk tomorrow.", |
| "School alert: soccer practice is cancelled because of weather. Students should go home as usual.", |
| "Clinic reminder: fasting is recommended before tomorrow's appointment. Use your normal portal for forms.", |
| "Library reminder: one book is due soon. Return it at the desk or renew through the usual account.", |
| "Apartment notice: package room hours changed this week. Ask the front desk if you need help.", |
| ], |
| }, |
| { |
| "category": "needs_check_official_route_v6", |
| "risk": "needs_check", |
| "scam_type": "possible_legitimate_alert", |
| "tactics": ["verification needed", "trusted route"], |
| "impersonates": "known service", |
| "pressure": "routine account or safety notice", |
| "ask": "verify through official app, official website, or known route", |
| "risk_text": "uncertain legitimacy", |
| "safe_action": "Verify directly through the official app, official website, or a known phone number.", |
| "messages": [ |
| "Bank security: a new device signed in. Open the official banking app yourself to review activity.", |
| "Credit union notice: contact information changed. Use the number on your card if this was not you.", |
| "Card fraud alert: did you attempt a $68.45 grocery purchase? Reply YES or NO, or call the number on your card.", |
| "Coinbase notice: your password was changed. Open the official app directly to check security.", |
| "PayPal notice: a new passkey was added. Visit paypal.com directly if this was not you.", |
| "Streaming service: your payment method needs attention. Open the official app or website yourself.", |
| "Delivery carrier: weather delayed your package. Track it only in the official carrier app.", |
| "Utility reminder: your statement is ready. Pay through your normal account or printed bill number.", |
| "Insurance portal: an explanation of benefits is ready. Review it in your usual member portal.", |
| "County tax office: your annual notice is available. Use the official county website from prior mail.", |
| "Airline notice: your flight time changed. Open the airline app directly to confirm.", |
| "Hospital portal: new results are available. Sign in through your usual patient portal.", |
| "School portal: report cards are available. Use the normal parent portal.", |
| "Phone carrier: SIM protection changed. Open the carrier app directly to review account settings.", |
| "Pharmacy account: order details were updated. Check through your normal pharmacy login.", |
| "Library account: a hold expires soon. Renew through the library website or front desk.", |
| ], |
| }, |
| { |
| "category": "suspicious_not_dangerous_marketplace_v6", |
| "risk": "suspicious", |
| "scam_type": "marketplace_scam", |
| "tactics": ["marketplace", "third party", "payment pressure"], |
| "impersonates": "buyer or seller", |
| "pressure": "unusual marketplace flow", |
| "ask": "leave the marketplace flow, use a third party, or handle unclear payment", |
| "risk_text": "overpayment or account theft", |
| "safe_action": "Pause before acting. Keep payment and messages inside the marketplace's official flow.", |
| "messages": [ |
| "I want the table but my cousin will pick it up. I can send extra money now.", |
| "Can you hold the chair if I mail a check and arrange movers later?", |
| "My assistant handles payment by email. Send your bank name so accounting can prepare it.", |
| "I am ready to buy but need your email to send the marketplace confirmation.", |
| "Please move to text because my marketplace account has a limit today.", |
| "The courier needs a refundable pickup fee. I can include it in the final amount.", |
| "I accidentally sent too much. Can you return the difference to the driver?", |
| "The buyer protection page says there is a refundable seller verification step.", |
| "I am out of town, but a delivery company can collect it if you coordinate with them.", |
| "Can you mark it sold before payment? My pickup person is nearby.", |
| "I use a third-party escrow service for all marketplace purchases.", |
| "Please confirm your email and phone so my payment department can send instructions.", |
| "I sent an account code to prove the listing is real. Please read it back before I pay.", |
| "I can pay today if you leave the marketplace chat and use this payment flow.", |
| "The shipping agent will contact you about a small insurance hold.", |
| "My company buyer will send a certified check and arrange pickup after it clears.", |
| ], |
| }, |
| { |
| "category": "suspicious_not_dangerous_link_v6", |
| "risk": "suspicious", |
| "scam_type": "unknown_link", |
| "tactics": ["suspicious link", "vague request"], |
| "impersonates": "unknown sender", |
| "pressure": "unclear account or document issue", |
| "ask": "open a link or review unclear issue", |
| "risk_text": "possible credential theft", |
| "safe_action": ( |
| "Do not use the link from the message. Open the official website or app yourself if you need to check." |
| ), |
| "messages": [ |
| "Your profile needs attention. Review details at http://profile-message-center.example.", |
| "We could not process your request. Open https://customer-resolution.example for more information.", |
| "A secure document is waiting for review at https://document-viewer-secure.example.", |
| "Your voicemail is available at http://voice-message-download.example.", |
| "Your account notice is ready. Visit http://account-message-center.example when you have time.", |
| "Please confirm your information through http://profile-update-center.example.", |
| "A shared file requires review: https://document-review-center.example.", |
| "Your delivery update is available here: http://package-info-review.example.", |
| "Your subscription message can be viewed at https://member-notice-center.example.", |
| "There is a pending form in your inbox: http://form-review-portal.example.", |
| "A billing message is available at https://billing-message-view.example.", |
| "Your support case was updated. See http://support-case-message.example.", |
| ], |
| }, |
| { |
| "category": "dangerous_anchor_v6", |
| "risk": "dangerous", |
| "scam_type": "credential_theft", |
| "tactics": ["fake authority", "credential request", "suspicious link", "urgency"], |
| "impersonates": "service provider", |
| "pressure": "account, payment, or delivery emergency", |
| "ask": "use a message link, message number, code, card, or login", |
| "risk_text": "credential or payment theft", |
| "safe_action": "Do not use links or phone numbers from the message. Open the official app or website yourself.", |
| "messages": [ |
| "Bank security: new device signed in. Enter your password at http://bank-device-review.example now.", |
| "Credit union: card locked. Call the number in this text and read your one-time code.", |
| "Card fraud center: suspicious purchase. Verify your full card number through this alert number.", |
| "Delivery center: package held for a $2.19 fee. Enter address and card at http://parcel-fee-release.example.", |
| "PayPal security: passkey added. Secure your account at https://paypal-case-review.example and enter the code.", |
| "Coinbase support: phone update requested. Contact the callback number in this text immediately.", |
| "Streaming billing: payment failed and your account closes tonight. Update at http://streaming-billing-review.example.", |
| "Utility shutoff warning: pay through this urgent portal today or service will end.", |
| "Hospital billing: results blocked until you upload ID and insurance number at this link.", |
| "School portal emergency form missing. Enter parent login and payment card at this link.", |
| "Airline refund desk: provide card details at this refund link to keep your ticket.", |
| "County tax final notice: pay the penalty fee through the link below before noon.", |
| "Remote support alert: call the number shown and read the desktop access code to fix your device.", |
| "Device protection: install the remote cleanup tool and pay the service fee before data loss.", |
| "Grandma, this is my new number. Send money today and please do not tell anyone.", |
| "TikTok Shop recruiter: earn $720 daily. Message us on WhatsApp and pay the training deposit.", |
| ], |
| }, |
| ] |
|
|
|
|
| def v6_calibration_cases(repeats: int) -> list[tuple[str, dict, dict]]: |
| cases = [] |
| for repeat in range(repeats): |
| for case in V6_CALIBRATION_CASES: |
| prediction = hard_prediction(case) |
| for index, message in enumerate(case["messages"]): |
| case_id = f"{case['category']}_{repeat:02d}_{index:02d}" |
| cases.append((case_id, {"message": message, "scenario": case}, prediction)) |
| return cases |
|
|
|
|
| def parse_args() -> argparse.Namespace: |
| parser = argparse.ArgumentParser(description="Generate Jawbreaker v6 final 1B calibration data.") |
| parser.add_argument("--base-train", type=int, default=680) |
| parser.add_argument("--base-dev", type=int, default=120) |
| parser.add_argument("--base-test", type=int, default=180) |
| parser.add_argument("--hard-repeats", type=int, default=4) |
| parser.add_argument("--boundary-repeats", type=int, default=5) |
| parser.add_argument("--v4-repeats", type=int, default=5) |
| parser.add_argument("--v5-repeats", type=int, default=5) |
| parser.add_argument("--v6-repeats", type=int, default=5) |
| parser.add_argument("--out-dir", type=Path, default=ROOT / "training" / "data") |
| parser.add_argument("--eval-out", type=Path, default=ROOT / "eval" / "hard_v6_eval.jsonl") |
| return parser.parse_args() |
|
|
|
|
| def main() -> None: |
| args = parse_args() |
| total = args.base_train + args.base_dev + args.base_test |
| base = list(generated_cases(total)) |
| v2_hard = hard_cases(args.hard_repeats) |
| boundary = boundary_cases(args.boundary_repeats) |
| v4 = v4_cases(args.v4_repeats) |
| v5 = v5_calibration_cases(args.v5_repeats) |
| v6 = v6_calibration_cases(args.v6_repeats) |
|
|
| v2_hard_train, v2_hard_dev = split_special_cases(v2_hard, dev_fraction=0.20) |
| boundary_train, boundary_dev = split_special_cases(boundary, dev_fraction=0.20) |
| v4_train, v4_dev = split_special_cases(v4, dev_fraction=0.20) |
| v5_train, v5_dev = split_special_cases(v5, dev_fraction=0.20) |
| v6_train, v6_dev = split_special_cases(v6, dev_fraction=0.20) |
|
|
| train_cases = base[: args.base_train] + v2_hard_train + boundary_train + v4_train + v5_train + v6_train |
| dev_cases = ( |
| base[args.base_train : args.base_train + args.base_dev] |
| + v2_hard_dev |
| + boundary_dev |
| + v4_dev |
| + v5_dev |
| + v6_dev |
| ) |
| test_cases = ( |
| base[args.base_train + args.base_dev :] |
| + v2_hard_dev |
| + boundary_dev |
| + v4_dev |
| + v5_dev |
| + v6_dev |
| ) |
|
|
| write_jsonl(args.out_dir / "train_v6.jsonl", (sft_row(*case) for case in train_cases)) |
| write_jsonl(args.out_dir / "dev_v6.jsonl", (sft_row(*case) for case in dev_cases)) |
| write_jsonl(args.out_dir / "test_v6.jsonl", (sft_row(*case) for case in test_cases)) |
| write_jsonl(args.eval_out, (eval_row(*case) for case in test_cases)) |
|
|
| print(f"wrote train_v6={len(train_cases)} dev_v6={len(dev_cases)} test_v6={len(test_cases)}") |
| print(f"v2_hard={len(v2_hard)} boundary={len(boundary)} v4={len(v4)} v5={len(v5)} v6={len(v6)}") |
| print(f"v6_train={len(v6_train)} v6_dev={len(v6_dev)}") |
| print(f"training data: {args.out_dir}") |
| print(f"hard eval: {args.eval_out}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|