safentic 1.0.5__py3-none-any.whl → 1.0.7__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.
@@ -1,60 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: safentic
3
- Version: 1.0.5
4
- Summary: Safentic SDK for behavior analysis
5
- Home-page: https://safentic.com
6
- Author: Safentic
7
- Author-email: contact@safentic.com
8
- License: Proprietary :: Safentic Commercial License
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: Other/Proprietary License
11
- Classifier: Operating System :: OS Independent
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
18
- Dynamic: author
19
- Dynamic: author-email
20
- Dynamic: classifier
21
- Dynamic: description
22
- Dynamic: description-content-type
23
- Dynamic: home-page
24
- Dynamic: license
25
- Dynamic: requires-dist
26
- Dynamic: summary
27
-
28
- # Safentic SDK
29
-
30
- Safentic is a runtime guardrail SDK for agentic AI systems.
31
- It intercepts and evaluates unsafe tool calls between agent **intent** and **execution**, enforcing custom safety policies.
32
-
33
- ---
34
-
35
- ## Installation
36
-
37
- Install from PyPI:
38
-
39
- pip install safentic
40
-
41
- ---
42
-
43
- ## API Key Required
44
-
45
- Safentic requires a valid API key to function.
46
- To obtain one, contact: contact@safentic.com
47
-
48
- ---
49
-
50
- ## Quick Start
51
-
52
- ```python
53
- from safentic import SafetyLayer, SafenticError
54
-
55
- layer = SafetyLayer(api_key="your-api-key", agent_id="agent-007")
56
-
57
- try:
58
- layer.protect("send_email", {"body": "Refund me now!"})
59
- except SafenticError as e:
60
- print("Blocked by policy:", e)
@@ -1,22 +0,0 @@
1
- safentic/LICENSE.txt,sha256=xl3AZ2rkiOG5qE01SPRBgoW5Ib5YKZQeszh6OlvKePk,2330
2
- safentic/__init__.py,sha256=RFKBKMmJGUZy436bF_wI0AAgiyWYJqeF3MIlhUi7_VU,124
3
- safentic/config.py,sha256=V6c8Fz0t-Ja278kjCrQMlGPBQ4Hj830t3q7U7oM4Q4k,90
4
- safentic/engine.py,sha256=Ho9-sHXnJuRUh0fsONLip8Dsf927EJK_8RwXDk4Mk-Q,3564
5
- safentic/layer.py,sha256=3sGVn7gGWoiStEKO3jTV4_2QxKUWEH1BiThR421GISo,2481
6
- safentic/policy.py,sha256=VKIxLfcJtwA3U7DVwefLWprKQCFbfya-RJZvU1n4sEc,4138
7
- safentic/helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- safentic/helper/auth.py,sha256=evhikKRTtGXYp0YKkdntBM4bUjdUTtvJ2nA5YLt2IIA,391
9
- safentic/logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- safentic/logger/audit.py,sha256=GAqZFVPERKyuugmGn6G1-fg9QLaHqN1aWl0S_J3dYXI,2947
11
- safentic/policies/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- safentic/policies/__init__.py,sha256=s37JO8wBcNstqvPif1q2Dw46imddeZZJBo-f3mSfd58,69
13
- safentic/policies/example_policy.txt,sha256=Vkv5p2Kcyppijl8BD--P1APy2cBgWrYCrKzfo2pW0lo,1955
14
- safentic/policies/policy.yaml,sha256=EoDjvloG56WGNjqNV18xytcCr2v4I0KG6uSx6_yTVAg,1241
15
- safentic/verifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- safentic/verifiers/sentence_verifier.py,sha256=jgObZ1t4UCYHYIfNayI0n69hVBWOHc9GAhpJsDbiD2c,2376
17
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- tests/test_all.py,sha256=makysLQtgKAFOfIutnvmBQcMLkMY3inkT6ig5h_I2e4,5878
19
- safentic-1.0.5.dist-info/METADATA,sha256=5S7U-KmaCPDVPcv_HsR7K6Px0inqySRxRr_fbqdxJv4,1477
20
- safentic-1.0.5.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
21
- safentic-1.0.5.dist-info/top_level.txt,sha256=8bLyq7i9we7XEcbDYLHTUfR3IIpICeMfG8NWAHCLU5s,15
22
- safentic-1.0.5.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- safentic
2
- tests
tests/test_all.py DELETED
@@ -1,132 +0,0 @@
1
- import os
2
- import math
3
- import pytest
4
- from unittest import mock
5
-
6
- from safentic.policy import PolicyEngine
7
- from safentic.engine import PolicyEnforcer
8
- from safentic.layer import SafetyLayer, SafenticError
9
- from safentic.verifiers.sentence_verifier import SentenceTransformerVerifier
10
- from safentic.logger.audit import AuditLogger
11
-
12
- os.environ["SAFE_AUDIT_LOG"] = "0"
13
-
14
- TESTS = {
15
- "valid_excerpt": "Here is our refund policy: any cancellation within 30 days of purchase receives a full refund.",
16
- "hallucination_one_device": "According to our refund policy, every user may only refund one device per subscription.",
17
- "made_up_timeframe": "Refund policy: you must request a refund within 24 hours of purchase.",
18
- "generic_refund": "I’m sorry for the trouble—please let us know more about your refund issue.",
19
- }
20
-
21
- class MockAgent:
22
- def call_tool(self, tool_name, **kwargs):
23
- return {"status": "mock-success"}
24
-
25
- @pytest.fixture
26
- def policy_engine():
27
- return PolicyEngine()
28
-
29
- @pytest.fixture
30
- def enforcer():
31
- return PolicyEnforcer()
32
-
33
- @pytest.mark.parametrize("case, expected_contains", [
34
- ("valid_excerpt", "refund"),
35
- ("generic_refund", "sorry"),
36
- ("hallucination_one_device", "one device per subscription"),
37
- ("made_up_timeframe", "24 hours"),
38
- ])
39
- def test_policy_engine_evaluate(policy_engine, case, expected_contains):
40
- violation = policy_engine.evaluate_policy("send_email", {"body": TESTS[case]})
41
- assert violation and expected_contains in violation["reason"].lower()
42
-
43
- def test_policy_engine_skips_empty(policy_engine):
44
- assert policy_engine.evaluate_policy("send_email", {"body": ""}) is None
45
-
46
- def test_policy_engine_unknown_rule_type(policy_engine):
47
- policy_engine.policy_cfg = {
48
- "tools": {"send_email": {"checks": [{"type": "unknown_type"}]}}
49
- }
50
- assert policy_engine.evaluate_policy("send_email", {"body": "test"}) is None
51
-
52
- def test_policy_engine_malformed_semantic(policy_engine):
53
- policy_engine.policy_cfg = {
54
- "tools": {"send_email": {"checks": [{"type": "semantic", "trigger_phrases": ["refund"]}]}},
55
- }
56
- assert policy_engine.evaluate_policy("send_email", {"body": "refund policy applies"}) is None
57
-
58
- def test_enforcer_blocks_expected_cases(enforcer):
59
- agent_id = "agent-block"
60
- for case in ["valid_excerpt", "generic_refund"]:
61
- result = enforcer.enforce(agent_id, "send_email", {"body": TESTS[case]})
62
- assert not result["allowed"]
63
- assert "reason" in result
64
-
65
- def test_enforcer_blocks_and_resets(enforcer):
66
- agent_id = "agent-reset"
67
- res = enforcer.enforce(agent_id, "send_email", {"body": TESTS["hallucination_one_device"]})
68
- assert not res["allowed"]
69
- repeat = enforcer.enforce(agent_id, "send_email", {"body": TESTS["hallucination_one_device"]})
70
- assert "temporarily blocked" in repeat["reason"].lower()
71
- enforcer.reset(agent_id)
72
- assert agent_id not in enforcer.agent_states
73
-
74
- def test_safety_layer_blocks_and_raises():
75
- layer = SafetyLayer(agent=MockAgent(), api_key="demo-1234", agent_id="safety-1", raise_on_block=True)
76
- with pytest.raises(SafenticError):
77
- layer.call_tool("send_email", {"body": TESTS["made_up_timeframe"]})
78
-
79
- def test_safety_layer_returns_result():
80
- safe_input = {"body": "This is a neutral and policy-safe email message."}
81
- layer = SafetyLayer(agent=MockAgent(), api_key="demo-1234", agent_id="safety-2", raise_on_block=False)
82
- assert layer.call_tool("send_email", safe_input)["status"] == "mock-success"
83
-
84
- @mock.patch("safentic.verifiers.sentence_verifier.SentenceTransformer")
85
- def test_similarity_score_consistency(mock_model_class):
86
- mock_model = mock_model_class.return_value
87
- mock_model.encode.return_value = [[1.0], [1.0]]
88
- verifier = SentenceTransformerVerifier()
89
- score = verifier.similarity("a", "a")
90
- assert math.isclose(score, 1.0, abs_tol=1e-4)
91
-
92
- @mock.patch("safentic.verifiers.sentence_verifier.SentenceTransformer")
93
- @pytest.mark.parametrize("a,b,score,expected", [
94
- ("a", "a", 0.95, "allow"),
95
- ("a", "b", 0.5, "verify"),
96
- ("x", "y", 0.1, "block"),
97
- ])
98
- def test_verifier_threshold_behavior(mock_model_class, a, b, score, expected):
99
- mock_model = mock_model_class.return_value
100
- with mock.patch("safentic.verifiers.sentence_verifier.util.cos_sim") as mock_cos_sim:
101
- mock_cos_sim.return_value = mock.Mock()
102
- mock_cos_sim.return_value.item.return_value = score
103
-
104
- verifier = SentenceTransformerVerifier(low_threshold=0.2, high_threshold=0.9)
105
- result = verifier.decision(a, b)
106
- assert result == expected
107
-
108
- @mock.patch("safentic.verifiers.sentence_verifier.SentenceTransformer")
109
- def test_verifier_explains_score(mock_model_class):
110
- mock_model = mock_model_class.return_value
111
- mock_model.encode.return_value = [[1.0], [1.0]]
112
- verifier = SentenceTransformerVerifier()
113
- assert "Semantic similarity" in verifier.explain("a", "a")
114
-
115
- @mock.patch("safentic.logger.audit.open", new_callable=mock.mock_open)
116
- def test_logger_set_level_and_log(mock_open_file):
117
- os.environ["SAFE_AUDIT_LOG"] = "1"
118
- logger = AuditLogger(config={"txt": "dummy.txt", "json": "dummy.jsonl"})
119
- logger.set_level("DEBUG")
120
- logger.log(agent_id="mock-agent", tool="send_email", allowed=True)
121
- logger.log(agent_id="mock-agent", tool="send_email", allowed=False, reason="test")
122
- handle = mock_open_file()
123
- handle.write.assert_called()
124
-
125
- def test_logger_invalid_level():
126
- with pytest.raises(ValueError):
127
- AuditLogger().set_level("FAKE")
128
-
129
- @mock.patch("safentic.logger.audit.open", side_effect=OSError("mocked failure"))
130
- def test_logger_gracefully_fails_json_write(mock_open):
131
- logger = AuditLogger(config={"txt": "ok.txt", "json": "fail.jsonl"})
132
- logger.log("agent", "tool", False, reason="test")
File without changes