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
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
State Observer for Listener Agent
|
|
5
|
+
|
|
6
|
+
Provides passive observation capabilities for monitoring graph states
|
|
7
|
+
without interfering with normal operations.
|
|
8
|
+
|
|
9
|
+
This observer is designed to:
|
|
10
|
+
1. Collect metrics from the knowledge graph
|
|
11
|
+
2. Track state changes over time
|
|
12
|
+
3. Calculate derived metrics for threshold evaluation
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import Dict, Any, Optional, List, Callable
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from datetime import datetime, timedelta
|
|
18
|
+
from collections import deque
|
|
19
|
+
from enum import Enum, auto
|
|
20
|
+
|
|
21
|
+
from ..knowledge_graph.multidimensional_graph import MultidimensionalKnowledgeGraph
|
|
22
|
+
from ..knowledge_graph.graph_elements import EdgeType
|
|
23
|
+
from ..core.handshake_protocol import HandshakeProtocol, HandshakeState
|
|
24
|
+
from ..super_system.router import SuperSystemRouter
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MetricType(Enum):
|
|
28
|
+
"""Types of metrics the observer can collect."""
|
|
29
|
+
|
|
30
|
+
# Graph metrics
|
|
31
|
+
NODE_COUNT = auto()
|
|
32
|
+
EDGE_COUNT = auto()
|
|
33
|
+
DIMENSION_COUNT = auto()
|
|
34
|
+
ACTION_SPACE_SIZE = auto()
|
|
35
|
+
CONFLICT_EDGE_COUNT = auto()
|
|
36
|
+
|
|
37
|
+
# Session metrics
|
|
38
|
+
ACTIVE_SESSIONS = auto()
|
|
39
|
+
COMPLETED_SESSIONS = auto()
|
|
40
|
+
FAILED_SESSIONS = auto()
|
|
41
|
+
REJECTED_SESSIONS = auto()
|
|
42
|
+
|
|
43
|
+
# Performance metrics
|
|
44
|
+
LAST_TRAVERSAL_TIME_MS = auto()
|
|
45
|
+
AVG_TRAVERSAL_TIME_MS = auto()
|
|
46
|
+
|
|
47
|
+
# Derived metrics
|
|
48
|
+
REJECTION_RATE = auto()
|
|
49
|
+
CONFLICT_RATIO = auto()
|
|
50
|
+
SESSION_SUCCESS_RATE = auto()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class MetricSample:
|
|
55
|
+
"""A single metric sample with timestamp."""
|
|
56
|
+
|
|
57
|
+
metric_type: MetricType
|
|
58
|
+
value: float
|
|
59
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
60
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ObservationResult:
|
|
65
|
+
"""Result of a state observation cycle."""
|
|
66
|
+
|
|
67
|
+
timestamp: datetime
|
|
68
|
+
metrics: Dict[MetricType, float]
|
|
69
|
+
derived_metrics: Dict[str, float]
|
|
70
|
+
anomalies_detected: List[str]
|
|
71
|
+
graph_snapshot: Optional[Dict[str, Any]] = None
|
|
72
|
+
|
|
73
|
+
def to_threshold_metrics(self) -> Dict:
|
|
74
|
+
"""
|
|
75
|
+
Convert observation result to threshold-compatible metrics.
|
|
76
|
+
|
|
77
|
+
Maps MetricType to ThresholdType for threshold evaluation.
|
|
78
|
+
"""
|
|
79
|
+
from .threshold_config import ThresholdType
|
|
80
|
+
|
|
81
|
+
mapping = {
|
|
82
|
+
"constraint_violation_count": ThresholdType.CONSTRAINT_VIOLATION_COUNT,
|
|
83
|
+
"dimension_conflict_ratio": ThresholdType.DIMENSION_CONFLICT_RATIO,
|
|
84
|
+
"action_rejection_rate": ThresholdType.ACTION_REJECTION_RATE,
|
|
85
|
+
"context_drift": ThresholdType.CONTEXT_DRIFT_MAXIMUM,
|
|
86
|
+
"ambiguity_score": ThresholdType.AMBIGUITY_SCORE_MAXIMUM,
|
|
87
|
+
"traversal_latency_ms": ThresholdType.GRAPH_TRAVERSAL_LATENCY_MS,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
result = {}
|
|
91
|
+
for key, threshold_type in mapping.items():
|
|
92
|
+
if key in self.derived_metrics:
|
|
93
|
+
result[threshold_type] = self.derived_metrics[key]
|
|
94
|
+
|
|
95
|
+
return result
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class StateObserver:
|
|
99
|
+
"""
|
|
100
|
+
Passive state observer for the Listener Agent.
|
|
101
|
+
|
|
102
|
+
Collects metrics from the knowledge graph, handshake protocol,
|
|
103
|
+
and router without interfering with their operation.
|
|
104
|
+
|
|
105
|
+
This class implements the Observer pattern - it watches but does not act.
|
|
106
|
+
The Listener Agent is responsible for deciding when to intervene.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
# Maximum samples to retain in memory per metric
|
|
110
|
+
MAX_SAMPLES_PER_METRIC = 1000
|
|
111
|
+
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
knowledge_graph: MultidimensionalKnowledgeGraph,
|
|
115
|
+
protocol: HandshakeProtocol,
|
|
116
|
+
router: SuperSystemRouter,
|
|
117
|
+
sample_window_seconds: float = 60.0,
|
|
118
|
+
):
|
|
119
|
+
"""
|
|
120
|
+
Initialize the state observer.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
knowledge_graph: The graph to observe
|
|
124
|
+
protocol: The handshake protocol to observe
|
|
125
|
+
router: The super system router to observe
|
|
126
|
+
sample_window_seconds: Window for calculating rolling metrics
|
|
127
|
+
"""
|
|
128
|
+
self.knowledge_graph = knowledge_graph
|
|
129
|
+
self.protocol = protocol
|
|
130
|
+
self.router = router
|
|
131
|
+
self.sample_window = timedelta(seconds=sample_window_seconds)
|
|
132
|
+
|
|
133
|
+
# Sample storage - bounded deques for memory safety
|
|
134
|
+
self._samples: Dict[MetricType, deque] = {
|
|
135
|
+
metric_type: deque(maxlen=self.MAX_SAMPLES_PER_METRIC)
|
|
136
|
+
for metric_type in MetricType
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# Observation history
|
|
140
|
+
self._observations: deque = deque(maxlen=1000)
|
|
141
|
+
|
|
142
|
+
# Custom metric collectors
|
|
143
|
+
self._custom_collectors: Dict[str, Callable[[], float]] = {}
|
|
144
|
+
|
|
145
|
+
# Baseline metrics for anomaly detection
|
|
146
|
+
self._baselines: Dict[MetricType, float] = {}
|
|
147
|
+
|
|
148
|
+
def observe(self, context: Optional[Dict[str, Any]] = None) -> ObservationResult:
|
|
149
|
+
"""
|
|
150
|
+
Perform a single observation cycle.
|
|
151
|
+
|
|
152
|
+
Collects all metrics from the observed components and calculates
|
|
153
|
+
derived metrics for threshold evaluation.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
context: Optional context to include in observation
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
ObservationResult with all collected metrics
|
|
160
|
+
"""
|
|
161
|
+
timestamp = datetime.now()
|
|
162
|
+
metrics = {}
|
|
163
|
+
anomalies = []
|
|
164
|
+
|
|
165
|
+
# Collect graph metrics
|
|
166
|
+
metrics.update(self._collect_graph_metrics())
|
|
167
|
+
|
|
168
|
+
# Collect session metrics
|
|
169
|
+
metrics.update(self._collect_session_metrics())
|
|
170
|
+
|
|
171
|
+
# Collect performance metrics
|
|
172
|
+
metrics.update(self._collect_performance_metrics())
|
|
173
|
+
|
|
174
|
+
# Store samples
|
|
175
|
+
for metric_type, value in metrics.items():
|
|
176
|
+
sample = MetricSample(metric_type=metric_type, value=value, timestamp=timestamp)
|
|
177
|
+
self._samples[metric_type].append(sample)
|
|
178
|
+
|
|
179
|
+
# Calculate derived metrics
|
|
180
|
+
derived_metrics = self._calculate_derived_metrics(metrics, context)
|
|
181
|
+
|
|
182
|
+
# Detect anomalies
|
|
183
|
+
anomalies = self._detect_anomalies(metrics)
|
|
184
|
+
|
|
185
|
+
# Create observation result
|
|
186
|
+
result = ObservationResult(
|
|
187
|
+
timestamp=timestamp,
|
|
188
|
+
metrics={k: v for k, v in metrics.items()},
|
|
189
|
+
derived_metrics=derived_metrics,
|
|
190
|
+
anomalies_detected=anomalies,
|
|
191
|
+
graph_snapshot=self._create_graph_snapshot() if context else None,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Store observation
|
|
195
|
+
self._observations.append(result)
|
|
196
|
+
|
|
197
|
+
return result
|
|
198
|
+
|
|
199
|
+
def _collect_graph_metrics(self) -> Dict[MetricType, float]:
|
|
200
|
+
"""Collect metrics from the knowledge graph."""
|
|
201
|
+
metrics = {}
|
|
202
|
+
|
|
203
|
+
# Count dimensions
|
|
204
|
+
metrics[MetricType.DIMENSION_COUNT] = float(len(self.knowledge_graph.dimensions))
|
|
205
|
+
|
|
206
|
+
# Count total nodes and edges across all subgraphs
|
|
207
|
+
total_nodes = 0
|
|
208
|
+
total_edges = 0
|
|
209
|
+
conflict_edges = 0
|
|
210
|
+
|
|
211
|
+
for dim_name, subgraph in self.knowledge_graph.subgraphs.items():
|
|
212
|
+
total_nodes += len(subgraph.nodes)
|
|
213
|
+
for edges in subgraph._adjacency_list.values():
|
|
214
|
+
total_edges += len(edges)
|
|
215
|
+
conflict_edges += sum(
|
|
216
|
+
1 for e in edges if e.edge_type == EdgeType.CONFLICTS_WITH
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
metrics[MetricType.NODE_COUNT] = float(total_nodes)
|
|
220
|
+
metrics[MetricType.EDGE_COUNT] = float(total_edges)
|
|
221
|
+
metrics[MetricType.CONFLICT_EDGE_COUNT] = float(conflict_edges)
|
|
222
|
+
|
|
223
|
+
return metrics
|
|
224
|
+
|
|
225
|
+
def _collect_session_metrics(self) -> Dict[MetricType, float]:
|
|
226
|
+
"""Collect metrics from the handshake protocol."""
|
|
227
|
+
metrics = {}
|
|
228
|
+
|
|
229
|
+
sessions = self.protocol.sessions
|
|
230
|
+
|
|
231
|
+
active = sum(
|
|
232
|
+
1 for s in sessions.values()
|
|
233
|
+
if s.state in [HandshakeState.INITIATED, HandshakeState.NEGOTIATING,
|
|
234
|
+
HandshakeState.VALIDATED, HandshakeState.ACCEPTED,
|
|
235
|
+
HandshakeState.EXECUTING]
|
|
236
|
+
)
|
|
237
|
+
completed = sum(
|
|
238
|
+
1 for s in sessions.values()
|
|
239
|
+
if s.state == HandshakeState.COMPLETED
|
|
240
|
+
)
|
|
241
|
+
failed = sum(
|
|
242
|
+
1 for s in sessions.values()
|
|
243
|
+
if s.state == HandshakeState.FAILED
|
|
244
|
+
)
|
|
245
|
+
rejected = sum(
|
|
246
|
+
1 for s in sessions.values()
|
|
247
|
+
if s.state == HandshakeState.REJECTED
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
metrics[MetricType.ACTIVE_SESSIONS] = float(active)
|
|
251
|
+
metrics[MetricType.COMPLETED_SESSIONS] = float(completed)
|
|
252
|
+
metrics[MetricType.FAILED_SESSIONS] = float(failed)
|
|
253
|
+
metrics[MetricType.REJECTED_SESSIONS] = float(rejected)
|
|
254
|
+
|
|
255
|
+
return metrics
|
|
256
|
+
|
|
257
|
+
def _collect_performance_metrics(self) -> Dict[MetricType, float]:
|
|
258
|
+
"""Collect performance-related metrics."""
|
|
259
|
+
metrics = {}
|
|
260
|
+
|
|
261
|
+
# Get routing statistics
|
|
262
|
+
routing_stats = self.router.get_routing_statistics()
|
|
263
|
+
|
|
264
|
+
# Use routing history for latency estimation
|
|
265
|
+
# In production, this would integrate with actual timing measurements
|
|
266
|
+
metrics[MetricType.LAST_TRAVERSAL_TIME_MS] = 0.0
|
|
267
|
+
metrics[MetricType.AVG_TRAVERSAL_TIME_MS] = 0.0
|
|
268
|
+
|
|
269
|
+
# Action space size from last routing
|
|
270
|
+
if self.router.routing_history:
|
|
271
|
+
last_routing = self.router.routing_history[-1]
|
|
272
|
+
metrics[MetricType.ACTION_SPACE_SIZE] = float(
|
|
273
|
+
len(last_routing.pruned_action_space)
|
|
274
|
+
)
|
|
275
|
+
else:
|
|
276
|
+
metrics[MetricType.ACTION_SPACE_SIZE] = 0.0
|
|
277
|
+
|
|
278
|
+
return metrics
|
|
279
|
+
|
|
280
|
+
def _calculate_derived_metrics(
|
|
281
|
+
self,
|
|
282
|
+
metrics: Dict[MetricType, float],
|
|
283
|
+
context: Optional[Dict[str, Any]] = None
|
|
284
|
+
) -> Dict[str, float]:
|
|
285
|
+
"""Calculate derived metrics from raw metrics."""
|
|
286
|
+
derived = {}
|
|
287
|
+
|
|
288
|
+
# Rejection rate
|
|
289
|
+
total_sessions = (
|
|
290
|
+
metrics.get(MetricType.COMPLETED_SESSIONS, 0) +
|
|
291
|
+
metrics.get(MetricType.FAILED_SESSIONS, 0) +
|
|
292
|
+
metrics.get(MetricType.REJECTED_SESSIONS, 0)
|
|
293
|
+
)
|
|
294
|
+
if total_sessions > 0:
|
|
295
|
+
derived["action_rejection_rate"] = (
|
|
296
|
+
metrics.get(MetricType.REJECTED_SESSIONS, 0) / total_sessions
|
|
297
|
+
)
|
|
298
|
+
else:
|
|
299
|
+
derived["action_rejection_rate"] = 0.0
|
|
300
|
+
|
|
301
|
+
# Conflict ratio
|
|
302
|
+
total_edges = metrics.get(MetricType.EDGE_COUNT, 0)
|
|
303
|
+
if total_edges > 0:
|
|
304
|
+
derived["dimension_conflict_ratio"] = (
|
|
305
|
+
metrics.get(MetricType.CONFLICT_EDGE_COUNT, 0) / total_edges
|
|
306
|
+
)
|
|
307
|
+
else:
|
|
308
|
+
derived["dimension_conflict_ratio"] = 0.0
|
|
309
|
+
|
|
310
|
+
# Session success rate
|
|
311
|
+
if total_sessions > 0:
|
|
312
|
+
derived["session_success_rate"] = (
|
|
313
|
+
metrics.get(MetricType.COMPLETED_SESSIONS, 0) / total_sessions
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
derived["session_success_rate"] = 1.0
|
|
317
|
+
|
|
318
|
+
# Constraint violation count (from recent history)
|
|
319
|
+
derived["constraint_violation_count"] = self._count_recent_violations()
|
|
320
|
+
|
|
321
|
+
# Placeholder for context-aware metrics
|
|
322
|
+
# These would integrate with caas (context-as-a-service) in production
|
|
323
|
+
derived["context_drift"] = 0.0
|
|
324
|
+
derived["ambiguity_score"] = 0.0
|
|
325
|
+
derived["traversal_latency_ms"] = metrics.get(MetricType.AVG_TRAVERSAL_TIME_MS, 0.0)
|
|
326
|
+
|
|
327
|
+
return derived
|
|
328
|
+
|
|
329
|
+
def _count_recent_violations(self) -> float:
|
|
330
|
+
"""Count constraint violations in the recent observation window."""
|
|
331
|
+
cutoff = datetime.now() - self.sample_window
|
|
332
|
+
|
|
333
|
+
# Count rejected sessions in window
|
|
334
|
+
violations = 0
|
|
335
|
+
for session in self.protocol.sessions.values():
|
|
336
|
+
if session.state == HandshakeState.REJECTED:
|
|
337
|
+
if session.updated_at >= cutoff:
|
|
338
|
+
violations += 1
|
|
339
|
+
|
|
340
|
+
return float(violations)
|
|
341
|
+
|
|
342
|
+
def _detect_anomalies(self, metrics: Dict[MetricType, float]) -> List[str]:
|
|
343
|
+
"""Detect anomalies based on baseline comparison."""
|
|
344
|
+
anomalies = []
|
|
345
|
+
|
|
346
|
+
for metric_type, value in metrics.items():
|
|
347
|
+
if metric_type in self._baselines:
|
|
348
|
+
baseline = self._baselines[metric_type]
|
|
349
|
+
# Simple anomaly detection: >2x deviation from baseline
|
|
350
|
+
if baseline > 0 and abs(value - baseline) / baseline > 2.0:
|
|
351
|
+
anomalies.append(
|
|
352
|
+
f"Anomaly in {metric_type.name}: "
|
|
353
|
+
f"current={value:.2f}, baseline={baseline:.2f}"
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return anomalies
|
|
357
|
+
|
|
358
|
+
def _create_graph_snapshot(self) -> Dict[str, Any]:
|
|
359
|
+
"""Create a lightweight snapshot of graph state."""
|
|
360
|
+
snapshot = {
|
|
361
|
+
"dimensions": list(self.knowledge_graph.dimensions.keys()),
|
|
362
|
+
"node_counts": {},
|
|
363
|
+
"edge_counts": {},
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
for dim_name, subgraph in self.knowledge_graph.subgraphs.items():
|
|
367
|
+
snapshot["node_counts"][dim_name] = len(subgraph._nodes)
|
|
368
|
+
snapshot["edge_counts"][dim_name] = sum(
|
|
369
|
+
len(edges) for edges in subgraph._adjacency_list.values()
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
return snapshot
|
|
373
|
+
|
|
374
|
+
def set_baseline(self, metric_type: MetricType, value: float) -> None:
|
|
375
|
+
"""Set a baseline value for anomaly detection."""
|
|
376
|
+
self._baselines[metric_type] = value
|
|
377
|
+
|
|
378
|
+
def calibrate_baselines(self, num_samples: int = 10) -> None:
|
|
379
|
+
"""
|
|
380
|
+
Calibrate baselines by averaging recent samples.
|
|
381
|
+
|
|
382
|
+
Call this during a known-good operational period to establish
|
|
383
|
+
baseline metrics for anomaly detection.
|
|
384
|
+
"""
|
|
385
|
+
for metric_type, samples in self._samples.items():
|
|
386
|
+
if len(samples) >= num_samples:
|
|
387
|
+
recent = list(samples)[-num_samples:]
|
|
388
|
+
avg_value = sum(s.value for s in recent) / len(recent)
|
|
389
|
+
self._baselines[metric_type] = avg_value
|
|
390
|
+
|
|
391
|
+
def register_custom_collector(
|
|
392
|
+
self,
|
|
393
|
+
name: str,
|
|
394
|
+
collector: Callable[[], float]
|
|
395
|
+
) -> None:
|
|
396
|
+
"""
|
|
397
|
+
Register a custom metric collector.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
name: Unique name for the custom metric
|
|
401
|
+
collector: Function that returns the metric value
|
|
402
|
+
"""
|
|
403
|
+
self._custom_collectors[name] = collector
|
|
404
|
+
|
|
405
|
+
def get_metric_history(
|
|
406
|
+
self,
|
|
407
|
+
metric_type: MetricType,
|
|
408
|
+
window_seconds: Optional[float] = None
|
|
409
|
+
) -> List[MetricSample]:
|
|
410
|
+
"""
|
|
411
|
+
Get historical samples for a metric.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
metric_type: The metric to retrieve
|
|
415
|
+
window_seconds: Optional time window (None = all samples)
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
List of metric samples
|
|
419
|
+
"""
|
|
420
|
+
samples = list(self._samples.get(metric_type, []))
|
|
421
|
+
|
|
422
|
+
if window_seconds is not None:
|
|
423
|
+
cutoff = datetime.now() - timedelta(seconds=window_seconds)
|
|
424
|
+
samples = [s for s in samples if s.timestamp >= cutoff]
|
|
425
|
+
|
|
426
|
+
return samples
|
|
427
|
+
|
|
428
|
+
def get_observation_history(
|
|
429
|
+
self,
|
|
430
|
+
count: Optional[int] = None
|
|
431
|
+
) -> List[ObservationResult]:
|
|
432
|
+
"""Get recent observation results."""
|
|
433
|
+
observations = list(self._observations)
|
|
434
|
+
if count is not None:
|
|
435
|
+
observations = observations[-count:]
|
|
436
|
+
return observations
|