Add feature-gated standalone image generation extension#24723
Conversation
| @@ -0,0 +1,11 @@ | |||
| The `image_gen.imagegen` tool enables image generation from descriptions and editing of existing images based on specific instructions. Use it when: | |||
There was a problem hiding this comment.
placeholder, subject to change
There was a problem hiding this comment.
Honestly not sure we need a doc file then... just drop it
There was a problem hiding this comment.
placeholder as in we need to change the guidelines later depending on how we want the model to use this image gen tool, we should still give it guidelines on generate and edit etc bc the model isn't trained to do it
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce9ab1187e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| fn standalone_image_generation_model_visible(turn_context: &TurnContext) -> bool { | ||
| image_generation_runtime_enabled(turn_context) | ||
| && turn_context.features.get().enabled(Feature::ImageGenExt) | ||
| && namespace_tools_enabled(turn_context) |
There was a problem hiding this comment.
Gate imagegenext behind image_generation
When a user disables [features].image_generation but enables [features].imagegenext, this predicate still returns true for ChatGPT auth on an image-capable OpenAI model, so append_extension_tool_executors exposes image_gen.imagegen even though the stable image generation kill switch is off. The existing hosted path used image_generation_tool_enabled, so this regresses configurations that intentionally disable all model image-generation tools; include Feature::ImageGeneration in the standalone visibility check as well.
Useful? React with 👍 / 👎.
| ResponseInputItem::FunctionCallOutput { | ||
| call_id: call_id.to_string(), | ||
| output: FunctionCallOutputPayload { | ||
| body: FunctionCallOutputBody::ContentItems(content), |
There was a problem hiding this comment.
Surface standalone generated images to clients
When the standalone image tool succeeds, the image is only recorded as a FunctionCallOutput for the follow-up model request; app-server thread reconstruction ignores ResponseItem::FunctionCallOutput and only creates visible image-generation thread items from ImageGenerationBegin/End events. Since the new tool instructions tell the model to say nothing after generation, users in the app/TUI can get a successful generation with no visible image or saved-path item, so this path should emit/reuse image-generation events or otherwise publish the generated artifact to the client.
Useful? React with 👍 / 👎.
| impl ToolOutput for GeneratedImageOutput { | ||
| /// Avoids copying image bytes into tool-call telemetry. | ||
| fn log_preview(&self) -> String { | ||
| "[generated image]".to_string() | ||
| } |
There was a problem hiding this comment.
Avoid piping generated images into hooks
When PostToolUse hooks are configured, the default CoreToolRuntime::post_tool_use_payload fallback serializes this output's to_response_item body because GeneratedImageOutput does not override post_tool_use_response; that includes the full data:image/png;base64,... payload for every generated image. Multi-MB image results can make hook stdin very large or expose image bytes to hooks despite the telemetry preview being redacted, so return a small hook-facing response (for example the saved path/hint) instead of letting the default fallback serialize the image content.
Useful? React with 👍 / 👎.
| use tracing::warn; | ||
|
|
||
| const MULTI_AGENT_V2_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents."; | ||
| const IMAGE_GEN_NAMESPACE: &str = "image_gen"; |
There was a problem hiding this comment.
What is the plan to drop this? (and drop the old tool)
There was a problem hiding this comment.
once the standalone path is ready to replace the hosted tool, I plan to remove the hosted tool and this replacement detection together
| } | ||
|
|
||
| /// Returns generated bytes and persisted-artifact context for the model's follow-up response. | ||
| fn to_response_item(&self, call_id: &str, _payload: &ToolPayload) -> ResponseInputItem { |
There was a problem hiding this comment.
OOC do you also plan to emit events after this? FunctionCallOutput might not be rich enough? Not sure what is the product expecting
There was a problem hiding this comment.
ya i am keeping this PR scoped to the standalone extension and fallback wiring, and will handle event emission in a follow-up PR
| @@ -0,0 +1,11 @@ | |||
| The `image_gen.imagegen` tool enables image generation from descriptions and editing of existing images based on specific instructions. Use it when: | |||
There was a problem hiding this comment.
Honestly not sure we need a doc file then... just drop it
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d121f9377a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
jif-oai
left a comment
There was a problem hiding this comment.
Is there a test or an eval to prove equivalence with the original tool?
| .await; | ||
| image_generation.assert_visible_contains(&["image_generation"]); | ||
|
|
||
| let extension_flag_without_imagegen_tool = probe(|turn| { |
There was a problem hiding this comment.
This only covers the fallback when imagegenext is on but the executor is absent. The replacement path itself is untested now
There was a problem hiding this comment.
note: doing as follow-up / as the need comes to invest time in making sure we are at parity
| if role != "user" { | ||
| return None; | ||
| } | ||
| let images = content |
There was a problem hiding this comment.
This reverses the user-visible attachment order before calling the edit API. If the user/model prompt says “edit the first image” after uploading [A, B], we now send [B, A], so positional references can target the wrong image. I guess this is a big deal but I'm not sure
Why
Add a standalone image generation path that can be exercised independently of hosted Responses image generation, while retaining the hosted tool as fallback unless the extension is actually available to the model.
What changed
codex-image-generation-extensioncrate with standalone generate/edit execution, prior-image selection for edits, model-visible image output, and local generated-image persistence.imagegenextfeature and backend eligibility checks.image_gen.imagegenexposure replaces hostedimage_generation, while unavailable configurations retain hosted fallback.gpt-image-2and uses automatic image parameters.Validation
just test -p codex-image-generation-extensionjust test -p codex-featuresjust test -p codex-core hosted_tools_follow_provider_auth_model_and_config_gatesjust test -p codex-app-serverjust fix -p codex-image-generation-extension -p codex-features -p codex-core -p codex-app-serverjust fmtjust bazel-lock-updatejust bazel-lock-check