diff --git a/codex-rs/app-server/tests/suite/v2/imagegen_extension.rs b/codex-rs/app-server/tests/suite/v2/imagegen_extension.rs index e892507291..d2a4e43751 100644 --- a/codex-rs/app-server/tests/suite/v2/imagegen_extension.rs +++ b/codex-rs/app-server/tests/suite/v2/imagegen_extension.rs @@ -506,6 +506,7 @@ chatgpt_base_url = "{server_uri}" [features] imagegenext = true +materialize_remote_images = true {code_mode_only} [model_providers.openai-custom] diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 22552d9c74..5080937997 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -872,7 +872,7 @@ async fn turn_start_tracks_turn_event_analytics() -> Result<()> { thread_id: thread.id.clone(), client_user_message_id: None, input: vec![V2UserInput::Image { - url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=".to_string(), + url: "https://example.com/a.png".to_string(), detail: None, }], responsesapi_client_metadata: Some(HashMap::from([( diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index b30b868bc7..90071f32de 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -512,6 +512,9 @@ "local_thread_store_compression": { "type": "boolean" }, + "materialize_remote_images": { + "type": "boolean" + }, "memories": { "type": "boolean" }, @@ -4638,6 +4641,9 @@ "local_thread_store_compression": { "type": "boolean" }, + "materialize_remote_images": { + "type": "boolean" + }, "memories": { "type": "boolean" }, @@ -5224,4 +5230,4 @@ }, "title": "ConfigToml", "type": "object" -} +} \ No newline at end of file diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 094745d622..32dd4c5f5c 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; @@ -2588,8 +2589,14 @@ impl Session { turn_context: &TurnContext, items: &[ResponseItem], ) { - let items = - crate::image_url_materializer::materialize_conversation_item_images(items).await; + let items = if turn_context + .features + .enabled(Feature::MaterializeRemoteImages) + { + crate::image_url_materializer::materialize_conversation_item_images(items).await + } else { + Cow::Borrowed(items) + }; let items = items.as_ref(); { let mut state = self.state.lock().await; diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 33a926f892..c46accd97e 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -2508,7 +2508,10 @@ image("data:image/png;base64,AAA"); "# ); let (_test, second_mock) = - run_code_mode_turn(&server, "use exec to return images", &code).await?; + run_code_mode_turn_with_config(&server, "use exec to return images", &code, |config| { + let _ = config.features.enable(Feature::MaterializeRemoteImages); + }) + .await?; let req = second_mock.single_request(); let items = custom_tool_output_items(&req, "call-1"); diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 8ee05aed21..6d71823949 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -183,6 +183,8 @@ pub enum Feature { ImageGeneration, /// Replace hosted image generation with the standalone image-generation extension. ImageGenExt, + /// Download remote images before recording them in conversation history. + MaterializeRemoteImages, /// Allow prompting and installing missing MCP dependencies. SkillMcpDependencyInstall, /// Removed compatibility flag for deleted skill env var dependency prompting. @@ -1090,6 +1092,12 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: false, }, + FeatureSpec { + id: Feature::MaterializeRemoteImages, + key: "materialize_remote_images", + stage: Stage::UnderDevelopment, + default_enabled: false, + }, FeatureSpec { id: Feature::SkillMcpDependencyInstall, key: "skill_mcp_dependency_install", diff --git a/sdk/python/tests/test_app_server_inputs.py b/sdk/python/tests/test_app_server_inputs.py index 42b63c5319..7741df0af4 100644 --- a/sdk/python/tests/test_app_server_inputs.py +++ b/sdk/python/tests/test_app_server_inputs.py @@ -24,7 +24,9 @@ def test_remote_image_input_is_materialized_before_responses_api( response_id="remote-image", ) - with Codex(config=harness.app_server_config()) as codex: + config = harness.app_server_config() + config.config_overrides = ("features.materialize_remote_images=true",) + with Codex(config=config) as codex: result = codex.thread_start().run( [ TextInput("Describe the remote image."),