Python SDK for WL-APDP (Watchlight Agentic Policy Decision Point) - secure your agentic AI systems
Project description
WL-APDP Python SDK
Python SDK for integrating WL-APDP (Watchlight Agentic Policy Decision Point) authorization into agentic AI systems. Framework-agnostic core with optional adapters for CrewAI, LangGraph, and AutoGen.
Features
- Framework-Agnostic: Core SDK works with any Python code
- Async/Sync Support: Both async and sync HTTP clients
- Context Propagation: Automatic context handling across async boundaries
- Tool Authorization: Decorators and wrappers for authorized tools
- Audit Logging: Built-in audit trail for all authorization decisions
- Multi-Tenant: First-class support for tenant isolation
- Framework Adapters: Optional integrations for popular agent frameworks
Installation
# Core SDK
pip install wl-apdp
# With framework adapters
pip install wl-apdp[crewai] # CrewAI support
pip install wl-apdp[langgraph] # LangGraph/LangChain support
pip install wl-apdp[autogen] # AutoGen support
pip install wl-apdp[all] # All frameworks
Quick Start
Basic Authorization Check
from wl_apdp import WlApdpClient
async with WlApdpClient("http://localhost:8081/api") as client:
result = await client.authorize(
principal='Agent::"my-agent"',
action='Action::"execute"',
resource='Tool::"web_search"'
)
if result.is_allowed:
# Proceed with action
pass
Authorized Tools
from wl_apdp import authorized_tool, AuthorizationContext, authorization_context
@authorized_tool(resource='Tool::"calculator"', action="execute")
def calculate(expression: str) -> float:
return eval(expression)
# Set up authorization context
ctx = AuthorizationContext(
principal="agent-123",
principal_type="Agent",
tenant_id="tenant-abc"
)
with authorization_context(ctx):
result = calculate("2 + 2") # Checks authorization first
Executor Pattern
from wl_apdp import AuthorizedExecutor
executor = AuthorizedExecutor(
principal_id="agent-123",
principal_type="Agent",
tenant_id="tenant-abc"
)
# Check before executing
if executor.can_execute("web_search"):
result = my_tool.run(query)
# Or execute with automatic check
result = executor.execute(
action="execute",
resource='Tool::"web_search"',
func=lambda: my_tool.run(query)
)
Constraint Manifest (Agent-Native Interface)
The constraint manifest gives agents a complete picture of their operational boundaries at session start. This enables proactive self-governance rather than reactive discovery through failures.
Get Constraint Manifest
from wl_apdp import WlApdpSyncClient, ConstraintManifest
client = WlApdpSyncClient("http://localhost:8081/api")
# Fetch manifest at session start
manifest = client.get_constraint_manifest(
agent_id="researcher",
goal_id="goal-123", # Optional
include_guidance=True
)
# Manifest contains:
# - capabilities: What actions on what resources
# - constraints: What's forbidden, what needs approval
# - escalation_rules: When and how to escalate
# - guidance: Natural language role summary and best practices
print(manifest.guidance.role_summary)
Self-Governance with Cached Manifest
# Quick local check against cached constraints
if manifest.can_perform("read", "Document"):
# Action appears to be allowed, safe to proceed
pass
else:
# Action is forbidden, don't even try
handle_forbidden_action()
# Check if pre-flight is recommended
if manifest.should_preflight("execute"):
# Use preflight before actual authorization
pass
# Get required intents for an action
required_intents = manifest.get_required_intent("execute")
Pre-flight Checks
Pre-flight checks validate actions without consuming quota:
# Check if action would be allowed
result = client.preflight(
principal='Agent::"researcher"',
action='Action::"execute"',
resource='Tool::"web_search"',
intent_category="Research"
)
if result.allowed:
# Safe to proceed with actual authorization
auth_result = client.authorize(...)
else:
print(f"Would be denied: {result.explanation}")
for suggestion in result.suggestions:
print(f"Suggestion: {suggestion.description}")
if result.escalation_required:
# Handle escalation
print(f"Escalation needed: {result.escalation_required.reason}")
Query Capabilities
Discover what actions are available:
# What can I do with Documents?
result = client.query_capabilities(
agent_id="researcher",
resource_type="Document"
)
for cap in result.capabilities:
print(f"Can {cap.action} with risk level {cap.risk_level}")
if cap.requires_intent:
print(f" Requires intent: {cap.requires_intent}")
Integration Pattern
# At session start
manifest = client.get_constraint_manifest(agent_id)
# Before each action
def execute_action(action, resource, intent):
# 1. Quick local check
if not manifest.can_perform(action, resource):
return {"denied": True, "reason": "Forbidden by manifest"}
# 2. Pre-flight for uncertain actions
if manifest.should_preflight(action):
preflight = client.preflight(principal, action, resource, intent)
if not preflight.allowed:
return {"denied": True, "suggestions": preflight.suggestions}
# 3. Actual authorization
result = client.authorize(principal, action, resource, context)
if result.is_allowed:
return do_action()
return {"denied": True}
Framework Integrations
CrewAI
from wl_apdp.contrib.crewai import AuthorizedCrewAITool, AuthorizedCrew
from crewai_tools import SerperDevTool
# Wrap individual tools
search_tool = AuthorizedCrewAITool(
wrapped_tool=SerperDevTool(),
resource='Tool::"web_search"'
)
# Or wrap entire crew
crew = AuthorizedCrew(
agents=[researcher, writer],
tasks=[research_task, write_task],
principal_id="crew-123",
tenant_id="tenant-abc"
)
result = crew.kickoff()
LangGraph/LangChain
from wl_apdp.contrib.langgraph import authorized_langchain_tool, LangGraphAuthContext
from langchain_core.tools import tool
@authorized_langchain_tool(resource='Tool::"calculator"')
@tool
def calculator(expression: str) -> float:
"""Calculate a mathematical expression."""
return eval(expression)
# Run graph with authorization
with LangGraphAuthContext(principal_id="agent-1", tenant_id="tenant-abc"):
result = graph.invoke({"input": "calculate 2+2"})
AutoGen
from wl_apdp.contrib.autogen import AuthorizedFunction, AutoGenAuthContext
@authorized_function(resource='Tool::"weather"')
def get_weather(city: str) -> str:
return f"Weather in {city}: Sunny"
# Run conversation with authorization
with AutoGenAuthContext(principal_id="user-123", tenant_id="org-abc"):
user_proxy.initiate_chat(assistant, message="What's the weather?")
Authorization Context
The SDK uses Python's contextvars for propagating authorization context:
from wl_apdp import AuthorizationContext, authorization_context, get_current_context
ctx = AuthorizationContext(
principal="agent-123",
principal_type="Agent", # User, Agent, Service, or Bot
tenant_id="tenant-abc",
session_id="session-001",
attributes={"trust_level": 5}
)
# Sync context
with authorization_context(ctx):
current = get_current_context()
print(current.to_cedar_principal()) # 'Agent::"agent-123"'
# Async context
async with authorization_context(ctx):
# Context available in all async code
pass
Cedar Policy Examples
// Allow agents to execute tools
permit(
principal is Agent,
action == Action::"execute",
resource is Tool
) when {
principal.trust_level >= 3
};
// Tenant isolation
forbid(
principal,
action,
resource
) when {
context has tenant_id &&
resource has tenant_id &&
context.tenant_id != resource.tenant_id
};
// Rate limiting for bots
permit(
principal is Bot,
action,
resource
) when {
context.request_count < principal.rate_limit
};
API Reference
Clients
WlApdpClient- Async HTTP clientWlApdpSyncClient- Sync HTTP client
Context
AuthorizationContext- Holds principal and context infoauthorization_context- Context manager for setting contextget_current_context()- Get current contextrequire_context()- Get context or raise
Decorators
@authorized_tool- Add authorization to any function@authorize- Pre-authorization decorator@audit_action- Post-execution audit logging
Executors
AuthorizedExecutor- Sync executor with authorizationAsyncAuthorizedExecutor- Async executor
Models
PrincipalType- Enum: User, Agent, Service, BotAuthorizationRequest/Response- API modelscedar_principal(),cedar_action(),cedar_resource()- Helpers
Exceptions
AuthorizationDenied- Access was deniedServiceUnavailable- Cannot reach WL-APDP serverConfigurationError- Invalid configuration
Requirements
- Python 3.10+
- WL-APDP server running
License
Proprietary. See LICENSE for details.
Support
- Partner Portal: https://www.watchlight.ai/partner
- Email: team@watchlight.ai
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 wl_apdp-0.1.0.tar.gz.
File metadata
- Download URL: wl_apdp-0.1.0.tar.gz
- Upload date:
- Size: 28.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f31181c9939fd9c8e55248e70bd57dcc2cfd32e7fb62e02d38a5e0505f53a74
|
|
| MD5 |
372efbfb36c280877e53730d7e28c620
|
|
| BLAKE2b-256 |
23f9dd12e42e9401dafa4acdb0935b87665976743e46ee144a9fe0ca7ed28ff9
|
File details
Details for the file wl_apdp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: wl_apdp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 36.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
821943c699a040c876b6b0da831666c95f2e8e47562a9b362c36a9e9694e0652
|
|
| MD5 |
a3ceb332454a2fc79fd4d07d773ca7c6
|
|
| BLAKE2b-256 |
cee271eabaead7017ffe44a5c5d4cf279a1fc4bb3a5c3efc47f86b1b0dff8749
|