File size: 6,747 Bytes
ebeaf88 75b6322 ebeaf88 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | """
The 11 PersonalAssistantBench tools + `respond`, with OpenAI-style function schemas and an
executor that drives the simulated iOS world. Result strings replicate the
Swift tools' outputs so trajectories are comparable with the on-device runs.
"""
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Any, Dict, List, Tuple
from .world import IOSWorld, format_event_date, web_search
def _str_arg(args: Dict[str, Any], key: str) -> str:
v = args.get(key, "")
return v if isinstance(v, str) else str(v)
TOOL_SCHEMAS: List[Dict[str, Any]] = [
{
"name": "create_reminder",
"description": "Create a reminder (a to-do item) in the Reminders app.",
"parameters": {"type": "object", "properties": {
"title": {"type": "string", "description": "The reminder text, e.g. 'Buy milk'."}},
"required": ["title"]},
},
{
"name": "list_reminders",
"description": "List the user's current reminders (returns their titles).",
"parameters": {"type": "object", "properties": {}, "required": []},
},
{
"name": "delete_all_reminders",
"description": "Permanently delete ALL of the user's reminders. This cannot be undone.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
{
"name": "create_calendar_event",
"description": "Create an event in the Calendar app at a specific date and time.",
"parameters": {"type": "object", "properties": {
"title": {"type": "string", "description": "The event title."},
"when": {"type": "string", "description": "When it starts, in natural language, e.g. 'tomorrow 12pm'."}},
"required": ["title", "when"]},
},
{
"name": "list_calendar_events",
"description": "List the user's upcoming calendar events with their dates and times.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
{
"name": "create_contact",
"description": "Add a new person to the Contacts app.",
"parameters": {"type": "object", "properties": {
"name": {"type": "string", "description": "The person's full name."}},
"required": ["name"]},
},
{
"name": "list_contacts",
"description": "List the names of the people in the user's Contacts.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
{
"name": "send_message",
"description": "Send a text message to someone. Provide the full message text.",
"parameters": {"type": "object", "properties": {
"text": {"type": "string", "description": "The complete message body to send."},
"recipient": {"type": "string", "description": "Who to send it to, e.g. 'me'."}},
"required": ["text"]},
},
{
"name": "web_search",
"description": "Search the web for current, factual world knowledge and return a short summary with its source.",
"parameters": {"type": "object", "properties": {
"query": {"type": "string", "description": "The search query, e.g. 'capital of Australia'."}},
"required": ["query"]},
},
{
"name": "search_personal",
"description": "Search the user's personal data (their emails and messages) and return matching items, each with its source and date.",
"parameters": {"type": "object", "properties": {
"query": {"type": "string", "description": "What to look for, e.g. 'Lisbon hotel confirmation'."}},
"required": ["query"]},
},
{
"name": "read_webpage",
"description": "Read the text content of the web page or note the user is currently viewing.",
"parameters": {"type": "object", "properties": {}, "required": []},
},
{
"name": "respond",
"description": "Reply to the user and end the current turn. Use this for your final answer, or to ask the user a question.",
"parameters": {"type": "object", "properties": {
"text": {"type": "string", "description": "What to say to the user."}},
"required": ["text"]},
},
]
def execute_tool(world: IOSWorld, name: str, args: Dict[str, Any]) -> Tuple[str, bool]:
"""Run one tool against the world. Returns (result_text, is_error)."""
if name == "create_reminder":
title = _str_arg(args, "title")
world.create_reminder(title)
return f"Created reminder '{title}'.", False
if name == "list_reminders":
titles = world.reminders
return (", ".join(titles) if titles else "No reminders."), False
if name == "delete_all_reminders":
world.delete_all_reminders()
return "Deleted all reminders.", False
if name == "create_calendar_event":
title = _str_arg(args, "title")
# Faithful quirk: the natural-language time is NOT parsed; the event is
# always placed one hour from now. The benchmark scores tool selection.
world.create_event(title, datetime.now() + timedelta(hours=1))
return f"Created calendar event '{title}'.", False
if name == "list_calendar_events":
events = world.events_snapshot()
if not events:
return "No upcoming events.", False
return "; ".join(
f"{t} — {format_event_date(s)}" for t, s in events
), False
if name == "create_contact":
person = _str_arg(args, "name")
world.create_contact(person)
return f"Added contact '{person}'.", False
if name == "list_contacts":
names = world.contacts
return (", ".join(names) if names else "No contacts."), False
if name == "send_message":
text = _str_arg(args, "text")
recip = _str_arg(args, "recipient")
world.message_draft = {"text": text, "recipient": recip}
return f"Drafted message to {recip or 'recipient'}: {text}", False
if name == "web_search":
res = web_search(_str_arg(args, "query"))
if res is None:
return "No web result.", True
return f"{res['extract']}\n(Source: {res['url']})"[:800], False
if name == "search_personal":
hits = world.search_personal(_str_arg(args, "query"))
if not hits:
return "No matching items.", True
return "\n".join(
f"[{d.source} · {d.date}] {d.title}: {d.body}" for d in hits
)[:800], False
if name == "read_webpage":
content = world.page_text
if not content:
return "The page is empty.", True
return content[:1200], False
return f"Unknown tool '{name}'.", True
|