ledgix-python 0.1.11__tar.gz → 0.1.13__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.
Files changed (24) hide show
  1. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/PKG-INFO +2 -2
  2. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/README.md +1 -1
  3. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/pyproject.toml +1 -1
  4. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/__init__.py +1 -1
  5. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/client.py +42 -4
  6. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/models.py +34 -1
  7. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/.gitignore +0 -0
  8. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/demo.py +0 -0
  9. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/requirements.txt +0 -0
  10. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/adapters/__init__.py +0 -0
  11. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/adapters/crewai.py +0 -0
  12. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/adapters/langchain.py +0 -0
  13. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/adapters/llamaindex.py +0 -0
  14. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/config.py +0 -0
  15. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/enforce.py +0 -0
  16. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/exceptions.py +0 -0
  17. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/src/ledgix_python/manifest.py +0 -0
  18. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/__init__.py +0 -0
  19. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/conftest.py +0 -0
  20. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/test_adapters.py +0 -0
  21. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/test_client.py +0 -0
  22. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/test_enforce.py +0 -0
  23. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/test_manifest.py +0 -0
  24. {ledgix_python-0.1.11 → ledgix_python-0.1.13}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ledgix-python
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Agent-agnostic compliance shim for SOX 404 policy enforcement via the ALCV Vault
5
5
  Project-URL: Homepage, https://github.com/ledgix-dev/python-sdk
6
6
  Project-URL: Documentation, https://docs.ledgix.dev
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
 
43
43
  # Ledgix ALCV — Python SDK
44
44
 
45
- [![PyPI](https://img.shields.io/badge/pypi-v0.1.11-blue)](https://pypi.org/project/ledgix-python/)
45
+ [![PyPI](https://img.shields.io/badge/pypi-v0.1.13-blue)](https://pypi.org/project/ledgix-python/)
46
46
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://python.org)
47
47
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
48
48
 
@@ -1,6 +1,6 @@
1
1
  # Ledgix ALCV — Python SDK
2
2
 
3
- [![PyPI](https://img.shields.io/badge/pypi-v0.1.11-blue)](https://pypi.org/project/ledgix-python/)
3
+ [![PyPI](https://img.shields.io/badge/pypi-v0.1.13-blue)](https://pypi.org/project/ledgix-python/)
4
4
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://python.org)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
6
6
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ledgix-python"
7
- version = "0.1.11"
7
+ version = "0.1.13"
8
8
  description = "Agent-agnostic compliance shim for SOX 404 policy enforcement via the ALCV Vault"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -59,7 +59,7 @@ from .models import (
59
59
  PolicyRegistrationResponse,
60
60
  )
61
61
 
62
- __version__ = "0.1.11"
62
+ __version__ = "0.1.13"
63
63
 
64
64
  __all__ = [
65
65
  # Core
@@ -40,6 +40,7 @@ from .models import (
40
40
  LedgerVerificationResult,
41
41
  PolicyRegistration,
42
42
  PolicyRegistrationResponse,
43
+ _MISSING,
43
44
  )
44
45
 
45
46
 
@@ -886,25 +887,62 @@ class LedgixClient:
886
887
  return []
887
888
 
888
889
  def _build_event_hash(self, entry: LedgerEntry) -> str:
890
+ raw_tool_args = entry.raw_tool_args if entry.raw_tool_args is not _MISSING else entry.tool_args
891
+ raw_action_metadata = (
892
+ entry.raw_action_metadata if entry.raw_action_metadata is not _MISSING else entry.action_metadata
893
+ )
894
+ if raw_action_metadata is _MISSING:
895
+ raw_action_metadata = {}
896
+ raw_citations = entry.raw_citations if entry.raw_citations is not _MISSING else entry.citations
897
+ raw_evidence_chunks = (
898
+ entry.raw_evidence_chunks if entry.raw_evidence_chunks is not _MISSING else entry.evidence_chunks
899
+ )
900
+
889
901
  payload = self._encode_deterministic_cbor(
902
+ {
903
+ "accepted_at": entry.accepted_at,
904
+ "action_category": entry.action_category,
905
+ "action_metadata": self._normalize_json_numbers_for_cbor(raw_action_metadata),
906
+ "agent_id": entry.agent_id,
907
+ "approved": entry.approved,
908
+ "canonical_version": entry.canonical_version,
909
+ "citations": self._normalize_json_numbers_for_cbor(raw_citations),
910
+ "confidence": entry.confidence,
911
+ "event_uuid": entry.event_uuid,
912
+ "evidence_chunks": self._normalize_json_numbers_for_cbor(raw_evidence_chunks),
913
+ "intent_hash": entry.intent_hash,
914
+ "policy_id": entry.policy_id,
915
+ "policy_version_id": entry.policy_version_id,
916
+ "policy_content_hash": entry.policy_content_hash,
917
+ "reason": entry.reason,
918
+ "request_id": entry.request_id,
919
+ "tool_args": self._normalize_json_numbers_for_cbor(raw_tool_args),
920
+ "tool_name": entry.tool_name,
921
+ }
922
+ )
923
+ current_hash = self._hash_event_payload(payload)
924
+ if current_hash == entry.event_hash:
925
+ return current_hash
926
+
927
+ legacy_payload = self._encode_deterministic_cbor(
890
928
  {
891
929
  "accepted_at": entry.accepted_at,
892
930
  "agent_id": entry.agent_id,
893
931
  "approved": entry.approved,
894
932
  "canonical_version": entry.canonical_version,
895
- "citations": self._normalize_json_numbers_for_cbor(entry.citations),
933
+ "citations": self._normalize_json_numbers_for_cbor(raw_citations),
896
934
  "confidence": entry.confidence,
897
935
  "event_uuid": entry.event_uuid,
898
- "evidence_chunks": self._normalize_json_numbers_for_cbor(entry.evidence_chunks),
936
+ "evidence_chunks": self._normalize_json_numbers_for_cbor(raw_evidence_chunks),
899
937
  "intent_hash": entry.intent_hash,
900
938
  "policy_id": entry.policy_id,
901
939
  "reason": entry.reason,
902
940
  "request_id": entry.request_id,
903
- "tool_args": self._normalize_json_numbers_for_cbor(entry.tool_args),
941
+ "tool_args": self._normalize_json_numbers_for_cbor(raw_tool_args),
904
942
  "tool_name": entry.tool_name,
905
943
  }
906
944
  )
907
- return self._hash_event_payload(payload)
945
+ return self._hash_event_payload(legacy_payload)
908
946
 
909
947
  def _has_protected_event_fields(self, entry: LedgerEntry) -> bool:
910
948
  return isinstance(entry.intent_hash, str) and len(entry.intent_hash) > 0
@@ -5,7 +5,9 @@ from __future__ import annotations
5
5
 
6
6
  from typing import Any
7
7
 
8
- from pydantic import AliasChoices, BaseModel, ConfigDict, Field, field_validator
8
+ from pydantic import AliasChoices, BaseModel, ConfigDict, Field, field_validator, model_validator
9
+
10
+ _MISSING = object()
9
11
 
10
12
 
11
13
  class ClearanceRequest(BaseModel):
@@ -75,12 +77,20 @@ class LedgerEntry(BaseModel):
75
77
  request_id: str
76
78
  agent_id: str = ""
77
79
  policy_id: str = ""
80
+ policy_version_id: str = ""
81
+ policy_content_hash: str = ""
78
82
  intent_hash: str = ""
79
83
  tool_name: str
80
84
  tool_args: dict[str, Any] = Field(default_factory=dict)
85
+ raw_tool_args: Any = Field(default_factory=lambda: _MISSING, exclude=True)
86
+ action_category: str = ""
87
+ action_metadata: dict[str, Any] = Field(default_factory=dict)
88
+ raw_action_metadata: Any = Field(default_factory=lambda: _MISSING, exclude=True)
81
89
  reason: str = ""
82
90
  citations: list[dict[str, Any]] = Field(default_factory=list)
91
+ raw_citations: Any = Field(default_factory=lambda: _MISSING, exclude=True)
83
92
  evidence_chunks: list[dict[str, Any]] = Field(default_factory=list)
93
+ raw_evidence_chunks: Any = Field(default_factory=lambda: _MISSING, exclude=True)
84
94
  confidence: float = Field(default=0.0, ge=0.0, le=1.0)
85
95
  approved: bool
86
96
  accepted_at: str = Field(validation_alias=AliasChoices("accepted_at", "decided_at"))
@@ -103,6 +113,29 @@ class LedgerEntry(BaseModel):
103
113
  )
104
114
  receipt_payload: str = Field(default="")
105
115
 
116
+ @model_validator(mode="before")
117
+ @classmethod
118
+ def _capture_raw_verification_fields(cls, value: Any) -> Any:
119
+ if not isinstance(value, dict):
120
+ return value
121
+ data = dict(value)
122
+ if "raw_tool_args" not in data and "tool_args" in data:
123
+ data["raw_tool_args"] = data.get("tool_args")
124
+ if "raw_action_metadata" not in data and "action_metadata" in data:
125
+ data["raw_action_metadata"] = data.get("action_metadata")
126
+ if "raw_citations" not in data and "citations" in data:
127
+ data["raw_citations"] = data.get("citations")
128
+ if "raw_evidence_chunks" not in data and "evidence_chunks" in data:
129
+ data["raw_evidence_chunks"] = data.get("evidence_chunks")
130
+ return data
131
+
132
+ @field_validator("tool_args", "action_metadata", mode="before")
133
+ @classmethod
134
+ def _normalize_nullable_dicts(cls, value: Any) -> Any:
135
+ if value is None:
136
+ return {}
137
+ return value
138
+
106
139
  @field_validator("citations", "evidence_chunks", mode="before")
107
140
  @classmethod
108
141
  def _normalize_nullable_lists(cls, value: Any) -> Any:
File without changes