Back to Openai Agents Python

Runner Lifecycle

.agents/references/runner-lifecycle.md

0.17.75.1 KB
Original Source

Runner Lifecycle

Use this reference for changes to Runner, turn accounting, guardrails, hooks, handoffs, interruptions, cancellation, or streaming and non-streaming behavior.

Turn Boundary

A turn is one logical model invocation plus processing of that response. Tool execution, handoff resolution, session persistence, interruption resume, and retries inside that logical invocation do not independently consume turns.

  • Increment the turn counter exactly once when the run loop starts a logical model turn. Transport or provider retries inside get_new_response() remain part of that turn.
  • A handoff changes the current agent, but the next turn begins only when the new agent invokes a model.
  • Resuming NextStepInterruption continues the paused turn. Resolve stored approvals and tool work before deciding whether another model call is needed.
  • Preserve max_turns and the current turn in RunState; resume must not reset the budget or charge a turn twice.

Guardrail Ordering

  • Input guardrails belong to the starting agent and run only for the initial user input. Do not rerun them after handoffs or when resuming an interruption.
  • Sequential input guardrails must finish before model-side effects begin. Parallel input guardrails may overlap the model call, so a tripwire or exception must cancel and await the in-flight model task and sibling guardrail tasks.
  • Tool input guardrails run before the approved tool side effect. Tool output guardrails run after local execution and before the output is accepted into the next step.
  • Output guardrails run only after a candidate final output exists. Streaming must await them and preserve the same tripwire and exception behavior as non-streaming execution before declaring completion.
  • Guardrail results are observable run state. Preserve them across handoffs, error handlers, streamed completion, and RunState round-trips.

Step State Machine

SingleStepResult.next_step is the control boundary after one model response and its local side effects:

StepMeaning
NextStepRunAgainContinue with the current agent and make another model call
NextStepHandoffSwitch the current agent, emit the transition, then continue
NextStepFinalOutputA final candidate exists; finish terminal hooks, output guardrails, persistence, and result construction
NextStepInterruptionPersist enough processed state to resume pending approvals without rerunning completed work

Do not bypass this state machine with path-local completion logic. New terminal or pausable behavior must define non-streaming, streaming, session, tracing, and serialized-resume semantics.

Streaming Parity and Cancellation

  • Streaming and non-streaming paths must produce equivalent final output, generated items, current agent, usage, guardrail results, session history, and interruption state for the same model behavior.
  • Raw transport events may differ, but semantic RunItemStreamEvent and AgentUpdatedStreamEvent emission must follow the same processed items and agent transitions used by the non-streaming result.
  • stream_events() is the stream driver's cleanup boundary. Keep consuming it until exhaustion after normal completion or cancel(), or explicitly close the async iterator; merely breaking after the last visible token does not prove session writes, guardrails, compaction, sandbox cleanup, usage, or terminal errors have settled.
  • Immediate cancellation marks the result complete and requests task cancellation. after_turn cancellation leaves the current model/tool turn running so it can persist state and usage before the next turn. Preserve this distinction instead of treating both modes as queue shutdown.
  • Terminal run-loop, guardrail, and max-turn errors must be surfaced from stream_events() after the required queued events are handled. Preserve run_loop_exception as a diagnostic view of the background task, not as a replacement completion primitive.
  • task.cancel() is a request, not cleanup completion. Await cancelled tasks when their finally blocks, exceptions, or owned resources affect run correctness.
  • Keep lifecycle hooks aligned across both paths, especially model start/end, handoff, tool start/end, and final-output hooks.

Review Checklist

  1. Identify which turn and which agent own the behavior.
  2. Trace every NextStep outcome, including interruption resume.
  3. Compare streaming and non-streaming side effects and terminal ordering.
  4. Test guardrail tripwires and exceptions in sequential and parallel modes when relevant.
  5. Verify normal exhaustion, explicit iterator close, immediate cancellation, and after-turn cancellation leave the documented result and owned resources in a coherent state.

Sources

  • src/agents/run.py
  • src/agents/run_internal/run_loop.py
  • src/agents/run_internal/run_steps.py
  • src/agents/run_internal/turn_preparation.py
  • src/agents/run_internal/turn_resolution.py
  • src/agents/run_internal/guardrails.py
  • tests/test_agent_runner.py
  • tests/test_agent_runner_streamed.py
  • tests/test_cancel_streaming.py
  • tests/test_guardrails.py
  • tests/test_run_state.py
  • docs/streaming.md
  • docs/results.md