Multi-turn conversations allow the AI to remember previous messages and maintain context throughout a discussion. This is essential for building chatbots, assistants, and any application that requires ongoing dialogue.
use t3router::t3::client::Client;use t3router::t3::message::{Message, Type};use t3router::t3::config::Config;let mut client = Client::new(cookies, convex_session_id);client.init().await?;let config = Config::new();// First message in the conversationlet response1 = client .send( "gemini-2.5-flash-lite", Some(Message::new( Type::User, "I'm planning a trip to Paris. What are the top 3 attractions?".to_string(), )), Some(config.clone()), ) .await?;println!("User: I'm planning a trip to Paris. What are the top 3 attractions?");println!("Assistant: {}", response1.content);
2
Continue the conversation
// The client remembers the previous contextlet response2 = client .send( "gemini-2.5-flash-lite", Some(Message::new( Type::User, "Tell me more about the first one.".to_string(), )), Some(config.clone()), ) .await?;println!("\nUser: Tell me more about the first one.");println!("Assistant: {}", response2.content);
The AI understands “the first one” refers to the first attraction mentioned earlier.
3
Ask follow-up questions
let response3 = client .send( "gemini-2.5-flash-lite", Some(Message::new( Type::User, "What's the best time to visit?".to_string(), )), Some(config), ) .await?;println!("\nUser: What's the best time to visit?");println!("Assistant: {}", response3.content);
The AI maintains full context about Paris and the attractions.
You can build conversation history before sending a message using append_message() (client.rs:264-266):
client.new_conversation();// Add context to the conversationclient.append_message(Message::new( Type::User, "Let's play a word association game.".to_string(),));client.append_message(Message::new( Type::Assistant, "Great! I'm ready to play. Go ahead!".to_string(),));client.append_message(Message::new(Type::User, "Ocean".to_string()));client.append_message(Message::new(Type::Assistant, "Waves".to_string()));client.append_message(Message::new(Type::User, "Beach".to_string()));// Now send without a new message - it will respond to "Beach"let response = client .send("gemini-2.5-flash-lite", None, Some(config)) .await?;
When you pass None as the second parameter to send(), it sends the existing conversation history without adding a new message. This is useful when you’ve pre-populated messages with append_message().
// Get the current thread IDif let Some(thread_id) = client.get_thread_id() { println!("Thread ID: {}", thread_id);}// Get message countlet message_count = client.get_messages().len();println!("Total messages: {}", message_count);
The new_conversation() method (client.rs:252-255) resets the thread and clears all messages:
// Clear all history and start freshclient.new_conversation();// This will start a completely new conversation threadlet response = client .send( "claude-3.7", Some(Message::new(Type::User, "Hello!".to_string())), Some(config), ) .await?;
use dotenv::dotenv;use t3router::t3::{ client::Client, config::Config, message::{Message, Type},};#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { dotenv().ok(); let cookies = std::env::var("COOKIES").expect("COOKIES not set"); let convex_session_id = std::env::var("CONVEX_SESSION_ID") .expect("CONVEX_SESSION_ID not set"); let mut client = Client::new(cookies, convex_session_id); client.init().await?; let config = Config::new(); println!("=== Multi-turn Conversation ==="); client.new_conversation(); client.append_message(Message::new( Type::User, "I'm planning a trip to Paris. What are the top 3 attractions?".to_string(), )); let response1 = client .send("gemini-2.5-flash-lite", None, Some(config.clone())) .await?; println!("User: I'm planning a trip to Paris. What are the top 3 attractions?"); println!("Assistant: {}", response1.content); let response2 = client .send( "gemini-2.5-flash-lite", Some(Message::new( Type::User, "Tell me more about the first one.".to_string(), )), Some(config.clone()), ) .await?; println!("\nUser: Tell me more about the first one."); println!("Assistant: {}", response2.content); let response3 = client .send( "gemini-2.5-flash-lite", Some(Message::new( Type::User, "What's the best time to visit?".to_string(), )), Some(config), ) .await?; println!("\nUser: What's the best time to visit?"); println!("Assistant: {}", response3.content); println!("\n=== Conversation Summary ==="); println!("Total messages: {}", client.get_messages().len()); println!("Thread ID: {:?}", client.get_thread_id()); Ok(())}
=== Multi-turn Conversation ===User: I'm planning a trip to Paris. What are the top 3 attractions?Assistant: The top 3 attractions in Paris are:1. The Eiffel Tower2. The Louvre Museum3. Notre-Dame CathedralUser: Tell me more about the first one.Assistant: The Eiffel Tower is an iconic iron lattice tower...User: What's the best time to visit?Assistant: The best time to visit Paris is during spring (April-June)...=== Conversation Summary ===Total messages: 6Thread ID: Some("a1b2c3d4-e5f6-7890-abcd-ef1234567890")
// More control over conversation structureclient.append_message(Message::new(Type::User, "Hello".into()));client.append_message(Message::new(Type::Assistant, "Hi there!".into()));client.append_message(Message::new(Type::User, "How are you?".into()));// Send all at oncelet response = client.send("model", None, config).await?;
Use Approach 2 when you need to inject specific conversation history or simulate a conversation state. This is useful for testing or implementing conversation templates.