#[ cfg( feature = "streaming" ) ]
use futures::StreamExt;
use api_claude::{ Client, CreateMessageRequest, Message, Role, Content };
use std::io::{ self, Write };
#[ tokio::main( flavor = "current_thread" ) ]
#[ allow( clippy::too_many_lines ) ]
async fn main() -> Result< (), Box< dyn core::error::Error > >
{
println!( "Initializing Claude client for interactive chat..." );
let client = Client::from_env()?;
println!( "🤖 Interactive Claude Chat" );
println!( "==========================" );
println!( "Type your messages and press Enter to chat with Claude." );
println!( "Commands : 'quit', 'exit', or 'bye' to end the conversation." );
#[ cfg( feature = "streaming" ) ]
println!( "✨ Streaming mode : Real-time responses enabled" );
#[ cfg( not( feature = "streaming" ) ) ]
println!( "📝 Standard mode : Simulated streaming responses" );
println!( "Model : Claude 3.5 Sonnet (optimized for interactive chat)\n" );
let mut conversation_history : Vec< Message > = Vec::new();
loop
{
print!( "💬 You : " );
io::stdout().flush()?;
let mut input = String::new();
match io::stdin().read_line( &mut input )
{
Ok( 0 ) =>
{
println!( "\n⚠️ No input available. Use this example in interactive terminal only." );
println!( "Run : cargo run --example claude_api_interactive" );
break;
}
Ok( _ ) => {}
Err( e ) =>
{
println!( "\n❌ Error reading input : {e}" );
println!( "Please try again or restart the chat application." );
break;
}
}
let user_message = input.trim().to_string();
if user_message.is_empty()
{
continue;
}
if matches!( user_message.to_lowercase().as_str(), "quit" | "exit" | "bye" )
{
println!( "\n👋 Goodbye! Thanks for chatting with Claude!" );
break;
}
conversation_history.push( Message {
role : Role::User,
content : vec![ Content::Text {
r#type : "text".to_string(),
text : user_message,
} ],
cache_control : None,
});
let request = CreateMessageRequest::builder()
.model( "claude-sonnet-4-5-20250929".to_string() ) .max_tokens( 1024 )
.messages( conversation_history.clone() )
.temperature( 0.7 ) .build();
print!( "\n🤖 Claude : " );
io::stdout().flush()?;
#[ cfg( feature = "streaming" ) ]
{
match client.create_message_stream( request ).await
{
Ok( mut stream ) =>
{
let mut full_response = String::new();
while let Some( chunk_result ) = stream.next().await
{
match chunk_result
{
Ok( event ) =>
{
if let Some( delta ) = event.delta()
{
if let Some( text_chunk ) = delta.text()
{
print!( "{text_chunk}" );
io::stdout().flush()?;
full_response.push_str( text_chunk );
}
}
}
Err( e ) =>
{
println!( "\n⚠️ Streaming error : {e}" );
println!( "Continuing with standard request..." );
break;
}
}
}
println!( "\n" );
if !full_response.is_empty()
{
conversation_history.push( Message {
role : Role::Assistant,
content : vec![ Content::Text {
r#type : "text".to_string(),
text : full_response,
} ],
cache_control : None,
});
}
}
Err( e ) =>
{
println!( "❌ Streaming error : {e}" );
println!( "Please try again or type 'quit' to exit.\n" );
}
}
}
#[ cfg( not( feature = "streaming" ) ) ]
{
match client.create_message( request ).await
{
Ok( response ) =>
{
if let Some( content ) = response.content.first()
{
if content.r#type == "text"
{
if let Some( text ) = &content.text
{
let words : Vec< &str > = text.split_whitespace().collect();
for ( i, word ) in words.iter().enumerate()
{
print!( "{}", word );
if i < words.len() - 1
{
print!( " " );
}
io::stdout().flush()?;
let delay_ms = match word.len()
{
0..=3 => 60,
4..=7 => 80,
_ => 100,
};
tokio::time::sleep( tokio::time::Duration::from_millis( delay_ms ) ).await;
}
println!( "\n" );
conversation_history.push( Message {
role : Role::Assistant,
content : vec![ Content::Text {
r#type : "text".to_string(),
text : text.clone(),
} ],
cache_control : None,
});
}
else
{
println!( "⚠️ Claude response contained no text content." );
}
}
else
{
println!( "⚠️ Claude returned non-text content (unexpected for chat)." );
}
}
else
{
println!( "⚠️ Claude generated no response content." );
}
}
Err( e ) =>
{
println!( "❌ Error : {}", e );
println!( "Please try again or type 'quit' to exit.\n" );
}
}
}
if conversation_history.len().is_multiple_of(10) && !conversation_history.is_empty()
{
println!( "📊 Conversation stats : {} messages exchanged", conversation_history.len() );
}
}
let user_messages = conversation_history.iter().filter( |m| matches!( m.role, Role::User ) ).count();
let ai_messages = conversation_history.iter().filter( |m| matches!( m.role, Role::Assistant ) ).count();
println!( "\n📊 === Chat Session Summary ===" );
println!( "User messages : {user_messages}" );
println!( "Claude responses : {ai_messages}" );
println!( "Total conversation turns : {}", conversation_history.len() );
#[ cfg( feature = "streaming" ) ]
println!( "Streaming mode : ✅ Real-time responses" );
#[ cfg( not( feature = "streaming" ) ) ]
println!( "Streaming mode : 📝 Simulated responses" );
println!( "\n🎯 === Production Implementation Notes ===" );
println!( "• Interactive chat with persistent conversation history" );
println!( "• Feature-gated streaming for different deployment scenarios" );
println!( "• Error recovery strategies for network issues" );
println!( "• Performance-optimized model selection (Claude 3.5 Sonnet)" );
println!( "• Production-ready input/output handling with proper flushing" );
println!( "• Graceful exit handling with session statistics" );
Ok( () )
}