Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
📝 WalkthroughWalkthroughAdds Changes
Sequence Diagram(s)sequenceDiagram
participant Client as "Client"
participant Route as "route_responses"
participant Load as "load_input_history"
participant Prepare as "prepare_agent_loop_input"
participant Converter as "converters / grpc builders"
participant ToolLoop as "Tool Loop / Upstream"
Client->>Route: POST ResponsesRequest(input)
Route->>Load: load_input_history(previous_response_id / conversation)
Load-->>Route: LoadedInputHistory
Route->>Prepare: prepare_agent_loop_input(history + client input)
Prepare->>Prepare: strip McpListTools (collect labels)
Prepare->>Prepare: expand McpCall -> FunctionToolCall + FunctionCallOutput
Prepare-->>Route: PreparedAgentLoopInput(upstream_input, labels)
Route->>Converter: convert / build messages (skip MCP items)
Converter-->>ToolLoop: push normalized messages
ToolLoop-->>Route: tool loop results / final response
Route-->>Client: Response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
b7fb329 to
381c879
Compare
381c879 to
3485930
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/responses/route.rs`:
- Around line 133-134: The RequestContext is still built from the original
body.clone(), so downstream tool-loop paths get the unnormalized input; after
calling super::history::prepare_agent_loop_input(&request_body.input) and
assigning request_body.input = prepared.upstream_input, update how the
RequestContext is created: ensure ctx.responses_request() (and the other
occurrence around the similar block at the later lines) uses the
prepared/modified request_body (or the serialized payload derived from it)
instead of the original body.clone(); in short, replace usages of body.clone()
when constructing payload/RequestContext with the prepared request_body so the
propagated payload matches prepare_agent_loop_input's canonicalized
upstream_input.
In `@model_gateway/tests/api/responses_api_test.rs`:
- Around line 647-663: The test currently only checks for absence of
"mcp_list_tools" but still allows a new "mcp_call" plus a final "message" to
pass; update the first assertion that inspects output to assert there are no
items with type "mcp_call" (i.e., change the predicate from "mcp_list_tools" to
"mcp_call" or otherwise assert output.iter().all(|item|
item.get("type").and_then(|v| v.as_str()) != Some("mcp_call"))), so the resume
response cannot contain a new MCP invocation, while keeping the existing
assertion that a final "message" exists.
🪄 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: 52aef8c8-26a6-4edc-bfb1-18be881807bd
📒 Files selected for processing (8)
crates/protocols/src/responses.rscrates/protocols/tests/responses.rsmodel_gateway/benches/routing_allocation_bench.rsmodel_gateway/src/routers/grpc/harmony/builder.rsmodel_gateway/src/routers/grpc/regular/responses/conversions.rsmodel_gateway/src/routers/openai/responses/history.rsmodel_gateway/src/routers/openai/responses/route.rsmodel_gateway/tests/api/responses_api_test.rs
3485930 to
7922bdd
Compare
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7922bdd448
ℹ️ 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".
7922bdd to
efd6558
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: efd6558683
ℹ️ 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".
ecda215 to
4211d52
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/protocols/tests/responses.rs`:
- Around line 1800-1848: The test response_input_accepts_mcp_trace_items only
checks serde deserialization but not the protocol validation; after creating the
ResponsesRequest named request, call the validator (request.validate()) and
assert it succeeds (e.g. request.validate().expect("request should validate") or
assert!(request.validate().is_ok())) so missing match arms in the
ResponsesRequest validation can't regress silently; place this assertion
immediately after the deserialization (after the .expect("request should
deserialize")) and before further assertions about ResponseInputOutputItem
variants.
In `@model_gateway/src/routers/openai/responses/history.rs`:
- Around line 384-388: The ResponseInput::Items branch inside
append_current_input is applying normalize_input_item to each incoming item,
which duplicates normalization because prepare_agent_loop_input also normalizes
the stitched transcript; remove the .map(normalize_input_item) call in the
ResponseInput::Items(current_items) handling in append_current_input so it
simply extends items with the incoming items (preserving their original
variants), and let prepare_agent_loop_input retain the single normalization
boundary via normalize_input_item when assembling the final input.
🪄 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: 4f22b052-84da-4814-9207-53fb4ba4cad3
📒 Files selected for processing (13)
crates/protocols/src/responses.rscrates/protocols/tests/responses.rsmodel_gateway/benches/routing_allocation_bench.rsmodel_gateway/src/routers/grpc/harmony/builder.rsmodel_gateway/src/routers/grpc/regular/responses/conversions.rsmodel_gateway/src/routers/openai/context.rsmodel_gateway/src/routers/openai/mcp/tool_loop.rsmodel_gateway/src/routers/openai/responses/history.rsmodel_gateway/src/routers/openai/responses/non_streaming.rsmodel_gateway/src/routers/openai/responses/route.rsmodel_gateway/src/routers/openai/responses/streaming.rsmodel_gateway/tests/api/responses_api_test.rsmodel_gateway/tests/routing/test_openai_routing.rs
Signed-off-by: Ziwen Zhao <zzw.mose@gmail.com>
Signed-off-by: Ziwen Zhao <zzw.mose@gmail.com>
Signed-off-by: Ziwen Zhao <zzw.mose@gmail.com>
e3569aa to
153b3b5
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/protocols/tests/responses.rs`:
- Around line 2038-2110: The test
mcp_call_input_omits_optional_fields_when_unset currently only checks that
optional fields are omitted; extend it to assert the full MCP wire shape for the
McpCall variant by verifying required serialized fields (e.g. "id",
"server_label", "name", "arguments", "output", "status", and that "type" ==
"mcp_call") are present and have the expected values after serializing the
ResponseInputOutputItem::McpCall instance; use the serialized serde_json::Value
(v) to check each key/value so regressions that drop required fields are
detected.
In `@model_gateway/src/routers/grpc/harmony/builder.rs`:
- Around line 1124-1129: The test currently only checks messages.len() == 2 but
should assert that the two surviving messages are the user "hello" and assistant
"done"; update the test after calling
HarmonyBuilder::new().construct_input_messages_with_harmony(&request) to inspect
the returned messages vector and assert each element's role and text (e.g.,
messages[0] is role "user" with content "hello" and messages[1] is role
"assistant" with content "done") using assert_eq! or pattern matches so the test
verifies which specific messages survived rather than only the count.
In `@model_gateway/src/routers/grpc/regular/responses/conversions.rs`:
- Around line 616-618: The test currently only checks chat_req.messages.len();
update it to assert the retained messages' roles and contents to ensure user and
assistant messages survive and MCP trace items are omitted: after creating
chat_req via responses_to_chat(&req), add assertions that chat_req.messages[0]
has role "user" and the expected content string, and that chat_req.messages[1]
has role "assistant" with its expected content (or compare full Message objects
if available), and also assert that none of the messages contains MCP trace
markers; reference responses_to_chat and chat_req.messages to locate where to
add these checks.
🪄 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: 1f7feb3c-3ddb-4d9f-b535-cbc6349d2756
📒 Files selected for processing (13)
crates/protocols/src/responses.rscrates/protocols/tests/responses.rsmodel_gateway/benches/routing_allocation_bench.rsmodel_gateway/src/routers/grpc/harmony/builder.rsmodel_gateway/src/routers/grpc/regular/responses/conversions.rsmodel_gateway/src/routers/openai/context.rsmodel_gateway/src/routers/openai/mcp/tool_loop.rsmodel_gateway/src/routers/openai/responses/history.rsmodel_gateway/src/routers/openai/responses/non_streaming.rsmodel_gateway/src/routers/openai/responses/route.rsmodel_gateway/src/routers/openai/responses/streaming.rsmodel_gateway/tests/api/responses_api_test.rsmodel_gateway/tests/routing/test_openai_routing.rs
|
This pull request has been automatically marked as stale because it has not had any activity within 14 days. It will be automatically closed if no further activity occurs within 16 days. Leave a comment if you feel this pull request should remain open. Thank you! |
|
Hi @zhaowenzi, this PR has merge conflicts that must be resolved before it can be merged. Please rebase your branch: git fetch origin main
git rebase origin/main
# resolve any conflicts, then:
git push --force-with-lease |
|
This pull request has been automatically marked as stale because it has not had any activity within 14 days. It will be automatically closed if no further activity occurs within 16 days. Leave a comment if you feel this pull request should remain open. Thank you! |
|
This pull request has been automatically closed due to inactivity. Please feel free to reopen if you intend to continue working on it. Thank you! |
Parent: #1316
Closes #1317
Description
Problem
Responses-side agentic control flow in SMG is spread across layers:
ResponsesRequest/ResponsesResponse, the upstream model payload, and the stored response chain used byprevious_response_id.previous_response_idreplay currently dropsmcp_list_tools/mcp_callitems (they fail to deserialize as input) and downstream MCP calls get re-executed.Solution
Introduce one shared agent loop used by every Responses surface. The loop decides the next action from state (
CallLlm/ExecuteTools/InterruptForApproval/Finish). Every request enters the loop, even one with no MCP tools — it simply never produces anExecuteToolsaction.The plan formalizes three representation boundaries — the client-facing
ResponsesRequest/ResponsesResponse, the canonical loop transcript, and the provider-specific upstream payload — and a surface-adapter contract covering history preparation, upstream request construction, turn ingestion, and final / interrupt / streaming rendering. MCP session state,mcp_list_toolsemission dedupe, pending tool batches, approval interrupts, andmax_tool_callsaccounting all live on the loop state rather than being rediscovered at each patch point.This design has already been implemented end-to-end once as a working prototype, with contract-level OpenAI parity checks for non-streaming and streaming MCP flows including approval interrupts. The expected outcome of the staged series below is to re-land that proven design on
mainin small, reviewable pieces without regressing anything along the way.Staged PRs:
NextActiondriver;previous_response_idstops changing loop behavior after preparation; continuation is a loop transition, not a side path.response.completedassembled from loop state; sibling buffering for mixed gateway + user function calls.mcp_approval_request→output_item.done→response.completed→[DONE]; approved continuation obeys the same budget/error behavior as non-streaming.ResponseFormatpresentation and internal/hidden MCP filtering across streaming + non-streaming.routers/common/agent_loop. Move the OpenAI-proven abstractions behind a shared adapter trait.Changes
Scope of this PR: OpenAI Responses only; history loading + input normalization only. Out of scope (deferred to later PRs):
NextActionloop driver, streaming loop rewrite, new approval workflow behavior.crates/protocols/src/responses.rs): addMcpListToolsandMcpCallasResponseInputOutputItemvariants. Structural validation only — continuation-specific semantics (e.g. pairing an approval response with its originating request) are left to the loop-entry layer in PR2.model_gateway/src/routers/openai/responses/history.rs:load_input_historynow does source acquisition only — it fetchesprevious_response_id/conversationhistory and appends clientinput; no label extraction, no normalization.prepare_agent_loop_inputis the single transcript-normalization boundary. It expandsmcp_callintofunction_call+function_call_outputso the upstream model sees a replayable tool execution, stripsmcp_list_toolsfrom the upstream transcript while recording itsserver_labelfor dedupe, and passes approval items through unchanged so the existing approval continuation flow (merged in feat(responses): interrupt approval-required MCP tool calls #1174) keeps working.model_gateway/src/routers/openai/responses/route.rs: calls the new preparation step betweenload_input_historyand upstream request build; populatesResponsesPayloadState.existing_mcp_list_tools_labelsfrom the preparation output.grpc/harmony/builder.rs,grpc/regular/responses/conversions.rs, andmodel_gateway/benches/routing_allocation_bench.rsinclude the two new enum variants (no behavior change).test_previous_response_id_does_not_repeat_mcp_list_tools_for_existing_bindingintests/api/responses_api_test.rs: previously asserted the pre-normalization behavior where replay dropped MCP items and the mock had to re-execute the tool; now asserts the invariant — no repeatedmcp_list_toolson aprevious_response_idcontinuation for an existing binding, and a finalmessageis still produced.Test Plan
Automated — all green:
New tests:
crates/protocols/tests/responses.rs::response_input_accepts_mcp_trace_items— protocol acceptsmcp_list_toolsandmcp_callas input items.crates/protocols/tests/responses.rs::mcp_call_input_omits_optional_fields_when_unset—approval_request_id/erroromitted on serialize whenNone.openai::responses::history::tests::prepare_agent_loop_input_passes_text_through— text input identity.…::prepare_agent_loop_input_expands_mcp_call_to_function_pair—mcp_call→ pairedfunction_call+function_call_outputwith matchingcall_id.…::prepare_agent_loop_input_reuses_approval_request_id_for_call_id— resumed call derives itscall_idfrom the originatingmcpr_*.…::prepare_agent_loop_input_collects_list_tools_labels—mcp_list_toolsstripped from upstream,server_labels deduped in first-seen order.…::prepare_agent_loop_input_preserves_approval_items_in_upstream— approval items pass through so existing continuation keeps working.Manual OpenAI parity — same style as PR #1174's test plan. Launched local
smgin IGW mode and registered the upstream OpenAI worker:Five scenarios exercised end-to-end against real OpenAI, all behaving as expected:
gpt-5.4, text input) — completes with a singlemessage.deepwiki,require_approval: never) — output order:mcp_list_tools→mcp_call→ (optional secondmcp_call) →message.inputcarriesmessage+mcp_list_tools+mcp_call(with a canned tool result) + assistant message + new user message. Upstream sees the tool result normalized intofunction_call+function_call_outputand answers directly without re-executing the tool; response output is a singlemessagecontaining the summarized answer. Without this PR the stitched MCP items fail to deserialize on the input side and the tool gets re-executed.previous_response_idfollow-up (after a stored MCP turn) — response has no repeatedmcp_list_tools, nomcp_call, just a finalmessagethat reuses the tool result from the stored transcript.deepwiki: never+openai-developer-docs: always) — output: twomcp_list_tools, deepwikimcp_call, terminates atmcp_approval_requestexactly as PR feat(responses): interrupt approval-required MCP tool calls #1174 specified. Confirms the existing approval interrupt path is untouched.Checklist
cargo +nightly fmtpassescargo clippy --all-targets --all-features -- -D warningspassesmainwith the router-common extraction PR.Summary by CodeRabbit
New Features
Improvements
Tests