Tool Calling Dialect System in TinyAgents: How Dispatcher.rs Bridges LLM Syntax and Core Execution
The OpenHuman framework implements a dialect-driven tool-calling architecture that decouples LLM interaction syntax from core execution logic through the dispatcher.rs seam, enabling support for multiple dialects like XML, PFormat, and Native without modifying tool implementations or security policies.
The tinyhumansai/openhuman repository provides a sophisticated tool calling dialect system designed to standardize how large language models (LLMs) invoke capabilities while maintaining strict security boundaries. This architecture isolates textual representations of tool calls from their execution, allowing the system to swap dialects—from XML to Pretty-Format—without altering the underlying Tool trait implementations or policy enforcement code.
Architecture of the Tool Calling Dialect System
The system separates three distinct concerns to maintain loose coupling between the LLM interface and the execution core:
-
Tool Vocabulary & Dialects: Defined in
tinyagents::harness::tool_calling::dialect(e.g.,XmlDialect,PFormatDialect,NativeDialect). These modules determine how the LLM perceives available tools and how tool call arguments are parsed from raw text into structuredParsedToolCallobjects. -
Tool Execution & Policy: Located in
src/openhuman/tools/*, this layer handles security policy validation, approval-gate checks, sandbox enforcement, and per-call timeouts. All security-sensitive logic remains centralized here, independent of dialect syntax. -
Seam & Conversion: The
src/openhuman/agent/dispatcher.rsfile serves as the single integration point between dialects and core tools. It mapsParsedToolCallinstances toToolExecutionResultobjects and translates the persistentConversationMessageformat (stored as JSONL) to the dialect-specificTranscriptEntryrepresentation.
The Five-Stage Dialect Execution Pipeline
According to the source code in dispatcher.rs, every tool invocation follows a strict pipeline that preserves dialect flexibility while enforcing security:
-
Prompt Generation: The core constructs a prompt containing a tool catalogue (
ToolsSection). The active dialect renders this catalogue via methods likerender_pformat_catalogue, emitting signatures such asCall as: my_tool(arg1="value"). -
LLM Response: The model returns text matching the chosen dialect's syntax, whether XML tags, pretty-format function calls, or native structured output.
-
Parsing: The dialect parses the response into a
ParsedToolCallstruct, extracting the tool name and argument map using dialect-specific parsers found invendor/tinyagents/src/harness/tool_calling/dialect/. -
Dispatch: The
dispatcher.rsseam receives theParsedToolCalland lookups the correspondingToolimplementation from the registry. It constructs aToolExecutionContextcontaining workspace handles, thread IDs, and budget constraints before invoking the tool. -
Result Handling: The tool returns a
ToolResult, whichdispatcher.rsconverts into aConversationMessagefor persistent storage in the transcript, completing the cycle.
Inside Dispatcher.rs: The Conversion Seam
The src/openhuman/agent/dispatcher.rs file is explicitly designed as the only location where LLM-level dialect representations meet core-level tool abstractions. This design ensures that dialect changes never penetrate security-critical code.
When dispatching, the seam performs three critical conversions:
-
Input Translation: Transforms the dialect's
ParsedToolCallinto the core'sToolExecutionContext, injecting runtime dependencies like sandbox configurations. -
Policy Enforcement: Applies approval gates and timeout policies defined in
src/openhuman/tools/ops.rsbefore execution begins, regardless of which dialect initiated the call. -
Output Translation: Converts raw
ToolResultdata intoConversationMessageobjects that maintain the persistent conversation state, while optionally formatting user-facing output through the dialect's transcript renderer.
This architecture means you can switch from XmlDialect to NativeDialect simply by changing the dialect configuration, without touching dispatcher.rs logic or any individual tool implementation.
Practical Implementation: Registering and Dispatching a Tool
Below is a complete workflow demonstrating how to define a tool, register it for dialect access, and process calls through the dispatcher seam.
// 1. Define a tool implementation (src/openhuman/tools/impl/my_tool.rs)
pub struct MyTool;
#[async_trait::async_trait]
impl tinytools::Tool for MyTool {
fn name(&self) -> &'static str { "my_tool" }
async fn run(
&self,
ctx: &dyn tinytools::ToolRunContext,
args: tinytools::ToolArgs,
) -> tinytools::ToolResult {
// Core execution logic respects sandbox, timeouts, and policies
Ok(tinytools::ToolResult::content("Tool executed successfully"))
}
}
// 2. Register the tool (src/openhuman/tools/ops.rs)
pub fn register_my_tool(reg: &mut tinytools::ToolRegistry) {
reg.register(Box::new(MyTool));
}
When the LLM generates a response using the PFormat dialect, the pipeline processes it as follows:
// 3. LLM response in PFormat dialect
let llm_response = r#"Call as: my_tool(message="hello world")"#;
// 4. Parse using the dialect (provided by tinyagents)
let parsed = tinyagents::harness::tool_calling::dialect::PFormatDialect::parse(
llm_response,
)?; // Returns ParsedToolCall { name: "my_tool", args: { "message": "hello world" } }
// 5. Dispatch through the seam (src/openhuman/agent/dispatcher.rs)
let result = dispatcher::dispatch(parsed, &core_context).await?;
// Result becomes a ConversationMessage stored in the persistent transcript
Summary
- Loose Coupling: Dialects can evolve to support new LLM output formats without requiring changes to tool implementations in
src/openhuman/tools/. - Centralized Security: All policy enforcement, including approval gates and sandboxing, occurs in the core tool layer before execution, independent of the dialect used to parse the request.
- Single Integration Point: The
dispatcher.rsseam is the exclusive bridge betweenParsedToolCalldialect objects and coreTooltrait executions, ensuring architectural integrity. - Extensibility: Adding tools requires only registration in
src/openhuman/tools/ops.rs; the dispatcher automatically handles conversion for all supported dialects including XML, PFormat, and Native.
Frequently Asked Questions
What is a tool calling dialect?
A tool calling dialect is a standardized syntax for how an LLM expresses its intention to invoke a tool. In the OpenHuman framework, dialects like XmlDialect, PFormatDialect, and NativeDialect live in tinyagents::harness::tool_calling::dialect and handle both the rendering of tool catalogues for prompts and the parsing of LLM responses into structured ParsedToolCall objects.
How does dispatcher.rs maintain security across different dialects?
The src/openhuman/agent/dispatcher.rs file acts as a sanitization boundary. While dialects handle raw text parsing, dispatcher.rs validates the resulting ParsedToolCall against the ToolRegistry, constructs a ToolExecutionContext with appropriate sandbox and budget constraints, and enforces approval policies defined in the core tool layer before any code executes.
Can I add a custom dialect without modifying existing tools?
Yes. New dialects implement the dialect trait (parsing text into ParsedToolCall and rendering catalogues) and reside in the tinyagents crate. Because dispatcher.rs only operates on the standardized ParsedToolCall struct, existing tools in src/openhuman/tools/ require no changes to support new dialect syntax.
What distinguishes the Native dialect from XML or PFormat?
According to the source in vendor/tinyagents/src/harness/tool_calling/dialect/native.rs, the Native dialect uses representations closest to Rust's type system, often leveraging structured serialization that maps directly to internal structs. In contrast, XML uses tag-based markup and PFormat (Pretty-Format) uses human-readable function call syntax like tool_name(arg="value"), making the latter more suitable for frontier LLMs trained on code-like outputs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →