Metadata-Version: 2.4
Name: gaas-pydantic-ai
Version: 0.1.0
Summary: Pydantic AI integration for GaaS (Governance as a Service)
Project-URL: Homepage, https://gaas.is
Project-URL: Documentation, https://gaas.to/sdks.html
Project-URL: Repository, https://github.com/H2OmAI/gaas
Project-URL: Changelog, https://github.com/H2OmAI/gaas/blob/main/CHANGELOG.md
Author-email: H2Om <sdk@gaas.is>
License-Expression: Apache-2.0
Keywords: agents,ai,gaas,governance,pydantic,pydantic-ai
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: gaas-sdk>=0.2.7
Requires-Dist: httpx>=0.27.0
Provides-Extra: all
Requires-Dist: pydantic-ai-slim>=2.3; extra == 'all'
Provides-Extra: dev
Requires-Dist: pydantic-ai-slim<3,>=2.3; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Provides-Extra: pydantic-ai
Requires-Dist: pydantic-ai-slim>=2.3; extra == 'pydantic-ai'
Description-Content-Type: text/markdown

# gaas-pydantic-ai

GaaS (Governance as a Service) integration for [Pydantic AI](https://ai.pydantic.dev) (v2).

Every tool call is governed **before it executes**: the toolset submits a
governance intent to GaaS, and only an APPROVE (or an approved escalation)
lets the tool run. On BLOCK, `GovernanceBlockedError` is raised — and because
Pydantic AI propagates ordinary exceptions out of `agent.run()` natively, a
blocked action **halts the run**. No wrappers fighting the framework: this is
the framework's own semantics.

```bash
pip install gaas-pydantic-ai[pydantic-ai]
```

## Quickstart

```python
from pydantic_ai import Agent
from gaas_pydantic_ai import govern_tools, GaaSGovernanceConfig

def search_web(query: str) -> str:
    """Search the web."""
    ...

def send_email(to: str, subject: str, body: str) -> str:
    """Send an email."""
    ...

config = GaaSGovernanceConfig(api_key="gsk_...", agent_id="my-agent")

agent = Agent(
    "anthropic:claude-sonnet-5",
    toolsets=[govern_tools([search_web, send_email], config=config)],
)

result = await agent.run("Email the Q3 report to finance@acme.com")
```

Already have a toolset? Wrap it directly:

```python
from gaas_pydantic_ai import govern_toolset

agent = Agent(model, toolsets=[govern_toolset(my_toolset, config=config)])
```

## Verdict flow

```
tool call → GaaS intent → ┌─────────┐
                          │ APPROVE │ → tool executes
                          │ BLOCK   │ → GovernanceBlockedError aborts agent.run()
                          │ ESCALATE│ → aborts by default; hold-and-poll optional
                          └─────────┘
```

`GovernanceBlockedError` is never converted into model-visible text — the
adapter deliberately does not use `ModelRetry`, so the model cannot route
around a block.

## Configuration

```python
config = GaaSGovernanceConfig(
    api_url="https://api.gaas.is",    # GaaS API endpoint
    api_key="gsk_...",                # Your API key
    agent_id="my-agent",              # Appears in the audit trail
    block_on_escalate=True,           # Raise on ESCALATE (default)
    timeout_seconds=5.0,              # Governance check timeout (fail open)
    sensitivity="INTERNAL",           # Default sensitivity for tool inputs
    raise_on_governance_error=False,  # Fail open if GaaS is unreachable
    extra_regulatory_domains=["HIPAA"],
    extra_data_categories=["PHI"],
    hold_on_escalate=False,           # Wait for the human decision on ESCALATE
    escalation_poll_seconds=5.0,
    escalation_max_wait_seconds=600.0,
)
```

### Hold-and-Poll on ESCALATE

With `hold_on_escalate=True`, an ESCALATE verdict holds the tool call while
GaaS routes the escalation to a human reviewer: **approve/modify** lets the
tool execute; **deny** raises `GovernanceBlockedError` (`ESCALATE_DENY`);
**timeout** raises `ESCALATE_TIMEOUT`.

## Handling blocked runs

```python
from gaas_pydantic_ai import GovernanceBlockedError

try:
    result = await agent.run("wire $250k to the new vendor")
except GovernanceBlockedError as err:
    print(err.verdict)                  # BLOCK / ESCALATE / ESCALATE_DENY / ESCALATE_TIMEOUT
    print(err.decision_id)              # audit reference
    print(err.blocking_policies)        # policy IDs that triggered the block
    print(err.governance_proof_token)   # GPT token ID for the legal audit trail
```

## Links

- Docs: https://gaas.to/sdks.html
- GaaS: https://gaas.is
