ardenpy
Policy enforcement and human approval for AI agent tool calls.
Arden sits between your agent and its tools. Every call is checked against policies you configure in the dashboard — automatically allowed, blocked, or held for a human to approve.
Install
pip install ardenpy
Claude Code skill
Let Claude Code integrate Arden into your project automatically — it detects your framework, adds the dependency, inserts configure(), and optionally sets up session tracking:
# Add the skill to your project
mkdir -p .claude/commands
curl -o .claude/commands/arden-setup.md \
https://raw.githubusercontent.com/ardenhq/adenpy/main/skills/arden-setup.md
Then in Claude Code, run:
/arden-setup
Quick start
1. Get your API key from app.arden.sh. You'll get two keys:
arden_test_...— development, hitsapi-test.arden.sharden_live_...— production, hitsapi.arden.sh
2. Configure once
import ardenpy as arden
arden.configure(api_key="arden_live_...")
3. Call your tools
For LangChain, CrewAI, and OpenAI Agents SDK — that's it. Arden auto-patches all three frameworks at configure-time, so every tool call is intercepted without any wrapping:
# LangChain, CrewAI, or OpenAI Agents SDK — use tools normally
arden.configure(api_key="arden_live_...")
agent = create_react_agent(llm, tools, prompt) # no wrapping needed
# Every tool call is now enforced and logged automatically
For custom agents with no framework, wrap functions explicitly:
def issue_refund(amount: float, customer_id: str) -> dict:
return {"refund_id": "re_123", "amount": amount}
safe_refund = arden.guard_tool("stripe.issue_refund", issue_refund)
result = safe_refund(150.0, customer_id="cus_abc")
# Arden checks policy first — allow, block, or wait for human approval
How it works
Every tool call goes through a policy check before executing:
| Policy decision | What happens |
|---|---|
allow |
Function executes immediately |
block |
PolicyDeniedError raised, function never runs |
requires_approval |
Pauses until a human approves or denies on the dashboard |
No policy configured? The call is allowed automatically and logged — you get a full audit trail from day one and can add policies incrementally.
Approval modes
When a tool requires approval, you choose how your code waits:
wait (default) — blocks until a human acts, then executes or raises PolicyDeniedError.
async — returns a PendingApproval immediately, background thread polls and calls your callback.
webhook — returns PendingApproval immediately, no polling. Arden POSTs to your endpoint when an admin decides.
# wait (default)
safe_refund = arden.guard_tool("stripe.issue_refund", issue_refund)
# async
safe_refund = arden.guard_tool("stripe.issue_refund", issue_refund,
approval_mode="async", on_approval=handle_approval, on_denial=handle_denial)
# webhook
safe_refund = arden.guard_tool("stripe.issue_refund", issue_refund,
approval_mode="webhook", on_approval=on_approval, on_denial=on_denial)
For webhook setup (FastAPI, Flask, Django examples) see the Library Reference.
Framework integrations
LangChain, CrewAI, and OpenAI Agents SDK — zero wrapping required
Arden automatically patches all three frameworks when configure() is called. Every tool call in the process is intercepted — including tools created after configure is called.
import ardenpy as arden
arden.configure(api_key="arden_live_...")
# All tool calls are now intercepted, enforced, and logged.
# No protect_tools(), no guard_tool(), no boilerplate.
| Framework | What gets patched |
|---|---|
| LangChain | BaseTool.run at the class level |
| CrewAI | BaseTool.run at the class level |
| OpenAI Agents SDK | FunctionTool.__init__ — wraps on_invoke_tool per instance |
Tool names in the dashboard match each tool's .name attribute. If you don't already have the framework installed, install it alongside ardenpy:
pip install ardenpy langchain-core # LangChain
pip install ardenpy crewai # CrewAI
pip install ardenpy openai-agents # OpenAI Agents SDK
OpenAI Chat Completions (raw loop)
The Chat Completions API has no patchable base class. Use ArdenToolExecutor to register and dispatch tool calls:
from ardenpy.integrations.openai import ArdenToolExecutor
executor = ArdenToolExecutor()
executor.register("issue_refund", issue_refund_fn)
executor.register("send_email", send_email_fn)
# In your tool-call loop:
result = executor.run(tc.function.name, json.loads(tc.function.arguments))
See examples/ for runnable code for every integration.
Session tracking
Attach a session ID to group all tool calls from a single conversation in the action log:
import ardenpy as arden
import uuid
arden.configure(api_key="arden_live_...")
# Set once per request — all guard_tool and auto-patched calls carry it automatically
arden.set_session(str(uuid.uuid4()))
Uses contextvars.ContextVar — safe for concurrent async requests. Fully optional.
Token usage tracking
Arden automatically tracks token usage and estimated cost for every LLM call — no extra setup needed. For LangChain, CrewAI, and the OpenAI Agents SDK, configure() captures usage from the framework at runtime and sends it to the dashboard in a background thread (zero latency impact).
import ardenpy as arden
arden.configure(api_key="arden_live_...")
# Token usage is now tracked automatically alongside tool calls.
# View cost breakdowns by model, day, and session in the dashboard.
For custom agent loops where you call the LLM directly, log usage manually:
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=messages,
)
arden.log_token_usage(
model="gpt-4o",
prompt_tokens=response.usage.prompt_tokens,
completion_tokens=response.usage.completion_tokens,
)
| What gets tracked | Automatically? |
|---|---|
LangChain (BaseChatModel) |
Yes — auto-patched by configure() |
| CrewAI | Yes — uses LangChain under the hood |
OpenAI Agents SDK (Runner.run) |
Yes — auto-patched by configure() |
| OpenAI Chat Completions (raw loop) | Manual — call arden.log_token_usage() |
| Any other LLM | Manual — call arden.log_token_usage() |
The dashboard shows cost broken down by model, day, and session — all scoped per agent.
Error handling
try:
result = safe_refund(150.0, customer_id="cus_abc")
except arden.PolicyDeniedError:
# blocked by policy, or denied by a human
except arden.ApprovalTimeoutError:
# nobody approved within max_poll_time (wait mode)
except arden.ArdenError:
# API/configuration error
Links
- Library Reference — full API docs
- Examples — runnable code
- Dashboard
- PyPI
- Support
Metadata
Release files for ardenpy 0.5.7
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 | |
|---|---|---|---|---|
| ardenpy-0.5.7.tar.gz | 55.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ardenpy-0.5.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 87.3 kB
Release files / ardenpy-0.5.7.tar.gz
| Download URL | ardenpy-0.5.7.tar.gz |
|---|---|
| Size | 55.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10c56470753b70dfb2a62b83feb4ec0c0f2735bc2cedd8979ba81f6741752064
|
|
BLAKE2b-256 checksum How to use checksums |
beeb539796e437b69bc737cd80f45fc5e4bc59765dffd5031294f0c19d29680a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / ardenpy-0.5.7-py3-none-any.whl
| Download URL | ardenpy-0.5.7-py3-none-any.whl |
|---|---|
| Size | 32.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b46fba55cdb299a820cc22cbd327883fd70ded049439835b3eff1024fd80766b
|
|
BLAKE2b-256 checksum How to use checksums |
cfe4857c544de10a6d79269982f274b072fd9ed48582a95dee0917e2a344016f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for ardenpy 0.5.7
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.