Text Chat
Send text messages and handle streaming responses from AI characters.
Sending Messages
Basic Send
// Fire-and-forget (synchronous wrapper)
character.SendText("Hello!");
// Async
await character.SendTextAsync("Hello!");
Text-Only Mode
By default, SendText automatically suppresses TTS when no voice session is active. To explicitly control this, pass a second bool argument:
// Force text-only (no TTS audio generated)
character.SendText("Hello!", true);
// Force voice response (even without an active voice session)
character.SendText("Hello!", false);
Scripted Lines
Use SayLine / SayLineAsync to make the character speak a specific prewritten line verbatim instead of generating a response from the LLM:
// Fire-and-forget
character.SayLine("Welcome to the museum!");
// Async, with optional text-only override
await character.SayLineAsync("Welcome to the museum!", textOnly: false);
When textOnly is false (the default), the server generates TTS audio for the line. When true, the line is delivered as text only.
Handling Streaming Responses
Bot responses arrive as a stream of chunks. Each BotResponse event contains a piece of the full reply.
Accumulating Text
private string _fullResponse = "";
void Start()
{
character.OnBotResponse += HandleBotResponse;
}
void HandleBotResponse(BotResponse response)
{
if (response.IsFinal)
{
// Final chunk: response.Text contains the complete response
_fullResponse = response.Text;
Debug.Log($"Complete: {_fullResponse}");
}
else
{
// Partial chunk: accumulate
_fullResponse += response.Text;
}
}
Using CurrentPartialResponse
The EstuaryCharacter component automatically accumulates streaming text:
void Update()
{
// Always shows the latest accumulated text
responseLabel.text = character.CurrentPartialResponse;
}
BotResponse Fields
| Field | Type | Description |
|---|---|---|
Text | string | Text content of this chunk |
IsFinal | bool | true when the response is complete |
IsPartial | bool | true for streaming chunks |
MessageId | string | Unique ID for this response |
ChunkIndex | int | Sequential index of this chunk |
IsInterjection | bool | true if this is a proactive message |
Chat UI Example
A simple TextMeshPro-based chat display:
using UnityEngine;
using TMPro;
using Estuary;
using Estuary.Models;
public class ChatUI : MonoBehaviour
{
[SerializeField] private EstuaryCharacter character;
[SerializeField] private TMP_InputField inputField;
[SerializeField] private TMP_Text chatLog;
[SerializeField] private TMP_Text streamingText;
void Start()
{
character.OnBotResponse += OnBotResponse;
character.OnConnected += OnConnected;
inputField.onSubmit.AddListener(OnSubmit);
}
void OnConnected(SessionInfo session)
{
chatLog.text += "\n<color=#888>[Connected]</color>";
}
void OnSubmit(string text)
{
if (string.IsNullOrEmpty(text)) return;
// Display user message
chatLog.text += $"\n<b>You:</b> {text}";
// Send to character
character.SendText(text, true);
// Clear input
inputField.text = "";
inputField.ActivateInputField();
// Clear streaming display
streamingText.text = "";
}
void OnBotResponse(BotResponse response)
{
if (response.IsFinal)
{
// Move final response to chat log
chatLog.text += $"\n<b>AI:</b> {response.Text}";
streamingText.text = "";
}
else
{
// Show streaming text
streamingText.text = character.CurrentPartialResponse;
}
}
}
Actions in Text Responses
Bot responses may contain action tags (e.g., <action name="wave" />). By default, EstuaryCharacter strips these before updating CurrentPartialResponse. To access the raw text with action tags, handle the BotResponse event directly -- the Text field contains the original text before stripping.
To disable automatic stripping, uncheck Strip Actions From Text in the EstuaryCharacter Inspector. This option is only configurable in the Inspector; there is no public runtime property.
See Action System for details on handling actions.
Interjections
Characters can send proactive messages (interjections) without user input. These have IsInterjection = true:
character.OnBotResponse += (response) =>
{
if (response.IsInterjection)
{
// Character is speaking unprompted
ShowNotification(response.Text);
}
};
Next Steps
- Voice Connection -- Add voice to your conversations
- Action System -- React to actions in responses