Aeonic Python SDK
Aeonic Python SDK enables runtime monitoring, drift detection, and governance for AI agents running inside Python applications.
It works by observing agent-powered routes at runtime, capturing request/response samples, and securely sending them to the Aeonic platform for analysis.
The SDK is:
- Framework-aware
- Declarative
- Non-intrusive
- Production-safe
✨ Supported Frameworks
- FastAPI
- Django
Only routes explicitly marked as agent routes are tracked.
All other routes are completely ignored by the SDK.
📦 Installation
Install the base SDK (core only — requests is included):
pip install aeonic-sdk-python
Install with your framework (recommended):
# FastAPI
pip install aeonic-sdk-python[fastapi]
# Django
pip install aeonic-sdk-python[django]
Note: The PyPI package name is
aeonic-sdk-python, but you import it asaeonic.
Check the installed version:
import aeonic
print(aeonic.__version__)
📋 Requirements
| Requirement | Details |
|---|---|
| Python | >=3.9 |
| Core dependency | requests>=2.25.0 (installed automatically) |
| FastAPI (optional) | fastapi>=0.68.0 — install via [fastapi] extra |
| Django (optional) | django>=3.2.0 — install via [django] extra |
Environment variables (optional)
| Variable | Purpose |
|---|---|
AEONIC_ENDPOINT |
Aeonic platform endpoint URL (provided in your Aeonic account or onboarding) |
AEONIC_DEBUG |
Set to any value to enable debug logging |
ORIGIN / BASE_URL |
Your application's public URL, sent with sample payloads |
PORT / HOST |
Used to auto-detect your app origin when ORIGIN is not set |
🚀 Quick Start (FastAPI)
from fastapi import Depends, FastAPI
from aeonic import init
from aeonic.adapters.fastapi import agent_middleware, with_agent
app = FastAPI()
app.add_middleware(agent_middleware)
init({
"api_key": "your_api_key",
"app": app,
"service_name": "my-service",
})
@app.post(
"/agent/chat",
dependencies=[Depends(with_agent({"name": "ChatAgent", "type": "support", "model": ["gpt-4o"]}))],
)
async def chat_agent(payload: dict):
return {"reply": "Hello!"}
Run with:
uvicorn main:app --reload
🔑 SDK Initialization (Required)
Initialize the SDK once at application startup.
Configuration Options
init({
"api_key": str, # REQUIRED
"app": Any | None, # Optional: FastAPI/Django app for route introspection
"service_name": str | None, # Optional (recommended)
"introspection_delay_ms": int, # Optional: Delay before route scan (default: 2000ms)
"capture_payloads": {
"max_samples": int # Default: 10
}
})
Example (Basic)
from aeonic import init
init({
"api_key": "ag_test_123",
"service_name": "payments-api",
"capture_payloads": {
"max_samples": 10
}
})
Example (With Route Introspection) ⭐ NEW
from aeonic import init
from fastapi import FastAPI
app = FastAPI()
# Pass app instance for automatic route discovery
init({
"api_key": "ag_test_123",
"app": app, # ← Enables automatic agent discovery at startup
"service_name": "payments-api",
"introspection_delay_ms": 2000, # Wait 2s for routes to be defined
"capture_payloads": {
"max_samples": 10
}
})
What this does
- Authenticates your service with Aeonic
- Automatically resolves tenant identity via
api_key - Sets payload sampling limits per agent route
- NEW: Discovers and registers all agent routes at startup (if
appis provided) - NEW: Emits complete agent inventory to the Aeonic platform before serving traffic
📖 Route introspection: Pass your
appinstance toinit()so the SDK can discover agent routes automatically at startup.
🚀 FastAPI Integration
Aeonic uses two components in FastAPI:
- A global middleware (registered once)
- A route-level dependency to mark agent routes
1️⃣ Register Global Middleware
from fastapi import FastAPI
from aeonic.adapters.fastapi import agent_middleware
app = FastAPI()
# 🔴 MUST be registered once
app.add_middleware(agent_middleware)
This middleware:
- Hooks into the response lifecycle
- Does nothing unless the route is marked as an agent route
2️⃣ Mark Agent Routes (Required)
Only routes using with_agent() are tracked.
from fastapi import Depends
from aeonic.adapters.fastapi import with_agent
@app.post(
"/agent/finance",
dependencies=[
Depends(
with_agent({
"name": "RefundRiskAgent",
"type": "finance",
"model": ["gpt-4o"]
})
)
]
)
async def finance_agent(payload: dict):
return {
"approved": True,
"amount": payload["amount"]
}
What happens internally
- Request + response payloads are captured
- Samples are buffered per agent route
- Once
max_samplesis reached, data is sent to the Aeonic platform - Agent health status is evaluated (healthy / warning / drift)
✅ FastAPI Summary
| Feature | Behavior |
|---|---|
| Only marked routes tracked | ✅ |
| Async safe | ✅ |
| Middleware-based | ✅ |
| No contextvars hacks | ✅ |
| Production ready | ✅ |
🏛️ Django Integration
Django uses:
- A global middleware
- A route decorator to mark agent routes
1️⃣ Register Middleware
settings.py
MIDDLEWARE = [
...
"aeonic.adapters.django.agent_middleware",
]
Ensure
init()is called during startup (e.g., insettings.pyorwsgi.py).
2️⃣ Mark Agent Routes with Decorator
from django.http import JsonResponse
from aeonic.adapters.django import with_agent
@with_agent({
"name": "RefundRiskAgent",
"type": "finance",
"model": ["gpt-4o"]
})
def finance_agent(request):
return JsonResponse({
"approved": True,
"amount": 500
})
✅ Django Summary
| Feature | Behavior |
|---|---|
| Declarative agent marking | ✅ |
| Middleware-based capture | ✅ |
| Sync-safe | ✅ |
| No framework coupling | ✅ |
| Production ready | ✅ |
🧠 Agent Concepts
Agent Identity
Each agent route is identified by:
name– logical agent nametype– business domain (finance, healthcare, etc.)model– AI models used
Route → Agent Mapping
Aeonic correlates:
HTTP Route + Method + Agent Metadata
This enables:
- Drift detection
- Agent behavior consistency checks
- Health classification per route
🔐 Security & Privacy
- Payloads are sampled, not streamed
- Only up to
max_samplesare collected per flush - SDK never blocks or alters application behavior
- Non-agent routes are never inspected
📊 Agent Status Lifecycle
The Aeonic platform classifies agents as:
- Healthy
- Warning
- Drifting
- Failed
Status updates are visible in the Aeonic dashboard.
⚠️ Important Rules
- ❌ Do NOT wrap business logic in SDK functions
- ❌ Do NOT rely on thread-local or contextvar hacks
- ✅ Always mark agent routes explicitly
- ✅ Initialize SDK before app starts
🧩 Advanced Use Cases
- Background workers
- Batch inference jobs
- Non-HTTP agents
(Contact the Aeonic team for advanced SDK APIs.)
🔧 Troubleshooting
RuntimeError: Aeonic SDK not initialized
Call init({...}) once at application startup before any SDK features are used.
Samples are not being captured
- Ensure the route is marked with
with_agent()(FastAPI dependency or Django decorator). - Ensure global middleware is registered (
agent_middleware). - Confirm the agent is not
blockedorquarantined(check Aeonic dashboard). - Set
AEONIC_DEBUG=1to see SDK debug output.
ModuleNotFoundError: No module named 'fastapi' or 'django'
Install the matching framework extra:
pip install aeonic-sdk-python[fastapi]
# or
pip install aeonic-sdk-python[django]
Agent inventory not emitted at startup
- For FastAPI, pass
"app": apptoinit(). - For Django, ensure
init()runs during startup and routes are registered before introspection completes. - Increase
introspection_delay_msif routes are registered late (e.g.3000or5000).
Platform connection issues
Set your Aeonic endpoint URL (provided during onboarding):
export AEONIC_ENDPOINT=https://your-aeonic-endpoint
On Windows PowerShell:
$env:AEONIC_ENDPOINT="https://your-aeonic-endpoint"
📄 License
MIT © Aeonic
📚 SDK Function Reference
This section documents the public SDK functions and middleware for integrating Aeonic into your application.
aeonic.init(config: dict)
Purpose
Initializes the Aeonic SDK once at application startup, validates and normalizes configuration, kicks off background route introspection, and (optionally) registers the process as an AgentGuard instance.
Arguments
config(dict) – configuration object with the following keys:api_key(str, required): Your Aeonic API key. If missing,initraisesValueError.app(FastAPI | Django | None, optional): Application instance used for automatic route introspection and agent discovery at startup. Required for FastAPI introspection; optional for Django (Django can be introspected withoutapp).service_name(str | None, optional): Logical name for the service (e.g."payments-api"). Propagated to all emitted events and inventories.capture_payloads(dict, optional): Controls how request/response payload samples are collected.max_samples(int, optional): Number of samples to buffer per agent route before flushing. Defaults to10when omitted.
introspection_delay_ms(int, optional): Delay (in milliseconds) before route introspection runs. Defaults to2000ms, giving the application time to register all routes.agentguard_enabled(bool, optional): WhenTrue(default), the SDK registers itself as an AgentGuard instance in a background thread. WhenFalse, this registration step is skipped, but sample collection still works.
Behavior
- Stores a normalized configuration internally containing:
api_key,service_name,max_samples,app,introspection_delay_ms,agentguard_enabled.
- Logs a startup message (
"[Aeonic] SDK initialized.") using theaeoniclogger. - If
agentguard_enabledisTrue, registers your service with Aeonic in a background daemon thread:- Failures are logged as warnings but never raised; the SDK continues collecting samples even if registration fails.
- Schedules deferred route introspection in another background daemon thread:
- Waits
introspection_delay_msbefore running. - For FastAPI: requires
config["app"]and callsintrospect_routes(app). - For Django: if
appisNonebut Django is installed, callsintrospect_routes(None), which uses Django’s URL resolver. - On success, emits an agent inventory event with all registered agents (name, type, model, route, method, framework, timestamps).
- On any error, logs the traceback and falls back to emitting inventory with whatever agents were registered manually.
- Waits
- If introspection is skipped (no
appand Django not installed), calls a fallback that attempts to emit inventory once agents exist. - Never blocks the main thread; all network calls and introspection work are done in background daemon threads.
Typical usage
from aeonic import init
from fastapi import FastAPI
app = FastAPI()
init({
"api_key": "ag_test_123",
"app": app,
"service_name": "payments-api",
"introspection_delay_ms": 2000,
"capture_payloads": {
"max_samples": 10,
},
# Optional – disable AgentGuard registration if needed:
# "agentguard_enabled": False,
})
Accessing effective config (advanced)
For debugging or advanced inspection, you can read the effective configuration:
from aeonic.core import get_config
cfg = get_config() # raises RuntimeError if init() was not called
aeonic.adapters.fastapi.agent_middleware
Purpose
FastAPI-compatible middleware (BaseHTTPMiddleware) that observes responses and captures samples only for requests that have been marked as agent routes via with_agent.
How to register
from fastapi import FastAPI
from aeonic.adapters.fastapi import agent_middleware
app = FastAPI()
app.add_middleware(agent_middleware)
Request/response handling
- For each request:
- Calls the next handler (
call_next(request)) first to let your business logic run. - Reads
request.state.aeonic:- If missing or
is_agentis falsy, returns the response immediately (non-agent route).
- If missing or
- Skips assessment traffic:
- If header
x-aeonic-assessment: trueis present, no samples are collected. - When the
AEONIC_DEBUGenvironment variable is set, the middleware logs a debug message and includes optionalx-aeonic-job-idfor traceability.
- If header
- Skips blocked/quarantined agents:
- Uses
should_collect_samples(agent_key)fromaeonic.core.agent_status_cache. agent_keyisagent["name"]when available, otherwise"<METHOD>:<PATH>".- When
AEONIC_DEBUGis set and an agent is blocked, a message is printed for easier debugging.
- Uses
- Calls the next handler (
Payload capture
- Request payload:
- Read from
request.state.aeonic["req_payload"], which is set by thewith_agentdependency (to avoid re-consuming the body). - Serialized via
safe_serializeto guarantee JSON-safe structures.
- Read from
- Response payload:
- Attempts to read
response.bodydirectly when available (typical for JSON responses). - If only a
body_iteratorexists (streaming responses), it:- Iterates over the body iterator, collects all chunks, and rebuilds a new
Responsewith the same status, headers, and media type.
- Iterates over the body iterator, collects all chunks, and rebuilds a new
- Checks
Content-Typeheader:- If it contains
"json"and the body is non-empty, attemptsjson.loads()and thensafe_serializethe result. - Any parsing failures are silently ignored (SDK never breaks your response).
- If it contains
- Attempts to read
Sample creation and buffering
- Builds a sample dictionary:
{"req": req_payload, "res": res_payload, "timestamp": int(time()), "status_code": status_code}.
- Reads SDK config via
get_config():- Uses
config["max_samples"],config["api_key"], andconfig["service_name"].
- Uses
- If the status code is 2xx:
- Resolves or creates a per-agent
SampleBufferin an in-memory_BUFFERSmap, keyed byagent_key. - Caps the total number of in-memory buffers at
100agents; when exceeded, the oldest key is evicted. - Calls
buffer.add(sample):- When the buffer is full (
max_samples),buffer.flush()is called and the payload is enqueued viaenqueue(...)for asynchronous delivery to Aeonic.
- When the buffer is full (
- Resolves or creates a per-agent
- If the status code is non-2xx:
- Immediately calls
emit_error_sample(...)with a single-sample payload.
- Immediately calls
Key properties
- Only applies to routes where
with_agenthas attached metadata. - Never throws or mutates your response in a way that changes semantics; at worst it falls back to no-op.
- Designed to be safe in high-throughput, async production environments.
aeonic.adapters.fastapi.with_agent(agent: AgentContext)
Purpose
FastAPI dependency factory that marks an endpoint as an agent route and attaches agent metadata and request payloads to the request.state.aeonic object.
Agent context (AgentContext)
AgentContext is a dict-like structure with keys such as:
name(str, recommended): Logical agent name, used as the primary key for statistics and status.type(str | None): Business/domain category (e.g."finance","support","claims").model(str | list[str] | None): Model identifier(s) powering the agent (e.g."gpt-4o").- Additional custom keys are supported and propagated as metadata.
Declaration-time behavior
- When you call
with_agent(agent)at import time:- Immediately calls
_register_agent_declaration(agent, framework="fastapi"), which in turn:- Resolves the current Aeonic config (if initialized) to obtain
service_name. - Registers the agent in the global agent registry with:
name,type,model,framework="fastapi", andservice_name.route_path=Noneandhttp_method=Noneinitially (filled in later by route introspection).
- Resolves the current Aeonic config (if initialized) to obtain
- Stores a copy of the agent metadata (with
"source": "manual") in an internal map keyed by the dependency function’s ID. - Also attaches metadata to the dependency function as
__aeonic_metadata__for robust detection, even when FastAPI wraps or decorates dependencies.
- Immediately calls
Request-time behavior
with_agent(agent)returns an async dependency callable compatible withDepends:
from fastapi import Depends
from aeonic.adapters.fastapi import with_agent
@app.post(
"/agent/finance",
dependencies=[Depends(with_agent({"name": "RefundRiskAgent", "type": "finance", "model": ["gpt-4o"]}))],
)
async def finance_agent(payload: dict):
...
- On each request, the dependency:
- Attempts to parse
await request.json():- If parsing succeeds,
bodyis serialized withsafe_serialize. - On any error (non-JSON, invalid body, etc.),
bodyis set toNone.
- If parsing succeeds,
- Writes to
request.state.aeonic:{"is_agent": True, "agent": {**agent, "source": "manual"}, "req_payload": body}.
- Attempts to parse
- This
request.state.aeonicobject is later consumed byagent_middlewareto decide whether and how to capture samples.
Introspection helper: get_agent_metadata(func) (advanced)
aeonic.adapters.fastapi.get_agent_metadata(func):- Attempts to recover an
AgentContextfrom:- Direct ID lookup in the metadata map.
__aeonic_metadata__attribute on the function or wrapped function.- Closure variables containing the agent context.
- Underlying
.callattribute (for FastAPI dependency objects).
- Used internally by the Aeonic introspection system to match FastAPI routes and dependencies to their agents.
- Attempts to recover an
- Typical SDK consumers do not need to call this directly; it is useful only for advanced tooling or framework integrations built on top of Aeonic.
aeonic.adapters.django.agent_middleware
Purpose
Global Django middleware class that observes responses and captures samples only for views that have been marked as agent routes via the with_agent decorator.
How to register
# settings.py
MIDDLEWARE = [
...
"aeonic.adapters.django.agent_middleware",
]
Request/response handling
- Constructed once per process with
get_response. - For each request:
- Calls
response = get_response(request)to run your Django view and middleware stack. - Reads
request.aeonic:- If missing or
is_agentis falsy, returns the response immediately (non-agent route).
- If missing or
- Skips assessment traffic:
- Uses
request.META["HTTP_X_AEONIC_ASSESSMENT"]to detect the headerX-Aeonic-Assessment: true. - When
AEONIC_DEBUGis set, logs a message including optionalHTTP_X_AEONIC_JOB_ID.
- Uses
- Skips blocked/quarantined agents:
- Uses
should_collect_samples(agent_key)withagent_key = agent["name"]or"<METHOD>:<PATH>". - When
AEONIC_DEBUGis set and an agent is blocked, logs a message.
- Uses
- Calls
Payload capture
- Request payload:
- Read from
request.aeonic["req_payload"], which is set by thewith_agentdecorator. - Serialized via
safe_serialize.
- Read from
- Response payload:
- Checks
response["Content-Type"]header. - If it contains
"json", attempts:- Decode
response.contentas UTF‑8. - Parse JSON and then
safe_serializethe result.
- Decode
- Any parsing or decoding error is swallowed; the SDK never breaks your response pipeline.
- Checks
Sample creation and buffering
- Builds a sample dictionary identical to the FastAPI middleware:
{"req": req_payload, "res": res_payload, "timestamp": int(time()), "status_code": status_code}.
- Reads SDK config via
get_config()and usesmax_samples,api_key, andservice_name. - For 2xx responses:
- Uses a per-agent in-memory
SampleBufferin_BUFFERS, capped at100distinct agent keys with eviction of the oldest entry when full. - When a buffer fills up (
max_samples), callsbuffer.flush()and enqueues the batch viaenqueue(...).
- Uses a per-agent in-memory
- For non-2xx responses:
- Calls
emit_error_sample(...)immediately with a single sample.
- Calls
aeonic.adapters.django.with_agent(agent: AgentContext)
Purpose
Decorator that marks a Django view as an agent route and attaches agent metadata and payloads to request.aeonic.
Declaration-time behavior
- When the decorator is applied:
- Calls
_register_agent_declaration(agent, framework="django"):- Registers the agent in the global registry with
name,type,model,framework="django",service_name, androute_path/http_methodinitially set toNone.
- Registers the agent in the global registry with
- Wraps the original view function with
functools.wraps. - Stores a copy of the agent context (with
"source": "manual") in an internal map keyed by the wrapped view’s ID. - Attaches metadata to the wrapped view as
__aeonic_metadata__for easier introspection later.
- Calls
Request-time behavior
- The wrapped view:
- Tries to build
req_payload:- For
POST,PUT,PATCH:- Reads
CONTENT_TYPEfromrequest.META. - If it contains
"json"andrequest.bodyis non-empty:- Decodes body as UTF‑8, parses JSON, and
safe_serializes the result.
- Decodes body as UTF‑8, parses JSON, and
- If body exists but is not JSON:
- Decodes as UTF‑8 string and serializes that.
- Reads
- For
GET:- If
request.GEThas query parameters, serializesdict(request.GET). - If no query params, sets
req_payloadto an empty dict{}.
- If
- On any exception,
req_payloadfalls back toNone.
- For
- Sets:
request.aeonic = {"is_agent": True, "agent": {**agent, "source": "manual"}, "req_payload": req_payload}.
- Calls and returns the original view function.
- Tries to build
Introspection helper: get_agent_metadata(func) (advanced)
aeonic.adapters.django.get_agent_metadata(func):- First checks the internal metadata map by ID.
- Then falls back to the
__aeonic_metadata__attribute if present. - Used by internal route introspection to match Django URL patterns to their agents.
- Typical SDK users do not need to call this directly, but it is useful for custom tooling.
Summary of Public Entry Points
- Initialization
aeonic.init(config: dict)– one-time SDK setup, background introspection, and optional AgentGuard registration.
- FastAPI
aeonic.adapters.fastapi.agent_middleware– global middleware for observing and sampling agent routes.aeonic.adapters.fastapi.with_agent(agent: AgentContext)– dependency factory to mark FastAPI endpoints as agent routes and capture request bodies.
- Django
aeonic.adapters.django.agent_middleware– middleware class for global observation and sampling of agent views.aeonic.adapters.django.with_agent(agent: AgentContext)– decorator to mark Django views as agent routes and capture request data.
- Advanced (optional)
aeonic.core.get_config()– read the effective runtime SDK configuration.aeonic.adapters.fastapi.get_agent_metadata(func)/aeonic.adapters.django.get_agent_metadata(func)– used by introspection to map framework objects back to agent metadata.
You can map each function/middleware above to your integration: use the descriptions as guidance and the code snippets as copy-paste examples.
Metadata
Release files for aeonic-sdk-python 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| aeonic_sdk_python-0.1.0.tar.gz | 38.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aeonic_sdk_python-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.4 kB
Release files / aeonic_sdk_python-0.1.0.tar.gz
| Download URL | aeonic_sdk_python-0.1.0.tar.gz |
|---|---|
| Size | 38.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fed7e6cf5ea74ad3a99301b3627ae9f7cae81c0e5ba9dde0c744fa22d025d74f
|
|
BLAKE2b-256 checksum How to use checksums |
6c0cee834755fd4461dfb6736295412574406ed0130a82f24e9e94720a12e448
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.2
|
Release files / aeonic_sdk_python-0.1.0-py3-none-any.whl
| Download URL | aeonic_sdk_python-0.1.0-py3-none-any.whl |
|---|---|
| Size | 38.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
50e134e288d7353cb8111270b580ba386b3fa4742b63dce2f8ae3ee0df8609cf
|
|
BLAKE2b-256 checksum How to use checksums |
9a8d32ce037422aa575aec715d779ee587360f1ba82ce992cec7999798ea66c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.2
|
Security for aeonic-sdk-python 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.