safentic 1.0.4__tar.gz → 1.0.6__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.
- {safentic-1.0.4/safentic → safentic-1.0.6}/LICENSE.txt +36 -36
- safentic-1.0.6/MANIFEST.in +15 -0
- safentic-1.0.6/PKG-INFO +193 -0
- safentic-1.0.6/README.md +160 -0
- safentic-1.0.6/pyproject.toml +55 -0
- safentic-1.0.6/requirements/constraints.txt +16 -0
- safentic-1.0.6/requirements/requirements-dev.txt +21 -0
- {safentic-1.0.4 → safentic-1.0.6/requirements}/requirements.txt +9 -10
- safentic-1.0.6/safentic/__init__.py +5 -0
- safentic-1.0.6/safentic/_internal/errors.py +26 -0
- safentic-1.0.6/safentic/adapters/mcp_adapter.py +46 -0
- safentic-1.0.6/safentic/cli/__init__.py +3 -0
- safentic-1.0.6/safentic/cli/commands/check_tool.py +47 -0
- safentic-1.0.6/safentic/cli/commands/logs.py +66 -0
- safentic-1.0.6/safentic/cli/commands/validate_policy.py +59 -0
- safentic-1.0.6/safentic/cli/main.py +153 -0
- safentic-1.0.6/safentic/cli/utils.py +169 -0
- {safentic-1.0.4 → safentic-1.0.6}/safentic/config.py +2 -2
- safentic-1.0.6/safentic/decorators.py +49 -0
- safentic-1.0.6/safentic/helper/helper.py +212 -0
- safentic-1.0.6/safentic/layer.py +96 -0
- safentic-1.0.6/safentic/logger/audit.py +181 -0
- safentic-1.0.6/safentic/policy_enforcer.py +116 -0
- safentic-1.0.6/safentic/policy_engine.py +141 -0
- safentic-1.0.6/safentic/verifiers/llm_verifier.py +238 -0
- safentic-1.0.6/safentic.egg-info/PKG-INFO +193 -0
- safentic-1.0.6/safentic.egg-info/SOURCES.txt +36 -0
- safentic-1.0.6/safentic.egg-info/entry_points.txt +2 -0
- safentic-1.0.6/safentic.egg-info/requires.txt +20 -0
- safentic-1.0.6/safentic.egg-info/top_level.txt +2 -0
- safentic-1.0.6/safentic_poc/backend/api/__init__.py +0 -0
- safentic-1.0.6/safentic_poc/backend/api/main.py +164 -0
- {safentic-1.0.4 → safentic-1.0.6}/setup.cfg +4 -4
- safentic-1.0.4/MANIFEST.in +0 -18
- safentic-1.0.4/PKG-INFO +0 -60
- safentic-1.0.4/README.md +0 -33
- safentic-1.0.4/safentic/__init__.py +0 -8
- safentic-1.0.4/safentic/engine.py +0 -80
- safentic-1.0.4/safentic/helper/auth.py +0 -12
- safentic-1.0.4/safentic/layer.py +0 -56
- safentic-1.0.4/safentic/logger/audit.py +0 -83
- safentic-1.0.4/safentic/policies/__init__.py +0 -3
- safentic-1.0.4/safentic/policies/example_policy.txt +0 -33
- safentic-1.0.4/safentic/policies/policy.yaml +0 -49
- safentic-1.0.4/safentic/policy.py +0 -106
- safentic-1.0.4/safentic/verifiers/sentence_verifier.py +0 -69
- safentic-1.0.4/safentic.egg-info/PKG-INFO +0 -60
- safentic-1.0.4/safentic.egg-info/SOURCES.txt +0 -25
- safentic-1.0.4/safentic.egg-info/requires.txt +0 -5
- safentic-1.0.4/safentic.egg-info/top_level.txt +0 -2
- safentic-1.0.4/setup.py +0 -27
- {safentic-1.0.4 → safentic-1.0.6}/safentic/helper/__init__.py +0 -0
- {safentic-1.0.4 → safentic-1.0.6}/safentic/logger/__init__.py +0 -0
- {safentic-1.0.4 → safentic-1.0.6}/safentic/verifiers/__init__.py +0 -0
- {safentic-1.0.4 → safentic-1.0.6}/safentic.egg-info/dependency_links.txt +0 -0
- /safentic-1.0.4/safentic/policies/.gitkeep → /safentic-1.0.6/safentic_poc/backend/__init__.py +0 -0
@@ -1,36 +1,36 @@
|
|
1
|
-
Safentic SDK Commercial License Agreement
|
2
|
-
=========================================
|
3
|
-
|
4
|
-
IMPORTANT – READ CAREFULLY:
|
5
|
-
|
6
|
-
This license governs the use of the Safentic SDK (“Software”) developed and owned by Safentic. By installing or using this Software, you (“Licensee”) agree to the following terms and conditions:
|
7
|
-
|
8
|
-
1. GRANT OF LICENSE
|
9
|
-
Licensor grants Licensee a non-exclusive, non-transferable, non-sublicensable license to use the Software solely for internal business purposes and only in accordance with the terms of the commercial agreement executed between the parties. This license may include limited evaluation rights, subject to expiration or usage restrictions.
|
10
|
-
|
11
|
-
2. RESTRICTIONS
|
12
|
-
Licensee shall NOT:
|
13
|
-
- Use the Software without a valid, active license key or subscription.
|
14
|
-
- Reverse engineer, decompile, or disassemble the Software.
|
15
|
-
- Modify, copy, or create derivative works based on the Software.
|
16
|
-
- Distribute, sublicense, lease, or otherwise make the Software available to any third party.
|
17
|
-
- Circumvent or attempt to disable any license verification, telemetry, or access control mechanisms.
|
18
|
-
|
19
|
-
3. OWNERSHIP
|
20
|
-
The Software is licensed, not sold. Safentic retains all rights, title, and interest in and to the Software, including all intellectual property rights.
|
21
|
-
|
22
|
-
4. TERMINATION
|
23
|
-
This license is effective until terminated. It will terminate automatically without notice if Licensee breaches any term of this agreement. Upon termination, Licensee must cease all use and destroy all copies of the Software.
|
24
|
-
|
25
|
-
5. NO WARRANTY
|
26
|
-
The Software is provided "as is" without warranty of any kind. Licensor disclaims all warranties, express or implied, including but not limited to warranties of merchantability and fitness for a particular purpose.
|
27
|
-
|
28
|
-
6. LIMITATION OF LIABILITY
|
29
|
-
In no event shall Licensor be liable for any damages arising out of the use or inability to use the Software, including but not limited to incidental, special, or consequential damages.
|
30
|
-
|
31
|
-
7. GOVERNING LAW
|
32
|
-
This agreement shall be governed by and construed in accordance with the laws of Ireland, without regard to its conflict of law principles.
|
33
|
-
|
34
|
-
For licensing inquiries, please contact: contact@safentic.com
|
35
|
-
|
36
|
-
Copyright © 2025, Safentic. All rights reserved.
|
1
|
+
Safentic SDK Commercial License Agreement
|
2
|
+
=========================================
|
3
|
+
|
4
|
+
IMPORTANT – READ CAREFULLY:
|
5
|
+
|
6
|
+
This license governs the use of the Safentic SDK (“Software”) developed and owned by Safentic. By installing or using this Software, you (“Licensee”) agree to the following terms and conditions:
|
7
|
+
|
8
|
+
1. GRANT OF LICENSE
|
9
|
+
Licensor grants Licensee a non-exclusive, non-transferable, non-sublicensable license to use the Software solely for internal business purposes and only in accordance with the terms of the commercial agreement executed between the parties. This license may include limited evaluation rights, subject to expiration or usage restrictions.
|
10
|
+
|
11
|
+
2. RESTRICTIONS
|
12
|
+
Licensee shall NOT:
|
13
|
+
- Use the Software without a valid, active license key or subscription.
|
14
|
+
- Reverse engineer, decompile, or disassemble the Software.
|
15
|
+
- Modify, copy, or create derivative works based on the Software.
|
16
|
+
- Distribute, sublicense, lease, or otherwise make the Software available to any third party.
|
17
|
+
- Circumvent or attempt to disable any license verification, telemetry, or access control mechanisms.
|
18
|
+
|
19
|
+
3. OWNERSHIP
|
20
|
+
The Software is licensed, not sold. Safentic retains all rights, title, and interest in and to the Software, including all intellectual property rights.
|
21
|
+
|
22
|
+
4. TERMINATION
|
23
|
+
This license is effective until terminated. It will terminate automatically without notice if Licensee breaches any term of this agreement. Upon termination, Licensee must cease all use and destroy all copies of the Software.
|
24
|
+
|
25
|
+
5. NO WARRANTY
|
26
|
+
The Software is provided "as is" without warranty of any kind. Licensor disclaims all warranties, express or implied, including but not limited to warranties of merchantability and fitness for a particular purpose.
|
27
|
+
|
28
|
+
6. LIMITATION OF LIABILITY
|
29
|
+
In no event shall Licensor be liable for any damages arising out of the use or inability to use the Software, including but not limited to incidental, special, or consequential damages.
|
30
|
+
|
31
|
+
7. GOVERNING LAW
|
32
|
+
This agreement shall be governed by and construed in accordance with the laws of Ireland, without regard to its conflict of law principles.
|
33
|
+
|
34
|
+
For licensing inquiries, please contact: contact@safentic.com
|
35
|
+
|
36
|
+
Copyright © 2025, Safentic. All rights reserved.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Essentials
|
2
|
+
include LICENSE.txt
|
3
|
+
include README.md
|
4
|
+
include setup.py
|
5
|
+
include pyproject.toml
|
6
|
+
include requirements/requirements.txt
|
7
|
+
include requirements/requirements-dev.txt
|
8
|
+
include requirements/constraints.txt
|
9
|
+
|
10
|
+
# Include all relevant files inside safentic package
|
11
|
+
recursive-include safentic *.py *.txt *.yaml *.yml
|
12
|
+
|
13
|
+
# Ignore tests and cache
|
14
|
+
prune tests
|
15
|
+
global-exclude __pycache__ *.py[cod] *.pyo
|
safentic-1.0.6/PKG-INFO
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: safentic
|
3
|
+
Version: 1.0.6
|
4
|
+
Summary: Safentic SDK for AI agent runtime enforcement interception.
|
5
|
+
Author-email: Safentic <contact@safentic.com>
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
7
|
+
Project-URL: Homepage, https://safentic.com
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Operating System :: OS Independent
|
10
|
+
Requires-Python: >=3.10
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE.txt
|
13
|
+
Requires-Dist: PyYAML
|
14
|
+
Requires-Dist: langchain
|
15
|
+
Requires-Dist: langchain-community
|
16
|
+
Requires-Dist: langchain-openai
|
17
|
+
Requires-Dist: openai
|
18
|
+
Requires-Dist: python-dotenv
|
19
|
+
Requires-Dist: sentence-transformers==3.2.1
|
20
|
+
Requires-Dist: requests
|
21
|
+
Requires-Dist: SQLAlchemy
|
22
|
+
Provides-Extra: dev
|
23
|
+
Requires-Dist: pytest; extra == "dev"
|
24
|
+
Requires-Dist: coverage; extra == "dev"
|
25
|
+
Requires-Dist: black; extra == "dev"
|
26
|
+
Requires-Dist: isort; extra == "dev"
|
27
|
+
Requires-Dist: ruff; extra == "dev"
|
28
|
+
Requires-Dist: mypy; extra == "dev"
|
29
|
+
Requires-Dist: pip-audit-extra; extra == "dev"
|
30
|
+
Requires-Dist: types-requests; extra == "dev"
|
31
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
32
|
+
Dynamic: license-file
|
33
|
+
|
34
|
+
# Safentic SDK
|
35
|
+
|
36
|
+
- **Safentic is a runtime guardrail SDK for agentic AI systems.**
|
37
|
+
- It intercepts and evaluates tool calls between agent intent and execution, enforcing custom safety policies and generating structured audit logs for compliance.
|
38
|
+
|
39
|
+
## Installation
|
40
|
+
`pip install safentic`
|
41
|
+
|
42
|
+
# Quickstart: Wrap Your Agent
|
43
|
+
|
44
|
+
- Safentic works at the action boundary, not inside the model itself. You wrap your agent with SafetyLayer:
|
45
|
+
|
46
|
+
```
|
47
|
+
from safentic.layer import SafetyLayer
|
48
|
+
from agent import AgentClassInstance # your existing agent
|
49
|
+
|
50
|
+
agent = AgentClassInstance()
|
51
|
+
```
|
52
|
+
|
53
|
+
## Wrap with Safentic
|
54
|
+
``` layer = SafetyLayer(agent=agent, api_key="your-api-key", agent_id="demo-agent") ```
|
55
|
+
|
56
|
+
## Example tool call
|
57
|
+
```
|
58
|
+
try:
|
59
|
+
result = layer.call_tool("some_tool", {"body": "example input"})
|
60
|
+
print(result)
|
61
|
+
except Exception as e:
|
62
|
+
print("Blocked:", e)
|
63
|
+
```
|
64
|
+
|
65
|
+
## Output:
|
66
|
+
|
67
|
+
- Blocked: Blocked by policy
|
68
|
+
|
69
|
+
# Configuring Your Policy File
|
70
|
+
|
71
|
+
- Safentic enforces rules defined in a YAML configuration file (e.g. policy.yaml).
|
72
|
+
- By default, it looks for config/policy.yaml, or you can set the path with:
|
73
|
+
|
74
|
+
```
|
75
|
+
export SAFENTIC_POLICY_PATH=/path/to/policy.yaml
|
76
|
+
```
|
77
|
+
|
78
|
+
## Schema
|
79
|
+
|
80
|
+
- At the moment, Safentic supports the llm_verifier rule type.
|
81
|
+
|
82
|
+
```
|
83
|
+
tools:
|
84
|
+
<tool_name>:
|
85
|
+
rules:
|
86
|
+
- type: llm_verifier
|
87
|
+
description: "<short description of what this rule enforces>"
|
88
|
+
instruction: "<prompt instruction given to the verifier LLM>"
|
89
|
+
model: "<llm model name, e.g. gpt-4>"
|
90
|
+
fields: [<list of input fields to check>]
|
91
|
+
reference_file: "<path to reference text file, optional>"
|
92
|
+
response_format: boolean
|
93
|
+
response_trigger: yes
|
94
|
+
match_mode: exact
|
95
|
+
level: block # enforcement level: block | warn
|
96
|
+
severity: high # severity: low | medium | high
|
97
|
+
tags: [<labels for filtering/searching logs>]
|
98
|
+
|
99
|
+
logging:
|
100
|
+
level: INFO
|
101
|
+
destination: "safentic/logs/txt_logs/safentic_audit.log"
|
102
|
+
jsonl: "safentic/logs/json_logs/safentic_audit.jsonl"
|
103
|
+
|
104
|
+
Example Policy (obfuscated)
|
105
|
+
tools:
|
106
|
+
sample_tool:
|
107
|
+
rules:
|
108
|
+
- type: llm_verifier
|
109
|
+
description: "Block outputs that contain disallowed terms"
|
110
|
+
instruction: "Does this text contain disallowed terms or references?"
|
111
|
+
model: gpt-4
|
112
|
+
fields: [body]
|
113
|
+
reference_file: sample_guidelines.txt
|
114
|
+
response_format: boolean
|
115
|
+
response_trigger: yes
|
116
|
+
match_mode: exact
|
117
|
+
level: block
|
118
|
+
severity: high
|
119
|
+
tags: [sample, denylist]
|
120
|
+
|
121
|
+
another_tool:
|
122
|
+
rules: [] # Explicitly allow all actions for this tool
|
123
|
+
|
124
|
+
logging:
|
125
|
+
level: INFO
|
126
|
+
destination: "safentic/logs/txt_logs/safentic_audit.log"
|
127
|
+
jsonl: "safentic/logs/json_logs/safentic_audit.jsonl"
|
128
|
+
```
|
129
|
+
|
130
|
+
## Audit Logs
|
131
|
+
|
132
|
+
- Every decision is logged with context for compliance and debugging:
|
133
|
+
|
134
|
+
```
|
135
|
+
{
|
136
|
+
"timestamp": "2025-09-09T14:25:11Z",
|
137
|
+
"agent_id": "demo-agent",
|
138
|
+
"tool": "sample_tool",
|
139
|
+
"allowed": false,
|
140
|
+
"reason": "Blocked by policy",
|
141
|
+
"rule": "sample_tool:denylist_check",
|
142
|
+
"severity": "high",
|
143
|
+
"level": "block",
|
144
|
+
"tags": ["sample", "denylist"]
|
145
|
+
}
|
146
|
+
```
|
147
|
+
|
148
|
+
### Log Fields
|
149
|
+
|
150
|
+
- timestamp – when the action was evaluated
|
151
|
+
- agent_id – the agent issuing the action
|
152
|
+
- tool – tool name
|
153
|
+
- allowed – whether the action was permitted
|
154
|
+
- reason – why it was allowed or blocked
|
155
|
+
- rule – the rule that applied (if any)
|
156
|
+
- severity – severity of the violation
|
157
|
+
- level – enforcement level (block, warn)
|
158
|
+
- tags – categories attached to the rule
|
159
|
+
- extra – additional metadata (e.g., missing fields, matched text)
|
160
|
+
|
161
|
+
# CLI Commands
|
162
|
+
|
163
|
+
- Safentic ships with a CLI for validating policies, running one-off checks, and inspecting logs:
|
164
|
+
|
165
|
+
## Validate a policy file
|
166
|
+
```
|
167
|
+
safentic validate-policy --policy config/policy.yaml --strict
|
168
|
+
```
|
169
|
+
|
170
|
+
## Run a one-off tool check
|
171
|
+
```
|
172
|
+
safentic check-tool --tool sample_tool \
|
173
|
+
--input-json '{"body": "some text"}' \
|
174
|
+
--policy config/policy.yaml
|
175
|
+
```
|
176
|
+
## Tail the audit log (JSONL by default)
|
177
|
+
```
|
178
|
+
safentic logs tail --path safentic/logs/json_logs/safentic_audit.jsonl -f
|
179
|
+
```
|
180
|
+
|
181
|
+
## Environment Variables
|
182
|
+
|
183
|
+
Set these before running Safentic:
|
184
|
+
|
185
|
+
- ```OPENAI_API_KEY``` – **required** for rules that use llm_verifier (e.g., GPT-4).
|
186
|
+
- ```SAFENTIC_POLICY_PATH``` – path to your policy.yaml (default: config/policy.yaml).
|
187
|
+
- ```SAFENTIC_LOG_PATH``` – override the default text audit log path.
|
188
|
+
- ```SAFENTIC_JSON_LOG_PATH``` – override the default JSONL audit log path.
|
189
|
+
- ```LOG_LEVEL``` – optional, sets verbosity (DEBUG, INFO, etc.).
|
190
|
+
|
191
|
+
# Supported Stacks
|
192
|
+
|
193
|
+
- Safentic integrates with frameworks like LangChain, AutoGen, and MCP by wrapping the tool dispatcher rather than modifying the model or prompts.
|
safentic-1.0.6/README.md
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# Safentic SDK
|
2
|
+
|
3
|
+
- **Safentic is a runtime guardrail SDK for agentic AI systems.**
|
4
|
+
- It intercepts and evaluates tool calls between agent intent and execution, enforcing custom safety policies and generating structured audit logs for compliance.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
`pip install safentic`
|
8
|
+
|
9
|
+
# Quickstart: Wrap Your Agent
|
10
|
+
|
11
|
+
- Safentic works at the action boundary, not inside the model itself. You wrap your agent with SafetyLayer:
|
12
|
+
|
13
|
+
```
|
14
|
+
from safentic.layer import SafetyLayer
|
15
|
+
from agent import AgentClassInstance # your existing agent
|
16
|
+
|
17
|
+
agent = AgentClassInstance()
|
18
|
+
```
|
19
|
+
|
20
|
+
## Wrap with Safentic
|
21
|
+
``` layer = SafetyLayer(agent=agent, api_key="your-api-key", agent_id="demo-agent") ```
|
22
|
+
|
23
|
+
## Example tool call
|
24
|
+
```
|
25
|
+
try:
|
26
|
+
result = layer.call_tool("some_tool", {"body": "example input"})
|
27
|
+
print(result)
|
28
|
+
except Exception as e:
|
29
|
+
print("Blocked:", e)
|
30
|
+
```
|
31
|
+
|
32
|
+
## Output:
|
33
|
+
|
34
|
+
- Blocked: Blocked by policy
|
35
|
+
|
36
|
+
# Configuring Your Policy File
|
37
|
+
|
38
|
+
- Safentic enforces rules defined in a YAML configuration file (e.g. policy.yaml).
|
39
|
+
- By default, it looks for config/policy.yaml, or you can set the path with:
|
40
|
+
|
41
|
+
```
|
42
|
+
export SAFENTIC_POLICY_PATH=/path/to/policy.yaml
|
43
|
+
```
|
44
|
+
|
45
|
+
## Schema
|
46
|
+
|
47
|
+
- At the moment, Safentic supports the llm_verifier rule type.
|
48
|
+
|
49
|
+
```
|
50
|
+
tools:
|
51
|
+
<tool_name>:
|
52
|
+
rules:
|
53
|
+
- type: llm_verifier
|
54
|
+
description: "<short description of what this rule enforces>"
|
55
|
+
instruction: "<prompt instruction given to the verifier LLM>"
|
56
|
+
model: "<llm model name, e.g. gpt-4>"
|
57
|
+
fields: [<list of input fields to check>]
|
58
|
+
reference_file: "<path to reference text file, optional>"
|
59
|
+
response_format: boolean
|
60
|
+
response_trigger: yes
|
61
|
+
match_mode: exact
|
62
|
+
level: block # enforcement level: block | warn
|
63
|
+
severity: high # severity: low | medium | high
|
64
|
+
tags: [<labels for filtering/searching logs>]
|
65
|
+
|
66
|
+
logging:
|
67
|
+
level: INFO
|
68
|
+
destination: "safentic/logs/txt_logs/safentic_audit.log"
|
69
|
+
jsonl: "safentic/logs/json_logs/safentic_audit.jsonl"
|
70
|
+
|
71
|
+
Example Policy (obfuscated)
|
72
|
+
tools:
|
73
|
+
sample_tool:
|
74
|
+
rules:
|
75
|
+
- type: llm_verifier
|
76
|
+
description: "Block outputs that contain disallowed terms"
|
77
|
+
instruction: "Does this text contain disallowed terms or references?"
|
78
|
+
model: gpt-4
|
79
|
+
fields: [body]
|
80
|
+
reference_file: sample_guidelines.txt
|
81
|
+
response_format: boolean
|
82
|
+
response_trigger: yes
|
83
|
+
match_mode: exact
|
84
|
+
level: block
|
85
|
+
severity: high
|
86
|
+
tags: [sample, denylist]
|
87
|
+
|
88
|
+
another_tool:
|
89
|
+
rules: [] # Explicitly allow all actions for this tool
|
90
|
+
|
91
|
+
logging:
|
92
|
+
level: INFO
|
93
|
+
destination: "safentic/logs/txt_logs/safentic_audit.log"
|
94
|
+
jsonl: "safentic/logs/json_logs/safentic_audit.jsonl"
|
95
|
+
```
|
96
|
+
|
97
|
+
## Audit Logs
|
98
|
+
|
99
|
+
- Every decision is logged with context for compliance and debugging:
|
100
|
+
|
101
|
+
```
|
102
|
+
{
|
103
|
+
"timestamp": "2025-09-09T14:25:11Z",
|
104
|
+
"agent_id": "demo-agent",
|
105
|
+
"tool": "sample_tool",
|
106
|
+
"allowed": false,
|
107
|
+
"reason": "Blocked by policy",
|
108
|
+
"rule": "sample_tool:denylist_check",
|
109
|
+
"severity": "high",
|
110
|
+
"level": "block",
|
111
|
+
"tags": ["sample", "denylist"]
|
112
|
+
}
|
113
|
+
```
|
114
|
+
|
115
|
+
### Log Fields
|
116
|
+
|
117
|
+
- timestamp – when the action was evaluated
|
118
|
+
- agent_id – the agent issuing the action
|
119
|
+
- tool – tool name
|
120
|
+
- allowed – whether the action was permitted
|
121
|
+
- reason – why it was allowed or blocked
|
122
|
+
- rule – the rule that applied (if any)
|
123
|
+
- severity – severity of the violation
|
124
|
+
- level – enforcement level (block, warn)
|
125
|
+
- tags – categories attached to the rule
|
126
|
+
- extra – additional metadata (e.g., missing fields, matched text)
|
127
|
+
|
128
|
+
# CLI Commands
|
129
|
+
|
130
|
+
- Safentic ships with a CLI for validating policies, running one-off checks, and inspecting logs:
|
131
|
+
|
132
|
+
## Validate a policy file
|
133
|
+
```
|
134
|
+
safentic validate-policy --policy config/policy.yaml --strict
|
135
|
+
```
|
136
|
+
|
137
|
+
## Run a one-off tool check
|
138
|
+
```
|
139
|
+
safentic check-tool --tool sample_tool \
|
140
|
+
--input-json '{"body": "some text"}' \
|
141
|
+
--policy config/policy.yaml
|
142
|
+
```
|
143
|
+
## Tail the audit log (JSONL by default)
|
144
|
+
```
|
145
|
+
safentic logs tail --path safentic/logs/json_logs/safentic_audit.jsonl -f
|
146
|
+
```
|
147
|
+
|
148
|
+
## Environment Variables
|
149
|
+
|
150
|
+
Set these before running Safentic:
|
151
|
+
|
152
|
+
- ```OPENAI_API_KEY``` – **required** for rules that use llm_verifier (e.g., GPT-4).
|
153
|
+
- ```SAFENTIC_POLICY_PATH``` – path to your policy.yaml (default: config/policy.yaml).
|
154
|
+
- ```SAFENTIC_LOG_PATH``` – override the default text audit log path.
|
155
|
+
- ```SAFENTIC_JSON_LOG_PATH``` – override the default JSONL audit log path.
|
156
|
+
- ```LOG_LEVEL``` – optional, sets verbosity (DEBUG, INFO, etc.).
|
157
|
+
|
158
|
+
# Supported Stacks
|
159
|
+
|
160
|
+
- Safentic integrates with frameworks like LangChain, AutoGen, and MCP by wrapping the tool dispatcher rather than modifying the model or prompts.
|
@@ -0,0 +1,55 @@
|
|
1
|
+
[project]
|
2
|
+
name = "safentic"
|
3
|
+
version = "1.0.6"
|
4
|
+
description = "Safentic SDK for AI agent runtime enforcement interception."
|
5
|
+
authors = [{ name = "Safentic", email = "contact@safentic.com" }]
|
6
|
+
readme = "README.md"
|
7
|
+
license = "LicenseRef-Proprietary" # SPDX-compatible custom license
|
8
|
+
requires-python = ">=3.10"
|
9
|
+
|
10
|
+
dependencies = [
|
11
|
+
"PyYAML",
|
12
|
+
"langchain",
|
13
|
+
"langchain-community",
|
14
|
+
"langchain-openai",
|
15
|
+
"openai",
|
16
|
+
"python-dotenv",
|
17
|
+
"sentence-transformers==3.2.1",
|
18
|
+
"requests",
|
19
|
+
"SQLAlchemy"
|
20
|
+
]
|
21
|
+
|
22
|
+
classifiers = [
|
23
|
+
"Programming Language :: Python :: 3",
|
24
|
+
"Operating System :: OS Independent"
|
25
|
+
]
|
26
|
+
|
27
|
+
[project.optional-dependencies]
|
28
|
+
dev = [
|
29
|
+
"pytest",
|
30
|
+
"coverage",
|
31
|
+
"black",
|
32
|
+
"isort",
|
33
|
+
"ruff",
|
34
|
+
"mypy",
|
35
|
+
"pip-audit-extra",
|
36
|
+
"types-requests",
|
37
|
+
"types-PyYAML"
|
38
|
+
]
|
39
|
+
|
40
|
+
[project.urls]
|
41
|
+
Homepage = "https://safentic.com"
|
42
|
+
|
43
|
+
[project.scripts]
|
44
|
+
safentic = "safentic.cli.main:main"
|
45
|
+
|
46
|
+
[tool.setuptools.packages.find]
|
47
|
+
include = ["safentic*"]
|
48
|
+
exclude = ["config", "integrations", "safentic_poc", "policy_file_docs"]
|
49
|
+
|
50
|
+
[tool.setuptools]
|
51
|
+
license-files = ["LICENSE.txt"]
|
52
|
+
|
53
|
+
[build-system]
|
54
|
+
requires = ["setuptools>=61", "wheel"]
|
55
|
+
build-backend = "setuptools.build_meta"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# requirements/constraints.txt
|
2
|
+
|
3
|
+
# Security guardrails (transitives)
|
4
|
+
aiohttp>=3.12.14
|
5
|
+
pillow>=11.3.0
|
6
|
+
transformers>=4.53.0
|
7
|
+
torch!=2.7.1
|
8
|
+
|
9
|
+
# Core dependencies
|
10
|
+
PyYAML>=6.0.2
|
11
|
+
requests>=2.32.0
|
12
|
+
SQLAlchemy>=2.0.0
|
13
|
+
langchain>=0.2.16
|
14
|
+
langchain-community>=0.2.16
|
15
|
+
langchain-openai>=0.1.20
|
16
|
+
openai>=1.40.0
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# requirements/requirements-dev.txt
|
2
|
+
|
3
|
+
# Pull in the SDK/runtime deps (relative to THIS file)
|
4
|
+
-r requirements.txt
|
5
|
+
|
6
|
+
# Testing
|
7
|
+
pytest
|
8
|
+
coverage
|
9
|
+
|
10
|
+
# Formatting / linting / typing
|
11
|
+
black
|
12
|
+
isort
|
13
|
+
ruff
|
14
|
+
mypy
|
15
|
+
|
16
|
+
# Security auditing
|
17
|
+
pip-audit-extra
|
18
|
+
|
19
|
+
# Type stubs
|
20
|
+
types-requests
|
21
|
+
types-PyYAML
|
@@ -1,10 +1,9 @@
|
|
1
|
-
PyYAML
|
2
|
-
langchain
|
3
|
-
langchain-community
|
4
|
-
langchain-openai
|
5
|
-
openai
|
6
|
-
python-dotenv
|
7
|
-
sentence-transformers==3.2.1
|
8
|
-
|
9
|
-
|
10
|
-
coverage
|
1
|
+
PyYAML
|
2
|
+
langchain
|
3
|
+
langchain-community
|
4
|
+
langchain-openai
|
5
|
+
openai
|
6
|
+
python-dotenv
|
7
|
+
sentence-transformers==3.2.1
|
8
|
+
requests
|
9
|
+
SQLAlchemy
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class SafenticError(Exception):
|
2
|
+
"""Base class for all Safentic errors."""
|
3
|
+
|
4
|
+
|
5
|
+
class PolicyValidationError(SafenticError):
|
6
|
+
"""Raised when a policy file or rule is invalid."""
|
7
|
+
|
8
|
+
|
9
|
+
class ReferenceFileError(SafenticError):
|
10
|
+
"""Raised when a reference file is missing, unreadable, or empty."""
|
11
|
+
|
12
|
+
|
13
|
+
class EnforcementError(SafenticError):
|
14
|
+
"""Raised when enforcement fails unexpectedly."""
|
15
|
+
|
16
|
+
|
17
|
+
class VerifierError(SafenticError):
|
18
|
+
"""Raised when the LLM verifier fails unexpectedly."""
|
19
|
+
|
20
|
+
|
21
|
+
class InvalidAPIKeyError(SafenticError):
|
22
|
+
"""Raised when an API key is missing or invalid."""
|
23
|
+
|
24
|
+
|
25
|
+
class InvalidAgentInterfaceError(SafenticError):
|
26
|
+
"""Raised when the wrapped agent doesn't expose the expected interface."""
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from typing import Any, Dict, Mapping, cast
|
2
|
+
from safentic.policy_enforcer import PolicyEnforcer
|
3
|
+
|
4
|
+
|
5
|
+
def handle_mcp_action(
|
6
|
+
action_request: Mapping[str, Any], enforcer: PolicyEnforcer
|
7
|
+
) -> Dict[str, Any]:
|
8
|
+
"""
|
9
|
+
Accepts an MCP ActionRequest and returns a Safentic policy enforcement result.
|
10
|
+
Requires a PolicyEnforcer instance to be passed in.
|
11
|
+
|
12
|
+
Expected shape (minimal):
|
13
|
+
{
|
14
|
+
"tool": {
|
15
|
+
"name": str,
|
16
|
+
"input": dict[str, Any]
|
17
|
+
},
|
18
|
+
"agent": {
|
19
|
+
"id": str
|
20
|
+
}
|
21
|
+
}
|
22
|
+
"""
|
23
|
+
|
24
|
+
tool: Dict[str, Any] = cast(Dict[str, Any], action_request.get("tool", {}))
|
25
|
+
agent: Dict[str, Any] = cast(Dict[str, Any], action_request.get("agent", {}))
|
26
|
+
|
27
|
+
tool_name: str = cast(str, tool.get("name", "unknown_tool"))
|
28
|
+
tool_args: Dict[str, Any] = cast(
|
29
|
+
Dict[str, Any], tool.get("input", {})
|
30
|
+
) # Expected to include "body" or "note"
|
31
|
+
agent_id: str = cast(str, agent.get("id", "unknown_agent"))
|
32
|
+
|
33
|
+
result: Dict[str, Any] = enforcer.enforce(
|
34
|
+
agent_id=agent_id,
|
35
|
+
tool_name=tool_name,
|
36
|
+
tool_args=tool_args,
|
37
|
+
)
|
38
|
+
|
39
|
+
return {
|
40
|
+
"tool": tool_name,
|
41
|
+
"agent_id": agent_id,
|
42
|
+
"allowed": result["allowed"],
|
43
|
+
"reason": result["reason"],
|
44
|
+
"agent_state": result.get("agent_state", {}),
|
45
|
+
"violation": result.get("violation"), # Optional violation metadata
|
46
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from typing import Any, Dict, Optional
|
2
|
+
|
3
|
+
from safentic.policy_engine import PolicyEngine
|
4
|
+
from safentic.policy_enforcer import PolicyEnforcer
|
5
|
+
from safentic.cli.utils import resolve_policy_path, load_json_arg, ok, error
|
6
|
+
|
7
|
+
|
8
|
+
def run(
|
9
|
+
policy: Optional[str],
|
10
|
+
tool_name: str,
|
11
|
+
agent_id: str,
|
12
|
+
input_json: Optional[str],
|
13
|
+
input_file: Optional[str],
|
14
|
+
dry_run: bool,
|
15
|
+
allow_fail: bool,
|
16
|
+
no_llm: bool = False,
|
17
|
+
) -> Dict[str, Any]:
|
18
|
+
"""
|
19
|
+
Run a one-off policy enforcement for a tool + payload.
|
20
|
+
Returns a JSON payload. Raises SystemExit(2) if blocked and allow_fail=False.
|
21
|
+
"""
|
22
|
+
policy_path = resolve_policy_path(policy)
|
23
|
+
|
24
|
+
try:
|
25
|
+
engine = PolicyEngine(policy_path=policy_path, dry_run=dry_run, no_llm=no_llm)
|
26
|
+
enforcer = PolicyEnforcer(policy_engine=engine)
|
27
|
+
|
28
|
+
payload: Dict[str, Any] = load_json_arg(input_json, input_file)
|
29
|
+
decision = enforcer.enforce(
|
30
|
+
agent_id=agent_id, tool_name=tool_name, tool_args=payload
|
31
|
+
)
|
32
|
+
|
33
|
+
result = ok(
|
34
|
+
f"Check completed for tool: {tool_name}",
|
35
|
+
{"policy_path": policy_path, "decision": decision},
|
36
|
+
)
|
37
|
+
|
38
|
+
if not decision.get("allowed", False) and not allow_fail:
|
39
|
+
# Signal to CI callers that this is a block
|
40
|
+
raise SystemExit(2)
|
41
|
+
|
42
|
+
return result
|
43
|
+
|
44
|
+
except SystemExit:
|
45
|
+
raise
|
46
|
+
except Exception as e:
|
47
|
+
return error("check-tool failed", {"details": str(e)})
|