mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
core: strip image detail from Responses Lite requests (#27246)
## Summary - Strip image `detail` fields from every Responses Lite request. - Apply stripping to message images and function/custom tool-output images. - Transform only the formatted request copy without mutating stored history. - Preserve image URLs byte-for-byte, including HTTP(S) URLs, without downloading, validating, or resizing them. - Preserve all image `detail` fields for non-Responses-Lite models. ## Motivation Responses Lite does not support image `detail` tags, so Codex must omit them whenever `model_info.use_responses_lite` is enabled. This transport requirement is independent of the `resize_all_images` feature. Stored history retains the original detail values. This keeps request-specific formatting isolated from conversation state and preserves the information for local image preparation and non-Responses-Lite requests. #### [git stack](https://github.com/magus/git-stack-cli) - ✅ `1` https://github.com/openai/codex/pull/27245 - ✅ `2` https://github.com/openai/codex/pull/27247 - 👉 `3` https://github.com/openai/codex/pull/27246 - ⏳ `4` https://github.com/openai/codex/pull/27266
This commit is contained in:
committed by
GitHub
Unverified
parent
e614fad02e
commit
4435ff2810
@@ -746,7 +746,7 @@ impl ModelClient {
|
||||
window_id: &str,
|
||||
) -> Result<ResponsesApiRequest> {
|
||||
let instructions = &prompt.base_instructions.text;
|
||||
let input = prompt.get_formatted_input();
|
||||
let input = prompt.get_formatted_input_for_request(model_info.use_responses_lite);
|
||||
let tools = create_tools_json_for_responses_api(&prompt.tools)?;
|
||||
let reasoning = Self::build_reasoning(model_info, effort, summary);
|
||||
let include = if reasoning.is_some() {
|
||||
|
||||
@@ -2,6 +2,8 @@ pub use codex_api::ResponseEvent;
|
||||
use codex_config::types::Personality;
|
||||
use codex_protocol::error::Result;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::FunctionCallOutputContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::InterAgentCommunication;
|
||||
use codex_tools::ToolSpec;
|
||||
@@ -71,6 +73,56 @@ impl Prompt {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn get_formatted_input_for_request(
|
||||
&self,
|
||||
use_responses_lite: bool,
|
||||
) -> Vec<ResponseItem> {
|
||||
let mut input = self.get_formatted_input();
|
||||
if use_responses_lite {
|
||||
strip_image_details(&mut input);
|
||||
}
|
||||
input
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_image_details(items: &mut [ResponseItem]) {
|
||||
for item in items {
|
||||
match item {
|
||||
ResponseItem::Message { content, .. } => {
|
||||
for content_item in content {
|
||||
if let ContentItem::InputImage { detail, .. } = content_item {
|
||||
*detail = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
ResponseItem::FunctionCallOutput { output, .. }
|
||||
| ResponseItem::CustomToolCallOutput { output, .. } => {
|
||||
if let Some(content) = output.content_items_mut() {
|
||||
for content_item in content {
|
||||
if let FunctionCallOutputContentItem::InputImage { detail, .. } =
|
||||
content_item
|
||||
{
|
||||
*detail = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ResponseItem::Reasoning { .. }
|
||||
| ResponseItem::AgentMessage { .. }
|
||||
| ResponseItem::LocalShellCall { .. }
|
||||
| ResponseItem::FunctionCall { .. }
|
||||
| ResponseItem::ToolSearchCall { .. }
|
||||
| ResponseItem::CustomToolCall { .. }
|
||||
| ResponseItem::ToolSearchOutput { .. }
|
||||
| ResponseItem::WebSearchCall { .. }
|
||||
| ResponseItem::ImageGenerationCall { .. }
|
||||
| ResponseItem::Compaction { .. }
|
||||
| ResponseItem::CompactionTrigger
|
||||
| ResponseItem::ContextCompaction { .. }
|
||||
| ResponseItem::Other => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResponseStream {
|
||||
|
||||
@@ -3,10 +3,95 @@ use codex_api::ResponsesApiRequest;
|
||||
use codex_api::TextControls;
|
||||
use codex_api::create_text_param_for_request;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::models::FunctionCallOutputPayload;
|
||||
use codex_protocol::models::ImageDetail;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn prompt_with_image_outputs() -> Prompt {
|
||||
Prompt {
|
||||
input: vec![
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputImage {
|
||||
image_url: "https://example.com/image.png".to_string(),
|
||||
detail: Some(ImageDetail::Original),
|
||||
}],
|
||||
phase: None,
|
||||
},
|
||||
ResponseItem::FunctionCallOutput {
|
||||
call_id: "function-call".to_string(),
|
||||
output: FunctionCallOutputPayload::from_content_items(vec![
|
||||
FunctionCallOutputContentItem::InputImage {
|
||||
image_url: "data:image/png;base64,function".to_string(),
|
||||
detail: Some(ImageDetail::High),
|
||||
},
|
||||
]),
|
||||
},
|
||||
ResponseItem::CustomToolCallOutput {
|
||||
call_id: "custom-call".to_string(),
|
||||
name: None,
|
||||
output: FunctionCallOutputPayload::from_content_items(vec![
|
||||
FunctionCallOutputContentItem::InputImage {
|
||||
image_url: "data:image/png;base64,custom".to_string(),
|
||||
detail: Some(ImageDetail::Auto),
|
||||
},
|
||||
]),
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn responses_lite_request_copies_strip_image_details() {
|
||||
let prompt = prompt_with_image_outputs();
|
||||
let original = prompt.input.clone();
|
||||
|
||||
let stripped = prompt.get_formatted_input_for_request(/*use_responses_lite*/ true);
|
||||
|
||||
assert_eq!(
|
||||
stripped,
|
||||
vec![
|
||||
ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentItem::InputImage {
|
||||
image_url: "https://example.com/image.png".to_string(),
|
||||
detail: None,
|
||||
}],
|
||||
phase: None,
|
||||
},
|
||||
ResponseItem::FunctionCallOutput {
|
||||
call_id: "function-call".to_string(),
|
||||
output: FunctionCallOutputPayload::from_content_items(vec![
|
||||
FunctionCallOutputContentItem::InputImage {
|
||||
image_url: "data:image/png;base64,function".to_string(),
|
||||
detail: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
ResponseItem::CustomToolCallOutput {
|
||||
call_id: "custom-call".to_string(),
|
||||
name: None,
|
||||
output: FunctionCallOutputPayload::from_content_items(vec![
|
||||
FunctionCallOutputContentItem::InputImage {
|
||||
image_url: "data:image/png;base64,custom".to_string(),
|
||||
detail: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
]
|
||||
);
|
||||
assert_eq!(prompt.input, original);
|
||||
assert_eq!(
|
||||
prompt.get_formatted_input_for_request(/*use_responses_lite*/ false),
|
||||
original
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serializes_text_verbosity_when_set() {
|
||||
let input: Vec<ResponseItem> = vec![];
|
||||
|
||||
Reference in New Issue
Block a user