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())

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.17

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.17
File Size Uploaded
axonpush-0.0.17.tar.gz 656.4 kB Details

Built distribution (wheel)

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

Total release size: 1.2 MB

Release files / axonpush-0.0.17.tar.gz

Download URL axonpush-0.0.17.tar.gz
Size 656.4 kB
Tags Source
SHA-256 checksum
How to use checksums
e2dff69bb3c601f169154d08ea7bdb5296d5675085379ce6460a507962fe8c37
BLAKE2b-256 checksum
How to use checksums
11203d3b29208b6887af7fca0807c5622838ce84d9dae91c0cf4c2f78e2853ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","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.17-py3-none-any.whl

Download URL axonpush-0.0.17-py3-none-any.whl
Size 589.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c5b1ff97dbca2e617b8fb40539093b69db25ef3060c966b6f2d1755c22674b6d
BLAKE2b-256 checksum
How to use checksums
84c6bbf6404807a4d0ca73e57e2cbb819b988ce1bdf3e56e391c35fc1eb6c74c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","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

0.0.18

2 release files

This release

0.0.17 This release

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