authbinder 1.0.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.
- authbinder-1.0.0/PKG-INFO +56 -0
- authbinder-1.0.0/README.md +39 -0
- authbinder-1.0.0/authbinder/__init__.py +236 -0
- authbinder-1.0.0/authbinder.egg-info/PKG-INFO +56 -0
- authbinder-1.0.0/authbinder.egg-info/SOURCES.txt +8 -0
- authbinder-1.0.0/authbinder.egg-info/dependency_links.txt +1 -0
- authbinder-1.0.0/authbinder.egg-info/requires.txt +1 -0
- authbinder-1.0.0/authbinder.egg-info/top_level.txt +1 -0
- authbinder-1.0.0/pyproject.toml +32 -0
- authbinder-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: authbinder
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AI agent audit platform SDK. Captures telemetry from LangChain, CrewAI, AutoGen, LlamaIndex, and Haystack agents.
|
|
5
|
+
Author-email: AuthBinder <info@getauthbinder.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://authbinder.com
|
|
8
|
+
Keywords: ai,agents,audit,security,compliance,langchain,crewai,autogen
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Security
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: httpx>=0.24.0
|
|
17
|
+
|
|
18
|
+
# AuthBinder SDK
|
|
19
|
+
|
|
20
|
+
AI agent audit platform. Automatically captures telemetry from your AI agents for risk scoring and compliance reporting.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
pip install authbinder
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
from authbinder import init, auto_instrument
|
|
29
|
+
|
|
30
|
+
init(
|
|
31
|
+
tenant_id="your-tenant-id",
|
|
32
|
+
api_key="ab_live_your-api-key",
|
|
33
|
+
package="baseline"
|
|
34
|
+
)
|
|
35
|
+
auto_instrument()
|
|
36
|
+
|
|
37
|
+
## Supported frameworks
|
|
38
|
+
|
|
39
|
+
- LangChain
|
|
40
|
+
- CrewAI
|
|
41
|
+
- AutoGen
|
|
42
|
+
- LlamaIndex
|
|
43
|
+
- Haystack
|
|
44
|
+
|
|
45
|
+
## Manual instrumentation
|
|
46
|
+
|
|
47
|
+
from authbinder import observe
|
|
48
|
+
|
|
49
|
+
@observe(destination="salesforce", action="read")
|
|
50
|
+
def crm_lookup(customer_id):
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
## Links
|
|
54
|
+
|
|
55
|
+
- Website: https://authbinder.com
|
|
56
|
+
- Support: info@getauthbinder.com
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# AuthBinder SDK
|
|
2
|
+
|
|
3
|
+
AI agent audit platform. Automatically captures telemetry from your AI agents for risk scoring and compliance reporting.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
pip install authbinder
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
from authbinder import init, auto_instrument
|
|
12
|
+
|
|
13
|
+
init(
|
|
14
|
+
tenant_id="your-tenant-id",
|
|
15
|
+
api_key="ab_live_your-api-key",
|
|
16
|
+
package="baseline"
|
|
17
|
+
)
|
|
18
|
+
auto_instrument()
|
|
19
|
+
|
|
20
|
+
## Supported frameworks
|
|
21
|
+
|
|
22
|
+
- LangChain
|
|
23
|
+
- CrewAI
|
|
24
|
+
- AutoGen
|
|
25
|
+
- LlamaIndex
|
|
26
|
+
- Haystack
|
|
27
|
+
|
|
28
|
+
## Manual instrumentation
|
|
29
|
+
|
|
30
|
+
from authbinder import observe
|
|
31
|
+
|
|
32
|
+
@observe(destination="salesforce", action="read")
|
|
33
|
+
def crm_lookup(customer_id):
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
## Links
|
|
37
|
+
|
|
38
|
+
- Website: https://authbinder.com
|
|
39
|
+
- Support: info@getauthbinder.com
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AuthBinder SDK v1.0
|
|
3
|
+
pip install authbinder
|
|
4
|
+
"""
|
|
5
|
+
import os, time, uuid, hashlib, logging, functools, threading, queue
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from typing import Optional, Callable
|
|
8
|
+
from dataclasses import dataclass, field, asdict
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("authbinder")
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class _Config:
|
|
15
|
+
tenant_id: str = ""
|
|
16
|
+
api_key: str = ""
|
|
17
|
+
api_url: str = "https://authbinder-prod-api.azurewebsites.net"
|
|
18
|
+
agent_id: str = "default-agent"
|
|
19
|
+
user_id: Optional[str] = None
|
|
20
|
+
batch_size: int = 50
|
|
21
|
+
flush_interval: float = 5.0
|
|
22
|
+
enabled: bool = False
|
|
23
|
+
debug: bool = False
|
|
24
|
+
package: str = "baseline"
|
|
25
|
+
expired: bool = False
|
|
26
|
+
|
|
27
|
+
_config = _Config()
|
|
28
|
+
_event_queue: queue.Queue = queue.Queue(maxsize=10000)
|
|
29
|
+
_flush_thread = None
|
|
30
|
+
_running = threading.Event()
|
|
31
|
+
|
|
32
|
+
def init(tenant_id=None, api_key=None, agent_id=None, api_url=None, package="baseline", debug=False):
|
|
33
|
+
global _config
|
|
34
|
+
_config.tenant_id = tenant_id or os.environ.get("AUTHBINDER_TENANT_ID", "")
|
|
35
|
+
_config.api_key = api_key or os.environ.get("AUTHBINDER_API_KEY", "")
|
|
36
|
+
_config.agent_id = agent_id or os.environ.get("AUTHBINDER_AGENT_ID", "default-agent")
|
|
37
|
+
_config.api_url = api_url or os.environ.get("AUTHBINDER_API_URL", "https://authbinder-prod-api.azurewebsites.net")
|
|
38
|
+
_config.package = package
|
|
39
|
+
_config.debug = debug
|
|
40
|
+
if not _config.tenant_id or not _config.api_key:
|
|
41
|
+
logger.warning("AuthBinder: credentials not set")
|
|
42
|
+
return
|
|
43
|
+
_config.enabled = True
|
|
44
|
+
_start_flush_thread()
|
|
45
|
+
logger.info(f"AuthBinder initialised - tenant={_config.tenant_id}")
|
|
46
|
+
|
|
47
|
+
def auto_instrument():
|
|
48
|
+
if not _config.enabled:
|
|
49
|
+
logger.warning("AuthBinder: call init() first")
|
|
50
|
+
return []
|
|
51
|
+
detected = []
|
|
52
|
+
if _patch_langchain(): detected.append("LangChain")
|
|
53
|
+
if _patch_crewai(): detected.append("CrewAI")
|
|
54
|
+
if _patch_autogen(): detected.append("AutoGen")
|
|
55
|
+
if _patch_llamaindex(): detected.append("LlamaIndex")
|
|
56
|
+
if _patch_haystack(): detected.append("Haystack")
|
|
57
|
+
logger.info(f"AuthBinder: instrumented {detected or 'none - use observe()'}")
|
|
58
|
+
return detected
|
|
59
|
+
|
|
60
|
+
def observe(func=None, *, tool_name=None, destination=None, action=None, approved=False):
|
|
61
|
+
def decorator(fn):
|
|
62
|
+
name = tool_name or fn.__name__
|
|
63
|
+
@functools.wraps(fn)
|
|
64
|
+
def wrapper(*args, **kwargs):
|
|
65
|
+
start = time.time()
|
|
66
|
+
success = True
|
|
67
|
+
try:
|
|
68
|
+
return fn(*args, **kwargs)
|
|
69
|
+
except Exception:
|
|
70
|
+
success = False
|
|
71
|
+
raise
|
|
72
|
+
finally:
|
|
73
|
+
_track(name, destination, action or _infer_action(name), approved, success, round((time.time()-start)*1000))
|
|
74
|
+
return wrapper
|
|
75
|
+
return decorator(func) if func is not None else decorator
|
|
76
|
+
|
|
77
|
+
def _patch_langchain():
|
|
78
|
+
try:
|
|
79
|
+
from langchain.tools import BaseTool
|
|
80
|
+
orig = BaseTool._run
|
|
81
|
+
@functools.wraps(orig)
|
|
82
|
+
def patched(self, *a, **kw):
|
|
83
|
+
start = time.time()
|
|
84
|
+
success = True
|
|
85
|
+
try: return orig(self, *a, **kw)
|
|
86
|
+
except Exception: success = False; raise
|
|
87
|
+
finally: _track(getattr(self,'name',self.__class__.__name__), _infer_destination_from_name(getattr(self,'name','')), _infer_action(getattr(self,'name','')), False, success, round((time.time()-start)*1000), "langchain")
|
|
88
|
+
BaseTool._run = patched
|
|
89
|
+
return True
|
|
90
|
+
except: return False
|
|
91
|
+
|
|
92
|
+
def _patch_crewai():
|
|
93
|
+
try:
|
|
94
|
+
from crewai.tools import BaseTool as T
|
|
95
|
+
orig = T._run
|
|
96
|
+
@functools.wraps(orig)
|
|
97
|
+
def patched(self, *a, **kw):
|
|
98
|
+
start = time.time()
|
|
99
|
+
success = True
|
|
100
|
+
try: return orig(self, *a, **kw)
|
|
101
|
+
except Exception: success = False; raise
|
|
102
|
+
finally: _track(getattr(self,'name',self.__class__.__name__), _infer_destination_from_name(getattr(self,'name','')), _infer_action(getattr(self,'name','')), False, success, round((time.time()-start)*1000), "crewai")
|
|
103
|
+
T._run = patched
|
|
104
|
+
return True
|
|
105
|
+
except: return False
|
|
106
|
+
|
|
107
|
+
def _patch_autogen():
|
|
108
|
+
try:
|
|
109
|
+
import autogen
|
|
110
|
+
orig = autogen.ConversableAgent._execute_tool_call
|
|
111
|
+
@functools.wraps(orig)
|
|
112
|
+
def patched(self, tool_call, *a, **kw):
|
|
113
|
+
start = time.time()
|
|
114
|
+
success = True
|
|
115
|
+
try: return orig(self, tool_call, *a, **kw)
|
|
116
|
+
except Exception: success = False; raise
|
|
117
|
+
finally:
|
|
118
|
+
fn = getattr(tool_call, 'function', {})
|
|
119
|
+
name = fn.name if hasattr(fn,'name') else fn.get('name','unknown') if isinstance(fn,dict) else str(fn)
|
|
120
|
+
_track(name, _infer_destination_from_name(name), _infer_action(name), False, success, round((time.time()-start)*1000), "autogen")
|
|
121
|
+
autogen.ConversableAgent._execute_tool_call = patched
|
|
122
|
+
return True
|
|
123
|
+
except: return False
|
|
124
|
+
|
|
125
|
+
def _patch_llamaindex():
|
|
126
|
+
try:
|
|
127
|
+
from llama_index.core.tools import BaseTool as T
|
|
128
|
+
orig = T.__call__
|
|
129
|
+
@functools.wraps(orig)
|
|
130
|
+
def patched(self, *a, **kw):
|
|
131
|
+
start = time.time()
|
|
132
|
+
success = True
|
|
133
|
+
try: return orig(self, *a, **kw)
|
|
134
|
+
except Exception: success = False; raise
|
|
135
|
+
finally:
|
|
136
|
+
meta = getattr(self,'metadata',None)
|
|
137
|
+
name = getattr(meta,'name',self.__class__.__name__) if meta else self.__class__.__name__
|
|
138
|
+
_track(name, _infer_destination_from_name(name), _infer_action(name), False, success, round((time.time()-start)*1000), "llamaindex")
|
|
139
|
+
T.__call__ = patched
|
|
140
|
+
return True
|
|
141
|
+
except: return False
|
|
142
|
+
|
|
143
|
+
def _patch_haystack():
|
|
144
|
+
try:
|
|
145
|
+
from haystack.core.component import Component
|
|
146
|
+
orig = Component.run
|
|
147
|
+
@functools.wraps(orig)
|
|
148
|
+
def patched(self, *a, **kw):
|
|
149
|
+
start = time.time()
|
|
150
|
+
success = True
|
|
151
|
+
try: return orig(self, *a, **kw)
|
|
152
|
+
except Exception: success = False; raise
|
|
153
|
+
finally: _track(self.__class__.__name__, _infer_destination_from_name(self.__class__.__name__), _infer_action(self.__class__.__name__), False, success, round((time.time()-start)*1000), "haystack")
|
|
154
|
+
Component.run = patched
|
|
155
|
+
return True
|
|
156
|
+
except: return False
|
|
157
|
+
|
|
158
|
+
def _infer_action(name):
|
|
159
|
+
n = name.lower()
|
|
160
|
+
if any(w in n for w in ["create","write","send","post","insert","add","upload"]): return "write"
|
|
161
|
+
if any(w in n for w in ["delete","remove","drop","destroy","purge"]): return "delete"
|
|
162
|
+
if any(w in n for w in ["update","patch","edit","modify"]): return "update"
|
|
163
|
+
return "read"
|
|
164
|
+
|
|
165
|
+
def _infer_destination_from_name(name):
|
|
166
|
+
n = name.lower()
|
|
167
|
+
for dest, keys in {"salesforce":["salesforce","crm"],"stripe":["stripe","payment"],"sendgrid":["email","smtp","mail"],"jira":["jira","ticket"],"github":["github","git"],"slack":["slack"],"postgres":["postgres","sql","database"],"s3":["s3","bucket","blob"],"openai":["openai","gpt"],"google":["google","gmail"]}.items():
|
|
168
|
+
if any(k in n for k in keys): return dest
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
@dataclass
|
|
172
|
+
class _Event:
|
|
173
|
+
tenantId: str
|
|
174
|
+
agentId: str
|
|
175
|
+
tool: str
|
|
176
|
+
timestamp: str
|
|
177
|
+
userId: Optional[str] = None
|
|
178
|
+
destination: Optional[str] = None
|
|
179
|
+
action: Optional[str] = None
|
|
180
|
+
approved: bool = False
|
|
181
|
+
success: bool = True
|
|
182
|
+
durationMs: Optional[int] = None
|
|
183
|
+
framework: Optional[str] = None
|
|
184
|
+
metadata: dict = field(default_factory=dict)
|
|
185
|
+
|
|
186
|
+
def _track(tool, destination, action, approved, success, duration_ms, framework=None, metadata=None):
|
|
187
|
+
if not _config.enabled or _config.expired: return
|
|
188
|
+
try:
|
|
189
|
+
_event_queue.put_nowait(_Event(tenantId=_config.tenant_id, agentId=_config.agent_id, userId=_config.user_id, tool=tool, destination=destination, action=action, approved=approved, success=success, durationMs=duration_ms, framework=framework, timestamp=datetime.now(timezone.utc).isoformat(), metadata=metadata or {}))
|
|
190
|
+
except queue.Full:
|
|
191
|
+
logger.warning("AuthBinder: queue full")
|
|
192
|
+
|
|
193
|
+
def _flush():
|
|
194
|
+
if _event_queue.empty(): return
|
|
195
|
+
batch = []
|
|
196
|
+
while not _event_queue.empty() and len(batch) < _config.batch_size:
|
|
197
|
+
try: batch.append(_event_queue.get_nowait())
|
|
198
|
+
except queue.Empty: break
|
|
199
|
+
if not batch: return
|
|
200
|
+
try:
|
|
201
|
+
r = httpx.post(f"{_config.api_url}/events/batch", json={"events":[asdict(e) for e in batch]}, headers={"X-API-Key":_config.api_key}, timeout=10.0)
|
|
202
|
+
if r.status_code == 403: _config.expired = True; _running.clear(); return
|
|
203
|
+
r.raise_for_status()
|
|
204
|
+
except Exception as e:
|
|
205
|
+
logger.error(f"AuthBinder flush failed: {e}")
|
|
206
|
+
for ev in batch:
|
|
207
|
+
try: _event_queue.put_nowait(ev)
|
|
208
|
+
except queue.Full: pass
|
|
209
|
+
|
|
210
|
+
def _flush_loop():
|
|
211
|
+
while _running.is_set():
|
|
212
|
+
time.sleep(_config.flush_interval)
|
|
213
|
+
_flush()
|
|
214
|
+
_flush()
|
|
215
|
+
|
|
216
|
+
def _start_flush_thread():
|
|
217
|
+
global _flush_thread
|
|
218
|
+
_running.set()
|
|
219
|
+
_flush_thread = threading.Thread(target=_flush_loop, daemon=True, name="authbinder-flush")
|
|
220
|
+
_flush_thread.start()
|
|
221
|
+
|
|
222
|
+
def shutdown():
|
|
223
|
+
_running.clear()
|
|
224
|
+
if _flush_thread: _flush_thread.join(timeout=10)
|
|
225
|
+
|
|
226
|
+
def ping():
|
|
227
|
+
try:
|
|
228
|
+
r = httpx.get(f"{_config.api_url}/health", timeout=5.0)
|
|
229
|
+
print("Connected to AuthBinder" if r.status_code == 200 else f"AuthBinder returned {r.status_code}")
|
|
230
|
+
return r.status_code == 200
|
|
231
|
+
except Exception as e:
|
|
232
|
+
print(f"Could not reach AuthBinder: {e}")
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
if os.environ.get("AUTHBINDER_API_KEY") and os.environ.get("AUTHBINDER_TENANT_ID"):
|
|
236
|
+
init()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: authbinder
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AI agent audit platform SDK. Captures telemetry from LangChain, CrewAI, AutoGen, LlamaIndex, and Haystack agents.
|
|
5
|
+
Author-email: AuthBinder <info@getauthbinder.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://authbinder.com
|
|
8
|
+
Keywords: ai,agents,audit,security,compliance,langchain,crewai,autogen
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Security
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: httpx>=0.24.0
|
|
17
|
+
|
|
18
|
+
# AuthBinder SDK
|
|
19
|
+
|
|
20
|
+
AI agent audit platform. Automatically captures telemetry from your AI agents for risk scoring and compliance reporting.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
pip install authbinder
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
from authbinder import init, auto_instrument
|
|
29
|
+
|
|
30
|
+
init(
|
|
31
|
+
tenant_id="your-tenant-id",
|
|
32
|
+
api_key="ab_live_your-api-key",
|
|
33
|
+
package="baseline"
|
|
34
|
+
)
|
|
35
|
+
auto_instrument()
|
|
36
|
+
|
|
37
|
+
## Supported frameworks
|
|
38
|
+
|
|
39
|
+
- LangChain
|
|
40
|
+
- CrewAI
|
|
41
|
+
- AutoGen
|
|
42
|
+
- LlamaIndex
|
|
43
|
+
- Haystack
|
|
44
|
+
|
|
45
|
+
## Manual instrumentation
|
|
46
|
+
|
|
47
|
+
from authbinder import observe
|
|
48
|
+
|
|
49
|
+
@observe(destination="salesforce", action="read")
|
|
50
|
+
def crm_lookup(customer_id):
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
## Links
|
|
54
|
+
|
|
55
|
+
- Website: https://authbinder.com
|
|
56
|
+
- Support: info@getauthbinder.com
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx>=0.24.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
authbinder
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "authbinder"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "AI agent audit platform SDK. Captures telemetry from LangChain, CrewAI, AutoGen, LlamaIndex, and Haystack agents."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "AuthBinder", email = "info@getauthbinder.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ai", "agents", "audit", "security", "compliance", "langchain", "crewai", "autogen"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Topic :: Security",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"httpx>=0.24.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://authbinder.com"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["."]
|
|
32
|
+
include = ["authbinder*"]
|