Skip to content

v0.9.0

This release gives every character its own voice. The voice moves off the speaking nodes and onto the agent as one voice profile. One workflow can then serve a whole cast, and game code can switch a character's delivery mode (angry, calm, whisper) mid-turn. LLM nodes can now load LoRA adapters, a new ClassifyAttributesLLM node scores several traits of a line in one call, and prompts can be localized through your game's own string tables. Qwen3-TTS clones a voice in ten languages, and new Use Cases and Model Benchmarks sections help you choose a design and a model.

The voice move, the classifier changes and the LoRA field all change the schema, and existing workflows need manual migration. See Breaking changes and behavior changes and Migrate to the agent voice before upgrading the plugin.

Highlights

  • One voice per agent. A voice profile holds the TTS model, the speaker or reference clip, speed, language, lip sync and named delivery modes. Every speaking node of the agent uses it, so one workflow can drive many characters, each with its own voice. See Add voice output to an agent.
  • Delivery modes. Define moods such as angry, calm or whisper on a profile and switch between them from game code at any time, even mid-turn. The new mode applies from the next sentence. See Delivery modes.
  • LoRA adapters on LLM nodes. Load a GGUF LoRA adapter trained for the node's model, with a scale, on any generating or classifying node. A small adapter can teach a small model a narrow job. See Generate.
  • Score several traits in one call. ClassifyAttributesLLM asks up to 16 specialist questions about a line in one batched call, with choice, true/false and 0–1 score answers written to slots.
  • Localized prompts from your string tables. Tick Localized on a prompt and Unity or Unreal resolves it through your own localization system before the agent is created. A language change updates live agents. See Localize prompts with your localization system.
  • Qwen3-TTS. Voice cloning from a 3–10 s reference clip, in 10 languages, on the same GPU backends as the language models. See TTS models.
  • Use Cases and Model Benchmarks. New docs sections that start from what the player experiences (Use Cases) and from how catalog models perform on Tryll's own jobs (Model Benchmarks).

Agent voice

  • Voice profiles. A VoiceProfile holds the TTS model (tts_model_name, now required), the base identity (speaker_id, reference_voice or voice_description), speed, lang, emit_visemes and named delivery modes. Unity has a Voice Profile asset (Create → Tryll → Voice Profile) assigned to the Voice field of TryllAgentComponent. Unreal has a Tryll Voice Profile data asset assigned to the agent component's Voice Profile. Python passes create_agent(graph, voice=...), and C++ sets AgentCreateOptions::voice. See Voice Profile, TryllVoiceProfile and the Blueprint catalog.
  • Delivery modes. A profile defines named moods. Switch with set_delivery_mode (Python), SetDeliveryMode (C++), SetDeliveryModeAsync (Unity) or Set Delivery Mode (Unreal), with an optional 0–1 intensity. neutral is built in. What a mode can change depends on the model: a reference clip (Pocket TTS, Qwen3-TTS), a delivery clause with a low/medium/high intensity word (Maya1), or only speed (Supertonic 3). Orpheus modes must be empty. See Delivery modes by family and Clone a voice, where "one clip per mood" is now one delivery mode per mood.
  • Per-agent delivery override. Unity TryllAgentComponent (OverrideVoiceDelivery / VoiceDelivery) and Unreal UTryllAgentComponent (Voice Delivery) can start one agent in a different mode without editing the shared profile asset. Changing it also switches a live agent in Play or in Chat.
  • Change the voice at runtime. set_voice / SetVoice / SetVoiceAsync / Set Voice replace the whole profile, model included, while the agent is idle. The new profile is checked and its clips are prepared before the swap, and if anything fails the old voice stays. The active mode is kept when the new profile defines it. Otherwise the new profile's default mode applies, and the acknowledgement carries a warning. See Change the voice or its delivery at runtime.
  • The voice is checked once, when it is set. A field the model cannot use fails with 3023 InvalidVoiceProfile and names the field. A missing TTS model is now 3023. A non-empty lang on a fixed-language voice (Pocket TTS, Orpheus, Maya1) is now rejected instead of silently ignored.
  • Clips are prepared up front. By default every reference clip in a profile, including each mode's clip, is prepared when the voice is set, so switching mood adds no delay. Set prepare to Lazy to prepare each clip on first use. See Clip preparation.
  • Speak nodes emit visemes. emit_visemes is on the voice, so every speaking node of a lip-synced character sends a viseme track, including Speak lines such as scripted refusals.
  • One audio stream per turn. The audio format is announced once per turn, by the first speaking node, and viseme offsets count from it across every speaking node of the turn. See Audio stream per turn.
  • Agents that never speak load no TTS model, even if they have a voice.
  • Model capabilities drive the editors. ListModels reports expressiveness and num_speakers for TTS models, and every shipped TTS model declares its capabilities. The Unity and Unreal profile editors show only the fields the chosen model accepts. See Model Management.
  • Maya1 delivery keywords match whole words. unhappy no longer resolves to the happy clause.
  • Diagnostics show the agent's voice. It appears under tts on GenerateAndSpeak and under a new voice section on Speak, with delivery_mode and intensity. The old tts_lang / tts_voice keys are now lang / reference_voice. See Turn Diagnostics JSON.

Migrate to the agent voice

No migration helper ships. Unity drops the removed node fields from a workflow asset the next time it is saved, and Unreal drops them when an old asset loads. So record the values first.

Before upgrading the plugin, write down the TTS values of every Speak or GenerateAndSpeak node in every workflow asset: Tts Model Name, Speaker Id, Speed, Tts Lang, Tts Voice, Tts Voice Description, Tts Delivery Instruction and Emit Visemes. Also note any game code that sets these fields on node params (for example a per-character speaker or speed) or changes them with ChangeParams / ChangeAgentParam.

Unity projects, after upgrading:

  1. Create one voice profile per character: Create → Tryll → Voice Profile.
  2. Fill it from your notes: Tts Model Name, Speaker Id, Speed, Lang (was Tts Lang), Reference Voice (was Tts Voice), Voice Description and Emit Visemes. Tts Model Name is now required. A node that left it empty used the first TTS model in the catalog, usually Supertonic 3 (int8). Turn a Tts Delivery Instruction into a delivery mode with that Instruction, and select it from code with SetDeliveryModeAsync.
  3. Assign the profile to the Voice field of each TryllAgentComponent whose workflow speaks. For characters spawned at runtime, set TryllAgentComponent.Voice before the agent is created. Replace code that patched SpeakerId / Speed into cloned node params with one profile per character.
  4. Re-save each workflow asset so it no longer carries the removed fields.
  5. Replace runtime voice changes made with ChangeParams by SetVoiceAsync (identity) or SetDeliveryModeAsync (mood).

Unreal projects, after upgrading:

  1. Create one Tryll Voice Profile data asset per character and fill it from your notes, as for Unity. Check Emit Visemes for characters with lip sync. It now also covers Speak nodes, so lines from a Speak node move the lips too.
  2. Assign the profile to the Voice Profile property of each UTryllAgentComponent whose workflow speaks.
  3. Open and re-save each workflow asset, and recompile Blueprints that read or set TTS fields on node params. They must not touch a removed field.
  4. Replace runtime voice changes made with Change Params by Set Voice (identity) or Set Delivery Mode (mood).

Python and C++ code:

  1. Move the tts_* fields, speaker_id, speed and emit_visemes off SpeakParams / GenerateAndSpeakParams into a VoiceProfile. tts_lang becomes lang, tts_voice becomes reference_voice, tts_voice_description becomes voice_description, and tts_delivery_instruction becomes a DeliveryMode with that instruction.
  2. Pass it at creation: client.create_agent(graph, voice=profile) in Python, AgentCreateOptions::voice in C++.
  3. Replace change_param / ChangeParams calls on voice fields with set_voice / SetVoice or set_delivery_mode / SetDeliveryMode.

If two speaking nodes in one workflow had different voice settings, choose one. An agent now has exactly one voice.

LoRA adapters

  • An adaptation table on every LLM node. Generate, ToolCall, ClassifyIntentLLM, ClassifyAttributesLLM, GenerateAndSpeak and Summarize take a list of GGUF LoRA adapters (loras), each with a scale (1 = as trained, 0 = off), up to 8 per node. Paths are relative to the storage root, like voice clips. An escaping path fails with 3009 StorageOutsideRoot, and a missing file with 3010 StorageFileNotFound. See Generate.
  • An adapter must be trained for the node's model. A file made for another model fails agent creation.
  • Adapters are fixed at creation. Changing adaptation on a live agent returns 3006 ParamNotMutable. Workflows without it run on the base model as before.
  • Unity has a file picker for adapter paths, and the workflow validator flags an absolute or .. path as an error and a missing or non-.gguf file as a warning.

Classification

  • New ClassifyAttributesLLM node. One node scores up to 16 specialist prompts in one cached, batched call. A Choice attribute writes a label and can trigger per option. A Boolean attribute writes true / false for one statement. A Score attribute writes a normalised 0–1 level. Any trigger takes triggered_exit, otherwise the node takes default_exit. A Choice that misses threshold or margin writes uncertain_choice, which is one of its choice ids or the reserved #uncertain. Empty input never triggers. Engine failure follows on_failure. Slots are named <output_name>_<attribute>, and notify_client emits attributes_classified with every measurement. Python, C++, Unity and Unreal all support it, including the editor palettes, inspectors and Dialog Lab attribute checks.
  • ClassifyIntentLLM writes its decision to a slot. New output_name (empty = node name). The slot holds the winning id on found and not_found_intent on every not_found path, so a Branch can route on it after either exit. not_found_intent also accepts the reserved #uncertain. See Output slot.
  • intents is the only intent list. The comma-separated intents_ids / intents_prompt pair deprecated in v0.8.0 is removed (see Breaking changes).

Localization

  • Localized prompts use your string tables. Tick Localized on a prompt field and the box becomes a Text ID. Before the agent is created, Unity or Unreal asks your text provider for the text and sends it. See Localize prompts with your localization system.
  • A language change updates live agents. Ticked non-structural prompts (system prompt, template, instruction, acknowledge text) are updated from the next turn, and params you changed at runtime are kept. The update is all or nothing per agent. If a Text ID is missing in the new language, that agent's prompts stay as they were and LocalizedTextFailed (Unity) / OnLocalizedTextFailed (Unreal) fires. ClassifyIntent entry prompts, files already bound and an open voice input still need the agent recreated or the input reopened. See Ship an agent in another language.
  • Unreal: wait for the new text. OnLocalizedTextApplied fires once an agent's new prompts are acknowledged, so a prefill or reset can wait for them. A server rejection of the update now also raises OnLocalizedTextFailed. The Unreal text provider is None by default, so existing projects keep using the Tryll Locale field.

Models and catalogs

  • Qwen3-TTS 12Hz 1.7B Base (Q4_K_M) is a public catalog voice. It clones its voice from a reference clip, so the profile's reference_voice is required. It speaks zh, en, de, it, pt, es, ja, ko, fr and ru through lang, and it runs on the CPU, Vulkan and CUDA backends without ONNX Runtime. Delivery modes switch reference clips. See TTS models.
  • SenseVoice Multilingual and Streaming Paraformer zh-en are now public speech-to-text entries.
  • LFM2.5 1.2B Thinking (Q4_K_M) joins the catalog. It always opens with a <think> block, so it is not a good host for an immersion guard.
  • New Model Benchmarks section. Model cards and per-trait leaderboards for catalog models: the immersion guard (General and World classifiers), prefill and generation speed for a 1K and a 4K prompt, and VRAM at an 8K context. Treat the results as provisional.

Diagnostics and Agent Log

  • Prefill and generation speed per node. Language-model nodes add engine.llama_perf to turn diagnostics, with prompt and generation time and token counts for the node's context. See Turn Diagnostics JSON.
  • Agent Log export. Export writes the journal to one JSON file in Unity and Unreal. The file has a header describing the server, the capture and the agents, then the events with their payloads, then a turn index. The inspectors show the event's raw JSON. The file can contain player input, prompts and tool arguments, so treat it like a bug report. See Use the Agent Log and Agent Log format.

Unreal

  • Play → Standalone Game starts its own server. A standalone game used to skip auto-launch silently and then fail every connect attempt. It now launches the bundled server on a free port, separate from the editor's server, and the server exits 60 s after the game closes. See Auto-launch the server.
  • Saved assets survive node parameter changes. Workflow and agent assets saved by an older plugin load correctly after a node field is removed, renamed or given a new default. A saved ClassifyIntentLLM CSV intent pair is converted into intents on load.

Documentation

Breaking changes and behavior changes

  • The schema changed — rebuild every client. The agent voice, ClassifyAttributesLLM, the ClassifyIntentLLM changes and LoRA adapters all change shape. Upgrade the server and every client together (Unity, Unreal, Python, C++). An older server would ignore adaptation and quietly run the base model, so mismatched peers fail at connect: the client rejects the server's protocol version with 5004 ProtocolVersionMismatch. See Wire Protocol.
  • One voice per agent. Speak and GenerateAndSpeak lose tts_model_name, speaker_id, speed, tts_lang, tts_voice, tts_voice_description, tts_delivery_instruction and emit_visemes. They keep only their text routing and chunking (min_sentence_chars, first_chunk_max_chars). A graph with a speaking node and no voice fails with 3025 AgentVoiceRequired. An agent created without a voice cannot be given one later. ChangeAgentParam can no longer change any voice field. Write down your TTS values before you upgrade the plugin, then follow Migrate to the agent voice.
  • tts_model_name is required. An empty name no longer means "the first TTS voice in the catalog", and a missing TTS model fails with 3023 InvalidVoiceProfile instead of 3003 GraphCompilationFailed.
  • Fixed-language voices reject lang. A non-empty lang on Pocket TTS, Orpheus or Maya1 fails with 3023 instead of being ignored. Leave it empty.
  • The audio format is announced once per turn. A turn with two speaking nodes no longer sends a second format frame or restarts the viseme clock. Code that reset playback on each format announcement now sees one per turn.
  • ClassifyIntentLLM CSV intents removed. intents_ids / intents_prompt are gone, and intents (id + prompt pairs) must hold 2–26 entries. Unreal assets convert a saved CSV pair on load. If the two lists had different lengths, the missing entries are left empty and a warning is logged. Other assets that still carry a CSV pair must move it to intents before upgrading. The diagnostics key parameters.intents_ids is unchanged.
  • LoRA adapters are structural. adaptation is fixed at agent creation. Changing it on a live agent returns 3006 ParamNotMutable.
  • Unreal Play → Standalone Game launches a server. A standalone run that used to have no server now starts its own on a free port.

New error codes

  • 3023 InvalidVoiceProfile — a voice profile field fails validation, names an unknown model, or uses a field the model cannot use.
  • 3024 UnknownDeliveryMode — SetAgentDeliveryMode named a mode the voice does not define. The current mode stays.
  • 3025 AgentVoiceRequired — a speaking graph has no voice, or a voice call was made on an agent created without one.

See Error Codes for the full list.