forge-openai 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- forge_openai-0.1.0/PKG-INFO +125 -0
- forge_openai-0.1.0/README.md +104 -0
- forge_openai-0.1.0/forge_openai/__init__.py +36 -0
- forge_openai-0.1.0/forge_openai/guardrail.py +312 -0
- forge_openai-0.1.0/forge_openai.egg-info/PKG-INFO +125 -0
- forge_openai-0.1.0/forge_openai.egg-info/SOURCES.txt +9 -0
- forge_openai-0.1.0/forge_openai.egg-info/dependency_links.txt +1 -0
- forge_openai-0.1.0/forge_openai.egg-info/requires.txt +2 -0
- forge_openai-0.1.0/forge_openai.egg-info/top_level.txt +1 -0
- forge_openai-0.1.0/pyproject.toml +33 -0
- forge_openai-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forge-openai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Forge Verify guardrail for OpenAI Agents SDK — verify every tool call before execution
|
|
5
|
+
Author-email: Veritera AI <engineering@veritera.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://veritera.ai
|
|
8
|
+
Project-URL: Documentation, https://veritera.ai/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/VeriteraAI/forge-openai
|
|
10
|
+
Keywords: veritera,forge,openai,agents,guardrail,verification,ai-safety
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: veritera>=0.2.0
|
|
20
|
+
Requires-Dist: openai-agents>=0.1.0
|
|
21
|
+
|
|
22
|
+
# forge-openai
|
|
23
|
+
|
|
24
|
+
Forge Verify guardrail for the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python). Verifies every AI agent tool call against your policies **before** execution.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install forge-openai
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import os
|
|
36
|
+
from agents import Agent, Runner, function_tool
|
|
37
|
+
from forge_openai import forge_tool_guardrail, forge_protect
|
|
38
|
+
|
|
39
|
+
os.environ["VERITERA_API_KEY"] = "vt_live_..."
|
|
40
|
+
os.environ["OPENAI_API_KEY"] = "sk-..."
|
|
41
|
+
|
|
42
|
+
# Define your tools
|
|
43
|
+
@function_tool
|
|
44
|
+
def send_payment(amount: float, recipient: str) -> str:
|
|
45
|
+
"""Send a payment to a recipient."""
|
|
46
|
+
return f"Sent ${amount} to {recipient}"
|
|
47
|
+
|
|
48
|
+
@function_tool
|
|
49
|
+
def delete_record(record_id: str) -> str:
|
|
50
|
+
"""Delete a database record."""
|
|
51
|
+
return f"Deleted {record_id}"
|
|
52
|
+
|
|
53
|
+
@function_tool
|
|
54
|
+
def read_balance() -> str:
|
|
55
|
+
"""Check account balance."""
|
|
56
|
+
return "Balance: $50,000"
|
|
57
|
+
|
|
58
|
+
# Protect all tools with Forge — one line
|
|
59
|
+
agent = Agent(
|
|
60
|
+
name="finance-bot",
|
|
61
|
+
instructions="You help with financial operations.",
|
|
62
|
+
tools=forge_protect(
|
|
63
|
+
send_payment, delete_record, read_balance,
|
|
64
|
+
policy="finance-controls",
|
|
65
|
+
skip_actions=["read_balance"], # read-only tools don't need verification
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Run the agent — Forge checks every tool call automatically
|
|
70
|
+
result = await Runner.run(agent, "Send $500 to vendor@acme.com")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## How It Works
|
|
74
|
+
|
|
75
|
+
1. Your agent decides to call a tool (e.g. `send_payment`)
|
|
76
|
+
2. **Before execution**, Forge checks the action against your policies
|
|
77
|
+
3. If **approved**: the tool runs normally
|
|
78
|
+
4. If **denied**: the LLM receives a denial message and can explain why to the user
|
|
79
|
+
|
|
80
|
+
No tool ever executes without verification. Every decision is logged with a cryptographic proof.
|
|
81
|
+
|
|
82
|
+
## Per-Tool Guardrail
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from forge_openai import forge_tool_guardrail
|
|
86
|
+
|
|
87
|
+
guardrail = forge_tool_guardrail(policy="email-controls")
|
|
88
|
+
|
|
89
|
+
@function_tool(tool_input_guardrails=[guardrail])
|
|
90
|
+
def send_email(to: str, subject: str, body: str) -> str:
|
|
91
|
+
"""Send an email."""
|
|
92
|
+
return f"Email sent to {to}"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Full Control
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from forge_openai import ForgeGuardrail
|
|
99
|
+
|
|
100
|
+
forge = ForgeGuardrail(
|
|
101
|
+
api_key="vt_live_...",
|
|
102
|
+
agent_id="prod-finance-bot",
|
|
103
|
+
policy="finance-controls",
|
|
104
|
+
fail_closed=True,
|
|
105
|
+
skip_actions=["read_balance", "get_time"],
|
|
106
|
+
on_blocked=lambda action, reason, result: print(f"BLOCKED: {action} — {reason}"),
|
|
107
|
+
on_verified=lambda action, result: print(f"APPROVED: {action}"),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
agent = Agent(
|
|
111
|
+
name="finance-bot",
|
|
112
|
+
tools=forge.protect(send_payment, delete_record),
|
|
113
|
+
input_guardrails=[forge.input_guardrail()], # also screen agent input
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Environment Variables
|
|
118
|
+
|
|
119
|
+
| Variable | Description |
|
|
120
|
+
|----------|-------------|
|
|
121
|
+
| `VERITERA_API_KEY` | Your Forge API key (starts with `vt_live_` or `vt_test_`) |
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT — [Veritera AI](https://veritera.ai)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# forge-openai
|
|
2
|
+
|
|
3
|
+
Forge Verify guardrail for the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python). Verifies every AI agent tool call against your policies **before** execution.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install forge-openai
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import os
|
|
15
|
+
from agents import Agent, Runner, function_tool
|
|
16
|
+
from forge_openai import forge_tool_guardrail, forge_protect
|
|
17
|
+
|
|
18
|
+
os.environ["VERITERA_API_KEY"] = "vt_live_..."
|
|
19
|
+
os.environ["OPENAI_API_KEY"] = "sk-..."
|
|
20
|
+
|
|
21
|
+
# Define your tools
|
|
22
|
+
@function_tool
|
|
23
|
+
def send_payment(amount: float, recipient: str) -> str:
|
|
24
|
+
"""Send a payment to a recipient."""
|
|
25
|
+
return f"Sent ${amount} to {recipient}"
|
|
26
|
+
|
|
27
|
+
@function_tool
|
|
28
|
+
def delete_record(record_id: str) -> str:
|
|
29
|
+
"""Delete a database record."""
|
|
30
|
+
return f"Deleted {record_id}"
|
|
31
|
+
|
|
32
|
+
@function_tool
|
|
33
|
+
def read_balance() -> str:
|
|
34
|
+
"""Check account balance."""
|
|
35
|
+
return "Balance: $50,000"
|
|
36
|
+
|
|
37
|
+
# Protect all tools with Forge — one line
|
|
38
|
+
agent = Agent(
|
|
39
|
+
name="finance-bot",
|
|
40
|
+
instructions="You help with financial operations.",
|
|
41
|
+
tools=forge_protect(
|
|
42
|
+
send_payment, delete_record, read_balance,
|
|
43
|
+
policy="finance-controls",
|
|
44
|
+
skip_actions=["read_balance"], # read-only tools don't need verification
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Run the agent — Forge checks every tool call automatically
|
|
49
|
+
result = await Runner.run(agent, "Send $500 to vendor@acme.com")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## How It Works
|
|
53
|
+
|
|
54
|
+
1. Your agent decides to call a tool (e.g. `send_payment`)
|
|
55
|
+
2. **Before execution**, Forge checks the action against your policies
|
|
56
|
+
3. If **approved**: the tool runs normally
|
|
57
|
+
4. If **denied**: the LLM receives a denial message and can explain why to the user
|
|
58
|
+
|
|
59
|
+
No tool ever executes without verification. Every decision is logged with a cryptographic proof.
|
|
60
|
+
|
|
61
|
+
## Per-Tool Guardrail
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from forge_openai import forge_tool_guardrail
|
|
65
|
+
|
|
66
|
+
guardrail = forge_tool_guardrail(policy="email-controls")
|
|
67
|
+
|
|
68
|
+
@function_tool(tool_input_guardrails=[guardrail])
|
|
69
|
+
def send_email(to: str, subject: str, body: str) -> str:
|
|
70
|
+
"""Send an email."""
|
|
71
|
+
return f"Email sent to {to}"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Full Control
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from forge_openai import ForgeGuardrail
|
|
78
|
+
|
|
79
|
+
forge = ForgeGuardrail(
|
|
80
|
+
api_key="vt_live_...",
|
|
81
|
+
agent_id="prod-finance-bot",
|
|
82
|
+
policy="finance-controls",
|
|
83
|
+
fail_closed=True,
|
|
84
|
+
skip_actions=["read_balance", "get_time"],
|
|
85
|
+
on_blocked=lambda action, reason, result: print(f"BLOCKED: {action} — {reason}"),
|
|
86
|
+
on_verified=lambda action, result: print(f"APPROVED: {action}"),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
agent = Agent(
|
|
90
|
+
name="finance-bot",
|
|
91
|
+
tools=forge.protect(send_payment, delete_record),
|
|
92
|
+
input_guardrails=[forge.input_guardrail()], # also screen agent input
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Environment Variables
|
|
97
|
+
|
|
98
|
+
| Variable | Description |
|
|
99
|
+
|----------|-------------|
|
|
100
|
+
| `VERITERA_API_KEY` | Your Forge API key (starts with `vt_live_` or `vt_test_`) |
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT — [Veritera AI](https://veritera.ai)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Forge Verify guardrail for OpenAI Agents SDK.
|
|
2
|
+
|
|
3
|
+
Verify every AI agent tool call against your policies before execution.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
from forge_openai import forge_tool_guardrail, forge_protect
|
|
7
|
+
|
|
8
|
+
# Option 1: Attach guardrail to individual tools
|
|
9
|
+
guardrail = forge_tool_guardrail(policy="finance-controls")
|
|
10
|
+
|
|
11
|
+
@function_tool(tool_input_guardrails=[guardrail])
|
|
12
|
+
def send_payment(amount: float, to: str) -> str:
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
# Option 2: Protect all tools at once
|
|
16
|
+
agent = Agent(
|
|
17
|
+
name="finance-bot",
|
|
18
|
+
tools=forge_protect(send_payment, read_balance, policy="finance-controls"),
|
|
19
|
+
)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
23
|
+
|
|
24
|
+
from .guardrail import (
|
|
25
|
+
ForgeGuardrail,
|
|
26
|
+
forge_tool_guardrail,
|
|
27
|
+
forge_protect,
|
|
28
|
+
forge_input_guardrail,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"ForgeGuardrail",
|
|
33
|
+
"forge_tool_guardrail",
|
|
34
|
+
"forge_protect",
|
|
35
|
+
"forge_input_guardrail",
|
|
36
|
+
]
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Forge Verify guardrails for OpenAI Agents SDK.
|
|
2
|
+
|
|
3
|
+
Provides ToolInputGuardrail (pre-tool) and InputGuardrail (pre-agent)
|
|
4
|
+
that call the Forge /v1/verify API before execution proceeds.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import copy
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
|
|
15
|
+
from veritera import Forge, ForgeError
|
|
16
|
+
|
|
17
|
+
from agents import (
|
|
18
|
+
Agent,
|
|
19
|
+
FunctionTool,
|
|
20
|
+
GuardrailFunctionOutput,
|
|
21
|
+
InputGuardrail,
|
|
22
|
+
RunContextWrapper,
|
|
23
|
+
ToolInputGuardrail,
|
|
24
|
+
ToolInputGuardrailData,
|
|
25
|
+
ToolGuardrailFunctionOutput,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("forge_openai")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ForgeGuardrail:
|
|
32
|
+
"""Configurable Forge verification client for OpenAI Agents SDK.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
api_key: Forge API key (or set VERITERA_API_KEY env var).
|
|
36
|
+
base_url: Forge API endpoint.
|
|
37
|
+
agent_id: Identifier for this agent in Forge audit logs.
|
|
38
|
+
policy: Default policy to evaluate against.
|
|
39
|
+
fail_closed: If True (default), deny actions when Forge API is unreachable.
|
|
40
|
+
timeout: Request timeout in seconds.
|
|
41
|
+
skip_actions: Tool names to skip verification for (e.g. read-only tools).
|
|
42
|
+
on_verified: Callback(action, result) when a tool call is approved.
|
|
43
|
+
on_blocked: Callback(action, reason, result) when a tool call is denied.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
api_key: Optional[str] = None,
|
|
49
|
+
base_url: str = "https://veritera.ai",
|
|
50
|
+
agent_id: str = "openai-agent",
|
|
51
|
+
policy: Optional[str] = None,
|
|
52
|
+
fail_closed: bool = True,
|
|
53
|
+
timeout: float = 10.0,
|
|
54
|
+
skip_actions: Optional[list[str]] = None,
|
|
55
|
+
on_verified: Optional[Any] = None,
|
|
56
|
+
on_blocked: Optional[Any] = None,
|
|
57
|
+
):
|
|
58
|
+
key = api_key or os.environ.get("VERITERA_API_KEY", "")
|
|
59
|
+
if not key:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
"Forge API key required. Pass api_key= or set VERITERA_API_KEY env var."
|
|
62
|
+
)
|
|
63
|
+
self._client = Forge(
|
|
64
|
+
api_key=key,
|
|
65
|
+
base_url=base_url,
|
|
66
|
+
timeout=timeout,
|
|
67
|
+
fail_closed=fail_closed,
|
|
68
|
+
)
|
|
69
|
+
self.agent_id = agent_id
|
|
70
|
+
self.policy = policy
|
|
71
|
+
self.fail_closed = fail_closed
|
|
72
|
+
self.skip_actions = set(skip_actions or [])
|
|
73
|
+
self.on_verified = on_verified
|
|
74
|
+
self.on_blocked = on_blocked
|
|
75
|
+
|
|
76
|
+
def tool_guardrail(self) -> ToolInputGuardrail:
|
|
77
|
+
"""Create a ToolInputGuardrail that verifies each tool call through Forge.
|
|
78
|
+
|
|
79
|
+
Attach to individual tools:
|
|
80
|
+
@function_tool(tool_input_guardrails=[guard.tool_guardrail()])
|
|
81
|
+
def send_email(...): ...
|
|
82
|
+
|
|
83
|
+
Or use forge_protect() to attach to multiple tools at once.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
async def _verify_tool(
|
|
87
|
+
data: ToolInputGuardrailData,
|
|
88
|
+
) -> ToolGuardrailFunctionOutput:
|
|
89
|
+
tool_name = data.context.tool_name
|
|
90
|
+
|
|
91
|
+
# Skip configured actions
|
|
92
|
+
if tool_name in self.skip_actions:
|
|
93
|
+
return ToolGuardrailFunctionOutput.allow(
|
|
94
|
+
output_info={"skipped": True, "action": tool_name}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Parse tool arguments
|
|
98
|
+
try:
|
|
99
|
+
params = (
|
|
100
|
+
json.loads(data.context.tool_arguments)
|
|
101
|
+
if data.context.tool_arguments
|
|
102
|
+
else {}
|
|
103
|
+
)
|
|
104
|
+
except (json.JSONDecodeError, TypeError):
|
|
105
|
+
params = {"raw": data.context.tool_arguments}
|
|
106
|
+
|
|
107
|
+
# Call Forge /v1/verify
|
|
108
|
+
try:
|
|
109
|
+
result = await self._client.verify_decision(
|
|
110
|
+
agent_id=self.agent_id,
|
|
111
|
+
action=tool_name,
|
|
112
|
+
params=params,
|
|
113
|
+
policy=self.policy,
|
|
114
|
+
)
|
|
115
|
+
except Exception as exc:
|
|
116
|
+
logger.error("Forge verify error for %s: %s", tool_name, exc)
|
|
117
|
+
if self.fail_closed:
|
|
118
|
+
if self.on_blocked:
|
|
119
|
+
self.on_blocked(tool_name, str(exc), None)
|
|
120
|
+
return ToolGuardrailFunctionOutput.reject_content(
|
|
121
|
+
message=f"Action '{tool_name}' blocked — policy verification unavailable.",
|
|
122
|
+
output_info={"error": str(exc), "fail_mode": "closed"},
|
|
123
|
+
)
|
|
124
|
+
return ToolGuardrailFunctionOutput.allow(
|
|
125
|
+
output_info={"error": str(exc), "fail_mode": "open"}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if result.verified:
|
|
129
|
+
logger.debug("Forge APPROVED: %s (proof=%s)", tool_name, result.proof_id)
|
|
130
|
+
if self.on_verified:
|
|
131
|
+
self.on_verified(tool_name, result)
|
|
132
|
+
return ToolGuardrailFunctionOutput.allow(
|
|
133
|
+
output_info={
|
|
134
|
+
"verified": True,
|
|
135
|
+
"proof_id": result.proof_id,
|
|
136
|
+
"latency_ms": result.latency_ms,
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
reason = result.reason or "Policy violation"
|
|
141
|
+
logger.warning("Forge DENIED: %s — %s", tool_name, reason)
|
|
142
|
+
if self.on_blocked:
|
|
143
|
+
self.on_blocked(tool_name, reason, result)
|
|
144
|
+
return ToolGuardrailFunctionOutput.reject_content(
|
|
145
|
+
message=f"Action '{tool_name}' denied by Forge: {reason}",
|
|
146
|
+
output_info={
|
|
147
|
+
"verified": False,
|
|
148
|
+
"reason": reason,
|
|
149
|
+
"proof_id": result.proof_id,
|
|
150
|
+
"latency_ms": result.latency_ms,
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return ToolInputGuardrail(
|
|
155
|
+
guardrail_function=_verify_tool,
|
|
156
|
+
name="forge_verify",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def input_guardrail(self) -> InputGuardrail:
|
|
160
|
+
"""Create an InputGuardrail that screens agent input through Forge.
|
|
161
|
+
|
|
162
|
+
Useful for blocking entire conversation turns based on policy.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
async def _screen_input(
|
|
166
|
+
ctx: RunContextWrapper, agent: Agent, input: Any
|
|
167
|
+
) -> GuardrailFunctionOutput:
|
|
168
|
+
input_text = str(input) if input else ""
|
|
169
|
+
try:
|
|
170
|
+
result = await self._client.verify_decision(
|
|
171
|
+
agent_id=self.agent_id,
|
|
172
|
+
action="agent.input",
|
|
173
|
+
params={"input": input_text[:2000]},
|
|
174
|
+
policy=self.policy,
|
|
175
|
+
)
|
|
176
|
+
return GuardrailFunctionOutput(
|
|
177
|
+
output_info={
|
|
178
|
+
"verified": result.verified,
|
|
179
|
+
"reason": result.reason,
|
|
180
|
+
},
|
|
181
|
+
tripwire_triggered=not result.verified,
|
|
182
|
+
)
|
|
183
|
+
except Exception as exc:
|
|
184
|
+
logger.error("Forge input guardrail error: %s", exc)
|
|
185
|
+
return GuardrailFunctionOutput(
|
|
186
|
+
output_info={"error": str(exc)},
|
|
187
|
+
tripwire_triggered=self.fail_closed,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
return InputGuardrail(
|
|
191
|
+
guardrail_function=_screen_input,
|
|
192
|
+
name="forge_input_screen",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def protect(self, *tools: FunctionTool, policy: Optional[str] = None) -> list[FunctionTool]:
|
|
196
|
+
"""Attach Forge guardrail to multiple tools at once.
|
|
197
|
+
|
|
198
|
+
Returns new tool instances — originals are not mutated.
|
|
199
|
+
"""
|
|
200
|
+
guardrail = self.tool_guardrail()
|
|
201
|
+
if policy and policy != self.policy:
|
|
202
|
+
# Create a separate guardrail with the override policy
|
|
203
|
+
original_policy = self.policy
|
|
204
|
+
self.policy = policy
|
|
205
|
+
guardrail = self.tool_guardrail()
|
|
206
|
+
self.policy = original_policy
|
|
207
|
+
|
|
208
|
+
protected = []
|
|
209
|
+
for tool in tools:
|
|
210
|
+
t = copy.copy(tool)
|
|
211
|
+
existing = list(t.tool_input_guardrails or [])
|
|
212
|
+
existing.append(guardrail)
|
|
213
|
+
t.tool_input_guardrails = existing
|
|
214
|
+
protected.append(t)
|
|
215
|
+
return protected
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# ── Convenience functions ──
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def forge_tool_guardrail(
|
|
222
|
+
api_key: Optional[str] = None,
|
|
223
|
+
base_url: str = "https://veritera.ai",
|
|
224
|
+
agent_id: str = "openai-agent",
|
|
225
|
+
policy: Optional[str] = None,
|
|
226
|
+
fail_closed: bool = True,
|
|
227
|
+
timeout: float = 10.0,
|
|
228
|
+
skip_actions: Optional[list[str]] = None,
|
|
229
|
+
on_verified: Optional[Any] = None,
|
|
230
|
+
on_blocked: Optional[Any] = None,
|
|
231
|
+
) -> ToolInputGuardrail:
|
|
232
|
+
"""Create a ToolInputGuardrail that verifies tool calls through Forge.
|
|
233
|
+
|
|
234
|
+
Usage:
|
|
235
|
+
guardrail = forge_tool_guardrail(policy="finance-controls")
|
|
236
|
+
|
|
237
|
+
@function_tool(tool_input_guardrails=[guardrail])
|
|
238
|
+
def send_payment(amount: float, recipient: str) -> str:
|
|
239
|
+
\"\"\"Send a payment.\"\"\"
|
|
240
|
+
return process_payment(amount, recipient)
|
|
241
|
+
"""
|
|
242
|
+
guard = ForgeGuardrail(
|
|
243
|
+
api_key=api_key,
|
|
244
|
+
base_url=base_url,
|
|
245
|
+
agent_id=agent_id,
|
|
246
|
+
policy=policy,
|
|
247
|
+
fail_closed=fail_closed,
|
|
248
|
+
timeout=timeout,
|
|
249
|
+
skip_actions=skip_actions,
|
|
250
|
+
on_verified=on_verified,
|
|
251
|
+
on_blocked=on_blocked,
|
|
252
|
+
)
|
|
253
|
+
return guard.tool_guardrail()
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def forge_input_guardrail(
|
|
257
|
+
api_key: Optional[str] = None,
|
|
258
|
+
base_url: str = "https://veritera.ai",
|
|
259
|
+
agent_id: str = "openai-agent",
|
|
260
|
+
policy: Optional[str] = None,
|
|
261
|
+
fail_closed: bool = True,
|
|
262
|
+
) -> InputGuardrail:
|
|
263
|
+
"""Create an InputGuardrail that screens agent input through Forge.
|
|
264
|
+
|
|
265
|
+
Usage:
|
|
266
|
+
agent = Agent(
|
|
267
|
+
name="my-agent",
|
|
268
|
+
input_guardrails=[forge_input_guardrail(policy="content-policy")],
|
|
269
|
+
tools=[...],
|
|
270
|
+
)
|
|
271
|
+
"""
|
|
272
|
+
guard = ForgeGuardrail(
|
|
273
|
+
api_key=api_key,
|
|
274
|
+
base_url=base_url,
|
|
275
|
+
agent_id=agent_id,
|
|
276
|
+
policy=policy,
|
|
277
|
+
fail_closed=fail_closed,
|
|
278
|
+
)
|
|
279
|
+
return guard.input_guardrail()
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def forge_protect(
|
|
283
|
+
*tools: FunctionTool,
|
|
284
|
+
api_key: Optional[str] = None,
|
|
285
|
+
base_url: str = "https://veritera.ai",
|
|
286
|
+
agent_id: str = "openai-agent",
|
|
287
|
+
policy: Optional[str] = None,
|
|
288
|
+
fail_closed: bool = True,
|
|
289
|
+
timeout: float = 10.0,
|
|
290
|
+
skip_actions: Optional[list[str]] = None,
|
|
291
|
+
) -> list[FunctionTool]:
|
|
292
|
+
"""Attach Forge verification to multiple tools at once.
|
|
293
|
+
|
|
294
|
+
Usage:
|
|
295
|
+
agent = Agent(
|
|
296
|
+
name="finance-bot",
|
|
297
|
+
tools=forge_protect(
|
|
298
|
+
send_payment, read_balance, delete_record,
|
|
299
|
+
policy="finance-controls",
|
|
300
|
+
),
|
|
301
|
+
)
|
|
302
|
+
"""
|
|
303
|
+
guard = ForgeGuardrail(
|
|
304
|
+
api_key=api_key,
|
|
305
|
+
base_url=base_url,
|
|
306
|
+
agent_id=agent_id,
|
|
307
|
+
policy=policy,
|
|
308
|
+
fail_closed=fail_closed,
|
|
309
|
+
timeout=timeout,
|
|
310
|
+
skip_actions=skip_actions,
|
|
311
|
+
)
|
|
312
|
+
return guard.protect(*tools)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forge-openai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Forge Verify guardrail for OpenAI Agents SDK — verify every tool call before execution
|
|
5
|
+
Author-email: Veritera AI <engineering@veritera.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://veritera.ai
|
|
8
|
+
Project-URL: Documentation, https://veritera.ai/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/VeriteraAI/forge-openai
|
|
10
|
+
Keywords: veritera,forge,openai,agents,guardrail,verification,ai-safety
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: veritera>=0.2.0
|
|
20
|
+
Requires-Dist: openai-agents>=0.1.0
|
|
21
|
+
|
|
22
|
+
# forge-openai
|
|
23
|
+
|
|
24
|
+
Forge Verify guardrail for the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python). Verifies every AI agent tool call against your policies **before** execution.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install forge-openai
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import os
|
|
36
|
+
from agents import Agent, Runner, function_tool
|
|
37
|
+
from forge_openai import forge_tool_guardrail, forge_protect
|
|
38
|
+
|
|
39
|
+
os.environ["VERITERA_API_KEY"] = "vt_live_..."
|
|
40
|
+
os.environ["OPENAI_API_KEY"] = "sk-..."
|
|
41
|
+
|
|
42
|
+
# Define your tools
|
|
43
|
+
@function_tool
|
|
44
|
+
def send_payment(amount: float, recipient: str) -> str:
|
|
45
|
+
"""Send a payment to a recipient."""
|
|
46
|
+
return f"Sent ${amount} to {recipient}"
|
|
47
|
+
|
|
48
|
+
@function_tool
|
|
49
|
+
def delete_record(record_id: str) -> str:
|
|
50
|
+
"""Delete a database record."""
|
|
51
|
+
return f"Deleted {record_id}"
|
|
52
|
+
|
|
53
|
+
@function_tool
|
|
54
|
+
def read_balance() -> str:
|
|
55
|
+
"""Check account balance."""
|
|
56
|
+
return "Balance: $50,000"
|
|
57
|
+
|
|
58
|
+
# Protect all tools with Forge — one line
|
|
59
|
+
agent = Agent(
|
|
60
|
+
name="finance-bot",
|
|
61
|
+
instructions="You help with financial operations.",
|
|
62
|
+
tools=forge_protect(
|
|
63
|
+
send_payment, delete_record, read_balance,
|
|
64
|
+
policy="finance-controls",
|
|
65
|
+
skip_actions=["read_balance"], # read-only tools don't need verification
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Run the agent — Forge checks every tool call automatically
|
|
70
|
+
result = await Runner.run(agent, "Send $500 to vendor@acme.com")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## How It Works
|
|
74
|
+
|
|
75
|
+
1. Your agent decides to call a tool (e.g. `send_payment`)
|
|
76
|
+
2. **Before execution**, Forge checks the action against your policies
|
|
77
|
+
3. If **approved**: the tool runs normally
|
|
78
|
+
4. If **denied**: the LLM receives a denial message and can explain why to the user
|
|
79
|
+
|
|
80
|
+
No tool ever executes without verification. Every decision is logged with a cryptographic proof.
|
|
81
|
+
|
|
82
|
+
## Per-Tool Guardrail
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from forge_openai import forge_tool_guardrail
|
|
86
|
+
|
|
87
|
+
guardrail = forge_tool_guardrail(policy="email-controls")
|
|
88
|
+
|
|
89
|
+
@function_tool(tool_input_guardrails=[guardrail])
|
|
90
|
+
def send_email(to: str, subject: str, body: str) -> str:
|
|
91
|
+
"""Send an email."""
|
|
92
|
+
return f"Email sent to {to}"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Full Control
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from forge_openai import ForgeGuardrail
|
|
99
|
+
|
|
100
|
+
forge = ForgeGuardrail(
|
|
101
|
+
api_key="vt_live_...",
|
|
102
|
+
agent_id="prod-finance-bot",
|
|
103
|
+
policy="finance-controls",
|
|
104
|
+
fail_closed=True,
|
|
105
|
+
skip_actions=["read_balance", "get_time"],
|
|
106
|
+
on_blocked=lambda action, reason, result: print(f"BLOCKED: {action} — {reason}"),
|
|
107
|
+
on_verified=lambda action, result: print(f"APPROVED: {action}"),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
agent = Agent(
|
|
111
|
+
name="finance-bot",
|
|
112
|
+
tools=forge.protect(send_payment, delete_record),
|
|
113
|
+
input_guardrails=[forge.input_guardrail()], # also screen agent input
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Environment Variables
|
|
118
|
+
|
|
119
|
+
| Variable | Description |
|
|
120
|
+
|----------|-------------|
|
|
121
|
+
| `VERITERA_API_KEY` | Your Forge API key (starts with `vt_live_` or `vt_test_`) |
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
MIT — [Veritera AI](https://veritera.ai)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
forge_openai/__init__.py
|
|
4
|
+
forge_openai/guardrail.py
|
|
5
|
+
forge_openai.egg-info/PKG-INFO
|
|
6
|
+
forge_openai.egg-info/SOURCES.txt
|
|
7
|
+
forge_openai.egg-info/dependency_links.txt
|
|
8
|
+
forge_openai.egg-info/requires.txt
|
|
9
|
+
forge_openai.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
forge_openai
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "forge-openai"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Forge Verify guardrail for OpenAI Agents SDK — verify every tool call before execution"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = {text = "MIT"}
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [{name = "Veritera AI", email = "engineering@veritera.ai"}]
|
|
9
|
+
keywords = ["veritera", "forge", "openai", "agents", "guardrail", "verification", "ai-safety"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Security",
|
|
16
|
+
"Topic :: Software Development :: Libraries",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"veritera>=0.2.0",
|
|
20
|
+
"openai-agents>=0.1.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://veritera.ai"
|
|
25
|
+
Documentation = "https://veritera.ai/docs"
|
|
26
|
+
Repository = "https://github.com/VeriteraAI/forge-openai"
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["setuptools>=68.0"]
|
|
30
|
+
build-backend = "setuptools.build_meta"
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
include = ["forge_openai*"]
|