Skip to main content

Runtime policy enforcement, human approval, and observability for AI agent tool calls

Project description

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, hits api-test.arden.sh
  • arden_live_... — production, hits api.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. Install optional extras if you don't already have the framework:

pip install "ardenpy[langchain]"       # 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.


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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ardenpy-0.5.2.tar.gz (44.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ardenpy-0.5.2-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file ardenpy-0.5.2.tar.gz.

File metadata

  • Download URL: ardenpy-0.5.2.tar.gz
  • Upload date:
  • Size: 44.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for ardenpy-0.5.2.tar.gz
Algorithm Hash digest
SHA256 02a1a0f98fd4ba9c2ad27ba34b5a019954c76ad3766ba9b71a4abb5ee95e44bb
MD5 4606d92c2638b0058d3c4b2a8e25223d
BLAKE2b-256 46546be9e302f5b374b7eef923383fa06f5c64d9de83f80c4a1a033d65c8685f

See more details on using hashes here.

File details

Details for the file ardenpy-0.5.2-py3-none-any.whl.

File metadata

  • Download URL: ardenpy-0.5.2-py3-none-any.whl
  • Upload date:
  • Size: 27.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for ardenpy-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 20a2ad60d41fd2193de44418918e3414ffb7d32af6a64bdbf2736ac1471edbfb
MD5 43337dace1369ade59e644084ba696e3
BLAKE2b-256 3d1c096d0f19614d56f8bd15ef94a5406c68528ded4db3f9d293d4c38ab8c875

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page