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:
// Force text-only (no TTS audio generated)
character.SendText("Hello!", textOnly: true);
// Force voice response (even without an active voice session)
character.SendText("Hello!", textOnly: false);
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, textOnly: 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 on the EstuaryCharacter component.
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