agent_os_kernel 3.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.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- nexus/tests/test_schemas.py +295 -0
cmvk/audit.py
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
CMVK Audit Trail Module
|
|
5
|
+
|
|
6
|
+
Provides immutable audit logging for verification operations.
|
|
7
|
+
All verifications can be logged with timestamps, inputs, and results
|
|
8
|
+
for compliance and forensic analysis.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import hashlib
|
|
14
|
+
import json
|
|
15
|
+
import threading
|
|
16
|
+
import uuid
|
|
17
|
+
from dataclasses import asdict, dataclass, field
|
|
18
|
+
from datetime import UTC, datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class AuditEntry:
|
|
27
|
+
"""
|
|
28
|
+
Immutable audit record for a single verification.
|
|
29
|
+
|
|
30
|
+
All fields are frozen to ensure auditability.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
id: Unique identifier for this verification
|
|
34
|
+
timestamp: UTC timestamp of verification
|
|
35
|
+
operation: Type of verification operation
|
|
36
|
+
inputs_hash: SHA-256 hash of input data (for privacy)
|
|
37
|
+
result_summary: Summary of verification result
|
|
38
|
+
drift_score: The drift score from verification
|
|
39
|
+
confidence: The confidence score
|
|
40
|
+
metric_used: Distance metric used
|
|
41
|
+
profile_used: Threshold profile applied (if any)
|
|
42
|
+
passed: Whether verification passed thresholds
|
|
43
|
+
metadata: Additional context (user, session, etc.)
|
|
44
|
+
checksum: Integrity checksum for this entry
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
id: str
|
|
48
|
+
timestamp: str
|
|
49
|
+
operation: str
|
|
50
|
+
inputs_hash: str
|
|
51
|
+
result_summary: dict
|
|
52
|
+
drift_score: float
|
|
53
|
+
confidence: float
|
|
54
|
+
metric_used: str
|
|
55
|
+
profile_used: str | None
|
|
56
|
+
passed: bool
|
|
57
|
+
metadata: dict
|
|
58
|
+
checksum: str
|
|
59
|
+
|
|
60
|
+
def to_dict(self) -> dict:
|
|
61
|
+
"""Convert to dictionary for serialization."""
|
|
62
|
+
return asdict(self)
|
|
63
|
+
|
|
64
|
+
def verify_integrity(self) -> bool:
|
|
65
|
+
"""Verify the entry's checksum is valid."""
|
|
66
|
+
computed = _compute_checksum(
|
|
67
|
+
self.id,
|
|
68
|
+
self.timestamp,
|
|
69
|
+
self.operation,
|
|
70
|
+
self.inputs_hash,
|
|
71
|
+
self.drift_score,
|
|
72
|
+
self.confidence,
|
|
73
|
+
)
|
|
74
|
+
return computed == self.checksum
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class AuditTrail:
|
|
79
|
+
"""
|
|
80
|
+
Audit trail manager for verification operations.
|
|
81
|
+
|
|
82
|
+
Maintains an immutable log of all verifications with optional
|
|
83
|
+
persistence to file.
|
|
84
|
+
|
|
85
|
+
Thread-safe for concurrent verifications.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
entries: list[AuditEntry] = field(default_factory=list)
|
|
89
|
+
persist_path: Path | None = None
|
|
90
|
+
auto_persist: bool = False
|
|
91
|
+
_lock: threading.Lock = field(default_factory=threading.Lock, repr=False)
|
|
92
|
+
|
|
93
|
+
def __post_init__(self) -> None:
|
|
94
|
+
"""Load existing entries if persist path exists."""
|
|
95
|
+
if self.persist_path and self.persist_path.exists():
|
|
96
|
+
self._load_from_file()
|
|
97
|
+
|
|
98
|
+
def log(
|
|
99
|
+
self,
|
|
100
|
+
operation: str,
|
|
101
|
+
inputs: dict[str, Any],
|
|
102
|
+
drift_score: float,
|
|
103
|
+
confidence: float,
|
|
104
|
+
metric_used: str = "cosine",
|
|
105
|
+
profile_used: str | None = None,
|
|
106
|
+
passed: bool = True,
|
|
107
|
+
result_details: dict | None = None,
|
|
108
|
+
metadata: dict | None = None,
|
|
109
|
+
) -> AuditEntry:
|
|
110
|
+
"""
|
|
111
|
+
Log a verification operation.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
operation: Type of operation (e.g., "verify_embeddings")
|
|
115
|
+
inputs: Input data (will be hashed, not stored directly)
|
|
116
|
+
drift_score: Drift score from verification
|
|
117
|
+
confidence: Confidence score
|
|
118
|
+
metric_used: Distance metric used
|
|
119
|
+
profile_used: Threshold profile applied
|
|
120
|
+
passed: Whether verification passed
|
|
121
|
+
result_details: Additional result information
|
|
122
|
+
metadata: Additional context (user, session, etc.)
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
The created AuditEntry
|
|
126
|
+
"""
|
|
127
|
+
entry_id = str(uuid.uuid4())
|
|
128
|
+
timestamp = datetime.now(UTC).isoformat()
|
|
129
|
+
inputs_hash = _hash_inputs(inputs)
|
|
130
|
+
|
|
131
|
+
checksum = _compute_checksum(
|
|
132
|
+
entry_id,
|
|
133
|
+
timestamp,
|
|
134
|
+
operation,
|
|
135
|
+
inputs_hash,
|
|
136
|
+
drift_score,
|
|
137
|
+
confidence,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
entry = AuditEntry(
|
|
141
|
+
id=entry_id,
|
|
142
|
+
timestamp=timestamp,
|
|
143
|
+
operation=operation,
|
|
144
|
+
inputs_hash=inputs_hash,
|
|
145
|
+
result_summary=result_details or {},
|
|
146
|
+
drift_score=drift_score,
|
|
147
|
+
confidence=confidence,
|
|
148
|
+
metric_used=metric_used,
|
|
149
|
+
profile_used=profile_used,
|
|
150
|
+
passed=passed,
|
|
151
|
+
metadata=metadata or {},
|
|
152
|
+
checksum=checksum,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
with self._lock:
|
|
156
|
+
self.entries.append(entry)
|
|
157
|
+
if self.auto_persist and self.persist_path:
|
|
158
|
+
self._persist_entry(entry)
|
|
159
|
+
|
|
160
|
+
return entry
|
|
161
|
+
|
|
162
|
+
def get_entries(
|
|
163
|
+
self,
|
|
164
|
+
start_time: datetime | None = None,
|
|
165
|
+
end_time: datetime | None = None,
|
|
166
|
+
operation: str | None = None,
|
|
167
|
+
passed_only: bool | None = None,
|
|
168
|
+
) -> list[AuditEntry]:
|
|
169
|
+
"""
|
|
170
|
+
Query audit entries with optional filters.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
start_time: Filter entries after this time
|
|
174
|
+
end_time: Filter entries before this time
|
|
175
|
+
operation: Filter by operation type
|
|
176
|
+
passed_only: If True, only passed; if False, only failed; if None, all
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
List of matching AuditEntry records
|
|
180
|
+
"""
|
|
181
|
+
with self._lock:
|
|
182
|
+
results = list(self.entries)
|
|
183
|
+
|
|
184
|
+
if start_time:
|
|
185
|
+
start_iso = start_time.isoformat()
|
|
186
|
+
results = [e for e in results if e.timestamp >= start_iso]
|
|
187
|
+
|
|
188
|
+
if end_time:
|
|
189
|
+
end_iso = end_time.isoformat()
|
|
190
|
+
results = [e for e in results if e.timestamp <= end_iso]
|
|
191
|
+
|
|
192
|
+
if operation:
|
|
193
|
+
results = [e for e in results if e.operation == operation]
|
|
194
|
+
|
|
195
|
+
if passed_only is not None:
|
|
196
|
+
results = [e for e in results if e.passed == passed_only]
|
|
197
|
+
|
|
198
|
+
return results
|
|
199
|
+
|
|
200
|
+
def get_statistics(self) -> dict:
|
|
201
|
+
"""
|
|
202
|
+
Calculate summary statistics for the audit trail.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Dictionary with counts, pass rates, and drift statistics
|
|
206
|
+
"""
|
|
207
|
+
with self._lock:
|
|
208
|
+
entries = list(self.entries)
|
|
209
|
+
|
|
210
|
+
if not entries:
|
|
211
|
+
return {"total_entries": 0}
|
|
212
|
+
|
|
213
|
+
drift_scores = [e.drift_score for e in entries]
|
|
214
|
+
passed_count = sum(1 for e in entries if e.passed)
|
|
215
|
+
|
|
216
|
+
# Group by operation
|
|
217
|
+
by_operation: dict[str, list[AuditEntry]] = {}
|
|
218
|
+
for e in entries:
|
|
219
|
+
by_operation.setdefault(e.operation, []).append(e)
|
|
220
|
+
|
|
221
|
+
operation_stats = {
|
|
222
|
+
op: {
|
|
223
|
+
"count": len(ops),
|
|
224
|
+
"pass_rate": sum(1 for e in ops if e.passed) / len(ops),
|
|
225
|
+
"mean_drift": float(np.mean([e.drift_score for e in ops])),
|
|
226
|
+
}
|
|
227
|
+
for op, ops in by_operation.items()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
"total_entries": len(entries),
|
|
232
|
+
"passed_count": passed_count,
|
|
233
|
+
"failed_count": len(entries) - passed_count,
|
|
234
|
+
"pass_rate": passed_count / len(entries),
|
|
235
|
+
"mean_drift": float(np.mean(drift_scores)),
|
|
236
|
+
"std_drift": float(np.std(drift_scores)),
|
|
237
|
+
"min_drift": float(np.min(drift_scores)),
|
|
238
|
+
"max_drift": float(np.max(drift_scores)),
|
|
239
|
+
"by_operation": operation_stats,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
def verify_integrity(self) -> tuple[bool, list[str]]:
|
|
243
|
+
"""
|
|
244
|
+
Verify integrity of all audit entries.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Tuple of (all_valid, list of invalid entry IDs)
|
|
248
|
+
"""
|
|
249
|
+
invalid_ids = []
|
|
250
|
+
with self._lock:
|
|
251
|
+
for entry in self.entries:
|
|
252
|
+
if not entry.verify_integrity():
|
|
253
|
+
invalid_ids.append(entry.id)
|
|
254
|
+
|
|
255
|
+
return len(invalid_ids) == 0, invalid_ids
|
|
256
|
+
|
|
257
|
+
def export_json(self, path: Path | str) -> None:
|
|
258
|
+
"""Export audit trail to JSON file."""
|
|
259
|
+
path = Path(path)
|
|
260
|
+
with self._lock:
|
|
261
|
+
data = {
|
|
262
|
+
"exported_at": datetime.now(UTC).isoformat(),
|
|
263
|
+
"entry_count": len(self.entries),
|
|
264
|
+
"entries": [e.to_dict() for e in self.entries],
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
path.write_text(json.dumps(data, indent=2))
|
|
268
|
+
|
|
269
|
+
def export_csv(self, path: Path | str) -> None:
|
|
270
|
+
"""Export audit trail to CSV file."""
|
|
271
|
+
import csv
|
|
272
|
+
|
|
273
|
+
path = Path(path)
|
|
274
|
+
with self._lock:
|
|
275
|
+
entries = list(self.entries)
|
|
276
|
+
|
|
277
|
+
if not entries:
|
|
278
|
+
path.write_text("")
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
fieldnames = [
|
|
282
|
+
"id",
|
|
283
|
+
"timestamp",
|
|
284
|
+
"operation",
|
|
285
|
+
"drift_score",
|
|
286
|
+
"confidence",
|
|
287
|
+
"metric_used",
|
|
288
|
+
"profile_used",
|
|
289
|
+
"passed",
|
|
290
|
+
"inputs_hash",
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
with path.open("w", newline="") as f:
|
|
294
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
295
|
+
writer.writeheader()
|
|
296
|
+
for entry in entries:
|
|
297
|
+
writer.writerow({k: getattr(entry, k) for k in fieldnames})
|
|
298
|
+
|
|
299
|
+
def _load_from_file(self) -> None:
|
|
300
|
+
"""Load entries from persist file."""
|
|
301
|
+
if not self.persist_path or not self.persist_path.exists():
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
data = json.loads(self.persist_path.read_text())
|
|
306
|
+
for entry_dict in data.get("entries", []):
|
|
307
|
+
self.entries.append(AuditEntry(**entry_dict))
|
|
308
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
309
|
+
# Log warning but don't fail
|
|
310
|
+
print(f"Warning: Could not load audit trail from {self.persist_path}: {e}")
|
|
311
|
+
|
|
312
|
+
def _persist_entry(self, entry: AuditEntry) -> None:
|
|
313
|
+
"""Append entry to persist file."""
|
|
314
|
+
if not self.persist_path:
|
|
315
|
+
return
|
|
316
|
+
|
|
317
|
+
# Read existing or create new
|
|
318
|
+
if self.persist_path.exists():
|
|
319
|
+
try:
|
|
320
|
+
data = json.loads(self.persist_path.read_text())
|
|
321
|
+
except json.JSONDecodeError:
|
|
322
|
+
data = {"entries": []}
|
|
323
|
+
else:
|
|
324
|
+
data = {"entries": []}
|
|
325
|
+
|
|
326
|
+
data["entries"].append(entry.to_dict())
|
|
327
|
+
data["updated_at"] = datetime.now(UTC).isoformat()
|
|
328
|
+
data["entry_count"] = len(data["entries"])
|
|
329
|
+
|
|
330
|
+
self.persist_path.write_text(json.dumps(data, indent=2))
|
|
331
|
+
|
|
332
|
+
def clear(self) -> None:
|
|
333
|
+
"""Clear all entries (use with caution)."""
|
|
334
|
+
with self._lock:
|
|
335
|
+
self.entries.clear()
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
# ============================================================================
|
|
339
|
+
# Helper Functions
|
|
340
|
+
# ============================================================================
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _hash_inputs(inputs: dict[str, Any]) -> str:
|
|
344
|
+
"""Create SHA-256 hash of inputs for privacy-preserving audit."""
|
|
345
|
+
|
|
346
|
+
def serialize(obj: Any) -> Any:
|
|
347
|
+
"""Convert objects to JSON-serializable format."""
|
|
348
|
+
if isinstance(obj, np.ndarray) or hasattr(obj, "tolist"):
|
|
349
|
+
return obj.tolist()
|
|
350
|
+
elif hasattr(obj, "__dict__"):
|
|
351
|
+
return str(type(obj).__name__)
|
|
352
|
+
return obj
|
|
353
|
+
|
|
354
|
+
# Create deterministic JSON representation
|
|
355
|
+
serialized = json.dumps(
|
|
356
|
+
{k: serialize(v) for k, v in sorted(inputs.items())},
|
|
357
|
+
sort_keys=True,
|
|
358
|
+
)
|
|
359
|
+
return hashlib.sha256(serialized.encode()).hexdigest()
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _compute_checksum(*values: Any) -> str:
|
|
363
|
+
"""Compute checksum for audit entry integrity."""
|
|
364
|
+
content = "|".join(str(v) for v in values)
|
|
365
|
+
return hashlib.sha256(content.encode()).hexdigest()[:16]
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# ============================================================================
|
|
369
|
+
# Global Audit Trail Instance
|
|
370
|
+
# ============================================================================
|
|
371
|
+
|
|
372
|
+
_global_audit_trail: AuditTrail | None = None
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def get_audit_trail() -> AuditTrail:
|
|
376
|
+
"""Get or create the global audit trail instance."""
|
|
377
|
+
global _global_audit_trail
|
|
378
|
+
if _global_audit_trail is None:
|
|
379
|
+
_global_audit_trail = AuditTrail()
|
|
380
|
+
return _global_audit_trail
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def configure_audit_trail(
|
|
384
|
+
persist_path: Path | str | None = None,
|
|
385
|
+
auto_persist: bool = False,
|
|
386
|
+
) -> AuditTrail:
|
|
387
|
+
"""
|
|
388
|
+
Configure the global audit trail.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
persist_path: Path to persist audit entries
|
|
392
|
+
auto_persist: Whether to automatically persist each entry
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
The configured AuditTrail instance
|
|
396
|
+
"""
|
|
397
|
+
global _global_audit_trail
|
|
398
|
+
_global_audit_trail = AuditTrail(
|
|
399
|
+
persist_path=Path(persist_path) if persist_path else None,
|
|
400
|
+
auto_persist=auto_persist,
|
|
401
|
+
)
|
|
402
|
+
return _global_audit_trail
|