This release is a pre-release and may not be stable for production use.
acceldata-aio-tracer
The Acceldata AIO instrumentation SDK. OpenTelemetry-native: it does not replace your instrumentation, it completes it.
Installation
pip install acceldata-aio-tracer
Requires Python 3.10 or newer. FastAPI and httpx instrumentation ship as an optional extra:
pip install 'acceldata-aio-tracer[http]'
Quickstart
import acceldata_aio_tracer as aio
aio.init(
service="my-app",
tenant_id="...",
project_id="...",
endpoint="https://aio.example.com",
access_key="...",
secret_key="...",
)
with aio.root_span(conversation_id=conversation_id, input_preview=user_message):
... # model calls, tools, MCP, outbound HTTP
That is the whole integration. init() runs once at startup, before you build
any client; root_span() wraps each unit of work.
tenant_id must be nonblank. It is a claim rather than proof: the Collector
checks it against the identity its gateway authenticated, and overwrites it
from there.
Endpoints
endpoint is the AIO server. Telemetry goes to that same host unless you say
otherwise, so a second endpoint is only needed when the Collector lives
somewhere else:
aio.init(
...,
endpoint="https://aio.example.com",
aio_collector_endpoint="https://collector.example.com",
)
Both fall back to the environment when the argument is omitted —
AIO_ENDPOINT and AIO_COLLECTOR_ENDPOINT — and the standard
OTEL_EXPORTER_OTLP_ENDPOINT is honoured for the Collector as well.
Selecting a project
Pass exactly one selector. When the UUID is known, project_id is used
unchanged and the SDK makes no request to the server.
Otherwise pass the name and let the SDK resolve it:
aio.init(
service="my-app",
tenant_id="...",
project_name="customer-facing-agent",
endpoint="https://aio.example.com",
access_key="...",
secret_key="...",
)
The name is matched exactly, and a project that does not exist is created with
only {"name": project_name}. Two processes racing to create the same name is
safe: the loser adopts the winner's project. Whether a project is one you may
write to — its status, its type — is the server's decision, so the SDK does not
second-guess it.
When a name cannot be resolved, whether from a failed lookup, a refused create,
or a malformed or ambiguous response, init() returns False and the
application runs without telemetry.
What it does
Gives a turn a root. Chat transports like socket.io have no server span, so
without a root the model call, each tool call, each MCP handshake and every
outbound HTTP request start their own disconnected trace. root_span() opens
one span they all nest under, so a user's question is one trace.
Puts the conversation id on every span. Instrumentation libraries tag the spans they own and leave the rest bare. Measured on one application, 2 of 8 instrumentation scopes carried the id, and the untagged spans held over a million tokens — so per-conversation totals were wrong by 15%. This SDK holds the id for the duration of the turn and stamps it on every span started inside, whichever library produced it. An id a library set for itself does not survive that: the open conversation is the one that reaches ingest.
Carries the user's message even when the turn fails. The preview is set when the turn opens, not when it completes, so a request that dies before the first model call still shows what was asked.
What to wrap
root_span() marks a unit of work the application knows the boundaries of and
instrumentation cannot infer. A chat turn is one. So is a workflow run:
with aio.root_span(conversation_id=f"{instance_id}:{session_id}", name="workflow_run"):
...
Call it once per unit of work, not once per span — it is not a replacement for
tracer.start_as_current_span(). name becomes a low-cardinality column, so
keep it a fixed label and never interpolate an id into it.
Pass user_id when a turn belongs to a particular person. It rides every span
in the unit of work alongside the conversation id, so usage and cost can be
attributed per user.
Instrumentation
Instrumentation for models, agents and MCP ships inside the package: init()
activates whichever of those libraries is installed in your environment and
skips the rest. The FastAPI and httpx layers are the one optional extra (see
Installation), and the SDK works with none of them — you still
get the root span and identity on whatever your application emits itself.
Call init() as early as possible in your process. Coverage for a client built
beforehand depends on how that library's instrumentation attaches: most patch a
shared class method and pick up existing instances on their next call, while a
few — the FastAPI middleware in the [http] extra among them — attach at
construction and only see clients built afterward. Calling init() first is
always safe and never the wrong choice.
If your application already configures OpenTelemetry, the SDK adds itself to the existing provider rather than replacing it, and warns if that provider's resource carries no tenant.
Stability
Telemetry never breaks the application: every failure path is swallowed, and
init() returns False rather than raising when it cannot start.
service, tenant_id, project_id and endpoint will not be renamed.
project_name and aio_collector_endpoint are keyword-only, as are the other
optional parameters; always pass them by name.
Upgrading to 1.0.0.dev2. endpoint used to mean the OTLP Collector and now
means the AIO server. Where the two differ, move the old value to
aio_collector_endpoint; otherwise project resolution is pointed at the
Collector.
Troubleshooting
init() returned False and nothing is showing up. It needs a nonblank
tenant_id and exactly one of project_id or project_name — passing both, or
neither, is refused. The reason is logged at warning level rather than emitting
telemetry under the wrong tenant.
Spans are missing tokens, tool calls, or model attributes. The client was
possibly constructed before init() ran — see Instrumentation
for which integrations that affects. Calling init() before constructing any
client sidesteps the question entirely.
Calling init() from more than one place. It is idempotent — the second and
later calls are no-ops that return True immediately — so it is safe to call
from more than one entry point (a test fixture and the application, for example)
without re-registering instrumentation.
License
Apache-2.0. The instrumentation layer is vendored from
OpenLIT; UPSTREAM.md, included in the
package, records exactly what was changed and why, per the license's
attribution requirements.
Release files for acceldata-aio-tracer 1.0.0.dev2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| acceldata_aio_tracer-1.0.0.dev2.tar.gz | 490.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| acceldata_aio_tracer-1.0.0.dev2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.2 MB
Release files / acceldata_aio_tracer-1.0.0.dev2.tar.gz
| Download URL | acceldata_aio_tracer-1.0.0.dev2.tar.gz |
|---|---|
| Size | 490.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dac2e399935c26fabee665a387339ca7a8c629b7e20c158d881ab8855669411b
|
|
BLAKE2b-256 checksum How to use checksums |
463d0f16765a6de3a1391cd89781d36bfa56fe481f00341a8b34e41e19b980f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.5 Darwin/25.5.0
|
Release files / acceldata_aio_tracer-1.0.0.dev2-py3-none-any.whl
| Download URL | acceldata_aio_tracer-1.0.0.dev2-py3-none-any.whl |
|---|---|
| Size | 671.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cf8b549428449734eb5eb5854563055dfe74fab7cb37f02012ca8540c1d8ca33
|
|
BLAKE2b-256 checksum How to use checksums |
a9e1330f353637f90d451bfe29ad2ea4dd2a7e22005b9b09ab481b988d8b73e0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.5 Darwin/25.5.0
|