"""The answer key, derived from persona features. ONE rule, any persona. 96 personas today; 10k real Expedia rows later — same function, zero authoring cost. Verified total and single-valued over all 96 combos (test_rules.py). """ from __future__ import annotations from .personas import Persona from .scenarios import ScenarioTruth _ALLOWED_OFFERS: dict[str, frozenset[str]] = { "low": frozenset({"15% off", "10% off"}), "mid": frozenset({"15% off", "free breakfast"}), "high": frozenset({"free breakfast", "free night"}), } def _intent(p: Persona) -> str: if p.trip_type == "conference" and p.near_event: return "book_hotel_near_event" if p.trip_type in ("conference", "business"): return "business_trip" # a conference with no nearby event is still a business trip if p.trip_type == "family": return "family_vacation" return "generic_leisure" def _background(p: Persona) -> str: if p.trip_type in ("conference", "business"): return "hotel_exterior" if p.trip_type == "family": return "room_interior" return "city_skyline" def _headline(p: Persona) -> str: # FIRST MATCH WINS — the order is load-bearing. 'proximity' is gated on trip_type as well as # near_event, which is what guarantees it is only ever CORRECT when an event actually exists. if p.near_event and p.trip_type in ("conference", "business"): return "proximity" if p.budget_tier == "high": return "boutique" if p.trip_type == "leisure": return "destination" return "generic" def _cta(p: Persona) -> str: return "Book now" if p.device == "mobile" else "Learn more" def truth_for(persona: Persona) -> ScenarioTruth: return ScenarioTruth( correct_intent=_intent(persona), correct_background=_background(persona), correct_headline_strategy=_headline(persona), correct_cta=_cta(persona), allowed_offers=_ALLOWED_OFFERS[persona.budget_tier], ) def oracle_action(persona: Persona) -> list[int]: """The perfect action for a persona. Used by tests, the demo, and (later) as the agent's upper-bound baseline. Scores exactly 1.0 by construction. sorted(allowed_offers)[0] — NEVER next(iter(frozenset)), whose order varies with PYTHONHASHSEED and would make the oracle non-deterministic across processes. """ from .brief import MENUS # local import: brief imports personas, rules imports personas t = truth_for(persona) return [ MENUS["intent"].index(t.correct_intent), MENUS["headline"].index(t.correct_headline_strategy), MENUS["offer"].index(sorted(t.allowed_offers)[0]), MENUS["cta"].index(t.correct_cta), MENUS["background"].index(t.correct_background), ]