From 4d2af263d32da9480b47ff2cd971471f2cd00310 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Sat, 13 Jun 2026 13:36:08 -0400 Subject: [PATCH 1/2] Route IWC workflow imports to the tool recommendation agent The router had no home for "import an IWC workflow" -- there's no import handoff, and import_workflow_from_iwc only exists on the operations/MCP side, not as an in-app tool. So the request scattered: on gpt-oss-120b (the demo/test model) "import a histological staining workflow from IWC" landed on the plain router 4 times out of 5 and answered with a how-to essay instead of surfacing anything importable. tool_recommendation already searches the IWC catalog and emits a WORKFLOW_IMPORT action for what it finds, so point the router there for IWC import asks via both the prompt and the handoff docstring -- and widen the handoff summary/arg, which still described it as tools-only. Also fixes the stale routing eval case that expected router. With this the imperative "import..." prompts route to tool_recommendation 5/5. --- lib/galaxy/agents/prompts/router.md | 2 ++ lib/galaxy/agents/router.py | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/galaxy/agents/prompts/router.md b/lib/galaxy/agents/prompts/router.md index d4f4d7ee518..8e46a28dce6 100644 --- a/lib/galaxy/agents/prompts/router.md +++ b/lib/galaxy/agents/prompts/router.md @@ -99,6 +99,7 @@ English. If the request really wants analysis (e.g. "summarize my history", - Wants to find/discover tools ("Is there a tool that converts BAM to FASTQ?") - Needs help choosing between tools for an analysis type - Asks "what tools are available for X?" +- Asks to import or find a workflow from IWC for an analysis ("import an IWC workflow for X", "is there an IWC workflow for X I can import?") -- it searches the IWC catalog and surfaces an import action; there is no separate import handoff **Use `hand_off_to_error_analysis`** when user PROVIDES specific error details: @@ -158,6 +159,7 @@ Key pattern: If user needs to FIND something (job, dataset, history) before anal - "What tool should I use for X?" → Use hand_off_to_tool_recommendation - "Is there a tool that does X?" → Use hand_off_to_tool_recommendation +- "Import an IWC workflow for X" → Use hand_off_to_tool_recommendation (surfaces the workflow + import action) - "How do I use tool X?" → Answer directly (usage help) - "What parameters does X need?" → Answer directly (usage help) - "Create a tool that does X" → Use hand_off_to_custom_tool diff --git a/lib/galaxy/agents/router.py b/lib/galaxy/agents/router.py index 245337d38dc..5fde7c300fb 100644 --- a/lib/galaxy/agents/router.py +++ b/lib/galaxy/agents/router.py @@ -5,7 +5,7 @@ Uses pydantic-ai output functions to either: - Answer general Galaxy questions directly (returns str) - Hand off to error_analysis for job debugging - Hand off to custom_tool for explicit tool creation requests -- Hand off to tool_recommendation for tool discovery +- Hand off to tool_recommendation for tool and IWC workflow discovery - Hand off to gtn_training for tutorial and learning requests Also exposes a small set of @agent.tool fast-path tools for read-only browsing @@ -330,13 +330,14 @@ class QueryRouterAgent(BaseGalaxyAgent): ctx: RunContext[GalaxyAgentDependencies], query: str, ) -> str: - """Route to tool recommendation agent for finding Galaxy tools. + """Route to tool recommendation agent for finding Galaxy tools and IWC workflows. Use this when the user: - Asks what tool to use for a task ("what tool aligns reads?") - Wants to find tools for a specific analysis type - Needs help discovering available tools - Asks "is there a tool that does X?" + - Asks to import or find a workflow from IWC for an analysis (it searches IWC and surfaces an import action) Do NOT use for: - How to USE a specific tool (answer directly) @@ -344,7 +345,7 @@ class QueryRouterAgent(BaseGalaxyAgent): - Job errors (use hand_off_to_error_analysis) Args: - query: The tool discovery question + query: The tool or workflow discovery question """ return await self._execute_handoff(ctx, AgentType.TOOL_RECOMMENDATION, query) From 1308690b33b4aeb6bb833c3a5c482f80ec906703 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Sat, 13 Jun 2026 13:36:09 -0400 Subject: [PATCH 2/2] Add IWC routing regression cases Pin the routing for IWC-related asks so a model or prompt change that breaks it gets caught. Both phrasings -- the imperative "import a workflow from IWC" and the "recommend an IWC workflow for X" discovery ask -- should route to tool_recommendation, which owns IWC search and surfaces an importable workflow plus an import action. Flips the existing import_iwc_workflow expectation to match and adds a recommendation-phrasing case. --- test/evals/datasets/routing.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/evals/datasets/routing.py b/test/evals/datasets/routing.py index 4484d27f146..455902f0110 100644 --- a/test/evals/datasets/routing.py +++ b/test/evals/datasets/routing.py @@ -194,8 +194,8 @@ ROUTING_CASES: list[Case[str, str, dict[str, Any]]] = [ _case( "import_iwc_workflow", "Import a histological staining workflow from IWC.", - "router", - "Router-direct action. On agent-ops-iwc-reintroduce this triggers search_iwc_workflows + import_workflow_from_iwc tool calls.", + "tool_recommendation", + "tool_recommendation owns IWC search (search_iwc_workflows) and emits a WORKFLOW_IMPORT action for the surfaced workflow; no in-app agent calls import_workflow_from_iwc directly.", ), _case( "omero_upload_guidance", @@ -223,6 +223,12 @@ ROUTING_CASES: list[Case[str, str, dict[str, Any]]] = [ "custom_tool", "Explicit custom_tool request -- writing a tool wrapper, not running an existing one.", ), + _case( + "recommend_iwc_workflow", + "Recommend an IWC workflow for histological staining quantification.", + "tool_recommendation", + "Recommendation-style IWC ask -- discovery phrasing routes to tool_recommendation, which searches IWC and surfaces an importable workflow.", + ), ]