Tool Router & Execution
This document covers the tool router service and tool execution subsystem, which processes LLM tool calls and routes them to the appropriate execution handlers. The tool router handles built-in tools, Composio integrations, deduplication, and result formatting for both chat and recipe execution contexts.
For information about how tools are resolved and selected for LLM context, see Tool Resolution Strategies. For details on Composio-specific integration patterns, see Composio Integration.
Architecture Overview
The tool router acts as a central dispatcher that receives tool call requests from LLM responses and routes them to appropriate executors. It provides deduplication, tracking, and consistent result formatting across all tool types.
Sources:
Tool Router Service
Core Interface
The tool router is accessed via get_tool_router() which returns a singleton instance:
The execute_and_format() method is the primary interface for tool execution:
tool_name
str
Name of the tool to execute (e.g. "read_file", "JIRA_GET_ISSUE")
tool_args
Dict[str, Any]
Tool arguments as parsed from LLM response
agent_id
int
Agent ID for permission checks and tracking
workspace_id
UUID
Workspace ID for multi-tenancy and Composio entity resolution
original_intent
str
Original user prompt for context-aware error messages
Returns a dictionary with:
llm_context: Formatted string result for LLM message historystatus: Execution status ("success"or"error")data: Structured result data (optional)
Sources:
Tool Execution Strategies
The tool router supports multiple execution strategies, automatically selected based on the tool name:
Strategy Selection Logic
Direct Composio Action Execution (Primary Path)
When SDK search returns per-action tools (see Tool Resolution Strategies), the recipe executor and chat service use direct execution to bypass the composio_execute mega-tool and its parameter mapping overhead:
The deduplication cache in recipes prevents identical calls within the same execution:
Key Implementation Details:
Cache key:
f"{tool_name}|{json.dumps(tool_args, sort_keys=True, default=str)}"Cache scope: Per-execution (not persistent)
Cache hit: Returns cached result, logs
"Composio dedup hit"Applies to: All Composio actions in recipe execution
Sources:
Built-in Tools
Built-in tools (e.g., read_file, search_knowledge, query_database) are registered in the tool registry and executed directly:
File Operations
read_file, write_file, list_directory
Tool executor modules
RAG/Search
search_knowledge, semantic_search, search_codebase
RAG service
Database
query_database, smart_query_database
NL2SQL module
Scratchpad
scratchpad_write
Inline handler (recipes only)
Sources:
Scratchpad Tool (Recipe-Specific)
The scratchpad_write tool is handled inline during recipe execution, bypassing the tool router entirely:
This tool allows agents to explicitly export key-value pairs to the recipe scratchpad for downstream steps. See Recipe Scratchpad for details.
Sources:
Deduplication & Loop Prevention
The ToolExecutionTracker class implements three-tier deduplication to prevent infinite tool loops:
Deduplication Strategies
Implementation Details
Per-Tool Retry Limits:
composio_execute
2
Expensive API calls; fail fast
Search tools (search_knowledge, semantic_search, etc.)
2
Prevent query loops
query_database, smart_query_database
2
Database load protection
File operations (read_file, write_file)
3 or 2
Moderate retry tolerance
Default (unlisted tools)
3
Conservative limit
Exact Deduplication:
Uses MD5 hash of JSON-serialized arguments (sorted keys) to detect identical calls:
Semantic Deduplication (Search Tools Only):
For search tools (search_knowledge, semantic_search, search_codebase, etc.), compares normalized query strings using SequenceMatcher with a 0.75 similarity threshold:
This prevents scenarios like:
search_knowledge("Python async patterns")search_knowledge("python async patterns")← Blocked as similarsearch_knowledge("async patterns in python")← Blocked if ratio ≥ 0.75
Sources:
Execution Context Differences
Tool execution behaves differently in chat vs. recipe contexts due to different requirements:
Chat Context
Characteristics:
Tool list filtered to top 25 relevant tools using
rank_tools_for_query()ToolExecutionTrackerscoped to single conversation turnAll tool execution via
tool_router.execute_and_format()Composio tools use mega-tool pattern or per-action tools based on SDK search results
Tool results inserted into message history with
role: "tool"
Sources:
Recipe Context
Characteristics:
No tool filtering (recipe steps are curated)
Direct Composio action execution when SDK search returns per-action tools
Per-execution deduplication cache (action+args hash)
tool_routerused only for built-in toolsscratchpad_writehandled inline (no router)No persistent
ToolExecutionTracker(single-step execution)
Sources:
Result Formatting
The tool router formats all tool execution results into a consistent llm_context string suitable for LLM message history:
Standard Format
Example Formatting
Success with structured data:
Error:
Truncation:
Applied to final
llm_contextstringLimit: 8000 characters for message history, 4000 for tool result storage
Prevents context window overflow
Sources:
Integration Points
Chat Service Integration
The chat service uses the tool router within a tool execution loop, processing multiple tool calls per LLM response:
Sources:
Recipe Executor Integration
Recipe execution uses selective tool router integration, preferring direct Composio execution when per-action tools are available:
Key Difference from Chat:
No persistent
ToolExecutionTracker(steps execute independently)Per-execution deduplication cache (action+args hash)
Inline
scratchpad_writehandlingDirect Composio execution when SDK search succeeds
Sources:
Code Entity Reference
Primary Classes and Functions
get_tool_router()
consumers/chatbot/tool_router.py
Factory function returning singleton ToolRouter instance
ToolRouter.execute_and_format()
consumers/chatbot/tool_router.py
Primary tool execution interface
ToolExecutionTracker
consumers/chatbot/service.py:88-186
Deduplication and loop prevention for chat
ComposioToolService.execute_action()
modules/tools/services/composio_tool_service.py:193-212
Direct Composio action execution
get_chat_tools()
consumers/chatbot/tool_router.py
Returns available tool schemas for chat context
handle_scratchpad_write()
modules/tools/builtin/scratchpad_tool.py
Inline handler for scratchpad exports in recipes
Tool Execution Sites
Chat tool loop
consumers/chatbot/service.py
640-850
Uses ToolExecutionTracker + tool_router
Recipe tool loop
api/recipe_executor.py
206-332
Direct Composio + selective router use
Direct Composio exec
api/recipe_executor.py
256-312
Bypasses router for per-action tools
Built-in tools
Various tool modules
-
Registered in tool registry
Sources:
Last updated

