from __future__ import annotations from copy import deepcopy from typing import Any from floodlight_space.state_snapshot import build_bootstrap_from_snapshot, load_state_snapshot def build_bootstrap(scenario: str = "normal") -> dict[str, Any]: payload = _base_payload() return _apply_scenario(payload, scenario) def _base_payload() -> dict[str, Any]: return build_bootstrap_from_snapshot(load_state_snapshot()) def _legacy_base_payload() -> dict[str, Any]: return { "runtime": { "app": "floodlight-venue-ops-agent", "version": "v1-design-prototype", "channel_label": "WhatsApp simulator", "channel_status": "simulator-connected", "channel_copy": "Demo channel connected. No live WhatsApp send proof claimed.", "pending_count": 7, "confirmed_today": 12, "occupancy_today": 68, "proof_boundary": "Static fixture prototype; live WhatsApp, external scoring/admin, video, model, and HF deployment proof are not claimed." }, "slots": [ {"id": "morning", "label": "8 AM - 12 PM", "period": "Morning"}, {"id": "afternoon", "label": "2 PM - 6 PM", "period": "Afternoon"} ], "venues": [ { "id": "north-field", "name": "North Field", "surface": "Grass", "grass_type": "Natural", "slots": { "morning": {"status": "available", "rate": 6000, "request_count": 0}, "afternoon": {"status": "partial", "rate": 6500, "request_count": 2} } }, { "id": "south-field", "name": "South Field", "surface": "Grass", "grass_type": "Astro turf", "slots": { "morning": {"status": "available", "rate": 5500, "request_count": 0}, "afternoon": {"status": "partial", "rate": 6000, "request_count": 1} } } ], "bookings": [ { "id": "north-afternoon-rohit", "venue_id": "north-field", "slot_id": "afternoon", "date_text": "tomorrow", "status": "pending_owner_review", "team_id": "friends-rohit", "players": 10 }, { "id": "south-afternoon-local-league", "venue_id": "south-field", "slot_id": "afternoon", "date_text": "tomorrow", "status": "pending_owner_review", "team_id": "league-karan", "players": 14 } ], "registered_teams": [ { "id": "corporate-aman", "name": "Aman Corporate XI", "sport": "cricket", "captain_player_id": "aman-sharma", "usual_players": 12, "notes": "Prefers North Field natural grass." }, { "id": "friends-rohit", "name": "Rohit's Friends", "sport": "cricket", "captain_player_id": "rohit-verma", "usual_players": 10, "notes": "Flexible between North Field and South Field." }, { "id": "league-karan", "name": "Karan League Squad", "sport": "cricket", "captain_player_id": "karan-mehta", "usual_players": 14, "notes": "League booking; owner approval required." } ], "players": [ { "id": "aman-sharma", "name": "Aman Sharma", "phone": "+91 98765 43210", "team_id": "corporate-aman" }, { "id": "rohit-verma", "name": "Rohit Verma", "phone": "+91 90123 45678", "team_id": "friends-rohit" }, { "id": "karan-mehta", "name": "Karan Mehta", "phone": "+91 99887 76655", "team_id": "league-karan" } ], "requests": [ { "id": "aman-sharma", "customer_name": "Aman Sharma", "phone": "+91 98765 43210", "received_label": "1 min ago", "activity": "Cricket", "date_label": "Tomorrow, 25 May 2025", "requested_slot_id": "north-field:morning", "players": 12, "group_type": "Corporate", "status": "new", "tone": "ready", "missing_fields": [], "summary": "12 players · Corporate", "reply_draft": "Hi Aman, North Field is available tomorrow from 8 AM - 12 PM. The slot is ₹6,000 for natural grass. Reply YES and we will hold it after owner approval." }, { "id": "rohit-verma", "customer_name": "Rohit Verma", "phone": "+91 90123 45678", "received_label": "3 min ago", "activity": "Cricket", "date_label": "Tomorrow, 25 May 2025", "requested_slot_id": "north-field:afternoon", "players": 10, "group_type": "Friends", "status": "new", "tone": "partial", "missing_fields": [], "summary": "10 players · Friends", "reply_draft": "Hi Rohit, North Field has partial demand tomorrow from 2 PM - 6 PM at ₹6,500. We can confirm after owner approval, or offer South Field at ₹6,000." }, { "id": "karan-mehta", "customer_name": "Karan Mehta", "phone": "+91 99887 76655", "received_label": "5 min ago", "activity": "Cricket", "date_label": "Tomorrow, 25 May 2025", "requested_slot_id": "south-field:morning", "players": 14, "group_type": "League", "status": "new", "tone": "ready", "missing_fields": [], "summary": "14 players · League", "reply_draft": "Hi Karan, South Field is available tomorrow from 8 AM - 12 PM. Astro turf surface, ₹5,500. I can hold it after owner approval." }, { "id": "vikas-singh", "customer_name": "Vikas Singh", "phone": "+91 91234 87650", "received_label": "7 min ago", "activity": "Cricket", "date_label": "Today", "requested_slot_id": "south-field:afternoon", "players": 12, "group_type": "Corporate", "status": "conflict", "tone": "conflict", "missing_fields": [], "summary": "12 players · Corporate", "reply_draft": "Hi Vikas, the requested afternoon slot has other demand. North Field morning is open, or South Field morning is open. Which one should I hold for you?" }, { "id": "neha-iyer", "customer_name": "Neha Iyer", "phone": "+91 95555 45670", "received_label": "9 min ago", "activity": "Cricket", "date_label": "Tomorrow, 25 May 2025", "requested_slot_id": "south-field:afternoon", "players": 8, "group_type": "Friends", "status": "clarification", "tone": "clarification", "missing_fields": ["format/overs"], "summary": "8 players · Friends", "reply_draft": "Hi Neha, South Field afternoon is available with one existing request. Could you confirm format or overs before I ask the owner to hold it?" } ], "decision": { "selected_request_id": "aman-sharma", "selected_slot_id": "north-field:morning", "selected_price": 6000, "notes": "", "state": "ready" }, "integrations": [ { "id": "video", "label": "Video Telecast", "state": "disabled", "copy": "Disabled" }, { "id": "external-scoring", "label": "External scoring/admin app", "state": "not-connected", "copy": "Not connected · Disabled" } ], "trace": [ {"id": "intake", "label": "Intake received from simulator", "tone": "neutral", "timestamp_label": "now"}, {"id": "policy", "label": "Approval required before outbound reply", "tone": "warning", "timestamp_label": "now"}, {"id": "draft", "label": "Reply drafted and pending owner review", "tone": "neutral", "timestamp_label": "now"} ], "scenarios": [ {"id": "normal", "label": "Live queue"}, {"id": "clarification", "label": "Clarification"}, {"id": "conflict", "label": "Slot conflict"}, {"id": "provider-error", "label": "Provider error"}, {"id": "empty", "label": "Empty queue"} ], "active_scenario": "normal" } def _apply_scenario(payload: dict[str, Any], scenario: str) -> dict[str, Any]: data = deepcopy(payload) data["active_scenario"] = scenario if scenario == "clarification": data["decision"]["selected_request_id"] = "neha-iyer" data["decision"]["selected_slot_id"] = "south-field:afternoon" data["decision"]["selected_price"] = 6000 data["trace"].append({"id": "missing-fields", "label": "Missing field: format/overs", "tone": "warning", "timestamp_label": "now"}) elif scenario == "conflict": data["decision"]["selected_request_id"] = "vikas-singh" data["decision"]["selected_slot_id"] = "south-field:afternoon" data["decision"]["selected_price"] = 6000 data["venues"][1]["slots"]["afternoon"]["status"] = "conflict" data["trace"].append({"id": "conflict", "label": "Requested slot has competing demand; alternatives visible", "tone": "warning", "timestamp_label": "now"}) elif scenario == "provider-error": data["runtime"]["channel_status"] = "provider-error" data["runtime"]["channel_copy"] = "Simulator provider error. No outbound reply can be marked sent." data["decision"]["state"] = "error" data["trace"].append({"id": "provider-error", "label": "Provider error state active; retry required", "tone": "error", "timestamp_label": "now"}) elif scenario == "empty": data["runtime"]["pending_count"] = 0 data["requests"] = [] data["decision"]["selected_request_id"] = "" data["decision"]["selected_slot_id"] = "north-field:morning" data["decision"]["state"] = "idle" data["trace"] = [{"id": "empty", "label": "Queue empty; board remains reviewable", "tone": "neutral", "timestamp_label": "now"}] return data