OpenAI Agents
Lifecycle hooks that trace agent runs, tool calls and handoffs from the OpenAI Agents SDK.
pip install "axonpush[openai-agents]"Tested against openai-agents>=0.1.0,<2.0.
Attach the hooks
The OpenAI Agents SDK is async-only, so this integration takes an
AsyncAxonPush.
from agents import Agent, Runner
from axonpush import AsyncAxonPush
from axonpush.integrations.openai_agents import AxonPushRunHooks
client = AsyncAxonPush()
hooks = AxonPushRunHooks(client, channel_id)
agent = Agent(name="research-agent", instructions="You are a research assistant.")
result = await Runner.run(agent, "Find papers on multi-agent systems", hooks=hooks)
await hooks.flush(timeout=2.0)client and channel_id are positional. AxonPushRunHooks subclasses the
SDK’s RunHooks, so it goes wherever a hooks= argument is accepted.
Pass an AsyncAxonPush. There is no sync variant, and mode="sync" drops
every event with a warning on the axonpush logger, the hooks publish
through an async background queue.
Constructor
AxonPushRunHooks(
client, # AsyncAxonPush - positional
channel_id, # str UUID - positional
*,
agent_id=None, # fallback when the agent has no name
trace_id=None,
mode=None, # "background" (default) | "sync"
max_pending=1000,
)agent_id is only a fallback. Each event is tagged with the agent’s own
name when the SDK provides one, so a multi-agent run attributes its events
correctly without configuration.
What gets published
| Identifier | Event type | Payload |
|---|---|---|
agent.run.start | agent.start | agent_name, model |
agent.run.end | agent.end | agent_name, output_length |
tool.<name>.start | agent.tool_call.start | tool_name, agent_name |
tool.<name>.end | agent.tool_call.end | tool_name, result_length |
agent.handoff | agent.handoff | from_agent, to_agent |
Every event carries framework: "openai-agents" in metadata, and shares the
trace id the hooks were constructed with, pass trace_id= to join a run to a
trace you already own, or leave it out to adopt the trace on the current
context.
Payloads are deliberately thin: lengths rather than contents. Nothing the model produced leaves your process through this integration.
Clean up
await hooks.flush(timeout=2.0) # drain the queue
await hooks.close() # drain and stop the worker
await client.close()Or let the context manager own the client:
async with AsyncAxonPush() as client:
hooks = AxonPushRunHooks(client, channel_id)
result = await Runner.run(agent, "…", hooks=hooks)
await hooks.flush(timeout=2.0)Both flush and close are coroutines here, unlike the LangChain handler’s
sync pair.