use futures::StreamExt;
use molo::provider::OpenAiProvider;
use molo::{ChatRequest, ContentBlock, ImageContent, Message, Provider, StreamEvent};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let args: Vec<String> = std::env::args().skip(1).collect();
let Some(image_path) = args.first().filter(|a| !a.starts_with("--")) else {
eprintln!("usage: cargo run --example multimodal -- <image path> [--stream]");
std::process::exit(2);
};
let stream = args.iter().any(|a| a == "--stream");
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 provider = OpenAiProvider::new(base_url, api_key, model);
let bytes = std::fs::read(image_path)?;
let mime_type = image_mime_type(image_path);
println!("sending image ({mime_type}, {} bytes)", bytes.len());
let messages = vec![
Message::system("You are a helpful assistant."),
Message::user_blocks(vec![
ContentBlock::Text("Describe this image in detail.".into()),
ContentBlock::Image(ImageContent::new(mime_type, bytes)),
]),
];
let request = ChatRequest {
messages,
tools: vec![],
..Default::default()
};
if stream {
let mut stream = provider.stream_chat(request).await?;
while let Some(event) = stream.next().await {
match event? {
StreamEvent::Reasoning(r) => println!("[thinking] {r}"),
StreamEvent::Delta(t) => print!("{t}"),
StreamEvent::Done { .. } => println!(),
_ => {}
}
}
} else {
let response = provider.chat(request).await?;
let Message::Assistant { content, .. } = &response.message else {
unreachable!("reply must be an Assistant message in tool-free chat");
};
println!("assistant: {content}");
}
Ok(())
}
fn image_mime_type(path: &str) -> &'static str {
match path.rsplit('.').next() {
Some("jpg") | Some("jpeg") => "image/jpeg",
Some("png") => "image/png",
Some("gif") => "image/gif",
Some("webp") => "image/webp",
Some("bmp") => "image/bmp",
_ => "image/png",
}
}