Tracentic SDK for Python — LLM observability with scoped tracing and OTLP export
Project description
Tracentic Python SDK
LLM observability with scoped tracing and OTLP export for Python applications.
Installation
pip install tracentic
Requires Python 3.10+. The only runtime dependency is httpx.
Endpoint
Point the SDK at the Tracentic ingestion endpoint by setting endpoint="https://tracentic.dev" on TracenticOptions. This is the hosted service URL that receives spans over OTLP/HTTP JSON — use it unless you're running a self-hosted Tracentic deployment, in which case set your own URL.
tracentic = create_tracentic(TracenticOptions(
api_key="your-api-key",
endpoint="https://tracentic.dev",
service_name="my-service",
))
Quick start
import asyncio
from datetime import datetime, timezone
from tracentic import TracenticOptions, TracenticSpan, create_tracentic
tracentic = create_tracentic(TracenticOptions(
api_key="your-api-key",
endpoint="https://tracentic.dev",
service_name="my-service",
environment="production",
))
async def summarize(text: str) -> str:
scope = tracentic.begin("summarize", attributes={"user_id": "user-123"})
started_at = datetime.now(timezone.utc)
result = await call_llm(text)
ended_at = datetime.now(timezone.utc)
tracentic.record_span(scope, TracenticSpan(
started_at=started_at,
ended_at=ended_at,
provider="anthropic",
model="claude-sonnet-4-20250514",
input_tokens=result.usage.input_tokens,
output_tokens=result.usage.output_tokens,
operation_type="chat",
))
return result.text
Singleton pattern
If you prefer a global instance:
from tracentic import configure, get_tracentic
# At startup
configure(TracenticOptions(api_key="...", service_name="my-service"))
# Anywhere else
tracentic = get_tracentic()
Features
Scoped tracing
Group related LLM calls under a logical scope. Nest scopes for multi-step pipelines:
pipeline = tracentic.begin("rag-pipeline", correlation_id="order-42")
# Child scope inherits the parent link automatically
synthesis = pipeline.create_child("synthesis", attributes={"strategy": "hybrid"})
Error recording
tracentic.record_error(scope, span, RuntimeError("rate limited"))
Scopeless spans
For standalone LLM calls that don't belong to a larger operation:
tracentic.record_span(TracenticSpan(
started_at=started_at,
ended_at=ended_at,
provider="openai",
model="gpt-4o-mini",
input_tokens=200,
output_tokens=50,
operation_type="chat",
))
Custom pricing
from tracentic import ModelPricing
tracentic = create_tracentic(TracenticOptions(
api_key="...",
custom_pricing={
"claude-sonnet-4-20250514": ModelPricing(3.0, 15.0),
"gpt-4o": ModelPricing(2.5, 10.0),
},
))
Cost is calculated automatically when a matching pricing entry exists and both token counts are present.
Global attributes
Static attributes applied to every span:
tracentic = create_tracentic(TracenticOptions(
api_key="...",
global_attributes={
"region": "us-east-1",
"version": "2.1.0",
},
))
Dynamic attributes can be set/removed at runtime:
from tracentic import TracenticGlobalContext
TracenticGlobalContext.current.set("deploy_id", "deploy-abc")
TracenticGlobalContext.current.remove("deploy_id")
ASGI middleware
Inject per-request attributes for the duration of each HTTP request. Works with FastAPI, Starlette, and any ASGI framework:
from tracentic.middleware.asgi import TracenticMiddleware
app = TracenticMiddleware(
app,
request_attributes=lambda scope: {
"method": scope.get("method"),
"path": scope.get("path"),
},
)
Cross-service linking
Tracentic does not propagate scope IDs automatically — you pass them explicitly through whatever transport connects your services (HTTP headers, message properties, etc.).
For cross-service linking to work, both services must integrate the Tracentic SDK (or implement the OTLP JSON ingest API directly) and their API keys must belong to the same tenant. Spans from different tenants are isolated and cannot be linked.
Via HTTP header:
# Service A — outgoing request
scope = tracentic.begin("gateway-handler")
response = await httpx.post(
"https://worker.internal/process",
headers={"x-tracentic-scope-id": scope.id},
)
# Service B — incoming request (FastAPI example)
@app.post("/process")
async def process(request: Request):
parent_scope_id = request.headers.get("x-tracentic-scope-id")
linked = tracentic.begin("worker", parent_scope_id=parent_scope_id)
Via message queue:
# Producer
scope = tracentic.begin("order-processor")
await queue.send(
body=payload,
properties={"tracentic-scope-id": scope.id},
)
# Consumer
async def handle(message):
parent_scope_id = message.properties["tracentic-scope-id"]
linked = tracentic.begin("fulfillment", parent_scope_id=parent_scope_id)
Shutdown
Flush buffered spans before process exit:
await tracentic.shutdown()
Configuration reference
| Option | Default | Description |
|---|---|---|
api_key |
None |
API key. If None, spans are created locally but not exported |
service_name |
"unknown-service" |
Service identifier in the dashboard |
endpoint |
"https://tracentic.dev" |
Tracentic ingestion endpoint. Use https://tracentic.dev for the hosted service. Override only for self-hosted deployments. |
environment |
"production" |
Deployment environment tag |
custom_pricing |
None |
Model pricing for cost calculation |
global_attributes |
None |
Static attributes on every span |
attribute_limits |
platform defaults | Limits on attribute count, key/value length |
Development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Running tests
# All tests
pytest
# Verbose output
pytest -v
# A single test file
pytest tests/test_scope.py
# A single test
pytest tests/test_scope.py::TestTracenticScope::test_create_child_sets_parent_id
Test files
| File | What it covers |
|---|---|
test_tracentic.py |
SDK factory, singleton, begin/record_span/record_error, cost calculation |
test_scope.py |
Scope creation, nesting, defensive copying, unique IDs |
test_global_context.py |
Global context set/get/remove, singleton access, snapshots |
test_attribute_merger.py |
Three-layer merge priority, key/value truncation, count cap |
test_options.py |
AttributeLimits defaults, clamping, platform constants |
test_exporter.py |
OTLP JSON structure, endpoint, headers, overflow, error handling |
Linting and type checking
ruff check src/ tests/
mypy src/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tracentic-0.1.0.tar.gz.
File metadata
- Download URL: tracentic-0.1.0.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ac2fae77cc9ef14ba9aee299ad14cadda74c6add356769cb397fe2f3915214c
|
|
| MD5 |
c25c4769d66f665d1e8f1b3ee75ce639
|
|
| BLAKE2b-256 |
ebe76207f5c8b58d74fb2a6516b5d7b0acdf6103889bed138f887aff5bbce275
|
Provenance
The following attestation bundles were made for tracentic-0.1.0.tar.gz:
Publisher:
publish.yml on tracentic/tracentic-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tracentic-0.1.0.tar.gz -
Subject digest:
8ac2fae77cc9ef14ba9aee299ad14cadda74c6add356769cb397fe2f3915214c - Sigstore transparency entry: 1311322884
- Sigstore integration time:
-
Permalink:
tracentic/tracentic-python@281deb716f69d30cbbf5405abe9db969dc181024 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tracentic
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@281deb716f69d30cbbf5405abe9db969dc181024 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tracentic-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tracentic-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5337c9fce0fc0acca669068b8bd7582915196a232b86eff841050c4c5fad4e55
|
|
| MD5 |
dd608b43cf7308a63658d8f49c13b51c
|
|
| BLAKE2b-256 |
bf7770a4ca760dc990a9b8b790ed1571298f2e1c52564360bb04f7767ec667f5
|
Provenance
The following attestation bundles were made for tracentic-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on tracentic/tracentic-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tracentic-0.1.0-py3-none-any.whl -
Subject digest:
5337c9fce0fc0acca669068b8bd7582915196a232b86eff841050c4c5fad4e55 - Sigstore transparency entry: 1311322952
- Sigstore integration time:
-
Permalink:
tracentic/tracentic-python@281deb716f69d30cbbf5405abe9db969dc181024 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tracentic
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@281deb716f69d30cbbf5405abe9db969dc181024 -
Trigger Event:
push
-
Statement type: