Skip to content

AgentRunner cannot use previous_response_id for multi-turn tool-calls in the first run #2124

@enzoliao

Description

@enzoliao

Hi there! Hope you’re doing well. This is the version from my pip freeze:

openai==2.8.1
openai-agents==0.6.1

When using openai-agents-python, AgentRunner only leverages previous_response_id if a non-null previous_response_id is passed into the run. This makes it impossible to benefit from Responses API’s incremental conversation state (previous_response_id) inside the very first AgentRunner.run call, even if that run internally performs multiple model turns and tool calls.

In other words, complex “first turns” (multi-tool, multi-round workflows within a single run) cannot use previous_response_id at all, unless the caller already has an existing response id from a previous Responses/Agents call.

Code references

_ServerConversationTracker

@dataclass
class _ServerConversationTracker:
    """Tracks server-side conversation state for either conversation_id or
    previous_response_id modes."""

    conversation_id: str | None = None
    previous_response_id: str | None = None
    sent_items: set[int] = field(default_factory=set)
    server_items: set[int] = field(default_factory=set)

    def track_server_items(self, model_response: ModelResponse) -> None:
        for output_item in model_response.output:
            self.server_items.add(id(output_item))

        # Update previous_response_id only when using previous_response_id
        if (
            self.conversation_id is None
            and self.previous_response_id is not None
            and model_response.response_id is not None
        ):
            self.previous_response_id = model_response.response_id

AgentRunner.run:

previous_response_id = kwargs.get("previous_response_id")
conversation_id = kwargs.get("conversation_id")
...
if conversation_id is not None or previous_response_id is not None:
    server_conversation_tracker = _ServerConversationTracker(
        conversation_id=conversation_id, previous_response_id=previous_response_id
    )
else:
    server_conversation_tracker = None

So:
• _ServerConversationTracker is only created when conversation_id or previous_response_id is not None.
• And track_server_items only updates previous_response_id when it is already non-None

This means:
• If I call Runner.run(..., previous_response_id=None, conversation_id=None) for my first ever turn:
• server_conversation_tracker = None
• Every internal model call in that run sends the full input items (original prompt + all generated items so far).
• previous_response_id is never used or updated within that run.

Why this feels wrong

previous_response_id mode only works if you already have a prior response id, i.e., it’s only useful to chain separate runs, not to optimize the internal multi-turn workflow of a single first run.

From a user perspective, this might be surprising and limiting:
• A single AgentRunner.run might contain many internal turns (model → tool → model → …).
• For a complex first user question, this first run can be the heaviest one.
• But there is no way to say: “for this first run, please start with no prior state, but after the first LLM call, keep using its response_id as previous_response_id for subsequent internal turns”.

Expected behavior

I would expect at least one of the following to be possible:

  • First run can automatically “enter” previous_response_id mode after the first model call.
    • Even if the caller does not pass a previous_response_id, the first internal get_response in the run could seed previous_response_id for later turns in the same run.
  • Or: The SDK exposes a clear way to opt in to using previous_response_id within a run, even when starting from no previous response.
    • For example, the presence of some flag or config could tell AgentRunner to create a _ServerConversationTracker and to start using response_id of the first internal call as previous_response_id for subsequent calls.

Thanks for your help.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions