Skip to main content
This demo uses the full graph node system to build a relatively complete single graph that combines modules including LLM, STT, TTS, and Safety.

Run the Template

  1. Go to Assets/InworldRuntime/Scenes/Nodes and play the CharacterInteractionNode scene. CharNode00
  2. After the scene loads, you can enter text and press Enter or click the SEND button to submit.
  3. You can also hold the Record button to record audio, then release it to send.
  4. The AI agent responds with both audio and text. If you send audio, it will be transcribed to text first.

Understanding the Graph

You can find the graph on the InworldGraphExecutor of CharacterInteractionCanvas. CharNode01 The graph is relatively complex—let’s use the graph editor to illustrate: CharNode02
1

FilterInputNode

CharNode02On the left, FilterInputNode acts as the StartNode and processes user input.If the data is InworldText or InworldAudio, it passes downstream; otherwise, it returns an error and stops.If the input is InworldAudio, it first goes through STTNode for transcription to text, then into SafetyNode. If it is text, it goes directly into SafetyNode.Note that the two outgoing edges from FilterInput are not default edges. One is TextEdge and the other is AudioEdge. Their MeetsCondition checks are simple: for InworldText, TextEdge passes; for InworldAudio, AudioEdge passes. Otherwise they block.
You can assume that, by default, data tries to flow forward in the graph node system. When designing:• Add a CustomNode before/after to convert the data into the expected type (slower), or• Configure a custom Edge to allow only the types needed by the next node and block the rest.
CharNodeLeftEdge
2

SafetyNode

SafetyNode checks input text against its SafetyData categories and thresholds.If the input is safe, it proceeds to AddPlayerSpeech, then on through LLM into AddCharacterSpeech.Otherwise, the user’s input is ignored and the flow goes to a SafetyResponse, which is a RandomCannedText node that randomly selects one predefined message and sends it directly to AddCharacterSpeech.
In this demo, no SafetyData is configured, which means all inputs are allowed.To change this, click SafetyNode.The Inspector will highlight the node, and you can adjust SafetyData in the panel below.CharNodeSafety
SafetyNode has two outgoing edges.The upper edge is a special SafetyEdge whose MeetsCondition simply checks whether the input is safe.If safe, it proceeds to AddCharacterSpeech; otherwise, it goes to RandomCannedText.CharNodeSafety
3

AddPlayerSpeech

AddPlayerSpeech is an AddSpeechEventNode that inherits from CustomNode.It converts various upstream types into text when possible.During creation, it uses the boolean m_IsPlayer to obtain the player or agent name, so the final output can be tagged with the correct speaker.In this demo, AddPlayerSpeech connects to an early exit PlayerFinal to notify Unity that the graph has the player’s input portion available.
AddSpeechEventNodeAsset.cs
This node also passes its output to FormatPrompt, then on to LLM and AddCharacterSpeech.
4

PlayerFinal

This is an EndNode.It emits the PlayerSpeech output, because sometimes we need an early return while the rest of the graph continues.In this demo, this node lets the handler registered to the graph executor’s OnGraphResult capture the user’s own message (especially STT‑transcribed text) to render a UI bubble, etc.
5

FormatPrompt

This is also a CustomNode.It stores the AddSpeechEvent result into the runtime DialogHistory, renders the prompt from the Jinja template, then wraps it into an LLMChatRequest and sends it to LLMNode.CharNodePromptDataHere is the Prompt Template used in this demo.
Prompt Template
And here is the Jinja prompt after filling it with CharacterData, DialogHistory, PlayerData, etc.
Jinja Prompt
You can compare the two prompts.
6

AddCharacterSpeech

CharNode02Like AddPlayerSpeech, AddCharacterSpeech is an AddSpeechEventNode that inherits from CustomNode and converts upstream types to text when possible.During creation, it uses m_IsPlayer to obtain either the player’s or the agent’s name so the final output is tagged with the speaker.In this demo, AddCharacterSpeech receives the value returned from the LLM and prefixes it with the character’s name.AddCharacterSpeech also connects to an early exit CharFinal to notify Unity that the character’s output portion is available.
7

TextChunking & TextProcessor

These two nodes trim the text generated by the LLM, because some models stream segmented output.TextChunking merges those segments into a single string.TextProcessor is a CustomNode that removes undesirable content before sending to TTS (e.g., brackets, emojis).Some TTS models will literally read those symbols.
8

TTSNode

This is the third and final EndNode.It takes text produced by either RandomCannedText or the LLM, processes it through the two text nodes above, and then synthesizes speech in TTSNode.

InworldController

The InworldController contains all the primitive modules and an InworldAudioManager, which also contains all the audio modules. CharNode03
For details about the primitive module, see the Primitive Demos.For details about the AudioManager, see the Speech-to-text Node Demo

Workflow

  1. When the game starts, InworldController initializes all its primitive modules.
Each model creates a factory and then builds its interface based on the provided configs.
  1. Next, InworldGraphExecutor initializes its graph asset by calling each component’s CreateRuntime().
  2. After initialization, the graph calls Compile() and returns the executor handle.
  3. After compilation, the OnGraphCompiled event is invoked. In this demo, the CharacterInteractionNodeTemplate of the CharacterInteractionPanel subscribes to it and configures the prompt. Users can then interact with the graph system.
CharacterInteractionNodeTemplate.cs
  1. If the user sends text, it reaches the Submit() function, which converts the input into InworldText.
CharacterInteractionNodeTemplate.cs
  1. If the user sends audio, the AudioDispatchModule of InworldAudioManager raises the onAudioSent event.
CharacterInteractionNodeTemplate subscribes to this event and handles it in SendAudio().
CharacterInteractionNodeTemplate.cs
  1. Calling ExecuteGraphAsync() eventually produces a result and invokes OnGraphResult(), which CharacterInteractionNodeTemplate subscribes to in order to receive the data.
If the result is user text (or STT‑transcribed text), a bubble is created directly. If it is a character reply, the bubble is updated (created if not found, otherwise appended). If the result is audio, it is converted into an AudioClip and played.
CharacterInteractionNodeTemplate.cs