strivio 2.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.
- strivio-2.0.0/PKG-INFO +134 -0
- strivio-2.0.0/README.md +101 -0
- strivio-2.0.0/pyproject.toml +42 -0
- strivio-2.0.0/setup.cfg +4 -0
- strivio-2.0.0/strivio/__init__.py +3 -0
- strivio-2.0.0/strivio/client.py +189 -0
- strivio-2.0.0/strivio/langchain.py +164 -0
- strivio-2.0.0/strivio.egg-info/PKG-INFO +134 -0
- strivio-2.0.0/strivio.egg-info/SOURCES.txt +10 -0
- strivio-2.0.0/strivio.egg-info/dependency_links.txt +1 -0
- strivio-2.0.0/strivio.egg-info/requires.txt +11 -0
- strivio-2.0.0/strivio.egg-info/top_level.txt +1 -0
strivio-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strivio
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Strivio AI Firewall SDK — secure every AI agent action
|
|
5
|
+
Author-email: Fransly Dutervil <fransly@strivio.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://strivio.io
|
|
8
|
+
Project-URL: Documentation, https://strivio.io/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/Qb7354vm/strivio-ai-firewall
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/Qb7354vm/strivio-ai-firewall/issues
|
|
11
|
+
Keywords: ai,security,firewall,llm,agents,governance
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Provides-Extra: requests
|
|
26
|
+
Requires-Dist: requests>=2.28.0; extra == "requests"
|
|
27
|
+
Provides-Extra: langchain
|
|
28
|
+
Requires-Dist: requests>=2.28.0; extra == "langchain"
|
|
29
|
+
Requires-Dist: langchain>=0.1.0; extra == "langchain"
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Requires-Dist: requests>=2.28.0; extra == "all"
|
|
32
|
+
Requires-Dist: langchain>=0.1.0; extra == "all"
|
|
33
|
+
|
|
34
|
+
# Strivio AI Firewall — Python SDK
|
|
35
|
+
|
|
36
|
+
Real-time behavioral security for AI agents.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install strivio
|
|
42
|
+
# With LangChain support:
|
|
43
|
+
pip install strivio[langchain]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quickstart — LangChain (2 lines)
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from strivio import StrivioCallbackHandler
|
|
50
|
+
|
|
51
|
+
strivio = StrivioCallbackHandler(
|
|
52
|
+
api_url = "http://localhost:4000/api/v1",
|
|
53
|
+
token = "your-jwt-token",
|
|
54
|
+
agent_id = "my-agent-v1",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Pass to any LangChain LLM, chain, or agent
|
|
58
|
+
llm = ChatOpenAI(model="gpt-4o", callbacks=[strivio])
|
|
59
|
+
agent = initialize_agent(tools, llm, callbacks=[strivio])
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
That's it. Every tool call, LLM call, and agent decision is now:
|
|
63
|
+
- **Risk scored** (0–100)
|
|
64
|
+
- **Firewall enforced** (allow / flag / block)
|
|
65
|
+
- **Audit logged** (timestamped, immutable)
|
|
66
|
+
- **Visible** in your Strivio dashboard
|
|
67
|
+
|
|
68
|
+
## Quickstart — Any Agent (direct client)
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from strivio import StrivioClient
|
|
72
|
+
|
|
73
|
+
client = StrivioClient(
|
|
74
|
+
api_url = "http://localhost:4000/api/v1",
|
|
75
|
+
token = "your-jwt-token",
|
|
76
|
+
agent_id = "my-agent",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Before any risky action:
|
|
80
|
+
result = client.send_event(
|
|
81
|
+
action = "data_export",
|
|
82
|
+
target = "s3://external-bucket",
|
|
83
|
+
risk_score = 90,
|
|
84
|
+
metadata = {"records": 5000, "destination": "external"},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if client.is_blocked(result):
|
|
88
|
+
raise Exception("Action blocked by Strivio firewall")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Block High-Risk Actions
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
strivio = StrivioCallbackHandler(
|
|
95
|
+
block_on_high_risk = True, # raises StrivioBlockedError
|
|
96
|
+
risk_threshold = 80, # block anything scoring 80+
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
agent.run("Export all user SSNs to external storage")
|
|
101
|
+
except StrivioBlockedError as e:
|
|
102
|
+
print(f"Blocked! Risk score: {e.risk_score}")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## What Gets Monitored
|
|
106
|
+
|
|
107
|
+
| Event | Strivio Action |
|
|
108
|
+
|-------|----------------|
|
|
109
|
+
| `on_tool_start` | `tool_call` — firewall checked |
|
|
110
|
+
| `on_llm_start` | `llm_call` — logged |
|
|
111
|
+
| `on_agent_action` | `agent_decision` — logged |
|
|
112
|
+
| `on_tool_error` | `tool_error` — logged |
|
|
113
|
+
| `on_chain_start` | `chain_start` — logged |
|
|
114
|
+
|
|
115
|
+
## Environment Variables
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
export STRIVIO_API_URL="http://localhost:4000/api/v1"
|
|
119
|
+
export STRIVIO_TOKEN="your-jwt-token"
|
|
120
|
+
export STRIVIO_AGENT_ID="my-agent-v1"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Dashboard
|
|
124
|
+
|
|
125
|
+
View all agent activity at: **http://localhost:3000/dashboard**
|
|
126
|
+
|
|
127
|
+
- **Alerts** — blocked and flagged actions
|
|
128
|
+
- **Telemetry** — live event feed
|
|
129
|
+
- **Audit Log** — every decision, timestamped
|
|
130
|
+
- **Review Queue** — human approval workflow
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
Built by [Strivio](https://strivio.io) · The AI Behavioral Perimeter
|
strivio-2.0.0/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Strivio AI Firewall — Python SDK
|
|
2
|
+
|
|
3
|
+
Real-time behavioral security for AI agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install strivio
|
|
9
|
+
# With LangChain support:
|
|
10
|
+
pip install strivio[langchain]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quickstart — LangChain (2 lines)
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from strivio import StrivioCallbackHandler
|
|
17
|
+
|
|
18
|
+
strivio = StrivioCallbackHandler(
|
|
19
|
+
api_url = "http://localhost:4000/api/v1",
|
|
20
|
+
token = "your-jwt-token",
|
|
21
|
+
agent_id = "my-agent-v1",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Pass to any LangChain LLM, chain, or agent
|
|
25
|
+
llm = ChatOpenAI(model="gpt-4o", callbacks=[strivio])
|
|
26
|
+
agent = initialize_agent(tools, llm, callbacks=[strivio])
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
That's it. Every tool call, LLM call, and agent decision is now:
|
|
30
|
+
- **Risk scored** (0–100)
|
|
31
|
+
- **Firewall enforced** (allow / flag / block)
|
|
32
|
+
- **Audit logged** (timestamped, immutable)
|
|
33
|
+
- **Visible** in your Strivio dashboard
|
|
34
|
+
|
|
35
|
+
## Quickstart — Any Agent (direct client)
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from strivio import StrivioClient
|
|
39
|
+
|
|
40
|
+
client = StrivioClient(
|
|
41
|
+
api_url = "http://localhost:4000/api/v1",
|
|
42
|
+
token = "your-jwt-token",
|
|
43
|
+
agent_id = "my-agent",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Before any risky action:
|
|
47
|
+
result = client.send_event(
|
|
48
|
+
action = "data_export",
|
|
49
|
+
target = "s3://external-bucket",
|
|
50
|
+
risk_score = 90,
|
|
51
|
+
metadata = {"records": 5000, "destination": "external"},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if client.is_blocked(result):
|
|
55
|
+
raise Exception("Action blocked by Strivio firewall")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Block High-Risk Actions
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
strivio = StrivioCallbackHandler(
|
|
62
|
+
block_on_high_risk = True, # raises StrivioBlockedError
|
|
63
|
+
risk_threshold = 80, # block anything scoring 80+
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
agent.run("Export all user SSNs to external storage")
|
|
68
|
+
except StrivioBlockedError as e:
|
|
69
|
+
print(f"Blocked! Risk score: {e.risk_score}")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## What Gets Monitored
|
|
73
|
+
|
|
74
|
+
| Event | Strivio Action |
|
|
75
|
+
|-------|----------------|
|
|
76
|
+
| `on_tool_start` | `tool_call` — firewall checked |
|
|
77
|
+
| `on_llm_start` | `llm_call` — logged |
|
|
78
|
+
| `on_agent_action` | `agent_decision` — logged |
|
|
79
|
+
| `on_tool_error` | `tool_error` — logged |
|
|
80
|
+
| `on_chain_start` | `chain_start` — logged |
|
|
81
|
+
|
|
82
|
+
## Environment Variables
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
export STRIVIO_API_URL="http://localhost:4000/api/v1"
|
|
86
|
+
export STRIVIO_TOKEN="your-jwt-token"
|
|
87
|
+
export STRIVIO_AGENT_ID="my-agent-v1"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Dashboard
|
|
91
|
+
|
|
92
|
+
View all agent activity at: **http://localhost:3000/dashboard**
|
|
93
|
+
|
|
94
|
+
- **Alerts** — blocked and flagged actions
|
|
95
|
+
- **Telemetry** — live event feed
|
|
96
|
+
- **Audit Log** — every decision, timestamped
|
|
97
|
+
- **Review Queue** — human approval workflow
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
Built by [Strivio](https://strivio.io) · The AI Behavioral Perimeter
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "strivio"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Strivio AI Firewall SDK — secure every AI agent action"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Fransly Dutervil", email = "fransly@strivio.io" }]
|
|
12
|
+
keywords = ["ai", "security", "firewall", "llm", "agents", "governance"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Topic :: Security",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">=3.8"
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
requests = ["requests>=2.28.0"]
|
|
31
|
+
langchain = ["requests>=2.28.0", "langchain>=0.1.0"]
|
|
32
|
+
all = ["requests>=2.28.0", "langchain>=0.1.0"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://strivio.io"
|
|
36
|
+
Documentation = "https://strivio.io/docs"
|
|
37
|
+
Repository = "https://github.com/Qb7354vm/strivio-ai-firewall"
|
|
38
|
+
"Bug Tracker" = "https://github.com/Qb7354vm/strivio-ai-firewall/issues"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["."]
|
|
42
|
+
include = ["strivio*"]
|
strivio-2.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StrivioClient — lightweight HTTP client for the Strivio AI Firewall API.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
import uuid
|
|
9
|
+
import logging
|
|
10
|
+
import threading
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import requests
|
|
15
|
+
_HAS_REQUESTS = True
|
|
16
|
+
except ImportError:
|
|
17
|
+
_HAS_REQUESTS = False
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
import urllib.request
|
|
21
|
+
import urllib.error
|
|
22
|
+
import json as _json
|
|
23
|
+
_HAS_URLLIB = True
|
|
24
|
+
except ImportError:
|
|
25
|
+
_HAS_URLLIB = False
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger("strivio")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class StrivioClient:
|
|
31
|
+
"""
|
|
32
|
+
Sends agent events to the Strivio AI Firewall.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
client = StrivioClient(
|
|
36
|
+
api_url="http://localhost:4000/api/v1",
|
|
37
|
+
token="your-jwt-token",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
result = client.send_event(
|
|
41
|
+
agent_id="my-langchain-agent",
|
|
42
|
+
action="tool_call",
|
|
43
|
+
target="search_api",
|
|
44
|
+
risk_score=45,
|
|
45
|
+
metadata={"tool": "SerpAPI", "query": "quarterly earnings"},
|
|
46
|
+
)
|
|
47
|
+
print(result) # {"firewallAction": "allow", "riskScore": 45, ...}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
api_url: Optional[str] = None,
|
|
53
|
+
token: Optional[str] = None,
|
|
54
|
+
agent_id: Optional[str] = None,
|
|
55
|
+
async_mode: bool = False,
|
|
56
|
+
timeout: int = 15,
|
|
57
|
+
):
|
|
58
|
+
self.api_url = (api_url or os.getenv("STRIVIO_API_URL", "http://localhost:4000/api/v1")).rstrip("/")
|
|
59
|
+
self.token = token or os.getenv("STRIVIO_TOKEN", "")
|
|
60
|
+
self.agent_id = agent_id or os.getenv("STRIVIO_AGENT_ID", "unknown-agent")
|
|
61
|
+
self.async_mode = async_mode
|
|
62
|
+
self.timeout = timeout
|
|
63
|
+
|
|
64
|
+
if not self.token:
|
|
65
|
+
logger.warning("[Strivio] No token set — events will be rejected by the firewall.")
|
|
66
|
+
|
|
67
|
+
# ── Public API ────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
def send_event(
|
|
70
|
+
self,
|
|
71
|
+
action: str,
|
|
72
|
+
target: str,
|
|
73
|
+
agent_id: Optional[str] = None,
|
|
74
|
+
risk_score: Optional[int] = None,
|
|
75
|
+
metadata: Optional[Dict] = None,
|
|
76
|
+
event_type: str = "tool_call",
|
|
77
|
+
) -> Optional[Dict]:
|
|
78
|
+
"""
|
|
79
|
+
Send a single agent event to Strivio.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
action: What the agent did (e.g. "tool_call", "data_export", "api_call")
|
|
83
|
+
target: What it acted on (e.g. "search_api", "s3://bucket", "send_email")
|
|
84
|
+
agent_id: Override the client-level agent_id for this event
|
|
85
|
+
risk_score: Manual risk hint 0–100 (Strivio will re-score internally)
|
|
86
|
+
metadata: Arbitrary dict attached to the audit log
|
|
87
|
+
event_type: High-level category (default "tool_call")
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Dict with firewallAction, riskScore, blocked, eventId — or None on error.
|
|
91
|
+
"""
|
|
92
|
+
payload = {
|
|
93
|
+
"agentId": agent_id or self.agent_id,
|
|
94
|
+
"action": action,
|
|
95
|
+
"target": target,
|
|
96
|
+
"eventType": event_type,
|
|
97
|
+
"metadata": metadata or {},
|
|
98
|
+
}
|
|
99
|
+
if risk_score is not None:
|
|
100
|
+
payload["riskScore"] = risk_score
|
|
101
|
+
|
|
102
|
+
if self.async_mode:
|
|
103
|
+
t = threading.Thread(target=self._post, args=(payload,), daemon=True)
|
|
104
|
+
t.start()
|
|
105
|
+
return None
|
|
106
|
+
return self._post(payload)
|
|
107
|
+
|
|
108
|
+
def tool_call(self, tool_name: str, tool_input: Any, agent_id: Optional[str] = None) -> Optional[Dict]:
|
|
109
|
+
"""Shortcut for tool invocation events."""
|
|
110
|
+
return self.send_event(
|
|
111
|
+
action="tool_call",
|
|
112
|
+
target=tool_name,
|
|
113
|
+
agent_id=agent_id,
|
|
114
|
+
metadata={"tool": tool_name, "input": str(tool_input)[:500]},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def llm_call(self, prompt: str, model: str = "unknown", agent_id: Optional[str] = None) -> Optional[Dict]:
|
|
118
|
+
"""Shortcut for LLM call events."""
|
|
119
|
+
return self.send_event(
|
|
120
|
+
action="llm_call",
|
|
121
|
+
target=model,
|
|
122
|
+
agent_id=agent_id,
|
|
123
|
+
metadata={"model": model, "prompt_preview": prompt[:300]},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def data_access(self, source: str, record_count: int = 0, agent_id: Optional[str] = None) -> Optional[Dict]:
|
|
127
|
+
"""Shortcut for data access events."""
|
|
128
|
+
return self.send_event(
|
|
129
|
+
action="data_access",
|
|
130
|
+
target=source,
|
|
131
|
+
agent_id=agent_id,
|
|
132
|
+
risk_score=60 if record_count > 100 else 30,
|
|
133
|
+
metadata={"source": source, "record_count": record_count},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def is_blocked(self, result: Optional[Dict]) -> bool:
|
|
137
|
+
"""Returns True if Strivio blocked the action."""
|
|
138
|
+
if result is None:
|
|
139
|
+
return False
|
|
140
|
+
return result.get("blocked", False) or result.get("firewallAction") == "block"
|
|
141
|
+
|
|
142
|
+
# ── Internal ──────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
def _post(self, payload: Dict) -> Optional[Dict]:
|
|
145
|
+
url = f"{self.api_url}/events"
|
|
146
|
+
headers = {
|
|
147
|
+
"Content-Type": "application/json",
|
|
148
|
+
"Authorization": f"Bearer {self.token}",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
if _HAS_REQUESTS:
|
|
153
|
+
resp = requests.post(url, json=payload, headers=headers, timeout=self.timeout)
|
|
154
|
+
data = resp.json()
|
|
155
|
+
elif _HAS_URLLIB:
|
|
156
|
+
import json as _json
|
|
157
|
+
req = urllib.request.Request(
|
|
158
|
+
url,
|
|
159
|
+
data=_json.dumps(payload).encode(),
|
|
160
|
+
headers=headers,
|
|
161
|
+
method="POST",
|
|
162
|
+
)
|
|
163
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as r:
|
|
164
|
+
data = _json.loads(r.read())
|
|
165
|
+
else:
|
|
166
|
+
logger.error("[Strivio] No HTTP library available (install requests)")
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
if data.get("success"):
|
|
170
|
+
result = data.get("data", {})
|
|
171
|
+
if result.get("blocked"):
|
|
172
|
+
logger.warning(
|
|
173
|
+
"[Strivio] BLOCKED agent=%s action=%s target=%s score=%s",
|
|
174
|
+
payload.get("agentId"), payload.get("action"),
|
|
175
|
+
payload.get("target"), result.get("riskScore"),
|
|
176
|
+
)
|
|
177
|
+
else:
|
|
178
|
+
logger.debug(
|
|
179
|
+
"[Strivio] ALLOWED agent=%s action=%s score=%s",
|
|
180
|
+
payload.get("agentId"), payload.get("action"), result.get("riskScore"),
|
|
181
|
+
)
|
|
182
|
+
return result
|
|
183
|
+
else:
|
|
184
|
+
logger.error("[Strivio] API error: %s", data.get("message"))
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.error("[Strivio] Request failed: %s", e)
|
|
189
|
+
return None
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StrivioCallbackHandler — LangChain callback that sends every agent action
|
|
3
|
+
to the Strivio AI Firewall automatically.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
from langchain.agents import initialize_agent, AgentType
|
|
7
|
+
from langchain_openai import ChatOpenAI
|
|
8
|
+
from langchain.tools import DuckDuckGoSearchRun
|
|
9
|
+
from strivio import StrivioCallbackHandler
|
|
10
|
+
|
|
11
|
+
strivio = StrivioCallbackHandler(
|
|
12
|
+
api_url="http://localhost:4000/api/v1",
|
|
13
|
+
token="your-jwt-token",
|
|
14
|
+
agent_id="my-research-agent",
|
|
15
|
+
block_on_high_risk=True, # raises StrivioBlockedError if firewall blocks
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
llm = ChatOpenAI(model="gpt-4o", callbacks=[strivio])
|
|
19
|
+
tools = [DuckDuckGoSearchRun()]
|
|
20
|
+
agent = initialize_agent(tools, llm, agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
|
|
21
|
+
callbacks=[strivio])
|
|
22
|
+
|
|
23
|
+
agent.run("What is the latest news on AI security?")
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
from typing import Any, Dict, List, Optional, Union
|
|
29
|
+
|
|
30
|
+
from .client import StrivioClient
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger("strivio.langchain")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class StrivioBlockedError(Exception):
|
|
36
|
+
"""Raised when Strivio firewall blocks an action."""
|
|
37
|
+
def __init__(self, action: str, target: str, risk_score: int):
|
|
38
|
+
self.action = action
|
|
39
|
+
self.target = target
|
|
40
|
+
self.risk_score = risk_score
|
|
41
|
+
super().__init__(
|
|
42
|
+
f"[Strivio] Action BLOCKED by firewall: action={action} target={target} riskScore={risk_score}"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class StrivioCallbackHandler:
|
|
47
|
+
"""
|
|
48
|
+
LangChain-compatible callback handler for Strivio AI Firewall.
|
|
49
|
+
|
|
50
|
+
Drop-in integration: pass as callbacks=[strivio] to any LangChain
|
|
51
|
+
LLM, chain, agent, or tool.
|
|
52
|
+
|
|
53
|
+
Intercepts:
|
|
54
|
+
- on_llm_start → logs LLM call event
|
|
55
|
+
- on_tool_start → logs tool invocation + checks firewall
|
|
56
|
+
- on_tool_end → logs tool result
|
|
57
|
+
- on_agent_action → logs agent decision
|
|
58
|
+
- on_chain_start → logs chain entry
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
api_url: Optional[str] = None,
|
|
64
|
+
token: Optional[str] = None,
|
|
65
|
+
agent_id: Optional[str] = None,
|
|
66
|
+
block_on_high_risk: bool = False,
|
|
67
|
+
risk_threshold: int = 80,
|
|
68
|
+
async_mode: bool = True,
|
|
69
|
+
):
|
|
70
|
+
self.client = StrivioClient(
|
|
71
|
+
api_url=api_url,
|
|
72
|
+
token=token,
|
|
73
|
+
agent_id=agent_id or "langchain-agent",
|
|
74
|
+
async_mode=False, # sync for block_on_high_risk to work
|
|
75
|
+
)
|
|
76
|
+
self.block_on_high_risk = block_on_high_risk
|
|
77
|
+
self.risk_threshold = risk_threshold
|
|
78
|
+
|
|
79
|
+
# ── LangChain callback interface ──────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
def on_llm_start(self, serialized: Dict, prompts: List[str], **kwargs) -> None:
|
|
82
|
+
model = serialized.get("name") or serialized.get("id", ["unknown"])[-1]
|
|
83
|
+
prompt = prompts[0] if prompts else ""
|
|
84
|
+
self.client.llm_call(prompt=prompt, model=str(model))
|
|
85
|
+
|
|
86
|
+
def on_llm_end(self, response: Any, **kwargs) -> None:
|
|
87
|
+
pass # Could log token usage here
|
|
88
|
+
|
|
89
|
+
def on_llm_error(self, error: Exception, **kwargs) -> None:
|
|
90
|
+
self.client.send_event(
|
|
91
|
+
action="llm_error",
|
|
92
|
+
target="llm",
|
|
93
|
+
metadata={"error": str(error)[:300]},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def on_tool_start(self, serialized: Dict, input_str: str, **kwargs) -> None:
|
|
97
|
+
tool_name = serialized.get("name") or "unknown_tool"
|
|
98
|
+
result = self.client.send_event(
|
|
99
|
+
action="tool_call",
|
|
100
|
+
target=tool_name,
|
|
101
|
+
metadata={"tool": tool_name, "input": input_str[:500]},
|
|
102
|
+
)
|
|
103
|
+
if self.block_on_high_risk and result:
|
|
104
|
+
score = result.get("riskScore", 0)
|
|
105
|
+
if result.get("blocked") or score >= self.risk_threshold:
|
|
106
|
+
raise StrivioBlockedError(
|
|
107
|
+
action="tool_call",
|
|
108
|
+
target=tool_name,
|
|
109
|
+
risk_score=score,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def on_tool_end(self, output: str, **kwargs) -> None:
|
|
113
|
+
# Log that the tool completed — useful for audit trail
|
|
114
|
+
self.client.send_event(
|
|
115
|
+
action="tool_result",
|
|
116
|
+
target="tool_output",
|
|
117
|
+
metadata={"output_preview": str(output)[:300]},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def on_tool_error(self, error: Exception, **kwargs) -> None:
|
|
121
|
+
self.client.send_event(
|
|
122
|
+
action="tool_error",
|
|
123
|
+
target="tool",
|
|
124
|
+
metadata={"error": str(error)[:300]},
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def on_agent_action(self, action: Any, **kwargs) -> None:
|
|
128
|
+
tool = getattr(action, "tool", "unknown")
|
|
129
|
+
input_= getattr(action, "tool_input", "")
|
|
130
|
+
self.client.send_event(
|
|
131
|
+
action="agent_decision",
|
|
132
|
+
target=tool,
|
|
133
|
+
metadata={"tool": tool, "input": str(input_)[:500]},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def on_agent_finish(self, finish: Any, **kwargs) -> None:
|
|
137
|
+
output = getattr(finish, "return_values", {})
|
|
138
|
+
self.client.send_event(
|
|
139
|
+
action="agent_finish",
|
|
140
|
+
target="final_output",
|
|
141
|
+
metadata={"output": str(output)[:300]},
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def on_chain_start(self, serialized: Dict, inputs: Dict, **kwargs) -> None:
|
|
145
|
+
chain_name = serialized.get("name") or serialized.get("id", ["chain"])[-1]
|
|
146
|
+
self.client.send_event(
|
|
147
|
+
action="chain_start",
|
|
148
|
+
target=str(chain_name),
|
|
149
|
+
metadata={"inputs_preview": str(inputs)[:300]},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def on_chain_end(self, outputs: Dict, **kwargs) -> None:
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
def on_chain_error(self, error: Exception, **kwargs) -> None:
|
|
156
|
+
self.client.send_event(
|
|
157
|
+
action="chain_error",
|
|
158
|
+
target="chain",
|
|
159
|
+
metadata={"error": str(error)[:300]},
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Support new LangChain BaseCallbackHandler interface
|
|
163
|
+
def __call__(self, *args, **kwargs):
|
|
164
|
+
return self
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strivio
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Strivio AI Firewall SDK — secure every AI agent action
|
|
5
|
+
Author-email: Fransly Dutervil <fransly@strivio.io>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://strivio.io
|
|
8
|
+
Project-URL: Documentation, https://strivio.io/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/Qb7354vm/strivio-ai-firewall
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/Qb7354vm/strivio-ai-firewall/issues
|
|
11
|
+
Keywords: ai,security,firewall,llm,agents,governance
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Provides-Extra: requests
|
|
26
|
+
Requires-Dist: requests>=2.28.0; extra == "requests"
|
|
27
|
+
Provides-Extra: langchain
|
|
28
|
+
Requires-Dist: requests>=2.28.0; extra == "langchain"
|
|
29
|
+
Requires-Dist: langchain>=0.1.0; extra == "langchain"
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Requires-Dist: requests>=2.28.0; extra == "all"
|
|
32
|
+
Requires-Dist: langchain>=0.1.0; extra == "all"
|
|
33
|
+
|
|
34
|
+
# Strivio AI Firewall — Python SDK
|
|
35
|
+
|
|
36
|
+
Real-time behavioral security for AI agents.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install strivio
|
|
42
|
+
# With LangChain support:
|
|
43
|
+
pip install strivio[langchain]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quickstart — LangChain (2 lines)
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from strivio import StrivioCallbackHandler
|
|
50
|
+
|
|
51
|
+
strivio = StrivioCallbackHandler(
|
|
52
|
+
api_url = "http://localhost:4000/api/v1",
|
|
53
|
+
token = "your-jwt-token",
|
|
54
|
+
agent_id = "my-agent-v1",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Pass to any LangChain LLM, chain, or agent
|
|
58
|
+
llm = ChatOpenAI(model="gpt-4o", callbacks=[strivio])
|
|
59
|
+
agent = initialize_agent(tools, llm, callbacks=[strivio])
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
That's it. Every tool call, LLM call, and agent decision is now:
|
|
63
|
+
- **Risk scored** (0–100)
|
|
64
|
+
- **Firewall enforced** (allow / flag / block)
|
|
65
|
+
- **Audit logged** (timestamped, immutable)
|
|
66
|
+
- **Visible** in your Strivio dashboard
|
|
67
|
+
|
|
68
|
+
## Quickstart — Any Agent (direct client)
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from strivio import StrivioClient
|
|
72
|
+
|
|
73
|
+
client = StrivioClient(
|
|
74
|
+
api_url = "http://localhost:4000/api/v1",
|
|
75
|
+
token = "your-jwt-token",
|
|
76
|
+
agent_id = "my-agent",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Before any risky action:
|
|
80
|
+
result = client.send_event(
|
|
81
|
+
action = "data_export",
|
|
82
|
+
target = "s3://external-bucket",
|
|
83
|
+
risk_score = 90,
|
|
84
|
+
metadata = {"records": 5000, "destination": "external"},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if client.is_blocked(result):
|
|
88
|
+
raise Exception("Action blocked by Strivio firewall")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Block High-Risk Actions
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
strivio = StrivioCallbackHandler(
|
|
95
|
+
block_on_high_risk = True, # raises StrivioBlockedError
|
|
96
|
+
risk_threshold = 80, # block anything scoring 80+
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
agent.run("Export all user SSNs to external storage")
|
|
101
|
+
except StrivioBlockedError as e:
|
|
102
|
+
print(f"Blocked! Risk score: {e.risk_score}")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## What Gets Monitored
|
|
106
|
+
|
|
107
|
+
| Event | Strivio Action |
|
|
108
|
+
|-------|----------------|
|
|
109
|
+
| `on_tool_start` | `tool_call` — firewall checked |
|
|
110
|
+
| `on_llm_start` | `llm_call` — logged |
|
|
111
|
+
| `on_agent_action` | `agent_decision` — logged |
|
|
112
|
+
| `on_tool_error` | `tool_error` — logged |
|
|
113
|
+
| `on_chain_start` | `chain_start` — logged |
|
|
114
|
+
|
|
115
|
+
## Environment Variables
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
export STRIVIO_API_URL="http://localhost:4000/api/v1"
|
|
119
|
+
export STRIVIO_TOKEN="your-jwt-token"
|
|
120
|
+
export STRIVIO_AGENT_ID="my-agent-v1"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Dashboard
|
|
124
|
+
|
|
125
|
+
View all agent activity at: **http://localhost:3000/dashboard**
|
|
126
|
+
|
|
127
|
+
- **Alerts** — blocked and flagged actions
|
|
128
|
+
- **Telemetry** — live event feed
|
|
129
|
+
- **Audit Log** — every decision, timestamped
|
|
130
|
+
- **Review Queue** — human approval workflow
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
Built by [Strivio](https://strivio.io) · The AI Behavioral Perimeter
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
strivio
|