Skip to content

Tingzhou/OpenAI imagegen ci test - #1094

Closed
TingtingZhou7 wants to merge 2 commits into
smg-project:mainfrom
TingtingZhou7:tingzhou/openai-imagegen-ci-test
Closed

TingtingZhou7 wants to merge 2 commits into
smg-project:mainfrom
TingtingZhou7:tingzhou/openai-imagegen-ci-test

Conversation

@TingtingZhou7

@TingtingZhou7 TingtingZhou7 commented Apr 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

Problem

Solution

Changes

Test Plan

Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

Summary by CodeRabbit

  • New Features
    • Introduced image generation as a new built-in tool option available through the API
    • Image generation requests now support full streaming with real-time progress updates and status events
    • Complete integration with MCP configuration system for managing image generation tool routing across servers

@github-actions github-actions Bot added grpc gRPC client and router changes mcp MCP related changes tests Test changes protocols Protocols crate changes model-gateway Model gateway crate changes openai OpenAI router changes labels Apr 10, 2026
@coderabbitai

coderabbitai Bot commented Apr 10, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@TingtingZhou7 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 27 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 21 minutes and 27 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8cfeb672-2cdb-43ee-908d-c668fd01ee2d

📥 Commits

Reviewing files that changed from the base of the PR and between 01365fb and c933a3e.

📒 Files selected for processing (18)
  • .gitignore
  • crates/mcp/src/core/config.rs
  • crates/mcp/src/lib.rs
  • crates/mcp/src/transform/mod.rs
  • crates/mcp/src/transform/transformer.rs
  • crates/mcp/src/transform/types.rs
  • crates/protocols/src/event_types.rs
  • crates/protocols/src/responses.rs
  • e2e_test/responses/test_builtin_tools.py
  • model_gateway/src/routers/grpc/common/responses/streaming.rs
  • model_gateway/src/routers/grpc/common/responses/utils.rs
  • model_gateway/src/routers/grpc/harmony/builder.rs
  • model_gateway/src/routers/mcp_utils.rs
  • model_gateway/src/routers/mod.rs
  • model_gateway/src/routers/openai/mcp/tool_loop.rs
  • model_gateway/src/routers/openai/responses/streaming.rs
  • model_gateway/src/routers/openai/responses/utils.rs
  • model_gateway/src/routers/tool_output_context.rs
📝 Walkthrough

Walkthrough

This pull request adds comprehensive image generation support across the MCP, protocols, and model gateway layers. It introduces new tool types, event types, response formats, transformer logic to extract image payloads from MCP responses, and updated routing infrastructure for both gRPC and OpenAI response paths, along with an end-to-end integration test.

Changes

Cohort / File(s) Summary
Configuration & Gitignore
.gitignore
Added local MCP configuration file to ignored patterns.
Core Type Definitions
crates/mcp/src/core/config.rs, crates/protocols/src/event_types.rs, crates/protocols/src/responses.rs
Added ImageGeneration and ImageGenerationCall enum variants, event types, and status enums across configuration, protocol event, and response structures with serde serialization support.
MCP Transform & Re-exports
crates/mcp/src/transform/transformer.rs, crates/mcp/src/transform/types.rs, crates/mcp/src/transform/mod.rs, crates/mcp/src/lib.rs
Implemented image payload extraction from MCP JSON-RPC responses, added helper functions for error detection and fallback text, updated response format mapping, and exposed new utilities via crate-level re-exports.
Model Gateway Infrastructure
model_gateway/src/routers/mod.rs, model_gateway/src/routers/tool_output_context.rs, model_gateway/src/routers/mcp_utils.rs
Added tool output context compaction module, extended built-in MCP tool routing to recognize image generation with server/tool mapping, and integrated corresponding test coverage.
gRPC Response Routes
model_gateway/src/routers/grpc/common/responses/streaming.rs, model_gateway/src/routers/grpc/common/responses/utils.rs, model_gateway/src/routers/grpc/harmony/builder.rs
Extended streaming event emission for image generation events, added upfront validation rejecting image generation on gRPC routes, and updated tool-type allowlisting logic.
OpenAI Response Routes
model_gateway/src/routers/openai/mcp/tool_loop.rs, model_gateway/src/routers/openai/responses/streaming.rs, model_gateway/src/routers/openai/responses/utils.rs
Modified tool execution to merge builtin overrides for image generation, updated state recording with compact model context, extended response format handling with ig_ ID prefix, and enabled tool restoration for image generation.
Integration Test
e2e_test/responses/test_builtin_tools.py
Added image generation tool constant, MCP configuration, and end-to-end test verifying image generation call output format.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ModelGateway as Model Gateway
    participant MCP as MCP Server
    participant Transformer
    participant Stream as Response Stream

    Client->>ModelGateway: Responses API Request<br/>(tools=[image_generation])
    ModelGateway->>ModelGateway: Route builtin tool type<br/>to MCP server
    ModelGateway->>MCP: Execute image generation<br/>via MCP
    MCP-->>ModelGateway: JSON-RPC Response<br/>(wrapped result.content)
    ModelGateway->>Transformer: Transform to<br/>ImageGenerationCall format
    Transformer->>Transformer: Extract image payload<br/>from wrapped content
    Transformer-->>ModelGateway: ImageGenerationCall<br/>ResponseOutputItem
    ModelGateway->>Stream: Emit IN_PROGRESS event
    ModelGateway->>Stream: Emit GENERATING event
    ModelGateway->>Stream: Emit COMPLETED event<br/>with image data
    Stream-->>Client: Streaming response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

mcp, protocols, model-gateway, openai, grpc, tests, feature

Suggested reviewers

  • CatherineSue
  • key4ng
  • slin1237
  • XinyueZhang369

Poem

🐰 Hoppy times! The MCP transforms,
Image calls now take new forms,
From wrapped content, payloads spring,
Streaming events swiftly wing,
Gateway routes with grace unfold,
Image generation stories told! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The pull request title does not clearly describe the main changes. The title references 'CI test' but the changeset primarily implements image generation support across multiple modules. Revise the title to reflect the core change, such as 'Add image generation tool support with MCP integration' or 'Implement ImageGeneration builtin tool type and routing'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

revert(grpc): drop grpc changes from readded image commit

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

chore: add signed-off empty commit

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

fix(grpc): reject image_generation tool while keeping build exhaustive

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

Apply suggestions from code review

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

revert(grpc): drop grpc response/builder changes from branch

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

fix(router): restore literal tool name in compact output context

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

fix(grpc): use ImageGenerationCallEvent for image generation stream events

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

refactor(mcp): reuse parsed object when extracting image result

Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>
Signed-off-by: TingtingZhou7 <zhoutt96@gmail.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a built-in image generation tool within the MCP framework, allowing tool results to be transformed into OpenAI-compatible image generation calls. It introduces new configuration variants, response transformers for image payload extraction, and a tool output compaction utility to summarize results for model context. The PR also updates streaming event types and routing logic. Review feedback identified a need to correctly report failure statuses in image generation calls, recommended replacing fixed sleeps in tests with robust polling, and suggested parameterizing hardcoded tool names in the compaction logic.

.as_object()
.or_else(|| parsed_payload.as_ref().and_then(|v| v.as_object()));

let status = ImageGenerationCallStatus::Completed;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The status of the image generation call is always set to Completed, even when an error is detected. This can lead to incorrect reporting of tool call outcomes. The status should be set to Failed when is_image_generation_error returns true. Additionally, if this outcome is recorded for a worker request, ensure that the record_outcome function is called with the HTTP status code, not a boolean, to allow the internal logic to determine if it represents a failure.

Suggested change
let status = ImageGenerationCallStatus::Completed;
let status = if is_image_generation_error(result) {
ImageGenerationCallStatus::Failed
} else {
ImageGenerationCallStatus::Completed
};
References
  1. When recording the outcome of a worker request, ensure that the record_outcome function is called with the HTTP status code, not a boolean indicating success or failure. The worker's internal logic will determine if the status code represents a circuit breaker failure based on its configured retryable status codes.

"""Test that image_generation produces image_generation_call output."""
gateway, client = gateway_with_mcp_config

time.sleep(2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a fixed time.sleep() can lead to flaky tests, as the required wait time might vary. If this sleep is intended to wait for the gateway to be ready, consider moving this logic into the gateway_with_mcp_config fixture and implementing a more robust waiting mechanism, such as polling a health check endpoint. Each process should have its own individual startup timeout rather than using a shared deadline.

References
  1. When waiting for multiple concurrent processes to become healthy, each process should have its own individual startup timeout, rather than using a shared deadline for all.

"Successfully generated the image".to_string()
};
let summary = json!({
"tool": "generate_image",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The tool name in the summary is hardcoded to "generate_image". This might be inaccurate if the actual tool name is different. Consider passing the tool name as an argument to compact_tool_output_for_model_context and using it here to make the summary more accurate.

For example, you could change the function signature to:

pub fn compact_tool_output_for_model_context(
response_format: &ResponseFormat,
output: &Value,
tool_name: &str,
) -> String

And then use tool_name here.

Suggested change
"tool": "generate_image",
"tool": tool_name,

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 01365fbc34

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

.as_object()
.or_else(|| parsed_payload.as_ref().and_then(|v| v.as_object()));

let status = ImageGenerationCallStatus::Completed;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Mark failed image tool calls as failed

to_image_generation_call hard-codes status to completed, so even error payloads (for example responses with result.isError=true and error text) are emitted as successful image_generation_call items. Clients that rely on status to drive retry/error handling will mis-handle real tool failures as successes.

Useful? React with 👍 / 👎.

Comment on lines +17 to +25
let is_error = is_image_generation_error(output);
let note = if is_error {
extract_image_generation_fallback_text(output).unwrap_or_default()
} else {
"Successfully generated the image".to_string()
};
let summary = json!({
"tool": "generate_image",
"status": if is_error { "failed" } else { "completed" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detect image-generation failures before compacting tool output

This compaction path infers failure only from result.isError; when image tool execution fails with other common shapes (e.g. {"error": ...}), it still writes a completed summary with a success note. Because this summary is fed into conversation history, the next model turn is given incorrect success context and can generate a wrong follow-up response.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
model_gateway/src/routers/grpc/harmony/builder.rs (1)

418-435: ⚠️ Potential issue | 🟠 Major

Don’t classify image_generation as a custom Harmony tool.

Line 428 adds "image_generation" to the tool_types list, but has_custom_tools() still only treats web_search_preview, code_interpreter, and container as builtins. That makes image-generation-only requests set with_custom_tools = true, even though build_developer_message_from_responses() only emits descriptions for ResponseTool::Function(_). The Responses Harmony path will therefore take the custom-tool prompt branch for a builtin and can emit an empty developer message.

Suggested fix
-            let tool_types: Vec<&str> = request
-                .tools
-                .as_ref()
-                .map(|tools| {
-                    tools
-                        .iter()
-                        .map(|tool| match tool {
-                            ResponseTool::Function(_) => "function",
-                            ResponseTool::WebSearchPreview(_) => "web_search_preview",
-                            ResponseTool::CodeInterpreter(_) => "code_interpreter",
-                            ResponseTool::ImageGeneration(_) => "image_generation",
-                            ResponseTool::Mcp(_) => "mcp",
-                        })
-                        .collect()
-                })
-                .unwrap_or_default();
-
-            let with_custom_tools = has_custom_tools(&tool_types);
+            let with_custom_tools = request.tools.as_ref().is_some_and(|tools| {
+                tools
+                    .iter()
+                    .any(|tool| matches!(tool, ResponseTool::Function(_) | ResponseTool::Mcp(_)))
+            });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/grpc/harmony/builder.rs` around lines 418 - 435,
tool_types currently includes "image_generation" which causes with_custom_tools
(called via has_custom_tools) to return true even though image generation is a
builtin; update the logic so image_generation is treated as a builtin. Fix by
modifying has_custom_tools to exclude "image_generation" from the custom-tools
set (or add "image_generation" to its builtin list), or alternatively filter out
"image_generation" from tool_types before calling has_custom_tools; ensure this
aligns with ResponseTool::ImageGeneration and with
build_developer_message_from_responses so image-generation-only requests do not
flip the custom-tool prompt branch.
model_gateway/src/routers/mcp_utils.rs (1)

133-158: ⚠️ Potential issue | 🟠 Major

Handle ResponseTool::FileSearch(_) in both builtin-routing helpers.

The updated docstrings now list file_search, but both match statements still skip ResponseTool::FileSearch(_). As a result, file-search builtins never make it into builtin routing or ensure_request_mcp_client(), so a configured MCP server for {type: "file_search"} still won't be attached on this path.

Also applies to: 192-201

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/mcp_utils.rs` around lines 133 - 158, The builtin
routing helpers currently ignore ResponseTool::FileSearch and so file_search
never gets routed; update collect_builtin_routing to include a match arm mapping
ResponseTool::FileSearch(_) => BuiltinToolType::FileSearch and likewise update
the other helper (ensure_request_mcp_client / the second match block that
mirrors lines ~192-201) to handle ResponseTool::FileSearch(_) so configured MCP
servers for type "file_search" are considered; make sure the BuiltinToolType
enum variant FileSearch is used in both places to mirror the other builtins.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@model_gateway/src/routers/openai/mcp/tool_loop.rs`:
- Around line 268-313: request_tool_overrides currently serializes the entire
ResponseTool::ImageGeneration object and can merge wrapper or deferred keys
(like type or per-option image settings) into MCP args; change
request_tool_overrides to only extract and return a JSON object containing the
explicitly supported image-generation override keys (e.g., "model" and
"revised_prompt")—dropping nulls—and nothing else so
apply_request_tool_overrides will only insert those allowed keys; keep the
null-filtering behavior and ensure behavior is consistent with
sanitize_builtin_tool_arguments for ResponseFormat::ImageGenerationCall and
return None when no supported overrides are present.

---

Outside diff comments:
In `@model_gateway/src/routers/grpc/harmony/builder.rs`:
- Around line 418-435: tool_types currently includes "image_generation" which
causes with_custom_tools (called via has_custom_tools) to return true even
though image generation is a builtin; update the logic so image_generation is
treated as a builtin. Fix by modifying has_custom_tools to exclude
"image_generation" from the custom-tools set (or add "image_generation" to its
builtin list), or alternatively filter out "image_generation" from tool_types
before calling has_custom_tools; ensure this aligns with
ResponseTool::ImageGeneration and with build_developer_message_from_responses so
image-generation-only requests do not flip the custom-tool prompt branch.

In `@model_gateway/src/routers/mcp_utils.rs`:
- Around line 133-158: The builtin routing helpers currently ignore
ResponseTool::FileSearch and so file_search never gets routed; update
collect_builtin_routing to include a match arm mapping
ResponseTool::FileSearch(_) => BuiltinToolType::FileSearch and likewise update
the other helper (ensure_request_mcp_client / the second match block that
mirrors lines ~192-201) to handle ResponseTool::FileSearch(_) so configured MCP
servers for type "file_search" are considered; make sure the BuiltinToolType
enum variant FileSearch is used in both places to mirror the other builtins.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: eb8217d3-6ed8-4824-8d64-b2a6ce6b1d30

📥 Commits

Reviewing files that changed from the base of the PR and between 6c39621 and 01365fb.

📒 Files selected for processing (18)
  • .gitignore
  • crates/mcp/src/core/config.rs
  • crates/mcp/src/lib.rs
  • crates/mcp/src/transform/mod.rs
  • crates/mcp/src/transform/transformer.rs
  • crates/mcp/src/transform/types.rs
  • crates/protocols/src/event_types.rs
  • crates/protocols/src/responses.rs
  • e2e_test/responses/test_builtin_tools.py
  • model_gateway/src/routers/grpc/common/responses/streaming.rs
  • model_gateway/src/routers/grpc/common/responses/utils.rs
  • model_gateway/src/routers/grpc/harmony/builder.rs
  • model_gateway/src/routers/mcp_utils.rs
  • model_gateway/src/routers/mod.rs
  • model_gateway/src/routers/openai/mcp/tool_loop.rs
  • model_gateway/src/routers/openai/responses/streaming.rs
  • model_gateway/src/routers/openai/responses/utils.rs
  • model_gateway/src/routers/tool_output_context.rs

Comment on lines +268 to +313
fn request_tool_overrides(
response_format: &ResponseFormat,
original_body: &ResponsesRequest,
) -> Option<Value> {
if !matches!(response_format, ResponseFormat::ImageGenerationCall) {
return None;
}

// Read request-defined tools and find the image_generation config.
let tools = original_body.tools.as_ref()?;

tools.iter().find_map(|tool| {
// Serialize image tool config into a JSON object for merge.
let mut serialized = match tool {
ResponseTool::ImageGeneration(image_tool) => match to_value(image_tool).ok()? {
Value::Object(obj) => obj,
_ => return None,
},
_ => return None,
};
// Drop nulls so absent fields do not overwrite generated call arguments.
serialized.retain(|_, v| !v.is_null());
if serialized.is_empty() {
None
} else {
Some(Value::Object(serialized))
}
})
}

fn apply_request_tool_overrides(
response_format: &ResponseFormat,
original_body: &ResponsesRequest,
arguments: &mut Value,
) {
if let (Some(overrides), Some(args_obj)) = (
request_tool_overrides(response_format, original_body),
arguments.as_object_mut(),
) {
let Some(override_obj) = overrides.as_object() else {
return;
};
for (k, v) in override_obj {
args_obj.insert(k.clone(), v.clone());
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Filter image-generation overrides before merging them into MCP args.

request_tool_overrides() serializes the entire ResponseTool::ImageGeneration object and merges every non-null field into the execution payload. That reintroduces wrapper keys like type and any currently-deferred image options into the MCP call, which can break tools that only accept the sanitized image-generation argument subset. Please restrict this merge to the explicitly supported keys instead of cloning the whole tool object.

Based on learnings, sanitize_builtin_tool_arguments in model_gateway/src/routers/openai/mcp/tool_loop.rs intentionally keeps only model and revised_prompt for ResponseFormat::ImageGenerationCall; per-option overrides/defaults are deferred.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@model_gateway/src/routers/openai/mcp/tool_loop.rs` around lines 268 - 313,
request_tool_overrides currently serializes the entire
ResponseTool::ImageGeneration object and can merge wrapper or deferred keys
(like type or per-option image settings) into MCP args; change
request_tool_overrides to only extract and return a JSON object containing the
explicitly supported image-generation override keys (e.g., "model" and
"revised_prompt")—dropping nulls—and nothing else so
apply_request_tool_overrides will only insert those allowed keys; keep the
null-filtering behavior and ensure behavior is consistent with
sanitize_builtin_tool_arguments for ResponseFormat::ImageGenerationCall and
return None when no supported overrides are present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes mcp MCP related changes model-gateway Model gateway crate changes openai OpenAI router changes protocols Protocols crate changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant