volidator-python 0.1.0__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.
@@ -0,0 +1,70 @@
1
+ from typing import Any, Optional
2
+
3
+
4
+ class VolidatorCompliance:
5
+ def __init__(self, client):
6
+ self.client = client
7
+
8
+ def _log_with_control(
9
+ self,
10
+ action: str,
11
+ soc2_control: str,
12
+ iso_control: str,
13
+ payload: dict[str, Any],
14
+ max_meta_override: Optional[int] = None
15
+ ) -> bool:
16
+ metadata = payload.get("metadata") or {}
17
+ enriched_metadata = {
18
+ **metadata,
19
+ "soc2_control": soc2_control,
20
+ "iso27001": iso_control,
21
+ }
22
+ full_payload = {**payload, "action": action, "metadata": enriched_metadata}
23
+ return self.client.log(full_payload, max_meta_override)
24
+
25
+ async def _log_with_control_async(
26
+ self,
27
+ action: str,
28
+ soc2_control: str,
29
+ iso_control: str,
30
+ payload: dict[str, Any],
31
+ max_meta_override: Optional[int] = None
32
+ ) -> bool:
33
+ metadata = payload.get("metadata") or {}
34
+ enriched_metadata = {
35
+ **metadata,
36
+ "soc2_control": soc2_control,
37
+ "iso27001": iso_control,
38
+ }
39
+ full_payload = {**payload, "action": action, "metadata": enriched_metadata}
40
+ return await self.client.log_async(full_payload, max_meta_override)
41
+
42
+ def access_revoked(self, payload: dict[str, Any]) -> bool:
43
+ return self._log_with_control("access.revoked", "CC6.1", "A.9.2.6", payload)
44
+
45
+ async def access_revoked_async(self, payload: dict[str, Any]) -> bool:
46
+ return await self._log_with_control_async("access.revoked", "CC6.1", "A.9.2.6", payload)
47
+
48
+ def access_granted(self, payload: dict[str, Any]) -> bool:
49
+ return self._log_with_control("access.granted", "CC6.1", "A.9.2.1", payload)
50
+
51
+ async def access_granted_async(self, payload: dict[str, Any]) -> bool:
52
+ return await self._log_with_control_async("access.granted", "CC6.1", "A.9.2.1", payload)
53
+
54
+ def data_exported(self, payload: dict[str, Any]) -> bool:
55
+ return self._log_with_control("data.exported", "CC6.6", "A.12.4.1", payload)
56
+
57
+ async def data_exported_async(self, payload: dict[str, Any]) -> bool:
58
+ return await self._log_with_control_async("data.exported", "CC6.6", "A.12.4.1", payload)
59
+
60
+ def system_config_changed(self, payload: dict[str, Any]) -> bool:
61
+ return self._log_with_control("system.config_changed", "CC6.2", "A.12.1.2", payload)
62
+
63
+ async def system_config_changed_async(self, payload: dict[str, Any]) -> bool:
64
+ return await self._log_with_control_async("system.config_changed", "CC6.2", "A.12.1.2", payload)
65
+
66
+ def mfa_enabled(self, payload: dict[str, Any]) -> bool:
67
+ return self._log_with_control("mfa.enabled", "CC6.3", "A.9.4.2", payload)
68
+
69
+ async def mfa_enabled_async(self, payload: dict[str, Any]) -> bool:
70
+ return await self._log_with_control_async("mfa.enabled", "CC6.3", "A.9.4.2", payload)
@@ -0,0 +1 @@
1
+ # Volidator framework integration plugins
@@ -0,0 +1,115 @@
1
+ import time
2
+ from typing import Any, Optional
3
+ from ..client import agent_context_store
4
+
5
+ try:
6
+ from langchain_core.callbacks import BaseCallbackHandler # type: ignore
7
+ HAS_LANGCHAIN = True
8
+ except ImportError:
9
+ class BaseCallbackHandler:
10
+ pass
11
+ HAS_LANGCHAIN = False
12
+
13
+
14
+ class VolidatorLangChainHandler(BaseCallbackHandler):
15
+ def __init__(self, client, actor: str, tenant: Optional[str] = None):
16
+ if not HAS_LANGCHAIN:
17
+ raise RuntimeError(
18
+ "langchain-core is not installed. Please install it with 'pip install langchain-core' to use this handler."
19
+ )
20
+ super().__init__()
21
+ self.client = client
22
+ self.actor = actor
23
+ self.tenant = tenant
24
+ self.run_map = {}
25
+ self._tokens = {}
26
+
27
+ def on_tool_start(
28
+ self,
29
+ serialized: dict[str, Any],
30
+ input_str: str,
31
+ *,
32
+ run_id: Any,
33
+ parent_run_id: Optional[Any] = None,
34
+ **kwargs: Any
35
+ ) -> None:
36
+ tool_name = serialized.get("name") or "unnamed_tool"
37
+ rationale = f"Executing tool {tool_name} with input: {input_str[:100]}"
38
+
39
+ # Bind agent context in contextvars
40
+ token = agent_context_store.set({
41
+ "traceId": str(run_id),
42
+ "toolName": tool_name,
43
+ "rationale": rationale,
44
+ })
45
+ self._tokens[str(run_id)] = token
46
+
47
+ self.run_map[str(run_id)] = {
48
+ "toolName": tool_name,
49
+ "toolInput": {"input": input_str},
50
+ "startTime": time.time(),
51
+ }
52
+
53
+ def on_tool_end(
54
+ self,
55
+ output: str,
56
+ *,
57
+ run_id: Any,
58
+ parent_run_id: Optional[Any] = None,
59
+ **kwargs: Any
60
+ ) -> None:
61
+ # Prevent token leaking
62
+ token = self._tokens.pop(str(run_id), None)
63
+ if token:
64
+ agent_context_store.reset(token)
65
+
66
+ run = self.run_map.pop(str(run_id), None)
67
+ if not run:
68
+ return
69
+
70
+ latency_ms = int((time.time() - run["startTime"]) * 1000)
71
+
72
+ try:
73
+ self.client.agent.tool_call({
74
+ "actor": self.actor,
75
+ "tenant": self.tenant,
76
+ "toolName": run["toolName"],
77
+ "toolInput": run["toolInput"],
78
+ "toolOutput": {"output": str(output)},
79
+ "latencyMs": latency_ms,
80
+ "success": True,
81
+ })
82
+ except Exception:
83
+ pass
84
+
85
+ def on_tool_error(
86
+ self,
87
+ error: BaseException,
88
+ *,
89
+ run_id: Any,
90
+ parent_run_id: Optional[Any] = None,
91
+ **kwargs: Any
92
+ ) -> None:
93
+ # Prevent token leaking
94
+ token = self._tokens.pop(str(run_id), None)
95
+ if token:
96
+ agent_context_store.reset(token)
97
+
98
+ run = self.run_map.pop(str(run_id), None)
99
+ if not run:
100
+ return
101
+
102
+ latency_ms = int((time.time() - run["startTime"]) * 1000)
103
+
104
+ try:
105
+ self.client.agent.tool_call({
106
+ "actor": self.actor,
107
+ "tenant": self.tenant,
108
+ "toolName": run["toolName"],
109
+ "toolInput": run["toolInput"],
110
+ "toolOutput": {"error": str(error)},
111
+ "latencyMs": latency_ms,
112
+ "success": False,
113
+ })
114
+ except Exception:
115
+ pass
@@ -0,0 +1,96 @@
1
+ import time
2
+ from typing import Any, Optional
3
+ from ..client import agent_context_store
4
+
5
+ try:
6
+ from llama_index.core.callbacks.base_handler import BaseCallbackHandler # type: ignore
7
+ from llama_index.core.callbacks.schema import CBEventType # type: ignore
8
+ HAS_LLAMAINDEX = True
9
+ except ImportError:
10
+ class BaseCallbackHandler:
11
+ pass
12
+ class CBEventType:
13
+ TOOL = "tool"
14
+ HAS_LLAMAINDEX = False
15
+
16
+
17
+ class VolidatorLlamaIndexHandler(BaseCallbackHandler):
18
+ def __init__(self, client, actor: str, tenant: Optional[str] = None):
19
+ if not HAS_LLAMAINDEX:
20
+ raise RuntimeError(
21
+ "llama-index-core is not installed. Please install it with 'pip install llama-index-core' to use this handler."
22
+ )
23
+ # Call llama-index BaseCallbackHandler constructor if it exists
24
+ if hasattr(BaseCallbackHandler, "__init__"):
25
+ super().__init__(event_starts_to_ignore=[], event_ends_to_ignore=[])
26
+ self.client = client
27
+ self.actor = actor
28
+ self.tenant = tenant
29
+ self.run_map = {}
30
+ self._tokens = {}
31
+
32
+ def on_event_start(
33
+ self,
34
+ event_type: CBEventType,
35
+ payload: Optional[dict[str, Any]] = None,
36
+ event_id: str = "",
37
+ **kwargs: Any
38
+ ) -> str:
39
+ if event_type == CBEventType.TOOL:
40
+ tool_name = "unnamed_tool"
41
+ tool_input = {}
42
+ if payload and "tool" in payload:
43
+ tool_name = payload["tool"].name
44
+ tool_input = payload.get("input", {})
45
+
46
+ rationale = f"Executing LlamaIndex tool {tool_name}"
47
+
48
+ token = agent_context_store.set({
49
+ "traceId": event_id,
50
+ "toolName": tool_name,
51
+ "rationale": rationale,
52
+ })
53
+ self._tokens[event_id] = token
54
+
55
+ self.run_map[event_id] = {
56
+ "toolName": tool_name,
57
+ "toolInput": tool_input,
58
+ "startTime": time.time(),
59
+ }
60
+ return event_id
61
+
62
+ def on_event_end(
63
+ self,
64
+ event_type: CBEventType,
65
+ payload: Optional[dict[str, Any]] = None,
66
+ event_id: str = "",
67
+ **kwargs: Any
68
+ ) -> None:
69
+ token = self._tokens.pop(event_id, None)
70
+ if token:
71
+ agent_context_store.reset(token)
72
+
73
+ if event_type == CBEventType.TOOL:
74
+ run = self.run_map.pop(event_id, None)
75
+ if not run:
76
+ return
77
+
78
+ latency_ms = int((time.time() - run["startTime"]) * 1000)
79
+
80
+ tool_output = {}
81
+ success = True
82
+ if payload and "response" in payload:
83
+ tool_output = {"output": str(payload["response"])}
84
+
85
+ try:
86
+ self.client.agent.tool_call({
87
+ "actor": self.actor,
88
+ "tenant": self.tenant,
89
+ "toolName": run["toolName"],
90
+ "toolInput": run["toolInput"],
91
+ "toolOutput": tool_output,
92
+ "latencyMs": latency_ms,
93
+ "success": success,
94
+ })
95
+ except Exception:
96
+ pass
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: volidator-python
3
+ Version: 0.1.0
4
+ Summary: Zero-knowledge, server-blind audit logging for Python and agentic workflows.
5
+ License: MIT
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: cryptography>=3.4.0
12
+ Provides-Extra: test
13
+ Requires-Dist: pytest>=7.0.0; extra == "test"
14
+ Requires-Dist: pytest-asyncio>=0.18.0; extra == "test"
15
+ Requires-Dist: httpx>=0.20.0; extra == "test"
16
+ Provides-Extra: langchain
17
+ Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
18
+ Provides-Extra: llamaindex
19
+ Requires-Dist: llama-index-core>=0.10.0; extra == "llamaindex"
20
+
21
+ # Volidator Python SDK
22
+
23
+ **Zero-knowledge, server-blind audit logging for Python and autonomous AI agentic workflows.**
24
+
25
+ Volidator encrypts every log entry locally — on your server — before sending it. The Volidator backend stores only ciphertext and blind indexes. Your plaintext data never leaves your infrastructure unencrypted.
26
+
27
+ ---
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install volidator-python
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Environment Setup
38
+
39
+ Generate a secure 32-byte encryption key (64 hex characters) with the `vol-dek-` prefix:
40
+ ```bash
41
+ python -c "import os; print('vol-dek-' + os.urandom(32).hex())"
42
+ ```
43
+
44
+ Add secrets to your environment:
45
+ ```bash
46
+ VOLIDATOR_API_KEY="val_live_xxxxxxxx..."
47
+ VOLIDATOR_ENCRYPTION_KEY="vol-dek-xxxxxxxx..."
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Quick Start
53
+
54
+ ```python
55
+ import os
56
+ from volidator import VolidatorClient
57
+
58
+ volidator = VolidatorClient(
59
+ api_key=os.environ["VOLIDATOR_API_KEY"],
60
+ encryption_key=os.environ["VOLIDATOR_ENCRYPTION_KEY"]
61
+ )
62
+
63
+ # Log a system event
64
+ volidator.log({
65
+ "actor": "usr_12345",
66
+ "action": "user.login.success",
67
+ "target": "workspace_abc789",
68
+ "metadata": {
69
+ "device": "Workstation",
70
+ "auth_provider": "Okta"
71
+ }
72
+ })
73
+ ```
74
+
75
+ ---
76
+
77
+ ## AI Agent Auditing (`VolidatorAgent`)
78
+
79
+ For auditing LLM chains, agent reasoning loops, and multi-agent handoffs, use the `volidator.agent` namespace:
80
+
81
+ ```python
82
+ # Log a decision made autonomously by the model
83
+ volidator.agent.decision({
84
+ "actor": "writer-agent-v1",
85
+ "traceId": "run-xyz-456",
86
+ "decision": "publish_article",
87
+ "rationale": "grammar check and safety alignments passed verification checks",
88
+ "confidenceScore": 0.98,
89
+ "modelId": "gpt-4o"
90
+ })
91
+
92
+ # Log a tool execution call
93
+ volidator.agent.tool_call({
94
+ "actor": "search-agent-v1",
95
+ "traceId": "run-xyz-456",
96
+ "toolName": "web_search",
97
+ "toolInput": {"query": "AI regulation"},
98
+ "toolOutput": {"results": [...]},
99
+ "success": True,
100
+ "latencyMs": 245
101
+ })
102
+ ```
@@ -0,0 +1,11 @@
1
+ volidator/__init__.py,sha256=-r28T-iGsOZO_GhUrcFCVLo961UKb1Mug-_N8LdLAeQ,186
2
+ volidator/agent.py,sha256=cHnP4cKIn_CtpXeyMeoshYUhrDprgusgPFZdhmoaE6Q,4982
3
+ volidator/client.py,sha256=0NalVvOg5yJKraytUQsNgt7X5VPsipQe4HOS5CpaxYI,44745
4
+ volidator/compliance.py,sha256=_Ci4C4QT4kOopLW4K6fWMjyUUSgxho-RxDHIwOFRcyU,2877
5
+ volidator/plugins/__init__.py,sha256=HvRs6AxjXeGeLI8exhFMRWH_Wdpn0dh49LT8VHWZ8f8,42
6
+ volidator/plugins/langchain.py,sha256=b1HTK51PXHH6SqS4skpQa2EbThHr7b3VhVTwsTMF_HQ,3329
7
+ volidator/plugins/llamaindex.py,sha256=4YrmM9yLfXx-FWSOJHnIQnxdr99qCc9Ce33unvz8_-Y,3169
8
+ volidator_python-0.1.0.dist-info/METADATA,sha256=1RkJviwt0SVRuOIAsoFdsYS0CDrBwXm9jY609fiYI7o,2781
9
+ volidator_python-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
10
+ volidator_python-0.1.0.dist-info/top_level.txt,sha256=7taXS7XldS3KWgMj_tly0_mG0o4wUN-rpEhfluSs3U4,10
11
+ volidator_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ volidator