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
amb_core/bus.py
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""Main MessageBus implementation."""
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
import traceback
|
|
7
|
+
from typing import Optional, Dict, Any, Union
|
|
8
|
+
from amb_core.broker import BrokerAdapter, MessageHandler
|
|
9
|
+
from amb_core.models import Message, MessagePriority, Priority
|
|
10
|
+
from amb_core.memory_broker import InMemoryBroker
|
|
11
|
+
from amb_core.schema import SchemaRegistry, SchemaValidationError
|
|
12
|
+
from amb_core.dlq import DeadLetterQueue, DLQEntry, DLQReason
|
|
13
|
+
from amb_core.persistence import MessageStore, InMemoryMessageStore, MessageStatus
|
|
14
|
+
from amb_core.tracing import TraceContext, get_current_trace
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MessageBus:
|
|
18
|
+
"""
|
|
19
|
+
Main message bus interface for AI Agents.
|
|
20
|
+
|
|
21
|
+
This class provides a simple API for publishing and subscribing to messages
|
|
22
|
+
with support for both "fire and forget" and "wait for verification" patterns.
|
|
23
|
+
|
|
24
|
+
New in v0.2.0:
|
|
25
|
+
- persistence: Enable durable message storage for replay capability
|
|
26
|
+
- schema_registry: Validate messages against schemas
|
|
27
|
+
- dlq_enabled: Route failed messages to dead letter queue
|
|
28
|
+
- Distributed tracing support via trace_id
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
# Basic usage (backward compatible)
|
|
32
|
+
bus = MessageBus(adapter=InMemoryAdapter())
|
|
33
|
+
await bus.publish("topic", Message(payload=data))
|
|
34
|
+
|
|
35
|
+
# With new features
|
|
36
|
+
bus = MessageBus(
|
|
37
|
+
adapter=InMemoryAdapter(),
|
|
38
|
+
persistence=True, # Durable messages
|
|
39
|
+
schema_registry=schemas, # Validation
|
|
40
|
+
dlq_enabled=True # Dead letter queue
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
await bus.publish(
|
|
44
|
+
"topic",
|
|
45
|
+
Message(
|
|
46
|
+
payload=data,
|
|
47
|
+
priority=Priority.HIGH, # Prioritization
|
|
48
|
+
ttl_seconds=300, # Expiration
|
|
49
|
+
trace_id=uuid4() # Distributed tracing
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
adapter: Optional[BrokerAdapter] = None,
|
|
57
|
+
*,
|
|
58
|
+
persistence: Union[bool, MessageStore] = False,
|
|
59
|
+
schema_registry: Optional[SchemaRegistry] = None,
|
|
60
|
+
dlq_enabled: Union[bool, DeadLetterQueue] = False,
|
|
61
|
+
auto_inject_trace: bool = True
|
|
62
|
+
):
|
|
63
|
+
"""
|
|
64
|
+
Initialize the message bus.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
adapter: Broker adapter to use. If None, uses InMemoryBroker.
|
|
68
|
+
persistence: Enable message persistence. Can be True for default
|
|
69
|
+
InMemoryMessageStore, or a MessageStore instance.
|
|
70
|
+
schema_registry: SchemaRegistry for message validation.
|
|
71
|
+
dlq_enabled: Enable dead letter queue. Can be True for default
|
|
72
|
+
DeadLetterQueue, or a DeadLetterQueue instance.
|
|
73
|
+
auto_inject_trace: Automatically inject trace context into messages.
|
|
74
|
+
"""
|
|
75
|
+
self._adapter = adapter or InMemoryBroker()
|
|
76
|
+
self._connected = False
|
|
77
|
+
|
|
78
|
+
# Persistence (AMB-001)
|
|
79
|
+
if persistence is True:
|
|
80
|
+
self._persistence: Optional[MessageStore] = InMemoryMessageStore()
|
|
81
|
+
elif persistence is False:
|
|
82
|
+
self._persistence = None
|
|
83
|
+
else:
|
|
84
|
+
self._persistence = persistence
|
|
85
|
+
|
|
86
|
+
# Schema validation (AMB-003)
|
|
87
|
+
self._schema_registry = schema_registry
|
|
88
|
+
|
|
89
|
+
# Dead letter queue (AMB-002)
|
|
90
|
+
if dlq_enabled is True:
|
|
91
|
+
self._dlq: Optional[DeadLetterQueue] = DeadLetterQueue()
|
|
92
|
+
elif dlq_enabled is False:
|
|
93
|
+
self._dlq = None
|
|
94
|
+
else:
|
|
95
|
+
self._dlq = dlq_enabled
|
|
96
|
+
|
|
97
|
+
# Tracing (AMB-004)
|
|
98
|
+
self._auto_inject_trace = auto_inject_trace
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def dlq(self) -> Optional[DeadLetterQueue]:
|
|
102
|
+
"""Get the dead letter queue."""
|
|
103
|
+
return self._dlq
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def persistence(self) -> Optional[MessageStore]:
|
|
107
|
+
"""Get the message store."""
|
|
108
|
+
return self._persistence
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def schema_registry(self) -> Optional[SchemaRegistry]:
|
|
112
|
+
"""Get the schema registry."""
|
|
113
|
+
return self._schema_registry
|
|
114
|
+
|
|
115
|
+
async def connect(self) -> None:
|
|
116
|
+
"""Connect to the broker."""
|
|
117
|
+
await self._adapter.connect()
|
|
118
|
+
self._connected = True
|
|
119
|
+
|
|
120
|
+
async def disconnect(self) -> None:
|
|
121
|
+
"""Disconnect from the broker."""
|
|
122
|
+
await self._adapter.disconnect()
|
|
123
|
+
self._connected = False
|
|
124
|
+
|
|
125
|
+
async def __aenter__(self):
|
|
126
|
+
"""Async context manager entry."""
|
|
127
|
+
await self.connect()
|
|
128
|
+
return self
|
|
129
|
+
|
|
130
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
131
|
+
"""Async context manager exit."""
|
|
132
|
+
await self.disconnect()
|
|
133
|
+
|
|
134
|
+
async def publish(
|
|
135
|
+
self,
|
|
136
|
+
topic: str,
|
|
137
|
+
payload: Dict[str, Any],
|
|
138
|
+
*,
|
|
139
|
+
priority: MessagePriority = MessagePriority.NORMAL,
|
|
140
|
+
sender: Optional[str] = None,
|
|
141
|
+
wait_for_confirmation: bool = False,
|
|
142
|
+
ttl_seconds: Optional[int] = None,
|
|
143
|
+
trace_id: Optional[str] = None,
|
|
144
|
+
**kwargs
|
|
145
|
+
) -> str:
|
|
146
|
+
"""
|
|
147
|
+
Publish a message to a topic.
|
|
148
|
+
|
|
149
|
+
This method supports both "fire and forget" (default) and
|
|
150
|
+
"wait for verification" patterns via the wait_for_confirmation parameter.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
topic: Topic to publish to
|
|
154
|
+
payload: Message payload
|
|
155
|
+
priority: Message priority level (use Priority.HIGH, Priority.URGENT, etc.)
|
|
156
|
+
sender: Optional sender identifier
|
|
157
|
+
wait_for_confirmation: If True, wait for broker confirmation
|
|
158
|
+
ttl_seconds: Time-to-live in seconds (message expires after this)
|
|
159
|
+
trace_id: Distributed trace ID for tracking message flow
|
|
160
|
+
**kwargs: Additional message attributes
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
Message ID
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
ConnectionError: If not connected to the bus
|
|
167
|
+
SchemaValidationError: If schema validation fails (when registry configured)
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
# Fire and forget (fast, no guarantee)
|
|
171
|
+
await bus.publish("agent.thoughts", {"thought": "Hello"})
|
|
172
|
+
|
|
173
|
+
# Wait for verification (slower, with guarantee)
|
|
174
|
+
msg_id = await bus.publish(
|
|
175
|
+
"agent.action",
|
|
176
|
+
{"action": "execute"},
|
|
177
|
+
wait_for_confirmation=True
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# With new features
|
|
181
|
+
await bus.publish(
|
|
182
|
+
"fraud.alerts",
|
|
183
|
+
{"transaction_id": "tx123", "risk_score": 0.95},
|
|
184
|
+
priority=Priority.CRITICAL,
|
|
185
|
+
ttl_seconds=300,
|
|
186
|
+
trace_id=str(uuid4())
|
|
187
|
+
)
|
|
188
|
+
"""
|
|
189
|
+
if not self._connected:
|
|
190
|
+
raise ConnectionError("Message bus not connected")
|
|
191
|
+
|
|
192
|
+
# Schema validation (AMB-003)
|
|
193
|
+
if self._schema_registry and self._schema_registry.has_schema(topic):
|
|
194
|
+
payload = self._schema_registry.validate(topic, payload)
|
|
195
|
+
|
|
196
|
+
# Auto-inject trace context (AMB-004)
|
|
197
|
+
if self._auto_inject_trace and trace_id is None:
|
|
198
|
+
current_trace = get_current_trace()
|
|
199
|
+
if current_trace:
|
|
200
|
+
trace_id = current_trace.trace_id
|
|
201
|
+
kwargs.setdefault('span_id', current_trace.span_id)
|
|
202
|
+
kwargs.setdefault('parent_span_id', current_trace.parent_span_id)
|
|
203
|
+
|
|
204
|
+
message = Message(
|
|
205
|
+
id=str(uuid.uuid4()),
|
|
206
|
+
topic=topic,
|
|
207
|
+
payload=payload,
|
|
208
|
+
priority=priority,
|
|
209
|
+
sender=sender,
|
|
210
|
+
ttl=ttl_seconds,
|
|
211
|
+
trace_id=trace_id,
|
|
212
|
+
**kwargs
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Persist message if enabled (AMB-001)
|
|
216
|
+
if self._persistence:
|
|
217
|
+
await self._persistence.store(message)
|
|
218
|
+
|
|
219
|
+
await self._adapter.publish(message, wait_for_confirmation=wait_for_confirmation)
|
|
220
|
+
|
|
221
|
+
# Update persistence status
|
|
222
|
+
if self._persistence:
|
|
223
|
+
await self._persistence.update_status(message.id, MessageStatus.DELIVERED)
|
|
224
|
+
|
|
225
|
+
return message.id
|
|
226
|
+
|
|
227
|
+
async def subscribe(
|
|
228
|
+
self,
|
|
229
|
+
topic: str,
|
|
230
|
+
handler: MessageHandler,
|
|
231
|
+
*,
|
|
232
|
+
with_dlq: bool = True
|
|
233
|
+
) -> str:
|
|
234
|
+
"""
|
|
235
|
+
Subscribe to a topic with a message handler.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
topic: Topic to subscribe to
|
|
239
|
+
handler: Async function to handle messages
|
|
240
|
+
with_dlq: If True and DLQ is enabled, route failed messages to DLQ
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Subscription ID
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
async def handle_message(msg: Message):
|
|
247
|
+
print(f"Received: {msg.payload}")
|
|
248
|
+
|
|
249
|
+
sub_id = await bus.subscribe("agent.thoughts", handle_message)
|
|
250
|
+
"""
|
|
251
|
+
if not self._connected:
|
|
252
|
+
raise ConnectionError("Message bus not connected")
|
|
253
|
+
|
|
254
|
+
# Wrap handler with DLQ support if enabled
|
|
255
|
+
if self._dlq and with_dlq:
|
|
256
|
+
wrapped_handler = self._wrap_handler_with_dlq(handler, topic)
|
|
257
|
+
else:
|
|
258
|
+
wrapped_handler = handler
|
|
259
|
+
|
|
260
|
+
return await self._adapter.subscribe(topic, wrapped_handler)
|
|
261
|
+
|
|
262
|
+
def _wrap_handler_with_dlq(self, handler: MessageHandler, topic: str) -> MessageHandler:
|
|
263
|
+
"""Wrap a message handler with DLQ error handling."""
|
|
264
|
+
async def dlq_handler(message: Message) -> None:
|
|
265
|
+
# Check if message is expired (AMB-007)
|
|
266
|
+
if message.is_expired:
|
|
267
|
+
if self._dlq:
|
|
268
|
+
entry = DLQEntry(
|
|
269
|
+
message=message,
|
|
270
|
+
reason=DLQReason.EXPIRED,
|
|
271
|
+
error_message=f"Message expired (TTL: {message.ttl}s, age: {message.age_seconds:.1f}s)",
|
|
272
|
+
original_topic=topic
|
|
273
|
+
)
|
|
274
|
+
await self._dlq.add(entry)
|
|
275
|
+
return # Don't process expired messages
|
|
276
|
+
|
|
277
|
+
try:
|
|
278
|
+
await handler(message)
|
|
279
|
+
# Update persistence status if enabled
|
|
280
|
+
if self._persistence:
|
|
281
|
+
await self._persistence.update_status(message.id, MessageStatus.ACKNOWLEDGED)
|
|
282
|
+
except Exception as e:
|
|
283
|
+
# Route to DLQ on failure (AMB-002)
|
|
284
|
+
if self._dlq:
|
|
285
|
+
entry = DLQEntry(
|
|
286
|
+
message=message,
|
|
287
|
+
reason=DLQReason.HANDLER_ERROR,
|
|
288
|
+
error_message=str(e),
|
|
289
|
+
original_topic=topic,
|
|
290
|
+
stack_trace=traceback.format_exc()
|
|
291
|
+
)
|
|
292
|
+
await self._dlq.add(entry)
|
|
293
|
+
|
|
294
|
+
# Update persistence status
|
|
295
|
+
if self._persistence:
|
|
296
|
+
await self._persistence.update_status(
|
|
297
|
+
message.id, MessageStatus.FAILED, error=str(e)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Re-raise if no DLQ to maintain original behavior
|
|
301
|
+
if not self._dlq:
|
|
302
|
+
raise
|
|
303
|
+
|
|
304
|
+
return dlq_handler
|
|
305
|
+
|
|
306
|
+
async def unsubscribe(self, subscription_id: str) -> None:
|
|
307
|
+
"""
|
|
308
|
+
Unsubscribe from a topic.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
subscription_id: Subscription ID to unsubscribe
|
|
312
|
+
"""
|
|
313
|
+
if not self._connected:
|
|
314
|
+
raise ConnectionError("Message bus not connected")
|
|
315
|
+
|
|
316
|
+
await self._adapter.unsubscribe(subscription_id)
|
|
317
|
+
|
|
318
|
+
async def request(
|
|
319
|
+
self,
|
|
320
|
+
topic: str,
|
|
321
|
+
payload: Dict[str, Any],
|
|
322
|
+
*,
|
|
323
|
+
timeout: float = 30.0,
|
|
324
|
+
sender: Optional[str] = None,
|
|
325
|
+
**kwargs
|
|
326
|
+
) -> Message:
|
|
327
|
+
"""
|
|
328
|
+
Send a request and wait for a response.
|
|
329
|
+
|
|
330
|
+
This implements the request-response pattern for cases where
|
|
331
|
+
you need to wait for a reply from another agent.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
topic: Topic to send request to
|
|
335
|
+
payload: Request payload
|
|
336
|
+
timeout: Maximum time to wait for response
|
|
337
|
+
sender: Optional sender identifier
|
|
338
|
+
**kwargs: Additional message attributes
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Response message
|
|
342
|
+
|
|
343
|
+
Raises:
|
|
344
|
+
TimeoutError: If no response within timeout
|
|
345
|
+
|
|
346
|
+
Example:
|
|
347
|
+
response = await bus.request(
|
|
348
|
+
"agent.query",
|
|
349
|
+
{"query": "What is the status?"},
|
|
350
|
+
timeout=10.0
|
|
351
|
+
)
|
|
352
|
+
print(response.payload)
|
|
353
|
+
"""
|
|
354
|
+
if not self._connected:
|
|
355
|
+
raise ConnectionError("Message bus not connected")
|
|
356
|
+
|
|
357
|
+
# Auto-inject trace context
|
|
358
|
+
trace_id = kwargs.pop('trace_id', None)
|
|
359
|
+
if self._auto_inject_trace and trace_id is None:
|
|
360
|
+
current_trace = get_current_trace()
|
|
361
|
+
if current_trace:
|
|
362
|
+
trace_id = current_trace.trace_id
|
|
363
|
+
|
|
364
|
+
message = Message(
|
|
365
|
+
id=str(uuid.uuid4()),
|
|
366
|
+
topic=topic,
|
|
367
|
+
payload=payload,
|
|
368
|
+
sender=sender,
|
|
369
|
+
correlation_id=str(uuid.uuid4()),
|
|
370
|
+
trace_id=trace_id,
|
|
371
|
+
**kwargs
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return await self._adapter.request(message, timeout=timeout)
|
|
375
|
+
|
|
376
|
+
async def reply(self, original_message: Message, payload: Dict[str, Any]) -> str:
|
|
377
|
+
"""
|
|
378
|
+
Reply to a request message.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
original_message: The original request message
|
|
382
|
+
payload: Reply payload
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
Reply message ID
|
|
386
|
+
|
|
387
|
+
Example:
|
|
388
|
+
async def handle_request(msg: Message):
|
|
389
|
+
result = process_request(msg.payload)
|
|
390
|
+
await bus.reply(msg, {"result": result})
|
|
391
|
+
"""
|
|
392
|
+
if not self._connected:
|
|
393
|
+
raise ConnectionError("Message bus not connected")
|
|
394
|
+
|
|
395
|
+
if not original_message.correlation_id:
|
|
396
|
+
raise ValueError("Original message has no correlation_id")
|
|
397
|
+
|
|
398
|
+
reply_topic = original_message.reply_to or original_message.topic
|
|
399
|
+
|
|
400
|
+
reply_message = Message(
|
|
401
|
+
id=str(uuid.uuid4()),
|
|
402
|
+
topic=reply_topic,
|
|
403
|
+
payload=payload,
|
|
404
|
+
correlation_id=original_message.correlation_id,
|
|
405
|
+
sender=None,
|
|
406
|
+
trace_id=original_message.trace_id, # Propagate trace_id
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
await self._adapter.publish(reply_message, wait_for_confirmation=False)
|
|
410
|
+
return reply_message.id
|
|
411
|
+
|
|
412
|
+
async def replay(
|
|
413
|
+
self,
|
|
414
|
+
topic: str,
|
|
415
|
+
handler: MessageHandler,
|
|
416
|
+
*,
|
|
417
|
+
from_timestamp: Optional["datetime"] = None,
|
|
418
|
+
to_timestamp: Optional["datetime"] = None
|
|
419
|
+
) -> int:
|
|
420
|
+
"""
|
|
421
|
+
Replay persisted messages from a topic (AMB-001).
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
topic: Topic to replay messages from
|
|
425
|
+
handler: Handler to process replayed messages
|
|
426
|
+
from_timestamp: Start timestamp (inclusive)
|
|
427
|
+
to_timestamp: End timestamp (inclusive)
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
Number of messages replayed
|
|
431
|
+
|
|
432
|
+
Raises:
|
|
433
|
+
ValueError: If persistence is not enabled
|
|
434
|
+
|
|
435
|
+
Example:
|
|
436
|
+
# Replay all messages from the last hour
|
|
437
|
+
from datetime import datetime, timedelta, timezone
|
|
438
|
+
|
|
439
|
+
one_hour_ago = datetime.now(timezone.utc) - timedelta(hours=1)
|
|
440
|
+
count = await bus.replay(
|
|
441
|
+
"fraud.alerts",
|
|
442
|
+
handle_alert,
|
|
443
|
+
from_timestamp=one_hour_ago
|
|
444
|
+
)
|
|
445
|
+
print(f"Replayed {count} messages")
|
|
446
|
+
"""
|
|
447
|
+
if not self._persistence:
|
|
448
|
+
raise ValueError("Message persistence is not enabled")
|
|
449
|
+
|
|
450
|
+
count = 0
|
|
451
|
+
async for message in self._persistence.replay(topic, from_timestamp, to_timestamp):
|
|
452
|
+
await handler(message)
|
|
453
|
+
count += 1
|
|
454
|
+
|
|
455
|
+
return count
|
|
456
|
+
|
|
457
|
+
async def get_dlq_stats(self) -> Optional[Dict[str, Any]]:
|
|
458
|
+
"""
|
|
459
|
+
Get DLQ statistics.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
DLQ stats dict or None if DLQ not enabled
|
|
463
|
+
"""
|
|
464
|
+
if self._dlq is None:
|
|
465
|
+
return None
|
|
466
|
+
return await self._dlq.get_stats()
|
|
467
|
+
|
|
468
|
+
async def get_persistence_stats(self) -> Optional[Dict[str, Any]]:
|
|
469
|
+
"""
|
|
470
|
+
Get persistence statistics.
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
Persistence stats dict or None if persistence not enabled
|
|
474
|
+
"""
|
|
475
|
+
if not self._persistence:
|
|
476
|
+
return None
|
|
477
|
+
return await self._persistence.get_stats()
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
# Import datetime for type hints
|
|
481
|
+
from datetime import datetime
|