Metadata-Version: 2.5
Name: shadow-warden-sdk
Version: 1.1.0
Summary: Python SDK for the Shadow Warden AI security gateway
Project-URL: Homepage, https://shadow-warden-ai.com
Project-URL: Repository, https://github.com/zborrman/Shadow-Warden-AI
Project-URL: Issues, https://github.com/zborrman/Shadow-Warden-AI/issues
License: Proprietary
Keywords: ai,dlp,gateway,jailbreak,llm,pii,security
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: otel
Requires-Dist: opentelemetry-sdk>=1.24; extra == 'otel'
Description-Content-Type: text/markdown

# shadow-warden-sdk

Python SDK for the [Shadow Warden AI](https://shadowwarden.ai) security gateway.

## Install

```bash
pip install shadow-warden-sdk
```

## Quick start

```python
from shadow_warden import WardenClient

with WardenClient(gateway_url="http://localhost:8001", api_key="sk_...") as warden:
    result = warden.filter("Summarise the contract for client@example.com")
    if result.allowed:
        # safe to forward to your AI model
        ...
    else:
        print("Blocked:", result.risk_level, result.flag_names)
```

## OpenAI wrapper (drop-in)

```python
import openai
from shadow_warden import WardenClient

warden = WardenClient(api_key="sk_warden_...")
client = warden.wrap_openai(openai.OpenAI(api_key="sk-openai-..."))

# Identical to the standard OpenAI API — Warden intercepts transparently
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "..."}],
    raise_on_block=True,    # raises WardenBlockedError if blocked
)
```

## Async

```python
from shadow_warden import AsyncWardenClient

async with AsyncWardenClient(gateway_url="...", api_key="...") as warden:
    result = await warden.filter("user prompt")
```

## Batch filtering

```python
results = warden.filter_batch([
    "What is the capital of France?",
    {"content": "My SSN is 123-45-6789", "strict": True},
])
```

## Fail-open mode

```python
# If the gateway is unreachable, return a permissive result instead of raising
warden = WardenClient(fail_open=True)
```

## Configuration

| Parameter | Default | Description |
|-----------|---------|-------------|
| `gateway_url` | `http://localhost:8001` | Warden gateway base URL |
| `api_key` | `""` | `X-API-Key` header value |
| `tenant_id` | `"default"` | Default tenant for all requests |
| `timeout` | `10.0` | HTTP timeout in seconds |
| `fail_open` | `False` | Return permissive result on network errors |

## Error handling

```python
from shadow_warden import WardenBlockedError, WardenGatewayError, WardenTimeoutError

try:
    result = warden.filter(content, raise_on_block=True)
except WardenBlockedError as e:
    print("Blocked:", e.result.risk_level)
except WardenTimeoutError:
    print("Gateway timeout")
except WardenGatewayError as e:
    print(f"HTTP {e.status_code}: {e.detail}")
```

## FilterResult fields

| Field | Type | Description |
|-------|------|-------------|
| `allowed` | `bool` | Whether the content passed all filters |
| `blocked` | `bool` | Convenience inverse of `allowed` |
| `risk_level` | `str` | `low` / `medium` / `high` / `block` |
| `filtered_content` | `str` | Content after PII redaction |
| `secrets_found` | `list[SecretFinding]` | Detected secrets/PII |
| `semantic_flags` | `list[SemanticFlag]` | Triggered semantic rules |
| `flag_names` | `list[str]` | Shorthand for flag names |
| `has_secrets` | `bool` | True if any secrets were found |
| `has_pii` | `bool` | True if `pii_detected` flag is present |
| `processing_ms` | `dict[str, float]` | Per-stage timing breakdown |

## Agentic commerce

`ShadowWardenClient` carries the mandate, order, spend and MCP-intent calls, and
`SecureAgent` is the mixin that gives any agent class mandate-controlled
purchasing:

```python
from shadow_warden import SecureAgent

class ProcurementAgent(SecureAgent):
    def run(self, task: str):
        if self.filter_prompt(task).get("blocked"):
            raise ValueError("task blocked")
        self.create_mandate(max_amount=200.0)
        return self.purchase({"store_url": "shop.example.com", "items": [...]})
```

## OpenTelemetry

`pip install "shadow-warden-sdk[otel]"` adds `WardenSpanProcessor`, which filters
span attributes through the gateway before they are exported.
