use mixtape_core::events::{AgentEvent, AgentHook};
use mixtape_core::{Agent, ImageFormat, Nova2Lite, Tool, ToolError, ToolResult};
use reqwest::Client;
use schemars::JsonSchema;
use serde::Deserialize;
use std::time::Duration;
#[derive(Debug, Deserialize, JsonSchema)]
struct GetTestImageInput {}
struct GetTestImageTool {
client: Client,
}
impl GetTestImageTool {
fn new() -> Self {
let client = Client::builder()
.timeout(Duration::from_secs(30))
.build()
.expect("Failed to create HTTP client");
Self { client }
}
}
impl Tool for GetTestImageTool {
type Input = GetTestImageInput;
fn name(&self) -> &str {
"get_test_image"
}
fn description(&self) -> &str {
"Fetch a test image. Returns the image data which you can then analyze and describe in detail."
}
async fn execute(&self, _input: Self::Input) -> Result<ToolResult, ToolError> {
let url = "https://picsum.photos/seed/demo/400/400";
let response = self
.client
.get(url)
.send()
.await
.map_err(|e| ToolError::Custom(format!("Failed to fetch image: {}", e)))?;
if !response.status().is_success() {
return Err(ToolError::Custom(format!(
"Failed to fetch image: HTTP {}",
response.status()
)));
}
let bytes = response
.bytes()
.await
.map_err(|e| ToolError::Custom(format!("Failed to read image data: {}", e)))?;
Ok(ToolResult::image(ImageFormat::Jpeg, bytes.to_vec()))
}
}
struct ToolResultLogger;
impl AgentHook for ToolResultLogger {
fn on_event(&self, event: &AgentEvent) {
match event {
AgentEvent::ToolRequested { name, .. } => {
println!("[Hook] Tool '{}' requested...", name);
}
AgentEvent::ToolCompleted {
name,
output,
duration,
..
} => {
match output {
ToolResult::Image { format, data } => {
println!(
"[Hook] Tool '{}' returned {:?} image: {} bytes (took {:?})",
name,
format,
data.len(),
duration
);
}
ToolResult::Document {
format,
data,
name: doc_name,
} => {
let doc_name = doc_name.as_deref().unwrap_or("unnamed");
println!(
"[Hook] Tool '{}' returned {:?} document '{}': {} bytes (took {:?})",
name,
format,
doc_name,
data.len(),
duration
);
}
ToolResult::Text(text) => {
println!(
"[Hook] Tool '{}' returned text: {} chars (took {:?})",
name,
text.len(),
duration
);
}
ToolResult::Json(value) => {
println!(
"[Hook] Tool '{}' returned JSON (took {:?}): {}",
name,
duration,
serde_json::to_string(value).unwrap_or_default()
);
}
}
}
AgentEvent::ToolFailed { name, error, .. } => {
println!("[Hook] Tool '{}' failed: {}", name, error);
}
_ => {}
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Image Tool Example ===\n");
println!("This example tests whether the model can actually see and describe images");
println!("returned by tools.\n");
println!("The test image is available at: https://picsum.photos/seed/demo/400/400");
println!("It shows a black-and-white photo of water droplets on a rough surface.\n");
println!("Evaluate how well the model describes what it sees!\n");
println!("---\n");
let agent = Agent::builder()
.bedrock(Nova2Lite)
.add_trusted_tool(GetTestImageTool::new())
.build()
.await?;
agent.add_hook(ToolResultLogger);
let response = agent
.run(
"Use the get_test_image tool to fetch the image, then describe in detail what you see.",
)
.await?;
println!("Model's description:\n");
println!("{}", response.text);
Ok(())
}