Instrument AI agents in one line. Cost, value, and SLO tracking, automatically.
Project description
Avenza Python SDK
Instrument AI agents in one line. Cost, value, and SLO tracking — automatically.
Install
pip install avenza
Quickstart (3 lines)
from avenza import Agent
agent = Agent(name='Invoice Bot', risk_tier='T2')
with agent.run() as run:
result = process_invoice(data)
run.success = result.is_valid
run.log_value('task_completed', quantity=1, unit_value_usd=1.50)
That's it. If you're using the official Anthropic, OpenAI, or Gemini client, token usage is captured automatically with zero additional code.
Setup wizard
avenza init
Interactive wizard that verifies your API key and writes a working starter script — not a docs page to interpret.
Diagnose
avenza doctor
Self-diagnose connection issues, missing instrumentation, and proxy configuration.
Auto-instrumentation
The SDK patches the official provider clients the moment it's imported:
import anthropic
from avenza import Agent
agent = Agent(name='Support Bot')
client = anthropic.Anthropic()
with agent.run() as run:
# Token usage captured automatically from this call
response = client.messages.create(model='claude-sonnet-4-6', ...)
run.success = True
Works across threads and async/await via Python's contextvars.
Manual token fallback
with agent.run() as run:
response = my_llm_client.call(...)
run.set_tokens(response.input_tokens, response.output_tokens)
run.success = True
LangChain
from avenza.integrations.langchain import AvenzaCallbackHandler
from langchain_anthropic import ChatAnthropic
handler = AvenzaCallbackHandler(agent_name='Support Classifier', risk_tier='T1')
llm = ChatAnthropic(model='claude-sonnet-4-6', callbacks=[handler])
response = llm.invoke('Classify this ticket: ...')
Testing
from avenza.testing import MockAgent
def test_my_agent():
agent = MockAgent(name='Invoice Bot')
with agent.run() as run:
run.success = True
run.log_value('task_completed', quantity=1, unit_value_usd=1.50)
assert agent.runs[-1].success is True
assert agent.runs[-1].value_events[0]['quantity'] == 1
Configuration
| Parameter | Default | Description |
|---|---|---|
name |
required | Agent display name |
risk_tier |
'T1' |
T1 (autonomous), T2 (approve-first), T3 (assist-only) |
api_key |
AVENZA_API_KEY env |
Bearer token from Settings → API Tokens |
model |
None | LLM model for cost lookup (auto-detected when using auto-instrumentation) |
auto_instrument |
True |
Patch provider clients automatically |
offline_buffer |
True |
Queue failed sends to disk and retry |
base_url |
https://app.avenza.app |
Override for self-hosted |
redact_fields |
None | Field names to scrub from outgoing payloads (see below) |
Redaction
If your agent handles sensitive data — patient names, SSNs, account numbers — in set_metadata() or escalate(reason=...), tell the SDK which field names to scrub before anything leaves your process:
agent = Agent(name="Intake Bot", redact_fields=["ssn", "patient_name", "email"])
with agent.run() as run:
run.set_metadata(patient_name="Jane Doe", ssn="123-45-6789", confidence=0.94)
# payload sent to Avenza has patient_name and ssn replaced with "[REDACTED]"
# confidence is untouched — only field names you list are affected
Matching is case-insensitive and recursive (checked inside meta and value_events, at any nesting depth). Redaction only affects the outgoing copy — run._metadata in your own process still has the real values, so your agent's own logic is unaffected.
Never blocks. Never raises.
Every network call is fire-and-forget on a background thread. Avenza being down, slow, or returning errors will never crash your agent or add latency to your agent's actual work. Failed sends are buffered to .avenza_buffer.jsonl and retried on next startup.
If a send fails and there's no offline buffer to catch it (offline_buffer=False, or the buffer write itself fails — e.g. read-only filesystem), the payload is genuinely lost rather than silently retried. That's tracked, not hidden:
if agent.dropped_count > 0:
logger.warning(f"Avenza lost {agent.dropped_count} governance record(s)")
dropped_count should stay 0 in normal operation. A nonzero value under sustained load is a signal to leave offline_buffer=True (the default) rather than disabling it.
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 avenza-1.1.0.tar.gz.
File metadata
- Download URL: avenza-1.1.0.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4dc98cbd371bec259cc0fd922b4e4aa0c1f6934386fc3d2d51f899536617e3c5
|
|
| MD5 |
364ec494867e6fce983264d877e6127f
|
|
| BLAKE2b-256 |
edee868f0d6088910c5f300781eaf613e56c4318cd936de54854d0d6c13f6353
|
File details
Details for the file avenza-1.1.0-py3-none-any.whl.
File metadata
- Download URL: avenza-1.1.0-py3-none-any.whl
- Upload date:
- Size: 23.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d740fa5b4fc5ca19437b0b726698d4ea76a68bafda95d52fec5256d6266736c0
|
|
| MD5 |
338d6ef3cc81162c5c7e738179cb1210
|
|
| BLAKE2b-256 |
6f78d3422d250780eb513935ce818caca7fa4b13b0f2651ab02def1e05c4135e
|