Show images read by agents; keep tool-result images with Claude Code
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
92a99c6a65
commit
99ba3835f3
4 files changed
+36
-3
No files matched your search
@@ -11,7 +11,7 @@ use crate::config::ModelConfig;
|
||||
use crate::permission::{Decision, Evaluator, ToolInfo, bash_prefix, is_dangerous, split_command};
|
||||
use crate::provider::{ChatRequest, Provider, StreamEvent};
|
||||
use crate::store::UsageRecord;
|
||||
use crate::types::{Event, Message, Part, Role, Session, Usage, new_id, now_ms};
|
||||
use crate::types::{Event, ImageData, Message, Part, Role, Session, Usage, new_id, now_ms};
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{Value, json};
|
||||
@@ -286,6 +286,20 @@ fn result_text(content: &Value) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Images inside a tool result (the CLI's Read of a picture): base64 blocks.
|
||||
fn result_images(content: &Value) -> Vec<ImageData> {
|
||||
content
|
||||
.as_array()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter(|b| b["type"] == "image" && b["source"]["type"] == "base64")
|
||||
.filter_map(|b| {
|
||||
let data = b["source"]["data"].as_str().filter(|d| !d.is_empty())?;
|
||||
Some(ImageData { media_type: b["source"]["media_type"].as_str().unwrap_or("image/png").to_string(), data: data.to_string(), blob: None, size: None })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Images of the prompt as files the CLI can read (it has no inline image input in this mode).
|
||||
fn save_images(cwd: &str, parts: &[Part]) -> Vec<String> {
|
||||
use base64::Engine;
|
||||
@@ -458,7 +472,7 @@ pub async fn run(app: Arc<App>, session_id: &str, cancel: CancellationToken) ->
|
||||
let id = b["tool_use_id"].as_str().unwrap_or("").to_string();
|
||||
let is_error = b["is_error"].as_bool().unwrap_or(false);
|
||||
let meta = if is_error { None } else { diffs.remove(&id).map(|d| json!({"diff": d})) };
|
||||
parts.push(Part::ToolResult { name: calls.get(&id).cloned().unwrap_or_default(), call_id: id, content: result_text(&b["content"]), images: vec![], is_error, meta });
|
||||
parts.push(Part::ToolResult { name: calls.get(&id).cloned().unwrap_or_default(), call_id: id, content: result_text(&b["content"]), images: result_images(&b["content"]), is_error, meta });
|
||||
}
|
||||
if !parts.is_empty() {
|
||||
let m = Message::new(Role::User, parts);
|
||||
@@ -852,6 +866,15 @@ pub async fn update() -> Result<String> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn tool_result_images_are_kept() {
|
||||
let content = json!([{"type": "text", "text": "ok"}, {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": "QUJD"}}]);
|
||||
let imgs = result_images(&content);
|
||||
assert_eq!(imgs.len(), 1);
|
||||
assert_eq!((imgs[0].media_type.as_str(), imgs[0].data.as_str()), ("image/jpeg", "QUJD"));
|
||||
assert!(result_images(&json!("plain text")).is_empty());
|
||||
}
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user