cmgl 1.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.
Files changed (91) hide show
  1. cmgl/__init__.py +269 -0
  2. cmgl/__main__.py +4 -0
  3. cmgl/absence.py +29 -0
  4. cmgl/adapters/__init__.py +3 -0
  5. cmgl/adapters/binding.py +212 -0
  6. cmgl/adapters/common.py +331 -0
  7. cmgl/adapters/graphiti.py +235 -0
  8. cmgl/adapters/langgraph.py +183 -0
  9. cmgl/adapters/langmem.py +395 -0
  10. cmgl/adapters/mem0.py +339 -0
  11. cmgl/admission.py +78 -0
  12. cmgl/audit.py +442 -0
  13. cmgl/authority.py +310 -0
  14. cmgl/backends/__init__.py +4 -0
  15. cmgl/backends/inmemory.py +216 -0
  16. cmgl/backends/protocol.py +34 -0
  17. cmgl/canonical.py +45 -0
  18. cmgl/challenge.py +28 -0
  19. cmgl/checker.py +64 -0
  20. cmgl/cli.py +119 -0
  21. cmgl/commands/__init__.py +1 -0
  22. cmgl/commands/_common.py +16 -0
  23. cmgl/commands/adapters.py +68 -0
  24. cmgl/commands/audit.py +42 -0
  25. cmgl/commands/authority.py +111 -0
  26. cmgl/commands/compression.py +29 -0
  27. cmgl/commands/conformance.py +44 -0
  28. cmgl/commands/ledger.py +80 -0
  29. cmgl/commands/lifecycle.py +98 -0
  30. cmgl/commands/memory.py +116 -0
  31. cmgl/commands/promotion.py +42 -0
  32. cmgl/commands/receipt.py +55 -0
  33. cmgl/commands/reference.py +27 -0
  34. cmgl/commands/retrieve.py +45 -0
  35. cmgl/commands/schema.py +27 -0
  36. cmgl/commands/telemetry.py +50 -0
  37. cmgl/commands/validate.py +56 -0
  38. cmgl/commands/workflow.py +29 -0
  39. cmgl/compression.py +250 -0
  40. cmgl/config.py +116 -0
  41. cmgl/conformance.py +119 -0
  42. cmgl/contracts/__init__.py +198 -0
  43. cmgl/contracts/adapters.py +57 -0
  44. cmgl/contracts/authority.py +104 -0
  45. cmgl/contracts/base.py +22 -0
  46. cmgl/contracts/compression.py +106 -0
  47. cmgl/contracts/conformance.py +52 -0
  48. cmgl/contracts/contamination.py +57 -0
  49. cmgl/contracts/enums.py +184 -0
  50. cmgl/contracts/ledger.py +89 -0
  51. cmgl/contracts/lifecycle.py +105 -0
  52. cmgl/contracts/memory.py +188 -0
  53. cmgl/contracts/obligations.py +49 -0
  54. cmgl/contracts/promotion.py +114 -0
  55. cmgl/contracts/receipts.py +61 -0
  56. cmgl/contracts/rules.py +21 -0
  57. cmgl/contracts/telemetry.py +203 -0
  58. cmgl/contracts/workflow.py +95 -0
  59. cmgl/current.py +20 -0
  60. cmgl/digest.py +18 -0
  61. cmgl/doctor.py +83 -0
  62. cmgl/evidence.py +134 -0
  63. cmgl/exceptions.py +29 -0
  64. cmgl/guarded.py +177 -0
  65. cmgl/layer.py +359 -0
  66. cmgl/ledger.py +375 -0
  67. cmgl/ledger_signing.py +77 -0
  68. cmgl/lifecycle.py +157 -0
  69. cmgl/live_smoke.py +212 -0
  70. cmgl/models.py +189 -0
  71. cmgl/obligations.py +306 -0
  72. cmgl/pipeline.py +345 -0
  73. cmgl/policy.py +343 -0
  74. cmgl/py.typed +1 -0
  75. cmgl/receipt_verifier.py +235 -0
  76. cmgl/rules.py +502 -0
  77. cmgl/schemas.py +195 -0
  78. cmgl/state.py +96 -0
  79. cmgl/stress.py +86 -0
  80. cmgl/telemetry.py +70 -0
  81. cmgl/telemetry_ingest.py +137 -0
  82. cmgl/telemetry_replay.py +299 -0
  83. cmgl/time.py +19 -0
  84. cmgl/transitions.py +118 -0
  85. cmgl/validation.py +160 -0
  86. cmgl/workflow.py +227 -0
  87. cmgl-1.1.0.dist-info/METADATA +407 -0
  88. cmgl-1.1.0.dist-info/RECORD +91 -0
  89. cmgl-1.1.0.dist-info/WHEEL +4 -0
  90. cmgl-1.1.0.dist-info/entry_points.txt +2 -0
  91. cmgl-1.1.0.dist-info/licenses/LICENSE +199 -0
cmgl/__init__.py ADDED
@@ -0,0 +1,269 @@
1
+ from cmgl.admission import RetrievalFilterResult, candidate_from_event, filter_retrieval
2
+ from cmgl.authority import (
3
+ StrictAuthorityVerifier,
4
+ authorize_bundle,
5
+ authorize_request,
6
+ make_authority_bundle,
7
+ make_authority_evidence_bundle,
8
+ make_authority_receipt,
9
+ make_declared_scope,
10
+ make_protected_action_request,
11
+ )
12
+ from cmgl.compression import (
13
+ CompressionProbeSuite,
14
+ audit_compression_certificate,
15
+ compression_metrics,
16
+ make_compression_certificate,
17
+ )
18
+ from cmgl.config import (
19
+ AuthorityConfig,
20
+ CMGLConfig,
21
+ LedgerConfig,
22
+ PolicyConfig,
23
+ load_config,
24
+ )
25
+ from cmgl.conformance import audit_ledger_conformance
26
+ from cmgl.digest import sha256_digest
27
+ from cmgl.evidence import (
28
+ build_evidence_manifest,
29
+ build_input_set_manifest,
30
+ build_promotion_evidence_bundle,
31
+ build_replay_evidence,
32
+ versioned_ref_from_event,
33
+ )
34
+ from cmgl.guarded import GuardedMemoryBackend
35
+ from cmgl.layer import GovernanceLayer
36
+ from cmgl.ledger import (
37
+ AppendOnlyLedger,
38
+ LedgerRecord,
39
+ LedgerVerificationResult,
40
+ make_duplicate_policy_receipt,
41
+ make_schema_migration_record,
42
+ )
43
+ from cmgl.models import (
44
+ AbsenceNoticeType,
45
+ ActivePromotionReceipt,
46
+ AdapterOperationReceipt,
47
+ AdapterOperationStatus,
48
+ AdmissionDecision,
49
+ AuthorityBundle,
50
+ AuthorityEvidenceBundle,
51
+ AuthorityReceipt,
52
+ BackendName,
53
+ ChallengeStatus,
54
+ CompressionAuditReport,
55
+ CompressionBridgeProbe,
56
+ CompressionCertificate,
57
+ CompressionDeploymentProbe,
58
+ CompressionFailureClass,
59
+ CompressionGluingProbe,
60
+ ConformanceFinding,
61
+ ConformanceLevel,
62
+ ConformanceProfile,
63
+ ConformanceReport,
64
+ ConformanceSeverity,
65
+ ContaminationAuditReport,
66
+ ContaminationContext,
67
+ ContaminationLane,
68
+ ContaminationStateReplay,
69
+ CurrentMemoryView,
70
+ DeclaredScope,
71
+ DuplicatePolicyReceipt,
72
+ EvidenceBindingReport,
73
+ EvidenceManifest,
74
+ ExternalMemoryRef,
75
+ GovernanceProfile,
76
+ GovernanceReceiptBundle,
77
+ InputSetManifest,
78
+ LeaseReceipt,
79
+ LedgerAppendReceipt,
80
+ LedgerIntegrityReceipt,
81
+ LedgerLineStatus,
82
+ MemoryCandidate,
83
+ MemoryChallengeRecord,
84
+ MemoryEvent,
85
+ MemoryEventType,
86
+ MemoryGovernanceEvidenceContract,
87
+ MemoryRevision,
88
+ MemoryStateSnapshot,
89
+ MemoryStatus,
90
+ MemoryTelemetryEvent,
91
+ MetricResult,
92
+ MetricStatus,
93
+ ObligationGraph,
94
+ ObligationStatus,
95
+ PromotionEvidenceBundle,
96
+ PromotionReceipt,
97
+ ProtectedAction,
98
+ ProtectedActionRequest,
99
+ QuarantineRecord,
100
+ RationalValue,
101
+ ReceiptObligation,
102
+ RecordAbsenceNotice,
103
+ ReplayEvidence,
104
+ ReportTermBinding,
105
+ RetrievalDecision,
106
+ RollbackReceipt,
107
+ RollbackSnapshot,
108
+ SchemaMigrationRecord,
109
+ SemanticRule,
110
+ ShadowTrialReceipt,
111
+ TelemetryAuditReport,
112
+ TelemetryEventOutcome,
113
+ TelemetryEventType,
114
+ TelemetryIngestResult,
115
+ TelemetryLineDiagnostic,
116
+ TelemetryOutcomeStatus,
117
+ TelemetryReplayReport,
118
+ TelemetryStateReplay,
119
+ VerificationWitness,
120
+ VersionedMemoryRef,
121
+ WorkflowBottleneckReport,
122
+ WorkflowEvidenceSet,
123
+ WorkflowLayer,
124
+ )
125
+ from cmgl.pipeline import PromotionPipeline, PromotionPipelineResult
126
+ from cmgl.policy import AdmissionPolicy
127
+ from cmgl.receipt_verifier import PromotionVerifier
128
+ from cmgl.state import current_memory_view_from_events, current_memory_view_from_ledger
129
+ from cmgl.stress import SharedMemoryStressResult, shared_memory_stress_fixture
130
+ from cmgl.telemetry_ingest import ingest_telemetry_jsonl
131
+ from cmgl.telemetry_replay import replay_telemetry_jsonl
132
+ from cmgl.workflow import (
133
+ make_memory_governance_evidence_contract,
134
+ make_workflow_evidence_set,
135
+ workflow_report_from_evidence,
136
+ )
137
+
138
+ __version__ = "1.1.0"
139
+
140
+ __all__ = [
141
+ "AbsenceNoticeType",
142
+ "ActivePromotionReceipt",
143
+ "AdapterOperationReceipt",
144
+ "AdapterOperationStatus",
145
+ "AdmissionDecision",
146
+ "AdmissionPolicy",
147
+ "AppendOnlyLedger",
148
+ "AuthorityBundle",
149
+ "AuthorityConfig",
150
+ "AuthorityEvidenceBundle",
151
+ "AuthorityReceipt",
152
+ "BackendName",
153
+ "CMGLConfig",
154
+ "ChallengeStatus",
155
+ "CompressionAuditReport",
156
+ "CompressionBridgeProbe",
157
+ "CompressionCertificate",
158
+ "CompressionDeploymentProbe",
159
+ "CompressionFailureClass",
160
+ "CompressionGluingProbe",
161
+ "CompressionProbeSuite",
162
+ "ConformanceFinding",
163
+ "ConformanceLevel",
164
+ "ConformanceProfile",
165
+ "ConformanceReport",
166
+ "ConformanceSeverity",
167
+ "ContaminationAuditReport",
168
+ "ContaminationContext",
169
+ "ContaminationLane",
170
+ "ContaminationStateReplay",
171
+ "CurrentMemoryView",
172
+ "DeclaredScope",
173
+ "DuplicatePolicyReceipt",
174
+ "EvidenceBindingReport",
175
+ "EvidenceManifest",
176
+ "ExternalMemoryRef",
177
+ "GovernanceLayer",
178
+ "GovernanceProfile",
179
+ "GovernanceReceiptBundle",
180
+ "GuardedMemoryBackend",
181
+ "InputSetManifest",
182
+ "LeaseReceipt",
183
+ "LedgerAppendReceipt",
184
+ "LedgerConfig",
185
+ "LedgerIntegrityReceipt",
186
+ "LedgerLineStatus",
187
+ "LedgerRecord",
188
+ "LedgerVerificationResult",
189
+ "MemoryCandidate",
190
+ "MemoryChallengeRecord",
191
+ "MemoryEvent",
192
+ "MemoryEventType",
193
+ "MemoryGovernanceEvidenceContract",
194
+ "MemoryRevision",
195
+ "MemoryStateSnapshot",
196
+ "MemoryStatus",
197
+ "MemoryTelemetryEvent",
198
+ "MetricResult",
199
+ "MetricStatus",
200
+ "ObligationGraph",
201
+ "ObligationStatus",
202
+ "PolicyConfig",
203
+ "PromotionEvidenceBundle",
204
+ "PromotionPipeline",
205
+ "PromotionPipelineResult",
206
+ "PromotionReceipt",
207
+ "PromotionVerifier",
208
+ "ProtectedAction",
209
+ "ProtectedActionRequest",
210
+ "QuarantineRecord",
211
+ "RationalValue",
212
+ "ReceiptObligation",
213
+ "RecordAbsenceNotice",
214
+ "ReplayEvidence",
215
+ "ReportTermBinding",
216
+ "RetrievalDecision",
217
+ "RetrievalFilterResult",
218
+ "RollbackReceipt",
219
+ "RollbackSnapshot",
220
+ "SchemaMigrationRecord",
221
+ "SemanticRule",
222
+ "ShadowTrialReceipt",
223
+ "SharedMemoryStressResult",
224
+ "StrictAuthorityVerifier",
225
+ "TelemetryAuditReport",
226
+ "TelemetryEventOutcome",
227
+ "TelemetryEventType",
228
+ "TelemetryIngestResult",
229
+ "TelemetryLineDiagnostic",
230
+ "TelemetryOutcomeStatus",
231
+ "TelemetryReplayReport",
232
+ "TelemetryStateReplay",
233
+ "VerificationWitness",
234
+ "VersionedMemoryRef",
235
+ "WorkflowBottleneckReport",
236
+ "WorkflowEvidenceSet",
237
+ "WorkflowLayer",
238
+ "__version__",
239
+ "audit_compression_certificate",
240
+ "audit_ledger_conformance",
241
+ "authorize_bundle",
242
+ "authorize_request",
243
+ "build_evidence_manifest",
244
+ "build_input_set_manifest",
245
+ "build_promotion_evidence_bundle",
246
+ "build_replay_evidence",
247
+ "candidate_from_event",
248
+ "compression_metrics",
249
+ "current_memory_view_from_events",
250
+ "current_memory_view_from_ledger",
251
+ "filter_retrieval",
252
+ "ingest_telemetry_jsonl",
253
+ "load_config",
254
+ "make_authority_bundle",
255
+ "make_authority_evidence_bundle",
256
+ "make_authority_receipt",
257
+ "make_compression_certificate",
258
+ "make_declared_scope",
259
+ "make_duplicate_policy_receipt",
260
+ "make_memory_governance_evidence_contract",
261
+ "make_protected_action_request",
262
+ "make_schema_migration_record",
263
+ "make_workflow_evidence_set",
264
+ "replay_telemetry_jsonl",
265
+ "sha256_digest",
266
+ "shared_memory_stress_fixture",
267
+ "versioned_ref_from_event",
268
+ "workflow_report_from_evidence",
269
+ ]
cmgl/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from cmgl.cli import app
2
+
3
+ if __name__ == "__main__":
4
+ app()
cmgl/absence.py ADDED
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from uuid import uuid4
4
+
5
+ from cmgl.digest import sha256_digest
6
+ from cmgl.models import AbsenceNoticeType, RecordAbsenceNotice
7
+ from cmgl.time import now_utc
8
+
9
+
10
+ def make_record_absence_notice(
11
+ *,
12
+ notice_type: AbsenceNoticeType,
13
+ memory_id: str | None = None,
14
+ missing_record: object | None = None,
15
+ disclosure: object | None = None,
16
+ reason_codes: list[str] | None = None,
17
+ ) -> RecordAbsenceNotice:
18
+ default_code = f"absence.{notice_type.value}"
19
+ body = {
20
+ "schema_version": "cmgl.record_absence_notice.v1",
21
+ "notice_id": f"absence:{uuid4()}",
22
+ "notice_type": notice_type,
23
+ "memory_id": memory_id,
24
+ "missing_record_digest": None if missing_record is None else sha256_digest(missing_record),
25
+ "disclosure_digest": None if disclosure is None else sha256_digest(disclosure),
26
+ "reason_codes": list(reason_codes or [default_code]),
27
+ "timestamp": now_utc(),
28
+ }
29
+ return RecordAbsenceNotice(**body, notice_digest=sha256_digest(body))
@@ -0,0 +1,3 @@
1
+ """Optional safe integration shims for external memory frameworks."""
2
+
3
+ __all__ = ["binding", "common", "graphiti", "langgraph", "langmem", "mem0"]
@@ -0,0 +1,212 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping, Sequence
4
+ from datetime import datetime
5
+ from enum import Enum
6
+ from typing import Any, Literal, cast
7
+ from uuid import uuid4
8
+
9
+ from pydantic import BaseModel
10
+
11
+ from cmgl.adapters.common import object_to_mapping
12
+ from cmgl.digest import sha256_digest
13
+ from cmgl.lifecycle import make_quarantine_record
14
+ from cmgl.models import (
15
+ AdapterOperationReceipt,
16
+ AdapterOperationStatus,
17
+ AdmissionDecision,
18
+ BackendName,
19
+ ExternalMemoryRef,
20
+ GovernanceReceiptBundle,
21
+ MemoryEvent,
22
+ )
23
+ from cmgl.pipeline import PromotionPipelineResult
24
+ from cmgl.time import now_utc
25
+
26
+ _EXTERNAL_ID_KEYS = (
27
+ "id",
28
+ "memory_id",
29
+ "uuid",
30
+ "episode_uuid",
31
+ "episode_id",
32
+ "node_uuid",
33
+ "edge_uuid",
34
+ "source_node_uuid",
35
+ "target_node_uuid",
36
+ )
37
+ _EXTERNAL_UPDATE_KEYS = (
38
+ "memory_update_id",
39
+ "update_id",
40
+ "revision_id",
41
+ "version",
42
+ "updated_at",
43
+ "created_at",
44
+ )
45
+
46
+
47
+ def external_ref_from_result(
48
+ result: Any,
49
+ *,
50
+ event: MemoryEvent,
51
+ backend: BackendName | str,
52
+ namespace: str | None = None,
53
+ external_id: str | None = None,
54
+ external_update_id: str | None = None,
55
+ metadata: Mapping[str, Any] | None = None,
56
+ ) -> ExternalMemoryRef:
57
+ """Create a stable CMGL-to-external-memory binding from an SDK result."""
58
+
59
+ mapping = object_to_mapping(result)
60
+ payload_digest = sha256_digest(_jsonable(result))
61
+ resolved_external_id = external_id or _first_present(mapping, _EXTERNAL_ID_KEYS)
62
+ resolved_update_id = external_update_id or _first_present(mapping, _EXTERNAL_UPDATE_KEYS)
63
+ body = {
64
+ "schema_version": "cmgl.external_memory_ref.v1",
65
+ "backend": backend,
66
+ "external_id": str(resolved_external_id or payload_digest),
67
+ "external_update_id": None if resolved_update_id is None else str(resolved_update_id),
68
+ "cmgl_memory_id": event.memory_id,
69
+ "cmgl_memory_update_id": event.memory_update_id,
70
+ "authority_scope": event.authority_scope,
71
+ "content_digest": event.content_digest,
72
+ "source_payload_digest": payload_digest,
73
+ "namespace": namespace,
74
+ "metadata": dict(metadata or {}),
75
+ "timestamp": now_utc(),
76
+ }
77
+ return ExternalMemoryRef(**body)
78
+
79
+
80
+ def make_adapter_operation_receipt(
81
+ *,
82
+ backend: BackendName | str,
83
+ operation: Literal["write", "update", "delete", "retrieve"],
84
+ event: MemoryEvent | None = None,
85
+ status: AdapterOperationStatus,
86
+ decision: AdmissionDecision,
87
+ external_ref: ExternalMemoryRef | None = None,
88
+ reason_codes: list[str] | None = None,
89
+ error: BaseException | None = None,
90
+ timestamp: datetime | None = None,
91
+ ) -> AdapterOperationReceipt:
92
+ """Build a digest-bound adapter operation receipt."""
93
+
94
+ body = {
95
+ "schema_version": "cmgl.adapter_operation_receipt.v1",
96
+ "operation_id": f"adapter-op:{uuid4()}",
97
+ "backend": backend,
98
+ "operation": operation,
99
+ "status": status,
100
+ "decision": decision,
101
+ "cmgl_memory_id": None if event is None else event.memory_id,
102
+ "cmgl_memory_update_id": None if event is None else event.memory_update_id,
103
+ "external_ref": external_ref,
104
+ "reason_codes": list(reason_codes or []),
105
+ "error_type": None if error is None else type(error).__name__,
106
+ "error_message": None if error is None else _safe_error_message(error),
107
+ "timestamp": timestamp or now_utc(),
108
+ }
109
+ return AdapterOperationReceipt(**body, receipt_digest=sha256_digest(body))
110
+
111
+
112
+ def success_reason(operation: Literal["write", "update", "delete", "retrieve"]) -> str:
113
+ return f"adapter.external_{operation}_succeeded"
114
+
115
+
116
+ def append_adapter_receipt(
117
+ layer: Any,
118
+ receipt: AdapterOperationReceipt,
119
+ *,
120
+ quarantine_on_failure: bool = True,
121
+ ) -> str | None:
122
+ """Append adapter operation evidence and optionally quarantine failures."""
123
+
124
+ layer.ledger.append_with_receipt(
125
+ "adapter_operation_receipt",
126
+ receipt,
127
+ persist_receipt=layer.config.ledger.persist_append_receipts,
128
+ )
129
+ if receipt.status != AdapterOperationStatus.FAILED or not quarantine_on_failure:
130
+ return None
131
+ quarantine = make_quarantine_record(
132
+ target=receipt,
133
+ target_type="adapter_operation_receipt",
134
+ reason_codes=["adapter.external_persistence_failed"],
135
+ )
136
+ record, _ = layer.ledger.append_with_receipt(
137
+ "quarantine_record",
138
+ quarantine,
139
+ persist_receipt=layer.config.ledger.persist_append_receipts,
140
+ )
141
+ return cast(str, record.record_digest)
142
+
143
+
144
+ def bundle_with_adapter_receipt(
145
+ layer: Any,
146
+ result: PromotionPipelineResult,
147
+ *,
148
+ adapter_receipt: AdapterOperationReceipt,
149
+ backend_result: Any | None = None,
150
+ quarantine_record_digest: str | None = None,
151
+ ) -> GovernanceReceiptBundle:
152
+ bundle = GovernanceReceiptBundle.model_validate(
153
+ layer.receipt_bundle(
154
+ result,
155
+ backend_result=backend_result,
156
+ adapter_operation_receipt=adapter_receipt,
157
+ )
158
+ )
159
+ if quarantine_record_digest is None:
160
+ return bundle
161
+ body = bundle.model_dump()
162
+ body["quarantine_record_digest"] = quarantine_record_digest
163
+ body.pop("bundle_digest", None)
164
+ return GovernanceReceiptBundle(**body, bundle_digest=sha256_digest(body))
165
+
166
+
167
+ def has_successful_binding(layer: Any, memory_id: str) -> bool:
168
+ """Return true when the ledger has a prior succeeded external binding."""
169
+
170
+ for record in layer.ledger.iter_records():
171
+ if record.record_type != "adapter_operation_receipt":
172
+ continue
173
+ payload = record.payload
174
+ if not isinstance(payload, Mapping):
175
+ continue
176
+ if payload.get("status") != AdapterOperationStatus.SUCCEEDED.value:
177
+ continue
178
+ external_ref = payload.get("external_ref")
179
+ if isinstance(external_ref, Mapping) and external_ref.get("cmgl_memory_id") == memory_id:
180
+ return True
181
+ return False
182
+
183
+
184
+ def _first_present(mapping: Mapping[str, Any], keys: Sequence[str]) -> Any:
185
+ for key in keys:
186
+ value = mapping.get(key)
187
+ if value not in (None, ""):
188
+ return value
189
+ return None
190
+
191
+
192
+ def _jsonable(value: Any) -> Any:
193
+ if isinstance(value, BaseModel):
194
+ return value.model_dump(mode="json")
195
+ if isinstance(value, Mapping):
196
+ return {str(key): _jsonable(item) for key, item in value.items()}
197
+ if isinstance(value, Sequence) and not isinstance(value, str | bytes):
198
+ return [_jsonable(item) for item in value]
199
+ if isinstance(value, datetime):
200
+ return value.isoformat()
201
+ if isinstance(value, Enum):
202
+ return value.value
203
+ if isinstance(value, str | int | float | bool) or value is None:
204
+ return value
205
+ return str(value)
206
+
207
+
208
+ def _safe_error_message(error: BaseException) -> str:
209
+ message = str(error).replace("\n", " ").strip()
210
+ if not message:
211
+ return type(error).__name__
212
+ return message[:240]