bidda-shield 0.1.0__py3-none-any.whl

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,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: bidda-shield
3
+ Version: 0.1.0
4
+ Summary: Bidda Compliance Intelligence SDK — LangChain, AutoGen, CrewAI integration
5
+ Home-page: https://bidda.com
6
+ Author: Bidda Intelligence
7
+ Author-email: api@bidda.com
8
+ Project-URL: Homepage, https://bidda.com
9
+ Project-URL: Documentation, https://bidda.com/developers
10
+ Project-URL: Source, https://git.bidda.com/Bidda-Ai/bidda-shield
11
+ Project-URL: Registry, https://bidda.com/intelligence
12
+ Project-URL: MCP Server, https://bidda.com/mcp
13
+ Keywords: compliance,GDPR,EU AI Act,LangChain,AutoGen,CrewAI,regulatory,legal,AI safety
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Office/Business :: Financial :: Accounting
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: httpx>=0.24.0
28
+ Provides-Extra: langchain
29
+ Requires-Dist: langchain>=0.1.0; extra == "langchain"
30
+ Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
31
+ Requires-Dist: pydantic>=2.0; extra == "langchain"
32
+ Provides-Extra: autogen
33
+ Requires-Dist: pyautogen>=0.2.0; extra == "autogen"
34
+ Provides-Extra: crewai
35
+ Requires-Dist: crewai>=0.28.0; extra == "crewai"
36
+ Requires-Dist: crewai-tools>=0.1.0; extra == "crewai"
37
+ Provides-Extra: all
38
+ Requires-Dist: langchain>=0.1.0; extra == "all"
39
+ Requires-Dist: langchain-core>=0.1.0; extra == "all"
40
+ Requires-Dist: pydantic>=2.0; extra == "all"
41
+ Requires-Dist: pyautogen>=0.2.0; extra == "all"
42
+ Requires-Dist: crewai>=0.28.0; extra == "all"
43
+ Requires-Dist: crewai-tools>=0.1.0; extra == "all"
44
+ Dynamic: author
45
+ Dynamic: author-email
46
+ Dynamic: classifier
47
+ Dynamic: description
48
+ Dynamic: description-content-type
49
+ Dynamic: home-page
50
+ Dynamic: keywords
51
+ Dynamic: license-file
52
+ Dynamic: project-url
53
+ Dynamic: provides-extra
54
+ Dynamic: requires-dist
55
+ Dynamic: requires-python
56
+ Dynamic: summary
57
+
58
+ # bidda-shield
59
+
60
+ **Verified compliance intelligence for AI agents.** Stop your LangChain, AutoGen, and CrewAI agents from hallucinating legal requirements.
61
+
62
+ ```bash
63
+ pip install bidda-shield
64
+ ```
65
+
66
+ ---
67
+
68
+ ## The problem
69
+
70
+ AI agents making decisions about hiring, credit scoring, data processing, or content moderation are operating under dozens of overlapping regulations — GDPR, EU AI Act, HIPAA, CCPA, Basel III. When an agent gets the legal logic wrong, it isn't just a bug. It's a regulatory liability event.
71
+
72
+ LLMs hallucinate regulations. bidda-shield doesn't.
73
+
74
+ Every compliance node in the Bidda registry traces to a specific clause of a primary legal instrument, verified against the source URL, and drift-checked weekly. No inference. No approximation.
75
+
76
+ ---
77
+
78
+ ## Quickstart
79
+
80
+ ```python
81
+ from bidda_shield import BiddaShield
82
+
83
+ shield = BiddaShield()
84
+
85
+ # Find the compliance node most relevant to your agent's action
86
+ result = shield.check_compliance("process biometric data for access control")
87
+ print(result["title"]) # EU AI Act Article 5 — Prohibited AI Practices
88
+ print(result["bluf"]) # Plain-English summary of the legal obligation
89
+ ```
90
+
91
+ ---
92
+
93
+ ## LangChain
94
+
95
+ ```python
96
+ from langchain.agents import initialize_agent, AgentType
97
+ from langchain_openai import ChatOpenAI
98
+ from bidda_shield import BiddaLangChainTool
99
+
100
+ llm = ChatOpenAI(model="gpt-4o")
101
+ tool = BiddaLangChainTool()
102
+
103
+ agent = initialize_agent(
104
+ tools=[tool],
105
+ llm=llm,
106
+ agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
107
+ verbose=True,
108
+ )
109
+
110
+ agent.run(
111
+ "My agent is about to make an automated credit decision. "
112
+ "What regulations apply and what do they require?"
113
+ )
114
+ ```
115
+
116
+ The tool returns the regulation title, domain, plain-English summary (BLUF), and a link to the full verified node — for $0.01 USDC per full unlock.
117
+
118
+ ---
119
+
120
+ ## AutoGen
121
+
122
+ ```python
123
+ import autogen
124
+ from bidda_shield import BiddaAutoGenTool
125
+
126
+ config_list = [{"model": "gpt-4o", "api_key": "YOUR_OPENAI_KEY"}]
127
+
128
+ bidda_tool = BiddaAutoGenTool()
129
+
130
+ assistant = autogen.AssistantAgent(
131
+ name="compliance_assistant",
132
+ llm_config={
133
+ "config_list": config_list,
134
+ "functions": [bidda_tool.function_schema],
135
+ },
136
+ )
137
+
138
+ user_proxy = autogen.UserProxyAgent(
139
+ name="user",
140
+ human_input_mode="NEVER",
141
+ function_map={"bidda_compliance_lookup": bidda_tool.execute},
142
+ )
143
+
144
+ user_proxy.initiate_chat(
145
+ assistant,
146
+ message="What does GDPR Article 22 require for automated decision-making?",
147
+ )
148
+ ```
149
+
150
+ ---
151
+
152
+ ## CrewAI
153
+
154
+ ```python
155
+ from crewai import Agent, Task, Crew
156
+ from bidda_shield import BiddaCrewAITool
157
+
158
+ compliance_tool = BiddaCrewAITool()
159
+
160
+ compliance_officer = Agent(
161
+ role="Chief Compliance Officer",
162
+ goal="Ensure all AI agent actions comply with applicable regulations",
163
+ backstory="Expert in GDPR, EU AI Act, HIPAA, and global data protection law.",
164
+ tools=[compliance_tool],
165
+ verbose=True,
166
+ )
167
+
168
+ task = Task(
169
+ description="Review the agent action 'train a model on employee performance data' and identify all applicable regulations.",
170
+ agent=compliance_officer,
171
+ )
172
+
173
+ crew = Crew(agents=[compliance_officer], tasks=[task])
174
+ crew.kickoff()
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Direct API usage
180
+
181
+ ```python
182
+ from bidda_shield import BiddaShield
183
+
184
+ shield = BiddaShield()
185
+
186
+ # Search by keyword
187
+ nodes = shield.search_nodes("automated decision making")
188
+ for n in nodes:
189
+ print(n["node_id"], "—", n["title"])
190
+
191
+ # Get a specific node (free discovery tier)
192
+ node = shield.get_node("gdpr-article-22-automated-decisions")
193
+ print(node["bluf"])
194
+
195
+ # Get full vault data (requires Skyfire JWT or USDC payment — $0.01)
196
+ shield_paid = BiddaShield(skyfire_token="YOUR_SKYFIRE_JWT")
197
+ full_node = shield_paid.get_node("gdpr-article-22-automated-decisions", vault=True)
198
+ print(full_node["deterministic_workflow"]) # Step-by-step legal compliance logic
199
+ ```
200
+
201
+ ---
202
+
203
+ ## What's in a full node
204
+
205
+ Each vault-tier node contains:
206
+
207
+ - **BLUF** — plain-English summary of the legal obligation
208
+ - **deterministic_workflow** — step-by-step compliance checklist derived from the primary legal text
209
+ - **actionable_schema** — machine-readable compliance checkpoints
210
+ - **primary_citations** — exact section references to the legal instrument
211
+ - **crosswalks** — mappings to NIST, ISO, and peer standards
212
+ - **dependencies** — other regulations this one depends on or triggers
213
+ - **verification** — source URL, jurisdiction, instrument type, integrity hash
214
+
215
+ All content traces to a real primary legal source. No secondary commentary. No paraphrasing.
216
+
217
+ ---
218
+
219
+ ## Payment
220
+
221
+ - **Discovery tier** (free): node ID, title, domain, plain-English summary
222
+ - **Vault tier** ($0.01 USDC per node): full deterministic logic, citations, crosswalks, workflow
223
+
224
+ Pay with:
225
+ - **Skyfire** — pass a `skyfire-pay-id` bearer token (agent-native, no wallet required)
226
+ - **L402 / Base USDC** — send $0.01 to the Bidda Base wallet, pass the tx hash
227
+
228
+ ```python
229
+ # With Skyfire
230
+ shield = BiddaShield(skyfire_token=os.getenv("BIDDA_SKYFIRE_TOKEN"))
231
+
232
+ # With Base tx hash
233
+ shield = BiddaShield(base_tx_hash="0xYOUR_TRANSACTION_HASH")
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Install options
239
+
240
+ ```bash
241
+ # Core (no framework dependencies)
242
+ pip install bidda-shield
243
+
244
+ # With LangChain
245
+ pip install "bidda-shield[langchain]"
246
+
247
+ # With AutoGen
248
+ pip install "bidda-shield[autogen]"
249
+
250
+ # With CrewAI
251
+ pip install "bidda-shield[crewai]"
252
+
253
+ # Everything
254
+ pip install "bidda-shield[all]"
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Registry coverage
260
+
261
+ - **4,600+ verified nodes** across 31 regulatory pillars
262
+ - **Pillars:** AI Governance, Cybersecurity, Banking & Finance, Healthcare, Legal & IP, ESG, Workplace, Aviation & Defense, Crypto, Cloud, and 21 more
263
+ - **Jurisdictions:** EU, US, UK, Germany, Australia, Singapore, South Africa, and global instruments
264
+ - **Sources:** EU AI Act, GDPR, NIST CSF, ISO 27001, Basel III/IV, HIPAA, DORA, NIS2, FATF, and 150+ authority bodies
265
+
266
+ Full registry: [bidda.com/intelligence](https://bidda.com/intelligence)
267
+
268
+ ---
269
+
270
+ ## Links
271
+
272
+ - Registry: [bidda.com](https://bidda.com)
273
+ - API docs: [bidda.com/developers](https://bidda.com/developers)
274
+ - MCP server: `https://bidda.com/mcp` (Claude.ai, Claude Desktop, any MCP client)
275
+ - Source: [git.bidda.com/Bidda-Ai/bidda-shield](https://git.bidda.com/Bidda-Ai/bidda-shield)
276
+ - Support: [api@bidda.com](mailto:api@bidda.com)
277
+
278
+ ---
279
+
280
+ ## License
281
+
282
+ MIT — use freely, attribution appreciated.
@@ -0,0 +1,6 @@
1
+ bidda_shield.py,sha256=pXl7bwjQWKz9dVW7sggk4N7cET_7rTUWKwbyHmdRs7c,13421
2
+ bidda_shield-0.1.0.dist-info/licenses/LICENSE,sha256=UAN_Gqn6B6doggwWpBlfMZC9RcDTG28IcVpg6agcphY,1083
3
+ bidda_shield-0.1.0.dist-info/METADATA,sha256=AjQTLUUjuPco8vdSNbzVP4JW-xb5_D7phqR9Kcdmzlw,8809
4
+ bidda_shield-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
5
+ bidda_shield-0.1.0.dist-info/top_level.txt,sha256=whaDbCykzJxK1gmeq9UKFMO_MBIF0OL_9lEDuYSuJNA,13
6
+ bidda_shield-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bidda Intelligence PTY LTD
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ bidda_shield
bidda_shield.py ADDED
@@ -0,0 +1,368 @@
1
+ """
2
+ bidda-shield: Bidda Compliance Intelligence SDK
3
+
4
+ Integrates with LangChain, AutoGen, and CrewAI to give AI agents
5
+ real-time access to verified compliance intelligence from bidda.com.
6
+
7
+ Usage:
8
+ from bidda_shield import BiddaShield, BiddaLangChainTool
9
+
10
+ # Basic usage
11
+ shield = BiddaShield()
12
+ node = shield.get_node("gdpr-article-5-data-principles")
13
+ print(node["bluf"])
14
+
15
+ # LangChain Tool
16
+ from langchain.agents import initialize_agent, AgentType
17
+ from langchain_openai import ChatOpenAI
18
+ tool = BiddaLangChainTool()
19
+ agent = initialize_agent([tool], ChatOpenAI(), agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
20
+
21
+ # AutoGen Tool
22
+ from autogen import AssistantAgent, UserProxyAgent
23
+ tool = BiddaAutoGenTool()
24
+
25
+ # CrewAI Tool
26
+ from crewai import Agent
27
+ tool = BiddaCrewAITool()
28
+ compliance_agent = Agent(role="Compliance Officer", tools=[tool])
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import os
34
+ import json
35
+ import time
36
+ import hashlib
37
+ import logging
38
+ from typing import Optional, Any
39
+
40
+ try:
41
+ import httpx
42
+ _HAS_HTTPX = True
43
+ except ImportError:
44
+ import urllib.request
45
+ _HAS_HTTPX = False
46
+
47
+ logger = logging.getLogger("bidda_shield")
48
+
49
+ BIDDA_API_BASE = "https://bidda.com/api/v1"
50
+ DISCOVERY_SUFFIX = "/nodes"
51
+ VAULT_SUFFIX = "/vault/nodes"
52
+ DEFAULT_TIMEOUT = 10
53
+
54
+
55
+ class BiddaError(Exception):
56
+ pass
57
+
58
+
59
+ class PaymentRequired(BiddaError):
60
+ """Raised when a vault node requires payment (x402 / L402)."""
61
+ def __init__(self, node_id: str, cost_usd: str):
62
+ self.node_id = node_id
63
+ self.cost_usd = cost_usd
64
+ super().__init__(
65
+ f"Node '{node_id}' requires payment: ${cost_usd} USDC. "
66
+ "Pass a Skyfire JWT via skyfire_token= or a Base tx hash via base_tx_hash=."
67
+ )
68
+
69
+
70
+ class BiddaShield:
71
+ """
72
+ Core Bidda client.
73
+
74
+ Parameters
75
+ ----------
76
+ skyfire_token : str, optional
77
+ Skyfire pay+jwt bearer token for autonomous agent payments.
78
+ Set BIDDA_SKYFIRE_TOKEN env var to avoid passing it explicitly.
79
+ base_tx_hash : str, optional
80
+ Base USDC transaction hash (0x...) for L402 settlement.
81
+ timeout : int
82
+ HTTP timeout in seconds.
83
+ """
84
+
85
+ def __init__(
86
+ self,
87
+ skyfire_token: Optional[str] = None,
88
+ base_tx_hash: Optional[str] = None,
89
+ timeout: int = DEFAULT_TIMEOUT,
90
+ ):
91
+ self.skyfire_token = skyfire_token or os.getenv("BIDDA_SKYFIRE_TOKEN")
92
+ self.base_tx_hash = base_tx_hash
93
+ self.timeout = timeout
94
+
95
+ def _headers(self) -> dict[str, str]:
96
+ h: dict[str, str] = {"Accept": "application/json"}
97
+ if self.skyfire_token:
98
+ h["skyfire-pay-id"] = self.skyfire_token
99
+ if self.base_tx_hash:
100
+ h["x-base-tx-hash"] = self.base_tx_hash
101
+ return h
102
+
103
+ def _get(self, url: str) -> dict:
104
+ if _HAS_HTTPX:
105
+ resp = httpx.get(url, headers=self._headers(), timeout=self.timeout)
106
+ if resp.status_code == 402:
107
+ raise PaymentRequired("unknown", "0.01")
108
+ resp.raise_for_status()
109
+ return resp.json()
110
+ else:
111
+ req = urllib.request.Request(url, headers=self._headers())
112
+ with urllib.request.urlopen(req, timeout=self.timeout) as r:
113
+ return json.loads(r.read())
114
+
115
+ # ── Public API ─────────────────────────────────────────────────────────────
116
+
117
+ def get_node(self, node_id: str, vault: bool = False) -> dict:
118
+ """
119
+ Fetch a compliance node by ID.
120
+
121
+ Parameters
122
+ ----------
123
+ node_id : str
124
+ The node identifier, e.g. 'gdpr-article-5-data-principles'.
125
+ vault : bool
126
+ If True, fetch the full 13-key vault payload (requires payment token).
127
+ If False (default), fetch the free 6-field discovery record.
128
+
129
+ Returns
130
+ -------
131
+ dict
132
+ Node data. Vault nodes include deterministic_workflow,
133
+ actionable_schema, primary_citations, and all crosswalks.
134
+ """
135
+ if vault:
136
+ url = f"{BIDDA_API_BASE}{VAULT_SUFFIX}/{node_id}.json"
137
+ else:
138
+ url = f"{BIDDA_API_BASE}{DISCOVERY_SUFFIX}/{node_id}.json"
139
+ return self._get(url)
140
+
141
+ def search_nodes(self, query: str, pillar: Optional[str] = None) -> list[dict]:
142
+ """
143
+ Search the discovery index for nodes matching a query string.
144
+
145
+ Parameters
146
+ ----------
147
+ query : str
148
+ Search term (regulation name, keyword, or topic).
149
+ pillar : str, optional
150
+ Filter by pillar slug, e.g. 'cybersecurity', 'ai-governance'.
151
+
152
+ Returns
153
+ -------
154
+ list[dict]
155
+ Matching discovery-tier node records (6 fields each).
156
+ """
157
+ index = self._get(f"{BIDDA_API_BASE}{DISCOVERY_SUFFIX}/index.json")
158
+ q = query.lower()
159
+ results = [
160
+ n for n in index
161
+ if q in n.get("title", "").lower()
162
+ or q in n.get("bluf", "").lower()
163
+ or q in n.get("domain", "").lower()
164
+ ]
165
+ if pillar:
166
+ results = [n for n in results if pillar.lower() in n.get("domain", "").lower()]
167
+ return results
168
+
169
+ def list_pillars(self) -> list[str]:
170
+ """Return the list of active sovereign pillars."""
171
+ index = self._get(f"{BIDDA_API_BASE}{DISCOVERY_SUFFIX}/index.json")
172
+ return sorted({n.get("domain", "") for n in index if n.get("domain")})
173
+
174
+ def check_compliance(
175
+ self,
176
+ agent_action: str,
177
+ regulation: Optional[str] = None,
178
+ vault: bool = False,
179
+ ) -> dict:
180
+ """
181
+ High-level method: find and return the most relevant compliance node
182
+ for a described agent action.
183
+
184
+ Parameters
185
+ ----------
186
+ agent_action : str
187
+ What the agent is about to do, e.g. "process biometric data for access control".
188
+ regulation : str, optional
189
+ Narrow to a specific regulation, e.g. "GDPR".
190
+ vault : bool
191
+ Whether to return vault-tier data (requires payment token).
192
+
193
+ Returns
194
+ -------
195
+ dict
196
+ The most relevant node's data, or an empty dict if nothing matches.
197
+ """
198
+ query = f"{agent_action} {regulation or ''}".strip()
199
+ results = self.search_nodes(query)
200
+ if not results:
201
+ return {}
202
+ best = results[0]
203
+ if vault:
204
+ return self.get_node(best["node_id"], vault=True)
205
+ return best
206
+
207
+
208
+ # ── LangChain Tool ─────────────────────────────────────────────────────────────
209
+
210
+ try:
211
+ from langchain.tools import BaseTool
212
+ from pydantic import BaseModel, Field
213
+
214
+ class _BiddaInput(BaseModel):
215
+ query: str = Field(description="Regulation name, keyword, or compliance topic to look up.")
216
+
217
+ class BiddaLangChainTool(BaseTool):
218
+ """
219
+ LangChain Tool: Bidda Compliance Intelligence
220
+
221
+ Gives a LangChain agent access to verified regulatory compliance nodes
222
+ from bidda.com. Returns source-cited, drift-free legal logic.
223
+
224
+ Example:
225
+ from bidda_shield import BiddaLangChainTool
226
+ tool = BiddaLangChainTool(skyfire_token="your-jwt")
227
+ """
228
+ name: str = "bidda_compliance"
229
+ description: str = (
230
+ "Look up verified compliance intelligence for any regulation, law, or standard. "
231
+ "Use this tool when you need to know what a regulation requires, "
232
+ "what the legal obligations are, or which compliance framework applies "
233
+ "to a specific AI action, data processing activity, or business operation. "
234
+ "Returns source-cited, drift-free regulatory logic from primary legal instruments. "
235
+ "Input: a regulation name, keyword, or compliance topic."
236
+ )
237
+ args_schema: type[BaseModel] = _BiddaInput
238
+
239
+ skyfire_token: Optional[str] = None
240
+ base_tx_hash: Optional[str] = None
241
+
242
+ def _run(self, query: str) -> str:
243
+ client = BiddaShield(skyfire_token=self.skyfire_token, base_tx_hash=self.base_tx_hash)
244
+ results = client.search_nodes(query)
245
+ if not results:
246
+ return f"No compliance nodes found for '{query}'. Try a broader search term."
247
+ top = results[0]
248
+ lines = [
249
+ f"Regulation: {top.get('title', 'Unknown')}",
250
+ f"Domain: {top.get('domain', 'Unknown')}",
251
+ f"Summary: {top.get('bluf', 'No summary available.')}",
252
+ f"Node ID: {top.get('node_id', '')}",
253
+ f"Full details (paid): https://bidda.com/intelligence/{top.get('node_id', '')}",
254
+ ]
255
+ if len(results) > 1:
256
+ lines.append(f"Related nodes: {', '.join(r['node_id'] for r in results[1:4])}")
257
+ return "\n".join(lines)
258
+
259
+ async def _arun(self, query: str) -> str:
260
+ return self._run(query)
261
+
262
+ except ImportError:
263
+ class BiddaLangChainTool: # type: ignore
264
+ """LangChain not installed. Install with: pip install langchain"""
265
+ def __init__(self, **_):
266
+ raise ImportError("LangChain not installed. Run: pip install langchain langchain-core")
267
+
268
+
269
+ # ── AutoGen Tool ───────────────────────────────────────────────────────────────
270
+
271
+ class BiddaAutoGenTool:
272
+ """
273
+ AutoGen-compatible Bidda compliance tool.
274
+
275
+ Registers a function that AutoGen agents can call to look up
276
+ compliance intelligence.
277
+
278
+ Example:
279
+ import autogen
280
+ from bidda_shield import BiddaAutoGenTool
281
+
282
+ tool = BiddaAutoGenTool()
283
+ assistant = autogen.AssistantAgent(
284
+ name="compliance_assistant",
285
+ llm_config={"functions": [tool.function_schema]},
286
+ )
287
+ tool.register(assistant)
288
+ """
289
+
290
+ def __init__(self, skyfire_token: Optional[str] = None):
291
+ self._client = BiddaShield(skyfire_token=skyfire_token)
292
+
293
+ @property
294
+ def function_schema(self) -> dict:
295
+ return {
296
+ "name": "bidda_compliance_lookup",
297
+ "description": (
298
+ "Look up verified compliance intelligence for any regulation, law, standard, "
299
+ "or compliance topic. Returns source-cited legal obligations from primary instruments."
300
+ ),
301
+ "parameters": {
302
+ "type": "object",
303
+ "properties": {
304
+ "query": {
305
+ "type": "string",
306
+ "description": "Regulation name, keyword, or compliance topic.",
307
+ }
308
+ },
309
+ "required": ["query"],
310
+ },
311
+ }
312
+
313
+ def execute(self, query: str) -> str:
314
+ results = self._client.search_nodes(query)
315
+ if not results:
316
+ return json.dumps({"error": f"No nodes found for: {query}"})
317
+ top = results[0]
318
+ return json.dumps({
319
+ "title": top.get("title"),
320
+ "domain": top.get("domain"),
321
+ "bluf": top.get("bluf"),
322
+ "node_id": top.get("node_id"),
323
+ "url": f"https://bidda.com/intelligence/{top.get('node_id')}",
324
+ "related": [r["node_id"] for r in results[1:4]],
325
+ }, indent=2)
326
+
327
+ def register(self, agent: Any) -> None:
328
+ """Register this tool with an AutoGen agent."""
329
+ try:
330
+ agent.register_function(
331
+ function_map={"bidda_compliance_lookup": lambda q: self.execute(q)}
332
+ )
333
+ except AttributeError:
334
+ raise RuntimeError("Agent does not support register_function. Check your AutoGen version.")
335
+
336
+
337
+ # ── CrewAI Tool ────────────────────────────────────────────────────────────────
338
+
339
+ try:
340
+ from crewai_tools import BaseTool as CrewBaseTool # type: ignore
341
+
342
+ class BiddaCrewAITool(CrewBaseTool):
343
+ name: str = "Bidda Compliance Lookup"
344
+ description: str = (
345
+ "Look up verified compliance intelligence for any regulation, law, or standard. "
346
+ "Returns source-cited legal obligations from primary instruments. Zero hallucination."
347
+ )
348
+
349
+ skyfire_token: Optional[str] = None
350
+
351
+ def _run(self, query: str) -> str:
352
+ client = BiddaShield(skyfire_token=self.skyfire_token)
353
+ results = client.search_nodes(query)
354
+ if not results:
355
+ return f"No nodes found for: {query}"
356
+ top = results[0]
357
+ return (
358
+ f"Regulation: {top.get('title')}\n"
359
+ f"Domain: {top.get('domain')}\n"
360
+ f"Summary: {top.get('bluf')}\n"
361
+ f"Full node: https://bidda.com/intelligence/{top.get('node_id')}\n"
362
+ )
363
+
364
+ except ImportError:
365
+ class BiddaCrewAITool: # type: ignore
366
+ """crewai-tools not installed. Install with: pip install crewai-tools"""
367
+ def __init__(self, **_):
368
+ raise ImportError("crewai-tools not installed. Run: pip install crewai crewai-tools")