Fix uploaded images reaching Claude Code empty
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
81d9df4e6e
commit
a2b9528e0b
3 files changed
+33
-1
No files matched your search
@@ -361,7 +361,12 @@ fn save_images(cwd: &str, parts: &[Part]) -> Vec<String> {
|
||||
let mut out = Vec::new();
|
||||
for (i, p) in parts.iter().enumerate() {
|
||||
let Part::Image { image } = p else { continue };
|
||||
// An image without bytes (an unresolved reference) would become an empty file.
|
||||
let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(&image.data) else { continue };
|
||||
if bytes.is_empty() {
|
||||
tracing::warn!("claude code: image {i} of the message has no data – not saved");
|
||||
continue;
|
||||
}
|
||||
let ext = image.media_type.split('/').nth(1).unwrap_or("png").split('+').next().unwrap_or("png");
|
||||
let path = dir.join(format!("chat-{}-{i}.{ext}", now_ms()));
|
||||
if std::fs::create_dir_all(&dir).is_ok() {
|
||||
@@ -379,7 +384,8 @@ pub async fn run(app: Arc<App>, session_id: &str, cancel: CancellationToken) ->
|
||||
let session = app.session(session_id)?;
|
||||
let owner = session.owner_id.clone();
|
||||
let model_id = session.model.strip_prefix("claude-code/").unwrap_or("default").to_string();
|
||||
let history = crate::runner::active_history(app.store.messages(session_id)?);
|
||||
// With the bytes of the images filled in (they are stored as references).
|
||||
let history = crate::runner::active_history(app.store.messages_for_model(session_id)?);
|
||||
let Some(last_user) = history.iter().rposition(|m| m.role == Role::User && m.parts.iter().any(|p| matches!(p, Part::Text { .. } | Part::Image { .. }))) else {
|
||||
bail!("nothing to do: no user message");
|
||||
};
|
||||
@@ -956,6 +962,21 @@ pub async fn update() -> Result<String> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn uploaded_images_are_saved_with_their_bytes_only() {
|
||||
let dir = std::env::temp_dir().join(format!("cc-uploads-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let parts = vec![
|
||||
Part::Image { image: ImageData { media_type: "image/png".into(), data: "QUJD".into(), blob: None, size: None } },
|
||||
// A reference whose bytes were not filled in: must not become an empty file.
|
||||
Part::Image { image: ImageData { media_type: "image/png".into(), data: String::new(), blob: Some("x".into()), size: None } },
|
||||
];
|
||||
let saved = save_images(&dir.display().to_string(), &parts);
|
||||
assert_eq!(saved.len(), 1);
|
||||
assert_eq!(std::fs::read(&saved[0]).unwrap(), b"ABC");
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn questions_roundtrip_with_the_cli_format() {
|
||||
let input = json!({"questions": [
|
||||
|
||||
Reference in new issue
Block a user