Skip to content

Canned Response

The Canned Response node emits a fixed reply chosen from a string storage according to its selection_strategy, and ends its turn without invoking a language model. Use it for scripted answers, refusal lines, and branches where you do not want the cost or variability of a generation pass.

NodeType: CannedResponse.

Parameters

All parameters are optional. The node resolves its string source in priority order; the first matching source wins.

Source resolution (priority order)

Priority Key Mutable Description
1 string_storage Yes Name of a string storage created earlier in the session via CreateStringStorageRequest. Can be rebound at runtime via ChangeParam.
2 response_0, response_1, ... No Inline responses directly on the node params. At least one must be present to take this path.
3 (none) The server default file (default_canned_responses_path in server-config.json) is loaded lazily.

Selection behaviour

After the source is resolved, the node picks one string from that source each time it runs and emits it. The selection_strategy param controls how that index is chosen.

Key Mutable Default Description
selection_strategy Yes random How the node picks a response from the resolved source. random — uniform random pick each run. first — always the first entry (deterministic; useful in tests). round_robin — cycles through entries in order; the internal counter resets when string_storage is rebound.

Exit routes

Route Fires when
default Always — the node never branches. Wire this to "END" to terminate the turn, or to another node to continue.

Side effects

  • Appends the chosen response to the current turn as the assistant's reply.
  • Emits one AnswerText frame (non-streaming: is_delta = false, is_final = true) with the full response.

No model inference, no KV-cache work, no network I/O beyond the AnswerText frame itself.

Minimum working example

from tryll_client import GraphDescription, NodeType

# 1. Create the storage first
client.create_string_storage(
    name="refusal_lines",
    strings=[
        "I can't help with that request.",
        "That's outside what I can assist with.",
    ],
)

# 2. Reference it from the node
graph = (
    GraphDescription()
    .add_node("guard",  NodeType.HumanMessageGuardrail,
              {"string_storage": "jailbreak_patterns"})
    .add_node("refuse", NodeType.CannedResponse,
              {"string_storage": "refusal_lines"})
    .add_node("answer", NodeType.Generate)
    .wire("guard",  "triggered",     "refuse")
    .wire("guard",  "not_triggered", "answer")
    .wire("refuse", "default",       "END")
    .wire("answer", "default",       "END")
    .set_start_node("guard")
    .set_default_model_name("My Local Model")
)

agent = client.create_agent(graph)
namespace TC = Tryll::Client;

// 1. Create the storage first
client.CreateStringStorage("refusal_lines", {
    "I can't help with that request.",
    "That's outside what I can assist with.",
});

// 2. Reference it from the node
TC::GraphDescription graph;
graph.AddNode("guard",  TC::NodeType::HumanMessageGuardrail,
              {{"string_storage", "jailbreak_patterns"}})
     .AddNode("refuse", TC::NodeType::CannedResponse,
              {{"string_storage", "refusal_lines"}})
     .AddNode("answer", TC::NodeType::Generate)
     .Wire("guard",  "triggered",     "refuse")
     .Wire("guard",  "not_triggered", "answer")
     .Wire("refuse", "default",       "END")
     .Wire("answer", "default",       "END")
     .SetStartNode("guard")
     .SetDefaultModelName("My Local Model");

auto agent = client.CreateAgent(graph);
auto* subsystem = GetGameInstance()->GetSubsystem<UTryllSubsystem>();
subsystem->RequestCreateStringStorage(
    TEXT("refusal_lines"),
    { TEXT("I can't help with that request."),
      TEXT("That's outside what I can assist with.") });

FTryllGraphDescription Graph = FTryllGraphBuilder()
    .AddNode(TEXT("guard"),  ETryllNodeType::HumanMessageGuardrail,
             {{TEXT("string_storage"), TEXT("jailbreak_patterns")}})
    .AddNode(TEXT("refuse"), ETryllNodeType::CannedResponse,
             {{TEXT("string_storage"), TEXT("refusal_lines")}})
    .AddNode(TEXT("answer"), ETryllNodeType::Generate)
    .Wire(TEXT("guard"),  TEXT("triggered"),     TEXT("refuse"))
    .Wire(TEXT("guard"),  TEXT("not_triggered"), TEXT("answer"))
    .Wire(TEXT("refuse"), TEXT("default"),       TEXT("END"))
    .Wire(TEXT("answer"), TEXT("default"),       TEXT("END"))
    .SetStartNode(TEXT("guard"))
    .SetDefaultModelName(TEXT("My Local Model"))
    .Build();

See the full flow in How to use canned responses and guardrails.

Client bindings

  • C++: Tryll::Client::GraphDescription::AddNode(name, NodeType::CannedResponse, params)GraphDescription.h
  • Python: tryll_client.GraphDescription.add_node(name, NodeType.CannedResponse, params)graph.py
  • Unreal: add an FTryllNodeDesc with Type = CannedResponse to FTryllGraphDescription.NodesTryllGraphDescription.h