nat-engine 1__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 (299) hide show
  1. mannf/__init__.py +33 -0
  2. mannf/__main__.py +10 -0
  3. mannf/_version.py +8 -0
  4. mannf/agents/__init__.py +7 -0
  5. mannf/agents/analyzer_agent.py +9 -0
  6. mannf/agents/base.py +9 -0
  7. mannf/agents/bdi_agent.py +9 -0
  8. mannf/agents/belief_state.py +9 -0
  9. mannf/agents/coordinator_agent.py +9 -0
  10. mannf/agents/executor_agent.py +9 -0
  11. mannf/agents/monitor_agent.py +9 -0
  12. mannf/agents/oracle_agent.py +9 -0
  13. mannf/agents/planner_agent.py +9 -0
  14. mannf/agents/test_agent.py +9 -0
  15. mannf/anomaly/__init__.py +7 -0
  16. mannf/anomaly/enhanced_detector.py +9 -0
  17. mannf/cli.py +9 -0
  18. mannf/core/__init__.py +26 -0
  19. mannf/core/agents/__init__.py +52 -0
  20. mannf/core/agents/accessibility_scanner_agent.py +245 -0
  21. mannf/core/agents/analyzer_agent.py +224 -0
  22. mannf/core/agents/autonomous_loop_agent.py +1086 -0
  23. mannf/core/agents/autonomous_loop_models.py +62 -0
  24. mannf/core/agents/autonomous_run_differ.py +427 -0
  25. mannf/core/agents/base.py +128 -0
  26. mannf/core/agents/bdi_agent.py +330 -0
  27. mannf/core/agents/belief_state.py +202 -0
  28. mannf/core/agents/browser_coordinator_agent.py +224 -0
  29. mannf/core/agents/browser_executor_agent.py +410 -0
  30. mannf/core/agents/coordinator_agent.py +262 -0
  31. mannf/core/agents/executor_agent.py +222 -0
  32. mannf/core/agents/monitor_agent.py +188 -0
  33. mannf/core/agents/oracle_agent.py +150 -0
  34. mannf/core/agents/performance_testing_agent.py +279 -0
  35. mannf/core/agents/planner_agent.py +128 -0
  36. mannf/core/agents/test_agent.py +249 -0
  37. mannf/core/agents/visual_regression_agent.py +311 -0
  38. mannf/core/agents/web_crawler_agent.py +510 -0
  39. mannf/core/agents/worker_pool.py +366 -0
  40. mannf/core/anomaly/__init__.py +14 -0
  41. mannf/core/anomaly/enhanced_detector.py +541 -0
  42. mannf/core/browser/__init__.py +63 -0
  43. mannf/core/browser/accessibility_scanner.py +424 -0
  44. mannf/core/browser/discovery_model.py +178 -0
  45. mannf/core/browser/dom_snapshot.py +349 -0
  46. mannf/core/browser/ingestor_bridge.py +371 -0
  47. mannf/core/browser/performance_metrics.py +217 -0
  48. mannf/core/browser/reflection_analyzer.py +442 -0
  49. mannf/core/browser/scenario_generator.py +1100 -0
  50. mannf/core/browser/security_scenario_generator.py +695 -0
  51. mannf/core/browser/visual_comparer.py +159 -0
  52. mannf/core/diagnostics/__init__.py +28 -0
  53. mannf/core/diagnostics/failure_clusterer.py +211 -0
  54. mannf/core/diagnostics/flake_detector.py +233 -0
  55. mannf/core/diagnostics/root_cause_analyzer.py +273 -0
  56. mannf/core/distributed/__init__.py +16 -0
  57. mannf/core/distributed/endpoint.py +139 -0
  58. mannf/core/distributed/system_under_test.py +207 -0
  59. mannf/core/functional_orchestrator.py +428 -0
  60. mannf/core/messaging/__init__.py +11 -0
  61. mannf/core/messaging/bus.py +113 -0
  62. mannf/core/messaging/messages.py +89 -0
  63. mannf/core/nat_orchestrator.py +342 -0
  64. mannf/core/neural/__init__.py +183 -0
  65. mannf/core/orchestrator.py +272 -0
  66. mannf/core/prioritization/__init__.py +17 -0
  67. mannf/core/prioritization/adaptive_controller.py +509 -0
  68. mannf/core/prioritization/belief_prioritizer.py +231 -0
  69. mannf/core/prioritization/risk_scorer.py +430 -0
  70. mannf/core/reporting/__init__.py +12 -0
  71. mannf/core/reporting/unified_report.py +664 -0
  72. mannf/core/testing/__init__.py +17 -0
  73. mannf/core/testing/adaptive_controller.py +149 -0
  74. mannf/core/testing/models.py +179 -0
  75. mannf/core/validation/__init__.py +10 -0
  76. mannf/core/validation/self_validation_runner.py +180 -0
  77. mannf/dashboard/__init__.py +7 -0
  78. mannf/dashboard/app.py +9 -0
  79. mannf/dashboard/models.py +9 -0
  80. mannf/dashboard/static/index.html +2538 -0
  81. mannf/dashboard/telemetry.py +9 -0
  82. mannf/distributed/__init__.py +7 -0
  83. mannf/distributed/endpoint.py +9 -0
  84. mannf/distributed/system_under_test.py +9 -0
  85. mannf/healing/__init__.py +7 -0
  86. mannf/healing/graphql_schema_diff.py +9 -0
  87. mannf/healing/healer.py +9 -0
  88. mannf/healing/models.py +9 -0
  89. mannf/healing/schema_diff.py +9 -0
  90. mannf/integrations/__init__.py +7 -0
  91. mannf/integrations/auth.py +9 -0
  92. mannf/integrations/graphql_parser.py +9 -0
  93. mannf/integrations/graphql_sut.py +9 -0
  94. mannf/integrations/http_sut.py +9 -0
  95. mannf/integrations/openapi_parser.py +9 -0
  96. mannf/integrations/postman_parser.py +9 -0
  97. mannf/llm/__init__.py +7 -0
  98. mannf/llm/anthropic_provider.py +9 -0
  99. mannf/llm/base.py +9 -0
  100. mannf/llm/config.py +9 -0
  101. mannf/llm/factory.py +9 -0
  102. mannf/llm/openai_provider.py +9 -0
  103. mannf/llm/prompts.py +9 -0
  104. mannf/messaging/__init__.py +7 -0
  105. mannf/messaging/bus.py +9 -0
  106. mannf/messaging/messages.py +9 -0
  107. mannf/nat_orchestrator.py +9 -0
  108. mannf/neural/__init__.py +7 -0
  109. mannf/orchestrator.py +9 -0
  110. mannf/prioritization/__init__.py +7 -0
  111. mannf/prioritization/adaptive_controller.py +9 -0
  112. mannf/prioritization/belief_prioritizer.py +9 -0
  113. mannf/prioritization/risk_scorer.py +9 -0
  114. mannf/product/__init__.py +29 -0
  115. mannf/product/admin/__init__.py +3 -0
  116. mannf/product/admin/routes.py +514 -0
  117. mannf/product/auth/__init__.py +5 -0
  118. mannf/product/auth/saml.py +212 -0
  119. mannf/product/billing/__init__.py +5 -0
  120. mannf/product/billing/audit.py +160 -0
  121. mannf/product/billing/feature_gates.py +180 -0
  122. mannf/product/billing/metering.py +179 -0
  123. mannf/product/billing/notifications.py +181 -0
  124. mannf/product/billing/plans.py +133 -0
  125. mannf/product/billing/rate_limits.py +35 -0
  126. mannf/product/billing/stripe_billing.py +906 -0
  127. mannf/product/billing/tenant_auth.py +233 -0
  128. mannf/product/billing/tenant_manager.py +873 -0
  129. mannf/product/cli.py +3900 -0
  130. mannf/product/cli_admin.py +408 -0
  131. mannf/product/dashboard/__init__.py +61 -0
  132. mannf/product/dashboard/app.py +3567 -0
  133. mannf/product/dashboard/models.py +460 -0
  134. mannf/product/dashboard/static/index.html +6347 -0
  135. mannf/product/dashboard/static/manifest.json +25 -0
  136. mannf/product/dashboard/static/pwa-icon-192.png +0 -0
  137. mannf/product/dashboard/static/pwa-icon-512.png +0 -0
  138. mannf/product/dashboard/static/sw.js +64 -0
  139. mannf/product/dashboard/telemetry.py +547 -0
  140. mannf/product/database.py +145 -0
  141. mannf/product/demo.py +844 -0
  142. mannf/product/doctor.py +509 -0
  143. mannf/product/exporters/__init__.py +65 -0
  144. mannf/product/exporters/azuredevops_exporter.py +257 -0
  145. mannf/product/exporters/base.py +307 -0
  146. mannf/product/exporters/bugzilla_exporter.py +200 -0
  147. mannf/product/exporters/dedup.py +275 -0
  148. mannf/product/exporters/finding_adapter.py +216 -0
  149. mannf/product/exporters/github_exporter.py +197 -0
  150. mannf/product/exporters/gitlab_exporter.py +215 -0
  151. mannf/product/exporters/jira_exporter.py +180 -0
  152. mannf/product/exporters/linear_exporter.py +195 -0
  153. mannf/product/exporters/loader.py +233 -0
  154. mannf/product/exporters/pagerduty_exporter.py +363 -0
  155. mannf/product/exporters/sentry_exporter.py +322 -0
  156. mannf/product/exporters/servicenow_exporter.py +240 -0
  157. mannf/product/exporters/shortcut_exporter.py +231 -0
  158. mannf/product/exporters/webhook_exporter.py +383 -0
  159. mannf/product/formatters/__init__.py +18 -0
  160. mannf/product/formatters/allure_formatter.py +161 -0
  161. mannf/product/formatters/ctrf_formatter.py +149 -0
  162. mannf/product/healing/__init__.py +30 -0
  163. mannf/product/healing/graphql_schema_diff.py +152 -0
  164. mannf/product/healing/healer.py +141 -0
  165. mannf/product/healing/models.py +175 -0
  166. mannf/product/healing/schema_diff.py +251 -0
  167. mannf/product/ingestors/__init__.py +77 -0
  168. mannf/product/ingestors/base.py +256 -0
  169. mannf/product/ingestors/bgstm_ingestor.py +764 -0
  170. mannf/product/ingestors/curl_ingestor.py +1019 -0
  171. mannf/product/ingestors/cypress_ingestor.py +487 -0
  172. mannf/product/ingestors/gherkin_ingestor.py +967 -0
  173. mannf/product/ingestors/graphql_ingestor.py +845 -0
  174. mannf/product/ingestors/grpc_ingestor.py +591 -0
  175. mannf/product/ingestors/har_ingestor.py +976 -0
  176. mannf/product/ingestors/loader.py +284 -0
  177. mannf/product/ingestors/models.py +146 -0
  178. mannf/product/ingestors/openapi_ingestor.py +606 -0
  179. mannf/product/ingestors/playwright_ingestor.py +449 -0
  180. mannf/product/ingestors/postman_ingestor.py +631 -0
  181. mannf/product/ingestors/traffic_ingestor.py +679 -0
  182. mannf/product/ingestors/websocket_ingestor.py +526 -0
  183. mannf/product/integrations/__init__.py +21 -0
  184. mannf/product/integrations/auth.py +190 -0
  185. mannf/product/integrations/graphql_parser.py +436 -0
  186. mannf/product/integrations/graphql_sut.py +247 -0
  187. mannf/product/integrations/grpc_sut.py +469 -0
  188. mannf/product/integrations/http_sut.py +237 -0
  189. mannf/product/integrations/kafka_adapter.py +342 -0
  190. mannf/product/integrations/openapi_parser.py +513 -0
  191. mannf/product/integrations/postman_parser.py +467 -0
  192. mannf/product/integrations/webhook_receiver.py +344 -0
  193. mannf/product/integrations/websocket_sut.py +434 -0
  194. mannf/product/llm/__init__.py +25 -0
  195. mannf/product/llm/anthropic_provider.py +94 -0
  196. mannf/product/llm/base.py +267 -0
  197. mannf/product/llm/config.py +48 -0
  198. mannf/product/llm/factory.py +42 -0
  199. mannf/product/llm/openai_provider.py +93 -0
  200. mannf/product/llm/prompts.py +403 -0
  201. mannf/product/llm/root_cause_service.py +311 -0
  202. mannf/product/llm/test_plan_models.py +78 -0
  203. mannf/product/metrics.py +149 -0
  204. mannf/product/middleware/__init__.py +3 -0
  205. mannf/product/middleware/audit_middleware.py +112 -0
  206. mannf/product/middleware/tenant_isolation.py +114 -0
  207. mannf/product/models.py +347 -0
  208. mannf/product/notifications/__init__.py +24 -0
  209. mannf/product/notifications/dispatcher.py +411 -0
  210. mannf/product/onboarding.py +190 -0
  211. mannf/product/orchestration/__init__.py +39 -0
  212. mannf/product/orchestration/ingest_scan_orchestrator.py +339 -0
  213. mannf/product/orchestration/pipeline.py +401 -0
  214. mannf/product/orchestrator.py +987 -0
  215. mannf/product/orchestrator_models.py +269 -0
  216. mannf/product/regression/__init__.py +36 -0
  217. mannf/product/regression/differ.py +172 -0
  218. mannf/product/regression/masking.py +100 -0
  219. mannf/product/regression/models.py +232 -0
  220. mannf/product/regression/recorder.py +124 -0
  221. mannf/product/regression/replayer.py +168 -0
  222. mannf/product/reports/__init__.py +10 -0
  223. mannf/product/reports/pdf.py +132 -0
  224. mannf/product/scheduling/__init__.py +57 -0
  225. mannf/product/scheduling/cron_utils.py +251 -0
  226. mannf/product/scheduling/engine.py +473 -0
  227. mannf/product/scheduling/models.py +86 -0
  228. mannf/product/scheduling/queue.py +894 -0
  229. mannf/product/scheduling/store.py +235 -0
  230. mannf/product/security/__init__.py +21 -0
  231. mannf/product/security/belief_guided.py +143 -0
  232. mannf/product/security/checks/__init__.py +55 -0
  233. mannf/product/security/checks/base.py +69 -0
  234. mannf/product/security/checks/bfla.py +77 -0
  235. mannf/product/security/checks/bola.py +77 -0
  236. mannf/product/security/checks/bopla.py +80 -0
  237. mannf/product/security/checks/broken_auth.py +86 -0
  238. mannf/product/security/checks/graphql_security.py +299 -0
  239. mannf/product/security/checks/inventory.py +70 -0
  240. mannf/product/security/checks/misconfig.py +158 -0
  241. mannf/product/security/checks/resource_consumption.py +70 -0
  242. mannf/product/security/checks/sensitive_flows.py +80 -0
  243. mannf/product/security/checks/ssrf.py +101 -0
  244. mannf/product/security/checks/unsafe_consumption.py +120 -0
  245. mannf/product/security/models.py +92 -0
  246. mannf/product/security/plugin_loader.py +182 -0
  247. mannf/product/security/reporter.py +92 -0
  248. mannf/product/security/scanner.py +183 -0
  249. mannf/product/server.py +6220 -0
  250. mannf/product/setup_wizard.py +873 -0
  251. mannf/product/status.py +404 -0
  252. mannf/product/storage/__init__.py +10 -0
  253. mannf/product/storage/artifact_store.py +343 -0
  254. mannf/product/telemetry.py +300 -0
  255. mannf/product/uninstall.py +169 -0
  256. mannf/product/upgrade.py +139 -0
  257. mannf/product/weights/__init__.py +13 -0
  258. mannf/product/weights/blob_store.py +299 -0
  259. mannf/product/weights/factory.py +42 -0
  260. mannf/product/weights/registry.py +159 -0
  261. mannf/product/weights/store.py +210 -0
  262. mannf/regression/__init__.py +7 -0
  263. mannf/regression/differ.py +9 -0
  264. mannf/regression/masking.py +9 -0
  265. mannf/regression/models.py +9 -0
  266. mannf/regression/recorder.py +9 -0
  267. mannf/regression/replayer.py +9 -0
  268. mannf/security/__init__.py +7 -0
  269. mannf/security/belief_guided.py +9 -0
  270. mannf/security/checks/__init__.py +7 -0
  271. mannf/security/checks/base.py +9 -0
  272. mannf/security/checks/bfla.py +9 -0
  273. mannf/security/checks/bola.py +9 -0
  274. mannf/security/checks/bopla.py +9 -0
  275. mannf/security/checks/broken_auth.py +9 -0
  276. mannf/security/checks/graphql_security.py +9 -0
  277. mannf/security/checks/inventory.py +9 -0
  278. mannf/security/checks/misconfig.py +9 -0
  279. mannf/security/checks/resource_consumption.py +9 -0
  280. mannf/security/checks/sensitive_flows.py +9 -0
  281. mannf/security/checks/ssrf.py +9 -0
  282. mannf/security/checks/unsafe_consumption.py +9 -0
  283. mannf/security/models.py +9 -0
  284. mannf/security/reporter.py +9 -0
  285. mannf/security/scanner.py +9 -0
  286. mannf/server.py +9 -0
  287. mannf/testing/__init__.py +7 -0
  288. mannf/testing/adaptive_controller.py +9 -0
  289. mannf/testing/models.py +9 -0
  290. mannf/weights/__init__.py +7 -0
  291. mannf/weights/registry.py +9 -0
  292. mannf/weights/store.py +9 -0
  293. nat_engine-1.dist-info/METADATA +555 -0
  294. nat_engine-1.dist-info/RECORD +299 -0
  295. nat_engine-1.dist-info/WHEEL +5 -0
  296. nat_engine-1.dist-info/entry_points.txt +4 -0
  297. nat_engine-1.dist-info/licenses/LICENSE +651 -0
  298. nat_engine-1.dist-info/licenses/NOTICE +178 -0
  299. nat_engine-1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,272 @@
1
+ # Copyright (C) 2026 Brad Guider
2
+ # This file is part of NAT (Neural Agent Testing Framework).
3
+ # Licensed under the AGPL-3.0. See LICENSE for details.
4
+ # Commercial licensing available — see COMMERCIAL_LICENSE.md.
5
+
6
+ """Orchestrator – the central coordinator of the MANNF framework.
7
+
8
+ The :class:`Orchestrator` manages:
9
+ 1. Startup / shutdown of all agents.
10
+ 2. The test-execution loop: request test cases → execute → record results.
11
+ 3. The adaptive feedback loop: update the :class:`AdaptiveController` after
12
+ each test and broadcast strategy updates to agents.
13
+ 4. Progress logging and final report generation.
14
+
15
+ Usage::
16
+
17
+ from mannf import Orchestrator
18
+ from mannf.core.distributed import MockDistributedSystem
19
+
20
+ sut = MockDistributedSystem(seed=42)
21
+ orch = Orchestrator(sut, num_test_agents=3, max_steps=200)
22
+ await orch.run()
23
+ report = orch.report()
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import asyncio
29
+ import logging
30
+ import time
31
+ from typing import Any, Dict, List, Optional
32
+
33
+ from mannf.core.agents.monitor_agent import MonitorAgent
34
+ from mannf.core.agents.oracle_agent import OracleAgent
35
+ from mannf.core.agents.test_agent import TestAgent
36
+ from mannf.core.distributed.system_under_test import SystemUnderTest
37
+ from mannf.core.messaging.bus import MessageBus
38
+ from mannf.core.messaging.messages import Message, MessageType
39
+ from mannf.core.testing.adaptive_controller import AdaptiveController
40
+ from mannf.core.testing.models import TestCase, TestResult, TestSuite
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class Orchestrator:
46
+ """Coordinates all agents and drives the adaptive testing loop.
47
+
48
+ Parameters
49
+ ----------
50
+ sut:
51
+ The system under test (real or mock).
52
+ num_test_agents:
53
+ How many parallel :class:`TestAgent` instances to run.
54
+ max_steps:
55
+ Total number of test-execution steps before stopping.
56
+ suite_name:
57
+ Name used in the test suite and final report.
58
+ log_interval:
59
+ Log a progress summary every *log_interval* steps.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ sut: SystemUnderTest,
65
+ num_test_agents: int = 2,
66
+ max_steps: int = 100,
67
+ suite_name: str = "Adaptive Test Run",
68
+ log_interval: int = 10,
69
+ ) -> None:
70
+ self.sut = sut
71
+ self.max_steps = max_steps
72
+ self.log_interval = log_interval
73
+
74
+ self._bus = MessageBus()
75
+ self._suite = TestSuite(name=suite_name)
76
+ self._controller = AdaptiveController()
77
+
78
+ service_names = sut.registry.all()
79
+ svc_names = [e.name for e in service_names]
80
+
81
+ # Create agents
82
+ self._test_agents: List[TestAgent] = [
83
+ TestAgent(f"test-agent-{i}", self._bus, svc_names)
84
+ for i in range(num_test_agents)
85
+ ]
86
+ self._monitor = MonitorAgent("monitor-agent", self._bus)
87
+ self._oracle = OracleAgent("oracle-agent", self._bus)
88
+
89
+ self._step = 0
90
+ self._start_time: Optional[float] = None
91
+
92
+ # ------------------------------------------------------------------
93
+ # Main entry point
94
+ # ------------------------------------------------------------------
95
+
96
+ async def run(self) -> "TestSuite":
97
+ """Execute the full adaptive testing loop.
98
+
99
+ Agents run as concurrent asyncio tasks so they process messages from
100
+ the bus while the orchestrator drives the test-execution loop.
101
+
102
+ Returns the completed :class:`TestSuite`.
103
+ """
104
+ self._start_time = time.monotonic()
105
+ logger.info("Orchestrator: starting with max_steps=%d", self.max_steps)
106
+
107
+ all_agents = [self._monitor, self._oracle] + self._test_agents
108
+
109
+ # Launch each agent as an independent asyncio task (their run() loops
110
+ # process bus messages concurrently with the test-execution loop).
111
+ agent_tasks = [asyncio.create_task(agent.run()) for agent in all_agents]
112
+
113
+ # Give agents a moment to complete their start() subscriptions
114
+ await asyncio.sleep(0)
115
+
116
+ try:
117
+ await self._run_loop()
118
+ finally:
119
+ # Signal all agents to stop and wait for clean shutdown
120
+ for agent in all_agents:
121
+ agent._running = False
122
+ await asyncio.gather(*agent_tasks, return_exceptions=True)
123
+
124
+ elapsed = time.monotonic() - (self._start_time or 0)
125
+ logger.info(
126
+ "Orchestrator: finished %d steps in %.1f s", self._step, elapsed
127
+ )
128
+ return self._suite
129
+
130
+ # ------------------------------------------------------------------
131
+ # Test-execution loop
132
+ # ------------------------------------------------------------------
133
+
134
+ async def _run_loop(self) -> None:
135
+ while self._step < self.max_steps:
136
+ # 1. Pick a test agent (round-robin)
137
+ agent = self._test_agents[self._step % len(self._test_agents)]
138
+
139
+ # 2. Request a test case
140
+ test_case = await self._request_test_case(agent)
141
+ if test_case is None:
142
+ await asyncio.sleep(0.01)
143
+ continue
144
+
145
+ # 3. Apply adaptive selection from a small candidate pool
146
+ candidates = [test_case]
147
+ for _ in range(2):
148
+ extra = await self._request_test_case(
149
+ self._test_agents[(self._step + 1) % len(self._test_agents)]
150
+ )
151
+ if extra:
152
+ candidates.append(extra)
153
+
154
+ selected = self._controller.select(candidates) or test_case
155
+
156
+ # 4. Execute against the SUT
157
+ result = await self.sut.execute(selected)
158
+ result.agent_id = agent.agent_id
159
+
160
+ # 5. Record result
161
+ self._suite.add_case(selected)
162
+ self._suite.record_result(result)
163
+
164
+ # 6. Broadcast result to monitor & oracle (single unified message)
165
+ await self._bus.publish(
166
+ Message(
167
+ MessageType.TEST_RESULT,
168
+ "orchestrator",
169
+ {"result": result, "test_case": selected},
170
+ )
171
+ )
172
+
173
+ # 7. Update the adaptive controller
174
+ self._controller.update(selected, result)
175
+
176
+ # 8. Send feedback to the test agents
177
+ feedback = self._build_feedback(selected, result)
178
+ await self._bus.publish(
179
+ Message(MessageType.FEEDBACK, "orchestrator", feedback)
180
+ )
181
+
182
+ self._step += 1
183
+
184
+ if self._step % self.log_interval == 0:
185
+ self._log_progress()
186
+
187
+ # Yield control to allow agent background tasks to process messages
188
+ await asyncio.sleep(0)
189
+
190
+ # ------------------------------------------------------------------
191
+ # Helpers
192
+ # ------------------------------------------------------------------
193
+
194
+ async def _request_test_case(self, agent: TestAgent) -> Optional[TestCase]:
195
+ """Ask an agent to generate a test case synchronously (in-process)."""
196
+ context = {
197
+ "pass_rate": self._suite.pass_rate,
198
+ "defect_detection_rate": self._suite.defect_detection_rate,
199
+ "step_count": self._step,
200
+ }
201
+ req = Message(MessageType.TEST_REQUEST, "orchestrator", context)
202
+ # Call the agent's generation logic directly for synchronous behaviour
203
+ test_case = agent._generate_test_case(context=context)
204
+ return test_case
205
+
206
+ def _build_feedback(
207
+ self, test_case: TestCase, result: TestResult
208
+ ) -> Dict[str, Any]:
209
+ """Build a feedback payload for the test agents."""
210
+ reward = result.reward_signal()
211
+ features = test_case.feature_vector().tolist()
212
+ # Target: bias towards the service that produced the reward
213
+ target = [0.0] * max(len(self._test_agents), 1)
214
+ return {
215
+ "features": features,
216
+ "target": [reward],
217
+ "reward": reward,
218
+ "test_case_id": test_case.id,
219
+ }
220
+
221
+ def _log_progress(self) -> None:
222
+ summary = self._suite.summary()
223
+ elapsed = time.monotonic() - (self._start_time or time.monotonic())
224
+ logger.info(
225
+ "Step %4d/%d | passed=%d failed=%d errored=%d "
226
+ "| defect_rate=%.1f%% | ε=%.3f | elapsed=%.1fs",
227
+ self._step,
228
+ self.max_steps,
229
+ summary["passed"],
230
+ summary["failed"],
231
+ summary["errored"],
232
+ summary["defect_detection_rate"] * 100,
233
+ self._controller.epsilon,
234
+ elapsed,
235
+ )
236
+
237
+ # ------------------------------------------------------------------
238
+ # Reporting
239
+ # ------------------------------------------------------------------
240
+
241
+ def report(self) -> Dict[str, Any]:
242
+ """Return a structured summary of the completed test run."""
243
+ elapsed = (
244
+ time.monotonic() - self._start_time if self._start_time else 0.0
245
+ )
246
+ suite_summary = self._suite.summary()
247
+ monitor_metrics = self._monitor.metrics
248
+
249
+ return {
250
+ "suite": suite_summary,
251
+ "monitor": monitor_metrics,
252
+ "oracle": {
253
+ "verdict_count": self._oracle.verdict_count,
254
+ "defect_count": self._oracle.defect_count,
255
+ "defect_rate": round(self._oracle.defect_rate, 4),
256
+ },
257
+ "adaptive_controller": {
258
+ "step_count": self._controller.step_count,
259
+ "epsilon": round(self._controller.epsilon, 4),
260
+ "average_reward": round(self._controller.average_reward, 4),
261
+ },
262
+ "agent_count": len(self._test_agents) + 2, # +monitor +oracle
263
+ "elapsed_seconds": round(elapsed, 2),
264
+ }
265
+
266
+ @property
267
+ def suite(self) -> TestSuite:
268
+ return self._suite
269
+
270
+ @property
271
+ def controller(self) -> AdaptiveController:
272
+ return self._controller
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2026 Brad Guider
2
+ # This file is part of NAT (Neural Agent Testing Framework).
3
+ # Licensed under the AGPL-3.0. See LICENSE for details.
4
+ # Commercial licensing available — see COMMERCIAL_LICENSE.md.
5
+
6
+ """Prioritization sub-package for belief-driven test ordering and risk scoring."""
7
+
8
+ from mannf.core.prioritization.adaptive_controller import AdaptiveController
9
+ from mannf.core.prioritization.belief_prioritizer import BeliefPrioritizer, PrioritizationStrategy
10
+ from mannf.core.prioritization.risk_scorer import RiskScorer
11
+
12
+ __all__ = [
13
+ "BeliefPrioritizer",
14
+ "PrioritizationStrategy",
15
+ "RiskScorer",
16
+ "AdaptiveController",
17
+ ]