proofledger 0.3.0__tar.gz → 0.4.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.
- {proofledger-0.3.0 → proofledger-0.4.0}/PKG-INFO +1 -1
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/__init__.py +12 -1
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/client.py +41 -3
- {proofledger-0.3.0 → proofledger-0.4.0}/pyproject.toml +1 -1
- {proofledger-0.3.0 → proofledger-0.4.0}/.gitignore +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/LICENSE +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/README.md +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/examples/basic.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/adapters.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/hashing.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/ids.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/pricing.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/signing.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/proofledger/transport.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/tests/test_adapters.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/tests/test_client.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/tests/test_hashing.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/tests/test_pricing.py +0 -0
- {proofledger-0.3.0 → proofledger-0.4.0}/tests/test_signing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proofledger
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Framework-agnostic, tamper-evident audit layer for AI agents. Hash chains verify byte-for-byte against the ProofLedger TypeScript SDK and dashboard.
|
|
5
5
|
Project-URL: Homepage, https://proofledger.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/jorama/proofledger
|
|
@@ -18,7 +18,13 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
from typing import Any, Callable, Dict, Optional, TypeVar
|
|
20
20
|
|
|
21
|
-
from .client import
|
|
21
|
+
from .client import (
|
|
22
|
+
RunHandle,
|
|
23
|
+
ProofLedgerClient,
|
|
24
|
+
PolicyBlockedError,
|
|
25
|
+
ApprovalDeniedError,
|
|
26
|
+
iso_now,
|
|
27
|
+
)
|
|
22
28
|
from .hashing import (
|
|
23
29
|
GENESIS_HASH,
|
|
24
30
|
ChainVerificationResult,
|
|
@@ -63,6 +69,7 @@ __all__ = [
|
|
|
63
69
|
"get_trust_score",
|
|
64
70
|
"flush",
|
|
65
71
|
"PolicyBlockedError",
|
|
72
|
+
"ApprovalDeniedError",
|
|
66
73
|
# Adapters
|
|
67
74
|
"instrument",
|
|
68
75
|
"wrap_openai",
|
|
@@ -127,6 +134,8 @@ def enable(
|
|
|
127
134
|
version: Optional[str] = None,
|
|
128
135
|
auto_capture: bool = True,
|
|
129
136
|
policy_mode: str = "monitor",
|
|
137
|
+
approval_interval_ms: float = 2000,
|
|
138
|
+
approval_timeout_ms: float = 300_000,
|
|
130
139
|
) -> ProofLedgerClient:
|
|
131
140
|
"""Initialize the global client and return it for advanced use."""
|
|
132
141
|
global _global_client
|
|
@@ -146,6 +155,8 @@ def enable(
|
|
|
146
155
|
version=version,
|
|
147
156
|
auto_capture=auto_capture,
|
|
148
157
|
policy_mode=policy_mode,
|
|
158
|
+
approval_interval_ms=approval_interval_ms,
|
|
159
|
+
approval_timeout_ms=approval_timeout_ms,
|
|
149
160
|
)
|
|
150
161
|
return _global_client
|
|
151
162
|
|
|
@@ -22,7 +22,13 @@ from .hashing import (
|
|
|
22
22
|
from .pricing import estimate_cost
|
|
23
23
|
from .transport import HttpTransport, LocalTransport, Transport
|
|
24
24
|
|
|
25
|
-
__all__ = [
|
|
25
|
+
__all__ = [
|
|
26
|
+
"ProofLedgerClient",
|
|
27
|
+
"RunHandle",
|
|
28
|
+
"PolicyBlockedError",
|
|
29
|
+
"ApprovalDeniedError",
|
|
30
|
+
"iso_now",
|
|
31
|
+
]
|
|
26
32
|
|
|
27
33
|
T = TypeVar("T")
|
|
28
34
|
|
|
@@ -97,6 +103,8 @@ class ProofLedgerClient:
|
|
|
97
103
|
version: Optional[str] = None,
|
|
98
104
|
auto_capture: bool = True,
|
|
99
105
|
policy_mode: str = "monitor",
|
|
106
|
+
approval_interval_ms: float = 2000,
|
|
107
|
+
approval_timeout_ms: float = 300_000,
|
|
100
108
|
) -> None:
|
|
101
109
|
use_local = local is True or not api_key
|
|
102
110
|
self.config: Dict[str, Any] = {
|
|
@@ -114,6 +122,8 @@ class ProofLedgerClient:
|
|
|
114
122
|
"version": version,
|
|
115
123
|
"auto_capture": auto_capture is not False,
|
|
116
124
|
"policy_mode": policy_mode if policy_mode in ("monitor", "enforce") else "monitor",
|
|
125
|
+
"approval_interval_ms": approval_interval_ms,
|
|
126
|
+
"approval_timeout_ms": approval_timeout_ms,
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
#: Active agent identity for trust events. The private key lives in
|
|
@@ -696,8 +706,21 @@ class ProofLedgerClient:
|
|
|
696
706
|
}
|
|
697
707
|
result = self.transport.send_trust_event(payload)
|
|
698
708
|
policy = result.get("policy") or {}
|
|
699
|
-
if self.config["policy_mode"] == "enforce"
|
|
700
|
-
|
|
709
|
+
if self.config["policy_mode"] == "enforce":
|
|
710
|
+
if policy.get("decision") == "block":
|
|
711
|
+
raise PolicyBlockedError(policy)
|
|
712
|
+
# require_approval GATES the action: block until a human decides.
|
|
713
|
+
# Fail closed — denied or timed-out-pending both raise.
|
|
714
|
+
approval = result.get("approval") or {}
|
|
715
|
+
if policy.get("decision") == "require_approval" and approval.get("id"):
|
|
716
|
+
status = self.await_approval(
|
|
717
|
+
approval["id"],
|
|
718
|
+
interval_ms=self.config["approval_interval_ms"],
|
|
719
|
+
timeout_ms=self.config["approval_timeout_ms"],
|
|
720
|
+
)
|
|
721
|
+
if status != "approved":
|
|
722
|
+
raise ApprovalDeniedError(approval["id"], status, action)
|
|
723
|
+
approval["status"] = "approved"
|
|
701
724
|
return result
|
|
702
725
|
|
|
703
726
|
def log_decision(self, **kwargs: Any) -> Dict[str, Any]:
|
|
@@ -775,6 +798,21 @@ class PolicyBlockedError(RuntimeError):
|
|
|
775
798
|
self.evaluation = evaluation
|
|
776
799
|
|
|
777
800
|
|
|
801
|
+
class ApprovalDeniedError(RuntimeError):
|
|
802
|
+
"""Raised in enforce mode when a require_approval action is denied — or
|
|
803
|
+
still pending when the wait times out (fail-closed)."""
|
|
804
|
+
|
|
805
|
+
def __init__(self, approval_id: str, status: str, title: Optional[str] = None) -> None:
|
|
806
|
+
suffix = f": {title}" if title else ""
|
|
807
|
+
super().__init__(
|
|
808
|
+
f"ProofLedger approval timed out while pending{suffix}"
|
|
809
|
+
if status == "pending"
|
|
810
|
+
else f"ProofLedger approval denied{suffix}"
|
|
811
|
+
)
|
|
812
|
+
self.approval_id = approval_id
|
|
813
|
+
self.status = status
|
|
814
|
+
|
|
815
|
+
|
|
778
816
|
def _now_ms() -> float:
|
|
779
817
|
"""Epoch milliseconds, matching JS ``Date.now()``."""
|
|
780
818
|
return time.time() * 1000.0
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "proofledger"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Framework-agnostic, tamper-evident audit layer for AI agents. Hash chains verify byte-for-byte against the ProofLedger TypeScript SDK and dashboard."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|