tavus-cvi-interactions
Tavus CVI Interactions Protocol
Control live conversations programmatically via WebRTC data channel.
Setup: Daily.js Client
<script src="https://unpkg.com/@daily-co/daily-js"></script>
<script>
const call = window.Daily.createFrame();
// Listen for events from CVI
call.on('app-message', (event) => {
console.log('CVI event:', event.data);
});
// Join the conversation
call.join({ url: 'YOUR_CONVERSATION_URL' });
// Send interaction
function send(interaction) {
call.sendAppMessage(interaction, '*');
}
</script>
Interactions You Can Send
Echo: Make Replica Speak Text
Bypass LLM, replica speaks exactly what you provide:
send({
"message_type": "conversation",
"event_type": "conversation.echo",
"conversation_id": "YOUR_CONVERSATION_ID",
"properties": {
"modality": "text",
"text": "Hello! Let me tell you about our product."
}
});
For streaming audio (base64):
send({
"message_type": "conversation",
"event_type": "conversation.echo",
"conversation_id": "YOUR_CONVERSATION_ID",
"properties": {
"modality": "audio",
"audio": "BASE64_ENCODED_AUDIO",
"sample_rate": 24000,
"inference_id": "unique-id",
"done": "true"
}
});
Respond: Inject User Input
Treat text as if user spoke it (goes through LLM):
send({
"message_type": "conversation",
"event_type": "conversation.respond",
"conversation_id": "YOUR_CONVERSATION_ID",
"properties": {
"text": "What are your pricing plans?"
}
});
Interrupt: Stop Replica Speaking
send({
"message_type": "conversation",
"event_type": "conversation.interrupt",
"conversation_id": "YOUR_CONVERSATION_ID"
});
Overwrite Context
Replace the entire conversational context:
send({
"message_type": "conversation",
"event_type": "conversation.overwrite_context",
"conversation_id": "YOUR_CONVERSATION_ID",
"properties": {
"context": "User is now asking about enterprise features."
}
});
Append Context
Add to existing context without replacing:
send({
"message_type": "conversation",
"event_type": "conversation.append_context",
"conversation_id": "YOUR_CONVERSATION_ID",
"properties": {
"context": "User mentioned they have a team of 50 people."
}
});
Adjust Sensitivity
Change turn-taking sensitivity mid-conversation:
send({
"message_type": "conversation",
"event_type": "conversation.sensitivity",
"conversation_id": "YOUR_CONVERSATION_ID",
"properties": {
"participant_pause_sensitivity": "high",
"participant_interrupt_sensitivity": "low"
}
});
Values: low, medium, high
Events You Receive
Utterance (What Was Said)
{
"event_type": "conversation.utterance",
"properties": {
"role": "user",
"content": "Tell me about your product"
}
}
Role: user or replica
Replica Started/Stopped Speaking
{
"event_type": "conversation.replica.started_speaking",
"properties": {
"inference_id": "inf-123"
}
}
{
"event_type": "conversation.replica.stopped_speaking",
"properties": {
"inference_id": "inf-123",
"duration": 4.5
}
}
User Started/Stopped Speaking
{
"event_type": "conversation.user.started_speaking"
}
Tool Call (Function Calling)
When LLM invokes a tool:
{
"event_type": "conversation.tool_call",
"properties": {
"tool_name": "get_weather",
"arguments": {
"location": "San Francisco, CA"
},
"inference_id": "inf-123"
}
}
Handle it, then respond with echo or respond interaction.
Perception Analysis
When Raven analyzes the user:
{
"event_type": "conversation.perception_analysis",
"properties": {
"analysis": "User appears engaged, smiling, looking at camera"
}
}
Replica Interrupted
Fired when replica was interrupted:
{
"event_type": "conversation.replica.interrupted",
"properties": {
"inference_id": "inf-123"
}
}
Python Client (Daily-Python)
from daily import Daily, CallClient
Daily.init()
client = CallClient()
def on_app_message(message, sender):
print(f"Received: {message}")
client.set_user_name("bot")
client.join(meeting_url, completion=on_join)
# Send interaction
client.send_app_message({
"message_type": "conversation",
"event_type": "conversation.echo",
"conversation_id": "xxx",
"properties": {"text": "Hello!"}
})
Common Patterns
Echo Mode with Manual Control
- Create persona with
pipeline_mode: "echo" - Join conversation with Daily client
- Send
conversation.echoevents to control speech - Send
conversation.interruptto stop - Listen for events to track state
Hybrid: LLM + Manual Injection
- Use
pipeline_mode: "full"for normal conversation - Inject context with
conversation.append_context - Override with
conversation.echowhen needed - Use
conversation.interrupt+conversation.echofor immediate takeover
More from tavus-engineering/tavus-skills
tavus-cvi-quickstart
Quick start guide for Tavus Conversational Video Interface (CVI). Use when starting a real-time video conversation, creating your first persona, or testing the CVI API. Covers the minimal setup to get a conversation running.
32tavus-video-gen
Generate AI videos with Tavus replicas. Use when creating personalized videos from scripts or audio, adding custom backgrounds, watermarks, or generating videos at scale. Covers the video generation API, not real-time conversations.
29tavus-replica
Create and manage Tavus replicas (AI digital twins). Use when training custom replicas from video, listing stock replicas, or managing replica assets. Covers training video requirements, consent statements, and the Phoenix-3 model.
28tavus-overview
Overview of Tavus, the AI research lab pioneering human computing. Use when you need context about what Tavus is, their mission, core concepts like CVI and Human Computing, the model stack (Phoenix, Raven, Sparrow), or links to docs/platform/resources.
28tavus-cvi-ui
Integrate Tavus CVI into React apps using @tavus/cvi-ui components. Use when embedding conversations in web apps, customizing the video UI, using React hooks for CVI events, or building custom conversation interfaces with Vite/Next.js.
27tavus-cvi-knowledge
Add knowledge bases and persistent memories to Tavus CVI personas. Use when uploading documents for RAG, enabling personas to reference PDFs/websites, persisting context across conversations, or building personas that remember users.
27