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.
@@ -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]")