meta-openenv / data_generators.py
Farbricated
v6.2
dfd4feb
Raw
History Blame Contribute Delete
43.2 kB
"""
data_generators.py β€” Deterministic procedural data generators for Meta v3.1
Replaces small hand-crafted datasets with richly varied, reproducible data.
Design principles
-----------------
* Every generator takes an `episode_id: str` and seeds Python's stdlib `random`
with `hash(episode_id + salt)` so the same episode always yields the same data.
* No third-party libs needed β€” stdlib only (random, string, datetime, hashlib).
* Each generator returns (agent_ctx, grader_ctx) tuples matching existing grader
contracts so graders need zero changes.
Drop-in replacement usage in Meta_environment.py::_load_context():
Replace the hardcoded-data blocks with:
from data_generators import gen_<task>(episode_id)
agent_ctx, grader_ctx = gen_<task>(episode_id)
Generators provided
-------------------
Email domain
gen_email_easy(episode_id) β€” 1 email, 3-way class, 30 sender/subject combos
gen_email_medium(episode_id) β€” 10 emails, varying priority pool
gen_email_hard(episode_id) β€” customer complaint, 8 scenario templates
Code domain
gen_code_easy(episode_id) β€” 6 code snippet variants
gen_code_medium(episode_id) β€” deterministic (same code, no variant needed)
gen_code_hard(episode_id) β€” deterministic (same code, no variant needed)
Data-cleaning domain
gen_data_easy(episode_id) β€” 6-10 rows, random nulls + one dup pair
gen_data_medium(episode_id) β€” 5 rows, randomised messy formats per field
gen_data_hard(episode_id) β€” 12-16 rows, IQR-detected outliers
Content moderation domain
gen_mod_easy(episode_id) β€” 7 posts drawn from larger pool
gen_mod_medium(episode_id) β€” 8 posts drawn from larger pool
(hard is already adversarial/static β€” left unchanged)
Ticket domain
gen_ticket_easy(episode_id) β€” 1 ticket from 8-variant pool
gen_ticket_medium(episode_id) β€” deterministic (same 8 tickets, no variant)
gen_ticket_hard(episode_id) β€” deterministic (same 6 incidents, no variant)
"""
from __future__ import annotations
import hashlib
import random
import string
from copy import deepcopy
from datetime import date, timedelta
from typing import Any
# ── Seeding helper ─────────────────────────────────────────────────────────────
def _rng(episode_id: str, salt: str = "") -> random.Random:
seed = int(hashlib.md5((episode_id + salt).encode()).hexdigest(), 16)
return random.Random(seed)
# ═══════════════════════════════════════════════════════════════════════════════
# EMAIL EASY β€” 30+ variant pool
# ═══════════════════════════════════════════════════════════════════════════════
_EMAIL_EASY_POOL = [
# (subject, sender, body, label)
("You won $1,000,000!!!", "promo@totally-legit-lottery.com",
"Click here to claim your prize now! Limited time offer!", "spam"),
("Q3 Budget Review β€” Urgent Action Required", "cfo@company.com",
"Please review the attached Q3 budget before our board meeting tomorrow at 9 AM.", "important"),
("Your weekly digest from Medium", "noreply@medium.com",
"Top stories this week: AI trends, Python tips, and startup news.", "newsletter"),
("CONGRATULATIONS! iPhone 15 Pro is yours!", "winner@prize-alert.net",
"You have been selected. Click now to claim your free iPhone!", "spam"),
("Action Required: Sign the NDA before Friday", "legal@company.com",
"Please sign the attached NDA before Friday's partnership meeting. Deadline is strict.", "important"),
("TechCrunch Newsletter β€” March 2026", "newsletter@techcrunch.com",
"This week's top stories in tech, AI, and startups.", "newsletter"),
("Claim your $500 Amazon gift card NOW", "deals@amazingoffers.biz",
"You are today's lucky winner. No purchase necessary. Act fast!", "spam"),
("Production incident P1 β€” please join bridge", "oncall@company.com",
"We have a P1 production incident. Please join the war room immediately.", "important"),
("Developer digest: April 2026", "digest@devweekly.io",
"This week: Rust 2.0 release, LLM benchmarks, and open-source highlights.", "newsletter"),
("FREE vacation cruise β€” you've been selected!", "winner@vacation-cruise.net",
"Claim your free cruise for two. Just pay shipping and handling.", "spam"),
("Board approval needed: Series B term sheet", "ceo@company.com",
"Our Series B term sheet requires your signature before the 5 PM deadline today.", "important"),
("Product Hunt daily digest", "digest@producthunt.com",
"Today's top products: AI code review, async stand-ups, no-code CRM.", "newsletter"),
("Congratulations β€” Survey reward $250!", "rewards@surveypanel.net",
"You qualify for a $250 reward. Complete our 2-minute survey to claim.", "spam"),
("HR: Performance review cycle starts Monday", "hr@company.com",
"The annual performance review cycle begins Monday. Please complete your self-assessment.", "important"),
("Hacker News digest: top 10 links this week", "weekly@hackernews.app",
"Rust, WASM, and the AI bubble: top discussions from the community.", "newsletter"),
("Double billing detected on account #AC-8821", "billing@ourplatform.com",
"We noticed a duplicate charge on your account. Reply to initiate a refund.", "spam"),
("Legal: Partnership agreement signature required EOD", "legal@company.com",
"The partnership agreement with Nexus Corp must be countersigned by 5 PM today.", "important"),
("SaaS Weekly β€” your industry roundup", "editor@saasweekly.com",
"Churn rates, AI copilots, and the future of vertical SaaS.", "newsletter"),
("You have unclaimed crypto rewards!", "support@cryptorewards.io",
"Wallet 0x4f…a2 has 0.3 ETH waiting. Connect now to claim.", "spam"),
("Security alert: admin password reset requested", "security@company.com",
"An admin-level password reset was requested from IP 198.51.100.42. Verify or block.", "important"),
("The Batch β€” AI newsletter by deeplearning.ai", "batch@deeplearning.ai",
"Andrew Ng on scaling laws, fine-tuning tips, and this week's papers.", "newsletter"),
("Limited offer: 90% off VPN subscription", "deals@vpndeals.biz",
"Today only: get lifetime VPN access for $1.99. Offer expires midnight.", "spam"),
("Vendor invoice #INV-7721 overdue by 14 days", "ar@vendor.com",
"Invoice #INV-7721 for $12,400 is now 14 days overdue. Please remit payment to avoid service interruption.", "important"),
("The Algorithm Newsletter β€” weekly edition", "hello@thealgorithm.co",
"ML papers, job listings, and a tutorial on RLHF.", "newsletter"),
("Your package was held β€” verify address", "delivery@postalnow.net",
"Your package is held due to an address discrepancy. Click to confirm.", "spam"),
("Compliance: GDPR audit documentation due Friday", "compliance@company.com",
"Please upload your GDPR data-processing records to the compliance portal by Friday.", "important"),
("Morning Brew β€” your daily business digest", "hello@morningbrew.com",
"Markets, tech, and the big stories you need to know today.", "newsletter"),
("Account suspended β€” click to restore", "support@accountalert.biz",
"Unusual activity detected. Your account has been temporarily suspended.", "spam"),
("CTO sync needed: infrastructure cost overrun", "cto@company.com",
"AWS bill came in 340% over budget. Need your sign-off on emergency cost controls.", "important"),
("The Hustle β€” daily newsletter", "news@thehustle.co",
"Today's biggest business stories, curated for founders and operators.", "newsletter"),
]
def gen_email_easy(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "email_easy")
email = dict(zip(["subject", "sender", "body", "label"],
rng.choice(_EMAIL_EASY_POOL)))
email["id"] = "e1"
agent_ctx = {"email": {k: v for k, v in email.items() if k != "label"}}
grader_ctx = {"email": email}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# EMAIL MEDIUM β€” varied priority pools
# ═══════════════════════════════════════════════════════════════════════════════
_CRITICAL_POOL = [
{"subject": "[ALERT] Production server DOWN", "sender": "ops@company.com",
"body": "All services unresponsive since 2 AM. Revenue impact: $10k/min.", "priority": 1, "category": "critical"},
{"subject": "[P1] Database primary down β€” all writes failing", "sender": "oncall@company.com",
"body": "The primary DB replica crashed. No write operations possible site-wide.", "priority": 1, "category": "critical"},
{"subject": "[WARNING] Security alert: unusual login attempt", "sender": "security@company.com",
"body": "Unauthorised login attempt from IP 45.33.32.156. Verify immediately.", "priority": 1, "category": "critical"},
{"subject": "[ALERT] DDoS attack in progress", "sender": "security@company.com",
"body": "We are under a DDoS attack. Site response time is 30s+. Join the war room.", "priority": 1, "category": "critical"},
]
_URGENT_POOL = [
{"subject": "Acme Corp contract renewal β€” URGENT", "sender": "sales@company.com",
"body": "Acme contract expires Friday. They need signature or they walk.", "priority": 2, "category": "business"},
{"subject": "Legal: Partnership agreement needs signature TODAY", "sender": "legal@company.com",
"body": "The partnership agreement must be countersigned by EOD or the deal falls through.", "priority": 2, "category": "legal"},
{"subject": "Series B term sheet β€” board sign-off required by 5 PM", "sender": "ceo@company.com",
"body": "VCs need the countersigned term sheet by 5 PM today or the round lapses.", "priority": 2, "category": "legal"},
]
_FINANCE_POOL = [
{"subject": "Invoice #INV-4521 overdue", "sender": "billing@vendor.com",
"body": "Invoice of $5,400 was due 3 days ago. Payment needed to avoid suspension.", "priority": 3, "category": "finance"},
{"subject": "AWS bill 340% over budget", "sender": "finance@company.com",
"body": "Last month's cloud bill was $82k vs $24k budget. Need approval to proceed.", "priority": 3, "category": "finance"},
{"subject": "Customer double-charged β€” refund dispute open", "sender": "billing@company.com",
"body": "Customer #C-1121 was charged twice. Finance blocked. Needs VP sign-off.", "priority": 3, "category": "finance"},
]
_BUSINESS_POOL = [
{"subject": "Q4 product launch deck β€” review needed", "sender": "marketing@company.com",
"body": "Please review launch slides before Thursday. Launch is next Monday.", "priority": 4, "category": "business"},
{"subject": "Quarterly board deck β€” slides need your input", "sender": "ea@company.com",
"body": "Board meeting is Tuesday. Your section on growth metrics needs updating.", "priority": 4, "category": "business"},
{"subject": "Key account at risk β€” needs executive call", "sender": "cs@company.com",
"body": "TechGiant Corp is threatening churn. They want an exec call this week.", "priority": 4, "category": "business"},
]
_SOCIAL_POOL = [
{"subject": "Team lunch this Friday?", "sender": "colleague@company.com",
"body": "Hey, thinking of doing a team lunch Friday. You in?", "priority": 9, "category": "social"},
{"subject": "Happy Work Anniversary!", "sender": "hr@company.com",
"body": "Celebrating your 3 years with us!", "priority": 9, "category": "social"},
{"subject": "Office trivia night β€” Thursday 7 PM", "sender": "social@company.com",
"body": "Join us for office trivia Thursday evening. Pizza provided!", "priority": 9, "category": "social"},
]
_NEWSLETTER_POOL = [
{"subject": "Weekly SaaS digest", "sender": "news@saasdigest.com",
"body": "Top SaaS stories this week...", "priority": 10, "category": "newsletter"},
{"subject": "Free AI productivity webinar", "sender": "promo@webinar.io",
"body": "Join our free webinar on AI tools this Thursday.", "priority": 10, "category": "newsletter"},
{"subject": "The Batch: AI news weekly", "sender": "batch@deeplearning.ai",
"body": "Andrew Ng on scaling, fine-tuning, and paper highlights.", "priority": 10, "category": "newsletter"},
]
def gen_email_medium(episode_id: str) -> tuple[dict, dict]:
"""
Always picks 2 critical, 1 urgent, 1 finance, 2 business, 2 social, 2 newsletter = 10 emails.
Correct ordering: criticals first, then urgent, finance, business, social, newsletter.
"""
rng = _rng(episode_id, "email_medium")
crits = rng.sample(_CRITICAL_POOL, k=min(2, len(_CRITICAL_POOL)))
urgents = rng.sample(_URGENT_POOL, k=min(1, len(_URGENT_POOL)))
fins = rng.sample(_FINANCE_POOL, k=min(1, len(_FINANCE_POOL)))
bizs = rng.sample(_BUSINESS_POOL, k=min(2, len(_BUSINESS_POOL)))
socials = rng.sample(_SOCIAL_POOL, k=min(2, len(_SOCIAL_POOL)))
news = rng.sample(_NEWSLETTER_POOL,k=min(2, len(_NEWSLETTER_POOL)))
ordered = crits + urgents + fins + bizs + socials + news
emails = []
correct_order = []
for i, e in enumerate(ordered):
eid = f"m{i+1}"
emails.append({"id": eid, **e})
correct_order.append(eid)
# Shuffle for presentation; keep correct_order as ground truth
display = emails[:]
rng.shuffle(display)
agent_ctx = {"emails": [{k: v for k, v in e.items() if k not in ("priority", "category")} for e in display]}
grader_ctx = {"emails": emails, "correct_order": correct_order}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# EMAIL HARD β€” 8 complaint scenario templates
# ═══════════════════════════════════════════════════════════════════════════════
_EMAIL_HARD_TEMPLATES = [
{
"subject": "Completely unacceptable β€” Order #{order_id}",
"sender": "furious.customer@email.com",
"body": (
"I have been a loyal customer for 5 years and I am absolutely furious. "
"My order #{order_id} arrived broken for the SECOND time this month. "
"Your support team has been dismissive and unhelpful every time I called. "
"I demand a full refund, a replacement sent overnight, AND compensation. "
"If this is not resolved within 24 hours, I will file a consumer court complaint."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "frustrat", "disappoint", "concern", "feel"],
"apologize": ["sorry", "apologize", "apology", "sincerely"],
"offer_refund": ["refund", "money back", "full refund"],
"offer_replacement": ["replacement", "replace", "new order", "send another", "overnight"],
"provide_timeline": ["24 hours", "48 hours", "within", "today", "tomorrow", "business day"],
"professional_tone": None,
"compensation_acknowledged": ["compensat", "inconvenience", "make it right", "goodwill"],
},
},
{
"subject": "Billing error β€” charged twice for subscription",
"sender": "billing.complaint@customer.com",
"body": (
"I was charged twice for my annual subscription this month β€” once on March 1st "
"and again on March 15th. I have the bank statements as proof. "
"This is a clear billing error on your end. I need an immediate refund of the "
"duplicate charge and a written confirmation that this won't happen again. "
"I've been waiting 2 weeks for a response from your support team."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "concern", "apologize", "sorry"],
"apologize": ["sorry", "apologize", "apology"],
"offer_refund": ["refund", "reimburse", "credit"],
"confirm_investigation": ["investigat", "look into", "check", "review", "verify"],
"provide_timeline": ["within", "hours", "days", "business day"],
"professional_tone": None,
"written_confirmation": ["confirm", "written", "email confirmation", "receipt"],
},
},
{
"subject": "Data breach β€” my account was hacked",
"sender": "angry.user@personal.com",
"body": (
"I just discovered that my account was used to make purchases I never authorised. "
"Someone accessed my account and changed my email and password. "
"I was only able to regain access because I remembered my security questions. "
"This is a serious security failure on your part. I demand to know what happened, "
"how my data was exposed, and what you will do to protect me going forward. "
"If I do not hear back within 24 hours, I will contact the data protection authority."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "serious", "concern", "alarming"],
"apologize": ["sorry", "apologize", "apology"],
"confirm_investigation": ["investigat", "security team", "look into", "review"],
"offer_remediation": ["secure", "reset", "protect", "steps", "safeguard"],
"provide_timeline": ["24 hours", "48 hours", "within", "shortly", "immediately"],
"professional_tone": None,
"regulatory_mention": ["authority", "regulat", "notify", "report", "transparent"],
},
},
{
"subject": "Defective product β€” 3 replacements still broken",
"sender": "frustrated.buyer@email.com",
"body": (
"This is my THIRD replacement unit and it is still defective. "
"The first unit arrived with a cracked screen. The second had a faulty battery. "
"This third unit powers off randomly. "
"I have spent over 6 hours on support calls. I want a full refund immediately β€” "
"no more replacements. I will also be leaving reviews on every platform I can find."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "frustrat", "disappoint", "three", "3"],
"apologize": ["sorry", "apologize", "sincerely"],
"offer_refund": ["refund", "full refund", "money back"],
"no_more_replacement_push": ["will not", "no further", "respect", "understand", "preference"],
"provide_timeline": ["within", "24 hours", "48 hours", "business day"],
"professional_tone": None,
"de_escalation": ["value", "loyal", "appreciate", "important to us"],
},
},
{
"subject": "Package lost β€” 3 weeks and still nothing",
"sender": "lost.package@customer.net",
"body": (
"I placed an order 3 weeks ago and my package has still not arrived. "
"The tracking has shown 'In Transit' for 18 days without any movement. "
"I have contacted your support team 4 times. Each time I am told to 'wait a few more days'. "
"I need this item for an event next week. Either find the package, send a replacement now, "
"or give me a full refund so I can purchase elsewhere. This is unacceptable."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "frustrat", "waiting", "3 weeks", "three weeks"],
"apologize": ["sorry", "apologize", "apology"],
"offer_refund": ["refund", "full refund"],
"offer_replacement": ["replacement", "resend", "new shipment", "send another"],
"provide_timeline": ["24 hours", "48 hours", "immediately", "today", "next day"],
"professional_tone": None,
"carrier_investigation": ["carrier", "investigate", "trace", "courier", "tracking"],
},
},
{
"subject": "Cancelled subscription still being charged",
"sender": "cancelled.user@email.com",
"body": (
"I cancelled my subscription on January 15th and have proof of the cancellation email. "
"However, I have been charged $99/month for the past 3 months since then. "
"Total unauthorised charges: $297. I want a full refund of all three charges immediately. "
"If this is not resolved within 48 hours, I will initiate a chargeback with my bank "
"and report this to the consumer protection agency."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "concern", "apologize", "recognised"],
"apologize": ["sorry", "apologize", "apology"],
"offer_refund": ["refund", "full refund", "$297", "three charges", "all charges"],
"confirm_cancellation": ["confirm", "cancellation", "verified", "record"],
"provide_timeline": ["48 hours", "24 hours", "immediately", "within"],
"professional_tone": None,
"prevent_recurrence": ["prevent", "ensure", "will not happen", "steps", "corrective"],
},
},
{
"subject": "Discriminatory treatment by your staff",
"sender": "discrimination.complaint@email.com",
"body": (
"I visited your store on April 2nd and was followed around by a staff member "
"while other customers were not treated this way. When I raised this with the manager, "
"I was told 'it's just store policy'. This felt discriminatory and I am deeply hurt. "
"I expect a formal apology, an explanation of your training policies, "
"and assurance that this will not happen to any customer again."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "serious", "concern", "upset", "hurt"],
"apologize": ["sorry", "apologize", "sincerely", "formal apology"],
"confirm_investigation": ["investigat", "review", "look into", "hr", "management"],
"policy_commitment": ["policy", "training", "standards", "commit", "improve"],
"provide_timeline": ["within", "48 hours", "days", "shortly"],
"professional_tone": None,
"empathy_specific": ["dignity", "respect", "every customer", "valued"],
},
},
{
"subject": "Software destroyed my data β€” urgent",
"sender": "data.loss@business.com",
"body": (
"Your software update on March 28th corrupted our entire database backup. "
"We lost 6 months of customer records. Our business has been at a standstill for 3 days. "
"We are a paying enterprise customer ($24,000/year). "
"I need immediate escalation to your engineering team, a post-mortem report, "
"and a discussion about compensation for our business losses."
),
"expected_elements": {
"acknowledge_frustration": ["understand", "serious", "impact", "significant", "recognise"],
"apologize": ["sorry", "sincerely", "deeply apologize"],
"escalate": ["engineering", "escalate", "senior", "team", "immediately"],
"post_mortem": ["post-mortem", "root cause", "analysis", "report", "findings"],
"provide_timeline": ["24 hours", "immediately", "today", "within"],
"professional_tone": None,
"compensation_acknowledged": ["compensat", "discuss", "impact", "losses", "make it right"],
},
},
]
def gen_email_hard(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "email_hard")
template = rng.choice(_EMAIL_HARD_TEMPLATES)
order_id = rng.randint(10000, 99999)
subject = template["subject"].format(order_id=order_id)
body = template["body"].format(order_id=order_id)
case = {**template, "subject": subject, "body": body, "sender": template["sender"], "id": "h1"}
agent_ctx = {"email": {k: v for k, v in case.items() if k not in ("expected_elements", "id")}}
grader_ctx = {"email": case}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# CODE EASY β€” 6 snippet variants
# ═══════════════════════════════════════════════════════════════════════════════
_CODE_EASY_POOL = [
{"code": "def calculate_average(numbers):\n total = 0\n for num in numbers\n total += num\n return total / len(numbers)\n",
"keywords": ["colon", "for loop", "syntax", "for statement"]},
{"code": "def greet(name)\n print(f'Hello, {name}!')\n\ngreet('World')\n",
"keywords": ["colon", "def", "function", "syntax"]},
{"code": "numbers = [1, 2, 3, 4, 5]\nif len(numbers) > 0\n print('Not empty')\n",
"keywords": ["colon", "if statement", "syntax"]},
{"code": "class Animal:\n def __init__(self, name)\n self.name = name\n\n def speak(self):\n return f'{self.name} speaks'\n",
"keywords": ["colon", "__init__", "constructor", "function", "def"]},
{"code": "def read_file(path):\n with open(path) as f\n return f.read()\n",
"keywords": ["colon", "with statement", "context manager", "syntax"]},
{"code": "def retry(fn, times=3):\n for i in range(times)\n try:\n return fn()\n except Exception:\n pass\n",
"keywords": ["colon", "for loop", "range", "syntax"]},
]
def gen_code_easy(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "code_easy")
variant = rng.choice(_CODE_EASY_POOL)
agent_ctx = {"code": variant["code"]}
grader_ctx = {"code": variant["code"], "keywords": variant["keywords"]}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# DATA EASY β€” procedurally generated 6-10 rows
# ═══════════════════════════════════════════════════════════════════════════════
_FIRST_NAMES = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Hiro", "Iris", "Jake"]
_DOMAINS = ["example.com", "company.net", "test.org", "work.io"]
def gen_data_easy(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "data_easy")
n = rng.randint(6, 10)
names = rng.sample(_FIRST_NAMES, k=n)
base_salary = 50000
rows = []
for i, name in enumerate(names, 1):
rows.append({
"id": i,
"name": name,
"age": rng.randint(22, 55),
"email": f"{name.lower()}@{rng.choice(_DOMAINS)}",
"salary": base_salary + rng.randint(0, 50) * 1000,
})
# Inject 3-4 nulls in distinct rows and fields
null_fields = rng.sample(["age", "name", "email", "salary"], k=rng.randint(3, 4))
null_rows = rng.sample(list(range(n)), k=len(null_fields))
missing_answers = []
for row_idx, field in zip(null_rows, null_fields):
rows[row_idx][field] = None
missing_answers.append(f"{field} (row {row_idx + 1})")
# Inject one duplicate pair
dup_src = rng.randint(0, n - 2)
dup_dst = dup_src + 1
rows[dup_dst] = {**rows[dup_src], "id": dup_dst + 1}
dup_ids = sorted([rows[dup_src]["id"], rows[dup_dst]["id"]])
agent_ctx = {"data": rows}
grader_ctx = {
"data": rows,
"answers": {"missing": sorted(missing_answers), "duplicates": dup_ids},
}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# DATA MEDIUM β€” randomised messy formats per field
# ═══════════════════════════════════════════════════════════════════════════════
_AGE_STRINGS = ["thirty", "twenty-two", "forty-five", "nineteen", "fifty"]
_DATE_FORMATS = [("%Y/%m/%d", "2020/01/15"), ("%d-%m-%Y", "15-03-2021"),
("%Y-%m-%d", "2019-07-22"), ("%Y/%m/%d", "2022/11/01"),
("%d/%m/%Y", "01/06/2023")]
_ACTIVE_MESSY = ["yes", "no", "TRUE", "FALSE", "1", "0"]
def gen_data_medium(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "data_medium")
names = rng.sample(_FIRST_NAMES, k=5)
rows = []
salaries = [50000, 60000, 70000, 80000, 45000]
for i, (name, salary) in enumerate(zip(names, salaries), 1):
# Introduce one of each issue type
if i == 1:
age_val = rng.choice(_AGE_STRINGS) # string age
sal_val = f"${salary:,}" # dollar+comma format
elif i == 2:
age_val = 25 # correct
sal_val = f"${salary:,}" # dollar+comma
elif i == 3:
age_val = str(rng.randint(25, 40)) # string digit
sal_val = str(salary) # clean
elif i == 4:
age_val = rng.randint(30, 45) # int β€” correct
sal_val = f"{salary:,}" # comma only
else:
age_val = rng.choice(_AGE_STRINGS) # string age
sal_val = str(salary) # clean
_, date_str = rng.choice(_DATE_FORMATS)
name_messy = name.lower() if i == 4 else name.upper() if i == 5 else name
active_val = rng.choice(_ACTIVE_MESSY) if i in (1, 3, 5) else rng.choice([True, False])
rows.append({"id": i, "name": name_messy, "age": age_val, "salary": sal_val,
"join_date": date_str, "active": active_val})
issues = {
"age": "mixed types: some ages are word strings, should be integers",
"salary": "inconsistent formats: dollar signs and commas present in some values",
"join_date": "three different date formats used across rows",
"name": "inconsistent casing: some names lowercase, some all-caps",
"active": "mixed types: booleans and strings ('yes','no','TRUE') mixed",
}
agent_ctx = {"data": rows}
grader_ctx = {"data": rows, "issues": issues}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# DATA HARD β€” IQR-detectable outliers, missing values
# ═══════════════════════════════════════════════════════════════════════════════
def gen_data_hard(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "data_hard")
n = rng.randint(12, 16)
base = round(rng.uniform(9.5, 12.5), 1)
spread = round(rng.uniform(0.3, 0.8), 2)
rows = []
for i in range(1, n + 1):
val = round(base + rng.gauss(0, spread), 2)
rows.append({"id": i, "value": val})
# Pick 2 outlier positions (ensure they're not neighbours)
positions = list(range(n))
rng.shuffle(positions)
o1, o2 = sorted(positions[:2])
outlier_mult = rng.choice([8, 10, 12, -5, -8])
rows[o1]["value"] = round(base * abs(outlier_mult) * (1 if outlier_mult > 0 else -1), 1)
outlier_mult2 = rng.choice([-4, -6, 15, 20])
rows[o2]["value"] = round(base * abs(outlier_mult2) * (1 if outlier_mult2 > 0 else -1), 1)
outlier_ids = sorted([o1 + 1, o2 + 1])
# Pick 2 missing positions (not overlapping outliers)
available = [i for i in range(n) if i not in (o1, o2)]
m1, m2 = sorted(rng.sample(available, 2))
rows[m1]["value"] = None
rows[m2]["value"] = None
missing_ids = sorted([m1 + 1, m2 + 1])
normal_vals = [r["value"] for r in rows if r["value"] is not None and r["id"] not in outlier_ids]
lo = round(min(normal_vals) - 1.0, 2)
hi = round(max(normal_vals) + 1.0, 2)
agent_ctx = {"data": rows}
grader_ctx = {
"data": rows,
"answers": {
"outliers": set(outlier_ids),
"missing": set(missing_ids),
"imputed_range": (lo, hi),
},
}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# CONTENT MODERATION EASY β€” draw 7 from a pool of 20
# ═══════════════════════════════════════════════════════════════════════════════
_MOD_EASY_POOL = [
{"text": "I hate you so much, I want to hurt you!", "label": "harmful"},
{"text": "Great weather today! Loving this sunshine", "label": "safe"},
{"text": "All people from that group should be eliminated.", "label": "harmful"},
{"text": "Can someone recommend a good Python beginner book?", "label": "safe"},
{"text": "You're an absolute idiot and nobody likes you.", "label": "harmful"},
{"text": "I just finished a 5k run β€” feeling amazing!", "label": "safe"},
{"text": "People like you should just disappear forever.", "label": "harmful"},
{"text": "Just dropped off a care package for my neighbor.", "label": "safe"},
{"text": "Kill yourself, nobody would miss you.", "label": "harmful"},
{"text": "Anyone else excited for the new season of that show?", "label": "safe"},
{"text": "I'm going to find you and make you pay for this.", "label": "harmful"},
{"text": "Finally got my herb garden started!", "label": "safe"},
{"text": "These people deserve to suffer for what they've done.", "label": "harmful"},
{"text": "Happy birthday to my best friend!", "label": "safe"},
{"text": "Someone should shoot that politician.", "label": "harmful"},
{"text": "Loving the new coffee shop that opened downtown.", "label": "safe"},
{"text": "I hope your family gets what's coming to them.", "label": "harmful"},
{"text": "Just hit 100 days of learning Spanish β€” so proud!", "label": "safe"},
{"text": "People from that country are all criminals.", "label": "harmful"},
{"text": "The cherry blossoms are out β€” spring is finally here!", "label": "safe"},
]
def gen_mod_easy(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "mod_easy")
# Ensure at least 3 harmful, 3 safe among 7
harmful = [p for p in _MOD_EASY_POOL if p["label"] == "harmful"]
safe = [p for p in _MOD_EASY_POOL if p["label"] == "safe"]
chosen = rng.sample(harmful, 4) + rng.sample(safe, 3)
rng.shuffle(chosen)
posts = [{"id": f"c{i+1}", **p} for i, p in enumerate(chosen)]
agent_ctx = {"posts": [{"id": p["id"], "text": p["text"]} for p in posts]}
grader_ctx = {"posts": posts}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# CONTENT MODERATION MEDIUM β€” draw 8 from a pool of 20
# ═══════════════════════════════════════════════════════════════════════════════
_MOD_MEDIUM_POOL = [
{"text": "Oh sure, like THAT went well. Classic genius move.", "label": "harmful"},
{"text": "I'm so done with these people. Absolutely insufferable.", "label": "harmful"},
{"text": "That presentation was... very unique.", "label": "safe"},
{"text": "Some people just shouldn't be allowed to have opinions.", "label": "harmful"},
{"text": "Oh wow, another 'brilliant' idea from upper management", "label": "harmful"},
{"text": "Congrats on the promotion! Well deserved!", "label": "safe"},
{"text": "Not everyone is cut out for this kind of work, clearly.", "label": "harmful"},
{"text": "Thanks for the feedback, I'll take it into account.", "label": "safe"},
{"text": "Some of us actually read the requirements before submitting.", "label": "harmful"},
{"text": "Good effort β€” interesting approach to the problem.", "label": "safe"},
{"text": "If you'd done even a little research, you'd know better.", "label": "harmful"},
{"text": "Really enjoyed the team retrospective today.", "label": "safe"},
{"text": "Must be nice having a job you're actually qualified for.", "label": "harmful"},
{"text": "Great work getting the release out on time!", "label": "safe"},
{"text": "Shocking that anyone thought that plan would work.", "label": "harmful"},
{"text": "Just wanted to say β€” the onboarding docs are really clear.", "label": "safe"},
{"text": "The bar for hiring has clearly dropped.", "label": "harmful"},
{"text": "Appreciate everyone's patience during the migration.", "label": "safe"},
{"text": "I see we've decided quantity over quality again.", "label": "harmful"},
{"text": "The demo went really smoothly β€” nice job everyone.", "label": "safe"},
]
def gen_mod_medium(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "mod_medium")
harmful = [p for p in _MOD_MEDIUM_POOL if p["label"] == "harmful"]
safe = [p for p in _MOD_MEDIUM_POOL if p["label"] == "safe"]
chosen = rng.sample(harmful, 5) + rng.sample(safe, 3)
rng.shuffle(chosen)
posts = [{"id": f"m{i+1}", **p} for i, p in enumerate(chosen)]
agent_ctx = {"posts": [{"id": p["id"], "text": p["text"]} for p in posts]}
grader_ctx = {"posts": posts}
return agent_ctx, grader_ctx
# ═══════════════════════════════════════════════════════════════════════════════
# TICKET EASY β€” 8 variant pool (already existed; keep contract-compatible)
# ═══════════════════════════════════════════════════════════════════════════════
_TICKET_EASY_POOL = [
{"id": "t1", "title": "App crashes on login for all users",
"description": "Since the 2.3.1 deploy, 100% of login attempts throw a 500 error.",
"reporter": "ops-monitoring@company.com", "correct_priority": "critical", "correct_category": "bug"},
{"id": "t2", "title": "Add dark mode to the dashboard",
"description": "Multiple users have requested a dark mode option.",
"reporter": "product@company.com", "correct_priority": "low", "correct_category": "feature_request"},
{"id": "t3", "title": "How do I export my data as CSV?",
"description": "I can't find the export button anywhere in the UI.",
"reporter": "user123@customer.com", "correct_priority": "low", "correct_category": "question"},
{"id": "t4", "title": "Charged twice for last month's invoice",
"description": "Account #AC-4421 shows two charges of $299 on March 1st.",
"reporter": "finance@customer.com", "correct_priority": "high", "correct_category": "billing"},
{"id": "t5", "title": "Memory leak in mobile app β€” crashes after 10 min",
"description": "The iOS app uses 2 GB RAM then crashes. Reproducible on all devices.",
"reporter": "qa@company.com", "correct_priority": "high", "correct_category": "bug"},
{"id": "t6", "title": "Add SSO support for Google Workspace",
"description": "Enterprise customers are requesting Google SSO. High deal-blocker frequency.",
"reporter": "sales@company.com", "correct_priority": "medium", "correct_category": "feature_request"},
{"id": "t7", "title": "Refund policy β€” how long does it take?",
"description": "A customer is asking about the refund timeline for a cancelled subscription.",
"reporter": "support@company.com", "correct_priority": "low", "correct_category": "question"},
{"id": "t8", "title": "Wrong amount charged β€” $0 instead of $499",
"description": "New enterprise contract shows $0 on the invoice. Revenue loss.",
"reporter": "billing@company.com", "correct_priority": "critical", "correct_category": "billing"},
]
def gen_ticket_easy(episode_id: str) -> tuple[dict, dict]:
rng = _rng(episode_id, "ticket_easy")
ticket = rng.choice(_TICKET_EASY_POOL)
agent_ctx = {"ticket": {k: v for k, v in ticket.items() if k not in ("correct_priority", "correct_category")}}
grader_ctx = {"ticket": ticket}
return agent_ctx, grader_ctx