Spaces:
Sleeping
Sleeping
paymybills
Add seller personalities, poker tells, WebSocket, multi-buyer arena, leaderboard, counterfactual analysis, and React UI
2524c65 | """Task configurations and graders for BazaarBot.""" | |
| from __future__ import annotations | |
| from .models import DealOutcome, DealRecord, SellerPersonalityType, TaskConfig | |
| # ββ Task Definitions ββββββββββββββββββββββββββββββββββββββββββββββ | |
| TASKS: dict[str, TaskConfig] = { | |
| "single_deal": TaskConfig( | |
| name="single_deal", | |
| difficulty="easy", | |
| description=( | |
| "Buyer negotiates one deal. Symmetric information. No career history. " | |
| "Seller concedes at moderate rate." | |
| ), | |
| max_steps=8, | |
| total_episodes=1, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.08, | |
| buyer_deadline=None, | |
| seller_inventory=1, | |
| seller_batna_probability=0.05, | |
| enable_career=False, | |
| success_threshold=0.3, | |
| ), | |
| "asymmetric_pressure": TaskConfig( | |
| name="asymmetric_pressure", | |
| difficulty="medium", | |
| description=( | |
| "Buyer has hidden hard deadline at round 5. Seller has hidden inventory pressure. " | |
| "Agent must infer seller urgency from offer velocity and close before deadline." | |
| ), | |
| max_steps=8, | |
| total_episodes=1, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.06, | |
| buyer_deadline=5, | |
| seller_inventory=5, | |
| seller_batna_probability=0.08, | |
| enable_career=False, | |
| success_threshold=0.4, | |
| ), | |
| "career_10": TaskConfig( | |
| name="career_10", | |
| difficulty="hard", | |
| description=( | |
| "Buyer plays 10 consecutive deals against same seller. Career history active. " | |
| "Seller adapts concession rate based on buyer's historical capitulation rate. " | |
| "Agent must manage reputation across episodes." | |
| ), | |
| max_steps=80, | |
| total_episodes=10, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.07, | |
| buyer_deadline=None, | |
| seller_inventory=10, | |
| seller_batna_probability=0.1, | |
| enable_career=True, | |
| success_threshold=0.5, | |
| ), | |
| # ββ New personality-based tasks ββββββββββββββββββββββββββββββ | |
| "deceptive_seller": TaskConfig( | |
| name="deceptive_seller", | |
| difficulty="hard", | |
| description=( | |
| "Seller bluffs about demand, fakes urgency, anchors 15% higher. " | |
| "Tells leak deception cues -- verbal over-justification, fidgeting, " | |
| "erratic concessions. Agent must read through the bluffs." | |
| ), | |
| max_steps=10, | |
| total_episodes=1, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.06, | |
| buyer_deadline=None, | |
| seller_inventory=3, | |
| seller_batna_probability=0.05, | |
| enable_career=False, | |
| success_threshold=0.35, | |
| seller_personality=SellerPersonalityType.DECEPTIVE, | |
| enable_tells=True, | |
| ), | |
| "impatient_seller": TaskConfig( | |
| name="impatient_seller", | |
| difficulty="medium", | |
| description=( | |
| "Seller concedes fast but walks fast. Shorter patience window. " | |
| "Agent must close quickly or risk losing the deal. " | |
| "Front-loaded concession pattern is the key tell." | |
| ), | |
| max_steps=8, | |
| total_episodes=1, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.08, | |
| buyer_deadline=None, | |
| seller_inventory=1, | |
| seller_batna_probability=0.15, | |
| enable_career=False, | |
| success_threshold=0.3, | |
| seller_personality=SellerPersonalityType.IMPATIENT, | |
| enable_tells=True, | |
| ), | |
| "collaborative_seller": TaskConfig( | |
| name="collaborative_seller", | |
| difficulty="easy", | |
| description=( | |
| "Seller seeks fair deals, concedes toward midpoint. Lower anchor, " | |
| "tighter margins. Agent should reciprocate to maximize joint surplus. " | |
| "Tests whether agent adapts to cooperative opponents." | |
| ), | |
| max_steps=8, | |
| total_episodes=1, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.10, | |
| buyer_deadline=None, | |
| seller_inventory=1, | |
| seller_batna_probability=0.02, | |
| enable_career=False, | |
| success_threshold=0.4, | |
| seller_personality=SellerPersonalityType.COLLABORATIVE, | |
| enable_tells=True, | |
| ), | |
| "read_the_tells": TaskConfig( | |
| name="read_the_tells", | |
| difficulty="expert", | |
| description=( | |
| "Deceptive seller with strong tells. Agent gets bonus score for " | |
| "exploiting tells -- closing below midpoint when deception cues are high " | |
| "indicates the agent read the bluff. Game theory meets poker." | |
| ), | |
| max_steps=10, | |
| total_episodes=5, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.2, | |
| seller_concession_rate=0.05, | |
| buyer_deadline=None, | |
| seller_inventory=5, | |
| seller_batna_probability=0.08, | |
| enable_career=True, | |
| success_threshold=0.45, | |
| seller_personality=SellerPersonalityType.DECEPTIVE, | |
| enable_tells=True, | |
| ), | |
| "marketplace_arena": TaskConfig( | |
| name="marketplace_arena", | |
| difficulty="expert", | |
| description=( | |
| "Multi-buyer marketplace: 2-3 buyers compete for the same item from one seller. " | |
| "Buyers can signal cooperation or competition. " | |
| "Seller plays buyers against each other. Facebook Marketplace dynamics." | |
| ), | |
| max_steps=12, | |
| total_episodes=1, | |
| buyer_budget=100.0, | |
| seller_cost=30.0, | |
| seller_anchor_multiplier=2.0, | |
| seller_concession_rate=0.06, | |
| buyer_deadline=None, | |
| seller_inventory=1, | |
| seller_batna_probability=0.05, | |
| enable_career=False, | |
| success_threshold=0.3, | |
| seller_personality=SellerPersonalityType.DEFAULT, | |
| enable_tells=True, | |
| num_buyers=3, | |
| enable_coalition=True, | |
| ), | |
| } | |
| # ββ Graders βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def grade_single_deal(results: list[DealRecord], task: TaskConfig) -> float: | |
| if not results: | |
| return 0.0 | |
| deal = results[0] | |
| if deal.outcome != DealOutcome.DEAL or deal.agreed_price is None: | |
| return 0.0 | |
| surplus = task.buyer_budget - deal.agreed_price | |
| max_surplus = task.buyer_budget - task.seller_cost | |
| if max_surplus <= 0: | |
| return 0.0 | |
| score = surplus / max_surplus | |
| return max(0.0, min(1.0, score)) | |
| def grade_asymmetric_pressure(results: list[DealRecord], task: TaskConfig) -> float: | |
| if not results: | |
| return 0.0 | |
| deal = results[0] | |
| if deal.outcome == DealOutcome.WALK: | |
| return 0.0 | |
| if deal.outcome == DealOutcome.EXPIRED: | |
| return 0.0 | |
| if deal.agreed_price is None: | |
| return 0.0 | |
| surplus = task.buyer_budget - deal.agreed_price | |
| max_surplus = task.buyer_budget - task.seller_cost | |
| surplus_score = max(0.0, surplus / max_surplus) if max_surplus > 0 else 0.0 | |
| deadline = task.buyer_deadline or 5 | |
| deadline_bonus = 1.0 if deal.rounds_taken <= deadline else 0.5 | |
| score = surplus_score * deadline_bonus | |
| return max(0.0, min(1.0, score)) | |
| def grade_career_10(results: list[DealRecord], task: TaskConfig) -> float: | |
| if not results: | |
| return 0.0 | |
| rounds_per_ep = task.max_steps // task.total_episodes | |
| weighted_scores = [] | |
| for deal in results: | |
| if deal.outcome != DealOutcome.DEAL or deal.agreed_price is None: | |
| weighted_scores.append(0.0) | |
| continue | |
| surplus = task.buyer_budget - deal.agreed_price | |
| max_surplus = task.buyer_budget - task.seller_cost | |
| norm_surplus = max(0.0, surplus / max_surplus) if max_surplus > 0 else 0.0 | |
| efficiency = max(0.0, 1.0 - (deal.rounds_taken / rounds_per_ep) * 0.3) | |
| weighted_scores.append(norm_surplus * efficiency) | |
| score = sum(weighted_scores) / max(len(weighted_scores), 1) | |
| return max(0.0, min(1.0, score)) | |
| def grade_personality_task(results: list[DealRecord], task: TaskConfig) -> float: | |
| """Generic grader for personality tasks -- same as single_deal but per-episode mean.""" | |
| if not results: | |
| return 0.0 | |
| scores = [] | |
| for deal in results: | |
| if deal.outcome != DealOutcome.DEAL or deal.agreed_price is None: | |
| scores.append(0.0) | |
| continue | |
| surplus = task.buyer_budget - deal.agreed_price | |
| max_surplus = task.buyer_budget - task.seller_cost | |
| norm = max(0.0, surplus / max_surplus) if max_surplus > 0 else 0.0 | |
| scores.append(norm) | |
| return max(0.0, min(1.0, sum(scores) / max(len(scores), 1))) | |
| def grade_read_the_tells(results: list[DealRecord], task: TaskConfig) -> float: | |
| """Bonus for reading deception -- closing well below midpoint earns extra.""" | |
| if not results: | |
| return 0.0 | |
| midpoint = (task.buyer_budget + task.seller_cost) / 2 | |
| scores = [] | |
| for deal in results: | |
| if deal.outcome != DealOutcome.DEAL or deal.agreed_price is None: | |
| scores.append(0.0) | |
| continue | |
| surplus = task.buyer_budget - deal.agreed_price | |
| max_surplus = task.buyer_budget - task.seller_cost | |
| norm = max(0.0, surplus / max_surplus) if max_surplus > 0 else 0.0 | |
| # Bonus for closing below midpoint (reading the bluff) | |
| if deal.agreed_price < midpoint: | |
| bluff_bonus = 0.15 * ((midpoint - deal.agreed_price) / (midpoint - task.seller_cost)) | |
| norm = min(1.0, norm + bluff_bonus) | |
| scores.append(norm) | |
| return max(0.0, min(1.0, sum(scores) / max(len(scores), 1))) | |
| GRADERS = { | |
| "single_deal": grade_single_deal, | |
| "asymmetric_pressure": grade_asymmetric_pressure, | |
| "career_10": grade_career_10, | |
| "deceptive_seller": grade_personality_task, | |
| "impatient_seller": grade_personality_task, | |
| "collaborative_seller": grade_personality_task, | |
| "read_the_tells": grade_read_the_tells, | |
| "marketplace_arena": grade_personality_task, | |
| } | |