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,262 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Redis broker adapter for AMB."""
4
+
5
+ import asyncio
6
+ import time
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
+ import redis.asyncio as aioredis
15
+ except ImportError:
16
+ raise ImportError(
17
+ "Redis adapter requires 'redis' package. "
18
+ "Install it with: pip install amb-core[redis]"
19
+ )
20
+
21
+
22
+ class RedisBroker(BrokerAdapter):
23
+ """
24
+ Redis-based broker adapter using pub/sub.
25
+
26
+ This adapter uses Redis pub/sub for message distribution and
27
+ Redis streams for request-response patterns.
28
+ """
29
+
30
+ def __init__(self, url: str = "redis://localhost:6379/0"):
31
+ """
32
+ Initialize Redis broker.
33
+
34
+ Args:
35
+ url: Redis connection URL
36
+ """
37
+ self.url = url
38
+ self._client: Optional[aioredis.Redis] = None
39
+ self._pubsub: Optional[aioredis.client.PubSub] = None
40
+ self._subscriptions: Dict[str, str] = {} # subscription_id -> topic
41
+ self._handlers: Dict[str, MessageHandler] = {} # topic -> handler
42
+ self._tasks: set = set()
43
+ self._running = False
44
+
45
+ async def connect(self) -> None:
46
+ """Connect to Redis."""
47
+ self._client = await aioredis.from_url(self.url)
48
+ self._pubsub = self._client.pubsub()
49
+ self._running = True
50
+
51
+ async def disconnect(self) -> None:
52
+ """Disconnect from Redis."""
53
+ self._running = False
54
+
55
+ # Cancel all tasks
56
+ for task in self._tasks:
57
+ if not task.done():
58
+ task.cancel()
59
+
60
+ if self._tasks:
61
+ await asyncio.gather(*self._tasks, return_exceptions=True)
62
+
63
+ self._tasks.clear()
64
+
65
+ if self._pubsub:
66
+ await self._pubsub.close()
67
+
68
+ if self._client:
69
+ await self._client.close()
70
+
71
+ async def publish(self, message: Message, wait_for_confirmation: bool = False) -> Optional[str]:
72
+ """
73
+ Publish message to Redis pub/sub.
74
+
75
+ Args:
76
+ message: Message to publish
77
+ wait_for_confirmation: Wait for Redis confirmation
78
+
79
+ Returns:
80
+ Message ID
81
+ """
82
+ if not self._client:
83
+ raise ConnectionError("Not connected to Redis")
84
+
85
+ # Serialize message
86
+ message_json = message.model_dump_json()
87
+
88
+ # Publish to channel
89
+ result = await self._client.publish(message.topic, message_json)
90
+
91
+ # Store in stream for pending messages
92
+ await self._client.xadd(
93
+ f"stream:{message.topic}",
94
+ {"data": message_json},
95
+ maxlen=1000 # Keep last 1000 messages
96
+ )
97
+
98
+ if wait_for_confirmation:
99
+ # For Redis pub/sub, confirmation is implicit
100
+ # result contains number of subscribers that received the message
101
+ return message.id
102
+
103
+ return message.id
104
+
105
+ async def subscribe(self, topic: str, handler: MessageHandler) -> str:
106
+ """
107
+ Subscribe to a Redis channel.
108
+
109
+ Args:
110
+ topic: Topic/channel to subscribe to
111
+ handler: Message handler
112
+
113
+ Returns:
114
+ Subscription ID
115
+ """
116
+ if not self._pubsub:
117
+ raise ConnectionError("Not connected to Redis")
118
+
119
+ subscription_id = str(uuid.uuid4())
120
+ self._subscriptions[subscription_id] = topic
121
+ self._handlers[topic] = handler
122
+
123
+ # Subscribe to channel
124
+ await self._pubsub.subscribe(topic)
125
+
126
+ # Start listener task if not already running
127
+ task = asyncio.create_task(self._listen_task(topic))
128
+ self._tasks.add(task)
129
+ task.add_done_callback(self._tasks.discard)
130
+
131
+ return subscription_id
132
+
133
+ async def _listen_task(self, topic: str):
134
+ """Listen for messages on a topic."""
135
+ while self._running:
136
+ try:
137
+ message = await self._pubsub.get_message(ignore_subscribe_messages=True, timeout=1.0)
138
+ if message and message['type'] == 'message':
139
+ # Parse message
140
+ data = message['data']
141
+ if isinstance(data, bytes):
142
+ data = data.decode('utf-8')
143
+
144
+ msg = Message.model_validate_json(data)
145
+
146
+ # Call handler
147
+ if topic in self._handlers:
148
+ await self._handlers[topic](msg)
149
+
150
+ except asyncio.CancelledError:
151
+ break
152
+ except Exception as e:
153
+ # Log error but continue
154
+ print(f"Error in Redis listener: {e}")
155
+ await asyncio.sleep(1.0)
156
+
157
+ async def unsubscribe(self, subscription_id: str) -> None:
158
+ """
159
+ Unsubscribe from a channel.
160
+
161
+ Args:
162
+ subscription_id: Subscription ID
163
+ """
164
+ if subscription_id not in self._subscriptions:
165
+ return
166
+
167
+ topic = self._subscriptions[subscription_id]
168
+
169
+ if self._pubsub:
170
+ await self._pubsub.unsubscribe(topic)
171
+
172
+ # Clean up
173
+ del self._subscriptions[subscription_id]
174
+ if topic in self._handlers:
175
+ del self._handlers[topic]
176
+
177
+ async def request(self, message: Message, timeout: float = 30.0) -> Message:
178
+ """
179
+ Send request and wait for response using Redis streams.
180
+
181
+ Args:
182
+ message: Request message
183
+ timeout: Timeout in seconds
184
+
185
+ Returns:
186
+ Response message
187
+ """
188
+ if not self._client:
189
+ raise ConnectionError("Not connected to Redis")
190
+
191
+ # Generate correlation ID
192
+ if not message.correlation_id:
193
+ message.correlation_id = str(uuid.uuid4())
194
+
195
+ # Create response stream
196
+ response_key = f"response:{message.correlation_id}"
197
+
198
+ # Publish request
199
+ await self.publish(message, wait_for_confirmation=False)
200
+
201
+ # Wait for response
202
+ try:
203
+ start_time = time.monotonic()
204
+ while True:
205
+ # Check for timeout
206
+ elapsed = time.monotonic() - start_time
207
+ if elapsed > timeout:
208
+ raise TimeoutError(f"No response received within {timeout} seconds")
209
+
210
+ # Calculate remaining time for blocking
211
+ remaining_time = timeout - elapsed
212
+ block_ms = max(int(remaining_time * 1000), 100) # At least 100ms
213
+
214
+ # Read from response stream
215
+ messages = await self._client.xread(
216
+ {response_key: '0'},
217
+ count=1,
218
+ block=block_ms
219
+ )
220
+
221
+ if messages:
222
+ # Parse response
223
+ stream_name, message_list = messages[0]
224
+ message_id, data = message_list[0]
225
+ response_json = data[b'data'].decode('utf-8')
226
+
227
+ # Clean up
228
+ await self._client.delete(response_key)
229
+
230
+ return Message.model_validate_json(response_json)
231
+
232
+ await asyncio.sleep(0.1)
233
+
234
+ except asyncio.TimeoutError:
235
+ raise TimeoutError(f"No response received within {timeout} seconds")
236
+
237
+ async def get_pending_messages(self, topic: str, limit: int = 10) -> List[Message]:
238
+ """
239
+ Get pending messages from Redis stream.
240
+
241
+ Args:
242
+ topic: Topic to get messages from
243
+ limit: Maximum messages to retrieve
244
+
245
+ Returns:
246
+ List of messages
247
+ """
248
+ if not self._client:
249
+ raise ConnectionError("Not connected to Redis")
250
+
251
+ stream_key = f"stream:{topic}"
252
+
253
+ # Read from stream
254
+ messages = await self._client.xrevrange(stream_key, count=limit)
255
+
256
+ result = []
257
+ for message_id, data in messages:
258
+ message_json = data[b'data'].decode('utf-8')
259
+ msg = Message.model_validate_json(message_json)
260
+ result.append(msg)
261
+
262
+ return result
amb_core/broker.py ADDED
@@ -0,0 +1,145 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Broker adapter interface for AMB."""
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Awaitable, Callable, List, Optional
7
+
8
+ from amb_core.models import Message
9
+
10
+ # Type alias for message handler functions
11
+ MessageHandler = Callable[[Message], Awaitable[None]]
12
+
13
+
14
+ class BrokerAdapter(ABC):
15
+ """
16
+ Abstract base class for broker adapters.
17
+
18
+ This provides a broker-agnostic interface that can be implemented
19
+ for different message brokers (Redis, RabbitMQ, Kafka, etc.).
20
+ """
21
+
22
+ @abstractmethod
23
+ async def connect(self) -> None:
24
+ """
25
+ Establish connection to the broker.
26
+
27
+ Raises:
28
+ ConnectionError: If connection fails
29
+ """
30
+ pass
31
+
32
+ @abstractmethod
33
+ async def disconnect(self) -> None:
34
+ """
35
+ Close connection to the broker.
36
+ """
37
+ pass
38
+
39
+ @abstractmethod
40
+ async def publish(self, message: Message, wait_for_confirmation: bool = False) -> Optional[str]:
41
+ """
42
+ Publish a message to the broker.
43
+
44
+ Args:
45
+ message: The message to publish
46
+ wait_for_confirmation: If True, wait for broker confirmation (slower but reliable)
47
+ If False, fire and forget (faster but no guarantee)
48
+
49
+ Returns:
50
+ Optional message ID or confirmation token if wait_for_confirmation is True
51
+
52
+ Raises:
53
+ PublishError: If publishing fails
54
+ """
55
+ pass
56
+
57
+ @abstractmethod
58
+ async def subscribe(self, topic: str, handler: MessageHandler) -> str:
59
+ """
60
+ Subscribe to a topic and register a handler.
61
+
62
+ Args:
63
+ topic: Topic to subscribe to
64
+ handler: Async function to handle incoming messages
65
+
66
+ Returns:
67
+ Subscription ID that can be used to unsubscribe
68
+
69
+ Raises:
70
+ SubscriptionError: If subscription fails
71
+ """
72
+ pass
73
+
74
+ @abstractmethod
75
+ async def unsubscribe(self, subscription_id: str) -> None:
76
+ """
77
+ Unsubscribe from a topic.
78
+
79
+ Args:
80
+ subscription_id: The subscription ID returned from subscribe()
81
+
82
+ Raises:
83
+ SubscriptionError: If unsubscription fails
84
+ """
85
+ pass
86
+
87
+ @abstractmethod
88
+ async def request(self, message: Message, timeout: float = 30.0) -> Message:
89
+ """
90
+ Send a request and wait for a response (request-response pattern).
91
+
92
+ Args:
93
+ message: The request message
94
+ timeout: Maximum time to wait for response in seconds
95
+
96
+ Returns:
97
+ The response message
98
+
99
+ Raises:
100
+ TimeoutError: If no response received within timeout
101
+ RequestError: If request fails
102
+ """
103
+ pass
104
+
105
+ @abstractmethod
106
+ async def get_pending_messages(self, topic: str, limit: int = 10) -> List[Message]:
107
+ """
108
+ Get pending messages from a topic (if supported by broker).
109
+
110
+ Args:
111
+ topic: Topic to get messages from
112
+ limit: Maximum number of messages to retrieve
113
+
114
+ Returns:
115
+ List of pending messages
116
+ """
117
+ pass
118
+
119
+ def get_backpressure_stats(self, topic: Optional[str] = None):
120
+ """
121
+ Get backpressure statistics (if supported by broker).
122
+
123
+ Optional method - brokers may choose not to implement this.
124
+
125
+ Args:
126
+ topic: Optional topic to get stats for
127
+
128
+ Returns:
129
+ Statistics about backpressure events
130
+ """
131
+ return {}
132
+
133
+ def get_queue_size(self, topic: str) -> int:
134
+ """
135
+ Get current queue size for a topic (if supported by broker).
136
+
137
+ Optional method - brokers may choose not to implement this.
138
+
139
+ Args:
140
+ topic: The topic to check
141
+
142
+ Returns:
143
+ Number of messages in the queue, or 0 if not supported
144
+ """
145
+ return 0