Files
openswarm/backend/apps/agents/browser/browser_validator.py
T

58 lines
2.2 KiB
Python

"""
Last-resort adjudication for a stuck browser sub-agent.
Deterministic stagnation detection (browser_loop) handles the common cases for
free. When it's exhausted (a run of actions with no progress despite escalating
nudges), we make ONE cheap aux-tier LLM call to suggest a concrete next step.
It is rare by construction, so the cost stays near zero. The model + client are
resolved provider-agnostically by the caller (cheap tier of whatever provider
the user has connected), so nothing here hardcodes Anthropic/Haiku.
"""
import logging
logger = logging.getLogger(__name__)
P_ADJUDICATION_PROMPT = (
"A browser automation agent is stuck: its recent actions produced no "
"progress on the page.\n\n"
"GOAL: {goal}\n\n"
"RECENT ACTIONS (most recent last):\n{recent}\n\n"
"CURRENT PAGE (truncated):\n{page}\n\n"
"In 2 to 3 sentences, give the single most promising concrete next step. "
"Prefer, in order: a different element or selector, a keyboard shortcut "
"(Tab then Enter), scrolling to reveal a hidden control, or calling "
"RequestHumanIntervention if this is a login / captcha / 2FA wall. Be "
"specific and brief; do not restate the goal."
)
def extract_text(response) -> str:
"""Pull the text out of an Anthropic-style response object."""
parts = []
for block in getattr(response, "content", None) or []:
if getattr(block, "type", None) == "text" and getattr(block, "text", ""):
parts.append(block.text.strip())
return " ".join(p for p in parts if p).strip()
async def adjudicate_stuck(
client, model: str, goal: str, recent_actions: str, page_text: str,
) -> str:
"""One cheap aux call returning concrete guidance, or "" on any failure."""
prompt = P_ADJUDICATION_PROMPT.format(
goal=(goal or "(unknown)")[:400],
recent=(recent_actions or "(none)")[:1200],
page=(page_text or "(empty)")[:1500],
)
try:
response = await client.messages.create(
model=model,
max_tokens=300,
messages=[{"role": "user", "content": prompt}],
)
except Exception as e:
logger.warning(f"[browser-validator] adjudication call failed: {e}")
return ""
return extract_text(response)