From 9ccc5180c3666bd7d71aebb7e52aa71cdcb54573 Mon Sep 17 00:00:00 2001 From: Won Park Date: Tue, 2 Jun 2026 15:30:08 -0700 Subject: [PATCH] Revert "Expose standalone image generation in code mode (#25923)" This reverts commit bec21c71142ed44d8eb7d7ca5719c87d961044d0. --- ...gegen_extension.rs => image_generation.rs} | 152 ++++-------------- codex-rs/app-server/tests/suite/v2/mod.rs | 2 +- codex-rs/ext/image-generation/src/tool.rs | 4 +- 3 files changed, 33 insertions(+), 125 deletions(-) rename codex-rs/app-server/tests/suite/v2/{imagegen_extension.rs => image_generation.rs} (70%) diff --git a/codex-rs/app-server/tests/suite/v2/imagegen_extension.rs b/codex-rs/app-server/tests/suite/v2/image_generation.rs similarity index 70% rename from codex-rs/app-server/tests/suite/v2/imagegen_extension.rs rename to codex-rs/app-server/tests/suite/v2/image_generation.rs index 924c6c58ba..e1f4cfbc9a 100644 --- a/codex-rs/app-server/tests/suite/v2/imagegen_extension.rs +++ b/codex-rs/app-server/tests/suite/v2/image_generation.rs @@ -30,12 +30,6 @@ use wiremock::matchers::path; const RESULT: &str = "cG5n"; -#[derive(Clone, Copy)] -enum ImagegenTestMode { - Direct, - CodeModeOnly, -} - // macOS and Windows Bazel CI can spend tens of seconds starting app-server // subprocesses or processing test RPCs under load. #[cfg(any(target_os = "macos", windows))] @@ -75,7 +69,7 @@ async fn standalone_image_generation_persists_image_and_returns_it_to_model() -> .await; let codex_home = TempDir::new()?; - create_config_toml(codex_home.path(), &server.uri(), ImagegenTestMode::Direct)?; + create_config_toml(codex_home.path(), &server.uri())?; write_chatgpt_auth( codex_home.path(), ChatGptAuthFixture::new("access-chatgpt"), @@ -85,7 +79,34 @@ async fn standalone_image_generation_persists_image_and_returns_it_to_model() -> let mut mcp = TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; - start_image_generation_turn(&mut mcp).await?; + + let thread_req = mcp + .send_thread_start_request(ThreadStartParams::default()) + .await?; + let thread_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), + ) + .await??; + let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; + + let turn_req = mcp + .send_turn_start_request(TurnStartParams { + thread_id: thread.id, + client_user_message_id: None, + input: vec![V2UserInput::Text { + text: "Generate an image".to_string(), + text_elements: Vec::new(), + }], + ..Default::default() + }) + .await?; + let turn_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), + ) + .await??; + let _turn: TurnStartResponse = to_response::(turn_resp)?; let completed = timeout( DEFAULT_READ_TIMEOUT, @@ -136,110 +157,6 @@ async fn standalone_image_generation_persists_image_and_returns_it_to_model() -> Ok(()) } -#[cfg_attr(windows, ignore = "covered by Linux and macOS CI")] -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn standalone_image_generation_is_callable_from_code_mode_only() -> Result<()> { - let call_id = "code-mode-image-run-1"; - let server = responses::start_mock_server().await; - mount_image_response(&server).await; - - let response_mock = responses::mount_sse_sequence( - &server, - vec![ - responses::sse(vec![ - responses::ev_response_created("resp-1"), - responses::ev_custom_tool_call( - call_id, - "exec", - r#" -const result = await tools.image_gen__imagegen({ - action: "generate", - prompt: "paint a blue whale", -}); -image(result); -"#, - ), - responses::ev_completed("resp-1"), - ]), - responses::sse(vec![ - responses::ev_assistant_message("msg-1", "Done"), - responses::ev_completed("resp-2"), - ]), - ], - ) - .await; - - let codex_home = TempDir::new()?; - create_config_toml( - codex_home.path(), - &server.uri(), - ImagegenTestMode::CodeModeOnly, - )?; - write_chatgpt_auth( - codex_home.path(), - ChatGptAuthFixture::new("access-chatgpt"), - AuthCredentialsStoreMode::File, - )?; - - let mut mcp = - TestAppServer::new_with_env(codex_home.path(), &[("OPENAI_API_KEY", None)]).await?; - timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; - start_image_generation_turn(&mut mcp).await?; - timeout( - DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_notification_message("turn/completed"), - ) - .await??; - - let requests = response_mock.requests(); - assert_eq!(requests.len(), 2); - assert!(requests[0].body_contains_text("image_gen__imagegen")); - let output = requests[1].custom_tool_call_output(call_id); - assert_eq!( - output["output"][1], - json!({ - "type": "input_image", - "image_url": format!("data:image/png;base64,{RESULT}"), - "detail": "high", - }) - ); - assert_eq!(output["output"].as_array().map(Vec::len), Some(2)); - - Ok(()) -} - -async fn start_image_generation_turn(mcp: &mut TestAppServer) -> Result<()> { - let thread_req = mcp - .send_thread_start_request(ThreadStartParams::default()) - .await?; - let thread_resp: JSONRPCResponse = timeout( - DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), - ) - .await??; - let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; - - let turn_req = mcp - .send_turn_start_request(TurnStartParams { - thread_id: thread.id, - client_user_message_id: None, - input: vec![V2UserInput::Text { - text: "Generate an image".to_string(), - text_elements: Vec::new(), - }], - ..Default::default() - }) - .await?; - let turn_resp: JSONRPCResponse = timeout( - DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), - ) - .await??; - let _turn: TurnStartResponse = to_response::(turn_resp)?; - - Ok(()) -} - async fn wait_for_image_generation_completed( mcp: &mut TestAppServer, ) -> Result { @@ -270,15 +187,7 @@ async fn mount_image_response(server: &MockServer) { .await; } -fn create_config_toml( - codex_home: &Path, - server_uri: &str, - mode: ImagegenTestMode, -) -> std::io::Result<()> { - let code_mode_only = match mode { - ImagegenTestMode::Direct => "", - ImagegenTestMode::CodeModeOnly => "code_mode_only = true", - }; +fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::Result<()> { std::fs::write( codex_home.join("config.toml"), format!( @@ -291,7 +200,6 @@ chatgpt_base_url = "{server_uri}" [features] imagegenext = true -{code_mode_only} [model_providers.openai-custom] name = "OpenAI" diff --git a/codex-rs/app-server/tests/suite/v2/mod.rs b/codex-rs/app-server/tests/suite/v2/mod.rs index 9aef40aade..950cccebb5 100644 --- a/codex-rs/app-server/tests/suite/v2/mod.rs +++ b/codex-rs/app-server/tests/suite/v2/mod.rs @@ -17,7 +17,7 @@ mod experimental_feature_list; mod external_agent_config; mod fs; mod hooks_list; -mod imagegen_extension; +mod image_generation; mod initialize; mod marketplace_add; mod marketplace_remove; diff --git a/codex-rs/ext/image-generation/src/tool.rs b/codex-rs/ext/image-generation/src/tool.rs index e4e8e13135..41d407d912 100644 --- a/codex-rs/ext/image-generation/src/tool.rs +++ b/codex-rs/ext/image-generation/src/tool.rs @@ -77,9 +77,9 @@ impl ToolExecutor for ImageGenerationTool { imagegen_tool_spec() } - /// Exposes image generation directly and through the nested code-mode tool surface. + /// Keeps this model-facing tool out of the nested code-mode tool surface. fn exposure(&self) -> ToolExposure { - ToolExposure::Direct + ToolExposure::DirectModelOnly } /// Executes the selected image operation and returns the completed image result.