use std::io::Write;
use std::sync::Arc;
use futures::stream::StreamExt;
use molo::agent::{Agent, MessageChunk};
use molo::memory::{SummarizeStrategy, WindowMemory};
use molo::{OpenAiProvider, ReActAgent, ToolRegistry};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_env_filter("molo=info")
.without_time()
.init();
dotenvy::dotenv().ok();
let base_url =
std::env::var("MOLO_BASE_URL").unwrap_or_else(|_| "https://api.openai.com/v1".to_string());
let api_key = std::env::var("MOLO_API_KEY").unwrap_or_default();
let model = std::env::var("MOLO_MODEL").unwrap_or_else(|_| "gpt-4o-mini".to_string());
let max_tokens: usize = std::env::var("MOLO_MAX_TOKENS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(400);
let summary_max_tokens: u32 = std::env::var("MOLO_SUMMARY_MAX_TOKENS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(150);
let provider = OpenAiProvider::new(base_url, api_key, model);
let memory = WindowMemory::new(max_tokens).with_strategy(Arc::new(
SummarizeStrategy::new(provider.clone()).with_summary_max_tokens(summary_max_tokens),
));
let mut agent = ReActAgent::new(
provider,
ToolRegistry::new(),
"You are a helpful assistant. The conversation may be long; early content will be compressed into summaries, but the key points will be kept.",
)
.with_memory(memory);
println!(
"Memory budget: {max_tokens} tokens; summary output cap: {summary_max_tokens} tokens.\n\
When compression happens the log prints: context summarized by LLM (compressed / kept / summary_tokens).\n"
);
let mut input = String::new();
loop {
input.clear();
println!("user:");
let read = std::io::stdin().read_line(&mut input)?;
if read == 0 {
break; }
let input = input.trim();
if input.is_empty() {
continue;
}
if input == "exit" || input == "quit" {
break;
}
let mut stream = agent.run_stream(input).await?;
let mut prefix_printed = false;
while let Some(event) = stream.next().await {
match event? {
MessageChunk::Delta(delta) => {
if !prefix_printed {
print!("assistant: ");
std::io::stdout().flush()?;
prefix_printed = true;
}
print!("{delta}");
std::io::stdout().flush()?;
}
MessageChunk::ToolCall {
id,
name,
arguments,
} => {
println!("\n → calling {name}(#{id}), arguments: {arguments}");
}
MessageChunk::ToolResult { id, name, content } => {
println!(" → {name}(#{id}) returned: {content}");
}
MessageChunk::Done(summary) => {
println!(
"\n — run summary: rounds={} tool_calls={} usage={}/{} tokens",
summary.rounds,
summary.tool_calls,
summary.usage.prompt_tokens,
summary.usage.completion_tokens,
);
break;
}
MessageChunk::Cancelled => {
println!("\n[cancelled]");
break;
}
_ => {}
}
}
println!();
}
Ok(())
}