Official Python SDK for Aeonic — runtime monitoring and governance for AI agents
Project description
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.
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 aeonic_sdk_python-0.1.0.tar.gz.
File metadata
- Download URL: aeonic_sdk_python-0.1.0.tar.gz
- Upload date:
- Size: 38.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fed7e6cf5ea74ad3a99301b3627ae9f7cae81c0e5ba9dde0c744fa22d025d74f
|
|
| MD5 |
e6bfb09f20c9196793e260299d25f59f
|
|
| BLAKE2b-256 |
6c0cee834755fd4461dfb6736295412574406ed0130a82f24e9e94720a12e448
|
File details
Details for the file aeonic_sdk_python-0.1.0-py3-none-any.whl.
File metadata
- Download URL: aeonic_sdk_python-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50e134e288d7353cb8111270b580ba386b3fa4742b63dce2f8ae3ee0df8609cf
|
|
| MD5 |
ffbb5d2c499b00f18a8cae9f3e5be725
|
|
| BLAKE2b-256 |
9a8d32ce037422aa575aec715d779ee587360f1ba82ce992cec7999798ea66c0
|