mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-13 13:17:40 +02:00
[eric] browser: a prose decline from the read script is a decline, not an answer
This commit is contained in:
@@ -8,6 +8,7 @@ Fail-open everywhere: thin page, decline, error = the loop runs exactly as today
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import Awaitable, Callable, Dict, Optional
|
||||
|
||||
@@ -57,12 +58,29 @@ def read_script_enabled() -> bool:
|
||||
return os.environ.get("OSW_READ_SCRIPT", "1") != "0"
|
||||
|
||||
|
||||
# A decline written as prose instead of the token. The aux is asked to say INSUFFICIENT when the
|
||||
# answer lives behind a click, and it usually does, but on a page that is ALMOST right it explains
|
||||
# itself instead: "I can see the search results, but I cannot access the individual product page."
|
||||
# That reply used to be accepted as the answer, which ended the run at the wrong page and made a
|
||||
# perfectly good multi-step task fail 3 times out of 3 on amazon, deterministically. It is a decline,
|
||||
# so it has to be read as one. Anchored on the aux declaring it cannot REACH somewhere, never on a
|
||||
# page merely lacking a field, because "the page does not show a price" IS a legitimate answer here.
|
||||
P_PROSE_DECLINE_RE = re.compile(
|
||||
r"\b(?:cannot|can'?t|unable to|don'?t have (?:the )?ability to)\s+"
|
||||
r"(?:\w+\s+){0,3}?(?:access|open|reach|navigate(?:\s+to)?|visit|click(?:\s+(?:on|into))?|load)\b"
|
||||
r"|\b(?:would|will|you)\s+need\s+to\s+(?:\w+\s+){0,2}?"
|
||||
r"(?:open|click|visit|navigate|go\s+to)\b",
|
||||
re.I)
|
||||
|
||||
|
||||
def is_answer(reply: str) -> Optional[str]:
|
||||
"""The usable answer text, or None. Declines, empties, and hedge-shaped replies
|
||||
all fail closed to the loop, so a thin extraction can never become a wrong answer."""
|
||||
answer = (reply or "").strip()
|
||||
if not answer or answer.upper().startswith("INSUFFICIENT"):
|
||||
return None
|
||||
if P_PROSE_DECLINE_RE.search(answer):
|
||||
return None
|
||||
return answer
|
||||
|
||||
|
||||
|
||||
@@ -132,3 +132,34 @@ def test_no_aux_client_and_tool_error_both_fail_open():
|
||||
return {"error": "card is gone"}
|
||||
assert asyncio.run(rs.run_read_script(
|
||||
Aux("answer"), "m", "t", "b1", "t1", broken_tool)) is None
|
||||
|
||||
|
||||
def test_a_prose_decline_is_not_an_answer():
|
||||
"""The exact reply that made amazon fail 3/3: the aux explains it cannot reach the page it
|
||||
needs instead of emitting INSUFFICIENT. Accepting it ended the run on the search results and
|
||||
the model loop, which could have clicked through, never got to run."""
|
||||
verbatim = ("I can see this is a search results page for \"usb c cable\" on Amazon, but I "
|
||||
"cannot access the individual product page for the first result. The page shows "
|
||||
"search results with multiple products listed, but to get the exact price, star "
|
||||
"rating, and number of ratings I would need to open the product page.")
|
||||
assert rs.is_answer(verbatim) is None
|
||||
|
||||
for decline in (
|
||||
"I can't open the product page from here.",
|
||||
"I am unable to navigate to that profile.",
|
||||
"You would need to click into the post to see the replies.",
|
||||
"I cannot reach the comments section on this page.",
|
||||
):
|
||||
assert rs.is_answer(decline) is None, decline
|
||||
|
||||
|
||||
def test_a_page_that_simply_lacks_the_field_is_still_an_answer():
|
||||
"""The guard must not eat real answers. Per the read-script contract, a page that visibly
|
||||
lacks the field IS the answer, and so is any answer that happens to contain the word 'open'."""
|
||||
for answer in (
|
||||
"The price is $12.99 and it has 4.5 stars from 8,214 ratings.",
|
||||
"The profile does not show a headline; the name is Eric Zeng.",
|
||||
"The first post is by @someone and the shop is open until 5pm.",
|
||||
"Title: 'How to open a jar'. Channel: Kitchen Tips. 1.2M views.",
|
||||
):
|
||||
assert rs.is_answer(answer) == answer, answer
|
||||
|
||||
Reference in New Issue
Block a user