Skip to main content

axonpush

PyPI

Python SDK for axonpush, observability and event infrastructure for AI agent systems.

Publish, trace, and deliver agent events. Drop-in integrations for LangChain, LangGraph Deep Agents, OpenAI Agents SDK, Anthropic, CrewAI, and the Python observability stack (stdlib logging, Loguru, structlog, OpenTelemetry, Sentry).

v1.0.0 is a breaking release. All IDs are str UUIDs; models live under a flat axonpush.models namespace; the realtime feature (MQTT / SSE / WebSocket / connect_realtime) has been removed. See CHANGELOG.md for the migration guide.

Install

pip install axonpush                  # or: uv add axonpush
pip install axonpush[langchain]       # LangChain / LangGraph
pip install axonpush[deepagents]      # LangChain Deep Agents
pip install axonpush[openai-agents]   # OpenAI Agents SDK
pip install axonpush[anthropic]       # Anthropic
pip install axonpush[crewai]          # CrewAI
pip install axonpush[loguru]          # Loguru sink
pip install axonpush[structlog]       # structlog processor
pip install axonpush[otel]            # OpenTelemetry SpanExporter
pip install axonpush[rq]              # Redis Queue durable backend
pip install axonpush[all]             # everything above

Installing the package also puts axonpush-eval on your PATH: the release gate that replays a dataset revision against a candidate in CI and exits non-zero when it regresses. Same flags and exit codes as the TypeScript and .NET builds. See the CLI reference.

Quick start

from axonpush import AxonPush, EventType

# Reads AXONPUSH_API_KEY / AXONPUSH_TENANT_ID / AXONPUSH_BASE_URL from env
# when the kwargs are omitted.
with AxonPush() as client:
    event = client.events.publish(
        "web_search",
        {"query": "AI agent frameworks"},
        channel_id="…channel uuid…",
        agent_id="researcher",
        event_type=EventType.AGENT_TOOL_CALL_START,
    )
    # event.event_id is server-assigned; event.queued is True within ~1 ms.

    listing = client.events.list(channel_id="…channel uuid…", limit=20)
    for ev in listing.data:
        print(ev.event_type, ev.identifier)

Async

import asyncio
from axonpush import AsyncAxonPush

async def main():
    async with AsyncAxonPush() as client:
        await client.events.publish(
            "web_search",
            {"query": "AI agents"},
            channel_id="…channel uuid…",
            agent_id="researcher",
            event_type="agent.tool_call.start",
        )

asyncio.run(main())

Zero-instrumentation: the LLM gateway

The fastest self-serve path to full observability is the axonpush gateway: no SDK, callback handler, or framework wrapper. Point an existing OpenAI (or Anthropic) client's base_url at the gateway and add the x-axonpush-api-key default header. Every call, tool call, cost, token count, and latency is captured, and any moderation / govern / spend policy runs inline, with zero code changes. It works alongside the framework integrations below.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.axonpush.xyz/gw/openai",
    default_headers={"x-axonpush-api-key": os.environ["AXONPUSH_API_KEY"]},
    # api_key still reads OPENAI_API_KEY; the gateway forwards it upstream.
)

The path segment (/gw/openai or /gw/anthropic) selects the wire shape. Add x-axonpush-target: openrouter|anthropic|groq|together|vercel|openai to route to a different upstream provider, with that provider's key in the standard Authorization: Bearer <provider_key> header.

Configuration

Every kwarg falls back to an AXONPUSH_… env var; constructor kwargs win.

AxonPush(
    api_key="ak_…",        # AXONPUSH_API_KEY
    tenant_id="…",         # AXONPUSH_TENANT_ID
    base_url="https://api.axonpush.xyz",  # AXONPUSH_BASE_URL
    environment="prod",    # AXONPUSH_ENVIRONMENT
    timeout=30.0,          # AXONPUSH_TIMEOUT
    max_retries=3,         # AXONPUSH_MAX_RETRIES
    fail_open=False,       # AXONPUSH_FAIL_OPEN
)

fail_open=True swallows APIConnectionError and returns None from every resource call, useful when axonpush observability must never break the host application.

Authentication

The SDK supports two ingest credentials:

Credential Header Prefix Use
API key X-API-Key ak_ Server-side ingestion and management. Full resource access, scoped per key.
Public ingest token X-Public-Token pt_ Browser and untrusted clients. Publish-only, safe to ship in a frontend.
# Server-side: API key
client = AxonPush(api_key="ak_…", tenant_id="…")

# Untrusted / browser context: public ingest token
client = AxonPush(api_key="pt_…", tenant_id="…")

Both are passed via the api_key argument (or AXONPUSH_API_KEY); the SDK sends ak_ values on X-API-Key and pt_ values on X-Public-Token.

Resources

The client exposes Stripe-style resource accessors:

Accessor Methods
client.events publish, list, search
client.channels create, get, update, delete
client.apps list, get, create, update, delete
client.environments list, create, update, delete, promote_to_default
client.webhooks create_endpoint, list_endpoints, delete_endpoint, deliveries
client.traces_v2 list, stats, detail, events, spans, facets, attribute_keys
client.api_keys list, create, delete
client.organizations list, get, create, update, delete, invite, remove_member, transfer_ownership
client.datasets list, get, create, delete, revisions, create_revision, items, …
client.experiments list, get, create, run, cancel, results, compare, gate, …
client.gates list_policies, get_policy, save_policy, delete_policy, list_runs

Every resource has an Async sibling on AsyncAxonPush, and is importable from axonpush.resources.

events.list() and events.search() return an EventListResponseDto with .data (list) and .meta (cursor + count).

Errors

from axonpush import (
    AxonPushError,            # base
    APIConnectionError,       # network / DNS / read timeout
    AuthenticationError,      # 401
    ForbiddenError,           # 403
    NotFoundError,            # 404
    ValidationError,          # 422 / code='validation_error'
    RateLimitError,           # 429, carries .retry_after
    ServerError,              # 5xx
    RetryableError,           # mixin: APIConnectionError, RateLimitError, ServerError
)

Every exception carries request_id, status_code, code, hint parsed from the backend's { code, message, hint, requestId } envelope. Anything that subclasses RetryableError is safe to retry; the SDK's transport already retries them up to max_retries with exponential backoff.

Integrations

Library Module Class / function
LangChain axonpush.integrations.langchain AxonPushCallbackHandler, AsyncAxonPushCallbackHandler
LangGraph Deep Agents axonpush.integrations.deepagents AxonPushDeepAgentHandler, AsyncAxonPushDeepAgentHandler
OpenAI Agents SDK axonpush.integrations.openai_agents AxonPushRunHooks
Anthropic axonpush.integrations.anthropic AxonPushAnthropicTracer
CrewAI axonpush.integrations.crewai AxonPushCrewCallbacks
stdlib logging axonpush.integrations.logging_handler AxonPushLoggingHandler
Loguru axonpush.integrations.loguru create_axonpush_loguru_sink
structlog axonpush.integrations.structlog axonpush_structlog_processor
print() capture axonpush.integrations.print_capture setup_print_capture
OpenTelemetry axonpush.integrations.otel AxonPushSpanExporter
Sentry compat axonpush.integrations.sentry install_sentry

All log/span integrations emit OpenTelemetry-shaped payloads (severityNumber, severityText, body, attributes, resource) so events line up with anything else you ship to an OTel-compatible backend.

Publishing modes

Every integration accepts a mode parameter:

Mode Backend Use case
"background" (default) In-process bounded queue Most apps
"sync" Direct HTTP call Tests, debugging
"rq" python-rq Durable delivery, serverless, high volume
from redis import Redis
from axonpush.integrations.langchain import AxonPushCallbackHandler

handler = AxonPushCallbackHandler(
    client, channel_id="…",
    mode="rq",
    rq_options={"redis_conn": Redis(), "queue_name": "axonpush"},
)

Then run rq worker axonpush somewhere.

Tracing

Group related events with a shared trace_id. The SDK auto-creates one per call, but you'll usually want to pin a trace to a logical request:

from axonpush import get_or_create_trace

trace = get_or_create_trace()
client.events.publish(
    "step.start", {"step": 1},
    channel_id="…",
    trace_id=trace.trace_id,
    span_id=trace.next_span_id(),
)

detail = client.traces_v2.detail(trace.trace_id)
summary = detail.summary
print(summary.event_count, summary.duration_ms, summary.tool_call_count)

get_or_create_trace() reads the active context (set via with TraceContext(...):) when one exists, so framework integrations propagate the trace automatically.

OpenTelemetry-native telemetry

axonpush.telemetry ships GenAI spans as real OTLP over HTTP to ${base}/v1/traces, routed by the X-Axonpush-Channel header and authed by X-API-Key. Spans follow the OpenTelemetry GenAI semantic conventions (gen_ai.*), so they are portable to any OTLP backend, not just axonpush. This is the recommended way to send traces.

pip install axonpush[otel]   # pulls opentelemetry-exporter-otlp-proto-http

configure_telemetry() reuses the application's own TracerProvider when one is already set globally (it never replaces it) and only creates one when the app does not own OpenTelemetry. Config resolves from arguments, then the matching AXONPUSH_BASE_URL / AXONPUSH_API_KEY / AXONPUSH_CHANNEL_ID env var.

from axonpush.telemetry import (
    configure_telemetry,
    genai_span,
    record_genai_response,
    record_genai_content,
)

# Reuses or creates a TracerProvider, attaches a batch OTLP/HTTP exporter,
# and stamps a Resource with service.name / deployment.environment.name / service.version.
handle = configure_telemetry(
    service_name="my-agent",
    environment="prod",
    service_version="1.4.0",
    content_capture="metadata_only",  # or "redacted" / "full"
)
tracer = handle.tracer()

with genai_span(tracer, operation="chat", request_model="gpt-4o", system="openai") as span:
    # ... call the model ...
    record_genai_response(
        span,
        response_model="gpt-4o",
        input_tokens=12,
        output_tokens=48,
        reasoning_tokens=0,
        cache_read_tokens=0,
        cache_write_tokens=0,
    )
    record_genai_content(span, prompt="…", completion="…", handle=handle)

handle.flush()  # or handle.shutdown() on clean exit

Prompt and completion land as span events (gen_ai.content.prompt / gen_ai.content.completion), gated by content_capture: metadata_only drops content, redacted keeps short previews, full keeps it. Credential-shaped keys are always stripped regardless of mode.

On serverless hosts (AWS Lambda and friends) the batch processor's atexit flush is unreliable because the container is frozen between invocations. configure_telemetry() logs a note when it detects one; call handle.flush() at the end of each invocation.

def handler(event, context):
    with genai_span(handle.tracer(), operation="chat", request_model="gpt-4o") as span:
        ...
    handle.flush()  # block until buffered spans ship

Which should I use

The OTel-native path above is the recommended way to send traces. The legacy per-framework event-model exporter (axonpush.integrations.otel.AxonPushSpanExporter, which maps calls to /event payloads) still works and is kept for compatibility, but it is being superseded by OTel-native. Frame it as the compatibility path: keep it if you already depend on channel fan-out on the events plane, otherwise reach for axonpush.telemetry. Traces from both land in the same dashboard (/v2/traces); the server reconciles them through the OTLP normalizer, so you can migrate call sites incrementally without a gap in your traces.

Examples

examples/ contains runnable recipes: quickstart, tracing, webhooks, async, error handling, plus one example per integration. Each reads AXONPUSH_API_KEY / AXONPUSH_TENANT_ID from your environment. See examples/README.md for the full table.

Advanced

For internal contracts, the resource ownership matrix, the OpenAPI codegen layer, and exception handling internals, see SHARED-CONTRACT.md.

License

MIT

Release files for axonpush 0.0.19

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for axonpush 0.0.19
File Size Uploaded
axonpush-0.0.19.tar.gz 634.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for axonpush 0.0.19
File Interpreter ABI Platform
axonpush-0.0.19-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / axonpush-0.0.19.tar.gz

Download URL axonpush-0.0.19.tar.gz
Size 634.4 kB
Tags Source
SHA-256 checksum
How to use checksums
86d98f86c58b5183a787f7320a8df76cf692dfc0888d04ff4a8990cb3a13d030
BLAKE2b-256 checksum
How to use checksums
cf65934fa999e567b3ecaf9613a32cf159f5c61658146e66a6fdebb9a16d371c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / axonpush-0.0.19-py3-none-any.whl

Download URL axonpush-0.0.19-py3-none-any.whl
Size 439.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1151642545b7fefaefecb9b8b72ac492bc861bd22055c8b9c1dfa5a69174abcc
BLAKE2b-256 checksum
How to use checksums
4420538c50c70489ae991af76afa17726ea246798ba5182d6b4cf2fb50fe5bfe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.0.19 This release

2 release files

0.0.18

2 release files

0.0.17

2 release files

0.0.16

2 release files

0.0.14

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page