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.
Files changed (337) hide show
  1. agent_control_plane/__init__.py +662 -0
  2. agent_control_plane/a2a_adapter.py +543 -0
  3. agent_control_plane/adapter.py +417 -0
  4. agent_control_plane/agent_hibernation.py +394 -0
  5. agent_control_plane/agent_kernel.py +470 -0
  6. agent_control_plane/compliance.py +720 -0
  7. agent_control_plane/constraint_graphs.py +478 -0
  8. agent_control_plane/control_plane.py +854 -0
  9. agent_control_plane/example_executors.py +195 -0
  10. agent_control_plane/execution_engine.py +231 -0
  11. agent_control_plane/flight_recorder.py +846 -0
  12. agent_control_plane/governance_layer.py +435 -0
  13. agent_control_plane/hf_utils.py +563 -0
  14. agent_control_plane/interfaces/__init__.py +55 -0
  15. agent_control_plane/interfaces/kernel_interface.py +361 -0
  16. agent_control_plane/interfaces/plugin_interface.py +497 -0
  17. agent_control_plane/interfaces/protocol_interfaces.py +387 -0
  18. agent_control_plane/kernel_space.py +1009 -0
  19. agent_control_plane/langchain_adapter.py +424 -0
  20. agent_control_plane/lifecycle.py +3113 -0
  21. agent_control_plane/mcp_adapter.py +653 -0
  22. agent_control_plane/ml_safety.py +563 -0
  23. agent_control_plane/multimodal.py +727 -0
  24. agent_control_plane/mute_agent.py +422 -0
  25. agent_control_plane/observability.py +787 -0
  26. agent_control_plane/orchestrator.py +482 -0
  27. agent_control_plane/plugin_registry.py +750 -0
  28. agent_control_plane/policy_engine.py +954 -0
  29. agent_control_plane/process_isolation.py +777 -0
  30. agent_control_plane/shadow_mode.py +310 -0
  31. agent_control_plane/signals.py +493 -0
  32. agent_control_plane/supervisor_agents.py +430 -0
  33. agent_control_plane/time_travel_debugger.py +557 -0
  34. agent_control_plane/tool_registry.py +452 -0
  35. agent_control_plane/vfs.py +697 -0
  36. agent_kernel/__init__.py +69 -0
  37. agent_kernel/analyzer.py +435 -0
  38. agent_kernel/auditor.py +36 -0
  39. agent_kernel/completeness_auditor.py +237 -0
  40. agent_kernel/detector.py +203 -0
  41. agent_kernel/kernel.py +744 -0
  42. agent_kernel/memory_manager.py +85 -0
  43. agent_kernel/models.py +374 -0
  44. agent_kernel/nudge_mechanism.py +263 -0
  45. agent_kernel/outcome_analyzer.py +338 -0
  46. agent_kernel/patcher.py +582 -0
  47. agent_kernel/semantic_analyzer.py +316 -0
  48. agent_kernel/semantic_purge.py +349 -0
  49. agent_kernel/simulator.py +449 -0
  50. agent_kernel/teacher.py +85 -0
  51. agent_kernel/triage.py +152 -0
  52. agent_os/__init__.py +409 -0
  53. agent_os/_adversarial_impl.py +200 -0
  54. agent_os/_circuit_breaker_impl.py +232 -0
  55. agent_os/_mcp_metrics.py +193 -0
  56. agent_os/adversarial.py +20 -0
  57. agent_os/agents_compat.py +490 -0
  58. agent_os/audit_logger.py +135 -0
  59. agent_os/base_agent.py +651 -0
  60. agent_os/circuit_breaker.py +34 -0
  61. agent_os/cli/__init__.py +659 -0
  62. agent_os/cli/cmd_audit.py +128 -0
  63. agent_os/cli/cmd_init.py +152 -0
  64. agent_os/cli/cmd_policy.py +41 -0
  65. agent_os/cli/cmd_policy_gen.py +180 -0
  66. agent_os/cli/cmd_validate.py +258 -0
  67. agent_os/cli/mcp_scan.py +265 -0
  68. agent_os/cli/output.py +192 -0
  69. agent_os/cli/policy_checker.py +330 -0
  70. agent_os/compat.py +74 -0
  71. agent_os/constraint_graph.py +234 -0
  72. agent_os/content_governance.py +140 -0
  73. agent_os/context_budget.py +305 -0
  74. agent_os/credential_redactor.py +224 -0
  75. agent_os/diff_policy.py +89 -0
  76. agent_os/egress_policy.py +159 -0
  77. agent_os/escalation.py +276 -0
  78. agent_os/event_bus.py +124 -0
  79. agent_os/exceptions.py +180 -0
  80. agent_os/execution_context_policy.py +141 -0
  81. agent_os/github_enterprise.py +96 -0
  82. agent_os/health.py +20 -0
  83. agent_os/integrations/__init__.py +279 -0
  84. agent_os/integrations/a2a_adapter.py +279 -0
  85. agent_os/integrations/agent_lightning/__init__.py +30 -0
  86. agent_os/integrations/anthropic_adapter.py +420 -0
  87. agent_os/integrations/autogen_adapter.py +620 -0
  88. agent_os/integrations/base.py +1137 -0
  89. agent_os/integrations/compat.py +229 -0
  90. agent_os/integrations/config.py +98 -0
  91. agent_os/integrations/conversation_guardian.py +957 -0
  92. agent_os/integrations/crewai_adapter.py +467 -0
  93. agent_os/integrations/drift_detector.py +425 -0
  94. agent_os/integrations/dry_run.py +124 -0
  95. agent_os/integrations/escalation.py +582 -0
  96. agent_os/integrations/gemini_adapter.py +364 -0
  97. agent_os/integrations/google_adk_adapter.py +633 -0
  98. agent_os/integrations/guardrails_adapter.py +394 -0
  99. agent_os/integrations/health.py +197 -0
  100. agent_os/integrations/langchain_adapter.py +654 -0
  101. agent_os/integrations/llamafirewall.py +343 -0
  102. agent_os/integrations/llamaindex_adapter.py +188 -0
  103. agent_os/integrations/logging.py +191 -0
  104. agent_os/integrations/maf_adapter.py +631 -0
  105. agent_os/integrations/mistral_adapter.py +365 -0
  106. agent_os/integrations/openai_adapter.py +816 -0
  107. agent_os/integrations/openai_agents_sdk.py +406 -0
  108. agent_os/integrations/policy_compose.py +171 -0
  109. agent_os/integrations/profiling.py +144 -0
  110. agent_os/integrations/pydantic_ai_adapter.py +420 -0
  111. agent_os/integrations/rate_limiter.py +130 -0
  112. agent_os/integrations/rbac.py +143 -0
  113. agent_os/integrations/registry.py +113 -0
  114. agent_os/integrations/scope_guard.py +303 -0
  115. agent_os/integrations/semantic_kernel_adapter.py +769 -0
  116. agent_os/integrations/smolagents_adapter.py +629 -0
  117. agent_os/integrations/templates.py +178 -0
  118. agent_os/integrations/token_budget.py +134 -0
  119. agent_os/integrations/tool_aliases.py +190 -0
  120. agent_os/integrations/webhooks.py +177 -0
  121. agent_os/lite.py +208 -0
  122. agent_os/mcp_gateway.py +385 -0
  123. agent_os/mcp_message_signer.py +273 -0
  124. agent_os/mcp_protocols.py +161 -0
  125. agent_os/mcp_response_scanner.py +232 -0
  126. agent_os/mcp_security.py +924 -0
  127. agent_os/mcp_session_auth.py +231 -0
  128. agent_os/mcp_sliding_rate_limiter.py +184 -0
  129. agent_os/memory_guard.py +409 -0
  130. agent_os/metrics.py +134 -0
  131. agent_os/mute.py +428 -0
  132. agent_os/mute_agent.py +209 -0
  133. agent_os/policies/__init__.py +77 -0
  134. agent_os/policies/async_evaluator.py +275 -0
  135. agent_os/policies/backends.py +670 -0
  136. agent_os/policies/bridge.py +169 -0
  137. agent_os/policies/budget.py +85 -0
  138. agent_os/policies/cli.py +294 -0
  139. agent_os/policies/conflict_resolution.py +270 -0
  140. agent_os/policies/data_classification.py +252 -0
  141. agent_os/policies/evaluator.py +239 -0
  142. agent_os/policies/policy_schema.json +228 -0
  143. agent_os/policies/rate_limiting.py +145 -0
  144. agent_os/policies/schema.py +115 -0
  145. agent_os/policies/shared.py +331 -0
  146. agent_os/prompt_injection.py +694 -0
  147. agent_os/providers.py +182 -0
  148. agent_os/py.typed +0 -0
  149. agent_os/retry.py +81 -0
  150. agent_os/reversibility.py +251 -0
  151. agent_os/sandbox.py +432 -0
  152. agent_os/sandbox_provider.py +140 -0
  153. agent_os/secure_codegen.py +525 -0
  154. agent_os/security_skills.py +538 -0
  155. agent_os/semantic_policy.py +422 -0
  156. agent_os/server/__init__.py +15 -0
  157. agent_os/server/__main__.py +25 -0
  158. agent_os/server/app.py +277 -0
  159. agent_os/server/models.py +104 -0
  160. agent_os/shift_left_metrics.py +130 -0
  161. agent_os/stateless.py +742 -0
  162. agent_os/supervisor.py +148 -0
  163. agent_os/task_outcome.py +148 -0
  164. agent_os/transparency.py +181 -0
  165. agent_os/trust_root.py +128 -0
  166. agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
  167. agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
  168. agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
  169. agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
  170. agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
  171. agent_os_observability/__init__.py +27 -0
  172. agent_os_observability/dashboards.py +898 -0
  173. agent_os_observability/metrics.py +398 -0
  174. agent_os_observability/server.py +223 -0
  175. agent_os_observability/tracer.py +232 -0
  176. agent_primitives/__init__.py +24 -0
  177. agent_primitives/failures.py +84 -0
  178. agent_primitives/py.typed +0 -0
  179. amb_core/__init__.py +177 -0
  180. amb_core/adapters/__init__.py +57 -0
  181. amb_core/adapters/aws_sqs_broker.py +376 -0
  182. amb_core/adapters/azure_servicebus_broker.py +340 -0
  183. amb_core/adapters/kafka_broker.py +260 -0
  184. amb_core/adapters/nats_broker.py +285 -0
  185. amb_core/adapters/rabbitmq_broker.py +235 -0
  186. amb_core/adapters/redis_broker.py +262 -0
  187. amb_core/broker.py +145 -0
  188. amb_core/bus.py +481 -0
  189. amb_core/cloudevents.py +509 -0
  190. amb_core/dlq.py +345 -0
  191. amb_core/hf_utils.py +536 -0
  192. amb_core/memory_broker.py +410 -0
  193. amb_core/models.py +141 -0
  194. amb_core/persistence.py +529 -0
  195. amb_core/schema.py +294 -0
  196. amb_core/tracing.py +358 -0
  197. atr/__init__.py +640 -0
  198. atr/access.py +348 -0
  199. atr/composition.py +645 -0
  200. atr/decorator.py +357 -0
  201. atr/executor.py +384 -0
  202. atr/health.py +557 -0
  203. atr/hf_utils.py +449 -0
  204. atr/injection.py +422 -0
  205. atr/metrics.py +440 -0
  206. atr/policies.py +403 -0
  207. atr/py.typed +2 -0
  208. atr/registry.py +452 -0
  209. atr/schema.py +480 -0
  210. atr/tools/safe/__init__.py +75 -0
  211. atr/tools/safe/calculator.py +467 -0
  212. atr/tools/safe/datetime_tool.py +443 -0
  213. atr/tools/safe/file_reader.py +402 -0
  214. atr/tools/safe/http_client.py +316 -0
  215. atr/tools/safe/json_parser.py +374 -0
  216. atr/tools/safe/text_tool.py +537 -0
  217. atr/tools/safe/toolkit.py +175 -0
  218. caas/__init__.py +162 -0
  219. caas/api/__init__.py +7 -0
  220. caas/api/server.py +1328 -0
  221. caas/caching.py +834 -0
  222. caas/cli.py +210 -0
  223. caas/conversation.py +223 -0
  224. caas/decay.py +72 -0
  225. caas/detection/__init__.py +9 -0
  226. caas/detection/detector.py +238 -0
  227. caas/enrichment.py +130 -0
  228. caas/gateway/__init__.py +27 -0
  229. caas/gateway/trust_gateway.py +474 -0
  230. caas/hf_utils.py +479 -0
  231. caas/ingestion/__init__.py +23 -0
  232. caas/ingestion/processors.py +253 -0
  233. caas/ingestion/structure_parser.py +188 -0
  234. caas/models.py +356 -0
  235. caas/pragmatic_truth.py +444 -0
  236. caas/routing/__init__.py +10 -0
  237. caas/routing/heuristic_router.py +58 -0
  238. caas/storage/__init__.py +9 -0
  239. caas/storage/store.py +389 -0
  240. caas/triad.py +213 -0
  241. caas/tuning/__init__.py +9 -0
  242. caas/tuning/tuner.py +329 -0
  243. caas/vfs/__init__.py +14 -0
  244. caas/vfs/filesystem.py +452 -0
  245. cmvk/__init__.py +218 -0
  246. cmvk/audit.py +402 -0
  247. cmvk/benchmarks.py +478 -0
  248. cmvk/constitutional.py +904 -0
  249. cmvk/hf_utils.py +301 -0
  250. cmvk/metrics.py +473 -0
  251. cmvk/profiles.py +300 -0
  252. cmvk/py.typed +0 -0
  253. cmvk/types.py +12 -0
  254. cmvk/verification.py +956 -0
  255. emk/__init__.py +89 -0
  256. emk/causal.py +352 -0
  257. emk/hf_utils.py +421 -0
  258. emk/indexer.py +83 -0
  259. emk/py.typed +0 -0
  260. emk/schema.py +204 -0
  261. emk/sleep_cycle.py +347 -0
  262. emk/store.py +281 -0
  263. iatp/__init__.py +166 -0
  264. iatp/attestation.py +461 -0
  265. iatp/cli.py +317 -0
  266. iatp/hf_utils.py +472 -0
  267. iatp/ipc_pipes.py +580 -0
  268. iatp/main.py +412 -0
  269. iatp/models/__init__.py +447 -0
  270. iatp/policy_engine.py +337 -0
  271. iatp/py.typed +2 -0
  272. iatp/recovery.py +321 -0
  273. iatp/security/__init__.py +270 -0
  274. iatp/sidecar/__init__.py +519 -0
  275. iatp/telemetry/__init__.py +164 -0
  276. iatp/tests/__init__.py +1 -0
  277. iatp/tests/test_attestation.py +370 -0
  278. iatp/tests/test_cli.py +131 -0
  279. iatp/tests/test_ed25519_attestation.py +211 -0
  280. iatp/tests/test_models.py +130 -0
  281. iatp/tests/test_policy_engine.py +347 -0
  282. iatp/tests/test_recovery.py +281 -0
  283. iatp/tests/test_security.py +222 -0
  284. iatp/tests/test_sidecar.py +167 -0
  285. iatp/tests/test_telemetry.py +175 -0
  286. mcp_kernel_server/__init__.py +28 -0
  287. mcp_kernel_server/cli.py +274 -0
  288. mcp_kernel_server/resources.py +217 -0
  289. mcp_kernel_server/server.py +564 -0
  290. mcp_kernel_server/tools.py +1174 -0
  291. mute_agent/__init__.py +68 -0
  292. mute_agent/core/__init__.py +1 -0
  293. mute_agent/core/execution_agent.py +166 -0
  294. mute_agent/core/handshake_protocol.py +201 -0
  295. mute_agent/core/reasoning_agent.py +238 -0
  296. mute_agent/knowledge_graph/__init__.py +1 -0
  297. mute_agent/knowledge_graph/graph_elements.py +65 -0
  298. mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
  299. mute_agent/knowledge_graph/subgraph.py +224 -0
  300. mute_agent/listener/__init__.py +43 -0
  301. mute_agent/listener/adapters/__init__.py +31 -0
  302. mute_agent/listener/adapters/base_adapter.py +189 -0
  303. mute_agent/listener/adapters/caas_adapter.py +344 -0
  304. mute_agent/listener/adapters/control_plane_adapter.py +436 -0
  305. mute_agent/listener/adapters/iatp_adapter.py +332 -0
  306. mute_agent/listener/adapters/scak_adapter.py +251 -0
  307. mute_agent/listener/listener.py +610 -0
  308. mute_agent/listener/state_observer.py +436 -0
  309. mute_agent/listener/threshold_config.py +313 -0
  310. mute_agent/super_system/__init__.py +1 -0
  311. mute_agent/super_system/router.py +204 -0
  312. mute_agent/visualization/__init__.py +10 -0
  313. mute_agent/visualization/graph_debugger.py +502 -0
  314. nexus/README.md +60 -0
  315. nexus/__init__.py +51 -0
  316. nexus/arbiter.py +359 -0
  317. nexus/client.py +466 -0
  318. nexus/dmz.py +444 -0
  319. nexus/escrow.py +430 -0
  320. nexus/exceptions.py +286 -0
  321. nexus/pyproject.toml +36 -0
  322. nexus/registry.py +393 -0
  323. nexus/reputation.py +425 -0
  324. nexus/schemas/__init__.py +51 -0
  325. nexus/schemas/compliance.py +276 -0
  326. nexus/schemas/escrow.py +251 -0
  327. nexus/schemas/manifest.py +225 -0
  328. nexus/schemas/receipt.py +208 -0
  329. nexus/tests/__init__.py +0 -0
  330. nexus/tests/conftest.py +146 -0
  331. nexus/tests/test_arbiter.py +192 -0
  332. nexus/tests/test_dmz.py +194 -0
  333. nexus/tests/test_escrow.py +276 -0
  334. nexus/tests/test_exceptions.py +225 -0
  335. nexus/tests/test_registry.py +232 -0
  336. nexus/tests/test_reputation.py +328 -0
  337. nexus/tests/test_schemas.py +295 -0
@@ -0,0 +1,340 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Azure Service Bus broker adapter for AMB.
4
+
5
+ Azure Service Bus provides enterprise-grade messaging for cloud applications
6
+ with features like dead-letter queues, sessions, and transactions.
7
+ """
8
+
9
+ import asyncio
10
+ import json
11
+ import uuid
12
+ from typing import Dict, List, Optional
13
+
14
+ from amb_core.broker import BrokerAdapter, MessageHandler
15
+ from amb_core.models import Message
16
+
17
+ try:
18
+ from azure.servicebus.aio import ServiceBusClient, ServiceBusSender, ServiceBusReceiver
19
+ from azure.servicebus import ServiceBusMessage
20
+ except ImportError:
21
+ raise ImportError(
22
+ "Azure Service Bus adapter requires 'azure-servicebus' package. "
23
+ "Install it with: pip install amb-core[azure]"
24
+ )
25
+
26
+
27
+ class AzureServiceBusBroker(BrokerAdapter):
28
+ """
29
+ Azure Service Bus broker adapter.
30
+
31
+ This adapter uses Azure Service Bus topics and subscriptions for
32
+ enterprise-grade messaging with guaranteed delivery.
33
+
34
+ Example:
35
+ ```python
36
+ from amb_core.adapters import AzureServiceBusBroker
37
+
38
+ broker = AzureServiceBusBroker(
39
+ connection_string="Endpoint=sb://...",
40
+ topic_name="agent-messages"
41
+ )
42
+ await broker.connect()
43
+
44
+ # Subscribe
45
+ async def handler(msg):
46
+ print(f"Received: {msg.payload}")
47
+
48
+ sub_id = await broker.subscribe("agent.tasks", handler)
49
+
50
+ # Publish
51
+ await broker.publish(Message(topic="agent.tasks", payload={"task": "analyze"}))
52
+ ```
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ connection_string: str,
58
+ topic_name: str = "amb-messages",
59
+ subscription_name: str = "amb-subscription"
60
+ ):
61
+ """
62
+ Initialize Azure Service Bus broker.
63
+
64
+ Args:
65
+ connection_string: Azure Service Bus connection string
66
+ topic_name: Service Bus topic name
67
+ subscription_name: Default subscription name
68
+ """
69
+ self.connection_string = connection_string
70
+ self.topic_name = topic_name
71
+ self.subscription_name = subscription_name
72
+
73
+ self._client: Optional[ServiceBusClient] = None
74
+ self._sender: Optional[ServiceBusSender] = None
75
+ self._receivers: Dict[str, ServiceBusReceiver] = {}
76
+ self._subscriptions: Dict[str, str] = {} # subscription_id -> topic
77
+ self._handlers: Dict[str, MessageHandler] = {}
78
+ self._tasks: set = set()
79
+ self._running = False
80
+
81
+ async def connect(self) -> None:
82
+ """Connect to Azure Service Bus."""
83
+ self._client = ServiceBusClient.from_connection_string(
84
+ conn_str=self.connection_string
85
+ )
86
+
87
+ # Create sender for the topic
88
+ self._sender = self._client.get_topic_sender(topic_name=self.topic_name)
89
+ self._running = True
90
+
91
+ async def disconnect(self) -> None:
92
+ """Disconnect from Azure Service Bus."""
93
+ self._running = False
94
+
95
+ # Cancel all tasks
96
+ for task in self._tasks:
97
+ if not task.done():
98
+ task.cancel()
99
+
100
+ if self._tasks:
101
+ await asyncio.gather(*self._tasks, return_exceptions=True)
102
+
103
+ self._tasks.clear()
104
+
105
+ # Close receivers
106
+ for receiver in self._receivers.values():
107
+ await receiver.close()
108
+
109
+ self._receivers.clear()
110
+
111
+ # Close sender
112
+ if self._sender:
113
+ await self._sender.close()
114
+
115
+ # Close client
116
+ if self._client:
117
+ await self._client.close()
118
+
119
+ async def publish(self, message: Message, wait_for_confirmation: bool = False) -> Optional[str]:
120
+ """
121
+ Publish message to Azure Service Bus topic.
122
+
123
+ Args:
124
+ message: Message to publish
125
+ wait_for_confirmation: Wait for broker acknowledgment
126
+
127
+ Returns:
128
+ Message ID
129
+ """
130
+ if not self._sender:
131
+ raise ConnectionError("Not connected to Azure Service Bus")
132
+
133
+ # Create Service Bus message
134
+ sb_message = ServiceBusMessage(
135
+ body=message.model_dump_json(),
136
+ message_id=message.id,
137
+ correlation_id=message.correlation_id,
138
+ subject=message.topic, # Use subject for filtering
139
+ application_properties={
140
+ "topic": message.topic,
141
+ "source": message.source,
142
+ "target": message.target or "",
143
+ "message_type": message.message_type.value if hasattr(message.message_type, 'value') else str(message.message_type)
144
+ }
145
+ )
146
+
147
+ # Send message
148
+ await self._sender.send_messages(sb_message)
149
+
150
+ return message.id
151
+
152
+ async def subscribe(self, topic: str, handler: MessageHandler) -> str:
153
+ """
154
+ Subscribe to messages on a topic.
155
+
156
+ Creates a receiver for the subscription and starts listening.
157
+
158
+ Args:
159
+ topic: Topic to filter messages by
160
+ handler: Message handler
161
+
162
+ Returns:
163
+ Subscription ID
164
+ """
165
+ if not self._client:
166
+ raise ConnectionError("Not connected to Azure Service Bus")
167
+
168
+ subscription_id = str(uuid.uuid4())
169
+
170
+ # Create receiver for the subscription
171
+ receiver = self._client.get_subscription_receiver(
172
+ topic_name=self.topic_name,
173
+ subscription_name=self.subscription_name,
174
+ max_wait_time=5 # seconds
175
+ )
176
+
177
+ self._receivers[subscription_id] = receiver
178
+ self._subscriptions[subscription_id] = topic
179
+ self._handlers[topic] = handler
180
+
181
+ # Start receiver task
182
+ task = asyncio.create_task(self._receive_task(subscription_id, topic, handler))
183
+ self._tasks.add(task)
184
+ task.add_done_callback(self._tasks.discard)
185
+
186
+ return subscription_id
187
+
188
+ async def _receive_task(self, subscription_id: str, topic: str, handler: MessageHandler):
189
+ """Receive messages from Azure Service Bus."""
190
+ receiver = self._receivers.get(subscription_id)
191
+ if not receiver:
192
+ return
193
+
194
+ while self._running:
195
+ try:
196
+ # Receive messages in batches
197
+ async with receiver:
198
+ messages = await receiver.receive_messages(max_message_count=10, max_wait_time=5)
199
+
200
+ for sb_message in messages:
201
+ try:
202
+ # Parse message
203
+ body = str(sb_message)
204
+ amb_message = Message.model_validate_json(body)
205
+
206
+ # Check if message matches topic filter
207
+ if amb_message.topic == topic or topic == "*":
208
+ await handler(amb_message)
209
+
210
+ # Complete the message
211
+ await receiver.complete_message(sb_message)
212
+
213
+ except Exception as e:
214
+ print(f"Error handling message: {e}")
215
+ # Dead-letter the message for investigation
216
+ await receiver.dead_letter_message(
217
+ sb_message,
218
+ reason="ProcessingError",
219
+ error_description=str(e)
220
+ )
221
+
222
+ except asyncio.CancelledError:
223
+ break
224
+ except Exception as e:
225
+ print(f"Error in Service Bus receiver: {e}")
226
+ await asyncio.sleep(1.0)
227
+
228
+ async def unsubscribe(self, subscription_id: str) -> None:
229
+ """
230
+ Unsubscribe from messages.
231
+
232
+ Args:
233
+ subscription_id: Subscription ID
234
+ """
235
+ if subscription_id in self._receivers:
236
+ receiver = self._receivers[subscription_id]
237
+ await receiver.close()
238
+ del self._receivers[subscription_id]
239
+
240
+ if subscription_id in self._subscriptions:
241
+ topic = self._subscriptions[subscription_id]
242
+ del self._subscriptions[subscription_id]
243
+ if topic in self._handlers:
244
+ del self._handlers[topic]
245
+
246
+ async def request(self, message: Message, timeout: float = 30.0) -> Message:
247
+ """
248
+ Send request and wait for response.
249
+
250
+ Uses correlation ID and reply-to for request-response pattern.
251
+
252
+ Args:
253
+ message: Request message
254
+ timeout: Timeout in seconds
255
+
256
+ Returns:
257
+ Response message
258
+ """
259
+ if not self._client or not self._sender:
260
+ raise ConnectionError("Not connected to Azure Service Bus")
261
+
262
+ # Generate correlation ID
263
+ if not message.correlation_id:
264
+ message.correlation_id = str(uuid.uuid4())
265
+
266
+ # Create response queue
267
+ response_queue: asyncio.Queue = asyncio.Queue()
268
+
269
+ # Create temporary receiver for response
270
+ response_subscription = f"response-{message.correlation_id[:8]}"
271
+
272
+ async def response_handler(msg: Message):
273
+ if msg.correlation_id == message.correlation_id:
274
+ await response_queue.put(msg)
275
+
276
+ # Subscribe to responses
277
+ sub_id = await self.subscribe(response_subscription, response_handler)
278
+
279
+ try:
280
+ # Set reply-to
281
+ message.reply_to = response_subscription
282
+
283
+ # Publish request
284
+ await self.publish(message, wait_for_confirmation=False)
285
+
286
+ # Wait for response
287
+ try:
288
+ response = await asyncio.wait_for(response_queue.get(), timeout=timeout)
289
+ return response
290
+ except asyncio.TimeoutError:
291
+ raise TimeoutError(f"No response received within {timeout} seconds")
292
+
293
+ finally:
294
+ await self.unsubscribe(sub_id)
295
+
296
+ async def get_pending_messages(self, topic: str, limit: int = 10) -> List[Message]:
297
+ """
298
+ Peek at pending messages without consuming them.
299
+
300
+ Args:
301
+ topic: Topic to filter by
302
+ limit: Maximum messages to retrieve
303
+
304
+ Returns:
305
+ List of messages
306
+ """
307
+ if not self._client:
308
+ raise ConnectionError("Not connected to Azure Service Bus")
309
+
310
+ messages = []
311
+
312
+ # Create temporary receiver
313
+ receiver = self._client.get_subscription_receiver(
314
+ topic_name=self.topic_name,
315
+ subscription_name=self.subscription_name
316
+ )
317
+
318
+ try:
319
+ async with receiver:
320
+ # Peek messages without completing them
321
+ peeked = await receiver.peek_messages(max_message_count=limit)
322
+
323
+ for sb_message in peeked:
324
+ try:
325
+ body = str(sb_message)
326
+ amb_message = Message.model_validate_json(body)
327
+
328
+ if amb_message.topic == topic or topic == "*":
329
+ messages.append(amb_message)
330
+ except Exception:
331
+ continue
332
+
333
+ finally:
334
+ await receiver.close()
335
+
336
+ return messages
337
+
338
+ async def health_check(self) -> bool:
339
+ """Check if connected to Azure Service Bus."""
340
+ return self._client is not None and self._sender is not None
@@ -0,0 +1,260 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Kafka broker adapter for AMB."""
4
+
5
+ import asyncio
6
+ import json
7
+ import uuid
8
+ from typing import Dict, List, Optional
9
+
10
+ from amb_core.broker import BrokerAdapter, MessageHandler
11
+ from amb_core.models import Message
12
+
13
+ try:
14
+ from aiokafka import AIOKafkaConsumer, AIOKafkaProducer
15
+ from aiokafka.errors import KafkaError
16
+ except ImportError:
17
+ raise ImportError(
18
+ "Kafka adapter requires 'aiokafka' package. "
19
+ "Install it with: pip install amb-core[kafka]"
20
+ )
21
+
22
+
23
+ class KafkaBroker(BrokerAdapter):
24
+ """
25
+ Kafka-based broker adapter.
26
+
27
+ This adapter uses Kafka topics for message distribution with
28
+ high throughput and durability.
29
+ """
30
+
31
+ def __init__(self, bootstrap_servers: str = "localhost:9092"):
32
+ """
33
+ Initialize Kafka broker.
34
+
35
+ Args:
36
+ bootstrap_servers: Kafka bootstrap servers
37
+ """
38
+ self.bootstrap_servers = bootstrap_servers
39
+ self._producer: Optional[AIOKafkaProducer] = None
40
+ self._consumers: Dict[str, AIOKafkaConsumer] = {}
41
+ self._subscriptions: Dict[str, str] = {} # subscription_id -> topic
42
+ self._tasks: set = set()
43
+
44
+ async def connect(self) -> None:
45
+ """Connect to Kafka."""
46
+ self._producer = AIOKafkaProducer(
47
+ bootstrap_servers=self.bootstrap_servers,
48
+ value_serializer=lambda v: json.dumps(v).encode()
49
+ )
50
+ await self._producer.start()
51
+
52
+ async def disconnect(self) -> None:
53
+ """Disconnect from Kafka."""
54
+ # Cancel all tasks
55
+ for task in self._tasks:
56
+ if not task.done():
57
+ task.cancel()
58
+
59
+ if self._tasks:
60
+ await asyncio.gather(*self._tasks, return_exceptions=True)
61
+
62
+ self._tasks.clear()
63
+
64
+ # Stop consumers
65
+ for consumer in self._consumers.values():
66
+ await consumer.stop()
67
+
68
+ self._consumers.clear()
69
+
70
+ # Stop producer
71
+ if self._producer:
72
+ await self._producer.stop()
73
+
74
+ async def publish(self, message: Message, wait_for_confirmation: bool = False) -> Optional[str]:
75
+ """
76
+ Publish message to Kafka topic.
77
+
78
+ Args:
79
+ message: Message to publish
80
+ wait_for_confirmation: Wait for broker confirmation
81
+
82
+ Returns:
83
+ Message ID
84
+ """
85
+ if not self._producer:
86
+ raise ConnectionError("Not connected to Kafka")
87
+
88
+ # Serialize message
89
+ message_dict = message.model_dump(mode='json')
90
+
91
+ # Publish
92
+ future = await self._producer.send(
93
+ message.topic,
94
+ value=message_dict,
95
+ key=message.id.encode() if message.id else None
96
+ )
97
+
98
+ if wait_for_confirmation:
99
+ # Wait for acknowledgment
100
+ await future
101
+
102
+ return message.id
103
+
104
+ async def subscribe(self, topic: str, handler: MessageHandler) -> str:
105
+ """
106
+ Subscribe to a Kafka topic.
107
+
108
+ Args:
109
+ topic: Topic to subscribe to
110
+ handler: Message handler
111
+
112
+ Returns:
113
+ Subscription ID
114
+ """
115
+ subscription_id = str(uuid.uuid4())
116
+
117
+ # Create consumer
118
+ consumer = AIOKafkaConsumer(
119
+ topic,
120
+ bootstrap_servers=self.bootstrap_servers,
121
+ value_deserializer=lambda v: json.loads(v.decode()),
122
+ group_id=f"amb-{subscription_id}",
123
+ auto_offset_reset='latest'
124
+ )
125
+
126
+ await consumer.start()
127
+ self._consumers[subscription_id] = consumer
128
+ self._subscriptions[subscription_id] = topic
129
+
130
+ # Start consumer task
131
+ task = asyncio.create_task(self._consume_task(subscription_id, handler))
132
+ self._tasks.add(task)
133
+ task.add_done_callback(self._tasks.discard)
134
+
135
+ return subscription_id
136
+
137
+ async def _consume_task(self, subscription_id: str, handler: MessageHandler):
138
+ """Consume messages from Kafka."""
139
+ consumer = self._consumers.get(subscription_id)
140
+ if not consumer:
141
+ return
142
+
143
+ try:
144
+ async for msg in consumer:
145
+ try:
146
+ # Parse message
147
+ message = Message.model_validate(msg.value)
148
+
149
+ # Call handler
150
+ await handler(message)
151
+
152
+ except Exception as e:
153
+ print(f"Error handling Kafka message: {e}")
154
+
155
+ except asyncio.CancelledError:
156
+ pass
157
+ except Exception as e:
158
+ print(f"Error in Kafka consumer: {e}")
159
+
160
+ async def unsubscribe(self, subscription_id: str) -> None:
161
+ """
162
+ Unsubscribe from a topic.
163
+
164
+ Args:
165
+ subscription_id: Subscription ID
166
+ """
167
+ if subscription_id in self._consumers:
168
+ consumer = self._consumers[subscription_id]
169
+ await consumer.stop()
170
+ del self._consumers[subscription_id]
171
+
172
+ if subscription_id in self._subscriptions:
173
+ del self._subscriptions[subscription_id]
174
+
175
+ async def request(self, message: Message, timeout: float = 30.0) -> Message:
176
+ """
177
+ Send request and wait for response.
178
+
179
+ This is a simplified implementation using temporary topics.
180
+
181
+ Args:
182
+ message: Request message
183
+ timeout: Timeout in seconds
184
+
185
+ Returns:
186
+ Response message
187
+ """
188
+ if not self._producer:
189
+ raise ConnectionError("Not connected to Kafka")
190
+
191
+ # Generate correlation ID
192
+ if not message.correlation_id:
193
+ message.correlation_id = str(uuid.uuid4())
194
+
195
+ # Create temporary response topic
196
+ response_topic = f"response.{message.correlation_id}"
197
+ message.reply_to = response_topic
198
+
199
+ # Create response queue
200
+ response_queue: asyncio.Queue = asyncio.Queue()
201
+
202
+ # Subscribe to response topic
203
+ async def response_handler(msg: Message):
204
+ if msg.correlation_id == message.correlation_id:
205
+ await response_queue.put(msg)
206
+
207
+ sub_id = await self.subscribe(response_topic, response_handler)
208
+
209
+ try:
210
+ # Publish request
211
+ await self.publish(message, wait_for_confirmation=False)
212
+
213
+ # Wait for response
214
+ try:
215
+ response = await asyncio.wait_for(response_queue.get(), timeout=timeout)
216
+ return response
217
+ except asyncio.TimeoutError:
218
+ raise TimeoutError(f"No response received within {timeout} seconds")
219
+
220
+ finally:
221
+ # Clean up
222
+ await self.unsubscribe(sub_id)
223
+
224
+ async def get_pending_messages(self, topic: str, limit: int = 10) -> List[Message]:
225
+ """
226
+ Get pending messages from Kafka topic.
227
+
228
+ This creates a temporary consumer to read recent messages.
229
+
230
+ Args:
231
+ topic: Topic to get messages from
232
+ limit: Maximum messages to retrieve
233
+
234
+ Returns:
235
+ List of messages
236
+ """
237
+ # Create temporary consumer
238
+ consumer = AIOKafkaConsumer(
239
+ topic,
240
+ bootstrap_servers=self.bootstrap_servers,
241
+ value_deserializer=lambda v: json.loads(v.decode()),
242
+ auto_offset_reset='earliest',
243
+ consumer_timeout_ms=1000
244
+ )
245
+
246
+ try:
247
+ await consumer.start()
248
+
249
+ messages = []
250
+ async for msg in consumer:
251
+ message = Message.model_validate(msg.value)
252
+ messages.append(message)
253
+
254
+ if len(messages) >= limit:
255
+ break
256
+
257
+ return messages
258
+
259
+ finally:
260
+ await consumer.stop()