Skip to main content

Policy engine for governing AI agent tool execution.

Project description

Guardian Angel

A lightweight Python SDK for governing AI agent tool execution.

Guardian Angel intercepts agent actions, evaluates policy, and returns allow, deny, or require_approval — before the tool runs.

Install

pip install guardian-angel

# optional CLI
pip install guardian-angel[cli]

Quickstart

# policy.yaml
rules:
  - name: block_risky_delete
    tool: resource.delete
    decision: deny
    all:
      - key: resource.environment
        op: eq
        value: prod
      - key: context.risk_level
        op: eq
        value: high
from guardian_angel import GuardianAngel, ActionRequest

guard = GuardianAngel.from_yaml("policy.yaml")

decision = guard.authorize(
    ActionRequest(
        tool="resource.delete",
        attributes={
            "resource.environment": "prod",
            "context.risk_level": "high",
        },
    )
)
print(decision.status)  # "deny"

First matching rule wins. No match → allow.

CLI

guardian-angel evaluate policy.yaml request.json
guardian-angel evaluate policy.yaml request.json --explain
guardian-angel --verbose evaluate policy.yaml request.json
guardian-angel --version

--explain prints the matched rule and reason. --verbose adds input context.

Features

  • Predicate ruleswhen, all, any, not with operators (eq, ne, in, not_in, contains, gt, gte, lt, lte, …)
  • Cross-field comparisonvalue_from to compare one attribute against another
  • Approval workflow — pluggable ApprovalHandler and AsyncApprovalHandler protocols for human-in-the-loop approval (Slack, email, GitHub issues, etc.)
  • Tool decorator@guard.tool() (sync) and @guard.async_tool() (async) for automatic policy enforcement, including approval
  • YAML or Python — define rules in files or construct Rule objects in code
  • CLI — evaluate policies from the command line with colored output

See examples/ for more.

How It Works

Agent tool call → ActionRequest → GuardianAngel.authorize() → Decision
                                                                 ├─ allow → execute
                                                                 ├─ deny  → block
                                                                 └─ require_approval → ApprovalHandler

Approval Workflow

When a rule returns require_approval, Guardian Angel can delegate to a pluggable approval backend. Both synchronous and asynchronous handlers are supported.

Sync handler

Any class with a submit(request: ApprovalRequest) -> ApprovalResponse method satisfies the ApprovalHandler protocol:

from guardian_angel import (
    ApprovalHandler, ApprovalRequest, ApprovalResponse, ApprovalStatus,
)

class SlackApprovalHandler:
    def submit(self, request: ApprovalRequest) -> ApprovalResponse:
        # send a Slack message, wait for reaction, return outcome
        ...
        return ApprovalResponse(
            approval_id=request.approval_id,
            status=ApprovalStatus.APPROVED,  # or REJECTED / EXPIRED
            approved_by="alice",
        )

Async handler

For non-blocking I/O, implement AsyncApprovalHandler with an async def submit():

from guardian_angel import (
    AsyncApprovalHandler, ApprovalRequest, ApprovalResponse, ApprovalStatus,
)

class AsyncSlackApprovalHandler:
    async def submit(self, request: ApprovalRequest) -> ApprovalResponse:
        # await Slack API call, return outcome
        ...
        return ApprovalResponse(
            approval_id=request.approval_id,
            status=ApprovalStatus.APPROVED,
            approved_by="alice",
        )

Wiring it up

Pass either handler type when creating a GuardianAngel instance:

# sync
guard = GuardianAngel(rules=rules, approval_handler=SlackApprovalHandler())

# async
guard = GuardianAngel(rules=rules, approval_handler=AsyncSlackApprovalHandler())

# from YAML (works with either)
guard = GuardianAngel.from_yaml("policy.yaml", approval_handler=handler)

Using request_approval() / request_approval_async()

from guardian_angel import ActionRequest

# Sync — requires a sync handler
response = guard.request_approval(
    ActionRequest(tool="resource.update", request_id="req-1", attributes={...})
)

# Async — works with both sync and async handlers
response = await guard.request_approval_async(
    ActionRequest(tool="resource.update", request_id="req-1", attributes={...})
)

print(response.approval_id)
print(response.status)  # "approved", "rejected", or "expired"

ActionRequest.request_id identifies the original tool call. ApprovalRequest.approval_id and ApprovalResponse.approval_id identify the approval workflow instance. Guardian Angel generates approval_id by default, but handlers or advanced integrations can override it when constructing an ApprovalRequest manually.

Behavior:

  • require_approval + sync handlerrequest_approval() calls handler.submit(), returns ApprovalResponse
  • require_approval + async handlerrequest_approval_async() awaits handler.submit(), returns ApprovalResponse
  • require_approval + async handler + sync callrequest_approval() raises TypeError (use the async variant)
  • require_approval + no handler → raises ApprovalRequiredError
  • allow → raises ValueError (no approval needed)
  • deny → raises PolicyDeniedError

With the @guard.tool() / @guard.async_tool() decorator

The decorator routes require_approval decisions through the handler automatically:

# Sync decorator — for sync functions + sync handler
@guard.tool(name="resource.update")
def update_resource(resource_id, *, attributes=None, request_id=None):
    return {"updated": True, "resource_id": resource_id}

# Async decorator — for async functions, works with sync or async handler
@guard.async_tool(name="resource.update")
async def update_resource(resource_id, *, attributes=None, request_id=None):
    return {"updated": True, "resource_id": resource_id}

Without a handler, ApprovalRequiredError is raised as before.

See examples/approval_example.py (sync) and examples/async_approval_example.py (async) for full working examples.

Roadmap

  • v0.1 — Local policy evaluation, YAML rules, decorator
  • v0.2 — Stronger validation, policy linting
  • v0.3 — CLI with evaluate, --explain, --verbose
  • v0.4 — Approval workflow with pluggable handlers (current)
  • v0.5 — Framework adapters (LangGraph, OpenAI, CrewAI)

License

MIT

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

guardian_angel-0.4.0.tar.gz (30.2 kB view details)

Uploaded Source

Built Distribution

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

guardian_angel-0.4.0-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file guardian_angel-0.4.0.tar.gz.

File metadata

  • Download URL: guardian_angel-0.4.0.tar.gz
  • Upload date:
  • Size: 30.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for guardian_angel-0.4.0.tar.gz
Algorithm Hash digest
SHA256 4e591a47096e4c1728b214b01d09e6d34b6255213a6f36fe624ed847800ad379
MD5 badc65bbd71e3e0c1f3afb74a9e202e5
BLAKE2b-256 1cc24da88c18b25c64c0cdb03ec4a14cad63e9749473c42a962390bed1bea412

See more details on using hashes here.

File details

Details for the file guardian_angel-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: guardian_angel-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 18.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for guardian_angel-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dadcbff26c2ca1f8f48371417ffcdd7433e89d66b7223b35199b4c0423d3e856
MD5 98e812217c662bcc90465b5522640893
BLAKE2b-256 bd02845ce6424a2d4b4c5ba31d025fb0a566ec7de1e7bbfac6eaffd947a9f3e4

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