> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwedai.com/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

### 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)]
)
```

***

## 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
3. If verification passes, the original item is yielded
4. 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"
  }
}
```

### 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)
