#![allow(clippy::uninlined_format_args)]
use openai_ergonomic::{Client, Error, Response};
use std::collections::VecDeque;
use std::io::{self, Write};
#[derive(Debug, Clone)]
struct ConversationTurn {
role: String,
content: String,
token_count: Option<i32>,
}
#[derive(Debug)]
struct ConversationManager {
history: VecDeque<ConversationTurn>,
max_history: usize,
total_tokens_used: i32,
system_message: Option<String>,
}
impl ConversationManager {
const fn new(system_message: Option<String>, max_history: usize) -> Self {
Self {
history: VecDeque::new(),
max_history,
total_tokens_used: 0,
system_message,
}
}
fn add_user_message(&mut self, content: String) {
self.add_turn(ConversationTurn {
role: "user".to_string(),
content,
token_count: None,
});
}
fn add_assistant_message(&mut self, content: String, token_count: Option<i32>) {
self.add_turn(ConversationTurn {
role: "assistant".to_string(),
content,
token_count,
});
}
fn add_turn(&mut self, turn: ConversationTurn) {
if self.history.len() >= self.max_history {
self.history.pop_front();
}
self.history.push_back(turn);
}
#[allow(clippy::missing_const_for_fn)] fn update_token_usage(&mut self, prompt_tokens: i32, completion_tokens: i32) {
let total = prompt_tokens + completion_tokens;
self.total_tokens_used += total;
}
fn display_history(&self) {
println!("\n=== Conversation History ===");
if let Some(ref system) = self.system_message {
println!("System: {system}");
println!();
}
for (i, turn) in self.history.iter().enumerate() {
let token_info = turn
.token_count
.map_or_else(String::new, |tokens| format!(" ({tokens} tokens)"));
println!(
"{}. {}{}: {}",
i + 1,
turn.role
.chars()
.next()
.unwrap()
.to_uppercase()
.collect::<String>()
+ &turn.role[1..],
token_info,
turn.content
);
}
println!("\nTotal tokens used: {}", self.total_tokens_used);
println!("Messages in history: {}", self.history.len());
println!("=============================\n");
}
fn get_conversation_for_api(&self) -> Vec<(String, String)> {
let mut messages = Vec::new();
if let Some(ref system) = self.system_message {
messages.push(("system".to_string(), system.clone()));
}
for turn in &self.history {
messages.push((turn.role.clone(), turn.content.clone()));
}
messages
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("OpenAI Ergonomic - Comprehensive Chat Example");
println!("============================================");
println!();
let client = match Client::from_env() {
Ok(client_builder) => {
println!(" Client initialized successfully");
client_builder.build()
}
Err(e) => {
eprintln!(" Failed to initialize client: {e}");
eprintln!("Make sure OPENAI_API_KEY environment variable is set");
return Err(e.into());
}
};
let system_message = "You are a helpful AI assistant. Provide concise, informative responses. \
Always be polite and professional. If asked about your capabilities, \
explain what you can help with clearly."
.to_string();
let mut conversation = ConversationManager::new(Some(system_message), 10);
println!(" Conversation manager initialized (max history: 10 messages)");
println!(" System message configured");
println!();
demonstrate_basic_chat(&client, &mut conversation).await?;
demonstrate_multi_turn_chat(&client, &mut conversation).await?;
demonstrate_streaming_chat(&client, &mut conversation).await?;
demonstrate_token_tracking(&client, &mut conversation).await?;
demonstrate_error_handling(&client).await?;
conversation.display_history();
println!(" Chat comprehensive example completed successfully!");
println!("This example demonstrated:");
println!(" • Multi-turn conversation management");
println!(" • Message history tracking and rotation");
println!(" • System message configuration");
println!(" • Token usage monitoring");
println!(" • Error handling patterns");
println!(" • Streaming response handling");
Ok(())
}
async fn demonstrate_basic_chat(
client: &Client,
conversation: &mut ConversationManager,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Example 1: Basic Chat Completion");
println!("----------------------------------");
let user_message = "Hello! Can you explain what you can help me with?";
conversation.add_user_message(user_message.to_string());
println!("User: {user_message}");
print!("Assistant: ");
io::stdout().flush()?;
let messages = conversation.get_conversation_for_api();
let mut chat_builder = client.chat();
for (role, content) in messages {
match role.as_str() {
"system" => chat_builder = chat_builder.system(content),
"user" => chat_builder = chat_builder.user(content),
"assistant" => chat_builder = chat_builder.assistant(content),
_ => {} }
}
let response = client.send_chat(chat_builder.temperature(0.7)).await?;
if let Some(content) = response.content() {
println!("{content}");
conversation.add_assistant_message(content.to_string(), None);
if let Some(usage) = response.usage() {
conversation.update_token_usage(usage.prompt_tokens, usage.completion_tokens);
}
} else {
println!("No response content received");
}
println!();
Ok(())
}
async fn demonstrate_multi_turn_chat(
client: &Client,
conversation: &mut ConversationManager,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Example 2: Multi-turn Conversation");
println!("------------------------------------");
let questions = vec![
"What's the capital of France?",
"What's the population of that city?",
"Can you tell me an interesting fact about it?",
];
for question in questions {
conversation.add_user_message(question.to_string());
println!("User: {question}");
print!("Assistant: ");
io::stdout().flush()?;
let messages = conversation.get_conversation_for_api();
let mut chat_builder = client.chat();
for (role, content) in messages {
match role.as_str() {
"system" => chat_builder = chat_builder.system(content),
"user" => chat_builder = chat_builder.user(content),
"assistant" => chat_builder = chat_builder.assistant(content),
_ => {}
}
}
let response = client.send_chat(chat_builder.temperature(0.3)).await?;
if let Some(content) = response.content() {
println!("{content}");
conversation.add_assistant_message(content.to_string(), None);
if let Some(usage) = response.usage() {
conversation.update_token_usage(usage.prompt_tokens, usage.completion_tokens);
}
}
println!();
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
}
Ok(())
}
async fn demonstrate_streaming_chat(
_client: &Client,
conversation: &mut ConversationManager,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Example 3: Streaming Chat Response");
println!("------------------------------------");
let streaming_question = "Can you write a short poem about programming?";
conversation.add_user_message(streaming_question.to_string());
println!("User: {streaming_question}");
println!("Assistant (streaming): ");
println!(" Streaming functionality is being implemented...");
println!("Future implementation will show real-time token-by-token responses");
let simulated_response = "Programming flows like poetry in motion,\nEach function a verse, each loop a devotion.\nVariables dance through memory's halls,\nWhile algorithms answer logic's calls.";
for char in simulated_response.chars() {
print!("{char}");
io::stdout().flush()?;
tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
}
println!("\n");
conversation.add_assistant_message(simulated_response.to_string(), None);
Ok(())
}
async fn demonstrate_token_tracking(
client: &Client,
conversation: &mut ConversationManager,
) -> Result<(), Box<dyn std::error::Error>> {
println!(" Example 4: Token Usage Tracking");
println!("---------------------------------");
let efficiency_question = "In one sentence, what is machine learning?";
conversation.add_user_message(efficiency_question.to_string());
println!("User: {efficiency_question}");
print!("Assistant: ");
io::stdout().flush()?;
let messages = conversation.get_conversation_for_api();
let mut chat_builder = client.chat().max_completion_tokens(50);
for (role, content) in messages {
match role.as_str() {
"system" => chat_builder = chat_builder.system(content),
"user" => chat_builder = chat_builder.user(content),
"assistant" => chat_builder = chat_builder.assistant(content),
_ => {}
}
}
let response = client.send_chat(chat_builder).await?;
if let Some(content) = response.content() {
println!("{content}");
if let Some(usage) = response.usage() {
println!("\n Token Usage Breakdown:");
println!(" Prompt tokens: {}", usage.prompt_tokens);
println!(" Completion tokens: {}", usage.completion_tokens);
println!(" Total tokens: {}", usage.total_tokens);
conversation.update_token_usage(usage.prompt_tokens, usage.completion_tokens);
conversation.add_assistant_message(content.to_string(), Some(usage.completion_tokens));
} else {
conversation.add_assistant_message(content.to_string(), None);
}
}
println!();
Ok(())
}
async fn demonstrate_error_handling(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
println!(" Example 5: Error Handling Patterns");
println!("------------------------------------");
println!("Testing various error scenarios...\n");
println!("Test 1: Invalid model name");
let invalid_model_builder = client.chat()
.user("Hello")
.temperature(0.7);
match client.send_chat(invalid_model_builder).await {
Ok(_) => println!(" Request succeeded (model validation not yet implemented)"),
Err(e) => match &e {
Error::Api {
status, message, ..
} => {
println!(" API Error ({status}): {message}");
}
Error::Http(reqwest_err) => {
println!(" HTTP Error: {reqwest_err}");
}
Error::InvalidRequest(msg) => {
println!(" Invalid Request: {msg}");
}
_ => {
println!(" Unexpected Error: {e}");
}
},
}
println!("\nTest 2: Empty message validation");
let empty_builder = client.chat();
match client.send_chat(empty_builder).await {
Ok(_) => println!(" Empty request unexpectedly succeeded"),
Err(Error::InvalidRequest(msg)) => {
println!(" Validation caught empty request: {msg}");
}
Err(e) => {
println!(" Unexpected error type: {e}");
}
}
println!("\nTest 3: Configuration validation");
println!(" Client configuration is valid (created successfully)");
println!("\n Error handling patterns demonstrated:");
println!(" • API error classification");
println!(" • Request validation");
println!(" • Network error handling");
println!(" • Configuration validation");
println!();
Ok(())
}