When you call the Runner.run methods, you receive one of two result types:
- [
RunResult][agents.result.RunResult] fromRunner.run(...)orRunner.run_sync(...) - [
RunResultStreaming][agents.result.RunResultStreaming] fromRunner.run_streamed(...)
Both inherit from [RunResultBase][agents.result.RunResultBase], which exposes the shared result surfaces such as final_output, new_items, last_agent, raw_responses, and to_state().
RunResultStreaming adds streaming-specific controls such as [stream_events()][agents.result.RunResultStreaming.stream_events], [current_agent][agents.result.RunResultStreaming.current_agent], [is_complete][agents.result.RunResultStreaming.is_complete], and [cancel(...)][agents.result.RunResultStreaming.cancel].
Most applications only need a few result properties or helpers:
| If you need... | Use |
|---|---|
| The final answer to show the user | final_output |
| A replay-ready next-turn input list with the full local transcript | to_input_list() |
| Rich run items with agent, tool, handoff, and approval metadata | new_items |
| The agent that should usually handle the next user turn | last_agent |
OpenAI Responses API chaining with previous_response_id |
last_response_id |
| Pending approvals and a resumable snapshot | interruptions and to_state() |
Metadata about the current nested Agent.as_tool() invocation |
agent_tool_invocation |
| Raw model calls or guardrail diagnostics | raw_responses and the guardrail result arrays |
The [final_output][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either:
- a
str, if the last agent did not have anoutput_typedefined - an object of type
last_agent.output_type, if the last agent had an output type defined None, if the run stopped before a final output was produced, for example because it paused on an approval interruption
!!! note
`final_output` is typed as `Any`. Handoffs can change which agent finishes the run, so the SDK cannot statically know the full set of possible output types.
In streaming mode, final_output stays None until the stream has finished processing. See Streaming for the event-by-event flow.
These surfaces answer different questions:
| Property or helper | What it contains | Best for |
|---|---|---|
[input][agents.result.RunResultBase.input] |
The base input for this run segment. If a handoff input filter rewrote the history, this reflects the filtered input the run continued with. | Auditing what this run actually used as input |
[to_input_list()][agents.result.RunResultBase.to_input_list] |
An input-item view of the run. The default mode="preserve_all" keeps the full converted history from new_items; mode="normalized" prefers canonical continuation input when handoff filtering rewrites model history. |
Manual chat loops, client-managed conversation state, and plain-item history inspection |
[new_items][agents.result.RunResultBase.new_items] |
Rich [RunItem][agents.items.RunItem] wrappers with agent, tool, handoff, and approval metadata. |
Logs, UIs, audits, and debugging |
[raw_responses][agents.result.RunResultBase.raw_responses] |
Raw [ModelResponse][agents.items.ModelResponse] objects from each model call in the run. |
Provider-level diagnostics or raw response inspection |
In practice:
- Use
to_input_list()when you want a plain input-item view of the run. - Use
to_input_list(mode="normalized")when you want the canonical local input for the nextRunner.run(..., input=...)call after handoff filtering or nested handoff history rewrites. - Use
session=...when you want the SDK to load and save history for you. - If you are using OpenAI server-managed state with
conversation_idorprevious_response_id, usually pass only the new user input and reuse the stored ID instead of resendingto_input_list(). - Use the default
to_input_list()mode ornew_itemswhen you need the full converted history for logs, UIs, or audits.
Unlike the JavaScript SDK, Python does not expose a separate output property for the model-shaped delta only. Use new_items when you need SDK metadata, or inspect raw_responses when you need the raw model payloads.
Computer-tool replay follows the raw Responses payload shape. Preview-model computer_call items preserve a single action, while gpt-5.4 computer calls can preserve batched actions[]. [to_input_list()][agents.result.RunResultBase.to_input_list] and [RunState][agents.run_state.RunState] keep whichever shape the model produced, so manual replay, pause/resume flows, and stored transcripts continue to work across both preview and GA computer-tool calls. Local execution results still appear as computer_call_output items in new_items.
[new_items][agents.result.RunResultBase.new_items] gives you the richest view of what happened during the run. Common item types are:
- [
MessageOutputItem][agents.items.MessageOutputItem] for assistant messages - [
ReasoningItem][agents.items.ReasoningItem] for reasoning items - [
ToolSearchCallItem][agents.items.ToolSearchCallItem] and [ToolSearchOutputItem][agents.items.ToolSearchOutputItem] for Responses tool search requests and loaded tool-search results - [
ToolCallItem][agents.items.ToolCallItem] and [ToolCallOutputItem][agents.items.ToolCallOutputItem] for tool calls and their results - [
ToolApprovalItem][agents.items.ToolApprovalItem] for tool calls that paused for approval - [
HandoffCallItem][agents.items.HandoffCallItem] and [HandoffOutputItem][agents.items.HandoffOutputItem] for handoff requests and completed transfers
Choose new_items over to_input_list() whenever you need agent associations, tool outputs, handoff boundaries, or approval boundaries.
When you use hosted tool search, inspect ToolSearchCallItem.raw_item to see the search request the model emitted, and ToolSearchOutputItem.raw_item to see which namespaces, functions, or hosted MCP servers were loaded for that turn.
[last_agent][agents.result.RunResultBase.last_agent] contains the last agent that ran. This is often the best agent to reuse for the next user turn after handoffs.
In streaming mode, [RunResultStreaming.current_agent][agents.result.RunResultStreaming.current_agent] updates as the run progresses, so you can observe handoffs before the stream finishes.
If a tool needs approval, pending approvals are exposed in [RunResult.interruptions][agents.result.RunResult.interruptions] or [RunResultStreaming.interruptions][agents.result.RunResultStreaming.interruptions]. This can include approvals raised by direct tools, by tools reached after a handoff, or by nested [Agent.as_tool()][agents.agent.Agent.as_tool] runs.
Call [to_state()][agents.result.RunResult.to_state] to capture a resumable [RunState][agents.run_state.RunState], approve or reject the pending items, and then resume with Runner.run(...) or Runner.run_streamed(...).
from agents import Agent, Runner
agent = Agent(name="Assistant", instructions="Use tools when needed.")
result = await Runner.run(agent, "Delete temp files that are no longer needed.")
if result.interruptions:
state = result.to_state()
for interruption in result.interruptions:
state.approve(interruption)
result = await Runner.run(agent, state)For streaming runs, finish consuming [stream_events()][agents.result.RunResultStreaming.stream_events] first, then inspect result.interruptions and resume from result.to_state(). For the full approval flow, see Human-in-the-loop.
[last_response_id][agents.result.RunResultBase.last_response_id] is the latest model response ID from the run. Pass it back as previous_response_id on the next turn when you want to continue an OpenAI Responses API chain.
If you already continue the conversation with to_input_list(), session, or conversation_id, you usually do not need last_response_id. If you need every model response from a multi-step run, inspect raw_responses instead.
When a result comes from a nested [Agent.as_tool()][agents.agent.Agent.as_tool] run, [agent_tool_invocation][agents.result.RunResultBase.agent_tool_invocation] exposes immutable metadata about the outer tool call:
tool_nametool_call_idtool_arguments
For ordinary top-level runs, agent_tool_invocation is None.
This is especially useful inside custom_output_extractor, where you may need the outer tool name, call ID, or raw arguments while post-processing the nested result. See Tools for the surrounding Agent.as_tool() patterns.
If you also need the parsed structured input for that nested run, read context_wrapper.tool_input. That is the field [RunState][agents.run_state.RunState] serializes generically for nested tool input, while agent_tool_invocation is the live result accessor for the current nested invocation.
[RunResultStreaming][agents.result.RunResultStreaming] inherits the same result surfaces above, but adds streaming-specific controls:
- [
stream_events()][agents.result.RunResultStreaming.stream_events] to consume semantic stream events - [
current_agent][agents.result.RunResultStreaming.current_agent] to track the active agent mid-run - [
is_complete][agents.result.RunResultStreaming.is_complete] to see whether the streamed run has fully finished - [
cancel(...)][agents.result.RunResultStreaming.cancel] to stop the run immediately or after the current turn
Keep consuming stream_events() until the async iterator finishes. A streaming run is not complete until that iterator ends, and summary properties such as final_output, interruptions, raw_responses, and session-persistence side effects may still be settling after the last visible token arrives.
If you call cancel(), continue consuming stream_events() so cancellation and cleanup can finish correctly.
Python does not expose a separate streamed completed promise or error property. Terminal streaming failures are surfaced by raising from stream_events(), and is_complete reflects whether the run has reached its terminal state.
[raw_responses][agents.result.RunResultBase.raw_responses] contains the raw model responses collected during the run. Multi-step runs can produce more than one response, for example across handoffs or repeated model/tool/model cycles.
[last_response_id][agents.result.RunResultBase.last_response_id] is just the ID from the last entry in raw_responses.
Agent-level guardrails are exposed as [input_guardrail_results][agents.result.RunResultBase.input_guardrail_results] and [output_guardrail_results][agents.result.RunResultBase.output_guardrail_results].
Tool guardrails are exposed separately as [tool_input_guardrail_results][agents.result.RunResultBase.tool_input_guardrail_results] and [tool_output_guardrail_results][agents.result.RunResultBase.tool_output_guardrail_results].
These arrays accumulate across the run, so they are useful for logging decisions, storing extra guardrail metadata, or debugging why a run was blocked.
[context_wrapper][agents.result.RunResultBase.context_wrapper] exposes your app context together with SDK-managed runtime metadata such as approvals, usage, and nested tool_input.
Usage is tracked on context_wrapper.usage. For streamed runs, the usage totals can lag until the stream's final chunks have been processed. See Context management for the full wrapper shape and persistence caveats.