gaas-langchain 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.
- gaas_langchain-0.1.0/.gitignore +71 -0
- gaas_langchain-0.1.0/PKG-INFO +281 -0
- gaas_langchain-0.1.0/README.md +246 -0
- gaas_langchain-0.1.0/examples/langchain_quickstart.py +76 -0
- gaas_langchain-0.1.0/examples/langgraph_quickstart.py +95 -0
- gaas_langchain-0.1.0/gaas_langchain/__init__.py +28 -0
- gaas_langchain-0.1.0/gaas_langchain/_intent.py +163 -0
- gaas_langchain-0.1.0/gaas_langchain/callback.py +172 -0
- gaas_langchain-0.1.0/gaas_langchain/graph.py +154 -0
- gaas_langchain-0.1.0/gaas_langchain/tools.py +291 -0
- gaas_langchain-0.1.0/pyproject.toml +50 -0
- gaas_langchain-0.1.0/tests/__init__.py +0 -0
- gaas_langchain-0.1.0/tests/test_callback.py +144 -0
- gaas_langchain-0.1.0/tests/test_govern_node.py +200 -0
- gaas_langchain-0.1.0/tests/test_govern_tool.py +226 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
.env
|
|
10
|
+
.env.*
|
|
11
|
+
!.env.example
|
|
12
|
+
|
|
13
|
+
# IDE
|
|
14
|
+
.vscode/
|
|
15
|
+
.idea/
|
|
16
|
+
*.swp
|
|
17
|
+
|
|
18
|
+
# OS
|
|
19
|
+
.DS_Store
|
|
20
|
+
Thumbs.db
|
|
21
|
+
nul
|
|
22
|
+
|
|
23
|
+
# Claude Code (keep shared configs, ignore local/transient)
|
|
24
|
+
.claude/settings.local.json
|
|
25
|
+
.claude/history/
|
|
26
|
+
.claude/plans/
|
|
27
|
+
.claude/projects/
|
|
28
|
+
.claude/todos/
|
|
29
|
+
|
|
30
|
+
# Node / TypeScript
|
|
31
|
+
node_modules/
|
|
32
|
+
.vite/
|
|
33
|
+
|
|
34
|
+
# Project
|
|
35
|
+
*.log
|
|
36
|
+
|
|
37
|
+
# Generated reports
|
|
38
|
+
gaas-cost-audit.html
|
|
39
|
+
|
|
40
|
+
# Super admin credentials (contains passwords)
|
|
41
|
+
SUPER_ADMIN_CREDENTIALS.txt
|
|
42
|
+
|
|
43
|
+
# Local development scripts (not part of tracked codebase)
|
|
44
|
+
scripts/test-*.py
|
|
45
|
+
scripts/diagnose-*.py
|
|
46
|
+
scripts/send-*.py
|
|
47
|
+
scripts/trigger-*.py
|
|
48
|
+
scripts/uat-*.py
|
|
49
|
+
test_results_*.json
|
|
50
|
+
tests/e2e/
|
|
51
|
+
|
|
52
|
+
# Local trash can (never committed)
|
|
53
|
+
gaascan/
|
|
54
|
+
|
|
55
|
+
# Local planning docs (never committed)
|
|
56
|
+
.planning/
|
|
57
|
+
|
|
58
|
+
# Confidential business & sales documents
|
|
59
|
+
docs/business/
|
|
60
|
+
docs/sales/
|
|
61
|
+
|
|
62
|
+
# Session artifacts (Claude Code working docs)
|
|
63
|
+
/DAY_*.md
|
|
64
|
+
/DAYS_*.md
|
|
65
|
+
/HANDOFF_*.md
|
|
66
|
+
/think_tank.md
|
|
67
|
+
|
|
68
|
+
# Auto-generated test reports
|
|
69
|
+
apps/dashboard/playwright-report/
|
|
70
|
+
apps/dashboard/test-results/
|
|
71
|
+
apps/dashboard/e2e/screenshots/
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gaas-langchain
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LangChain and LangGraph integration for GaaS (Governance as a Service)
|
|
5
|
+
Project-URL: Homepage, https://gaas.is
|
|
6
|
+
Project-URL: Documentation, https://gaas.is/docs/integrations/langchain
|
|
7
|
+
Project-URL: Repository, https://github.com/H2OmAI/gaas
|
|
8
|
+
Project-URL: Changelog, https://github.com/H2OmAI/gaas/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: H2Om <sdk@gaas.is>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: agents,ai,gaas,governance,langchain,langgraph
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: gaas-sdk>=0.2.6
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'all'
|
|
24
|
+
Requires-Dist: langgraph>=0.1.0; extra == 'all'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: langgraph>=0.1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
30
|
+
Provides-Extra: langchain
|
|
31
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'langchain'
|
|
32
|
+
Provides-Extra: langgraph
|
|
33
|
+
Requires-Dist: langgraph>=0.1.0; extra == 'langgraph'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# gaas-langchain
|
|
37
|
+
|
|
38
|
+
**GaaS governance for LangChain and LangGraph agents.**
|
|
39
|
+
|
|
40
|
+
Add runtime policy enforcement to any LangChain agent in 3 lines. Every tool call is evaluated against your governance policies before it executes — blocked calls never reach the tool.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install gaas-langchain[all]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Quickstart
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from gaas_langchain import govern_tools, GaaSGovernanceConfig
|
|
52
|
+
from langgraph.prebuilt import create_react_agent
|
|
53
|
+
|
|
54
|
+
# 1. Configure governance
|
|
55
|
+
config = GaaSGovernanceConfig(
|
|
56
|
+
api_key="gsk_your_key", # from https://gaas.is/start
|
|
57
|
+
agent_id="my-agent",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# 2. Wrap your tools
|
|
61
|
+
tools = govern_tools([search, email_sender, db_query], config=config)
|
|
62
|
+
|
|
63
|
+
# 3. Run your agent — every tool call is now governed
|
|
64
|
+
agent = create_react_agent(llm, tools)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
That's it. Every call to `search`, `email_sender`, or `db_query` is evaluated against your GaaS governance policies before execution. Blocked calls raise `GovernanceBlockedError` before the tool runs.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## What Happens on Every Tool Call
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Agent decides to call send_email("user@corp.com", ...)
|
|
75
|
+
│
|
|
76
|
+
▼
|
|
77
|
+
GaaS governance check (< 500ms)
|
|
78
|
+
│
|
|
79
|
+
┌────┴────┐
|
|
80
|
+
│ APPROVE │ → tool.run() executes normally
|
|
81
|
+
│ BLOCK │ → GovernanceBlockedError raised, tool never executes
|
|
82
|
+
│ ESCALATE│ → GovernanceBlockedError raised (configurable)
|
|
83
|
+
│CONDITIONAL│ → tool executes with logged conditions
|
|
84
|
+
└─────────┘
|
|
85
|
+
│
|
|
86
|
+
Governance Proof Token (ECDSA P-256 signed)
|
|
87
|
+
attached to every decision for audit trail
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# LangChain tools support only
|
|
96
|
+
pip install gaas-langchain[langchain]
|
|
97
|
+
|
|
98
|
+
# LangGraph node wrapper only
|
|
99
|
+
pip install gaas-langchain[langgraph]
|
|
100
|
+
|
|
101
|
+
# Everything
|
|
102
|
+
pip install gaas-langchain[all]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Core Concepts
|
|
108
|
+
|
|
109
|
+
### `govern_tools()` — Wrap Tool Lists
|
|
110
|
+
|
|
111
|
+
Wraps a list of LangChain tools with governance. Use this with `create_react_agent`.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from gaas_langchain import govern_tools, GaaSGovernanceConfig
|
|
115
|
+
|
|
116
|
+
config = GaaSGovernanceConfig(api_key="gsk_...", agent_id="my-agent")
|
|
117
|
+
governed = govern_tools([search, calculator, email_sender], config=config)
|
|
118
|
+
agent = create_react_agent(llm, governed)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `govern_tool()` — Wrap a Single Tool
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from gaas_langchain import govern_tool
|
|
125
|
+
|
|
126
|
+
governed_email = govern_tool(email_sender, config=config)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `@govern_node()` — Govern LangGraph Nodes
|
|
130
|
+
|
|
131
|
+
Wrap any LangGraph node with a governance checkpoint. If the node is blocked, `GovernanceBlockedError` is raised before the node executes.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from gaas_langchain import govern_node
|
|
135
|
+
|
|
136
|
+
@govern_node(config=config, node_name="send_report", sensitivity="CONFIDENTIAL")
|
|
137
|
+
def send_report_node(state: AgentState) -> AgentState:
|
|
138
|
+
# This node will be governed before execution
|
|
139
|
+
send_report(state["report"])
|
|
140
|
+
return state
|
|
141
|
+
|
|
142
|
+
# Works with async nodes too
|
|
143
|
+
@govern_node(config=config)
|
|
144
|
+
async def async_tool_node(state: AgentState) -> AgentState:
|
|
145
|
+
result = await call_external_api(state["query"])
|
|
146
|
+
return {**state, "result": result}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `GaaSCallbackHandler` — Observability Without Blocking
|
|
150
|
+
|
|
151
|
+
Log governance decisions for all tool calls without blocking execution. Useful for monitoring shadow mode.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from gaas_langchain import GaaSCallbackHandler
|
|
155
|
+
|
|
156
|
+
handler = GaaSCallbackHandler(config=config)
|
|
157
|
+
agent.invoke({"input": "..."}, config={"callbacks": [handler]})
|
|
158
|
+
|
|
159
|
+
# Review governance decisions
|
|
160
|
+
print(handler.summary())
|
|
161
|
+
# {
|
|
162
|
+
# "total_tool_calls": 12,
|
|
163
|
+
# "approved": 10,
|
|
164
|
+
# "blocked": 1,
|
|
165
|
+
# "escalated": 1,
|
|
166
|
+
# "block_rate": 0.083
|
|
167
|
+
# }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from gaas_langchain import GaaSGovernanceConfig
|
|
176
|
+
|
|
177
|
+
config = GaaSGovernanceConfig(
|
|
178
|
+
api_url="https://api.gaas.is", # GaaS API endpoint
|
|
179
|
+
api_key="gsk_...", # Your API key
|
|
180
|
+
agent_id="my-langchain-agent", # Agent identifier (appears in audit trail)
|
|
181
|
+
block_on_escalate=True, # Raise GovernanceBlockedError on ESCALATE
|
|
182
|
+
timeout_seconds=5.0, # Governance check timeout (fail open on timeout)
|
|
183
|
+
sensitivity="INTERNAL", # Default sensitivity level for tool inputs
|
|
184
|
+
raise_on_governance_error=False, # Fail open if GaaS API is unreachable
|
|
185
|
+
extra_regulatory_domains=["HIPAA"], # Additional domains for all intents
|
|
186
|
+
extra_data_categories=["PHI"], # Additional data categories for all intents
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Per-Node Overrides
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
@govern_node(
|
|
194
|
+
config=config,
|
|
195
|
+
node_name="process_patient_data",
|
|
196
|
+
sensitivity="RESTRICTED",
|
|
197
|
+
financial_exposure_usd=0.0,
|
|
198
|
+
regulatory_domains=["HIPAA", "HITECH"],
|
|
199
|
+
)
|
|
200
|
+
def patient_data_node(state: dict) -> dict: ...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Handling Blocked Calls
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from gaas_langchain import GovernanceBlockedError
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
result = agent.invoke({"input": "Send all customer data to external API"})
|
|
212
|
+
except GovernanceBlockedError as e:
|
|
213
|
+
print(f"Blocked: {e.tool_name}")
|
|
214
|
+
print(f"Verdict: {e.verdict}") # BLOCK or ESCALATE
|
|
215
|
+
print(f"Decision ID: {e.decision_id}") # For audit reference
|
|
216
|
+
print(f"Risk score: {e.risk_score}") # 0.0–1.0
|
|
217
|
+
print(f"Policies: {e.blocking_policies}") # Policy IDs that triggered block
|
|
218
|
+
print(f"GPT token: {e.governance_proof_token}") # Governance Proof Token ID
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The `governance_proof_token` is a cryptographically-signed artefact (ECDSA P-256) that proves governance was active at the moment of the decision. Verify it at `GET /v1/verify/proof/{token_id}`.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Fail-Open Design
|
|
226
|
+
|
|
227
|
+
`gaas-langchain` is designed to fail open: if the GaaS API is unreachable, tool calls proceed normally. This ensures your agent keeps working during network interruptions.
|
|
228
|
+
|
|
229
|
+
Set `raise_on_governance_error=True` to fail closed instead:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
config = GaaSGovernanceConfig(
|
|
233
|
+
raise_on_governance_error=True, # Fail closed: error if GaaS unreachable
|
|
234
|
+
)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Shadow Mode
|
|
240
|
+
|
|
241
|
+
Test governance policies without blocking agent actions:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
# Use the GaaS shadow endpoint — full pipeline, zero enforcement
|
|
245
|
+
config = GaaSGovernanceConfig(
|
|
246
|
+
api_url="https://api.gaas.is",
|
|
247
|
+
api_key="gsk_...",
|
|
248
|
+
agent_id="my-agent",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Append ?mode=shadow to the intent URL in your own govern_tool subclass,
|
|
252
|
+
# or use GaaSCallbackHandler with enforce=False for pure observation mode.
|
|
253
|
+
handler = GaaSCallbackHandler(config=config, enforce=False)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Examples
|
|
259
|
+
|
|
260
|
+
- [`examples/langchain_quickstart.py`](examples/langchain_quickstart.py) — ReAct agent with governed tools
|
|
261
|
+
- [`examples/langgraph_quickstart.py`](examples/langgraph_quickstart.py) — LangGraph workflow with governed nodes
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Get a GaaS API Key
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
curl -X POST https://api.gaas.is/v1/onboarding/quickstart \
|
|
269
|
+
-H "Content-Type: application/json" \
|
|
270
|
+
-d '{"organization_name": "Acme Corp", "contact_email": "you@acme.com"}'
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Returns your API key (`gsk_...`) and a pre-configured governance membrane.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
Apache 2.0. See [LICENSE](../../sdks/python/LICENSE).
|
|
280
|
+
|
|
281
|
+
Built on [GaaS](https://gaas.is) — AI Agent Governance as a Service.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# gaas-langchain
|
|
2
|
+
|
|
3
|
+
**GaaS governance for LangChain and LangGraph agents.**
|
|
4
|
+
|
|
5
|
+
Add runtime policy enforcement to any LangChain agent in 3 lines. Every tool call is evaluated against your governance policies before it executes — blocked calls never reach the tool.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install gaas-langchain[all]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from gaas_langchain import govern_tools, GaaSGovernanceConfig
|
|
17
|
+
from langgraph.prebuilt import create_react_agent
|
|
18
|
+
|
|
19
|
+
# 1. Configure governance
|
|
20
|
+
config = GaaSGovernanceConfig(
|
|
21
|
+
api_key="gsk_your_key", # from https://gaas.is/start
|
|
22
|
+
agent_id="my-agent",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# 2. Wrap your tools
|
|
26
|
+
tools = govern_tools([search, email_sender, db_query], config=config)
|
|
27
|
+
|
|
28
|
+
# 3. Run your agent — every tool call is now governed
|
|
29
|
+
agent = create_react_agent(llm, tools)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
That's it. Every call to `search`, `email_sender`, or `db_query` is evaluated against your GaaS governance policies before execution. Blocked calls raise `GovernanceBlockedError` before the tool runs.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## What Happens on Every Tool Call
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Agent decides to call send_email("user@corp.com", ...)
|
|
40
|
+
│
|
|
41
|
+
▼
|
|
42
|
+
GaaS governance check (< 500ms)
|
|
43
|
+
│
|
|
44
|
+
┌────┴────┐
|
|
45
|
+
│ APPROVE │ → tool.run() executes normally
|
|
46
|
+
│ BLOCK │ → GovernanceBlockedError raised, tool never executes
|
|
47
|
+
│ ESCALATE│ → GovernanceBlockedError raised (configurable)
|
|
48
|
+
│CONDITIONAL│ → tool executes with logged conditions
|
|
49
|
+
└─────────┘
|
|
50
|
+
│
|
|
51
|
+
Governance Proof Token (ECDSA P-256 signed)
|
|
52
|
+
attached to every decision for audit trail
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# LangChain tools support only
|
|
61
|
+
pip install gaas-langchain[langchain]
|
|
62
|
+
|
|
63
|
+
# LangGraph node wrapper only
|
|
64
|
+
pip install gaas-langchain[langgraph]
|
|
65
|
+
|
|
66
|
+
# Everything
|
|
67
|
+
pip install gaas-langchain[all]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Core Concepts
|
|
73
|
+
|
|
74
|
+
### `govern_tools()` — Wrap Tool Lists
|
|
75
|
+
|
|
76
|
+
Wraps a list of LangChain tools with governance. Use this with `create_react_agent`.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from gaas_langchain import govern_tools, GaaSGovernanceConfig
|
|
80
|
+
|
|
81
|
+
config = GaaSGovernanceConfig(api_key="gsk_...", agent_id="my-agent")
|
|
82
|
+
governed = govern_tools([search, calculator, email_sender], config=config)
|
|
83
|
+
agent = create_react_agent(llm, governed)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### `govern_tool()` — Wrap a Single Tool
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from gaas_langchain import govern_tool
|
|
90
|
+
|
|
91
|
+
governed_email = govern_tool(email_sender, config=config)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `@govern_node()` — Govern LangGraph Nodes
|
|
95
|
+
|
|
96
|
+
Wrap any LangGraph node with a governance checkpoint. If the node is blocked, `GovernanceBlockedError` is raised before the node executes.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from gaas_langchain import govern_node
|
|
100
|
+
|
|
101
|
+
@govern_node(config=config, node_name="send_report", sensitivity="CONFIDENTIAL")
|
|
102
|
+
def send_report_node(state: AgentState) -> AgentState:
|
|
103
|
+
# This node will be governed before execution
|
|
104
|
+
send_report(state["report"])
|
|
105
|
+
return state
|
|
106
|
+
|
|
107
|
+
# Works with async nodes too
|
|
108
|
+
@govern_node(config=config)
|
|
109
|
+
async def async_tool_node(state: AgentState) -> AgentState:
|
|
110
|
+
result = await call_external_api(state["query"])
|
|
111
|
+
return {**state, "result": result}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `GaaSCallbackHandler` — Observability Without Blocking
|
|
115
|
+
|
|
116
|
+
Log governance decisions for all tool calls without blocking execution. Useful for monitoring shadow mode.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from gaas_langchain import GaaSCallbackHandler
|
|
120
|
+
|
|
121
|
+
handler = GaaSCallbackHandler(config=config)
|
|
122
|
+
agent.invoke({"input": "..."}, config={"callbacks": [handler]})
|
|
123
|
+
|
|
124
|
+
# Review governance decisions
|
|
125
|
+
print(handler.summary())
|
|
126
|
+
# {
|
|
127
|
+
# "total_tool_calls": 12,
|
|
128
|
+
# "approved": 10,
|
|
129
|
+
# "blocked": 1,
|
|
130
|
+
# "escalated": 1,
|
|
131
|
+
# "block_rate": 0.083
|
|
132
|
+
# }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from gaas_langchain import GaaSGovernanceConfig
|
|
141
|
+
|
|
142
|
+
config = GaaSGovernanceConfig(
|
|
143
|
+
api_url="https://api.gaas.is", # GaaS API endpoint
|
|
144
|
+
api_key="gsk_...", # Your API key
|
|
145
|
+
agent_id="my-langchain-agent", # Agent identifier (appears in audit trail)
|
|
146
|
+
block_on_escalate=True, # Raise GovernanceBlockedError on ESCALATE
|
|
147
|
+
timeout_seconds=5.0, # Governance check timeout (fail open on timeout)
|
|
148
|
+
sensitivity="INTERNAL", # Default sensitivity level for tool inputs
|
|
149
|
+
raise_on_governance_error=False, # Fail open if GaaS API is unreachable
|
|
150
|
+
extra_regulatory_domains=["HIPAA"], # Additional domains for all intents
|
|
151
|
+
extra_data_categories=["PHI"], # Additional data categories for all intents
|
|
152
|
+
)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Per-Node Overrides
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
@govern_node(
|
|
159
|
+
config=config,
|
|
160
|
+
node_name="process_patient_data",
|
|
161
|
+
sensitivity="RESTRICTED",
|
|
162
|
+
financial_exposure_usd=0.0,
|
|
163
|
+
regulatory_domains=["HIPAA", "HITECH"],
|
|
164
|
+
)
|
|
165
|
+
def patient_data_node(state: dict) -> dict: ...
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Handling Blocked Calls
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from gaas_langchain import GovernanceBlockedError
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
result = agent.invoke({"input": "Send all customer data to external API"})
|
|
177
|
+
except GovernanceBlockedError as e:
|
|
178
|
+
print(f"Blocked: {e.tool_name}")
|
|
179
|
+
print(f"Verdict: {e.verdict}") # BLOCK or ESCALATE
|
|
180
|
+
print(f"Decision ID: {e.decision_id}") # For audit reference
|
|
181
|
+
print(f"Risk score: {e.risk_score}") # 0.0–1.0
|
|
182
|
+
print(f"Policies: {e.blocking_policies}") # Policy IDs that triggered block
|
|
183
|
+
print(f"GPT token: {e.governance_proof_token}") # Governance Proof Token ID
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The `governance_proof_token` is a cryptographically-signed artefact (ECDSA P-256) that proves governance was active at the moment of the decision. Verify it at `GET /v1/verify/proof/{token_id}`.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Fail-Open Design
|
|
191
|
+
|
|
192
|
+
`gaas-langchain` is designed to fail open: if the GaaS API is unreachable, tool calls proceed normally. This ensures your agent keeps working during network interruptions.
|
|
193
|
+
|
|
194
|
+
Set `raise_on_governance_error=True` to fail closed instead:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
config = GaaSGovernanceConfig(
|
|
198
|
+
raise_on_governance_error=True, # Fail closed: error if GaaS unreachable
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Shadow Mode
|
|
205
|
+
|
|
206
|
+
Test governance policies without blocking agent actions:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
# Use the GaaS shadow endpoint — full pipeline, zero enforcement
|
|
210
|
+
config = GaaSGovernanceConfig(
|
|
211
|
+
api_url="https://api.gaas.is",
|
|
212
|
+
api_key="gsk_...",
|
|
213
|
+
agent_id="my-agent",
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Append ?mode=shadow to the intent URL in your own govern_tool subclass,
|
|
217
|
+
# or use GaaSCallbackHandler with enforce=False for pure observation mode.
|
|
218
|
+
handler = GaaSCallbackHandler(config=config, enforce=False)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Examples
|
|
224
|
+
|
|
225
|
+
- [`examples/langchain_quickstart.py`](examples/langchain_quickstart.py) — ReAct agent with governed tools
|
|
226
|
+
- [`examples/langgraph_quickstart.py`](examples/langgraph_quickstart.py) — LangGraph workflow with governed nodes
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Get a GaaS API Key
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
curl -X POST https://api.gaas.is/v1/onboarding/quickstart \
|
|
234
|
+
-H "Content-Type: application/json" \
|
|
235
|
+
-d '{"organization_name": "Acme Corp", "contact_email": "you@acme.com"}'
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Returns your API key (`gsk_...`) and a pre-configured governance membrane.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
Apache 2.0. See [LICENSE](../../sdks/python/LICENSE).
|
|
245
|
+
|
|
246
|
+
Built on [GaaS](https://gaas.is) — AI Agent Governance as a Service.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""LangChain quickstart example — governance in 3 lines.
|
|
2
|
+
|
|
3
|
+
This example shows how to add GaaS governance to any LangChain ReAct agent.
|
|
4
|
+
Replace the tools, LLM, and API key with your own.
|
|
5
|
+
|
|
6
|
+
Run:
|
|
7
|
+
pip install gaas-langchain[langchain] langchain-openai
|
|
8
|
+
python examples/langchain_quickstart.py
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
# ── 1. Import gaas-langchain ─────────────────────────────────────────────────
|
|
14
|
+
from gaas_langchain import GaaSGovernanceConfig, GovernanceBlockedError, govern_tools
|
|
15
|
+
|
|
16
|
+
# ── 2. Set up governance config ──────────────────────────────────────────────
|
|
17
|
+
config = GaaSGovernanceConfig(
|
|
18
|
+
api_url="https://api.gaas.is",
|
|
19
|
+
api_key="gsk_your_api_key_here", # replace with your GaaS API key
|
|
20
|
+
agent_id="my-langchain-agent",
|
|
21
|
+
block_on_escalate=True, # raise on ESCALATE (not just BLOCK)
|
|
22
|
+
sensitivity="INTERNAL",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ── Example tools ────────────────────────────────────────────────────────────
|
|
27
|
+
# Replace with real tools (TavilySearchResults, etc.)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from langchain_core.tools import tool
|
|
31
|
+
|
|
32
|
+
@tool
|
|
33
|
+
def search_web(query: str) -> str:
|
|
34
|
+
"""Search the web for information."""
|
|
35
|
+
return f"[mock result for: {query}]"
|
|
36
|
+
|
|
37
|
+
@tool
|
|
38
|
+
def send_email(to: str, subject: str, body: str) -> str:
|
|
39
|
+
"""Send an email to a recipient."""
|
|
40
|
+
return f"[mock email sent to {to}]"
|
|
41
|
+
|
|
42
|
+
@tool
|
|
43
|
+
def query_database(sql: str) -> str:
|
|
44
|
+
"""Execute a SQL query on the company database."""
|
|
45
|
+
return f"[mock result for: {sql}]"
|
|
46
|
+
|
|
47
|
+
raw_tools = [search_web, send_email, query_database]
|
|
48
|
+
|
|
49
|
+
# ── 3. Wrap tools with governance ────────────────────────────────────────
|
|
50
|
+
governed_tools = govern_tools(raw_tools, config=config)
|
|
51
|
+
|
|
52
|
+
# ── Build and run the agent ──────────────────────────────────────────────
|
|
53
|
+
print("Tools wrapped with GaaS governance:")
|
|
54
|
+
for t in governed_tools:
|
|
55
|
+
print(f" - {t.name}")
|
|
56
|
+
|
|
57
|
+
print("\nTo run a full agent:")
|
|
58
|
+
print(" from langchain_openai import ChatOpenAI")
|
|
59
|
+
print(" from langgraph.prebuilt import create_react_agent")
|
|
60
|
+
print(" llm = ChatOpenAI(model='gpt-4o')")
|
|
61
|
+
print(" agent = create_react_agent(llm, governed_tools)")
|
|
62
|
+
print(" result = agent.invoke({'messages': [('user', 'Search for AI governance')]})")
|
|
63
|
+
|
|
64
|
+
# Demonstrate what a blocked call looks like:
|
|
65
|
+
print("\nSimulating a blocked tool call (will use mock server or fail gracefully):")
|
|
66
|
+
try:
|
|
67
|
+
# This will attempt to call GaaS — will fail gracefully since no real server
|
|
68
|
+
result = governed_tools[1].run("nick@example.com")
|
|
69
|
+
print(f" Result: {result}")
|
|
70
|
+
except GovernanceBlockedError as e:
|
|
71
|
+
print(f" BLOCKED: {e}")
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f" GaaS not reachable (expected in demo): {type(e).__name__}")
|
|
74
|
+
|
|
75
|
+
except ImportError:
|
|
76
|
+
print("langchain-core not installed. Run: pip install gaas-langchain[langchain]")
|