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,197 @@
1
+ """
2
+ Planner: convert one task into multiple ordered subtasks.
3
+
4
+ Lifecycle: PLANNER_STARTED → TASK_CREATED (×N) → PLANNER_FINISHED.
5
+ Subtasks have sequential dependencies; each subtask gets a short unique ID.
6
+ """
7
+
8
+ import re
9
+ import secrets
10
+ from datetime import datetime, timezone
11
+
12
+ from devsper.types.task import Task
13
+ from devsper.types.event import Event, events
14
+ from devsper.agents.roles import infer_role_from_description
15
+ from devsper.utils.event_logger import EventLog
16
+ from devsper.utils.models import generate
17
+
18
+
19
+ PLANNER_PROMPT = """Break the following task into the minimal number of smaller steps needed (use only as many as the task requires—often 1–3 for simple tasks, more for complex ones).
20
+
21
+ Task:
22
+ {task_description}
23
+ {kg_section}
24
+
25
+ Each step must be a concrete action that can be done with tools (e.g. list directory, read file, run a command). Do NOT use vague steps like "open a terminal", "navigate to X", or "access the environment"—instead say exactly what to do: "List the contents of /tmp", "Read and summarize file X", etc. Steps will be executed by agents with filesystem and other tools; they do not need to "open" anything.
26
+
27
+ Return a numbered list.
28
+
29
+ Example format:
30
+ 1. List the contents of /tmp
31
+ 2. Summarize the file listing
32
+ """
33
+
34
+ EXPAND_TASKS_PROMPT = """A task just completed in a workflow.
35
+
36
+ Completed task: {task_description}
37
+ Result (preview): {result_preview}
38
+
39
+ Suggest 0 to 3 additional follow-up tasks that would extend or build on this work.
40
+ Return a numbered list only. One task per line.
41
+ Example:
42
+ 1. Compare methods
43
+ 2. Identify trends
44
+ """
45
+
46
+
47
+ NUMBERED_LINE = re.compile(r"^\s*\d+[.)]\s*(.+)$", re.MULTILINE)
48
+
49
+ # Skip decomposition for short, single-step prompts (e.g. "What is 2+2?")
50
+ MAX_SIMPLE_TASK_LEN = 200
51
+ MULTI_STEP_PATTERN = re.compile(
52
+ r"\b(then|and then|first|second|step\s*\d|finally|after that|next,?)\b|\n|^\s*\d+[.)]\s+",
53
+ re.IGNORECASE | re.MULTILINE,
54
+ )
55
+
56
+
57
+ def _is_simple_task(description: str) -> bool:
58
+ """True if the task looks like a single question/request, not a multi-step workflow."""
59
+ if not description or len(description.strip()) > MAX_SIMPLE_TASK_LEN:
60
+ return False
61
+ return MULTI_STEP_PATTERN.search(description.strip()) is None
62
+
63
+
64
+ def _short_id() -> str:
65
+ """Return a short, URL-safe task id (8 hex chars)."""
66
+ return secrets.token_hex(4)
67
+
68
+
69
+ class Planner:
70
+ """Converts one Task into a list of subtasks with sequential dependencies."""
71
+
72
+ def __init__(
73
+ self,
74
+ model_name: str = "gpt-4o",
75
+ event_log: EventLog | None = None,
76
+ strategy=None,
77
+ prompt_suffix: str = "",
78
+ knowledge_graph=None,
79
+ guide_planning: bool = False,
80
+ min_confidence: float = 0.30,
81
+ parallel: bool = False,
82
+ ):
83
+ self.model_name = model_name
84
+ self.event_log = event_log or EventLog()
85
+ self.strategy = strategy
86
+ self.prompt_suffix = prompt_suffix or ""
87
+ self.knowledge_graph = knowledge_graph
88
+ self.guide_planning = guide_planning
89
+ self.min_confidence = min_confidence
90
+ self.parallel = parallel
91
+
92
+ def plan(self, task: Task) -> list[Task]:
93
+ """Break task into subtasks (strategy DAG or LLM). Emit planner lifecycle events."""
94
+ self._emit(events.PLANNER_STARTED, {"task_id": task.id})
95
+
96
+ if self.strategy is not None:
97
+ subtasks = self.strategy.plan(task)
98
+ if subtasks:
99
+ for st in subtasks:
100
+ if getattr(st, "role", None) is None:
101
+ st.role = infer_role_from_description(st.description or "")
102
+ if self.parallel:
103
+ st.dependencies = []
104
+ self._emit(events.TASK_CREATED, {"task_id": st.id, "description": st.description})
105
+ self._emit(events.PLANNER_FINISHED, {"task_id": task.id, "subtask_count": len(subtasks)})
106
+ return subtasks
107
+
108
+ # Simple single-step prompt: skip LLM decomposition (avoids overcomplication)
109
+ if _is_simple_task(task.description or ""):
110
+ if getattr(task, "role", None) is None:
111
+ task.role = infer_role_from_description(task.description or "")
112
+ self._emit(events.TASK_CREATED, {"task_id": task.id, "description": task.description})
113
+ self._emit(events.PLANNER_FINISHED, {"task_id": task.id, "subtask_count": 1})
114
+ return [task]
115
+
116
+ task_description = (task.description or "") + self.prompt_suffix
117
+ kg_section = ""
118
+ if self.guide_planning and self.knowledge_graph is not None:
119
+ from devsper.knowledge.query import query_for_planning, format_planning_context
120
+ planning_ctx = query_for_planning(self.knowledge_graph, task_description)
121
+ if planning_ctx.confidence > self.min_confidence:
122
+ kg_section = "\n\n## Relevant prior knowledge:\n" + format_planning_context(planning_ctx) + "\n\nUse this to avoid re-discovering known facts."
123
+ self._emit(
124
+ events.PLANNER_KG_CONTEXT_INJECTED,
125
+ {
126
+ "concept_count": len(planning_ctx.relevant_concepts),
127
+ "finding_count": len(planning_ctx.prior_findings),
128
+ "confidence": planning_ctx.confidence,
129
+ },
130
+ )
131
+ prompt = PLANNER_PROMPT.format(task_description=task_description, kg_section=kg_section or "")
132
+ raw = generate(self.model_name, prompt)
133
+ steps = self._parse_numbered_list(raw)
134
+
135
+ subtasks: list[Task] = []
136
+ task_ids: list[str] = []
137
+ for i, description in enumerate(steps, start=1):
138
+ task_id = _short_id()
139
+ task_ids.append(task_id)
140
+ deps = [] if self.parallel else ([task_ids[i - 2]] if i > 1 else [])
141
+ role = infer_role_from_description(description.strip())
142
+ subtask = Task(id=task_id, description=description.strip(), dependencies=deps, role=role)
143
+ subtasks.append(subtask)
144
+ self._emit(
145
+ events.TASK_CREATED,
146
+ {"task_id": task_id, "description": subtask.description},
147
+ )
148
+
149
+ self._emit(events.PLANNER_FINISHED, {"task_id": task.id, "subtask_count": len(subtasks)})
150
+ return subtasks
151
+
152
+ def expand_tasks(self, completed_task: Task, context: list[Task] | None = None) -> list[Task]:
153
+ """
154
+ After a task completes, optionally generate additional tasks (dynamic DAG growth).
155
+ New tasks depend on the completed task. Emits TASK_CREATED for each.
156
+ """
157
+ result_preview = (completed_task.result or "")[:500]
158
+ prompt = EXPAND_TASKS_PROMPT.format(
159
+ task_description=completed_task.description,
160
+ result_preview=result_preview,
161
+ )
162
+ raw = generate(self.model_name, prompt)
163
+ steps = self._parse_numbered_list(raw)
164
+ if not steps:
165
+ return []
166
+
167
+ new_tasks: list[Task] = []
168
+ for i, description in enumerate(steps, start=1):
169
+ task_id = _short_id()
170
+ role = infer_role_from_description(description.strip())
171
+ subtask = Task(
172
+ id=task_id,
173
+ description=description.strip(),
174
+ dependencies=[completed_task.id],
175
+ role=role,
176
+ )
177
+ new_tasks.append(subtask)
178
+ self._emit(events.TASK_CREATED, {"task_id": task_id, "description": subtask.description})
179
+
180
+ return new_tasks
181
+
182
+ def _parse_numbered_list(self, text: str) -> list[str]:
183
+ """Parse '1. step\\n2. step' into ['step', 'step', ...]."""
184
+ steps = []
185
+ for line in text.strip().splitlines():
186
+ line = line.strip()
187
+ if not line:
188
+ continue
189
+ m = NUMBERED_LINE.match(line)
190
+ if m:
191
+ steps.append(m.group(1).strip())
192
+ return steps
193
+
194
+ def _emit(self, event_type: events, payload: dict) -> None:
195
+ self.event_log.append_event(
196
+ Event(timestamp=datetime.now(timezone.utc), type=event_type, payload=payload)
197
+ )
@@ -0,0 +1,91 @@
1
+ """
2
+ Speculative pre-fetching: pre-warm memory context and tool selection for likely successor tasks.
3
+ v1.7.
4
+ """
5
+
6
+ import asyncio
7
+ from dataclasses import dataclass
8
+ from datetime import datetime, timezone
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ from devsper.tools.base import Tool
13
+
14
+
15
+ @dataclass
16
+ class PrefetchResult:
17
+ memory_context: str
18
+ tools: list # list[Tool]
19
+ computed_at: datetime
20
+
21
+
22
+ class TaskPrefetcher:
23
+ """
24
+ While a task is running, pre-warm its likely successors:
25
+ fetch memory context and select tools in background.
26
+ Results are cached and consumed by the agent when the task actually starts.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ memory_router,
32
+ tool_selector,
33
+ score_store=None,
34
+ max_age_seconds: float = 30.0,
35
+ ):
36
+ self._memory_router = memory_router
37
+ self._tool_selector = tool_selector
38
+ self._score_store = score_store
39
+ self._max_age_seconds = max_age_seconds
40
+ self._warmup_cache: dict[str, PrefetchResult] = {}
41
+ self._lock = asyncio.Lock()
42
+
43
+ async def prefetch(self, task) -> None:
44
+ """Run in background via asyncio.create_task()."""
45
+ task_id = getattr(task, "id", "")
46
+ description = getattr(task, "description", "") or ""
47
+ loop = asyncio.get_running_loop()
48
+
49
+ def _get_memory() -> str:
50
+ if not self._memory_router or not description:
51
+ return ""
52
+ try:
53
+ return self._memory_router.get_memory_context(description)
54
+ except Exception:
55
+ return ""
56
+
57
+ def _get_tools():
58
+ if not self._tool_selector:
59
+ return []
60
+ try:
61
+ return self._tool_selector(
62
+ description,
63
+ role=getattr(task, "role", None),
64
+ score_store=self._score_store,
65
+ )
66
+ except Exception:
67
+ return []
68
+
69
+ memory_ctx = await loop.run_in_executor(None, _get_memory)
70
+ tools = await loop.run_in_executor(None, _get_tools)
71
+ async with self._lock:
72
+ self._warmup_cache[task_id] = PrefetchResult(
73
+ memory_context=memory_ctx,
74
+ tools=tools,
75
+ computed_at=datetime.now(timezone.utc),
76
+ )
77
+
78
+ def consume(self, task_id: str) -> PrefetchResult | None:
79
+ """
80
+ Called by Agent at start — returns pre-warmed context or None.
81
+ Result older than max_age_seconds is treated as stale and not used.
82
+ """
83
+ result = self._warmup_cache.pop(task_id, None)
84
+ if result is None:
85
+ return None
86
+ age_seconds = (
87
+ datetime.now(timezone.utc) - result.computed_at
88
+ ).total_seconds()
89
+ if age_seconds > self._max_age_seconds:
90
+ return None
91
+ return result
@@ -0,0 +1,153 @@
1
+ """
2
+ Scheduler: manage the task DAG and determine which tasks are runnable.
3
+
4
+ Supports: add_tasks, get_ready_tasks, get_speculative_tasks, mark_completed,
5
+ confirm_speculative_for, discard_speculative_for, is_finished.
6
+ v1.9: Single source of truth — get_task, get_all_tasks, get_results, snapshot, restore.
7
+ """
8
+
9
+ from datetime import datetime, timezone
10
+ import networkx as nx
11
+
12
+ from devsper.types.task import Task, TaskStatus
13
+ from devsper.types.exceptions import TaskNotFoundError
14
+ from devsper.swarm.speculation import (
15
+ get_speculative_candidates,
16
+ confirm_speculative,
17
+ discard_speculative,
18
+ )
19
+
20
+
21
+ class Scheduler:
22
+ """Manages a DAG of tasks. Tracks dependencies and exposes runnable tasks. Single source of truth for task state."""
23
+
24
+ def __init__(self, run_id: str = "") -> None:
25
+ self._graph: nx.DiGraph = nx.DiGraph()
26
+ self._tasks: dict[str, Task] = {}
27
+ self.run_id = run_id
28
+
29
+ def get_task(self, task_id: str) -> Task:
30
+ """Return task by id. Raises TaskNotFoundError if not found."""
31
+ if task_id not in self._tasks:
32
+ raise TaskNotFoundError(f"Task not found: {task_id!r}")
33
+ return self._tasks[task_id]
34
+
35
+ def get_all_tasks(self) -> list[Task]:
36
+ """Return all tasks."""
37
+ return list(self._tasks.values())
38
+
39
+ def add_tasks(self, tasks: list[Task]) -> None:
40
+ """Add tasks and build the internal dependency graph."""
41
+ for task in tasks:
42
+ self._tasks[task.id] = task
43
+ self._graph.add_node(task.id)
44
+ for task in tasks:
45
+ for dep in task.dependencies:
46
+ if dep not in self._tasks:
47
+ raise ValueError(f"Unknown dependency: {dep!r}")
48
+ self._graph.add_edge(dep, task.id)
49
+ if not nx.is_directed_acyclic_graph(self._graph):
50
+ raise ValueError("Task graph contains a cycle")
51
+
52
+ def get_ready_tasks(self) -> list[Task]:
53
+ """Return tasks that are runnable: PENDING and all dependencies completed."""
54
+ ready: list[Task] = []
55
+ for task_id, task in self._tasks.items():
56
+ if task.status != TaskStatus.PENDING:
57
+ continue
58
+ deps = list(self._graph.predecessors(task_id))
59
+ if all(self._tasks[dep].status == TaskStatus.COMPLETED for dep in deps):
60
+ ready.append(task)
61
+ return ready
62
+
63
+ def mark_completed(self, task_id: str, result: str = "") -> None:
64
+ """Mark a task completed and set its result. Dependent tasks can become runnable."""
65
+ if task_id in self._tasks:
66
+ t = self._tasks[task_id]
67
+ t.status = TaskStatus.COMPLETED
68
+ t.result = result
69
+
70
+ def mark_failed(self, task_id: str, error: str = "") -> None:
71
+ """Mark a task failed and set its error."""
72
+ if task_id in self._tasks:
73
+ t = self._tasks[task_id]
74
+ t.status = TaskStatus.FAILED
75
+ t.error = error
76
+
77
+ def is_finished(self) -> bool:
78
+ """Return True when every task is completed or failed (no pending/running left)."""
79
+ return all(
80
+ t.status in (TaskStatus.COMPLETED, TaskStatus.FAILED)
81
+ for t in self._tasks.values()
82
+ )
83
+
84
+ def get_results(self) -> dict[str, str]:
85
+ """Return task_id -> result for completed tasks only."""
86
+ return {
87
+ task_id: (t.result or "")
88
+ for task_id, t in self._tasks.items()
89
+ if t.status == TaskStatus.COMPLETED and t.result is not None
90
+ }
91
+
92
+ def snapshot(self) -> dict:
93
+ """Full serializable state for checkpointing and node sync."""
94
+ edges = list(self._graph.edges())
95
+ tasks_data = [t.to_dict() for t in self._tasks.values()]
96
+ completed_count = sum(1 for t in self._tasks.values() if t.status == TaskStatus.COMPLETED)
97
+ failed_count = sum(1 for t in self._tasks.values() if t.status == TaskStatus.FAILED)
98
+ return {
99
+ "run_id": self.run_id,
100
+ "tasks": tasks_data,
101
+ "edges": [[str(u), str(v)] for u, v in edges],
102
+ "completed_count": completed_count,
103
+ "failed_count": failed_count,
104
+ "snapshot_at": datetime.now(timezone.utc).isoformat(),
105
+ }
106
+
107
+ @classmethod
108
+ def restore(cls, snapshot: dict) -> "Scheduler":
109
+ """Reconstruct Scheduler from snapshot dict."""
110
+ run_id = snapshot.get("run_id", "")
111
+ s = cls(run_id=run_id)
112
+ tasks_data = snapshot.get("tasks", [])
113
+ for tdata in tasks_data:
114
+ task = Task.from_dict(tdata)
115
+ s._tasks[task.id] = task
116
+ s._graph.add_node(task.id)
117
+ for edge in snapshot.get("edges", []):
118
+ if len(edge) >= 2:
119
+ u, v = str(edge[0]), str(edge[1])
120
+ if u in s._tasks and v in s._tasks:
121
+ s._graph.add_edge(u, v)
122
+ return s
123
+
124
+ def get_completed_tasks(self) -> list[Task]:
125
+ """Return all completed tasks (for learning/memory storage)."""
126
+ return [
127
+ t
128
+ for t in self._tasks.values()
129
+ if t.status == TaskStatus.COMPLETED and t.result is not None
130
+ ]
131
+
132
+ def get_speculative_tasks(self) -> list[Task]:
133
+ """Return tasks that can be run speculatively (one dep running, rest completed)."""
134
+ candidates = get_speculative_candidates(self._tasks, self._graph)
135
+ for t in candidates:
136
+ t.speculative = True
137
+ return candidates
138
+
139
+ def get_successors(self, task_id: str) -> list[str]:
140
+ """Return task ids that depend on the given task."""
141
+ return list(self._graph.successors(task_id))
142
+
143
+ def confirm_speculative_for(self, completed_task_id: str) -> None:
144
+ """When completed_task_id finishes, confirm its speculative successors (keep results)."""
145
+ for sid in self.get_successors(completed_task_id):
146
+ if sid in self._tasks:
147
+ confirm_speculative(self._tasks[sid])
148
+
149
+ def discard_speculative_for(self, failed_task_id: str) -> None:
150
+ """When failed_task_id fails, discard results of speculative successors."""
151
+ for sid in self.get_successors(failed_task_id):
152
+ if sid in self._tasks:
153
+ discard_speculative(self._tasks[sid])
@@ -0,0 +1,47 @@
1
+ """
2
+ Speculative execution: predict likely next tasks and execute them early.
3
+
4
+ When planner produces DAG A → B → C:
5
+ - Run A
6
+ - Speculatively schedule B and C (they depend only on A)
7
+ - When A completes: confirm B and C (use speculative results) or discard
8
+ """
9
+
10
+ from devsper.types.task import Task, TaskStatus
11
+
12
+
13
+ def get_speculative_candidates(
14
+ tasks: dict[str, Task],
15
+ graph,
16
+ ) -> list[Task]:
17
+ """
18
+ Return tasks that are candidates for speculative execution: all dependencies
19
+ are either COMPLETED or exactly one is RUNNING (and we bet it will complete).
20
+ graph must have .predecessors(node_id) returning an iterable of dependency ids.
21
+ """
22
+ candidates: list[Task] = []
23
+ for task_id, task in tasks.items():
24
+ if task.status != TaskStatus.PENDING:
25
+ continue
26
+ deps = list(graph.predecessors(task_id))
27
+ if not deps:
28
+ continue
29
+ completed = sum(1 for d in deps if tasks[d].status == TaskStatus.COMPLETED)
30
+ running = sum(1 for d in deps if tasks[d].status == TaskStatus.RUNNING)
31
+ # Speculative: all deps done except one which is running
32
+ if running == 1 and completed == len(deps) - 1:
33
+ candidates.append(task)
34
+ return candidates
35
+
36
+
37
+ def confirm_speculative(task: Task) -> None:
38
+ """Mark a speculatively run task as confirmed (keep result)."""
39
+ if task.speculative and task.result is not None:
40
+ task.status = TaskStatus.COMPLETED
41
+
42
+
43
+ def discard_speculative(task: Task) -> None:
44
+ """Discard speculative result; task will be re-run when deps are confirmed."""
45
+ if task.speculative:
46
+ task.status = TaskStatus.PENDING
47
+ task.result = None