mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[eric] web-search: gate the browser-fallback nudge on browser-delegation availability
This commit is contained in:
@@ -132,7 +132,8 @@ class RunOptions(AgentManagerProtocol):
|
||||
connection_mode=getattr(global_settings, "connection_mode", "own_key"),
|
||||
)
|
||||
if need_web_mcp:
|
||||
register_web_mcp_server(mcp_servers, p_m)
|
||||
# browser_ok gates the search-dead fallback nudge: never tell the model to call CreateBrowserAgent in a session where browser delegation is denied.
|
||||
register_web_mcp_server(mcp_servers, p_m, browser_ok=bool(browser_delegation_tools))
|
||||
|
||||
effective_allowed, effective_disallowed = build_effective_tool_lists(
|
||||
session, mcp_servers, builtin_perms, need_web_mcp,
|
||||
|
||||
@@ -92,7 +92,7 @@ def set_framework_overhead(session: AgentSession, composed_prompt: Optional[str]
|
||||
|
||||
|
||||
@typechecked
|
||||
def register_web_mcp_server(mcp_servers: Dict, p_m: str) -> None:
|
||||
def register_web_mcp_server(mcp_servers: Dict, p_m: str, browser_ok: bool = False) -> None:
|
||||
"""Register the DDG-backed openswarm-web stdio MCP into the server set when the primary has no
|
||||
reliable native web path. The server script lives in the agents package (not here), so resolve
|
||||
it off that package dir, not __file__."""
|
||||
@@ -115,6 +115,7 @@ def register_web_mcp_server(mcp_servers: Dict, p_m: str) -> None:
|
||||
"OPENSWARM_PORT": os.environ.get("OPENSWARM_PORT", "8324"),
|
||||
"OPENSWARM_AUTH_TOKEN": p_get_auth_token3(),
|
||||
"OPENSWARM_PRIMARY_API": p_primary_hint,
|
||||
"OPENSWARM_BROWSER_OK": "1" if browser_ok else "0",
|
||||
},
|
||||
"type": "stdio",
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ FETCH_URL = f"http://127.0.0.1:{BACKEND_PORT}/api/web/fetch"
|
||||
|
||||
# Primary-provider hint from agent_manager; backend picks the native search tool (googleSearch/web_search_preview) so searches use the user's existing budget.
|
||||
PRIMARY_HINT = os.environ.get("OPENSWARM_PRIMARY_API", "") or None
|
||||
# Whether this session actually has browser-delegation tools; gates the backend's "fall back to the browser" nudge.
|
||||
BROWSER_OK = os.environ.get("OPENSWARM_BROWSER_OK", "0") == "1"
|
||||
|
||||
TOOLS = [
|
||||
{
|
||||
@@ -104,7 +106,7 @@ def handle_tool_call(tool_name: str, arguments: dict) -> dict:
|
||||
return {"content": [{"type": "text", "text": "Error: query is required"}], "isError": True}
|
||||
num = int(arguments.get("num_results", 5))
|
||||
num = max(1, min(num, 10))
|
||||
body = {"query": query, "num_results": num}
|
||||
body = {"query": query, "num_results": num, "browser_ok": BROWSER_OK}
|
||||
if PRIMARY_HINT:
|
||||
body["primary"] = PRIMARY_HINT
|
||||
r = p_post(SEARCH_URL, body, timeout=45.0)
|
||||
|
||||
@@ -36,6 +36,8 @@ class SearchBody(BaseModel):
|
||||
num_results: int = Field(5, ge=1, le=10, description="Max results to return.")
|
||||
# Hint from the MCP server about which primary provider the session is using. Lets us route to that provider's native search tool (Gemini googleSearch, OpenAI web_search_preview) when available, costs come out of the user's existing primary budget.
|
||||
primary: str | None = Field(None, description="Primary provider hint: 'gemini' | 'openai' | 'anthropic' | None")
|
||||
# Set by the openswarm-web shim from OPENSWARM_BROWSER_OK; the browser-fallback nudge must never fire in a session without browser-delegation tools.
|
||||
browser_ok: bool = Field(False, description="Whether this session has browser-delegation tools available.")
|
||||
|
||||
|
||||
class FetchBody(BaseModel):
|
||||
@@ -477,10 +479,11 @@ async def search(body: SearchBody) -> dict:
|
||||
"DuckDuckGo is rate-limiting this network and every configured provider "
|
||||
"errored (see details below)."
|
||||
)
|
||||
nudge = p_browser_fallback_nudge(body.query)
|
||||
nudge = p_browser_fallback_nudge(body.query) if body.browser_ok else ""
|
||||
p_results_text = f"No results for: {body.query}\n\n{tail}" + (f"\n\n{nudge}" if nudge else "")
|
||||
return {
|
||||
"query": body.query,
|
||||
"results": f"No results for: {body.query}\n\n{tail}\n\n{nudge}",
|
||||
"results": p_results_text,
|
||||
"backend": "none",
|
||||
"cascade_errors": errors,
|
||||
}
|
||||
|
||||
@@ -138,12 +138,28 @@ async def test_everything_fails_nudges_browser_not_retry(monkeypatch):
|
||||
raise RuntimeError("openai down")
|
||||
monkeypatch.setattr(W, "p_openai_websearch", p_openai_boom)
|
||||
|
||||
res = await search(SearchBody(query="sony zv-e10 price"))
|
||||
res = await search(SearchBody(query="sony zv-e10 price", browser_ok=True))
|
||||
assert res["backend"] == "none"
|
||||
assert "CreateBrowserAgent" in res["results"]
|
||||
assert "retry" not in res["results"].lower()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_nudge_suppressed_when_browser_denied(monkeypatch):
|
||||
# A session without browser-delegation tools must never be told to call CreateBrowserAgent.
|
||||
p_ddg_throttled(monkeypatch)
|
||||
monkeypatch.setattr(W, "p_resolve_openai_api_key", lambda: "okey")
|
||||
|
||||
async def p_openai_boom(*a, **k):
|
||||
raise RuntimeError("openai down")
|
||||
monkeypatch.setattr(W, "p_openai_websearch", p_openai_boom)
|
||||
|
||||
res = await search(SearchBody(query="sony zv-e10 price"))
|
||||
assert res["backend"] == "none"
|
||||
assert "CreateBrowserAgent" not in res["results"]
|
||||
assert "retry" not in res["results"].lower()
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------- /fetch mirrors /search: local httpx + trafilatura is the fast path, grounded fetchers are the fallback for JS/paywalled pages, every attempt is bounded. --------------------------------------------------------------------------
|
||||
|
||||
from backend.apps.web.web import fetch, FetchBody
|
||||
|
||||
Reference in New Issue
Block a user