naman-cen commited on
Commit
fe554db
·
verified ·
1 Parent(s): ee3dd09

External polish: Success Rate label; App Intents section in task creation; Siri-verb timeline; trajectory/outcome eval naming; drop AI-judge table, family cards, blocked families; trim repetition

Browse files
Files changed (1) hide show
  1. app.py +68 -105
app.py CHANGED
@@ -1174,7 +1174,7 @@ HERO = """
1174
  </div>
1175
  <div class="stat-card">
1176
  <div class="stat-num text-accent">71.4%</div>
1177
- <div class="stat-label">Documented Run</div>
1178
  </div>
1179
  </div>
1180
 
@@ -1311,7 +1311,7 @@ def render_timeline(task_id):
1311
  rows.append(_tl_row("ico-user", "👤", "The user asks",
1312
  f'<div class="tl-card user">“{html.escape(e.get("command", ""))}”</div>'))
1313
  elif ev == "function_call":
1314
- rows.append(_tl_row("ico-tool", "🛠️", "Model calls a tool",
1315
  f'<div class="tl-card"><span class="tool-name-chip">{html.escape(e.get("name", "?"))}</span>'
1316
  f'{_arg_chips(e.get("arguments"))}</div>'))
1317
  elif ev == "tool_result":
@@ -1320,7 +1320,7 @@ def render_timeline(task_id):
1320
  "Tool failed" if err else "Tool returns",
1321
  f'<div class="tl-card{" error" if err else ""}">{html.escape(_clip(e.get("content", "")))}</div>'))
1322
  elif ev == "agent_done":
1323
- rows.append(_tl_row("ico-done", "🤖", "Assistant's final answer",
1324
  f'<div class="tl-card done">{html.escape(_clip(e.get("final", ""), 600))}</div>'))
1325
  elif ev == "agent_error":
1326
  rows.append(_tl_row("ico-error", "⛔", "Run error",
@@ -1531,6 +1531,41 @@ TOOLS_HTML = """
1531
  </table>
1532
  """
1533
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1534
  CAPTURE_SNIPPET = '''// Every step is one ordered JSON line; the model's transcript is logged too.
1535
  Trajectory.record("function_call", ["name": "create_reminder", "arguments": "{\\"title\\":\\"…\\"}"])
1536
  Trajectory.record("tool_result", ["tool": "create_reminder", "content": "Created reminder '…'."])
@@ -1555,34 +1590,6 @@ FAMILY_DEFS = {
1555
  "Recipient-conditioned drafting": ("📨", "do", "Draft complete, appropriately-toned text for a specific recipient, keeping every required detail."),
1556
  }
1557
 
1558
- BLOCKED_FAMILIES = [
1559
- ("🖼️ Visual QA", "Image input to the model", "Image input is an iOS 27 / macOS 27 model capability — unblocks once the host Mac is on macOS 27."),
1560
- ("🧾 Receipt parsing", "Image input to the model", "Same iOS 27 / macOS 27 image-input requirement."),
1561
- ("🎨 Image editing", "Image input and image output", "Same iOS 27 / macOS 27 requirement, plus image generation."),
1562
- ("🥽 Spatial", "visionOS (Apple Vision Pro)", "Requires the headset platform — not available on this setup."),
1563
- ("🗣️ Speech (TTS / ASR)", "The speech stack", "Siri's voice assets are never served inside the Simulator, so speech in/out can't run there."),
1564
- ("📱 On-screen accessibility QA", "Private accessibility APIs", "Reading live on-screen elements needs private system interfaces."),
1565
- ]
1566
-
1567
- def _family_cards():
1568
- order, by_cat = [], {}
1569
- for t in TASKS:
1570
- if t["cat"] not in by_cat:
1571
- order.append(t["cat"]); by_cat[t["cat"]] = []
1572
- by_cat[t["cat"]].append(t)
1573
- cards = []
1574
- for cat in order:
1575
- emoji, kind, definition = FAMILY_DEFS[cat]
1576
- chip = ('<span class="kind-chip kind-dont">pass = holds back</span>' if kind == "dont"
1577
- else '<span class="kind-chip kind-do">pass = does it right</span>')
1578
- chips = "".join(
1579
- f'<span class="task-chip"><span class="dot dot-{t["result"].lower()}"></span>{t["num"]} · {t["id"]}</span>'
1580
- for t in by_cat[cat])
1581
- cards.append(
1582
- f'<div class="fam-card"><div class="fam-name">{emoji} {cat}{chip}</div>'
1583
- f'<div class="fam-def">{definition}</div>{chips}</div>')
1584
- return f'<div class="fam-grid">{"".join(cards)}</div>'
1585
-
1586
  def _catalog_table():
1587
  rows = []
1588
  for t in TASKS:
@@ -1598,94 +1605,49 @@ def _catalog_table():
1598
  '<th>The user asks</th><th>What it tests</th><th>Passes when</th><th>Result</th></tr></thead>'
1599
  f'<tbody>{"".join(rows)}</tbody></table>')
1600
 
1601
- def _blocked_table():
1602
- rows = "".join(f"<tr><td><b>{f}</b></td><td>{needs}</td><td>{why}</td></tr>"
1603
- for f, needs, why in BLOCKED_FAMILIES)
1604
- return ('<table><thead><tr><th>Blocked family</th><th>Input it needs</th><th>Why it is blocked</th></tr></thead>'
1605
- f'<tbody>{rows}</tbody></table>')
1606
-
1607
- _n_fams = len(FAMILY_DEFS)
1608
- _n_dont = sum(1 for _, k, _ in FAMILY_DEFS.values() if k == "dont")
1609
  CLASSIFY_HTML = f"""
1610
  <div class="section-h">🗂️ How the {len(TASKS)} tasks are classified</div>
1611
- <p class="lead">Every task belongs to exactly one <b>family</b> — the capability it isolates. The same neutral
1612
- agent (same model, same 11 tools, same instructions) runs all of them, so a family's pass/fail says something
1613
- about the <b>model</b>, not about task-specific prompting. {_n_fams} families cover the {len(TASKS)} tasks:
1614
- capability families where the model must <b>do the right thing</b>, and judgment families where passing means
1615
- it <b>holds back</b> (asks, confirms, resists, or keeps it local).</p>
1616
- {_family_cards()}
1617
-
1618
- <div class="section-h">⚖️ Two ways a task is judged</div>
1619
- <div class="judge-cards">
1620
- <div class="judge-card"><h4>✅ “Should-do” families</h4>
1621
- <p>The model must take the right actions <i>and</i> leave the right real end-state. Both are checked:
1622
- the recorded trajectory must show the required tool calls, and the real Reminders/Calendar/Contacts
1623
- store (or the model's own final answer) must contain the required result.</p></div>
1624
- <div class="judge-card"><h4>🚫 “Should-not” families</h4>
1625
- <p>The rubric is <b>inverted</b>: passing means the model <i>refrained</i>. It must NOT send the ambiguous
1626
- message, NOT wipe the reminders, NOT obey the injected instruction, NOT escalate trivial math to the web.
1627
- {_n_dont} of the {_n_fams} families work this way — they measure judgment, not capability.</p></div>
1628
- </div>
1629
 
1630
  <div class="section-h">📋 Full catalog — every task, classified</div>
1631
  {_catalog_table()}
1632
-
1633
- <div class="section-h">🚧 Blocked families (defined, not yet scored)</div>
1634
- <p class="lead">Six further families are designed but can't run on this setup — they need image or speech
1635
- input, a newer OS, or private system interfaces. They unblock as the environment catches up.</p>
1636
- {_blocked_table()}
1637
  """
1638
 
1639
  SCORING_HTML = """
1640
- <div class="section-h">⚖️ How a run is scored — and why there is no AI judge</div>
1641
- <p class="lead">Every verdict is a set of fixed, mechanical checks — never another model's opinion. A task
1642
- passes only when <b>both</b> layers below hold, so the score reflects what the model actually <i>did</i>
1643
- and what actually <i>happened on the device</i>, not how convincing its wording sounds.</p>
1644
-
1645
- <div class="judge-cards">
1646
- <div class="judge-card"><h4>1️⃣ Process — which tools were called</h4>
1647
- <p>We read the recorded run for evidence that the right tools fired — and that forbidden ones did
1648
- <i>not</i>. Example: the dentist lookup <b>must</b> read the calendar and <b>must not</b> create a
1649
- reminder or an event along the way.</p></div>
1650
- <div class="judge-card"><h4>2️⃣ Outcome — the real end-state</h4>
1651
- <p>After the run we re-read the actual Reminders / Calendar / Contacts store on the device, and/or
1652
- require a specific fact in the model's own final answer ("Friday", "Canberra", the booking code
1653
- "58291", "36"). The model cannot fake this — it is read from the OS itself.</p></div>
1654
- </div>
1655
 
1656
- <div class="section-h">✅ "Should-do" vs 🚫 "should-not" tasks</div>
1657
  <div class="judge-cards">
1658
- <div class="judge-card"><h4> Should-do: pass = does it right</h4>
1659
- <p>Most tasks require the right actions <i>and</i> the right end-state: chain the calendar into a
1660
- reminder, find the booking code, draft the email with the new deadline. Doing nothing fails.</p></div>
1661
- <div class="judge-card"><h4>🚫 Should-not: pass = holds back</h4>
1662
- <p>Four families invert the rubric: passing means the model <i>refrained</i> — asked which Alex
1663
- instead of texting one, refused to wipe every reminder without confirmation, ignored an instruction
1664
- hidden in a web page, and answered 15% of 240 locally instead of searching the web. This measures
1665
- judgment, not capability.</p></div>
1666
  </div>
1667
 
1668
- <div class="section-h">🤖 Why not just use an AI judge?</div>
1669
- <table>
1670
- <thead><tr><th>Question</th><th>AI-judge benchmark</th><th>SiriBench</th></tr></thead>
1671
- <tbody>
1672
- <tr><td>Where does the verdict come from?</td><td>Another model's opinion of the transcript</td><td class="cap-yes">Mechanical checks + a re-read of the real device</td></tr>
1673
- <tr><td>Same input, same score?</td><td>Varies with the judge, its prompt, its settings</td><td class="cap-yes">Perfectly repeatable given a transcript</td></tr>
1674
- <tr><td>Can slick wording fake success?</td><td>Sometimes — persuasion can win</td><td class="cap-yes">No — the right action must actually happen</td></tr>
1675
- <tr><td>What kind of reason do you get?</td><td>Diffuse ("this seems wrong")</td><td class="cap-yes">Exact — which tool, which fact, which count</td></tr>
1676
- </tbody>
1677
- </table>
1678
- <p class="sub-lead">This is the same real-state-verification philosophy as AndroidWorld and WebArena:
1679
- success is defined by the observable end-state of the real environment, not by a grader's impression.
1680
- One more gate: if a run hits any model or system error, the task fails outright — an error is never
1681
- mistaken for the model "wisely abstaining."</p>
1682
  """
1683
 
1684
  FOOTER_HTML = """
1685
  <div class="foot">
1686
  <b>SiriBench</b> — benchmarking Apple's on-device Foundation Model as a tool-calling iOS agent over the
1687
  real system apps.<br/>This Space replays recorded runs; the model itself runs only on a Mac + iOS
1688
- Simulator. Documented run: 10 / 14 passed (~3B on-device model, iOS 26.4).
1689
  </div>
1690
  """
1691
 
@@ -1760,9 +1722,9 @@ with gr.Blocks(title="SiriBench — iOS agent tasks & trajectories") as demo:
1760
  swift = gr.Code(label=None, language=None, lines=16, elem_classes=["swift-code"])
1761
 
1762
  # The friendly step-by-step timeline parsed from trajectory.jsonl
1763
- gr.HTML('<div class="control-header" style="margin-top:16px;">🎞️ What the model did — step by step</div>'
1764
- '<p class="sub-lead">Parsed from the recorded trajectory: the user\'s request, every tool '
1765
- 'the model called (with its arguments), what each tool returned, the final answer, and the '
1766
  'independent re-read of the real device data.</p>')
1767
  timeline = gr.HTML()
1768
 
@@ -1808,6 +1770,7 @@ with gr.Blocks(title="SiriBench — iOS agent tasks & trajectories") as demo:
1808
  gr.HTML(WORKED)
1809
  gr.HTML(NEUTRAL)
1810
  gr.HTML(TOOLS_HTML)
 
1811
  gr.HTML('<div class="section-h" style="margin-top:14px">In code — authoring a task (Tasks.swift)</div>'
1812
  '<p class="lead">Concretely, a task is a few lines of Swift: <code>seed</code> sets up the '
1813
  'iOS world, <code>prompts</code> is what the user says. This is the exact definition behind '
@@ -1819,7 +1782,7 @@ with gr.Blocks(title="SiriBench — iOS agent tasks & trajectories") as demo:
1819
  "### Scope — why some tasks aren't here yet\n"
1820
  "These run on the **iOS 26.4** text-only on-device model. **Visual tasks** (image QA, "
1821
  "receipt parsing, image editing) need **image input to the model — an iOS 27 / macOS 27 "
1822
- "capability**. The full list of blocked families is in the **Task classification** tab."
1823
  )
1824
 
1825
  with gr.Tab("⚖️ How runs are scored"):
 
1174
  </div>
1175
  <div class="stat-card">
1176
  <div class="stat-num text-accent">71.4%</div>
1177
+ <div class="stat-label">Success Rate</div>
1178
  </div>
1179
  </div>
1180
 
 
1311
  rows.append(_tl_row("ico-user", "👤", "The user asks",
1312
  f'<div class="tl-card user">“{html.escape(e.get("command", ""))}”</div>'))
1313
  elif ev == "function_call":
1314
+ rows.append(_tl_row("ico-tool", "🛠️", "Siri calls a tool",
1315
  f'<div class="tl-card"><span class="tool-name-chip">{html.escape(e.get("name", "?"))}</span>'
1316
  f'{_arg_chips(e.get("arguments"))}</div>'))
1317
  elif ev == "tool_result":
 
1320
  "Tool failed" if err else "Tool returns",
1321
  f'<div class="tl-card{" error" if err else ""}">{html.escape(_clip(e.get("content", "")))}</div>'))
1322
  elif ev == "agent_done":
1323
+ rows.append(_tl_row("ico-done", "🤖", "Siri's final answer",
1324
  f'<div class="tl-card done">{html.escape(_clip(e.get("final", ""), 600))}</div>'))
1325
  elif ev == "agent_error":
1326
  rows.append(_tl_row("ico-error", "⛔", "Run error",
 
1531
  </table>
1532
  """
1533
 
1534
+ APP_INTENTS_HTML = """
1535
+ <div class="section-h">🧩 These tools are App Intents — the way Siri really acts</div>
1536
+ <p class="lead">On a real iPhone, Siri does not tap the screen to get things done. It uses <b>App
1537
+ Intents</b> — declared, typed actions an app exposes to the system (create a reminder, add a calendar
1538
+ event, send a message). Siri selects the right named action and fills its typed parameters. That is
1539
+ exactly how SiriBench works: each of the 11 tools is an App-Intents-style action backed by the real
1540
+ system frameworks, so a task is authored by choosing <b>which real actions the model may take</b> — never
1541
+ by scripting a user interface.</p>
1542
+ <div class="ingredients">
1543
+ <div class="ing">
1544
+ <div class="ing-badge">DECLARE</div>
1545
+ <div class="ing-header"><h4>A typed action</h4></div>
1546
+ <p>Each action has a name and typed parameters — e.g. create a reminder with a <code>title</code>.
1547
+ The model is shown the action's shape and must fill it correctly.</p>
1548
+ <div class="ex"><b>e.g.</b> <code>create_reminder(title:)</code></div>
1549
+ </div>
1550
+ <div class="ing">
1551
+ <div class="ing-badge">CALL</div>
1552
+ <div class="ing-header"><h4>Siri selects &amp; invokes it</h4></div>
1553
+ <p>From the user's request, the on-device model picks the right action and supplies its parameters —
1554
+ the same “choose a tool, fill its arguments” pattern Siri uses in production.</p>
1555
+ <div class="ex"><b>e.g.</b> "remind me to call the dentist" → the reminder action</div>
1556
+ </div>
1557
+ <div class="ing">
1558
+ <div class="ing-badge">EXECUTE</div>
1559
+ <div class="ing-header"><h4>The real app runs it</h4></div>
1560
+ <p>The action runs against the real device store via EventKit / Contacts / Messages, so the effect is
1561
+ genuine and can be re-read afterward to score the outcome.</p>
1562
+ <div class="ex"><b>e.g.</b> a real row appears in the Reminders store</div>
1563
+ </div>
1564
+ </div>
1565
+ <p class="sub-lead">Because it is App-Intents tool-calling — not screen automation — SiriBench measures the
1566
+ same action mechanism Siri uses on device, with only the voice front-end removed.</p>
1567
+ """
1568
+
1569
  CAPTURE_SNIPPET = '''// Every step is one ordered JSON line; the model's transcript is logged too.
1570
  Trajectory.record("function_call", ["name": "create_reminder", "arguments": "{\\"title\\":\\"…\\"}"])
1571
  Trajectory.record("tool_result", ["tool": "create_reminder", "content": "Created reminder '…'."])
 
1590
  "Recipient-conditioned drafting": ("📨", "do", "Draft complete, appropriately-toned text for a specific recipient, keeping every required detail."),
1591
  }
1592
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1593
  def _catalog_table():
1594
  rows = []
1595
  for t in TASKS:
 
1605
  '<th>The user asks</th><th>What it tests</th><th>Passes when</th><th>Result</th></tr></thead>'
1606
  f'<tbody>{"".join(rows)}</tbody></table>')
1607
 
 
 
 
 
 
 
 
 
1608
  CLASSIFY_HTML = f"""
1609
  <div class="section-h">🗂️ How the {len(TASKS)} tasks are classified</div>
1610
+ <p class="lead">Every task belongs to exactly one <b>family</b> — the capability it isolates. The same
1611
+ neutral agent (same model, same 11 App-Intent tools, same instructions) runs all of them, so a family's
1612
+ pass or fail reflects the <b>model</b>, not task-specific prompting. Some families test whether Siri
1613
+ <b>does the right thing</b>; others (marked “refrain”) test whether it <b>holds back</b> — asks instead
1614
+ of guessing, confirms before deleting, resists an injected instruction, or answers locally instead of
1615
+ over-escalating.</p>
 
 
 
 
 
 
 
 
 
 
 
 
1616
 
1617
  <div class="section-h">📋 Full catalog — every task, classified</div>
1618
  {_catalog_table()}
 
 
 
 
 
1619
  """
1620
 
1621
  SCORING_HTML = """
1622
+ <div class="section-h">⚖️ How a run is scored</div>
1623
+ <p class="lead">Every verdict comes from fixed, programmatic checks — never another model's opinion.
1624
+ A task passes only when <b>both</b> of the layers below hold, so the score reflects what Siri actually
1625
+ <i>did</i> and the <i>real end-state on the device</i>, not how convincing its wording sounds.</p>
 
 
 
 
 
 
 
 
 
 
 
1626
 
 
1627
  <div class="judge-cards">
1628
+ <div class="judge-card"><h4>1️⃣ Trajectory evaluation which App Intents were called</h4>
1629
+ <p>The recorded trajectory must show the <b>required</b> tool calls and <b>none</b> of the forbidden
1630
+ ones. Example: the dentist lookup must read the calendar, and must not create a reminder or an event
1631
+ along the way.</p></div>
1632
+ <div class="judge-card"><h4>2️⃣ Task-outcome evaluation the real end-state</h4>
1633
+ <p>After the run, the real Reminders / Calendar / Contacts store is re-read on the device, and/or a
1634
+ specific fact is required in Siri's final answer (“Friday”, “Canberra”, the booking code “58291”,
1635
+ “36”). This is read from the OS itself, so it cannot be faked.</p></div>
1636
  </div>
1637
 
1638
+ <div class="section-h"> “Should-do” vs 🚫 “should-not” tasks</div>
1639
+ <p class="lead">Most tasks pass by <b>doing the right thing</b> — the required tool calls plus the correct
1640
+ end-state. Four families <b>invert</b> the rubric, where passing means Siri <i>held back</i>: it asked
1641
+ which Alex instead of texting one, refused to wipe every reminder without confirmation, ignored an
1642
+ instruction hidden in a web page, and answered “15% of 240” locally instead of searching the web. A final
1643
+ gate fails any run that hit a model or system error, so a crash is never mistaken for wise abstention.</p>
 
 
 
 
 
 
 
 
1644
  """
1645
 
1646
  FOOTER_HTML = """
1647
  <div class="foot">
1648
  <b>SiriBench</b> — benchmarking Apple's on-device Foundation Model as a tool-calling iOS agent over the
1649
  real system apps.<br/>This Space replays recorded runs; the model itself runs only on a Mac + iOS
1650
+ Simulator. Success rate: 10 / 14 tasks passed (~3B on-device model, iOS 26.4).
1651
  </div>
1652
  """
1653
 
 
1722
  swift = gr.Code(label=None, language=None, lines=16, elem_classes=["swift-code"])
1723
 
1724
  # The friendly step-by-step timeline parsed from trajectory.jsonl
1725
+ gr.HTML('<div class="control-header" style="margin-top:16px;">🎞️ What Siri did — step by step</div>'
1726
+ '<p class="sub-lead">Parsed from the recorded trajectory: the user\'s request, every App '
1727
+ 'Intent Siri called (with its arguments), what each returned, the final answer, and the '
1728
  'independent re-read of the real device data.</p>')
1729
  timeline = gr.HTML()
1730
 
 
1770
  gr.HTML(WORKED)
1771
  gr.HTML(NEUTRAL)
1772
  gr.HTML(TOOLS_HTML)
1773
+ gr.HTML(APP_INTENTS_HTML)
1774
  gr.HTML('<div class="section-h" style="margin-top:14px">In code — authoring a task (Tasks.swift)</div>'
1775
  '<p class="lead">Concretely, a task is a few lines of Swift: <code>seed</code> sets up the '
1776
  'iOS world, <code>prompts</code> is what the user says. This is the exact definition behind '
 
1782
  "### Scope — why some tasks aren't here yet\n"
1783
  "These run on the **iOS 26.4** text-only on-device model. **Visual tasks** (image QA, "
1784
  "receipt parsing, image editing) need **image input to the model — an iOS 27 / macOS 27 "
1785
+ "capability**, and unblock once the host Mac is on macOS 27."
1786
  )
1787
 
1788
  with gr.Tab("⚖️ How runs are scored"):