axonpush
Recipes

Add observability to an agent framework

Drop-in handlers for LangChain, OpenAI Agents, Anthropic, CrewAI, Deep Agents, LangGraph, LlamaIndex, Mastra, Vercel AI SDK, Google ADK and Semantic Kernel, plus what each one actually emits.

You already use a framework. You want every chain step, tool call and model interaction on a trace without wrapping each one by hand. Each integration is a handler, hook or middleware the framework already knows how to call.

Install

pip install "axonpush[langchain]"        # LangChain / LangGraph
pip install "axonpush[openai-agents]"   # OpenAI Agents SDK
pip install "axonpush[anthropic]"       # Anthropic
pip install "axonpush[crewai]"          # CrewAI
pip install "axonpush[deepagents]"      # LangChain Deep Agents
pip install "axonpush[otel]"            # OpenTelemetry exporter
pip install "axonpush[all]"             # all of the above

Python ships one extra per framework. TypeScript ships them all in @axonpush/sdk, imported from @axonpush/sdk/integrations/<name>, with the framework itself as a peer dependency.

Python

LangChain and LangGraph

from axonpush import AxonPush
from axonpush.integrations.langchain import AxonPushCallbackHandler

client = AxonPush()
handler = AxonPushCallbackHandler(client, "ch_...", agent_id="my-agent")

chain.invoke({"input": "research AI frameworks"}, config={"callbacks": [handler]})

AsyncAxonPushCallbackHandler is the AsyncAxonPush sibling.

OpenAI Agents SDK

from axonpush import AsyncAxonPush
from axonpush.integrations.openai_agents import AxonPushRunHooks

client = AsyncAxonPush()
hooks = AxonPushRunHooks(client, "ch_...")

result = await Runner.run(agent, input="research AI frameworks", hooks=hooks)

Anthropic

from axonpush import AxonPush
from axonpush.integrations.anthropic import AxonPushAnthropicTracer

client = AxonPush()
tracer = AxonPushAnthropicTracer(client, "ch_...")

response = tracer.create_message(
    anthropic_client,
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Research AI frameworks"}],
)

CrewAI

from axonpush import AxonPush
from axonpush.integrations.crewai import AxonPushCrewCallbacks

client = AxonPush()
callbacks = AxonPushCrewCallbacks(client, "ch_...")

callbacks.on_crew_start()
result = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    step_callback=callbacks.on_step,
    task_callback=callbacks.on_task_complete,
).kickoff()
callbacks.on_crew_end(result)

Deep Agents

from deepagents import create_deep_agent

from axonpush import AxonPush
from axonpush.integrations.deepagents import AxonPushDeepAgentHandler

client = AxonPush()
handler = AxonPushDeepAgentHandler(client, "ch_...", agent_id="deep-agent")

agent = create_deep_agent(tools=[], system_prompt="You are a helpful assistant.")
agent.invoke(
    {"messages": [{"role": "user", "content": "Research AI frameworks"}]},
    config={"callbacks": [handler]},
)

AsyncAxonPushDeepAgentHandler is the async sibling.

TypeScript

Every integration takes the same config object: client, channelId, and optionally agentId, traceId, mode, queueSize, overflowPolicy, shutdownTimeoutMs, concurrency.

import { AxonPush } from "@axonpush/sdk";
import { AxonPushCallbackHandler } from "@axonpush/sdk/integrations/langchain";
import { AxonPushRunHooks } from "@axonpush/sdk/integrations/openai-agents";
import { axonPushMiddleware } from "@axonpush/sdk/integrations/vercel-ai";

const client = new AxonPush();

// LangChain
const handler = new AxonPushCallbackHandler({ client, channelId: "ch_...", agentId: "my-agent" });
await chain.invoke({ input: "…" }, { callbacks: [handler] });

// OpenAI Agents
const hooks = new AxonPushRunHooks({ client, channelId: "ch_..." });

// Vercel AI SDK - a LanguageModelMiddleware
const model = wrapLanguageModel({
  model: openai("gpt-4.1"),
  middleware: axonPushMiddleware({ client, channelId: "ch_..." }),
});

Available under @axonpush/sdk/integrations/:

Import pathExport
langchainAxonPushCallbackHandler
langgraphAxonPushLangGraphHandler
openai-agentsAxonPushRunHooks
anthropicAxonPushAnthropicTracer
llamaindexAxonPushLlamaIndexHandler
mastraAxonPushMastraExporter, AxonPushMastraHooks
vercel-aiaxonPushMiddleware
google-adkaxonPushADKCallbacks
otelAxonPushSpanExporter
sentryinstallSentry, buildDsn
pinocreateAxonPushPinoStream
winstoncreateAxonPushWinstonTransport
consolesetupConsoleCapture

.NET

There is no callback-handler equivalent; the .NET SDK covers Semantic Kernel and general OpenTelemetry:

var builder = Kernel.CreateBuilder();
builder.AddAxonPushTelemetry(
    client => { client.ApiKey = "ak_..."; client.TenantId = "org_..."; },
    exporter => { exporter.ChannelId = "ch_..."; });

See the .NET SDK reference. Note the wizard does not detect or wire .NET projects, install and configure these by hand.

What each integration emits

The identifier is what you filter and search on; the eventType is what dashboards, webhooks and alert rules key off.

FrameworkIdentifiersEvent types
LangChainchain.start, chain.end, chain.error, llm.start, llm.end, llm.token, tool.<name>.start, tool.end, tool.erroragent.start, agent.end, agent.error, agent.llm.token, agent.tool_call.start, agent.tool_call.end
OpenAI Agentsagent.run.start, agent.run.end, agent.handoff, tool.<name>.start, tool.<name>.endagent.start, agent.end, agent.handoff, agent.tool_call.start, agent.tool_call.end
Anthropicconversation.turn, agent.response, agent.usage, tool.<name>.start, tool.resultagent.start, agent.message, agent.tool_call.start, agent.tool_call.end
CrewAIcrew.start, crew.end, agent.step, task.complete, tool.<name>.start, tool.<name>.endagent.start, agent.end, agent.message, agent.tool_call.start, agent.tool_call.end
Deep Agentseverything LangChain emits, plus planning.update, planning.complete, subagent.spawn, subagent.complete, sandbox.execute, sandbox.execute.completeas LangChain, plus agent.handoff

Log forwarders, logging, loguru, structlog, pino, winston, console, emit app.log and are covered in the SDK references, not here.

Publishing off the hot path

Every Python integration publishes through a background publisher by default: events go onto an in-memory queue and a worker thread drains it, so a slow network never blocks a chain step. Pass mode="sync" to publish inline, useful in a test, wrong in production, or mode="rq" to hand off to an RQ queue. TypeScript takes the same mode on its config object and additionally offers a BullMQ publisher.

handler = AxonPushCallbackHandler(
    client,
    "ch_...",
    agent_id="my-agent",
    mode="background",     # "background" (default) | "sync" | "rq"
                           # CrewAI accepts "background" and "sync" only
    queue_size=1000,
    shutdown_timeout=5.0,
)

A short-lived process, a Lambda handler, a CLI, should either use mode="sync" or flush before exit, or the queue dies with the process and the tail of the run never arrives. The TypeScript publisher exposes flushAfterInvocation and detects serverless runtimes for exactly this.

Integrations always suppress publish errors, whatever the client’s fail_open setting. An observability failure cannot break the pipeline it is watching. That also means a silently misconfigured channel produces no events and no complaint, verify once, at wiring time, that events are landing.

Sharing a trace across integrations

Pass the same trace_id and two services’ events land in one trace:

handler = AxonPushCallbackHandler(client_a, "ch_a", trace_id=shared)
hooks = AxonPushRunHooks(client_b, "ch_b", trace_id=shared)

If you leave trace_id out, each integration adopts the ambient trace context, which, in one process, is already shared. See Trace a multi-step agent run.

Next