refactor(browser_exec): schema diet 803->663 tok/call, A/B-gated; eval harness runs on Windows + Nous auth (#96300)
This commit is contained in:
@@ -52,7 +52,13 @@ if os.path.exists(args.results):
|
||||
|
||||
def reset_browser_state():
|
||||
"""Kill lingering drivers and clear cookies between cells."""
|
||||
subprocess.run(["pkill", "-f", "agent-browser"], capture_output=True)
|
||||
if sys.platform == "win32":
|
||||
subprocess.run(
|
||||
["taskkill", "/F", "/IM", "agent-browser.exe", "/T"],
|
||||
capture_output=True,
|
||||
)
|
||||
else:
|
||||
subprocess.run(["pkill", "-f", "agent-browser"], capture_output=True)
|
||||
code = "cdp('Network.clearBrowserCookies')\nprint('cleared')\n"
|
||||
try:
|
||||
subprocess.run(
|
||||
|
||||
@@ -77,7 +77,9 @@ logging.disable(logging.CRITICAL)
|
||||
|
||||
import run_agent # noqa: E402
|
||||
|
||||
assert run_agent.__file__.startswith(WT), f"wrong tree: {run_agent.__file__}"
|
||||
_loaded = os.path.normcase(os.path.normpath(run_agent.__file__))
|
||||
_want = os.path.normcase(os.path.normpath(WT))
|
||||
assert _loaded.startswith(_want), f"wrong tree: {run_agent.__file__}"
|
||||
|
||||
if ARM == "prns":
|
||||
# Strip the helpers digest from the schema: header-only description.
|
||||
@@ -89,10 +91,30 @@ if ARM == "prns":
|
||||
|
||||
from run_agent import AIAgent # noqa: E402
|
||||
|
||||
# Provider resolution: default openrouter (original battery), but allow the
|
||||
# Nous-subscription path on boxes without an OpenRouter key. Credentials are
|
||||
# resolved through the product's own auth state, never printed.
|
||||
_or_key = os.environ.get("OPENROUTER_API_KEY", "").strip()
|
||||
if _or_key:
|
||||
_agent_auth = dict(
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_key=_or_key,
|
||||
provider="openrouter",
|
||||
)
|
||||
else:
|
||||
# Resolved by the orchestrator BEFORE HERMES_HOME is redirected to the
|
||||
# throwaway home (auth state lives in the real profile). Never printed.
|
||||
_tok = os.environ.get("BUBENCH_NOUS_TOKEN", "").strip()
|
||||
if not _tok:
|
||||
raise SystemExit("no OPENROUTER_API_KEY and no Nous auth available")
|
||||
_agent_auth = dict(
|
||||
base_url=os.environ.get("BUBENCH_NOUS_BASE_URL", "https://inference-api.nousresearch.com/v1"),
|
||||
api_key=_tok,
|
||||
provider="nous",
|
||||
)
|
||||
|
||||
agent = AIAgent(
|
||||
base_url="https://openrouter.ai/api/v1",
|
||||
api_key=os.environ["OPENROUTER_API_KEY"],
|
||||
provider="openrouter",
|
||||
**_agent_auth,
|
||||
model=MODEL,
|
||||
max_iterations=30,
|
||||
quiet_mode=True,
|
||||
|
||||
@@ -822,32 +822,25 @@ def browser_exec(
|
||||
|
||||
# The tool description is the CLI's skill, fetched from browser-use skill
|
||||
_HEADER_BASE = (
|
||||
"Drive a real web browser via the Browser Use CLI. The `code` argument "
|
||||
"is piped verbatim to the `browser-use` CLI on stdin and executed as "
|
||||
"full Python (standard library available) with the CLI's pre-imported "
|
||||
"browser helpers; stdout comes back in the result. Start `code` with a "
|
||||
"one-line comment describing the step for the user in plain, "
|
||||
"non-technical language, max 60 chars (e.g. `# Searching Amazon for "
|
||||
"paper towels`) — the UI displays it as the step label.\n\n"
|
||||
"STATE: the browser session and the workspace persist across calls; "
|
||||
"Python variables do NOT (each call is a fresh interpreter). The "
|
||||
"workspace is a stable directory — path in $BH_AGENT_WORKSPACE and "
|
||||
"returned as `workspace` in every result. For multi-item tasks "
|
||||
"('collect all N products / every entry / the full table'), append each "
|
||||
"batch to a JSON/CSV file in the workspace as you go, then read it back "
|
||||
"to assemble the final answer; define reusable functions in "
|
||||
"agent_helpers.py there — the harness auto-imports it into every call. "
|
||||
"Do aggregation in code, not in your head: dedupe, count, sort, and "
|
||||
"format with Python inside the exec. Before giving a final answer on a "
|
||||
"multi-item task, verify the collected count against what was asked "
|
||||
"and go back for anything missing.\n\n"
|
||||
"Drive a real web browser via the Browser Use CLI: `code` runs as full "
|
||||
"Python (stdlib available) with pre-imported browser helpers; stdout "
|
||||
"comes back in the result. Start `code` with a one-line comment "
|
||||
"describing the step for the user in plain language, max 60 chars "
|
||||
"(e.g. `# Searching Amazon for paper towels`) — the UI shows it as the "
|
||||
"step label.\n\n"
|
||||
"STATE: the browser session and workspace persist across calls; Python "
|
||||
"variables do NOT (fresh interpreter each call). The workspace dir is "
|
||||
"$BH_AGENT_WORKSPACE (also `workspace` in every result); functions "
|
||||
"defined in agent_helpers.py there are auto-imported into every call. "
|
||||
"For multi-item tasks ('all N products / every entry'), append each "
|
||||
"batch to a JSON/CSV file in the workspace, then read it back and "
|
||||
"aggregate in code — dedupe/count/sort with Python, not in your head — "
|
||||
"and verify the collected count against what was asked before "
|
||||
"answering.\n\n"
|
||||
"Batch each sub-procedure (navigate, wait, extract, act) into one call "
|
||||
"— do not spend a call per action — but for long extractions prefer "
|
||||
"several medium calls that append to workspace files over one giant "
|
||||
"call, so progress survives timeouts. For an isolated concurrent "
|
||||
"browser session (parallel tasks that must not share tabs), pass "
|
||||
"session=<name> (never BU_NAME env syntax) and reuse the same name on "
|
||||
"every related call."
|
||||
"call, so progress survives timeouts."
|
||||
)
|
||||
|
||||
_HEADER_VISION = (
|
||||
@@ -967,7 +960,7 @@ BROWSER_EXEC_SCHEMA = {
|
||||
},
|
||||
"session": {
|
||||
"type": "string",
|
||||
"description": "Named isolated browser session (sets BU_NAME): each name gets its own harness daemon — and on cloud backends its own browser — so concurrent tasks don't clobber each other. Omit for the shared default session. Reuse the same name across calls to keep working in that session (and the name passed to start_remote_daemon(), if used).",
|
||||
"description": "Named isolated browser session — its own daemon and (on cloud backends) own browser, so concurrent tasks don't share tabs. Reuse the same name on every related call; omit for the shared default session.",
|
||||
},
|
||||
"timeout_s": {
|
||||
"type": "integer",
|
||||
|
||||
Reference in New Issue
Block a user