devsper 2.1.6__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 (375) hide show
  1. devsper/__init__.py +14 -0
  2. devsper/agents/a2a/__init__.py +27 -0
  3. devsper/agents/a2a/client.py +126 -0
  4. devsper/agents/a2a/discovery.py +24 -0
  5. devsper/agents/a2a/server.py +128 -0
  6. devsper/agents/a2a/tool_adapter.py +68 -0
  7. devsper/agents/a2a/types.py +49 -0
  8. devsper/agents/agent.py +602 -0
  9. devsper/agents/critic.py +80 -0
  10. devsper/agents/message_bus.py +124 -0
  11. devsper/agents/roles.py +181 -0
  12. devsper/agents/run_agent.py +78 -0
  13. devsper/analytics/__init__.py +5 -0
  14. devsper/analytics/tool_analytics.py +78 -0
  15. devsper/audit/__init__.py +5 -0
  16. devsper/audit/logger.py +214 -0
  17. devsper/bus/__init__.py +29 -0
  18. devsper/bus/backends/__init__.py +5 -0
  19. devsper/bus/backends/base.py +38 -0
  20. devsper/bus/backends/memory.py +55 -0
  21. devsper/bus/backends/redis.py +146 -0
  22. devsper/bus/message.py +56 -0
  23. devsper/bus/schema_version.py +3 -0
  24. devsper/bus/topics.py +19 -0
  25. devsper/cache/__init__.py +6 -0
  26. devsper/cache/embedding_index.py +98 -0
  27. devsper/cache/hashing.py +24 -0
  28. devsper/cache/store.py +153 -0
  29. devsper/cache/task_cache.py +191 -0
  30. devsper/cli/__init__.py +6 -0
  31. devsper/cli/commands/reg.py +733 -0
  32. devsper/cli/github_oauth.py +157 -0
  33. devsper/cli/init.py +637 -0
  34. devsper/cli/main.py +2956 -0
  35. devsper/cli/run_progress.py +103 -0
  36. devsper/cli/ui/__init__.py +65 -0
  37. devsper/cli/ui/components.py +94 -0
  38. devsper/cli/ui/errors.py +104 -0
  39. devsper/cli/ui/logging.py +120 -0
  40. devsper/cli/ui/onboarding.py +102 -0
  41. devsper/cli/ui/progress.py +43 -0
  42. devsper/cli/ui/run_view.py +308 -0
  43. devsper/cli/ui/theme.py +40 -0
  44. devsper/cluster/__init__.py +29 -0
  45. devsper/cluster/election.py +84 -0
  46. devsper/cluster/local.py +97 -0
  47. devsper/cluster/node_info.py +77 -0
  48. devsper/cluster/registry.py +71 -0
  49. devsper/cluster/router.py +117 -0
  50. devsper/cluster/state_backend.py +105 -0
  51. devsper/compliance/__init__.py +5 -0
  52. devsper/compliance/pii.py +147 -0
  53. devsper/config/__init__.py +52 -0
  54. devsper/config/config_loader.py +121 -0
  55. devsper/config/defaults.py +77 -0
  56. devsper/config/resolver.py +342 -0
  57. devsper/config/schema.py +237 -0
  58. devsper/credentials/__init__.py +19 -0
  59. devsper/credentials/cli.py +197 -0
  60. devsper/credentials/migration.py +124 -0
  61. devsper/credentials/store.py +142 -0
  62. devsper/dashboard/__init__.py +9 -0
  63. devsper/dashboard/dashboard.py +87 -0
  64. devsper/dev/__init__.py +25 -0
  65. devsper/dev/builder.py +195 -0
  66. devsper/dev/debugger.py +95 -0
  67. devsper/dev/repo_index.py +138 -0
  68. devsper/dev/sandbox.py +203 -0
  69. devsper/dev/scaffold.py +122 -0
  70. devsper/embeddings/__init__.py +5 -0
  71. devsper/embeddings/service.py +36 -0
  72. devsper/explainability/__init__.py +14 -0
  73. devsper/explainability/decision_tree.py +104 -0
  74. devsper/explainability/rationale.py +38 -0
  75. devsper/explainability/simulation.py +56 -0
  76. devsper/hitl/__init__.py +13 -0
  77. devsper/hitl/approval.py +160 -0
  78. devsper/hitl/escalation.py +95 -0
  79. devsper/intelligence/__init__.py +9 -0
  80. devsper/intelligence/adaptation.py +88 -0
  81. devsper/intelligence/analysis/__init__.py +19 -0
  82. devsper/intelligence/analysis/analyzer.py +71 -0
  83. devsper/intelligence/analysis/cost_estimator.py +66 -0
  84. devsper/intelligence/analysis/formatter.py +103 -0
  85. devsper/intelligence/analysis/run_report.py +402 -0
  86. devsper/intelligence/learning_engine.py +92 -0
  87. devsper/intelligence/strategies/__init__.py +23 -0
  88. devsper/intelligence/strategies/base.py +14 -0
  89. devsper/intelligence/strategies/code_analysis_strategy.py +33 -0
  90. devsper/intelligence/strategies/data_science_strategy.py +33 -0
  91. devsper/intelligence/strategies/document_pipeline_strategy.py +33 -0
  92. devsper/intelligence/strategies/experiment_strategy.py +33 -0
  93. devsper/intelligence/strategies/research_strategy.py +34 -0
  94. devsper/intelligence/strategy_selector.py +84 -0
  95. devsper/intelligence/synthesis.py +132 -0
  96. devsper/intelligence/task_optimizer.py +92 -0
  97. devsper/knowledge/__init__.py +5 -0
  98. devsper/knowledge/extractor.py +204 -0
  99. devsper/knowledge/knowledge_graph.py +184 -0
  100. devsper/knowledge/query.py +285 -0
  101. devsper/memory/__init__.py +35 -0
  102. devsper/memory/consolidation.py +138 -0
  103. devsper/memory/embeddings.py +60 -0
  104. devsper/memory/memory_index.py +97 -0
  105. devsper/memory/memory_router.py +62 -0
  106. devsper/memory/memory_store.py +221 -0
  107. devsper/memory/memory_types.py +54 -0
  108. devsper/memory/namespaces.py +45 -0
  109. devsper/memory/scoring.py +77 -0
  110. devsper/memory/summarizer.py +52 -0
  111. devsper/nodes/__init__.py +5 -0
  112. devsper/nodes/controller.py +449 -0
  113. devsper/nodes/rpc.py +127 -0
  114. devsper/nodes/single.py +161 -0
  115. devsper/nodes/worker.py +506 -0
  116. devsper/orchestration/__init__.py +19 -0
  117. devsper/orchestration/meta_planner.py +239 -0
  118. devsper/orchestration/priority_queue.py +61 -0
  119. devsper/plugins/__init__.py +19 -0
  120. devsper/plugins/marketplace/__init__.py +0 -0
  121. devsper/plugins/plugin_loader.py +70 -0
  122. devsper/plugins/plugin_registry.py +34 -0
  123. devsper/plugins/registry.py +83 -0
  124. devsper/protocols/__init__.py +6 -0
  125. devsper/providers/__init__.py +17 -0
  126. devsper/providers/anthropic.py +84 -0
  127. devsper/providers/base.py +75 -0
  128. devsper/providers/complexity_router.py +94 -0
  129. devsper/providers/gemini.py +36 -0
  130. devsper/providers/github.py +180 -0
  131. devsper/providers/model_router.py +40 -0
  132. devsper/providers/openai.py +105 -0
  133. devsper/providers/router/__init__.py +21 -0
  134. devsper/providers/router/backends/__init__.py +19 -0
  135. devsper/providers/router/backends/anthropic_backend.py +111 -0
  136. devsper/providers/router/backends/custom_backend.py +138 -0
  137. devsper/providers/router/backends/gemini_backend.py +89 -0
  138. devsper/providers/router/backends/github_backend.py +165 -0
  139. devsper/providers/router/backends/ollama_backend.py +104 -0
  140. devsper/providers/router/backends/openai_backend.py +142 -0
  141. devsper/providers/router/backends/vllm_backend.py +35 -0
  142. devsper/providers/router/base.py +60 -0
  143. devsper/providers/router/factory.py +92 -0
  144. devsper/providers/router/legacy.py +101 -0
  145. devsper/providers/router/router.py +135 -0
  146. devsper/reasoning/__init__.py +12 -0
  147. devsper/reasoning/graph.py +59 -0
  148. devsper/reasoning/nodes.py +20 -0
  149. devsper/reasoning/store.py +67 -0
  150. devsper/runtime/__init__.py +12 -0
  151. devsper/runtime/health.py +88 -0
  152. devsper/runtime/replay.py +53 -0
  153. devsper/runtime/replay_engine.py +142 -0
  154. devsper/runtime/run_history.py +204 -0
  155. devsper/runtime/telemetry.py +116 -0
  156. devsper/runtime/visualize.py +58 -0
  157. devsper/sandbox/__init__.py +13 -0
  158. devsper/sandbox/sandbox.py +161 -0
  159. devsper/swarm/checkpointer.py +65 -0
  160. devsper/swarm/executor.py +558 -0
  161. devsper/swarm/map_reduce.py +44 -0
  162. devsper/swarm/planner.py +197 -0
  163. devsper/swarm/prefetcher.py +91 -0
  164. devsper/swarm/scheduler.py +153 -0
  165. devsper/swarm/speculation.py +47 -0
  166. devsper/swarm/swarm.py +562 -0
  167. devsper/tools/__init__.py +33 -0
  168. devsper/tools/base.py +29 -0
  169. devsper/tools/code_intelligence/__init__.py +13 -0
  170. devsper/tools/code_intelligence/api_surface_extractor.py +73 -0
  171. devsper/tools/code_intelligence/architecture_analyzer.py +65 -0
  172. devsper/tools/code_intelligence/codebase_indexer.py +71 -0
  173. devsper/tools/code_intelligence/dependency_graph_builder.py +67 -0
  174. devsper/tools/code_intelligence/design_pattern_detector.py +62 -0
  175. devsper/tools/code_intelligence/large_function_detector.py +68 -0
  176. devsper/tools/code_intelligence/module_responsibility_mapper.py +56 -0
  177. devsper/tools/code_intelligence/parallel_codebase_analysis.py +44 -0
  178. devsper/tools/code_intelligence/refactor_candidate_detector.py +81 -0
  179. devsper/tools/code_intelligence/repository_semantic_index.py +61 -0
  180. devsper/tools/code_intelligence/test_coverage_estimator.py +62 -0
  181. devsper/tools/coding/__init__.py +12 -0
  182. devsper/tools/coding/analyze_code_complexity.py +48 -0
  183. devsper/tools/coding/dependency_analyzer.py +42 -0
  184. devsper/tools/coding/extract_functions.py +38 -0
  185. devsper/tools/coding/format_python.py +50 -0
  186. devsper/tools/coding/generate_docstrings.py +40 -0
  187. devsper/tools/coding/generate_unit_tests.py +42 -0
  188. devsper/tools/coding/lint_python.py +51 -0
  189. devsper/tools/coding/refactor_function.py +41 -0
  190. devsper/tools/coding/repo_structure_map.py +54 -0
  191. devsper/tools/coding/run_python.py +53 -0
  192. devsper/tools/data/__init__.py +12 -0
  193. devsper/tools/data/column_type_detection.py +64 -0
  194. devsper/tools/data/csv_summary.py +52 -0
  195. devsper/tools/data/dataframe_filter.py +51 -0
  196. devsper/tools/data/dataframe_groupby.py +47 -0
  197. devsper/tools/data/dataframe_stats.py +38 -0
  198. devsper/tools/data/dataset_sampling.py +55 -0
  199. devsper/tools/data/dataset_schema.py +45 -0
  200. devsper/tools/data/json_pretty_print.py +37 -0
  201. devsper/tools/data/json_query.py +46 -0
  202. devsper/tools/data/missing_value_report.py +47 -0
  203. devsper/tools/data_science/__init__.py +13 -0
  204. devsper/tools/data_science/correlation_heatmap.py +72 -0
  205. devsper/tools/data_science/dataset_bias_detector.py +49 -0
  206. devsper/tools/data_science/dataset_distribution_report.py +64 -0
  207. devsper/tools/data_science/dataset_drift_detector.py +64 -0
  208. devsper/tools/data_science/dataset_outlier_detector.py +65 -0
  209. devsper/tools/data_science/dataset_profile.py +76 -0
  210. devsper/tools/data_science/distributed_dataset_processor.py +54 -0
  211. devsper/tools/data_science/feature_engineering_suggestions.py +69 -0
  212. devsper/tools/data_science/feature_importance_estimator.py +82 -0
  213. devsper/tools/data_science/model_input_validator.py +59 -0
  214. devsper/tools/data_science/time_series_analyzer.py +57 -0
  215. devsper/tools/documents/__init__.py +11 -0
  216. devsper/tools/documents/_docproc.py +56 -0
  217. devsper/tools/documents/document_to_markdown.py +29 -0
  218. devsper/tools/documents/extract_document_images.py +39 -0
  219. devsper/tools/documents/extract_document_text.py +29 -0
  220. devsper/tools/documents/extract_equations.py +36 -0
  221. devsper/tools/documents/extract_tables.py +47 -0
  222. devsper/tools/documents/summarize_document.py +42 -0
  223. devsper/tools/documents/write_latex_document.py +133 -0
  224. devsper/tools/documents/write_markdown_document.py +89 -0
  225. devsper/tools/documents/write_word_document.py +149 -0
  226. devsper/tools/experiments/__init__.py +13 -0
  227. devsper/tools/experiments/bootstrap_estimator.py +54 -0
  228. devsper/tools/experiments/experiment_report_generator.py +50 -0
  229. devsper/tools/experiments/experiment_tracker.py +36 -0
  230. devsper/tools/experiments/grid_search_runner.py +50 -0
  231. devsper/tools/experiments/model_benchmark_runner.py +45 -0
  232. devsper/tools/experiments/monte_carlo_experiment.py +38 -0
  233. devsper/tools/experiments/parameter_sweep_runner.py +51 -0
  234. devsper/tools/experiments/result_comparator.py +58 -0
  235. devsper/tools/experiments/simulation_runner.py +43 -0
  236. devsper/tools/experiments/statistical_significance_test.py +56 -0
  237. devsper/tools/experiments/swarm_map_reduce.py +42 -0
  238. devsper/tools/filesystem/__init__.py +12 -0
  239. devsper/tools/filesystem/append_file.py +42 -0
  240. devsper/tools/filesystem/file_hash.py +40 -0
  241. devsper/tools/filesystem/file_line_count.py +36 -0
  242. devsper/tools/filesystem/file_metadata.py +38 -0
  243. devsper/tools/filesystem/file_preview.py +55 -0
  244. devsper/tools/filesystem/find_large_files.py +50 -0
  245. devsper/tools/filesystem/list_directory.py +39 -0
  246. devsper/tools/filesystem/read_file.py +35 -0
  247. devsper/tools/filesystem/search_files.py +60 -0
  248. devsper/tools/filesystem/write_file.py +41 -0
  249. devsper/tools/flagship/__init__.py +15 -0
  250. devsper/tools/flagship/distributed_document_analysis.py +77 -0
  251. devsper/tools/flagship/docproc_corpus_pipeline.py +91 -0
  252. devsper/tools/flagship/repository_semantic_map.py +99 -0
  253. devsper/tools/flagship/research_graph_builder.py +111 -0
  254. devsper/tools/flagship/swarm_experiment_runner.py +86 -0
  255. devsper/tools/knowledge/__init__.py +10 -0
  256. devsper/tools/knowledge/citation_graph_builder.py +69 -0
  257. devsper/tools/knowledge/concept_frequency_analyzer.py +74 -0
  258. devsper/tools/knowledge/corpus_builder.py +66 -0
  259. devsper/tools/knowledge/cross_document_entity_linker.py +71 -0
  260. devsper/tools/knowledge/document_corpus_summary.py +68 -0
  261. devsper/tools/knowledge/document_topic_extractor.py +58 -0
  262. devsper/tools/knowledge/knowledge_graph_extractor.py +58 -0
  263. devsper/tools/knowledge/timeline_extractor.py +59 -0
  264. devsper/tools/math/__init__.py +12 -0
  265. devsper/tools/math/calculate_expression.py +52 -0
  266. devsper/tools/math/correlation.py +44 -0
  267. devsper/tools/math/distribution_summary.py +39 -0
  268. devsper/tools/math/histogram.py +53 -0
  269. devsper/tools/math/linear_regression.py +47 -0
  270. devsper/tools/math/matrix_multiply.py +38 -0
  271. devsper/tools/math/mean_std.py +35 -0
  272. devsper/tools/math/monte_carlo_simulation.py +43 -0
  273. devsper/tools/math/polynomial_fit.py +40 -0
  274. devsper/tools/math/random_sample.py +36 -0
  275. devsper/tools/mcp/__init__.py +23 -0
  276. devsper/tools/mcp/adapter.py +53 -0
  277. devsper/tools/mcp/client.py +235 -0
  278. devsper/tools/mcp/discovery.py +53 -0
  279. devsper/tools/memory/__init__.py +16 -0
  280. devsper/tools/memory/delete_memory.py +25 -0
  281. devsper/tools/memory/list_memory.py +34 -0
  282. devsper/tools/memory/search_memory.py +36 -0
  283. devsper/tools/memory/store_memory.py +47 -0
  284. devsper/tools/memory/summarize_memory.py +41 -0
  285. devsper/tools/memory/tag_memory.py +47 -0
  286. devsper/tools/pipelines.py +92 -0
  287. devsper/tools/registry.py +39 -0
  288. devsper/tools/research/__init__.py +12 -0
  289. devsper/tools/research/arxiv_download.py +55 -0
  290. devsper/tools/research/arxiv_search.py +58 -0
  291. devsper/tools/research/citation_extractor.py +35 -0
  292. devsper/tools/research/duckduckgo_search.py +42 -0
  293. devsper/tools/research/paper_metadata_extractor.py +45 -0
  294. devsper/tools/research/paper_summarizer.py +41 -0
  295. devsper/tools/research/research_question_generator.py +39 -0
  296. devsper/tools/research/topic_cluster.py +46 -0
  297. devsper/tools/research/web_search.py +47 -0
  298. devsper/tools/research/wikipedia_lookup.py +50 -0
  299. devsper/tools/research_advanced/__init__.py +14 -0
  300. devsper/tools/research_advanced/citation_context_extractor.py +60 -0
  301. devsper/tools/research_advanced/literature_review_generator.py +79 -0
  302. devsper/tools/research_advanced/methodology_extractor.py +58 -0
  303. devsper/tools/research_advanced/paper_contribution_extractor.py +50 -0
  304. devsper/tools/research_advanced/paper_dataset_identifier.py +49 -0
  305. devsper/tools/research_advanced/paper_method_comparator.py +62 -0
  306. devsper/tools/research_advanced/paper_similarity_search.py +69 -0
  307. devsper/tools/research_advanced/paper_trend_analyzer.py +69 -0
  308. devsper/tools/research_advanced/parallel_document_analyzer.py +56 -0
  309. devsper/tools/research_advanced/research_gap_finder.py +71 -0
  310. devsper/tools/research_advanced/research_topic_mapper.py +69 -0
  311. devsper/tools/research_advanced/swarm_literature_review.py +58 -0
  312. devsper/tools/scoring/__init__.py +52 -0
  313. devsper/tools/scoring/report.py +44 -0
  314. devsper/tools/scoring/scorer.py +39 -0
  315. devsper/tools/scoring/selector.py +61 -0
  316. devsper/tools/scoring/store.py +267 -0
  317. devsper/tools/selector.py +130 -0
  318. devsper/tools/system/__init__.py +12 -0
  319. devsper/tools/system/cpu_usage.py +22 -0
  320. devsper/tools/system/disk_usage.py +35 -0
  321. devsper/tools/system/environment_variables.py +29 -0
  322. devsper/tools/system/memory_usage.py +23 -0
  323. devsper/tools/system/pip_install.py +44 -0
  324. devsper/tools/system/pip_search.py +29 -0
  325. devsper/tools/system/process_list.py +34 -0
  326. devsper/tools/system/python_package_list.py +40 -0
  327. devsper/tools/system/run_shell_command.py +51 -0
  328. devsper/tools/system/system_info.py +26 -0
  329. devsper/tools/tool_runner.py +122 -0
  330. devsper/tui/__init__.py +5 -0
  331. devsper/tui/activity_feed_view.py +73 -0
  332. devsper/tui/adaptive_tasks_view.py +75 -0
  333. devsper/tui/agent_role_view.py +35 -0
  334. devsper/tui/app.py +395 -0
  335. devsper/tui/dashboard_screen.py +290 -0
  336. devsper/tui/dev_view.py +99 -0
  337. devsper/tui/inject_screen.py +73 -0
  338. devsper/tui/knowledge_graph_view.py +46 -0
  339. devsper/tui/layout.py +43 -0
  340. devsper/tui/logs_view.py +83 -0
  341. devsper/tui/memory_view.py +58 -0
  342. devsper/tui/performance_view.py +33 -0
  343. devsper/tui/reasoning_graph_view.py +39 -0
  344. devsper/tui/results_view.py +139 -0
  345. devsper/tui/swarm_view.py +37 -0
  346. devsper/tui/task_detail_screen.py +55 -0
  347. devsper/tui/task_view.py +103 -0
  348. devsper/types/event.py +97 -0
  349. devsper/types/exceptions.py +21 -0
  350. devsper/types/swarm.py +41 -0
  351. devsper/types/task.py +80 -0
  352. devsper/upgrade/__init__.py +21 -0
  353. devsper/upgrade/changelog.py +124 -0
  354. devsper/upgrade/cli.py +145 -0
  355. devsper/upgrade/installer.py +103 -0
  356. devsper/upgrade/notifier.py +52 -0
  357. devsper/upgrade/version_check.py +121 -0
  358. devsper/utils/event_logger.py +88 -0
  359. devsper/utils/http.py +43 -0
  360. devsper/utils/models.py +54 -0
  361. devsper/visualization/__init__.py +5 -0
  362. devsper/visualization/dag_export.py +67 -0
  363. devsper/workflow/__init__.py +18 -0
  364. devsper/workflow/conditions.py +157 -0
  365. devsper/workflow/context.py +108 -0
  366. devsper/workflow/loader.py +156 -0
  367. devsper/workflow/resolver.py +109 -0
  368. devsper/workflow/runner.py +562 -0
  369. devsper/workflow/schema.py +63 -0
  370. devsper/workflow/validator.py +128 -0
  371. devsper-2.1.6.dist-info/METADATA +346 -0
  372. devsper-2.1.6.dist-info/RECORD +375 -0
  373. devsper-2.1.6.dist-info/WHEEL +4 -0
  374. devsper-2.1.6.dist-info/entry_points.txt +3 -0
  375. devsper-2.1.6.dist-info/licenses/LICENSE +639 -0
@@ -0,0 +1,67 @@
1
+ """
2
+ Reasoning store: in-memory store backed by a ReasoningGraph. Used by swarm/agents.
3
+
4
+ Thread-safe for concurrent add_node from executor's worker pool (asyncio run_in_executor).
5
+ """
6
+
7
+ import secrets
8
+ import threading
9
+ from devsper.reasoning.graph import ReasoningGraph
10
+ from devsper.reasoning.nodes import ReasoningNode
11
+
12
+
13
+ def _short_id() -> str:
14
+ return secrets.token_hex(4)
15
+
16
+
17
+ class ReasoningStore:
18
+ """Store for reasoning nodes. Wraps ReasoningGraph; agents write/query via this."""
19
+
20
+ def __init__(self) -> None:
21
+ self._graph = ReasoningGraph()
22
+ self._lock = threading.Lock()
23
+
24
+ @property
25
+ def graph(self) -> ReasoningGraph:
26
+ return self._graph
27
+
28
+ def add_node(
29
+ self,
30
+ agent_id: str,
31
+ task_id: str,
32
+ content: str,
33
+ dependencies: list[str] | None = None,
34
+ node_id: str | None = None,
35
+ ) -> ReasoningNode:
36
+ """Create and add a reasoning node. Returns the new node. Thread-safe for concurrent workers."""
37
+ nid = node_id or _short_id()
38
+ node = ReasoningNode(
39
+ id=nid,
40
+ agent_id=agent_id,
41
+ task_id=task_id,
42
+ content=content,
43
+ dependencies=dependencies or [],
44
+ )
45
+ with self._lock:
46
+ self._graph.add_node(node)
47
+ return node
48
+
49
+ def query_nodes(
50
+ self,
51
+ *,
52
+ agent_id: str | None = None,
53
+ task_id: str | None = None,
54
+ limit: int = 100,
55
+ ) -> list[ReasoningNode]:
56
+ """Query stored nodes by optional agent_id and/or task_id. Thread-safe."""
57
+ with self._lock:
58
+ return self._graph.query_nodes(
59
+ agent_id=agent_id,
60
+ task_id=task_id,
61
+ limit=limit,
62
+ )
63
+
64
+ def get_dependencies(self, node_id: str) -> list[ReasoningNode]:
65
+ """Return dependencies of the given node. Thread-safe."""
66
+ with self._lock:
67
+ return self._graph.get_dependencies(node_id)
@@ -0,0 +1,12 @@
1
+ """Runtime utilities: replay, telemetry, visualization."""
2
+
3
+ from devsper.runtime.replay import replay_execution
4
+ from devsper.runtime.telemetry import collect_telemetry, print_telemetry_summary
5
+ from devsper.runtime.visualize import visualize_scheduler_dag
6
+
7
+ __all__ = [
8
+ "replay_execution",
9
+ "collect_telemetry",
10
+ "print_telemetry_summary",
11
+ "visualize_scheduler_dag",
12
+ ]
@@ -0,0 +1,88 @@
1
+ """
2
+ Health and readiness checks for orchestration (Docker healthcheck, k8s liveness).
3
+
4
+ v1.9: bus_reachable, memory_store_readable, tool_scores_readable, knowledge_graph_loadable, checkpoint_dir_writable.
5
+ """
6
+
7
+ import asyncio
8
+ from dataclasses import dataclass
9
+ from datetime import datetime, timezone
10
+
11
+
12
+ @dataclass
13
+ class HealthReport:
14
+ checks: dict[str, bool]
15
+ errors: dict[str, str]
16
+ healthy: bool
17
+ timestamp: str
18
+
19
+
20
+ class HealthChecker:
21
+ """Run health checks against config subsystems."""
22
+
23
+ async def check(self, config: object) -> HealthReport:
24
+ checks: dict[str, bool] = {}
25
+ errors: dict[str, str] = {}
26
+
27
+ # bus_reachable: start and stop bus (for Redis: connect; for memory: no-op)
28
+ try:
29
+ from devsper.bus import get_bus
30
+ bus = get_bus(config)
31
+ await bus.start()
32
+ await bus.stop()
33
+ checks["bus_reachable"] = True
34
+ except Exception as e:
35
+ checks["bus_reachable"] = False
36
+ errors["bus_reachable"] = str(e)
37
+
38
+ # memory_store_readable
39
+ try:
40
+ from devsper.memory.memory_store import get_default_store
41
+ store = get_default_store()
42
+ store.list_memory(limit=1)
43
+ checks["memory_store_readable"] = True
44
+ except Exception as e:
45
+ checks["memory_store_readable"] = False
46
+ errors["memory_store_readable"] = str(e)
47
+
48
+ # tool_scores_readable
49
+ try:
50
+ from devsper.tools.scoring import get_default_score_store
51
+ get_default_score_store().get_all_scores()
52
+ checks["tool_scores_readable"] = True
53
+ except Exception as e:
54
+ checks["tool_scores_readable"] = False
55
+ errors["tool_scores_readable"] = str(e)
56
+
57
+ # knowledge_graph_loadable
58
+ try:
59
+ from devsper.knowledge.knowledge_graph import KnowledgeGraph
60
+ from devsper.memory.memory_store import get_default_store
61
+ kg = KnowledgeGraph(store=get_default_store())
62
+ kg.load()
63
+ checks["knowledge_graph_loadable"] = True
64
+ except Exception as e:
65
+ checks["knowledge_graph_loadable"] = False
66
+ errors["knowledge_graph_loadable"] = str(e)
67
+
68
+ # checkpoint_dir_writable
69
+ try:
70
+ events_dir = getattr(config, "events_dir", ".devsper/events") or ".devsper/events"
71
+ import os
72
+ os.makedirs(events_dir, exist_ok=True)
73
+ test_file = os.path.join(events_dir, ".health_check_tmp")
74
+ with open(test_file, "w") as f:
75
+ f.write("ok")
76
+ os.remove(test_file)
77
+ checks["checkpoint_dir_writable"] = True
78
+ except Exception as e:
79
+ checks["checkpoint_dir_writable"] = False
80
+ errors["checkpoint_dir_writable"] = str(e)
81
+
82
+ healthy = all(checks.values()) if checks else False
83
+ return HealthReport(
84
+ checks=checks,
85
+ errors=errors,
86
+ healthy=healthy,
87
+ timestamp=datetime.now(timezone.utc).isoformat(),
88
+ )
@@ -0,0 +1,53 @@
1
+ """
2
+ Replay swarm execution from an event log.
3
+
4
+ Load events.jsonl, group by task, reconstruct execution timeline, print step-by-step replay.
5
+ """
6
+
7
+ import os
8
+ from devsper.types.event import Event
9
+
10
+
11
+ def _load_events(log_path: str) -> list[Event]:
12
+ """Load events from a JSONL file."""
13
+ if not os.path.exists(log_path):
14
+ return []
15
+ events_list = []
16
+ with open(log_path, "r") as f:
17
+ for line in f:
18
+ line = line.strip()
19
+ if not line:
20
+ continue
21
+ try:
22
+ events_list.append(Event.model_validate_json(line))
23
+ except Exception:
24
+ continue
25
+ return events_list
26
+
27
+
28
+ def replay_execution(log_path: str) -> str:
29
+ """
30
+ Replay swarm execution from the event log at log_path.
31
+
32
+ Loads events.jsonl, sorts by timestamp, and returns a step-by-step replay transcript
33
+ (e.g. [planner_started] task_1 created agent_started task_1 ...).
34
+ """
35
+ events_list = _load_events(log_path)
36
+ if not events_list:
37
+ return f"No events found at {log_path}"
38
+
39
+ events_list.sort(key=lambda e: e.timestamp)
40
+
41
+ lines = []
42
+ for e in events_list:
43
+ event_type = e.type.value if hasattr(e.type, "value") else str(e.type)
44
+ payload = e.payload or {}
45
+ task_id = payload.get("task_id", "")
46
+ part = f"[{event_type}]"
47
+ if task_id:
48
+ part += f" {task_id}"
49
+ if event_type == "task_created" and payload.get("description"):
50
+ part += f" created"
51
+ lines.append(part)
52
+
53
+ return "\n".join(lines)
@@ -0,0 +1,142 @@
1
+ """
2
+ Deterministic replay: reconstruct entire swarm execution from an event log.
3
+
4
+ Includes: planner decisions, scheduler events, agent prompts (when logged),
5
+ tool calls, reasoning graph updates.
6
+ """
7
+
8
+ import os
9
+ from pathlib import Path
10
+ from devsper.types.event import Event
11
+
12
+
13
+ def _find_log_path(events_dir: str | Path, run_id: str) -> str | None:
14
+ """Find events JSONL file for run_id (stem of filename). Returns path or None."""
15
+ path = Path(events_dir)
16
+ if not path.is_dir():
17
+ return None
18
+ candidate = path / f"{run_id}.jsonl"
19
+ if candidate.is_file():
20
+ return str(candidate)
21
+ for f in path.glob("*.jsonl"):
22
+ if f.stem == run_id:
23
+ return str(f)
24
+ return None
25
+
26
+
27
+ def _load_events(log_path: str) -> list[Event]:
28
+ """Load events from a JSONL file."""
29
+ if not os.path.isfile(log_path):
30
+ return []
31
+ out = []
32
+ with open(log_path, "r", encoding="utf-8") as f:
33
+ for line in f:
34
+ line = line.strip()
35
+ if not line:
36
+ continue
37
+ try:
38
+ out.append(Event.model_validate_json(line))
39
+ except Exception:
40
+ continue
41
+ return out
42
+
43
+
44
+ def replay_run(
45
+ run_id: str,
46
+ events_dir: str | Path | None = None,
47
+ ) -> str:
48
+ """
49
+ Reconstruct the entire swarm execution for run_id.
50
+ Returns a transcript string: planner decisions, scheduler (task) events,
51
+ executor/agent lifecycle, tool calls, reasoning graph updates.
52
+ """
53
+ if events_dir is None:
54
+ try:
55
+ from devsper.config import get_config
56
+
57
+ events_dir = get_config().events_dir
58
+ except Exception:
59
+ events_dir = ".devsper/events"
60
+
61
+ log_path = _find_log_path(events_dir, run_id)
62
+ if not log_path:
63
+ return f"No event log found for run_id: {run_id} (looked in {events_dir})"
64
+
65
+ evs = _load_events(log_path)
66
+ if not evs:
67
+ return f"Empty event log for run_id: {run_id}"
68
+
69
+ evs.sort(key=lambda e: e.timestamp)
70
+ lines = [f"# Replay: {run_id}", ""]
71
+
72
+ for e in evs:
73
+ event_type = e.type.value if hasattr(e.type, "value") else str(e.type)
74
+ payload = e.payload or {}
75
+ ts = getattr(e.timestamp, "isoformat", lambda: str(e.timestamp))()
76
+
77
+ if event_type == "swarm_started":
78
+ lines.append(f"[{ts}] SWARM_STARTED")
79
+ lines.append(f" user_task: {payload.get('user_task', '')[:200]}")
80
+ elif event_type == "planner_started":
81
+ lines.append(
82
+ f"[{ts}] PLANNER_STARTED task_id={payload.get('task_id', '')}"
83
+ )
84
+ elif event_type == "task_created":
85
+ lines.append(f"[{ts}] TASK_CREATED task_id={payload.get('task_id', '')}")
86
+ lines.append(f" description: {(payload.get('description') or '')[:120]}")
87
+ elif event_type == "planner_finished":
88
+ lines.append(
89
+ f"[{ts}] PLANNER_FINISHED subtask_count={payload.get('subtask_count', 0)}"
90
+ )
91
+ elif event_type == "executor_started":
92
+ lines.append(f"[{ts}] EXECUTOR_STARTED")
93
+ elif event_type == "agent_started":
94
+ lines.append(f"[{ts}] AGENT_STARTED task_id={payload.get('task_id', '')}")
95
+ elif event_type == "task_started":
96
+ lines.append(f"[{ts}] TASK_STARTED task_id={payload.get('task_id', '')}")
97
+ elif event_type == "tool_called":
98
+ lines.append(
99
+ f"[{ts}] TOOL_CALLED task_id={payload.get('task_id', '')} tool={payload.get('tool', '')}"
100
+ )
101
+ lines.append(
102
+ f" result_preview: {(payload.get('result_preview') or '')[:150]}"
103
+ )
104
+ elif event_type == "reasoning_node_added":
105
+ lines.append(
106
+ f"[{ts}] REASONING_NODE_ADDED node_id={payload.get('node_id', '')} task_id={payload.get('task_id', '')}"
107
+ )
108
+ elif event_type == "task_completed":
109
+ lines.append(f"[{ts}] TASK_COMPLETED task_id={payload.get('task_id', '')}")
110
+ elif event_type == "task_failed":
111
+ lines.append(
112
+ f"[{ts}] TASK_FAILED task_id={payload.get('task_id', '')} error={payload.get('error', '')[:100]}"
113
+ )
114
+ elif event_type == "agent_finished":
115
+ lines.append(f"[{ts}] AGENT_FINISHED task_id={payload.get('task_id', '')}")
116
+ elif event_type == "executor_finished":
117
+ lines.append(f"[{ts}] EXECUTOR_FINISHED")
118
+ elif event_type == "swarm_finished":
119
+ lines.append(
120
+ f"[{ts}] SWARM_FINISHED task_count={payload.get('task_count', 0)}"
121
+ )
122
+ else:
123
+ lines.append(f"[{ts}] {event_type.upper()} {payload}")
124
+ lines.append("")
125
+
126
+ return "\n".join(lines).rstrip()
127
+
128
+
129
+ def list_run_ids(events_dir: str | Path | None = None) -> list[str]:
130
+ """List run IDs that have event logs (from *.jsonl in events_dir)."""
131
+ if events_dir is None:
132
+ try:
133
+ from devsper.config import get_config
134
+
135
+ events_dir = get_config().events_dir
136
+ except Exception:
137
+ events_dir = ".devsper/events"
138
+ path = Path(events_dir)
139
+ if not path.is_dir():
140
+ return []
141
+ ids_ = [f.stem for f in path.glob("*.jsonl")]
142
+ return sorted(ids_, reverse=True)
@@ -0,0 +1,204 @@
1
+ """
2
+ Persistent run history: SQLite DB at ~/.config/devsper/runs.db.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ import sqlite3
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+
12
+ from devsper.intelligence.analysis.run_report import RunReport
13
+
14
+
15
+ HISTORY_DB = Path("~/.config/devsper/runs.db").expanduser()
16
+
17
+
18
+ @dataclass
19
+ class RunRow:
20
+ run_id: str
21
+ root_task: str
22
+ strategy: str
23
+ started_at: str
24
+ finished_at: str
25
+ duration_seconds: float
26
+ total_tasks: int
27
+ completed_tasks: int
28
+ failed_tasks: int
29
+ estimated_cost_usd: float | None
30
+ models_used: str # JSON array string
31
+ events_path: str
32
+
33
+
34
+ _SCHEMA = """
35
+ CREATE TABLE IF NOT EXISTS runs (
36
+ run_id TEXT PRIMARY KEY,
37
+ root_task TEXT,
38
+ strategy TEXT,
39
+ started_at TEXT,
40
+ finished_at TEXT,
41
+ duration_seconds REAL,
42
+ total_tasks INTEGER,
43
+ completed_tasks INTEGER,
44
+ failed_tasks INTEGER,
45
+ estimated_cost_usd REAL,
46
+ models_used TEXT,
47
+ events_path TEXT
48
+ );
49
+ """
50
+
51
+
52
+ def _ensure_db(db_path: Path) -> None:
53
+ db_path.parent.mkdir(parents=True, exist_ok=True)
54
+ with sqlite3.connect(str(db_path)) as conn:
55
+ conn.executescript(_SCHEMA)
56
+
57
+
58
+ class RunHistory:
59
+ def __init__(self, db_path: Path | None = None) -> None:
60
+ self._db = db_path or HISTORY_DB
61
+ _ensure_db(self._db)
62
+
63
+ def record_run(self, report: RunReport) -> None:
64
+ """Persist report to DB. Called at SWARM_FINISHED."""
65
+ events_path = ""
66
+ try:
67
+ from devsper.config import get_config
68
+ cfg = get_config()
69
+ events_dir = Path(cfg.events_dir)
70
+ candidate = events_dir / f"{report.run_id}.jsonl"
71
+ if candidate.is_file():
72
+ events_path = str(candidate)
73
+ else:
74
+ for f in events_dir.glob("*.jsonl"):
75
+ if f.stem == report.run_id:
76
+ events_path = str(f)
77
+ break
78
+ except Exception:
79
+ pass
80
+ models_used_json = json.dumps(report.models_used or [])
81
+ with sqlite3.connect(str(self._db)) as conn:
82
+ conn.execute(
83
+ """
84
+ INSERT OR REPLACE INTO runs (
85
+ run_id, root_task, strategy, started_at, finished_at,
86
+ duration_seconds, total_tasks, completed_tasks, failed_tasks,
87
+ estimated_cost_usd, models_used, events_path
88
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
89
+ """,
90
+ (
91
+ report.run_id,
92
+ report.root_task,
93
+ report.strategy,
94
+ report.started_at,
95
+ report.finished_at,
96
+ report.total_duration_seconds,
97
+ report.total_tasks,
98
+ report.completed_tasks,
99
+ report.failed_tasks,
100
+ report.estimated_cost_usd,
101
+ models_used_json,
102
+ events_path,
103
+ ),
104
+ )
105
+
106
+ def list_runs(
107
+ self,
108
+ limit: int = 20,
109
+ filter_status: str | None = None,
110
+ ) -> list[RunRow]:
111
+ """filter_status: 'failed' to only return runs with failed_tasks > 0."""
112
+ with sqlite3.connect(str(self._db)) as conn:
113
+ conn.row_factory = sqlite3.Row
114
+ if filter_status == "failed":
115
+ cur = conn.execute(
116
+ """
117
+ SELECT run_id, root_task, strategy, started_at, finished_at,
118
+ duration_seconds, total_tasks, completed_tasks, failed_tasks,
119
+ estimated_cost_usd, models_used, events_path
120
+ FROM runs WHERE failed_tasks > 0
121
+ ORDER BY started_at DESC LIMIT ?
122
+ """,
123
+ (limit,),
124
+ )
125
+ else:
126
+ cur = conn.execute(
127
+ """
128
+ SELECT run_id, root_task, strategy, started_at, finished_at,
129
+ duration_seconds, total_tasks, completed_tasks, failed_tasks,
130
+ estimated_cost_usd, models_used, events_path
131
+ FROM runs ORDER BY started_at DESC LIMIT ?
132
+ """,
133
+ (limit,),
134
+ )
135
+ rows = cur.fetchall()
136
+ return [_row_to_run(dict(r)) for r in rows]
137
+
138
+ def get_run(self, run_id: str) -> RunRow | None:
139
+ with sqlite3.connect(str(self._db)) as conn:
140
+ conn.row_factory = sqlite3.Row
141
+ cur = conn.execute(
142
+ "SELECT run_id, root_task, strategy, started_at, finished_at, "
143
+ "duration_seconds, total_tasks, completed_tasks, failed_tasks, "
144
+ "estimated_cost_usd, models_used, events_path FROM runs WHERE run_id = ?",
145
+ (run_id,),
146
+ )
147
+ row = cur.fetchone()
148
+ return _row_to_run(dict(row)) if row else None
149
+
150
+ def delete_run(self, run_id: str) -> None:
151
+ """Remove from DB and delete the events file if path is stored."""
152
+ row = self.get_run(run_id)
153
+ with sqlite3.connect(str(self._db)) as conn:
154
+ conn.execute("DELETE FROM runs WHERE run_id = ?", (run_id,))
155
+ if row and row.events_path and Path(row.events_path).is_file():
156
+ try:
157
+ Path(row.events_path).unlink()
158
+ except OSError:
159
+ pass
160
+
161
+ def get_stats(self) -> dict:
162
+ """Aggregate: total runs, avg duration, total cost, most-used strategy."""
163
+ with sqlite3.connect(str(self._db)) as conn:
164
+ cur = conn.execute(
165
+ "SELECT COUNT(*) as n, AVG(duration_seconds) as avg_dur, "
166
+ "SUM(estimated_cost_usd) as total_cost FROM runs"
167
+ )
168
+ row = cur.fetchone()
169
+ n = row[0] or 0
170
+ avg_dur = row[1] or 0.0
171
+ total_cost = row[2] or 0.0
172
+ cur2 = conn.execute(
173
+ "SELECT strategy, COUNT(*) as c FROM runs GROUP BY strategy ORDER BY c DESC LIMIT 1"
174
+ )
175
+ strat_row = cur2.fetchone()
176
+ most_used_strategy = strat_row[0] if strat_row else ""
177
+ return {
178
+ "total_runs": n,
179
+ "avg_duration_seconds": round(avg_dur, 2),
180
+ "total_estimated_cost_usd": round(total_cost, 4) if total_cost else 0.0,
181
+ "most_used_strategy": most_used_strategy,
182
+ }
183
+
184
+
185
+ def _row_to_run(d: dict) -> RunRow:
186
+ models_raw = d.get("models_used") or "[]"
187
+ try:
188
+ _ = json.loads(models_raw)
189
+ except Exception:
190
+ models_raw = "[]"
191
+ return RunRow(
192
+ run_id=d["run_id"],
193
+ root_task=d.get("root_task") or "",
194
+ strategy=d.get("strategy") or "",
195
+ started_at=d.get("started_at") or "",
196
+ finished_at=d.get("finished_at") or "",
197
+ duration_seconds=float(d.get("duration_seconds") or 0),
198
+ total_tasks=int(d.get("total_tasks") or 0),
199
+ completed_tasks=int(d.get("completed_tasks") or 0),
200
+ failed_tasks=int(d.get("failed_tasks") or 0),
201
+ estimated_cost_usd=d.get("estimated_cost_usd") if d.get("estimated_cost_usd") is not None else None,
202
+ models_used=models_raw,
203
+ events_path=d.get("events_path") or "",
204
+ )
@@ -0,0 +1,116 @@
1
+ """
2
+ Execution telemetry: collect runtime metrics from event log.
3
+
4
+ Metrics: task_duration, agent_latency, concurrency_levels, task_success_rate.
5
+ Emit telemetry summary when swarm finishes.
6
+ """
7
+
8
+ import os
9
+
10
+ from devsper.types.event import Event
11
+
12
+
13
+ def _load_events(log_path: str) -> list[Event]:
14
+ if not os.path.exists(log_path):
15
+ return []
16
+ events_list = []
17
+ with open(log_path, "r") as f:
18
+ for line in f:
19
+ line = line.strip()
20
+ if not line:
21
+ continue
22
+ try:
23
+ events_list.append(Event.model_validate_json(line))
24
+ except Exception:
25
+ continue
26
+ return events_list
27
+
28
+
29
+ def collect_telemetry(log_path: str) -> dict:
30
+ """
31
+ Collect runtime metrics from an event log.
32
+
33
+ Returns a dict with: tasks_completed, tasks_failed, avg_task_duration_seconds,
34
+ avg_agent_latency_seconds, max_concurrency, task_success_rate.
35
+ """
36
+ events_list = _load_events(log_path)
37
+ if not events_list:
38
+ return {
39
+ "tasks_completed": 0,
40
+ "tasks_failed": 0,
41
+ "avg_task_duration_seconds": 0.0,
42
+ "avg_agent_latency_seconds": 0.0,
43
+ "max_concurrency": 0,
44
+ "task_success_rate": 0.0,
45
+ }
46
+
47
+ events_list.sort(key=lambda e: e.timestamp)
48
+ task_started = {}
49
+ task_completed = {}
50
+ agent_started = {}
51
+ agent_finished = {}
52
+ concurrency = 0
53
+ max_concurrency = 0
54
+
55
+ for e in events_list:
56
+ t = e.timestamp
57
+ payload = e.payload or {}
58
+ task_id = payload.get("task_id")
59
+ typ = e.type.value if hasattr(e.type, "value") else str(e.type)
60
+
61
+ if typ == "task_started" and task_id:
62
+ task_started[task_id] = t
63
+ concurrency += 1
64
+ max_concurrency = max(max_concurrency, concurrency)
65
+ elif typ == "task_completed" and task_id:
66
+ task_completed[task_id] = t
67
+ concurrency = max(0, concurrency - 1)
68
+ elif typ == "task_failed" and task_id:
69
+ concurrency = max(0, concurrency - 1)
70
+ elif typ == "agent_started" and task_id:
71
+ agent_started[task_id] = t
72
+ elif typ == "agent_finished" and task_id:
73
+ agent_finished[task_id] = t
74
+
75
+ task_durations = []
76
+ for tid, start in task_started.items():
77
+ if tid in task_completed:
78
+ delta = (task_completed[tid] - start).total_seconds()
79
+ task_durations.append(delta)
80
+
81
+ agent_latencies = []
82
+ for tid, start in agent_started.items():
83
+ if tid in agent_finished:
84
+ delta = (agent_finished[tid] - start).total_seconds()
85
+ agent_latencies.append(delta)
86
+
87
+ completed = len(task_completed)
88
+ failed = sum(1 for e in events_list if (e.type.value if hasattr(e.type, "value") else str(e.type)) == "task_failed")
89
+ total_tasks = completed + failed
90
+ success_rate = (completed / total_tasks) if total_tasks else 0.0
91
+
92
+ return {
93
+ "tasks_completed": completed,
94
+ "tasks_failed": failed,
95
+ "avg_task_duration_seconds": round(sum(task_durations) / len(task_durations), 2) if task_durations else 0.0,
96
+ "avg_agent_latency_seconds": round(sum(agent_latencies) / len(agent_latencies), 2) if agent_latencies else 0.0,
97
+ "max_concurrency": max_concurrency,
98
+ "task_success_rate": round(success_rate, 2),
99
+ }
100
+
101
+
102
+ def print_telemetry_summary(log_path: str) -> str:
103
+ """
104
+ Emit telemetry summary to string (e.g. for printing when swarm finishes).
105
+
106
+ Example: tasks_completed: 5, avg_task_time: 2.3s, max_concurrency: 3
107
+ """
108
+ m = collect_telemetry(log_path)
109
+ return (
110
+ f"tasks_completed: {m['tasks_completed']}\n"
111
+ f"tasks_failed: {m['tasks_failed']}\n"
112
+ f"avg_task_time: {m['avg_task_duration_seconds']}s\n"
113
+ f"avg_agent_latency: {m['avg_agent_latency_seconds']}s\n"
114
+ f"max_concurrency: {m['max_concurrency']}\n"
115
+ f"task_success_rate: {m['task_success_rate']}"
116
+ )