use api_huggingface::
{
Client,
environment::HuggingFaceEnvironmentImpl,
components::
{
input::InferenceParameters,
models::Models,
},
secret::Secret,
};
use std::
{
io::{ self, Write as IoWrite },
};
use core::fmt::Write;
#[ derive( Debug ) ]
struct ChatSession
{
history : Vec< ( String, String ) >, }
impl ChatSession
{
fn new() -> Self
{
Self { history : Vec::new() }
}
fn add_exchange( &mut self, user_msg : impl Into< String >, assistant_msg : impl Into< String > )
{
self.history.push( ( user_msg.into(), assistant_msg.into() ) );
if self.history.len() > 10
{
self.history.remove( 0 );
}
}
fn build_prompt( &self, new_message : &str ) -> String
{
let mut prompt = String::from( "You are a helpful, friendly AI assistant. Provide clear, concise, and helpful responses.\n\n" );
for ( user_msg, assistant_msg ) in &self.history
{
writeln!( &mut prompt, "User : {user_msg}" ).unwrap();
writeln!( &mut prompt, "Assistant : {assistant_msg}" ).unwrap();
writeln!( &mut prompt ).unwrap();
}
write!( &mut prompt, "User : {new_message}\nAssistant:" ).unwrap();
prompt
}
fn exchange_count( &self ) -> usize
{
self.history.len()
}
}
#[ tokio::main ]
async fn main() -> Result< (), Box< dyn std::error::Error > >
{
println!( "🤗 HuggingFace Interactive Chat" );
println!( "================================\n" );
let api_key = Secret::load_from_env( "HUGGINGFACE_API_KEY" )?;
let env = HuggingFaceEnvironmentImpl::build( api_key, None )?;
let client = Client::build( env )?;
let params = InferenceParameters::new()
.with_temperature( 0.7 )
.with_max_new_tokens( 300 )
.with_top_p( 0.9 );
let model = Models::kimi_k2_instruct();
println!( "🤖 Model : {model}" );
println!( "📋 Temperature : 0.7 | Max tokens : 300\n" );
println!( "💬 Chat with the AI! Type 'quit', 'exit', or 'bye' to end the conversation.\n" );
println!( "─────────────────────────────────────────\n" );
let mut session = ChatSession::new();
loop
{
print!( "You : " );
io::stdout().flush()?;
let mut input = String::new();
let bytes_read = io::stdin().read_line( &mut input )?;
if bytes_read == 0
{
println!( "\n👋 Goodbye!" );
break;
}
let input = input.trim();
if input.is_empty()
{
continue;
}
if matches!( input.to_lowercase().as_str(), "quit" | "exit" | "bye" )
{
println!( "\n👋 Thanks for chatting! Goodbye!" );
break;
}
let prompt = session.build_prompt( input );
print!( "\n🤖 " );
io::stdout().flush()?;
match client.inference().create_with_parameters( &prompt, model, params.clone() ).await
{
Ok( response ) =>
{
let answer = response.extract_text_or_default( "Sorry, I couldn't generate a response." );
let answer = clean_response( &answer );
println!( "{answer}\n" );
session.add_exchange( input, &answer );
let exchanges = session.exchange_count();
if exchanges > 0
{
println!( "📊 Context : {exchanges} exchange{}\n", if exchanges == 1 { "" } else { "s" } );
}
println!( "─────────────────────────────────────────\n" );
},
Err( e ) =>
{
eprintln!( "\n❌ Error : {e}\n" );
eprintln!( "💡 The conversation will continue. Try your message again.\n" );
println!( "─────────────────────────────────────────\n" );
}
}
}
let exchanges = session.exchange_count();
if exchanges > 0
{
println!( "\n📈 Session Summary:" );
println!( " Total exchanges : {exchanges}" );
println!( " Context retained : {} exchanges\n", exchanges.min( 10 ) );
}
println!( "✅ Chat session ended successfully!\n" );
Ok( () )
}
fn clean_response( response : &str ) -> String
{
let response = response.trim();
let prefixes = [ "Assistant:", "AI:", "A:" ];
let mut cleaned = response.to_string();
for prefix in &prefixes
{
if cleaned.starts_with( prefix )
{
cleaned = cleaned[ prefix.len().. ].trim().to_string();
}
}
while cleaned.contains( "\n\n\n" )
{
cleaned = cleaned.replace( "\n\n\n", "\n\n" );
}
cleaned.trim().to_string()
}