Skip to main content

Open-source observability SDK for AI agents — zero-instrumentation capture, OpenTelemetry-native

Project description

wardex-sdk

PyPI License

Open-source observability SDK for AI agents — zero-instrumentation capture, OpenTelemetry-native.

⚠️ Beta. PII masking is on by default (see below), but the SDK is still early: review the caveats below before sending sensitive data through it.

Install

pip install wardex-sdk

Quickstart

import wardex_sdk as wardex
from wardex_sdk import OtlpHttpTransport

wardex.init(
    transport=OtlpHttpTransport(endpoint="https://<your-collector>/v1/traces"),
    intercept=True,  # zero-instrumentation capture of LLM calls
)

# your app code — OpenAI/Anthropic calls are captured automatically

wardex.close()   # optional — spans auto-flush every 5s, on buffer threshold, and at exit

Status

Works today

  • Zero-instrumentation capture of LLM HTTP calls (OpenAI, Anthropic) over https, cleartext http, and h2c
  • gen_ai semantics: model, tokens, parameters, finish reasons, input/output messages
  • Transport metrics (TCP/TLS timing, TTFT), gRPC (grpclib), WebSocket (wss), MCP stdio
  • Export to any OpenTelemetry backend via OtlpHttpTransport
  • Manual span decorators: @workflow / @agent / @task / @tool / @span
  • PII masking on by default: emails, phone numbers, credit cards (Luhn-verified), US SSNs, IP addresses, bank routing numbers, IBANs, and API-key/token secrets are masked before anything leaves the process (pii_mode=PIIMode.OFF to disable, pii_disabled_categories={PIICategory.IP_ADDRESS} for per-category opt-out)
  • Background batching: automatic flush every 5s / on buffer threshold / at exit and on SIGINT/SIGTERM (chained; opt out with flush_on_signals=False)

Not yet (see Roadmap)

  • Framework adapters (LangGraph, Anthropic/OpenAI Agent SDKs)
  • Node/TS and Java SDKs

Notes

  • After os.fork() the worker respawns lazily in the child on first capture; spans buffered before the fork may be sent by both processes (duplicates are possible; a fork landing mid-export can also strand the child's pre-fork buffer — re-init in the child for a clean slate). Under uWSGI enable threads (--enable-threads).

Distributed tracing

Trace context propagation is opt-in — a plain wardex.init(...) never touches your outbound requests or headers. Turn it on with:

wardex.init(
    transport=OtlpHttpTransport(endpoint="https://<your-collector>/v1/traces"),
    intercept=True,
    propagate_trace=True,                       # inject W3C headers on outbound calls
    propagate_targets=["api.internal.example.com", "*.svc.cluster.local"],  # optional glob allowlist; default None = all hosts
)

With propagate_trace=True, outbound calls made through httpx (sync + async), requests, or aiohttp get a traceparent (and tracestate, if one was received) header attached automatically, as long as an active trace context exists and the request doesn't already carry a traceparent. If propagate_targets is left unset, the trace ID is sent to every host you call — including third-party LLM providers. Set it to an allowlist of glob patterns to scope injection to your own services.

Joining an inbound trace

Drop the middleware in front of your app to join whatever trace the caller started:

# ASGI (FastAPI, Starlette, Django ASGI)
app.add_middleware(wardex.WardexMiddleware)

# WSGI (Flask, Django WSGI)
app.wsgi_app = wardex.WardexWSGIMiddleware(app.wsgi_app)

Both extract the incoming traceparent/tracestate and continue the trace for the lifetime of the request; a missing or malformed header just starts a fresh trace (never raises). One WSGI caveat: the joined context covers the app callable only, so streaming responses (work done while iterating the returned iterable) run outside it.

Manual propagation (the universal escape hatch)

The baton is just a string, so it travels over any channel that can carry one — not just HTTP. get_traceparent() and get_trace_headers() are plain functions that return the current trace headers; continue_trace(headers) is a context manager — the remote parent is only installed inside the with block, so it must be entered, not merely called. Use them directly wherever the automatic client patches or ASGI/WSGI middleware don't reach:

# gRPC metadata
stub.Check(req, metadata=[("traceparent", wardex.get_traceparent())])

# WebSocket handshake
websockets.connect(uri, extra_headers=wardex.get_trace_headers())

# Celery: put get_trace_headers() on the task's headers when sending it,
# then inside the worker:
with wardex.continue_trace(task.request.headers):
    ...  # task body

# Kafka: put get_trace_headers() on the message headers when producing,
# then inside the consumer:
with wardex.continue_trace(dict(msg.headers())):
    ...  # process the message

with wardex.continue_from_otel(): is a one-line alternative to continue_trace() for code that already runs under an active OpenTelemetry span — it adopts that span as the remote parent for the duration of the with block (no-op if opentelemetry isn't installed or there's no active span). Like continue_trace(), it is a context manager and must be entered with with.

Propagating into threads

asyncio tasks inherit the current trace context automatically; threads do not. Wrap the target with wardex.run_in_context() at the point where you still have the right context:

thread = threading.Thread(target=wardex.run_in_context(worker_fn), args=(...,))
thread.start()

capture_mode: what gets captured without an active span

capture_mode defaults to "agent": LLM-semantic traffic (recognized gen_ai calls, MCP stdio) is always captured, but generic HTTP/gRPC/WS traffic is only captured while it happens inside an active local wardex span (a traceparent received from an upstream caller doesn't count on its own — this keeps a service mesh stamping every request with a traceparent from reviving the pre-Phase-4 "capture everything" noise).

This means a bare, unwrapped call to an LLM provider wardex doesn't recognize (or a WS-based provider such as OpenAI Realtime, which carries no parseable semantics) can be silently dropped if it isn't inside a local span. Wrap it with @wardex.workflow (or any of the span decorators), or set capture_mode=wardex.CaptureMode.ALL to restore the previous capture-everything behavior:

wardex.init(..., capture_mode=wardex.CaptureMode.ALL)

Plaintext hosts you've explicitly named via intercept_hosts are always captured regardless of capture_mode — a targeted allowlist entry is a stronger opt-in than the default policy.

Roadmap

  1. PII masking (pre-send safety) — shipped
  2. Batching & lifecycle (background worker, at-exit/periodic flush, concurrency) — shipped
  3. Distributed propagation (W3C) — shipped
  4. Framework adapters
  5. Node/TS and Java SDKs

PII masking caveats: before_send sees pre-masking data (masking runs inside the encoder), the Console transport prints raw (local debugging only), and non-UTF-8 binary payloads pass through unmasked.

License

Apache-2.0. See LICENSE and NOTICE.

"Wardex" is a trademark of Wardex Labs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

wardex_sdk-0.1.0b5-cp310-abi3-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.10+Windows x86-64

wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.9 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

wardex_sdk-0.1.0b5-cp310-abi3-macosx_11_0_arm64.whl (1.6 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

wardex_sdk-0.1.0b5-cp310-abi3-macosx_10_12_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file wardex_sdk-0.1.0b5-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for wardex_sdk-0.1.0b5-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 375c196a7dfc0761280551f98bcd7c8871adda7331f876b81528dcbbb42eca7a
MD5 7df510ab88ddd5c772503e643e1a91da
BLAKE2b-256 d88ed57ed90a950972d4c455da8098aaf2a6601a146a8a4af1ed3b30386181fe

See more details on using hashes here.

Provenance

The following attestation bundles were made for wardex_sdk-0.1.0b5-cp310-abi3-win_amd64.whl:

Publisher: release-python.yml on wardex-labs/wardex-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c13bc067066b482dcec1a0854e277ec3b063e81ec806feed1df2123a2baeb600
MD5 dd0fa71fa1ba44644a12a85bd5235cdc
BLAKE2b-256 754d9917e7a6a2b6b2375d3459d03e08579d1966256ccb35747b5ca0c830f973

See more details on using hashes here.

Provenance

The following attestation bundles were made for wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-python.yml on wardex-labs/wardex-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2811cefb66614140370f0bff252ecbf5be60ac09c4ceba5cf783b08448d4e2be
MD5 b041f3ab24b7c23aa0a78b33258a5f03
BLAKE2b-256 0bce0dc461af343d6b384f9701067a241062c03a21b4320c9580c2b21771c1e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for wardex_sdk-0.1.0b5-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release-python.yml on wardex-labs/wardex-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wardex_sdk-0.1.0b5-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for wardex_sdk-0.1.0b5-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a7333dfd796cd9e6ae4bcd5f476bffb419f2003428bfbe2003d3baed821829d9
MD5 36726f71f70e50e6f6f3290774078bbe
BLAKE2b-256 37b1ce37b48cdb8d1da275025f3c938877de2e5976765d6ac35f7d25ccf9c693

See more details on using hashes here.

Provenance

The following attestation bundles were made for wardex_sdk-0.1.0b5-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release-python.yml on wardex-labs/wardex-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wardex_sdk-0.1.0b5-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for wardex_sdk-0.1.0b5-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 0ab777bd4480f3ff102666aa93cdbf30c6ba19da0ba5d3a58cc629b498d2a9c4
MD5 d46136674e7a956bac5f67b1b6ecf1df
BLAKE2b-256 2bd6969d042ee90065e0815628ffb1bf71d028ef43676c0b510dcd180c9f4797

See more details on using hashes here.

Provenance

The following attestation bundles were made for wardex_sdk-0.1.0b5-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: release-python.yml on wardex-labs/wardex-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page