> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwedai.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# QWED Open Responses: verified tool calls for AI agents

> QWED Open Responses adds verified tool calls, deterministic guards, and AI agent security checks to OpenAI, LangChain, and LlamaIndex workflows.

**Verify AI agent outputs before execution.**

[![PyPI version](https://img.shields.io/pypi/v/qwed-open-responses)](https://pypi.org/project/qwed-open-responses/)
[![Tests](https://github.com/QWED-AI/qwed-open-responses/actions/workflows/ci.yml/badge.svg)](https://github.com/QWED-AI/qwed-open-responses/actions)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

***

## What is QWED Open Responses?

QWED Open Responses provides **deterministic verification guards** for AI agent outputs. It works with:

* OpenAI Responses API
* LangChain agents
* LlamaIndex
* Any AI framework

Use it when you need runtime tool call verification, AI safety guardrails, and policy enforcement before an agent executes an action.

### The problem

When AI agents execute tools or generate structured outputs, they can:

* 🔧 **Call dangerous functions** - `rm -rf /`, `DROP TABLE`
* 🧮 **Produce incorrect calculations** - Financial errors, wrong totals
* 📋 **Violate business rules** - Invalid state transitions
* 🔐 **Leak sensitive data** - PII, API keys in responses
* 💰 **Exceed budgets** - Unlimited API calls

### The solution

QWED Open Responses intercepts and verifies every agent output before execution:

```
AI Agent Output → Guards → Verified? → Execute
                           │
                    YES ───┘
                    NO ────→ Block + Error
```

***

## How it works

```
┌─────────────────────────────────────────────────────────────────┐
│                    AI Agent (GPT, Claude, etc.)                  │
│                                                                  │
│  "Call calculator with x=150, y=10, result=1600"                 │
└──────────────────────────┬───────────────────────────────────────┘
                           │
                           │ Tool Call / Structured Output
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                QWED Open Responses Verifier                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌───────────────────────────┐│
│  │ SchemaGuard │  │  ToolGuard  │  │       MathGuard          ││
│  │  JSON Valid │  │  Blocklist  │  │  150 × 10 ≠ 1600 ❌      ││
│  └─────────────┘  └─────────────┘  └───────────────────────────┘│
│                                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌───────────────────────────┐│
│  │ StateGuard  │  │ArgumentGuard│  │      SafetyGuard         ││
│  │ Transitions │  │ Type Check  │  │  PII, Injection, Budget  ││
│  └─────────────┘  └─────────────┘  └───────────────────────────┘│
│                                                                  │
│               MathGuard Failed: 150 × 10 = 1500, not 1600       │
│                                                                  │
└──────────────────────────┬───────────────────────────────────────┘
                           │
                           ▼
                  ┌─────────────────┐
                  │  ❌ BLOCKED     │
                  │  Return error   │
                  └─────────────────┘
```

***

## The 6 guards

| Guard | What it verifies | Example catch |
| - | - | - |
| **SchemaGuard** | JSON Schema compliance | Missing required field |
| **ToolGuard** | Block dangerous tool calls | `execute_shell` blocked |
| **MathGuard** | Verify calculations | `150 × 10 ≠ 1600` |
| **StateGuard** | Valid state transitions | `completed → pending` invalid |
| **ArgumentGuard** | Tool argument validation | `amount: "abc"` not a number |
| **SafetyGuard** | PII, injection, budget | SSN detected in output |

***

## Installation

### Basic

```bash theme={null}
pip install qwed-open-responses
```

### With framework integrations

```bash theme={null}
# OpenAI
pip install qwed-open-responses[openai]

# LangChain
pip install qwed-open-responses[langchain]

# All integrations
pip install qwed-open-responses[all]
```

***

## Quick start

<Warning>
  Calling `verify()` with no guards configured fails closed. `ResponseVerifier()` without `default_guards` (or `VerifiedOpenAI` without `guards`) returns `verified=False` with the reason "No guards configured", and blocks the response in strict mode. Earlier versions returned `verified=True` in this case. Constructing `VerifiedOpenAI` without guards also emits a `UserWarning`, because fail-closed verification would block every response. Always configure at least one guard.
</Warning>

### Basic verification

```python theme={null}
from qwed_open_responses import ResponseVerifier
from qwed_open_responses.guards import ToolGuard, MathGuard, SafetyGuard

verifier = ResponseVerifier()

# Verify a tool call before execution
result = verifier.verify_tool_call(
    tool_name="calculator",
    arguments={
        "operation": "multiply",
        "x": 150,
        "y": 10,
        "result": 1500  # Correct!
    },
    guards=[ToolGuard(), MathGuard(), SafetyGuard()]
)

if result.verified:
    print("✅ Safe to execute")
    execute_tool(result.tool_name, result.arguments)
else:
    print(f"❌ Blocked: {result.block_reason}")
    print(f"   Failed guard: {result.failed_guard}")
```

### Verify structured output

```python theme={null}
from qwed_open_responses.guards import SchemaGuard

# Define expected schema
order_schema = {
    "type": "object",
    "required": ["order_id", "total", "items"],
    "properties": {
        "order_id": {"type": "string"},
        "total": {"type": "number", "minimum": 0},
        "items": {"type": "array", "minItems": 1}
    }
}

result = verifier.verify_structured_output(
    output={
        "order_id": "ORD-123",
        "total": 99.99,
        "items": [{"name": "Widget", "price": 99.99}]
    },
    guards=[SchemaGuard(schema=order_schema)]
)
```

`verify_structured_output` requires a `schema` or at least one guard. Calling it with neither raises `ValueError`, because the call would verify nothing. An explicitly supplied empty schema `{}` is honored as a valid JSON Schema that matches anything.

***

## Framework integration

### LangChain

```python theme={null}
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from qwed_open_responses.middleware.langchain import QWEDCallbackHandler

# Create callback with guards
callback = QWEDCallbackHandler(
    guards=[ToolGuard(), SafetyGuard()],
    block_on_failure=True,  # Stop execution if guard fails
)

# Add to agent
llm = ChatOpenAI(model="gpt-4")
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(
    agent=agent,
    tools=tools,
    callbacks=[callback]
)

# Every tool call is now verified!
result = executor.invoke({"input": "Calculate 25% of 500"})
```

### OpenAI SDK

```python theme={null}
from qwed_open_responses.middleware.openai_sdk import VerifiedOpenAI
from qwed_open_responses.guards import SchemaGuard, SafetyGuard

# Create verified client
client = VerifiedOpenAI(
    api_key="sk-...",
    guards=[
        SchemaGuard(schema=my_schema),
        SafetyGuard(block_pii=True)
    ]
)

# Use normally - verification is automatic
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Generate an order"}],
    tools=my_tools
)

# Tool calls are verified before returning
for tool_call in response.choices[0].message.tool_calls:
    print(f"Verified tool call: {tool_call.function.name}")
```

***

## Streaming middleware

The `OpenResponsesMiddleware` intercepts streaming events from the Open Responses protocol, verifying `tool_call` and `function_call` items through your guard stack before they are yielded to the consumer.

### Usage

```python theme={null}
from qwed_open_responses.middleware.streaming_interceptor import OpenResponsesMiddleware
from qwed_open_responses.guards import ToolGuard, SafetyGuard

middleware = OpenResponsesMiddleware(
    guards=[ToolGuard(), SafetyGuard()],
    block_on_failure=True,       # Replace blocked items with system_intervention
    on_blocked=my_callback,      # Optional callback when an item is blocked
)

# Wrap any async stream of Open Responses items
async for item in middleware.verify_stream(response_stream):
    process(item)

# Check runtime stats
print(middleware.get_stats())
# {"total": 12, "verified": 10, "blocked": 2}
```

### Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `guards` | `list[BaseGuard]` | `[]` | Guards to run against each tool-call item |
| `block_on_failure` | `bool` | `True` | When `True`, blocked items are replaced with a `system_intervention` item. When `False`, blocked items pass through unmodified (warn-only) |
| `on_blocked` | `callable` | `None` | Optional callback invoked with `(item, result)` when a tool call is blocked |

### How it works

1. Non-tool items (text, metadata) pass through unchanged
2. Items with `type` of `tool_call` or `function_call` are verified against all guards in their original shape, before the middleware yields them. The `type` match ignores case and surrounding whitespace, so `" Function_Call "` is verified as a `function_call`
3. Tool-shaped items that can't be verified unambiguously are blocked. This covers items with an unknown `type`, ambiguous envelopes, and tool calls nested inside other items
4. `tool_result` and `function_call_output` items pass through; the middleware does not validate their correlation metadata. Results that hide a nested tool call are blocked
5. If verification passes, the original item is yielded
6. If verification fails and `block_on_failure` is `True`, a `system_intervention` item is yielded instead:

```json theme={null}
{
  "type": "system_intervention",
  "status": "blocked",
  "tool_name": "execute_shell",
  "reason": "QWED blocked execute_shell: dangerous tool call",
  "verification": {
    "guards_passed": ["SafetyGuard"],
    "guards_failed": ["ToolGuard"],
    "mechanism": "QWED Open Responses Streaming Interceptor"
  }
}
```

ArgumentGuard and TaxGuard checks also run on batched and nested tool calls inside a streamed item, not only on the top-level call.

### Budget tracking across a stream

Pass a `context` to `verify_stream` to enforce SafetyGuard budget limits for the whole stream:

```python theme={null}
middleware = OpenResponsesMiddleware(
    guards=[ToolGuard(), SafetyGuard(max_cost=5.0, max_tokens=20_000)],
)

async for item in middleware.verify_stream(
    response_stream,
    context={"total_cost": 1.25, "total_tokens": 4_000},
):
    process(item)
```

The middleware copies the context and adds each item's reported `usage.cost` and `usage.total_tokens` to the running `total_cost` and `total_tokens`. Later tool calls are verified against those totals, so a call that arrives after the budget is spent is blocked. Your original context dictionary is not modified.

### Stats methods

| Method | Description |
| - | - |
| `get_stats()` | Returns `{"total": int, "verified": int, "blocked": int}` |
| `reset_stats()` | Resets all counters to zero |

***

## Why QWED Open Responses?

### Security comparison

| Threat | Without Verification | With QWED |
| - | - | - |
| Agent calls `rm -rf /` | 💀 System destroyed | ✅ **BLOCKED** |
| SQL injection in query | 💀 Data breach | ✅ **BLOCKED** |
| Wrong calculation | 💸 Financial loss | ✅ **CAUGHT** |
| PII in API response | 📋 Compliance violation | ✅ **DETECTED** |
| Infinite tool loop | 💰 \$10,000 API bill | ✅ **BUDGET GUARD** |

### Real-world impact

* **Finance:** Prevent wrong calculations in trading bots
* **Healthcare:** Block PII leaks in patient summaries
* **E-commerce:** Verify order totals before payment
* **DevOps:** Prevent dangerous shell commands

***

## Configuration

### Environment variables

| Variable | Description | Default |
| - | - | - |
| `QWED_OR_LOG_LEVEL` | Logging level | `INFO` |
| `QWED_OR_STRICT` | Fail on any guard failure | `true` |
| `QWED_OR_MAX_BUDGET` | Maximum API cost allowed | `100.0` |

### Custom guard configuration

```python theme={null}
from qwed_open_responses.guards import ToolGuard, SafetyGuard

# Custom tool blocklist
tool_guard = ToolGuard(
    blocklist=["execute_shell", "delete_database", "send_email"],
    allow_unknown=False  # Block tools not in whitelist
)

# Custom safety settings
safety_guard = SafetyGuard(
    block_pii=True,
    block_injection=True,
    max_budget=50.0,  # $50 limit
    harmful_patterns=["password", "secret", "token"]
)

verifier = ResponseVerifier(guards=[tool_guard, safety_guard])
```

***

## Next steps

* [Guards reference](./guards) - Deep dive into each guard
* [Examples](./examples) - Real-world use cases
* [LangChain integration](./langchain) - Agent verification
* [OpenAI integration](./openai) - Responses API
* [Troubleshooting](./troubleshooting) - Common issues

***

## Links

* **GitHub:** [QWED-AI/qwed-open-responses](https://github.com/QWED-AI/qwed-open-responses)
* **PyPI:** [qwed-open-responses](https://pypi.org/project/qwed-open-responses/)
* **npm:** [qwed-open-responses](https://www.npmjs.com/package/qwed-open-responses)


## Related topics

- [QWED and Guardrails AI](/advanced/qwed-vs-guardrails.md)
- [LLM verification with formal methods](/advanced/llm-verification.md)
- [QWED Open Responses guards for verified AI agents](/open-responses/guards.md)
- [QWED Open Responses examples for verified AI agents](/open-responses/examples.md)
- [OpenAI integration for verified AI agents](/open-responses/openai.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.