String Storage¶
A string storage is a session-owned, named container of strings. It exists to back workflow nodes with fixed text content without baking that content into the graph definition at authoring time.
String storages are not embedded and not indexed. For vector retrieval, see embedded string storage.
Kinds¶
Every string storage has a kind set at creation time; the kind determines what accessors the server exposes to nodes and what validation rules apply.
| Kind | Description | Key uniqueness |
|---|---|---|
List |
Ordered sequence of plain strings. No keys. | — |
Map |
Ordered sequence of key → value pairs. |
Each key must be unique. |
Multimap |
Ordered sequence of key → value pairs. |
Duplicate keys allowed. |
All three kinds share a list view — the server can iterate every
value by position regardless of kind. Map and Multimap additionally
expose a keyed view (FindFirst, FindAll) that is used by nodes
which need to look up values by a string key.
Internal storage order for Map and Multimap
The server stores Map and Multimap entries sorted by key
(lexicographic, stable within each key group so that duplicate-key
values appear in their original insertion order). This makes FindFirst
and FindAll O(log n) binary-search lookups, and guarantees that
FindAll returns every matching value even when the keys were not
grouped at creation time. The list view (GetByIndex) reflects
this sorted order rather than the original construction order.
Current node support
CannedResponseNode and HumanMessageGuardrailNode use only the
list view — they iterate all values in order and are not key-aware.
Both nodes accept any kind.
IntentToInstructionNode uses the
keyed view (FindFirst) and requires Map kind. It is the
primary consumer of Map-kind storages today.
Key-aware CannedResponse selection (keyed by a runtime intent label)
is planned as future work.
Lifecycle¶
| Step | Frame | Notes |
|---|---|---|
| Create | CreateStringStorageRequest |
Sent before CreateAgentRequest. Server responds with CreateStringStorageResponse. |
| Use | (referenced by node string_storage param) |
Scoped to the session. |
| Destroy | DestroyStringStorageRequest |
Server responds with Ack. Agents that already hold the storage keep it alive — the call only removes the session's named reference. |
The session drops every storage it owns on disconnect. See Lifetime and Ownership → StringStorage for the full ownership model.
Creation¶
CreateStringStorageRequest carries these fields:
| Field | Type | Description |
|---|---|---|
name |
string | Unique name within the session. Required. |
kind |
StringStorageKind enum |
List (0, default), Map (1), or Multimap (2). |
strings |
[string] |
Inline values. For List: the strings themselves. For Map/Multimap: the values, parallel to keys. |
keys |
[string] |
Inline keys (Map/Multimap only). Must have the same length as strings. |
file_path |
string | Server-side file path. .txt for List; .json for Map/Multimap. |
Exactly one content source must be provided:
stringsalone → List inline.strings+keys→ Map or Multimap inline.file_path→ file-based (kind determines the expected format).
Old clients that do not set kind default to List and keep working
without any changes.
File format — List (.txt)¶
UTF-8 text, one value per line. Lines starting with # and blank lines
are ignored:
# Lines starting with # are comments.
# Blank lines are also ignored.
I can't help with that.
I can't assist with that request.
File format — Map / Multimap (.json)¶
A JSON array of objects with id (key) and text (value) fields.
This is the same on-disk shape used by knowledge-base records; a
metadata field is accepted but ignored in this context.
[
{"id": "greet_friendly", "text": "Hello there, traveller!"},
{"id": "greet_friendly", "text": "Well met!"},
{"id": "greet_hostile", "text": "What do you want?"}
]
Duplicate id values are rejected for Map kind and accepted for
Multimap kind.
Note
Relative paths in file_path are resolved against the server's
current working directory, not the client's. Use absolute paths for
portable deployments or rely on server defaults (see below).
Server defaults¶
If a canned-response or guardrail node is created with no
string_storage param and no inline response_N / pattern_N
params, the server falls back to a file configured in
server-config.json:
default_canned_responses_path— default responses (List kind).default_guardrail_patterns_path— default patterns (List kind, regex).
Both files are plain .txt (List kind). There are no JSON defaults.
Resolution order¶
A canned-response or guardrail node resolves its string source in this order:
- Named storage —
string_storageparam set → look up in the session's storage manager. - Inline params —
response_0/pattern_0(etc.) set → use as-is. - Server default — load from the configured default file.
Minimum working examples¶
Errors¶
| Code | Cause |
|---|---|
7001 |
Name empty, malformed, or reserved. |
7002 |
A storage with this name already exists in the session. |
7003 |
Content rejected: unreadable file, empty array, bad format, duplicate key in Map, size mismatch between keys and strings. |
Client bindings¶
| List (inline) | List (file) | Map / Multimap (inline) | Map / Multimap (file) | |
|---|---|---|---|---|
| C++ | CreateStringStorage(name, strings) |
CreateStringStorageFromFile(name, path) |
CreateStringStorageKeyed(name, keys, values, kind) |
CreateStringStorageFromFile(name, path, kind) |
| Python | create_string_storage(name, strings=…) |
create_string_storage(name, file_path=…) |
create_keyed_string_storage(name, keys, values, kind) |
create_string_storage(name, file_path=…, kind=…) |
| Unreal | RequestCreateStringStorage(Name, Strings) |
RequestCreateStringStorageFromFile(Name, Path) |
RequestCreateStringStorageKeyed(Name, Keys, Values, Kind) |
RequestCreateStringStorageFromFile(Name, Path, Kind) |
| Unity | RequestCreateStringStorageAsync(name, strings) |
RequestCreateStringStorageFromFileAsync(name, path) |
RequestCreateKeyedStringStorageAsync(name, keys, values, kind) |
RequestCreateStringStorageFromFileAsync(name, path, kind) |
All methods complete asynchronously (C++: blocking with timeout; Python: blocking; Unreal/Unity: Task/callback).
The kind parameter for Unreal uses uint8 ordinals: 0 = List, 1 = Map, 2 = Multimap.
For C++ and Python, use the typed ::Tryll::StringStorageKind_Map / StringStorageKind.Map constants.
Related¶
- Canned Response node
- Human-Message Guardrail node
- Intent to Instruction node — the keyed consumer of Map-kind storages.
- How to use canned responses and guardrails
- How to build an intent-driven NPC
- Embedded String Storage — for vector retrieval.
- Glossary