safentic 1.0.1__py3-none-any.whl → 1.0.3__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.
- safentic/__init__.py +1 -1
- safentic/layer.py +2 -2
- safentic/policies/policy.yaml +28 -4
- {safentic-1.0.1.dist-info → safentic-1.0.3.dist-info}/METADATA +7 -1
- {safentic-1.0.1.dist-info → safentic-1.0.3.dist-info}/RECORD +8 -8
- tests/test_all.py +13 -18
- {safentic-1.0.1.dist-info → safentic-1.0.3.dist-info}/WHEEL +0 -0
- {safentic-1.0.1.dist-info → safentic-1.0.3.dist-info}/top_level.txt +0 -0
safentic/__init__.py
CHANGED
safentic/layer.py
CHANGED
@@ -16,12 +16,11 @@ class SafetyLayer():
|
|
16
16
|
# Raises SafenticError if blocked
|
17
17
|
"""
|
18
18
|
|
19
|
-
def __init__(self, api_key
|
19
|
+
def __init__(self, api_key="", agent_id="", enforcer: PolicyEnforcer = None, raise_on_block: bool = True):
|
20
20
|
self.agent_id = agent_id
|
21
21
|
self.raise_on_block = raise_on_block
|
22
22
|
self.logger = AuditLogger()
|
23
23
|
|
24
|
-
# If no custom enforcer is provided, instantiate one with the API key
|
25
24
|
self.enforcer = enforcer or PolicyEnforcer()
|
26
25
|
self.api_key = validate_api_key(api_key)
|
27
26
|
self.enforcer.reset(agent_id)
|
@@ -31,6 +30,7 @@ class SafetyLayer():
|
|
31
30
|
Checks whether a tool action is allowed.
|
32
31
|
Raises SafenticError if blocked (default), or returns result if raise_on_block=False.
|
33
32
|
"""
|
33
|
+
|
34
34
|
result = self.enforcer.enforce(self.agent_id, tool_name, tool_args)
|
35
35
|
|
36
36
|
# Log structured event
|
safentic/policies/policy.yaml
CHANGED
@@ -5,7 +5,11 @@ tools:
|
|
5
5
|
phrases:
|
6
6
|
- "one device per subscription"
|
7
7
|
- "24 hours"
|
8
|
-
|
8
|
+
- "apology"
|
9
|
+
- "apologize"
|
10
|
+
- "sorry"
|
11
|
+
- "refund"
|
12
|
+
- "refund delay"
|
9
13
|
- type: semantic
|
10
14
|
trigger_phrases:
|
11
15
|
- "refund policy"
|
@@ -13,13 +17,33 @@ tools:
|
|
13
17
|
- "our policy"
|
14
18
|
reference_file: "example_policy.txt"
|
15
19
|
|
20
|
+
issue_refund:
|
21
|
+
checks:
|
22
|
+
- type: deny_phrase
|
23
|
+
phrases:
|
24
|
+
- "refund"
|
25
|
+
- "refunding"
|
26
|
+
- "issue refund"
|
27
|
+
- "send refund"
|
28
|
+
- "credit customer"
|
29
|
+
- "reverse charge"
|
30
|
+
- "double charge"
|
31
|
+
- "processed refund"
|
32
|
+
- "grant compensation"
|
33
|
+
- "money back"
|
34
|
+
- type: semantic
|
35
|
+
trigger_phrases:
|
36
|
+
- "we've sent your refund"
|
37
|
+
- "your money is on the way"
|
38
|
+
reference_file: "refund_statement.txt"
|
39
|
+
|
16
40
|
update_ticket:
|
17
|
-
checks: [] # Explicitly allow all
|
41
|
+
checks: [] # Explicitly allow all
|
18
42
|
|
19
43
|
log_to_crm:
|
20
|
-
checks: [] # Explicitly allow all
|
44
|
+
checks: [] # Explicitly allow all
|
21
45
|
|
22
46
|
logging:
|
23
47
|
level: INFO
|
24
48
|
destination: "safentic/logs/txt_logs/safentic_audit.log"
|
25
|
-
jsonl: "safentic/logs/json_logs/safentic_audit.jsonl"
|
49
|
+
jsonl: "safentic/logs/json_logs/safentic_audit.jsonl"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: safentic
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.3
|
4
4
|
Summary: Safentic SDK for behavior analysis
|
5
5
|
Home-page: https://safentic.com
|
6
6
|
Author: Safentic
|
@@ -10,6 +10,11 @@ Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: License :: Other/Proprietary License
|
11
11
|
Classifier: Operating System :: OS Independent
|
12
12
|
Description-Content-Type: text/markdown
|
13
|
+
Requires-Dist: requests
|
14
|
+
Requires-Dist: PyYAML
|
15
|
+
Requires-Dist: sentence-transformers==3.2.1
|
16
|
+
Requires-Dist: sqlalchemy
|
17
|
+
Requires-Dist: python-dotenv
|
13
18
|
Dynamic: author
|
14
19
|
Dynamic: author-email
|
15
20
|
Dynamic: classifier
|
@@ -17,6 +22,7 @@ Dynamic: description
|
|
17
22
|
Dynamic: description-content-type
|
18
23
|
Dynamic: home-page
|
19
24
|
Dynamic: license
|
25
|
+
Dynamic: requires-dist
|
20
26
|
Dynamic: summary
|
21
27
|
|
22
28
|
# Safentic SDK
|
@@ -1,8 +1,8 @@
|
|
1
1
|
safentic/LICENSE.txt,sha256=xl3AZ2rkiOG5qE01SPRBgoW5Ib5YKZQeszh6OlvKePk,2330
|
2
|
-
safentic/__init__.py,sha256=
|
2
|
+
safentic/__init__.py,sha256=g1-LJf81rNvDYq7aoGRfS60Y3Wdmd-HQmjA_PmB0X_c,132
|
3
3
|
safentic/config.py,sha256=V6c8Fz0t-Ja278kjCrQMlGPBQ4Hj830t3q7U7oM4Q4k,90
|
4
4
|
safentic/engine.py,sha256=-a90x70SY15WkOIkgxoPVLs_9xGsf4Krj-CmpoMs6tE,2597
|
5
|
-
safentic/layer.py,sha256=
|
5
|
+
safentic/layer.py,sha256=xcsgI4aOCepoWo9jG3JB_sAXoYPXDglC78zJML7qDtY,1673
|
6
6
|
safentic/policy.py,sha256=ApAAAxiWb_M5TUTtYKk10BVWEy4xViSbM8ikocIqWoI,4111
|
7
7
|
safentic/helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
safentic/helper/auth.py,sha256=evhikKRTtGXYp0YKkdntBM4bUjdUTtvJ2nA5YLt2IIA,391
|
@@ -11,12 +11,12 @@ safentic/logger/audit.py,sha256=GAqZFVPERKyuugmGn6G1-fg9QLaHqN1aWl0S_J3dYXI,2947
|
|
11
11
|
safentic/policies/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
safentic/policies/__init__.py,sha256=s37JO8wBcNstqvPif1q2Dw46imddeZZJBo-f3mSfd58,69
|
13
13
|
safentic/policies/example_policy.txt,sha256=Vkv5p2Kcyppijl8BD--P1APy2cBgWrYCrKzfo2pW0lo,1955
|
14
|
-
safentic/policies/policy.yaml,sha256=
|
14
|
+
safentic/policies/policy.yaml,sha256=EoDjvloG56WGNjqNV18xytcCr2v4I0KG6uSx6_yTVAg,1241
|
15
15
|
safentic/verifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
safentic/verifiers/sentence_verifier.py,sha256=jgObZ1t4UCYHYIfNayI0n69hVBWOHc9GAhpJsDbiD2c,2376
|
17
17
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
|
-
tests/test_all.py,sha256=
|
19
|
-
safentic-1.0.
|
20
|
-
safentic-1.0.
|
21
|
-
safentic-1.0.
|
22
|
-
safentic-1.0.
|
18
|
+
tests/test_all.py,sha256=oKq-vbo_THyoupx1r7aoNqIAb72rH2t9uyUJtpbDOyQ,5642
|
19
|
+
safentic-1.0.3.dist-info/METADATA,sha256=HxEV91N6UP0uo94sEkUVt2dhmGj2FU-2yoxPjh46d_M,1477
|
20
|
+
safentic-1.0.3.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
|
21
|
+
safentic-1.0.3.dist-info/top_level.txt,sha256=8bLyq7i9we7XEcbDYLHTUfR3IIpICeMfG8NWAHCLU5s,15
|
22
|
+
safentic-1.0.3.dist-info/RECORD,,
|
tests/test_all.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
import os
|
2
|
-
import sys
|
3
2
|
import math
|
4
|
-
import logging
|
5
3
|
import pytest
|
6
4
|
from unittest import mock
|
7
5
|
|
@@ -29,17 +27,14 @@ def enforcer():
|
|
29
27
|
return PolicyEnforcer()
|
30
28
|
|
31
29
|
@pytest.mark.parametrize("case, expected_contains", [
|
32
|
-
("valid_excerpt",
|
33
|
-
("generic_refund",
|
30
|
+
("valid_excerpt", "refund"),
|
31
|
+
("generic_refund", "sorry"),
|
34
32
|
("hallucination_one_device", "one device per subscription"),
|
35
33
|
("made_up_timeframe", "24 hours"),
|
36
34
|
])
|
37
35
|
def test_policy_engine_evaluate(policy_engine, case, expected_contains):
|
38
36
|
reason = policy_engine.evaluate_policy("send_email", {"body": TESTS[case]})
|
39
|
-
|
40
|
-
assert reason is None
|
41
|
-
else:
|
42
|
-
assert expected_contains in reason.lower()
|
37
|
+
assert expected_contains in reason.lower()
|
43
38
|
|
44
39
|
def test_policy_engine_skips_empty(policy_engine):
|
45
40
|
assert policy_engine.evaluate_policy("send_email", {"body": ""}) is None
|
@@ -56,13 +51,14 @@ def test_policy_engine_malformed_semantic(policy_engine):
|
|
56
51
|
}
|
57
52
|
assert policy_engine.evaluate_policy("send_email", {"body": "refund policy applies"}) is None
|
58
53
|
|
59
|
-
def
|
60
|
-
agent_id = "agent-
|
54
|
+
def test_enforcer_blocks_expected_cases(enforcer):
|
55
|
+
agent_id = "agent-block"
|
61
56
|
for case in ["valid_excerpt", "generic_refund"]:
|
62
|
-
|
57
|
+
result = enforcer.enforce(agent_id, "send_email", {"body": TESTS[case]})
|
58
|
+
assert not result["allowed"]
|
63
59
|
|
64
60
|
def test_enforcer_blocks_and_resets(enforcer):
|
65
|
-
agent_id = "agent-
|
61
|
+
agent_id = "agent-reset"
|
66
62
|
res = enforcer.enforce(agent_id, "send_email", {"body": TESTS["hallucination_one_device"]})
|
67
63
|
assert not res["allowed"]
|
68
64
|
repeat = enforcer.enforce(agent_id, "send_email", {"body": TESTS["hallucination_one_device"]})
|
@@ -76,8 +72,9 @@ def test_safety_layer_blocks_and_raises():
|
|
76
72
|
layer.protect("send_email", {"body": TESTS["made_up_timeframe"]})
|
77
73
|
|
78
74
|
def test_safety_layer_returns_result():
|
75
|
+
safe_input = {"body": "This is a neutral and policy-safe email message."}
|
79
76
|
layer = SafetyLayer(api_key="demo-1234", agent_id="safety-2", raise_on_block=False)
|
80
|
-
assert layer.protect("send_email",
|
77
|
+
assert layer.protect("send_email", safe_input)["allowed"]
|
81
78
|
|
82
79
|
@mock.patch("safentic.verifiers.sentence_verifier.SentenceTransformer")
|
83
80
|
def test_similarity_score_consistency(mock_model_class):
|
@@ -112,15 +109,13 @@ def test_verifier_explains_score(mock_model_class):
|
|
112
109
|
|
113
110
|
@mock.patch("safentic.logger.audit.open", new_callable=mock.mock_open)
|
114
111
|
def test_logger_set_level_and_log(mock_open_file):
|
115
|
-
os.environ["SAFE_AUDIT_LOG"] = "1"
|
116
|
-
|
112
|
+
os.environ["SAFE_AUDIT_LOG"] = "1"
|
117
113
|
logger = AuditLogger(config={"txt": "dummy.txt", "json": "dummy.jsonl"})
|
118
114
|
logger.set_level("DEBUG")
|
119
115
|
logger.log(agent_id="mock-agent", tool="send_email", allowed=True)
|
120
116
|
logger.log(agent_id="mock-agent", tool="send_email", allowed=False, reason="test")
|
121
|
-
|
122
117
|
handle = mock_open_file()
|
123
|
-
handle.write.assert_called()
|
118
|
+
handle.write.assert_called()
|
124
119
|
|
125
120
|
def test_logger_invalid_level():
|
126
121
|
with pytest.raises(ValueError):
|
@@ -129,4 +124,4 @@ def test_logger_invalid_level():
|
|
129
124
|
@mock.patch("safentic.logger.audit.open", side_effect=OSError("mocked failure"))
|
130
125
|
def test_logger_gracefully_fails_json_write(mock_open):
|
131
126
|
logger = AuditLogger(config={"txt": "ok.txt", "json": "fail.jsonl"})
|
132
|
-
logger.log("agent", "tool", False, reason="test")
|
127
|
+
logger.log("agent", "tool", False, reason="test")
|
File without changes
|
File without changes
|