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
amb_core/dlq.py ADDED
@@ -0,0 +1,345 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """Dead Letter Queue (DLQ) for AMB.
4
+
5
+ This module provides DLQ functionality for handling failed messages.
6
+ Messages that cannot be processed are moved to the DLQ for investigation
7
+ and potential retry.
8
+ """
9
+
10
+ from enum import Enum
11
+ from typing import Any, Dict, List, Optional, Callable, Awaitable
12
+ from datetime import datetime, timezone
13
+ from dataclasses import dataclass, field
14
+ from collections import defaultdict
15
+ import asyncio
16
+
17
+ from amb_core.models import Message
18
+
19
+
20
+ class DLQReason(str, Enum):
21
+ """Reasons why a message was moved to DLQ."""
22
+ HANDLER_ERROR = "handler_error" # Handler raised an exception
23
+ VALIDATION_ERROR = "validation_error" # Schema validation failed
24
+ EXPIRED = "expired" # Message TTL exceeded
25
+ MAX_RETRIES = "max_retries" # Maximum retry attempts exceeded
26
+ REJECTED = "rejected" # Explicitly rejected by handler
27
+ UNKNOWN = "unknown" # Unknown error
28
+
29
+
30
+ @dataclass
31
+ class DLQEntry:
32
+ """
33
+ Entry in the Dead Letter Queue.
34
+
35
+ Contains the original message plus metadata about the failure.
36
+ """
37
+ message: Message
38
+ reason: DLQReason
39
+ error_message: str
40
+ timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
41
+ retry_count: int = 0
42
+ original_topic: str = ""
43
+ stack_trace: Optional[str] = None
44
+ metadata: Dict[str, Any] = field(default_factory=dict)
45
+
46
+ def __post_init__(self):
47
+ if not self.original_topic:
48
+ self.original_topic = self.message.topic
49
+
50
+ def to_dict(self) -> Dict[str, Any]:
51
+ """Convert entry to dictionary for serialization."""
52
+ return {
53
+ "message": self.message.model_dump(),
54
+ "reason": self.reason.value,
55
+ "error_message": self.error_message,
56
+ "timestamp": self.timestamp.isoformat(),
57
+ "retry_count": self.retry_count,
58
+ "original_topic": self.original_topic,
59
+ "stack_trace": self.stack_trace,
60
+ "metadata": self.metadata
61
+ }
62
+
63
+
64
+ # Type alias for DLQ handler
65
+ DLQHandler = Callable[[DLQEntry], Awaitable[None]]
66
+
67
+
68
+ class DeadLetterQueue:
69
+ """
70
+ Dead Letter Queue for managing failed messages.
71
+
72
+ Provides storage, retrieval, and retry capabilities for messages
73
+ that failed to process successfully.
74
+
75
+ Example:
76
+ dlq = DeadLetterQueue(max_size=1000)
77
+
78
+ # Add failed message
79
+ entry = DLQEntry(
80
+ message=failed_message,
81
+ reason=DLQReason.HANDLER_ERROR,
82
+ error_message="Connection timeout"
83
+ )
84
+ await dlq.add(entry)
85
+
86
+ # Get entries for investigation
87
+ entries = await dlq.get_entries(topic="fraud.alerts")
88
+
89
+ # Retry a message
90
+ await dlq.retry(entry.message.id, retry_handler)
91
+ """
92
+
93
+ def __init__(
94
+ self,
95
+ max_size: int = 10000,
96
+ max_retries: int = 3,
97
+ on_entry_added: Optional[DLQHandler] = None
98
+ ):
99
+ """
100
+ Initialize the Dead Letter Queue.
101
+
102
+ Args:
103
+ max_size: Maximum number of entries to store
104
+ max_retries: Maximum retry attempts before permanent failure
105
+ on_entry_added: Optional callback when entry is added
106
+ """
107
+ self._entries: Dict[str, DLQEntry] = {} # message_id -> entry
108
+ self._topic_index: Dict[str, List[str]] = defaultdict(list) # topic -> [message_ids]
109
+ self._max_size = max_size
110
+ self._max_retries = max_retries
111
+ self._on_entry_added = on_entry_added
112
+ self._lock = asyncio.Lock()
113
+
114
+ async def add(self, entry: DLQEntry) -> bool:
115
+ """
116
+ Add an entry to the DLQ.
117
+
118
+ Args:
119
+ entry: DLQ entry to add
120
+
121
+ Returns:
122
+ True if added, False if DLQ is full and oldest entry was evicted
123
+ """
124
+ async with self._lock:
125
+ message_id = entry.message.id
126
+ evicted = False
127
+
128
+ # Evict oldest if at capacity
129
+ if len(self._entries) >= self._max_size and message_id not in self._entries:
130
+ oldest_id = next(iter(self._entries))
131
+ oldest_entry = self._entries[oldest_id]
132
+ del self._entries[oldest_id]
133
+ if oldest_id in self._topic_index.get(oldest_entry.original_topic, []):
134
+ self._topic_index[oldest_entry.original_topic].remove(oldest_id)
135
+ evicted = True
136
+
137
+ # Add or update entry
138
+ self._entries[message_id] = entry
139
+ if message_id not in self._topic_index[entry.original_topic]:
140
+ self._topic_index[entry.original_topic].append(message_id)
141
+
142
+ # Call handler outside lock
143
+ if self._on_entry_added:
144
+ try:
145
+ await self._on_entry_added(entry)
146
+ except Exception:
147
+ pass # Don't fail on handler errors
148
+
149
+ return not evicted
150
+
151
+ async def get(self, message_id: str) -> Optional[DLQEntry]:
152
+ """
153
+ Get a specific DLQ entry by message ID.
154
+
155
+ Args:
156
+ message_id: The message ID to look up
157
+
158
+ Returns:
159
+ DLQ entry or None if not found
160
+ """
161
+ return self._entries.get(message_id)
162
+
163
+ async def get_entries(
164
+ self,
165
+ topic: Optional[str] = None,
166
+ reason: Optional[DLQReason] = None,
167
+ limit: int = 100,
168
+ offset: int = 0
169
+ ) -> List[DLQEntry]:
170
+ """
171
+ Get DLQ entries with optional filtering.
172
+
173
+ Args:
174
+ topic: Filter by original topic
175
+ reason: Filter by failure reason
176
+ limit: Maximum entries to return
177
+ offset: Number of entries to skip
178
+
179
+ Returns:
180
+ List of DLQ entries
181
+ """
182
+ if topic:
183
+ message_ids = self._topic_index.get(topic, [])
184
+ entries = [self._entries[mid] for mid in message_ids if mid in self._entries]
185
+ else:
186
+ entries = list(self._entries.values())
187
+
188
+ # Filter by reason if specified
189
+ if reason:
190
+ entries = [e for e in entries if e.reason == reason]
191
+
192
+ # Sort by timestamp (newest first)
193
+ entries.sort(key=lambda e: e.timestamp, reverse=True)
194
+
195
+ # Apply pagination
196
+ return entries[offset:offset + limit]
197
+
198
+ async def remove(self, message_id: str) -> bool:
199
+ """
200
+ Remove an entry from the DLQ.
201
+
202
+ Args:
203
+ message_id: Message ID to remove
204
+
205
+ Returns:
206
+ True if removed, False if not found
207
+ """
208
+ async with self._lock:
209
+ if message_id not in self._entries:
210
+ return False
211
+
212
+ entry = self._entries[message_id]
213
+ del self._entries[message_id]
214
+
215
+ if message_id in self._topic_index.get(entry.original_topic, []):
216
+ self._topic_index[entry.original_topic].remove(message_id)
217
+
218
+ return True
219
+
220
+ async def retry(
221
+ self,
222
+ message_id: str,
223
+ handler: Callable[[Message], Awaitable[None]]
224
+ ) -> bool:
225
+ """
226
+ Retry processing a failed message.
227
+
228
+ Args:
229
+ message_id: Message ID to retry
230
+ handler: Handler function to process the message
231
+
232
+ Returns:
233
+ True if successful, False if failed or not found
234
+
235
+ Raises:
236
+ ValueError: If max retries exceeded
237
+ """
238
+ entry = await self.get(message_id)
239
+ if not entry:
240
+ return False
241
+
242
+ if entry.retry_count >= self._max_retries:
243
+ raise ValueError(
244
+ f"Message {message_id} has exceeded max retries ({self._max_retries})"
245
+ )
246
+
247
+ try:
248
+ await handler(entry.message)
249
+ await self.remove(message_id)
250
+ return True
251
+ except Exception as e:
252
+ # Update retry count
253
+ entry.retry_count += 1
254
+ entry.error_message = str(e)
255
+ if entry.retry_count >= self._max_retries:
256
+ entry.reason = DLQReason.MAX_RETRIES
257
+ return False
258
+
259
+ async def retry_all(
260
+ self,
261
+ handler: Callable[[Message], Awaitable[None]],
262
+ topic: Optional[str] = None
263
+ ) -> Dict[str, bool]:
264
+ """
265
+ Retry all messages in the DLQ.
266
+
267
+ Args:
268
+ handler: Handler function to process messages
269
+ topic: Optional topic filter
270
+
271
+ Returns:
272
+ Dict mapping message IDs to success/failure
273
+ """
274
+ entries = await self.get_entries(topic=topic, limit=self._max_size)
275
+ results = {}
276
+
277
+ for entry in entries:
278
+ try:
279
+ results[entry.message.id] = await self.retry(entry.message.id, handler)
280
+ except ValueError:
281
+ results[entry.message.id] = False
282
+
283
+ return results
284
+
285
+ async def clear(self, topic: Optional[str] = None) -> int:
286
+ """
287
+ Clear entries from the DLQ.
288
+
289
+ Args:
290
+ topic: Optional topic filter (clears all if None)
291
+
292
+ Returns:
293
+ Number of entries cleared
294
+ """
295
+ async with self._lock:
296
+ if topic:
297
+ message_ids = self._topic_index.get(topic, []).copy()
298
+ for mid in message_ids:
299
+ if mid in self._entries:
300
+ del self._entries[mid]
301
+ self._topic_index[topic].clear()
302
+ return len(message_ids)
303
+ else:
304
+ count = len(self._entries)
305
+ self._entries.clear()
306
+ self._topic_index.clear()
307
+ return count
308
+
309
+ def __len__(self) -> int:
310
+ """Get number of entries in DLQ."""
311
+ return len(self._entries)
312
+
313
+ @property
314
+ def size(self) -> int:
315
+ """Get current size of DLQ."""
316
+ return len(self._entries)
317
+
318
+ @property
319
+ def max_size(self) -> int:
320
+ """Get maximum size of DLQ."""
321
+ return self._max_size
322
+
323
+ async def get_stats(self) -> Dict[str, Any]:
324
+ """
325
+ Get DLQ statistics.
326
+
327
+ Returns:
328
+ Dict with DLQ statistics
329
+ """
330
+ entries = list(self._entries.values())
331
+
332
+ reason_counts = defaultdict(int)
333
+ topic_counts = defaultdict(int)
334
+
335
+ for entry in entries:
336
+ reason_counts[entry.reason.value] += 1
337
+ topic_counts[entry.original_topic] += 1
338
+
339
+ return {
340
+ "total_entries": len(entries),
341
+ "max_size": self._max_size,
342
+ "by_reason": dict(reason_counts),
343
+ "by_topic": dict(topic_counts),
344
+ "max_retries_setting": self._max_retries
345
+ }