Spaces:
Sleeping
Sleeping
| """Shared heuristic policy for Flatmate RL scenario execution.""" | |
| from __future__ import annotations | |
| from copy import deepcopy | |
| from typing import Any | |
| try: | |
| from .scenarios import SCENARIOS | |
| except ImportError: | |
| from server.scenarios import SCENARIOS | |
| def active_scenario(task_id: str) -> dict[str, Any]: | |
| return SCENARIOS[task_id] | |
| def _enrich_expected_tool_arguments(task_id: str, observation: dict[str, Any], payload: dict[str, Any]) -> dict[str, Any]: | |
| enriched = deepcopy(payload) | |
| if enriched.get("action_type") != "tool_call": | |
| return enriched | |
| scenario = active_scenario(task_id) | |
| tool_name = enriched.get("tool_name", "") | |
| phase = observation.get("phase", "buyer") | |
| if tool_name == "store_user_details" and phase == "buyer": | |
| enriched["tool_arguments"] = deepcopy(scenario["scenario_creation_config"].get("expected_answers", {})) | |
| elif tool_name == "store_seller_details" and phase == "seller": | |
| enriched["tool_arguments"] = deepcopy(scenario["scenario_creation_config"].get("followup_seller_expected_answers", {})) | |
| else: | |
| enriched.setdefault("tool_arguments", {}) | |
| return enriched | |
| def expected_policy_action(task_id: str, observation: dict[str, Any]) -> dict[str, Any] | None: | |
| payload = autopolicy_next_request(task_id, observation) | |
| if payload is None: | |
| return None | |
| return _enrich_expected_tool_arguments(task_id, observation, payload) | |
| def _missing_fields_from_feedback(observation: dict[str, Any]) -> list[str]: | |
| feedback = " ".join( | |
| [ | |
| str(observation.get("feedback_summary", "")), | |
| str(observation.get("message", "")), | |
| str(observation.get("last_tool_result", {}).get("message", "")), | |
| ] | |
| ).lower() | |
| fields = [] | |
| patterns = { | |
| "diet": ["diet"], | |
| "visit_availability": ["visit_availability", "visit availability"], | |
| "occupation": ["occupation"], | |
| "budget": ["budget"], | |
| "areas": ["areas", "area"], | |
| "listing_choices": ["listing_choices", "listing choices"], | |
| "dietary": ["dietary"], | |
| "occupation_requirement": ["occupation requirement", "who the flat is for"], | |
| } | |
| for field, phrases in patterns.items(): | |
| if any(phrase in feedback for phrase in phrases): | |
| fields.append(field) | |
| return fields | |
| def _ask_for_missing_fields(missing: list[str], phase: str, task_id: str) -> dict[str, Any] | None: | |
| if phase == "seller": | |
| need_dietary = "dietary" in missing | |
| need_occupation = "occupation_requirement" in missing | |
| need_slots = "calendar_slots" in missing | |
| if need_dietary and need_occupation and need_slots: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share the household dietary setup, who the flat is for, and available visit time slots."} | |
| if need_dietary and need_occupation: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share the household dietary setup and who the flat is for."} | |
| if need_dietary and need_slots: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share the household dietary setup and available visit time slots."} | |
| if need_occupation and need_slots: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share who the flat is for and available visit time slots."} | |
| if need_dietary: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share the household dietary setup."} | |
| if need_occupation: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share who the flat is for."} | |
| if need_slots: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share available visit time slots."} | |
| return None | |
| if "diet" in missing and "visit_availability" in missing: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share your dietary preference and visit availability."} | |
| if "diet" in missing: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share your dietary preference."} | |
| if "visit_availability" in missing: | |
| return {"action_type": "assistant_message", "assistant_message": "Please share your visit availability."} | |
| if "listing_choices" in missing and task_id == "task_visit_multi": | |
| return {"action_type": "assistant_message", "assistant_message": "I shortlisted post_031 at tomorrow 7pm and post_052 at Sunday 4pm. Which listings do you want to pursue?"} | |
| return None | |
| def _store_or_ask(remaining: set[str], task_id: str, phase: str) -> dict[str, Any]: | |
| """Return ask-for-fields message or store_user_details tool call.""" | |
| missing_prompt = _ask_for_missing_fields(sorted(remaining), phase=phase, task_id=task_id) | |
| if missing_prompt is not None: | |
| return missing_prompt | |
| return {"action_type": "tool_call", "tool_name": "store_user_details", "tool_arguments": {}} | |
| def _autopolicy_negotiation( | |
| observation: dict[str, Any], | |
| tool_names: list[str], | |
| user_has_replied: bool, | |
| remaining: set[str], | |
| ) -> dict[str, Any] | None: | |
| """Heuristic for task_negotiation_hidden_budget.""" | |
| def has_tool(name: str) -> bool: | |
| return name in tool_names | |
| if not observation.get("buyer_profile_stored"): | |
| return _store_or_ask(remaining, "task_negotiation_hidden_budget", "buyer") | |
| if not has_tool("search_posts"): | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_155"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_155"]}} | |
| if not has_tool("check_calendar_slots"): | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_155"]}} | |
| if not has_tool("shortlist"): | |
| return {"action_type": "tool_call", "tool_name": "shortlist", "tool_arguments": {"post_ids": ["post_155"]}} | |
| # Probe buyer: first at a value above their ceiling (expect reject), then below | |
| buyer_probes = [t for t in observation.get("tool_trace", []) if t.get("tool") == "propose_price_to_buyer"] | |
| if len(buyer_probes) == 0: | |
| return {"action_type": "tool_call", "tool_name": "propose_price_to_buyer", "tool_arguments": {"post_id": "post_155", "proposed_rent": 23000}} | |
| if len(buyer_probes) == 1: | |
| return {"action_type": "tool_call", "tool_name": "propose_price_to_buyer", "tool_arguments": {"post_id": "post_155", "proposed_rent": 21000}} | |
| seller_probes = [t for t in observation.get("tool_trace", []) if t.get("tool") == "propose_price_to_seller"] | |
| if not seller_probes: | |
| return {"action_type": "tool_call", "tool_name": "propose_price_to_seller", "tool_arguments": {"post_id": "post_155", "proposed_rent": 21000}} | |
| return {"action_type": "tool_call", "tool_name": "confirm_negotiated_deal", "tool_arguments": {"post_id": "post_155", "agreed_rent": 21000}} | |
| def _autopolicy_waitlist( | |
| observation: dict[str, Any], | |
| tool_names: list[str], | |
| user_has_replied: bool, | |
| remaining: set[str], | |
| ) -> dict[str, Any] | None: | |
| """Heuristic for task_slot_cancellation_waitlist.""" | |
| def has_tool(name: str) -> bool: | |
| return name in tool_names | |
| if not observation.get("buyer_profile_stored"): | |
| return _store_or_ask(remaining, "task_slot_cancellation_waitlist", "buyer") | |
| if not has_tool("search_posts"): | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_162"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_162"]}} | |
| if not has_tool("check_calendar_slots"): | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_162"]}} | |
| if not has_tool("add_to_waitlist") and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "All slots for post_162 are currently fully booked. Let me add you to the waitlist."} | |
| if not has_tool("add_to_waitlist"): | |
| return {"action_type": "tool_call", "tool_name": "add_to_waitlist", "tool_arguments": {"post_id": "post_162"}} | |
| if not has_tool("notify_buyer_slot_freed") and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "You're on the waitlist for post_162. I'll reach out as soon as a slot opens up."} | |
| if not has_tool("notify_buyer_slot_freed"): | |
| return {"action_type": "tool_call", "tool_name": "notify_buyer_slot_freed", "tool_arguments": {"post_id": "post_162", "slot": "Saturday 10am"}} | |
| if not has_tool("contact_poster"): | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_162", "time_text": "Saturday 10am"}} | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_162", "time_text": "Saturday 10am"}} | |
| def _autopolicy_multi_visit( | |
| observation: dict[str, Any], | |
| tool_names: list[str], | |
| user_has_replied: bool, | |
| remaining: set[str], | |
| booked: list[str], | |
| last_buyer_message: str, | |
| ) -> dict[str, Any] | None: | |
| """Heuristic for task_multi_visit_preference_evolution.""" | |
| def has_tool(name: str) -> bool: | |
| return name in tool_names | |
| def count_tool(name: str) -> int: | |
| return tool_names.count(name) | |
| trace = observation.get("tool_trace", []) | |
| debrief_posts = [t.get("args", {}).get("post_id") for t in trace if t.get("tool") == "debrief_visit"] | |
| debrief1_done = "post_023" in debrief_posts | |
| debrief2_done = "post_052" in debrief_posts | |
| visit1_booked = "post_023" in booked | |
| visit2_booked = "post_052" in booked | |
| visit3_booked = "post_067" in booked | |
| if not observation.get("buyer_profile_stored"): | |
| return _store_or_ask(remaining, "task_multi_visit_preference_evolution", "buyer") | |
| # ---- Initial search and visit 1 setup ---- | |
| if count_tool("search_posts") == 0: | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_023", "post_052"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_023"]}} | |
| if count_tool("check_calendar_slots") == 0: | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_023"]}} | |
| # Propose and confirm visit 1 | |
| if not visit1_booked and count_tool("contact_poster") == 0 and "confirm saturday 11am" not in last_buyer_message.lower(): | |
| return {"action_type": "assistant_message", "assistant_message": "post_023 has Saturday 11am available. Please confirm Saturday 11am if that works."} | |
| if not visit1_booked and count_tool("contact_poster") == 0: | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_023", "time_text": "Saturday 11am"}} | |
| if not visit1_booked: | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_023", "time_text": "Saturday 11am"}} | |
| # ---- Debrief visit 1 and update preferences ---- | |
| if not debrief1_done and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "How was your visit to post_023? What did you think of the flat?"} | |
| if not debrief1_done: | |
| return {"action_type": "tool_call", "tool_name": "debrief_visit", "tool_arguments": {"post_id": "post_023", "user_feedback": "buyer found the area too noisy, needs a quiet location"}} | |
| if count_tool("store_user_details") < 2: | |
| return {"action_type": "tool_call", "tool_name": "store_user_details", "tool_arguments": {}} | |
| if count_tool("filter_new_arrivals") == 0: | |
| return {"action_type": "tool_call", "tool_name": "filter_new_arrivals", "tool_arguments": {"post_ids": ["post_n01", "post_n02", "post_q01"]}} | |
| if count_tool("search_posts") == 1: | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| # ---- Visit 2 setup ---- | |
| if count_tool("check_calendar_slots") == 1: | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_052"]}} | |
| if not visit2_booked and count_tool("contact_poster") == 1 and "confirm sunday 4pm" not in last_buyer_message.lower(): | |
| return {"action_type": "assistant_message", "assistant_message": "post_052 has Sunday 4pm available. Please confirm Sunday 4pm if that works."} | |
| if not visit2_booked and count_tool("contact_poster") == 1: | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_052", "time_text": "Sunday 4pm"}} | |
| if not visit2_booked: | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_052", "time_text": "Sunday 4pm"}} | |
| # ---- Debrief visit 2 and update preferences ---- | |
| if not debrief2_done and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "How was your visit to post_052? Did it meet your needs?"} | |
| if not debrief2_done: | |
| return {"action_type": "tool_call", "tool_name": "debrief_visit", "tool_arguments": {"post_id": "post_052", "user_feedback": "flat was quiet but no gym nearby, needs gym access"}} | |
| if count_tool("store_user_details") < 3: | |
| return {"action_type": "tool_call", "tool_name": "store_user_details", "tool_arguments": {}} | |
| if count_tool("filter_new_arrivals") == 1: | |
| return {"action_type": "tool_call", "tool_name": "filter_new_arrivals", "tool_arguments": {"post_ids": ["post_g01", "post_i01", "post_067"]}} | |
| if count_tool("search_posts") == 2: | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| # ---- Visit 3 setup ---- | |
| if count_tool("check_calendar_slots") == 2: | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_067"]}} | |
| if not visit3_booked and count_tool("contact_poster") == 2 and "confirm saturday 3pm" not in last_buyer_message.lower(): | |
| return {"action_type": "assistant_message", "assistant_message": "post_067 has Saturday 3pm available — quiet area with a gym nearby. Please confirm Saturday 3pm."} | |
| if not visit3_booked and count_tool("contact_poster") == 2: | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_067", "time_text": "Saturday 3pm"}} | |
| if not visit3_booked: | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_067", "time_text": "Saturday 3pm"}} | |
| return None | |
| def autopolicy_next_request(task_id: str, observation: dict[str, Any]) -> dict[str, Any] | None: | |
| trace = observation.get("tool_trace", []) | |
| tool_names = [item.get("tool", "") for item in trace] | |
| phase = observation.get("phase", "buyer") | |
| remaining = set(observation.get("remaining_required_fields", [])) | |
| selected_posts = list(observation.get("selected_posts", [])) | |
| booked = [item["post_id"] for item in observation.get("booked_visits", [])] | |
| buyer_history = observation.get("buyer_conversation_history", []) | |
| last_buyer_role = str(buyer_history[-1].get("role", "")) if buyer_history else "" | |
| last_buyer_message = str(observation.get("last_user_message", "")) | |
| user_has_replied = observation.get("status") == "user_response" and last_buyer_role == "user" | |
| feedback_missing = _missing_fields_from_feedback(observation) | |
| if feedback_missing: | |
| remaining.update(feedback_missing) | |
| def has_tool(name: str) -> bool: | |
| return name in tool_names | |
| if observation.get("done"): | |
| return None | |
| if phase == "buyer": | |
| if task_id == "task_negotiation_hidden_budget": | |
| return _autopolicy_negotiation(observation, tool_names, user_has_replied, remaining) | |
| if task_id == "task_slot_cancellation_waitlist": | |
| return _autopolicy_waitlist(observation, tool_names, user_has_replied, remaining) | |
| if task_id == "task_multi_visit_preference_evolution": | |
| return _autopolicy_multi_visit(observation, tool_names, user_has_replied, remaining, booked, last_buyer_message) | |
| if task_id == "task_visit_single_seller_followup" and not observation.get("seller_profile_stored"): | |
| if not observation.get("buyer_profile_stored"): | |
| missing_prompt = _ask_for_missing_fields(sorted(remaining), phase=phase, task_id=task_id) | |
| if missing_prompt is not None: | |
| return missing_prompt | |
| return {"action_type": "tool_call", "tool_name": "store_user_details", "tool_arguments": {}} | |
| if not has_tool("search_posts"): | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| return {"action_type": "tool_call", "tool_name": "close_buyer_conversation", "tool_arguments": {}} | |
| if not observation.get("buyer_profile_stored"): | |
| missing_prompt = _ask_for_missing_fields(sorted(remaining), phase=phase, task_id=task_id) | |
| if missing_prompt is not None: | |
| return missing_prompt | |
| return {"action_type": "tool_call", "tool_name": "store_user_details", "tool_arguments": {}} | |
| if not has_tool("search_posts"): | |
| return {"action_type": "tool_call", "tool_name": "search_posts", "tool_arguments": {}} | |
| if task_id == "task_visit_single": | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_023", "post_031"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_023", "post_031"]}} | |
| if not has_tool("check_calendar_slots"): | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_023"]}} | |
| if "contact_poster" not in tool_names and "book_viewing" not in tool_names and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "post_023 is available Saturday 11am. Please confirm Saturday 11am if that works."} | |
| if not has_tool("contact_poster"): | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_023", "time_text": "Saturday 11am"}} | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_023", "time_text": "Saturday 11am"}} | |
| if task_id == "task_visit_conflict_check": | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_142"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_142"]}} | |
| if not has_tool("check_calendar_slots"): | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_142"]}} | |
| if "contact_poster" not in tool_names and "book_viewing" not in tool_names and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "post_142 has Sunday 5pm available (Saturday slots are already booked by other buyers). Please confirm Sunday 5pm if that works for you."} | |
| if not has_tool("contact_poster"): | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_142", "time_text": "Sunday 5pm"}} | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_142", "time_text": "Sunday 5pm"}} | |
| if task_id == "task_visit_single_hidden_flex": | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_023", "post_052"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_023", "post_052"]}} | |
| if not has_tool("check_calendar_slots"): | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_023", "post_052"]}} | |
| if not has_tool("contact_poster") and not has_tool("book_viewing") and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "No Tuesday slot matches. I can offer Saturday 1pm or Sunday 5pm instead."} | |
| if not has_tool("contact_poster"): | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_023", "time_text": "Sunday 5pm"}} | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_023", "time_text": "Sunday 5pm"}} | |
| if task_id == "task_visit_multi": | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_031", "post_052"]}} | |
| if not has_tool("get_commute_time"): | |
| return {"action_type": "tool_call", "tool_name": "get_commute_time", "tool_arguments": {"post_ids": ["post_031", "post_052"]}} | |
| if not has_tool("check_calendar_slots"): | |
| return {"action_type": "tool_call", "tool_name": "check_calendar_slots", "tool_arguments": {"post_ids": ["post_031", "post_052"]}} | |
| if not selected_posts and not user_has_replied: | |
| return {"action_type": "assistant_message", "assistant_message": "I shortlisted post_031 at tomorrow 7pm and post_052 at Sunday 4pm. Which listings do you want to pursue?"} | |
| if "post_031" not in booked and tool_names.count("contact_poster") == 0 and "confirm tomorrow 7pm" not in last_buyer_message.lower(): | |
| return {"action_type": "assistant_message", "assistant_message": "Please confirm tomorrow 7pm for post_031."} | |
| if "post_031" not in booked and tool_names.count("contact_poster") == 0: | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_031", "time_text": "tomorrow 7pm"}} | |
| if "post_031" not in booked: | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_031", "time_text": "tomorrow 7pm"}} | |
| if "post_052" not in booked and tool_names.count("contact_poster") == 1 and "confirm sunday 4pm" not in last_buyer_message.lower(): | |
| return {"action_type": "assistant_message", "assistant_message": "Please confirm Sunday 4pm for post_052."} | |
| if "post_052" not in booked and tool_names.count("contact_poster") == 1 and tool_names.count("book_viewing") == 1: | |
| return {"action_type": "tool_call", "tool_name": "contact_poster", "tool_arguments": {"post_id": "post_052", "time_text": "Sunday 4pm"}} | |
| if "post_052" not in booked: | |
| return {"action_type": "tool_call", "tool_name": "book_viewing", "tool_arguments": {"post_id": "post_052", "time_text": "Sunday 4pm"}} | |
| if phase == "seller": | |
| if not observation.get("seller_profile_stored"): | |
| missing_prompt = _ask_for_missing_fields(sorted(remaining), phase=phase, task_id=task_id) | |
| if missing_prompt is not None: | |
| return missing_prompt | |
| return {"action_type": "tool_call", "tool_name": "store_seller_details", "tool_arguments": {}} | |
| if not has_tool("match_location_preference"): | |
| return {"action_type": "tool_call", "tool_name": "match_location_preference", "tool_arguments": {"post_ids": ["post_dynamic_followup_1"]}} | |
| if not has_tool("check_table_slot_matches"): | |
| return {"action_type": "tool_call", "tool_name": "check_table_slot_matches", "tool_arguments": {"post_ids": ["post_dynamic_followup_1"]}} | |
| if not has_tool("confirm_seller_match"): | |
| return {"action_type": "tool_call", "tool_name": "confirm_seller_match", "tool_arguments": {"post_id": "post_dynamic_followup_1", "time_text": "Sunday 5pm"}} | |
| if not has_tool("offer_matched_listing_to_buyer"): | |
| return {"action_type": "tool_call", "tool_name": "offer_matched_listing_to_buyer", "tool_arguments": {"post_id": "post_dynamic_followup_1", "time_text": "Sunday 5pm"}} | |
| return {"action_type": "tool_call", "tool_name": "schedule_table_visit", "tool_arguments": {"post_id": "post_dynamic_followup_1", "time_text": "Sunday 5pm"}} | |
| return None | |