mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Expose local image paths to models (#25944)
## Why Local image attachments include image bytes, but the adjacent model-visible label omits the source path. Exposing the path lets model-selected workflows refer back to the intended local image explicitly. ## What changed - Include an escaped `path` attribute in model-visible local image opening tags. - Reuse the path-aware marker generator in rollout coverage. - Update protocol, replay, and rollout coverage for the new request shape. ## Validation - `just fmt` - `just test -p codex-protocol` - `just test -p codex-core skips_local_image_label_text` - `just test -p codex-core copy_paste_local_image_persists_rollout_request_shape` - `git diff --check`
This commit is contained in:
committed by
GitHub
Unverified
parent
aeac226d16
commit
57ab4c89e0
@@ -67,7 +67,7 @@ fn parses_user_message_with_text_and_two_images() {
|
||||
#[test]
|
||||
fn skips_local_image_label_text() {
|
||||
let image_url = "data:image/png;base64,abc".to_string();
|
||||
let label = codex_protocol::models::local_image_open_tag_text(/*label_number*/ 1);
|
||||
let label = r#"<image name=[Image #1] path="/tmp/local.png">"#.to_string();
|
||||
let user_text = "Please review this image.".to_string();
|
||||
|
||||
let item = ResponseItem::Message {
|
||||
|
||||
@@ -161,7 +161,9 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu
|
||||
role: "user".to_string(),
|
||||
content: vec![
|
||||
ContentItem::InputText {
|
||||
text: codex_protocol::models::local_image_open_tag_text(/*label_number*/ 1),
|
||||
text: codex_protocol::models::local_image_open_tag_text_with_path(
|
||||
/*label_number*/ 1, &abs_path,
|
||||
),
|
||||
},
|
||||
ContentItem::InputImage {
|
||||
image_url,
|
||||
|
||||
@@ -1019,9 +1019,10 @@ pub fn local_image_label_text(label_number: usize) -> String {
|
||||
format!("[Image #{label_number}]")
|
||||
}
|
||||
|
||||
pub fn local_image_open_tag_text(label_number: usize) -> String {
|
||||
pub fn local_image_open_tag_text_with_path(label_number: usize, path: &std::path::Path) -> String {
|
||||
let label = local_image_label_text(label_number);
|
||||
format!("{LOCAL_IMAGE_OPEN_TAG_PREFIX}{label}{LOCAL_IMAGE_OPEN_TAG_SUFFIX}")
|
||||
let path = path.display();
|
||||
format!("{LOCAL_IMAGE_OPEN_TAG_PREFIX}{label} path=\"{path}\"{LOCAL_IMAGE_OPEN_TAG_SUFFIX}")
|
||||
}
|
||||
|
||||
pub fn is_local_image_open_tag_text(text: &str) -> bool {
|
||||
@@ -1080,7 +1081,7 @@ pub fn local_image_content_items_with_label_number(
|
||||
let mut items = Vec::with_capacity(3);
|
||||
if let Some(label_number) = label_number {
|
||||
items.push(ContentItem::InputText {
|
||||
text: local_image_open_tag_text(label_number),
|
||||
text: local_image_open_tag_text_with_path(label_number, path),
|
||||
});
|
||||
}
|
||||
items.push(ContentItem::InputImage {
|
||||
@@ -2866,7 +2867,7 @@ mod tests {
|
||||
detail: None,
|
||||
},
|
||||
UserInput::LocalImage {
|
||||
path: local_path,
|
||||
path: local_path.clone(),
|
||||
detail: None,
|
||||
},
|
||||
]);
|
||||
@@ -2883,7 +2884,10 @@ mod tests {
|
||||
assert_eq!(
|
||||
content.get(1),
|
||||
Some(&ContentItem::InputText {
|
||||
text: local_image_open_tag_text(/*label_number*/ 2),
|
||||
text: local_image_open_tag_text_with_path(
|
||||
/*label_number*/ 2,
|
||||
&local_path
|
||||
),
|
||||
})
|
||||
);
|
||||
assert!(matches!(
|
||||
@@ -2903,6 +2907,17 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_image_open_tag_preserves_path() {
|
||||
assert_eq!(
|
||||
local_image_open_tag_text_with_path(
|
||||
/*label_number*/ 1,
|
||||
std::path::Path::new(r#"/tmp/a&"<b>.png"#),
|
||||
),
|
||||
r#"<image name=[Image #1] path="/tmp/a&"<b>.png">"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_image_user_input_preserves_requested_detail() -> Result<()> {
|
||||
let dir = tempdir()?;
|
||||
|
||||
@@ -2190,8 +2190,9 @@ pub struct UserMessageEvent {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub image_details: Vec<Option<ImageDetail>>,
|
||||
/// Local file paths sourced from `UserInput::LocalImage`. These are kept so
|
||||
/// the UI can reattach images when editing history, and should not be sent
|
||||
/// to the model or treated as API-ready URLs.
|
||||
/// the UI can reattach images when editing history. Local image prompts may
|
||||
/// include a display form of the path, but these should not be treated as
|
||||
/// API-ready URLs.
|
||||
#[serde(default)]
|
||||
pub local_images: Vec<std::path::PathBuf>,
|
||||
/// Detail hints for `local_images`, indexed in parallel. Missing entries
|
||||
|
||||
Reference in New Issue
Block a user