OpenAI Agents SDK
Record and replay non-streaming OpenAI Agents SDK runs with the separately packaged Kitaru v2 adapter
The Kitaru v2 OpenAI Agents adapter records a native OpenAI Agents SDK run as one Kitaru session. Model calls, tools, hosted tools, and handoffs appear as child nodes inside that session. The OpenAI SDK still executes the agent and returns its own result object.
This adapter ships as its own separately versioned distribution, kitaru-openai-agents (uv add kitaru-openai-agents); it is not exported by the installed kitaru package. Do not import it from kitaru.adapters.
Run an agent
Install the adapter package:
uv add kitaru-openai-agentsIn a Kitaru repository checkout, sync the plugin workspace instead:
uv sync --project plugins --all-packagesCreate the OpenAI agent as usual, then pass it to KitaruRunner.run(...) or KitaruRunner.run_sync(...):
import uuid
from agents import Agent
from kitaru_openai_agents import KitaruRunner
agent = Agent(
name="support_agent",
instructions="Answer briefly and accurately.",
model="gpt-5-nano",
)
runner = KitaruRunner(agent_id=uuid.UUID("00000000-0000-0000-0000-000000000001"))
result = runner.run_sync(agent, "Where is my order?")
print(result.final_output)Use await runner.run(...) in asynchronous code. run_sync(...) must not be called while an event loop is already running.
Both methods return the exact native OpenAI RunResult. Kitaru does not replace it with a custom result type, and it preserves caller-owned agents, hooks, context, OpenAI SDK sessions, run configuration, and response or conversation identifiers unless a worker-managed replay overrides the corresponding supported value.
Kitaru identity
A standalone run must set either agent_id or agent_version_id on KitaruRunner. You can also set session_name and batch_size:
A Kitaru worker provides KITARU_TASK_ID for task-bound runs. In that case, the server links the result session to the task and infers the agent identity, so the runner does not need either identity argument.
Correlate the native result with its session
Pass a sync or async session_observer when constructing KitaruRunner. Kitaru calls it with the new SessionResponse after the session and its root node exist. The callback can retain the session ID, then the caller can associate the exact native result returned later with that Kitaru session:
Define remember_session with async def when it needs to await other work. The observer runs before OpenAI executes the agent, so it identifies the session even if the native run later fails.
What Kitaru records
Before OpenAI executes the agent, Kitaru creates one session and an in-progress root node. As the run proceeds, it records structured observations for supported model calls, direct function-tool calls, provider-hosted tools, handoffs, token usage, final output, and failures. It completes the session only after all observations have been persisted.
Each model-call node names its model and provider and carries an estimated cost from the bundled genai-prices catalog. The OpenAI Agents SDK does not pass the model name the provider reports back to the adapter, so Kitaru records the name the run configured, in the SDK's own order: RunConfig.model, then the agent's model, then the SDK default. Names without a prefix, names prefixed with openai/, and OpenAIResponsesModel or OpenAIChatCompletionsModel instances are recorded with provider openai. Other prefixed names and custom model providers keep the configured name as requested_model but have no provider, served model, or cost, because Kitaru cannot tell which backend served them. A reasoning model's returned summary and reasoning text is stored in its model-call node's outputs and selected as that node's visible reasoning.
These nodes are observations of what the OpenAI SDK run did. They are not independently replayable units, and they do not change how the SDK runs the agent.
Replay behavior
Replay selection is worker-managed through KITARU_REPLAY_ID. KitaruRunner.run(...) and run_sync(...) have no per-run replay argument. Use the Kitaru replay and task flow, which starts the agent task with the selected replay, rather than mutating the process environment around concurrent standalone calls. Environment variables are process-wide, so one concurrent call could otherwise read another call's replay ID.
For the selected replay, the adapter can replace the root input, starting-agent instructions, run-level model, and model settings without mutating the caller's objects.
Direct FunctionTool replay supports three named policies:
Passthrough: call the original tool. This is the default.
Static: return the recorded or configured static value without calling the original tool.
History: return a recorded result with matching canonical JSON arguments without calling the original tool.
The default policy must remain passthrough, so configure history for each named tool you want to replay. A named static or history substitution must match one ordinary, direct, enabled, non-approval FunctionTool on the starting agent. Unsupported, ambiguous, duplicate, or unmatched tool policies fail before Kitaru creates a session or OpenAI calls the model. LLM, hosted, MCP, programmatic, agent-as-tool, handoff-target, and unknown tool substitutions are rejected.
With baseline history scope, repeated calls with identical arguments consume matching recorded results in invocation order. Concurrent identical calls receive distinct occurrences, but the adapter does not promise that callback scheduling reproduces the model response's source order. Cohort-version and agent history scopes use the newest matching result for every call.
Completed history matches replay their result, including null. A matched recorded failure raises ToolPolicyError inside the tool callback and does not execute the live tool. During a normal KitaruRunner run, the OpenAI Agents SDK exposes this as agents.exceptions.UserError with the ToolPolicyError in __cause__. History also fails closed when arguments are not strict canonical JSON, when a completed result contains Kitaru truncation metadata, when the target tool has an SDK timeout, or when the server predates the explicit match contract introduced in Kitaru 0.22.3. Sessions recorded by kitaru-openai-agents 0.1.x stored function arguments in a different shape and may not match calls made by 0.2.0; record a new baseline when you need history replay.
Deliberate exclusions
This first v2 adapter release does not provide:
run_streamedor token and event streamingper-call or mid-run replay from recorded observation nodes
a Kitaru sandbox helper for OpenAI tools
an adapter-specific request or result envelope
RunStateinput or durable approval interruption and resumeadapter-specific CLI, MCP, or server support
Approval interruptions fail closed instead of completing the Kitaru session. RunState input is rejected because Kitaru v2 does not yet have a durable interrupted-session state to resume.
Exceptions after OpenAI starts
The adapter exposes two public exceptions in kitaru_openai_agents for failures after OpenAI starts:
KitaruRecordingErrormeans OpenAI produced a native result but Kitaru failed while reconciling observations, finalizing the session, or closing the client. Itsresultfield preserves thatRunResult;session_ididentifies the Kitaru session when available; andphasenames the failed recording phase. It setsretry_safe=Falseandside_effects_possible=True, because automatically running the model or tools again could duplicate work or side effects.UnsupportedInterruptionErrormeans OpenAI returned an approval interruption that this adapter cannot durably resume. Itsresultfield preserves the interrupted nativeRunResult.
Data safety
Kitaru recording is independent of OpenAI tracing. Disabling OpenAI tracing does not disable Kitaru session and node recording.
The adapter excludes caller context, clients, credentials, environment state, callbacks, OpenAI SDK session objects, private SDK fields, encrypted reasoning content, and unknown-object serialization. Recorded values use deterministic size, depth, and collection limits with truncation metadata.
Effective prompts, tool arguments, tool results, and exception summaries can still contain sensitive application data. Review what your application sends to models and tools, and apply the same access controls and retention policy to Kitaru data that you apply to the original application payloads.
Runnable example
The repository example has a no-cost help check and an opt-in real run:
See examples/python/openai_agents_v2/README.md for setup details.
Last updated
Was this helpful?