From f2bc65b204c4a26d122783c73b43826af5e37f0c Mon Sep 17 00:00:00 2001 From: ciregenz Date: Mon, 20 Jul 2026 13:23:05 -0700 Subject: [PATCH] [eric] agents: web results carry a render-with-ShowUI reminder when openswarm-ui is live (system-prompt nudge alone loses to the prose prior; live-proven haiku weather widget) --- backend/apps/agents/manager/run/RunOptions.py | 7 ++++++- .../apps/agents/manager/run/run_options_helpers.py | 3 ++- backend/apps/agents/web_mcp_server.py | 13 +++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/backend/apps/agents/manager/run/RunOptions.py b/backend/apps/agents/manager/run/RunOptions.py index e58e72e8..08ce61d5 100644 --- a/backend/apps/agents/manager/run/RunOptions.py +++ b/backend/apps/agents/manager/run/RunOptions.py @@ -133,7 +133,12 @@ class RunOptions(AgentManagerProtocol): ) if need_web_mcp: # 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)) + # rich_ui_ok plants the render-as-component reminder inside web results: the system-prompt nudge alone loses to the prose prior (live-proven on haiku). + register_web_mcp_server( + mcp_servers, p_m, + browser_ok=bool(browser_delegation_tools), + rich_ui_ok="openswarm-ui" in mcp_servers, + ) effective_allowed, effective_disallowed = build_effective_tool_lists( session, mcp_servers, builtin_perms, need_web_mcp, diff --git a/backend/apps/agents/manager/run/run_options_helpers.py b/backend/apps/agents/manager/run/run_options_helpers.py index a471a974..4ebadbe1 100644 --- a/backend/apps/agents/manager/run/run_options_helpers.py +++ b/backend/apps/agents/manager/run/run_options_helpers.py @@ -101,7 +101,7 @@ def set_framework_overhead(session: AgentSession, composed_prompt: Optional[str] @typechecked -def register_web_mcp_server(mcp_servers: Dict, p_m: str, browser_ok: bool = False) -> None: +def register_web_mcp_server(mcp_servers: Dict, p_m: str, browser_ok: bool = False, rich_ui_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__.""" @@ -125,6 +125,7 @@ def register_web_mcp_server(mcp_servers: Dict, p_m: str, browser_ok: bool = Fals "OPENSWARM_AUTH_TOKEN": p_get_auth_token3(), "OPENSWARM_PRIMARY_API": p_primary_hint, "OPENSWARM_BROWSER_OK": "1" if browser_ok else "0", + "OPENSWARM_RICH_UI_OK": "1" if rich_ui_ok else "0", }, "type": "stdio", } diff --git a/backend/apps/agents/web_mcp_server.py b/backend/apps/agents/web_mcp_server.py index dada7086..973550ad 100755 --- a/backend/apps/agents/web_mcp_server.py +++ b/backend/apps/agents/web_mcp_server.py @@ -16,6 +16,15 @@ FETCH_URL = f"http://127.0.0.1:{BACKEND_PORT}/api/web/fetch" 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" +# Whether the openswarm-ui server is live this session. The render-as-component reminder rides the +# tool RESULT because that's what the model reads right before answering; the system-prompt nudge +# alone loses to the prose prior (live-proven on haiku). +RICH_UI_OK = os.environ.get("OPENSWARM_RICH_UI_OK", "0") == "1" +RICH_UI_HINT = ( + "\n\n[presentation] When you answer the user with this data, render it with the ShowUI tool " + "(weather for forecasts, data-table for rows, stats-display for metrics, links for sources, " + "chart for series) and keep prose to one line. Answer in plain text only if no component fits." +) TOOLS = [ { @@ -115,6 +124,8 @@ def handle_tool_call(tool_name: str, arguments: dict) -> dict: results = r.get("results", "") if not results: results = f"No results for: {query}" + elif RICH_UI_OK: + results += RICH_UI_HINT return {"content": [{"type": "text", "text": results}]} if tool_name == "WebFetch": @@ -135,6 +146,8 @@ def handle_tool_call(tool_name: str, arguments: dict) -> dict: content = r.get("content", "") if not content: content = f"No content returned from {url}" + elif RICH_UI_OK: + content += RICH_UI_HINT return {"content": [{"type": "text", "text": content}]} return {"content": [{"type": "text", "text": f"Unknown tool: {tool_name}"}], "isError": True}