Starter template — langgraph-agent
Skip the copy-paste dance. Download a ready-to-run LangGraph agent pre-wired for tracing — 5 files, ≈17 KB. Apache 2.0.
What's inside
- template.py— Minimal copy-paste agent with AuditTrailIngestor
- agent.py— Full 7-tool agent showing rich DAG patterns
- requirements.txt— pip dependencies
- README.md— Agent-specific quickstart
- LICENSE— Apache 2.0
AuditTrail Quickstart Guide
Tip — Five-minute path
If you have Docker already, step 1 alone will get you the dashboard at
http://localhost:3000. Everything below step 1 is instrumentation for your own agents.
Prerequisites
- Docker and Docker Compose (recommended)
- Python 3.12+ -- for running agents
- An OpenAI API key -- for the example LangGraph agent (uses gpt-4o-mini)
1. Start AuditTrail
cd AuditTrailCodebase
docker compose up --build| Service | URL | Description |
|---|---|---|
| Dashboard | http://localhost:3000 | Next.js 16 frontend |
| API | http://localhost:8000 | FastAPI backend |
| Proxy (prod) | https://your-domain | Caddy reverse proxy (TLS + /api/* routing) |
Verify the API is running:
curl http://localhost:8000/api/v1/health
# {"status":"ok","version":"<current release>","db_connected":true,...}Want to skip this? Click View Demo on auditrail.imaginaerium.in — no install required. The demo user is seeded with a live fleet burst (12 traces across 6 agents in the last 15 min), a gateway-routed Operations Assistant chat, and an open Tier 2 deployment action proposal.
2. Create Your Account
- Open http://localhost:3000
- Click Register -- create an account with email and password
- Log in with your credentials
- You'll see an empty dashboard -- that's correct, you have no traces yet
3. Create an API Key
Your agents need an API key to send traces to your account.
- Go to Profile (click your avatar in the sidebar)
- In the API Keys section, enter a key name (e.g., "My Agent")
- Select scope ingest and click the + button
- A yellow box appears with your secret key -- copy it immediately!
The secret key starts with
sk-at-...and is shown only once. If you lose it, delete the key and create a new one.
- Save the key in your agent's environment:
export AUDITTRAIL_API_KEY=sk-at-your-secret-key-here4. Run Your First Agent
cd examples/langgraph-agent
# Install dependencies
pip install -r requirements.txt
# Set your keys
export OPENAI_API_KEY=sk-proj-...
export AUDITTRAIL_API_KEY=sk-at-...
# Run the template agent
python template.py "Search for the latest AI developments and summarize them"You'll see:
[OK] API key configured (sk-at-384327...)
[OK] Trace registered: abc123...
[LIVE] Open http://localhost:3000/traces/abc123... to watch live
...
[LIVE] All spans sent incrementally. Trace complete.
5. View Your Trace
Open the trace URL printed by the agent. You'll see:
- Spans -- Every step the agent took (LLM calls, tool executions)
- DAG -- Interactive decision-chain graph with hierarchical grouping
- Timeline -- Waterfall view of sequential and parallel execution
- Sankey -- Causal attribution diagram (click "Run Ablation" to generate)
The trace shows as Running while the agent executes and transitions to Complete when done.
Try the Full Agent (7 tools, rich DAG)
python agent.py "Research climate change impacts, check current time, look up reports from the database, read the agent_config.json file, calculate crop yield at 8 percent per degree, compare output of 92 tons against target of 100, and summarize findings"Tried & Tested: Deep Search Agent
examples/deep-search-agent/ is a fully working LangGraph agent that was
used during AuditTrail's own development and integration testing. It is not
a template — it is a complete, battle-tested agent you can run against a
local or hosted AuditTrail instance.
cd examples/deep-search-agent
# Install dependencies
pip install -r requirements.txt
# Configure (copy and fill in your keys)
cp .env.example .env
# Edit .env: set OPENAI_API_KEY, AUDITTRAIL_API and AUDITTRAIL_API_KEY
# Run
python agent.pyWhat it does: a LangGraph multi-tool agent (web search, calculator, file reader, database lookup, weather, time, summariser) that streams full live traces to AuditTrail as it runs — every LLM call, every tool invocation, every state transition. In the dashboard you get the multi-level DAG, live streaming, per-span cost attribution, causal attribution (run Ablation on the trace detail page), and constitutional evaluation against the default rules.
To point it at a hosted instance, set AUDITTRAIL_API in .env to the
instance URL and use an API key minted from that dashboard.
Agent Integration Guide
Authentication
All agents authenticate via API key in the Authorization header:
headers = {"Authorization": "Bearer sk-at-your-key-here"}
httpx.post(f"{API}/api/v1/ingest/spans", json={"batch": spans}, headers=headers)Cost Calculation
AuditTrail calculates costs automatically using a four-layer system (most specific wins):
| Priority | Source | How | Badge Color |
|---|---|---|---|
| 1 | Agent-reported | Agent sends cost field in span | Green "Agent" |
| 2 | Operator overrides | rules/pricing.yaml — substring match, longest key wins | Blue "Estimated" |
| 3 | Community catalog | ~3k known models priced automatically (bundled snapshot of LiteLLM's price index, auto-refreshed daily) | Blue "Estimated" |
| 4 | Default rate | Model in neither table, uses fallback | Yellow "Default Rate" |
You don't need to calculate cost in your agent code. Just send model and
tokens_in/tokens_out — known models (GPT, Claude, Gemini, DeepSeek, …)
price automatically from the catalog, including dated variants like
gpt-4o-mini-2024-07-18. Each span shows a badge indicating where the cost
came from.
To override: send cost=0.05 in your span and it will be used as-is (source: "agent").
To set your own rates (negotiated pricing, zero-cost self-hosted models):
edit rules/pricing.yaml — rates are per 1M tokens in USD and beat the catalog.
The catalog refreshes from upstream every 24h while
AUDITTRAIL_PRICING_AUTO_REFRESH is enabled (default). Air-gapped installs
simply run on the bundled snapshot — a failed refresh never degrades pricing.
To view the current configuration: GET /api/v1/settings/pricing (operator
overrides, default rate, and catalog size/freshness).
Traces are automatically assigned to the user who owns the API key. Each user only sees their own traces.
Option 1: Copy the Template (Fastest)
cp examples/langgraph-agent/template.py my_agent.py
# Edit: replace tools, update AGENT_NAME, set AUDITTRAIL_API_KEY
python my_agent.py "Your prompt"The template includes the AuditTrailIngestor class with authentication, live tracing, and correct formats built in.
If you already have the Python SDK installed (pip install audittrail),
the same templates are available from the CLI — no browser needed:
audittrail list # print the template catalog (id · title · description)
audittrail scaffold quickstart # materialise a runnable copy into ./<name>/
# then: cd into it, pip install -r requirements.txt, cp .env.example .env, python agent.py "your prompt"scaffold takes --name, --dir and --force; it prints the exact
next-steps recipe when it finishes. These are siblings of the documented
audittrail daemon (Local runner) and
audittrail secrets (Agent secrets) subcommands.
Option 2: HTTP Ingest API (Any Framework)
Send spans directly via HTTP. Works with any language or framework.
Register a Trace
import httpx
from uuid import uuid4
API = "http://localhost:8000"
headers = {"Authorization": f"Bearer {os.environ['AUDITTRAIL_API_KEY']}"}
trace_id = str(uuid4())
httpx.post(f"{API}/api/v1/ingest/traces", json={
"trace": {
"trace_id": trace_id,
"agent_name": "my-agent",
"environment": "development",
"metadata": {"user_prompt": "..."},
}
}, headers=headers)Send Spans
spans = [{
"span_id": str(uuid4()),
"trace_id": trace_id,
"parent_span_id": None,
"name": "my_llm_call",
"span_type": "llm", # llm | tool | agent | chain | retriever | custom
"start_time": datetime.now(UTC).isoformat(),
"end_time": (datetime.now(UTC) + timedelta(milliseconds=500)).isoformat(),
"status": "ok", # ok | error | running
"input": {"prompt": "..."},
"output": {"response": "..."},
"model": "gpt-4o-mini",
"tokens_in": 100,
"tokens_out": 50,
"cost": 0.0001,
"tool_calls": None,
"attributes": {},
}]
resp = httpx.post(f"{API}/api/v1/ingest/spans", json={"batch": spans}, headers=headers)Live Tracing Pattern
For real-time dashboard updates, send spans incrementally:
# When a step STARTS — send with status="running", no end_time:
httpx.post(f"{API}/api/v1/ingest/spans", json={"batch": [{
"span_id": span_id,
"trace_id": trace_id,
"name": "my_tool",
"span_type": "tool",
"start_time": datetime.now(UTC).isoformat(),
"end_time": None,
"status": "running",
...
}]}, headers=headers)
# When the step COMPLETES — send same span_id with status="ok":
httpx.post(f"{API}/api/v1/ingest/spans", json={"batch": [{
"span_id": span_id, # same span_id = upsert
"trace_id": trace_id,
"name": "my_tool",
"span_type": "tool",
"start_time": start.isoformat(),
"end_time": datetime.now(UTC).isoformat(),
"status": "ok",
"output": {"result": "..."},
...
}]}, headers=headers)The API upserts on span_id. WebSocket broadcasts notify the dashboard to re-render live.
Tool Calls Format
"tool_calls": [
{
"tool_name": "web_search",
"arguments": {"query": "AI safety 2026"},
"result": {"output": "Found 3 papers..."}, # MUST be dict or null
"selected_from": ["web_search", "calculator"],
}
]The
resultfield must be a dict (e.g.,{"output": "..."}) ornull. Passing a plain string causes a422error.
Reading API Responses
All GET endpoints wrap responses in {"data": ...}:
resp = httpx.get(f"{API}/api/v1/traces/{trace_id}", headers=headers)
trace = resp.json()["data"] # NOT resp.json()Option 3: LangChain Callback Handler
For LangGraph/LangChain agents, use the built-in callback handler:
cd apps/api && pip install -e ".[agent]"from audittrail.callback_handler import AuditTrailCallbackHandler
from audittrail.traceable import init_tracer, start_trace, shutdown_tracer
await init_tracer()
async with start_trace("my-agent", "development") as trace_id:
handler = AuditTrailCallbackHandler(trace_id=trace_id)
result = await graph.ainvoke(
{"messages": [HumanMessage(content="...")]},
config={"callbacks": [handler]},
)
await shutdown_tracer()Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
| 422 on span ingestion | tool_calls[].result is a string | Use {"output": "..."} not "..." |
| Empty trace data | Reading resp.json() | Use resp.json()["data"] |
| Traces not showing | No API key or wrong key | Check AUDITTRAIL_API_KEY env var |
| Flat DAG | All spans same parent | Create intermediate chain/agent spans |
| Trace stuck on "Running" | Root span not completed | Send final send_complete() for root |
| 401 on all requests | Session expired | Re-login or use API key |
| Cost shows $0.00 | No model field in span | Send model="gpt-4o-mini" with tokens |
| Cost shows "Default Rate" | Model in neither the catalog nor pricing.yaml | Add your model to rules/pricing.yaml (or check the id for typos — catalog matching is exact) |
Demo Mode
Click View Demo on the landing page to explore AuditTrail with sample data (no account needed).
Manual Development Setup
For development without Docker:
Backend
cd apps/api
cp ../../.env.example ../../.env # Edit: set AUDITTRAIL_SECRET_KEY
pip install -e ".[dev]"
uvicorn audittrail.main:app --reload --port 8000Frontend
cd apps/web
npm install
npm run devEnvironment Variables
| Variable | Default | Description |
|---|---|---|
AUDITTRAIL_SECRET_KEY | dev-secret-... | JWT signing secret. Change in production. |
AUDITTRAIL_DEBUG | false | Verbose logging |
AUDITTRAIL_RULES_DIR | ./rules | Constitutional rule YAML directory |
AUDITTRAIL_CORS_ORIGINS | ["http://localhost:3000"] | Allowed CORS origins |
AUDITTRAIL_DATABASE_URL | sqlite+aiosqlite:///./data/audittrail.db | Database connection for the running app (all app settings use the AUDITTRAIL_ prefix) |
OPENAI_API_KEY | (empty) | For agents and ablation |
API_INTERNAL_URL | http://localhost:8000/api | Build-time Docker ARG — the Next.js /api/* rewrite destination is baked at next build (compose passes http://api:8000/api via build.args); changing it requires a rebuild, not a restart |
Two DB variables, two consumers: the running app reads only the prefixed
AUDITTRAIL_DATABASE_URL; the unprefixedDATABASE_URLis read only by the Alembic migration runner (alembic/env.py). Setting the unprefixed one has no effect on the live API process.
The frontend uses a relative
/apiURL in the browser and derives the WebSocket URL fromwindow.locationat runtime, so it works on any host without bakingNEXT_PUBLIC_API_URL/NEXT_PUBLIC_WS_URLinto the bundle.
New in V2.x — Live Ops Surfaces
Once traces are flowing, explore the three operations surfaces shipped between V2.2 and V2.5:
/fleet(V2.2) — per-agent summary with error rate, p95 latency, violation rate, and one-click jump to the most recent trace. Auto-refreshes every 15s./assistant(V2.3) — BYOK chatbot grounded on your fleet. Add your OpenAI / Anthropic API key at Settings → AI Assistant. Keys are encrypted with Fernet (HKDF-derived from yourAUDITTRAIL_SECRET_KEY), scoped to your account, and never returned in API responses.- Per-node actions (V2.2) — right-click any DAG node to propose a 3-tier deployment action (Tier 1 autonomous-safe alerts, Tier 2 supervised throttle/swap-model, Tier 3 typed-confirm kill-run). Every proposal lands at
/deploymentswith a full audit row.
For deeper docs on compliance + gateway + SDKs, see the respective sidebar entries.
Project Structure
AuditTrailCodebase/
apps/
api/ # FastAPI backend (Python 3.12)
web/ # Next.js frontend (TypeScript)
rules/ # Constitutional rule YAML files
examples/
langgraph-agent/ # Real LangGraph agent + template
agent.py # Full 7-tool agent with live tracing
template.py # Minimal copy-paste template
requirements.txt
deep-search-agent/ # Tried & tested multi-tool LangGraph agent
agent.py # Complete integration-test agent (~280 LOC)
requirements.txt
.env.example
demo_happy_path.py # Mock demo scenarios (no API key needed)
demo_constitutional.py
demo_debugging.py
docs/ # Documentation
docker-compose.yml # Production compose