Tool Call¶
The Tool Call node uses language-model inference to detect whether the model wants to call an external function, given a set of tool definitions. It does not execute the tool — the server is agnostic to the tool's implementation; the detected call is surfaced to the client, which runs the tool and feeds the result back into the next turn.
This detection-only contract makes the node safe to run on-device with no special sandboxing: the server never touches the client's tool implementation.
NodeType: ToolCall.
Parameters¶
| Param | Type | Default | Range | Structural | Description |
|---|---|---|---|---|---|
model_name |
Optional[str] | inherit model default | — | ✓ | Model catalog name. Empty = use the agent's default_model_name. |
system_prompt |
Optional[str] (multiline) | inherit model default | — | — | Prepended before the user turn during projection. |
tool_call_format |
Optional[str] | inherit model default | — | ✓ | Tool-call dialect used for both prompt construction and output parsing. Structural because the format config is baked into the projection strategy. |
tools |
Optional[Any] | inherit model default | — | ✓ | Callable tool definitions. Structural because the tool-call prompt schema and parser contract are materialised at construction. |
generate_on_no_tool |
bool | False | — | — | When no tool call is detected in the response, generate a plain text reply. |
notify_client |
bool | False | — | — | When true, emit an OnNodeEvent("tool_call", …) for each tool call parsed. |
sampling |
Optional[Any] | inherit model default | — | — | Sparse sampling overrides. |
Exits¶
Each exit is a structural string field on the node's params; its value names the target node (empty = END).
| Exit | Param field | Description |
|---|---|---|
tool_called |
tool_called_exit |
Exit taken when one or more tool calls are parsed from the response. Empty string = END. |
no_tool_called |
no_tool_called_exit |
Exit taken when no tool call is parsed (or when generate_on_no_tool produced a plain text reply). Empty string = END. |
Exit routes¶
| Route | Fires when |
|---|---|
tool_called |
At least one tool call was detected in the model's output. |
no_tool_called |
The model produced no parseable tool call. |
Both routes must be wired.
Side effects¶
- Records every detected tool call on the current turn (one record per call; a single turn may produce more than one).
- When
generate_on_no_tool = true(experimental) and no tool is detected, additionally appends the model's text as the assistant's reply and firesAnswerTextchunks (as a Generate node would). - When
notify_client = true, fires oneNodeEvent(event_type="tool_call") per detected call, out-of-band, before the turn completes.
Tool-call formats¶
| Format | Family | Typical model |
|---|---|---|
chatml |
OpenAI / Qwen / default | Qwen2.5, OpenAI-compatible chat models. |
llama3 |
Llama 3 | Meta Llama 3 / 3.1 / 3.2. |
mistral |
Mistral function-calling | Mistral-Instruct v0.3+. |
generic |
JSON-in-text | Fallback for models with no native format. |
The format controls:
- How tool definitions are rendered into the prompt.
- How the model's output is parsed to extract calls.
Pick the format matching your model's training. If unsure, start with
chatml and inspect debug_info.tool_call_format and the raw model
output in diagnostics.
Diagnostics¶
When enable_diagnostics = true, the node contributes these keys to
TurnComplete.debug_info:
| Key | Meaning |
|---|---|
tool_call_format |
The format config name that was actually used. |
tool_count |
Number of tool definitions passed to the model. |
generate_on_no_tool |
Current value of the param. (Experimental.) |
| (plus sampling / prompt diagnostics shared with other inference nodes) | — |
Minimum working example¶
from tryll_client.graph import (
GraphDescription, ToolCallParams, GenerateParams,
ToolDefinition, ToolParamDefinition,
)
tools = [
ToolDefinition(
name="get_weather",
description="Get the current weather for a city.",
parameters=[
ToolParamDefinition(name="city", type="string",
description="City name"),
],
),
]
graph = (
GraphDescription()
.add_node("detect", ToolCallParams(
tools=tools,
tool_call_format="chatml",
notify_client=True,
tool_called_exit="", # empty = END (client handles the tool)
no_tool_called_exit="answer",
))
.add_node("answer", GenerateParams(
default_exit="", # empty = END
))
.set_start_node("detect")
.set_default_model_name("Qwen2.5-3B-Instruct")
)
agent = client.create_agent(graph)
using namespace Tryll::Client;
using namespace Tryll::NodeParams;
auto cityParam = std::make_unique<ToolParamDefinitionT>();
cityParam->name = "city";
cityParam->type = "string";
cityParam->description = "City name";
auto getWeather = std::make_unique<ToolDefinitionT>();
getWeather->name = "get_weather";
getWeather->description = "Get the current weather for a city.";
getWeather->parameters.push_back(std::move(cityParam));
ToolCallParamsT dp;
dp.tool_call_format = "chatml";
dp.notify_client = true;
dp.tool_called_exit = ""; // empty = END (client handles the tool)
dp.no_tool_called_exit = "answer";
dp.tools.push_back(std::move(getWeather));
GenerateParamsT answerP;
// answerP.default_exit = ""; // empty = END (the default)
GraphDescription graph;
graph.AddToolCall("detect", std::move(dp))
.AddGenerate("answer", std::move(answerP))
.SetStartNode("detect")
.SetDefaultModelName("Qwen2.5-3B-Instruct");
auto agent = client.CreateAgent(graph);
using Tryll.Client;
using System.Collections.Generic;
var tools = new List<TryllToolDefinition>
{
new TryllToolDefinition
{
Name = "get_weather",
Description = "Get the current weather for a city.",
Parameters = new List<TryllToolParamDefinition>
{
new TryllToolParamDefinition
{ Name = "city", Type = "string", Description = "City name" },
},
},
};
var graph = new TryllGraphBuilder()
.AddToolCall("detect", new TryllToolCallParams
{
Tools = tools,
ToolCallFormat = "chatml",
NotifyClient = true,
ToolCalledExit = "", // empty = END (client handles the tool)
NoToolCalledExit = "answer",
})
.AddGenerate("answer", new TryllGenerateParams())
.SetStartNode("detect")
.SetDefaultModelName("Qwen2.5-3B-Instruct")
.Build();
#include "Generated/TryllGraphBuilder.Nodes.h"
#include "Generated/TryllNodeParamsFactory.h"
UTryllToolCallParams* DetectP = UTryllNodeParamsFactory::MakeToolCallParams(this);
DetectP->bOverrideToolCallFormat = true;
DetectP->ToolCallFormat = TEXT("chatml");
DetectP->NotifyClient = true;
DetectP->ToolCalledExit = TEXT(""); // empty = END
DetectP->NoToolCalledExit = TEXT("answer");
// Author tool definitions via DetectP->Tools (TArray<FTryllToolDefinition>)
FTryllToolDefinition GetWeather;
GetWeather.Name = TEXT("get_weather");
GetWeather.Description = TEXT("Get the current weather for a city.");
GetWeather.Parameters.Add({ TEXT("city"), TEXT("string"), TEXT("City name") });
DetectP->Tools.Add(GetWeather);
FTryllGraphDescription Graph = FTryllGraphBuilder()
.AddNode(TEXT("detect"), DetectP)
.AddNode(TEXT("answer"), UTryllNodeParamsFactory::MakeGenerateParams(this))
.SetStartNode(TEXT("detect"))
.SetDefaultModelName(TEXT("Qwen2.5-3B-Instruct"))
.Build();
Or author UTryllToolCallParams (with tool definitions) inside a
UTryllWorkflowAsset; bind UTryllSubsystem::OnToolCall to
receive the detection.
Receive the notification in your client with agent.set_on_tool_call(cb) (Python),
agent.SetOnToolCall(cb) (C++), or UTryllSubsystem::OnToolCall (Unreal).
See the full flow (including client-side execution and feeding the result back) in How to define and handle tool calls.
Client bindings¶
- C++:
GraphDescription::AddToolCall(name, ToolCallParamsT)—GraphDescription.h - Python:
GraphDescription.add_node(name, ToolCallParams(...))—tryll_client.graph - Unity:
TryllGraphBuilder.AddToolCall(name, new TryllToolCallParams{...})—Runtime/Generated/TryllGraphBuilder.Nodes.cs - Unreal:
AddToolCallNode(builder, name, UTryllToolCallParams*)—Generated/TryllGraphBuilder.Nodes.h