diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 2dde6c07c..0b38db1eb 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1491,7 +1491,6 @@ name = "codex-protocol" version = "0.0.0" dependencies = [ "anyhow", - "base64", "codex-git", "codex-utils-image", "icu_decimal", diff --git a/codex-rs/protocol/Cargo.toml b/codex-rs/protocol/Cargo.toml index 08f837535..46f030c60 100644 --- a/codex-rs/protocol/Cargo.toml +++ b/codex-rs/protocol/Cargo.toml @@ -14,7 +14,6 @@ workspace = true [dependencies] codex-git = { workspace = true } -base64 = { workspace = true } codex-utils-image = { workspace = true } icu_decimal = { workspace = true } icu_locale_core = { workspace = true } diff --git a/codex-rs/protocol/src/models.rs b/codex-rs/protocol/src/models.rs index f93c157b7..9f66d08dc 100644 --- a/codex-rs/protocol/src/models.rs +++ b/codex-rs/protocol/src/models.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use base64::Engine; use codex_utils_image::load_and_resize_to_fit; use mcp_types::CallToolResult; use mcp_types::ContentBlock; @@ -175,6 +174,16 @@ fn invalid_image_error_placeholder( } } +fn unsupported_image_error_placeholder(path: &std::path::Path, mime: &str) -> ContentItem { + ContentItem::InputText { + text: format!( + "Codex cannot attach image at `{}`: unsupported image format `{}`.", + path.display(), + mime + ), + } +} + impl From for ResponseItem { fn from(item: ResponseInputItem) -> Self { match item { @@ -285,37 +294,20 @@ impl From> for ResponseInputItem { } else if err.is_invalid_image() { invalid_image_error_placeholder(&path, &err) } else { - match std::fs::read(&path) { - Ok(bytes) => { - let Some(mime_guess) = mime_guess::from_path(&path).first() - else { - return local_image_error_placeholder( - &path, - "unsupported MIME type (unknown)", - ); - }; - let mime = mime_guess.essence_str().to_owned(); - if !mime.starts_with("image/") { - return local_image_error_placeholder( - &path, - format!("unsupported MIME type `{mime}`"), - ); - } - let encoded = - base64::engine::general_purpose::STANDARD.encode(bytes); - ContentItem::InputImage { - image_url: format!("data:{mime};base64,{encoded}"), - } - } - Err(read_err) => { - tracing::warn!( - "Skipping image {} – could not read file: {}", - path.display(), - read_err - ); - local_image_error_placeholder(&path, &read_err) - } + let Some(mime_guess) = mime_guess::from_path(&path).first() else { + return local_image_error_placeholder( + &path, + "unsupported MIME type (unknown)", + ); + }; + let mime = mime_guess.essence_str().to_owned(); + if !mime.starts_with("image/") { + return local_image_error_placeholder( + &path, + format!("unsupported MIME type `{mime}`"), + ); } + unsupported_image_error_placeholder(&path, &mime) } } }, @@ -823,4 +815,36 @@ mod tests { Ok(()) } + + #[test] + fn local_image_unsupported_image_format_adds_placeholder() -> Result<()> { + let dir = tempdir()?; + let svg_path = dir.path().join("example.svg"); + std::fs::write( + &svg_path, + br#" +"#, + )?; + + let item = ResponseInputItem::from(vec![UserInput::LocalImage { + path: svg_path.clone(), + }]); + + match item { + ResponseInputItem::Message { content, .. } => { + assert_eq!(content.len(), 1); + let expected = format!( + "Codex cannot attach image at `{}`: unsupported image format `image/svg+xml`.", + svg_path.display() + ); + match &content[0] { + ContentItem::InputText { text } => assert_eq!(text, &expected), + other => panic!("expected placeholder text but found {other:?}"), + } + } + other => panic!("expected message response but got {other:?}"), + } + + Ok(()) + } }