Skip to content

Conversation

@aibrahim-oai
Copy link
Collaborator

@aibrahim-oai aibrahim-oai commented Nov 26, 2025

  • Inline response recording during streaming: run_turn now records items as they arrive instead of building a ProcessedResponseItem list and post‑processing via process_items.
  • Simplify turn handling: handle_output_item_done returns the follow‑up signal + optional tool future; needs_follow_up is set only there, and in‑flight tool futures are drained once at the end (errors logged, no extra state writes).
  • Flattened stream loop: removed process_items indirection and the extra output queue
    • Tests: relaxed tool_parallelism::tool_results_grouped to allow any completion order while still requiring matching call/output IDs.

Copy link
Contributor

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

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.

ℹ️ 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".

Comment on lines 2225 to 2229
sess.record_conversation_items(
&turn_context,
std::slice::from_ref(&item),
)
.await;
Copy link
Contributor

Choose a reason for hiding this comment

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

P1 Badge Avoid persisting partial items before stream retries

Stream items are written to conversation history immediately in the OutputItemDone handler (record_conversation_items at lines 2225-2229), but run_turn will retry the same turn on transient stream errors (see the retry loop at lines 2113-2137). If the SSE disconnects before ResponseEvent::Completed, the partial tool call or message is already persisted and will be included in the next prompt when the turn retries, producing duplicated/partial conversation history and misleading turn diffs. The retry should happen before committing history or roll back the writes when the turn is retried.

Useful? React with 👍 / 👎.

@aibrahim-oai
Copy link
Collaborator Author

@codex review this

Copy link
Contributor

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

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.

ℹ️ 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".


output.push_back(
async move {
Ok(ProcessedResponseItem {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's what I mean. we were pushing to output directly

Ok(turn_output) => {
let processed_items = turn_output;
let TurnRunResult {
needs_follow_up,
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we need both of these? Can there be end of turn without assistant message?

Copy link
Collaborator

Choose a reason for hiding this comment

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

end_of_turn_message: Option<..> ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For OpenAI provider no. I am not sure about azure or why it was implemented like that originally

Copy link
Collaborator

Choose a reason for hiding this comment

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

From the API point of view, yes you can. But from a client point of view, a turn is stopped when we decide so it should be agnostic from the provider

/// history and rollout stay in sync even if the turn is later cancelled.
pub(crate) type InFlightFuture<'f> = Pin<Box<dyn Future<Output = Result<()>> + Send + 'f>>;

pub(crate) struct OutputItemResult {
Copy link
Collaborator

Choose a reason for hiding this comment

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

does it feel like we unnessesarily mixing tool (long running) and non-tool items?

This type is almost like a union, either we return a future or a possible end of turn.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

or reasoning

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I can have the match at an earlier layer. just wanted to decrease the code in codex.rs

if let Some(agent_message) = output_result.last_agent_message {
last_agent_message = Some(agent_message);
}
needs_follow_up |= output_result.needs_follow_up;
Copy link
Collaborator

Choose a reason for hiding this comment

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

this value always comes from the last item, isn't it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, ideally a tool call will be at the end if we need a follow up. Not the most correct if ResponsesAPI changed under us though

}
}
Ok(None) => {
if let Some(turn_item) = handle_non_tool_response_item(&item).await {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We handle handle_non_tool_response_item for item_added in the main loop. Should we do the same for done?

@jif-oai
Copy link
Collaborator

jif-oai commented Dec 2, 2025

Can you make sure this fixes the issue described here?
#7038

}

impl From<CancelErr> for CodexErr {
fn from(_: CancelErr) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we at least log the error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we log errors. which part you mean?

Err(err) => {
tracing::error!(error = ?err, "in-flight tool future failed during drain");
}

Ok(turn_output) => {
let processed_items = turn_output;
let TurnRunResult {
needs_follow_up,
Copy link
Collaborator

Choose a reason for hiding this comment

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

From the API point of view, yes you can. But from a client point of view, a turn is stopped when we decide so it should be agnostic from the provider

@aibrahim-oai aibrahim-oai merged commit 6e6338a into main Dec 4, 2025
26 checks passed
@aibrahim-oai aibrahim-oai deleted the linear-processing branch December 4, 2025 20:17
@github-actions github-actions bot locked and limited conversation to collaborators Dec 4, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants