Metadata-Version: 2.5
Name: gaas-agent-framework
Version: 0.1.0
Summary: Microsoft Agent Framework 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: agent-framework,agents,ai,autogen,gaas,governance,microsoft
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: agent-framework
Requires-Dist: agent-framework-core<2,>=1.15; extra == 'agent-framework'
Provides-Extra: all
Requires-Dist: agent-framework-core<2,>=1.15; extra == 'all'
Provides-Extra: dev
Requires-Dist: agent-framework-core<2,>=1.15; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Description-Content-Type: text/markdown

# gaas-agent-framework

GaaS (Governance as a Service) integration for
[Microsoft Agent Framework](https://github.com/microsoft/agent-framework) (Python, 1.15 or later),
the successor to AutoGen.

Every tool call is governed **before it runs**. The middleware submits a governance intent to
GaaS, and only an APPROVE (or an approved escalation) lets the tool run. On BLOCK,
`GovernanceBlockedError` stops `agent.run()`. It is an Agent Framework `MiddlewareFailure`, the one
exception the framework lets abort a run from function middleware; any other exception would be
turned into a tool result and the agent would carry on.

```bash
pip install "gaas-agent-framework[agent-framework]"
```

> **Fails closed by default.** If GaaS cannot give a decision (unreachable, timeout, any HTTP
> error including a wrong API key), the tool does **not** run. Set `fail_open=True` to run it
> anyway. See [When GaaS can't answer](#when-gaas-cant-answer).

## Quickstart

<!-- doc-test: quickstart -->
```python
from agent_framework import Agent, tool
from gaas_agent_framework import GaaSGovernanceConfig, GaaSGovernanceMiddleware

@tool
def search_web(query: str) -> str:
    """Search the web."""
    return f"Results for {query}"

@tool
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email."""
    return f"Sent to {to}"

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

agent = Agent(
    client=chat_client,  # any Agent Framework chat client, e.g. OpenAIChatClient()
    tools=[search_web, send_email],
    middleware=[GaaSGovernanceMiddleware(config)],
)

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

The middleware can also be attached to a single run (`agent.run(..., middleware=[...])`) or to a
chat client. A runnable script is in
[`examples/agent_framework_quickstart.py`](examples/agent_framework_quickstart.py).

## Verdict flow

```
tool call → GaaS intent → ┌─────────┐
                          │ APPROVE │ → tool runs
                          │ BLOCK   │ → GovernanceBlockedError stops agent.run()
                          │ ESCALATE│ → treated as a block by default; hold-and-poll optional
                          │NO ANSWER│ → GovernanceBlockedError (UNEVALUATED) stops agent.run()
                          └─────────┘
```

### Tell the model instead of stopping

With `on_block="inform"`, a call GaaS does not allow still never runs, but the run continues and
the model receives a short notice as the tool's result ("The tool 'send_email' was not run: GaaS
governance did not allow it (verdict BLOCK, decision dec_…, policies pol_…)."). The model can then
explain or choose another step. The default, `on_block="stop"`, is the safest choice.

## 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,           # Treat ESCALATE as a block (default)
    timeout_seconds=240.0,            # Covers a deliberated decision (about 40-60 s)
    sensitivity="INTERNAL",           # Default sensitivity for tool inputs
    fail_open=False,                  # No decision from GaaS → the tool does not run (default)
    raise_on_governance_error=False,  # True: raise GaaSGovernanceError instead
    on_block="stop",                  # or "inform": tell the model and continue
    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_on_block=False,              # Wait for a person to approve a BLOCK, then retry once
    block_poll_seconds=10.0,
    block_max_wait_seconds=900.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 run; **deny** is a block
(`ESCALATE_DENY`); **timeout** is a block (`ESCALATE_TIMEOUT`).

### Hold on BLOCK

With `hold_on_block=True`, a BLOCK waits for a person to approve the action (from the block email
or the dashboard). If they do, the call is submitted once more with the approval attached, and that
second verdict decides. Not approved in time, or still blocked: a block, as without the setting.

## Handling blocked runs

<!-- doc-test: handling -->
```python
from gaas_agent_framework 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 / UNEVALUATED
    print(err.reason)                   # UNEVALUATED only, e.g. "HTTP 401", "timeout"
    print(err.decision_id)              # audit reference
    print(err.blocking_policies)        # policy IDs that triggered the block
    print(err.governance_proof_token)   # proof token ID for the audit trail
```

## When GaaS can't answer

If GaaS gives no decision (a network error, a timeout, any HTTP status of 400 or above, where a
wrong API key is a 401, or a response without a verdict), the middleware **fails closed**: the tool
does not run, and `GovernanceBlockedError` is raised with `verdict == "UNEVALUATED"` and a short
`reason` such as `"HTTP 401"` or `"timeout"`. It stops `agent.run()` exactly as a BLOCK does (or,
with `on_block="inform"`, the model is told). The API key never appears in the message or the logs.

Three settings, checked in this order:

| Setting | When GaaS gives no decision |
|---|---|
| `raise_on_governance_error=True` | `GaaSGovernanceError` is raised, with the underlying error (e.g. `httpx.HTTPStatusError`, `httpx.ReadTimeout`) as its `__cause__`. |
| `fail_open=True` | The tool runs anyway, ungoverned, and a WARNING is logged on the `gaas_agent_framework` logger. |
| neither (default) | The tool does not run; `GovernanceBlockedError` with verdict `UNEVALUATED`. |

`GaaSGovernanceError` wraps the underlying error instead of re-raising it (as the other GaaS
plugins do) because Agent Framework turns any ordinary exception from middleware into a tool
result and keeps the run going.

## Notes

- Tools the model provider runs on its side (hosted tools such as hosted web search or hosted MCP)
  never pass through function middleware, so GaaS cannot govern them here. Local tools, including
  local MCP tools, are governed.
- Intents are sent with `agent.framework = "custom"`, and carry the model's tool-call id and the
  Agent Framework session id when present, so an audit record can be matched to a run.
- `APPROVE_MODIFIED` runs the tool with its original arguments; modifications are not applied.

## Links

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