use siumai::prelude::*;
use std::env;
async fn test_image_analysis_url<T: ChatCapability>(client: &T, provider_name: &str) {
println!(" 🖼️ Testing image analysis (URL) for {}...", provider_name);
let image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg";
let message = ChatMessage::user("What do you see in this image? Describe it briefly.")
.with_image(image_url.to_string(), Some("high".to_string()))
.build();
let messages = vec![message];
match client.chat(messages).await {
Ok(response) => {
let content = response.content_text().unwrap_or_default();
if !content.is_empty() {
println!(" ✅ Image analysis successful");
println!(" 📝 Description: {}", content.trim());
let content_lower = content.to_lowercase();
if content_lower.contains("image")
|| content_lower.contains("see")
|| content_lower.contains("picture")
|| content_lower.contains("photo")
{
println!(" 🎯 Response appears to contain image analysis");
} else {
println!(" ⚠️ Response may not contain actual image analysis");
}
} else {
println!(" ⚠️ Empty response received");
}
if let Some(usage) = response.usage {
println!(
" 📊 Usage: {} prompt + {} completion = {} total tokens",
usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
);
}
}
Err(e) => {
println!(" ⚠️ Image analysis failed: {}", e);
println!(" 💡 Note: Vision capability may not be available for this model/provider");
}
}
}
async fn test_multimodal_conversation<T: ChatCapability>(client: &T, provider_name: &str) {
println!(
" 💬 Testing multimodal conversation for {}...",
provider_name
);
let image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Square_-_black_simple.svg/240px-Square_-_black_simple.svg.png";
let messages = vec![
system!(
"You are a helpful assistant that can analyze images and answer questions about them."
),
ChatMessage::user(
"I'm going to show you a shape. Please tell me what shape it is and what color it is.",
)
.with_image(image_url.to_string(), Some("high".to_string()))
.build(),
];
match client.chat(messages).await {
Ok(response) => {
let content = response.content_text().unwrap_or_default();
if !content.is_empty() {
println!(" ✅ Multimodal conversation successful");
println!(" 📝 Response: {}", content.trim());
let content_lower = content.to_lowercase();
if content_lower.contains("square") || content_lower.contains("rectangle") {
println!(" 🎯 Correctly identified shape");
}
if content_lower.contains("black") {
println!(" 🎯 Correctly identified color");
}
}
if let Some(usage) = response.usage {
println!(
" 📊 Usage: {} prompt + {} completion = {} total tokens",
usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
);
}
}
Err(e) => {
println!(" ⚠️ Multimodal conversation failed: {}", e);
}
}
}
async fn test_multiple_images<T: ChatCapability>(client: &T, provider_name: &str) {
println!(" 🖼️🖼️ Testing multiple images for {}...", provider_name);
let image1_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Square_-_black_simple.svg/240px-Square_-_black_simple.svg.png";
let image2_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Circle_-_black_simple.svg/240px-Circle_-_black_simple.svg.png";
let message = ChatMessage::user("I'm showing you two shapes. Please describe each one.")
.with_image(image1_url.to_string(), Some("high".to_string()))
.with_image(image2_url.to_string(), Some("high".to_string()))
.build();
let messages = vec![message];
match client.chat(messages).await {
Ok(response) => {
let content = response.content_text().unwrap_or_default();
if !content.is_empty() {
println!(" ✅ Multiple images analysis successful");
println!(" 📝 Response: {}", content.trim());
let content_lower = content.to_lowercase();
let mentions_square =
content_lower.contains("square") || content_lower.contains("rectangle");
let mentions_circle =
content_lower.contains("circle") || content_lower.contains("round");
if mentions_square && mentions_circle {
println!(" 🎯 Both shapes correctly identified");
} else if mentions_square || mentions_circle {
println!(" 🎯 At least one shape identified");
} else {
println!(" ⚠️ Shapes may not have been correctly identified");
}
}
if let Some(usage) = response.usage {
println!(
" 📊 Usage: {} prompt + {} completion = {} total tokens",
usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
);
}
}
Err(e) => {
println!(" ⚠️ Multiple images analysis failed: {}", e);
println!(" 💡 Note: Some providers may not support multiple images in one message");
}
}
}
async fn test_vision_reasoning<T: ChatCapability>(client: &T, provider_name: &str) {
println!(
" 🧠 Testing vision with reasoning for {}...",
provider_name
);
let image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg";
let message = ChatMessage::user("Look at this image and tell me: 1) What type of environment is this? 2) What time of day might it be? 3) What activities could someone do here? Please explain your reasoning.")
.with_image(image_url.to_string(), Some("high".to_string()))
.build();
let messages = vec![message];
match client.chat(messages).await {
Ok(response) => {
let content = response.content_text().unwrap_or_default();
if !content.is_empty() {
println!(" ✅ Vision reasoning successful");
println!(" 📝 Analysis: {}", content.trim());
let content_lower = content.to_lowercase();
let has_reasoning = content_lower.contains("because")
|| content_lower.contains("since")
|| content_lower.contains("due to")
|| content_lower.contains("appears")
|| content_lower.contains("suggests");
if has_reasoning {
println!(" 🧠 Response contains reasoning elements");
}
}
if let Some(usage) = response.usage {
println!(
" 📊 Usage: {} prompt + {} completion = {} total tokens",
usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
);
}
}
Err(e) => {
println!(" ⚠️ Vision reasoning failed: {}", e);
}
}
}
async fn test_provider_vision(provider_name: &str, api_key_env: &str, model: &str) {
if env::var(api_key_env).is_err() {
println!(
"⏭️ Skipping {} vision tests: {} not set",
provider_name, api_key_env
);
return;
}
println!("👁️ Testing {} vision capabilities...", provider_name);
match provider_name {
"OpenAI" => {
let api_key = env::var(api_key_env).unwrap();
let mut builder = LlmBuilder::new().openai().api_key(api_key).model(model);
if let Ok(base_url) = env::var("OPENAI_BASE_URL") {
builder = builder.base_url(base_url);
}
match builder.build().await {
Ok(client) => {
test_image_analysis_url(&client, provider_name).await;
test_multimodal_conversation(&client, provider_name).await;
test_multiple_images(&client, provider_name).await;
test_vision_reasoning(&client, provider_name).await;
}
Err(e) => {
println!("❌ Failed to build OpenAI client: {}", e);
return;
}
}
}
"Anthropic" => {
let api_key = env::var(api_key_env).unwrap();
let mut builder = LlmBuilder::new().anthropic().api_key(api_key).model(model);
if let Ok(base_url) = env::var("ANTHROPIC_BASE_URL") {
builder = builder.base_url(base_url);
}
match builder.build().await {
Ok(client) => {
test_image_analysis_url(&client, provider_name).await;
test_multimodal_conversation(&client, provider_name).await;
test_multiple_images(&client, provider_name).await;
test_vision_reasoning(&client, provider_name).await;
}
Err(e) => {
println!("❌ Failed to build Anthropic client: {}", e);
return;
}
}
}
"Gemini" => {
let api_key = env::var(api_key_env).unwrap();
match LlmBuilder::new()
.gemini()
.api_key(api_key)
.model(model)
.build()
.await
{
Ok(client) => {
test_image_analysis_url(&client, provider_name).await;
test_multimodal_conversation(&client, provider_name).await;
test_multiple_images(&client, provider_name).await;
test_vision_reasoning(&client, provider_name).await;
}
Err(e) => {
println!("❌ Failed to build Gemini client: {}", e);
return;
}
}
}
"xAI" => {
let api_key = env::var(api_key_env).unwrap();
match LlmBuilder::new()
.xai()
.api_key(api_key)
.model(model)
.build()
.await
{
Ok(client) => {
test_image_analysis_url(&client, provider_name).await;
test_multimodal_conversation(&client, provider_name).await;
test_multiple_images(&client, provider_name).await;
test_vision_reasoning(&client, provider_name).await;
}
Err(e) => {
println!("❌ Failed to build xAI client: {}", e);
return;
}
}
}
_ => {
println!("❌ Unknown provider: {}", provider_name);
return;
}
}
println!("✅ {} vision testing completed\n", provider_name);
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
#[ignore]
async fn test_openai_vision() {
test_provider_vision("OpenAI", "OPENAI_API_KEY", "gpt-4o").await;
}
#[tokio::test]
#[ignore]
async fn test_anthropic_vision() {
test_provider_vision(
"Anthropic",
"ANTHROPIC_API_KEY",
"claude-3-5-sonnet-20241022",
)
.await;
}
#[tokio::test]
#[ignore]
async fn test_gemini_vision() {
test_provider_vision("Gemini", "GEMINI_API_KEY", "gemini-2.5-pro").await;
}
#[tokio::test]
#[ignore]
async fn test_xai_vision() {
test_provider_vision("xAI", "XAI_API_KEY", "grok-2-vision-1212").await;
}
#[tokio::test]
#[ignore]
async fn test_all_provider_vision() {
println!("🚀 Running vision capability tests for all available providers...\n");
let providers = vec![
("OpenAI", "OPENAI_API_KEY", "gpt-4o"),
(
"Anthropic",
"ANTHROPIC_API_KEY",
"claude-3-5-sonnet-20241022",
),
("Gemini", "GEMINI_API_KEY", "gemini-2.5-pro"),
("xAI", "XAI_API_KEY", "grok-2-vision-1212"),
];
for (provider_name, api_key_env, model) in providers {
test_provider_vision(provider_name, api_key_env, model).await;
}
println!("🎉 All provider vision testing completed!");
}
}