#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::io::Cursor;
use image::{Rgb, RgbImage};
use rig_core::{
OneOrMany,
completion::{Message, Prompt},
message::{DocumentSourceKind, Image, ImageMediaType, UserContent},
};
#[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] fn draw_scene_png() -> Vec<u8> {
let (w, h) = (256u32, 256u32);
let mut img = RgbImage::from_pixel(w, h, Rgb([255, 255, 255]));
for y in 88..168 {
for x in 32..112 {
img.put_pixel(x, y, Rgb([220, 30, 30]));
}
}
let (cx, cy, r) = (184i32, 128i32, 44i32);
for y in 0..h as i32 {
for x in 0..w as i32 {
if (x - cx).pow(2) + (y - cy).pow(2) <= r.pow(2) {
img.put_pixel(x as u32, y as u32, Rgb([30, 60, 220]));
}
}
}
let mut png = Cursor::new(Vec::new());
img.write_to(&mut png, image::ImageFormat::Png)
.expect("png encode");
png.into_inner()
}
fn image_question(png: Vec<u8>, question: &str) -> Message {
Message::User {
content: OneOrMany::many(vec![
UserContent::Image(Image {
data: DocumentSourceKind::Raw(png),
media_type: Some(ImageMediaType::PNG),
detail: None,
additional_params: None,
}),
UserContent::text(question),
])
.expect("two content items"),
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let model_dir = std::env::args()
.nth(1)
.expect("usage: vision <mlx-model-dir>");
let png = draw_scene_png();
std::fs::write("vision_input.png", &png)?;
println!("wrote vision_input.png ({} bytes)", png.len());
let agent = emelex::Client::from_path(model_dir)?
.agent()
.preamble(
"You are a precise visual analyst. Answer questions about the attached \
image exactly and concisely.",
)
.build();
let description = agent
.prompt(image_question(
png.clone(),
"Describe every shape in this image: its kind, its color, and where it \
sits (left/right/center). One line per shape.",
))
.await?;
println!("\n[analysis]\n{description}");
let count = agent
.prompt(image_question(
png,
"How many distinct shapes are in this image? Reply with just the number.",
))
.await?;
println!("\n[shape count] {count}");
let svg = agent
.prompt(format!(
"Recreate this scene as an SVG image, 256x256, white \
background:\n{description}\n\nReply with ONLY the SVG markup, starting \
with <svg and ending with </svg>, no code fences."
))
.await?;
let svg = svg
.find("<svg")
.map_or(svg.as_str(), |at| &svg[at..])
.trim()
.to_string();
std::fs::write("vision_output.svg", &svg)?;
println!(
"\n[recreated] wrote vision_output.svg ({} bytes)\n{svg}",
svg.len()
);
Ok(())
}