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,239 @@
1
+ """
2
+ MetaPlanner: decomposes mega-tasks into sub-swarms with dependencies, SLAs, and priorities.
3
+ Coordinates execution across sub-swarms and monitors SLA breaches.
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ from dataclasses import dataclass, field
9
+ from datetime import datetime, timezone
10
+ from typing import Literal
11
+
12
+ from devsper.utils.models import generate
13
+
14
+
15
+ @dataclass
16
+ class SLAConfig:
17
+ max_duration_seconds: int
18
+ max_cost_usd: float | None = None
19
+ min_quality_score: float | None = None # from critic scores
20
+ on_breach: Literal["cancel", "escalate", "continue"] = "continue"
21
+
22
+
23
+ @dataclass
24
+ class SubSwarmSpec:
25
+ swarm_id: str
26
+ root_task: str
27
+ priority: int # 1 (highest) to 10 (lowest)
28
+ sla: SLAConfig
29
+ worker_count: int
30
+ model_override: str | None = None
31
+ depends_on: list[str] = field(default_factory=list) # other swarm_ids
32
+
33
+
34
+ @dataclass
35
+ class SLABreach:
36
+ swarm_id: str
37
+ breach_type: Literal["duration", "cost", "quality"]
38
+ limit: float
39
+ actual: float
40
+ action_taken: str
41
+
42
+
43
+ @dataclass
44
+ class MetaRunResult:
45
+ mega_task: str
46
+ sub_swarm_results: dict[str, dict] # swarm_id -> results
47
+ total_duration_seconds: float
48
+ total_cost_usd: float | None
49
+ sla_breaches: list[SLABreach]
50
+ final_synthesis: str
51
+
52
+
53
+ def _validate_specs(specs: list[SubSwarmSpec]) -> None:
54
+ """Raise ValueError if priority out of range or depends_on has cycles."""
55
+ ids_ = {s.swarm_id for s in specs}
56
+ for s in specs:
57
+ if not (1 <= s.priority <= 10):
58
+ raise ValueError(f"SubSwarmSpec {s.swarm_id}: priority must be 1-10, got {s.priority}")
59
+ for dep in s.depends_on:
60
+ if dep not in ids_:
61
+ raise ValueError(f"SubSwarmSpec {s.swarm_id}: depends_on {dep!r} not in swarm ids")
62
+ # Cycle detection
63
+ graph: dict[str, list[str]] = {s.swarm_id: list(s.depends_on) for s in specs}
64
+ path = set()
65
+ visited = set()
66
+
67
+ def visit(n: str) -> None:
68
+ if n in path:
69
+ raise ValueError(f"Cycle in depends_on involving {n!r}")
70
+ if n in visited:
71
+ return
72
+ path.add(n)
73
+ for c in graph.get(n, []):
74
+ visit(c)
75
+ path.remove(n)
76
+ visited.add(n)
77
+
78
+ for n in graph:
79
+ if n not in visited:
80
+ visit(n)
81
+
82
+
83
+ def _topological_order(specs: list[SubSwarmSpec]) -> list[SubSwarmSpec]:
84
+ """Return specs in dependency order (dependencies first)."""
85
+ by_id = {s.swarm_id: s for s in specs}
86
+ in_degree = {s.swarm_id: 0 for s in specs}
87
+ for s in specs:
88
+ for dep in s.depends_on:
89
+ in_degree[s.swarm_id] += 1
90
+ from collections import deque
91
+ q: list[str] = [i for i, d in in_degree.items() if d == 0]
92
+ order: list[str] = []
93
+ while q:
94
+ n = q.pop(0)
95
+ order.append(n)
96
+ for s in specs:
97
+ if n in s.depends_on:
98
+ in_degree[s.swarm_id] -= 1
99
+ if in_degree[s.swarm_id] == 0:
100
+ q.append(s.swarm_id)
101
+ if len(order) != len(specs):
102
+ order = [s.swarm_id for s in sorted(specs, key=lambda x: (x.priority, x.swarm_id))]
103
+ return [by_id[i] for i in order if i in by_id]
104
+
105
+
106
+ class MetaPlanner:
107
+ """Decomposes mega-tasks into sub-swarms and runs them with SLA monitoring."""
108
+
109
+ def __init__(self, model_name: str = "mock", max_swarms: int = 20) -> None:
110
+ self.model_name = model_name
111
+ self.max_swarms = max_swarms
112
+
113
+ async def decompose(self, mega_task: str) -> list[SubSwarmSpec]:
114
+ """LLM call: given mega_task, produce JSON list of SubSwarmSpecs. Validates no cycles and priority 1-10."""
115
+ prompt = f"""You are a meta-planner. Given the following mega-task, decompose it into independent or dependent sub-tasks, each to be run as a separate swarm.
116
+
117
+ Mega-task: {mega_task}
118
+
119
+ Respond with a JSON array of objects. Each object must have:
120
+ - swarm_id: string (short id, e.g. "research", "code", "review")
121
+ - root_task: string (the task this swarm will execute)
122
+ - priority: int (1 = highest, 10 = lowest)
123
+ - sla: object with max_duration_seconds (int), max_cost_usd (number or null), min_quality_score (number 0-1 or null), on_breach ("cancel"|"escalate"|"continue")
124
+ - worker_count: int (1-8)
125
+ - model_override: string or null
126
+ - depends_on: array of swarm_ids that must complete before this one (can be empty)
127
+
128
+ Ensure no circular dependencies. Return only the JSON array, no markdown."""
129
+
130
+ out = await asyncio.to_thread(generate, self.model_name, prompt)
131
+ text = (out or "").strip()
132
+ if "```" in text:
133
+ for part in text.split("```"):
134
+ part = part.strip()
135
+ if part.startswith("json") or part.startswith("["):
136
+ text = part[4:].strip() if part.startswith("json") else part
137
+ break
138
+ try:
139
+ raw = json.loads(text)
140
+ except json.JSONDecodeError:
141
+ raw = []
142
+ if not isinstance(raw, list):
143
+ raw = []
144
+ specs: list[SubSwarmSpec] = []
145
+ for i, item in enumerate(raw[: self.max_swarms]):
146
+ if not isinstance(item, dict):
147
+ continue
148
+ sla = item.get("sla") or {}
149
+ sla_config = SLAConfig(
150
+ max_duration_seconds=int(sla.get("max_duration_seconds", 300)),
151
+ max_cost_usd=float(sla["max_cost_usd"]) if sla.get("max_cost_usd") is not None else None,
152
+ min_quality_score=float(sla["min_quality_score"]) if sla.get("min_quality_score") is not None else None,
153
+ on_breach=(sla.get("on_breach") or "continue").lower()[:8],
154
+ )
155
+ if sla_config.on_breach not in ("cancel", "escalate", "continue"):
156
+ sla_config.on_breach = "continue"
157
+ spec = SubSwarmSpec(
158
+ swarm_id=str(item.get("swarm_id", f"swarm_{i}")),
159
+ root_task=str(item.get("root_task", mega_task)),
160
+ priority=int(item.get("priority", 5)),
161
+ sla=sla_config,
162
+ worker_count=max(1, min(8, int(item.get("worker_count", 2)))),
163
+ model_override=str(item["model_override"]) if item.get("model_override") else None,
164
+ depends_on=[str(d) for d in item.get("depends_on") or []],
165
+ )
166
+ specs.append(spec)
167
+ _validate_specs(specs)
168
+ return specs
169
+
170
+ async def run(self, mega_task: str, max_swarms: int | None = None, budget_usd: float | None = None) -> MetaRunResult:
171
+ """Decompose -> build DAG -> run sub-swarms in order; monitor SLAs; return aggregated result with synthesis."""
172
+ max_n = max_swarms or self.max_swarms
173
+ specs = await self.decompose(mega_task)
174
+ specs = specs[:max_n]
175
+ ordered = _topological_order(specs)
176
+ sub_swarm_results: dict[str, dict] = {}
177
+ sla_breaches: list[SLABreach] = []
178
+ total_cost: float | None = None
179
+ start = datetime.now(timezone.utc)
180
+
181
+ from devsper.swarm.swarm import Swarm
182
+ from devsper.config import get_config
183
+ from devsper.utils.event_logger import EventLog
184
+
185
+ cfg = get_config()
186
+ event_log = EventLog(events_folder_path=cfg.events_dir)
187
+
188
+ for spec in ordered:
189
+ swarm_start = datetime.now(timezone.utc)
190
+ swarm = Swarm(
191
+ worker_count=spec.worker_count,
192
+ worker_model=spec.model_override or cfg.models.worker,
193
+ planner_model=cfg.models.planner,
194
+ event_log=event_log,
195
+ config=cfg,
196
+ )
197
+ try:
198
+ result = swarm.run(spec.root_task)
199
+ except Exception as e:
200
+ result = {"error": str(e)}
201
+ elapsed = (datetime.now(timezone.utc) - swarm_start).total_seconds()
202
+ sub_swarm_results[spec.swarm_id] = result
203
+
204
+ if elapsed > spec.sla.max_duration_seconds:
205
+ breach = SLABreach(
206
+ swarm_id=spec.swarm_id,
207
+ breach_type="duration",
208
+ limit=float(spec.sla.max_duration_seconds),
209
+ actual=elapsed,
210
+ action_taken=spec.sla.on_breach,
211
+ )
212
+ sla_breaches.append(breach)
213
+ if spec.sla.on_breach == "cancel":
214
+ break
215
+
216
+ total_duration_seconds = (datetime.now(timezone.utc) - start).total_seconds()
217
+
218
+ # Final synthesis via LLM
219
+ results_summary = json.dumps({k: (v if isinstance(v, dict) else {"result": str(v)}) for k, v in sub_swarm_results.items()}, indent=0)[:6000]
220
+ synth_prompt = f"""Mega-task: {mega_task}
221
+
222
+ Sub-swarm results summary:
223
+ {results_summary}
224
+
225
+ SLA breaches (if any): {[f"{b.swarm_id}: {b.breach_type}" for b in sla_breaches]}
226
+
227
+ Write a short final synthesis (2-4 sentences) summarizing the overall outcome and any caveats."""
228
+
229
+ synth_out = await asyncio.to_thread(generate, self.model_name, synth_prompt)
230
+ final_synthesis = (synth_out or "").strip() or "No synthesis generated."
231
+
232
+ return MetaRunResult(
233
+ mega_task=mega_task,
234
+ sub_swarm_results=sub_swarm_results,
235
+ total_duration_seconds=total_duration_seconds,
236
+ total_cost_usd=total_cost,
237
+ sla_breaches=sla_breaches,
238
+ final_synthesis=final_synthesis,
239
+ )
@@ -0,0 +1,61 @@
1
+ """
2
+ PriorityScheduler: extends Scheduler with priority-aware task ordering.
3
+ get_ready_tasks() returns tasks sorted by priority, then dependency impact, then estimated duration.
4
+ """
5
+
6
+ from devsper.swarm.scheduler import Scheduler
7
+ from devsper.types.task import Task, TaskStatus
8
+
9
+
10
+ class PriorityScheduler(Scheduler):
11
+ """Scheduler that orders ready tasks by priority (1=highest), then by dependency impact, then shortest first."""
12
+
13
+ def __init__(self, run_id: str = "") -> None:
14
+ super().__init__(run_id=run_id)
15
+ self._priority: dict[str, int] = {} # task_id -> priority (lower = higher priority)
16
+
17
+ def add_task(self, task: Task, priority: int = 5) -> None:
18
+ """Add a single task with optional priority (1-10, default 5)."""
19
+ if not (1 <= priority <= 10):
20
+ priority = max(1, min(10, priority))
21
+ self._priority[task.id] = priority
22
+ super().add_tasks([task])
23
+
24
+ def add_tasks(self, tasks: list[Task]) -> None:
25
+ """Add tasks; each task gets priority from task.priority if present, else 5."""
26
+ for t in tasks:
27
+ if t.id not in self._priority:
28
+ p = getattr(t, "priority", 5)
29
+ self._priority[t.id] = max(1, min(10, p)) if isinstance(p, int) else 5
30
+ super().add_tasks(tasks)
31
+
32
+ def bump_priority(self, task_id: str, new_priority: int) -> None:
33
+ """Set priority for a task (e.g. for SLA escalation). Lower number = higher priority."""
34
+ if task_id in self._tasks:
35
+ self._priority[task_id] = max(1, min(10, new_priority))
36
+
37
+ def _priority_of(self, task_id: str) -> int:
38
+ return self._priority.get(task_id, 5)
39
+
40
+ def get_ready_tasks(self) -> list[Task]:
41
+ """Return ready tasks sorted by: 1) priority (lower first), 2) dependencies satisfied count (unblock more first), 3) estimated duration (shortest first)."""
42
+ ready = super().get_ready_tasks()
43
+ if not ready:
44
+ return ready
45
+
46
+ def unblock_count(task_id: str) -> int:
47
+ return len(list(self._graph.successors(task_id)))
48
+
49
+ def estimated_duration(task: Task) -> float:
50
+ # Heuristic: length of description as proxy for duration
51
+ desc = task.description or ""
52
+ return float(len(desc))
53
+
54
+ ready.sort(
55
+ key=lambda t: (
56
+ self._priority_of(t.id),
57
+ -unblock_count(t.id), # higher successor count first
58
+ estimated_duration(t),
59
+ )
60
+ )
61
+ return ready
@@ -0,0 +1,19 @@
1
+ """Plugin system: discover and load tools via entry_points."""
2
+
3
+ from devsper.plugins.plugin_loader import load_plugins
4
+ from devsper.plugins.plugin_registry import (
5
+ PluginInfo,
6
+ clear_plugins,
7
+ get_plugin,
8
+ list_plugins,
9
+ register_plugin,
10
+ )
11
+
12
+ __all__ = [
13
+ "load_plugins",
14
+ "PluginInfo",
15
+ "register_plugin",
16
+ "get_plugin",
17
+ "list_plugins",
18
+ "clear_plugins",
19
+ ]
File without changes
@@ -0,0 +1,70 @@
1
+ """Discover and load plugins via entry_points (devsper.plugins)."""
2
+
3
+ import importlib.metadata
4
+ import logging
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+ from devsper.plugins.plugin_registry import register_plugin
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ ENTRY_POINT_GROUP = "devsper.plugins"
13
+
14
+
15
+ def _load_entry_points(group: str = ENTRY_POINT_GROUP):
16
+ try:
17
+ eps = importlib.metadata.entry_points(group=group)
18
+ except TypeError:
19
+ eps = importlib.metadata.entry_points().get(group, [])
20
+ return list(eps)
21
+
22
+
23
+ def _invoke_plugin(ep: importlib.metadata.EntryPoint) -> list[str]:
24
+ """
25
+ Load entry point and register tools. Entry point can be:
26
+ - A callable that returns a list of Tool instances
27
+ - A callable that takes no args and calls register() itself (returns None or list of tool names)
28
+ Returns list of tool names registered.
29
+ """
30
+ try:
31
+ fn = ep.load()
32
+ except Exception as e:
33
+ logger.warning("Plugin %s failed to load: %s", ep.name, e)
34
+ return []
35
+ registered: list[str] = []
36
+ try:
37
+ result = fn()
38
+ if result is None:
39
+ return registered
40
+ if isinstance(result, list):
41
+ for item in result:
42
+ if isinstance(item, Tool):
43
+ register(item)
44
+ registered.append(item.name)
45
+ elif isinstance(item, str):
46
+ registered.append(item)
47
+ except Exception as e:
48
+ logger.warning("Plugin %s failed to run: %s", ep.name, e)
49
+ return registered
50
+
51
+
52
+ def load_plugins(enabled: list[str] | None = None) -> list[str]:
53
+ """
54
+ Discover plugins from entry_points and register their tools.
55
+ If enabled is provided, only load those plugin names; otherwise load all.
56
+ Returns list of plugin names that were loaded.
57
+ """
58
+ loaded: list[str] = []
59
+ for ep in _load_entry_points():
60
+ if enabled is not None and ep.name not in enabled:
61
+ continue
62
+ try:
63
+ dist = ep.dist
64
+ version = dist.version if dist else ""
65
+ except Exception:
66
+ version = ""
67
+ tool_names = _invoke_plugin(ep)
68
+ register_plugin(ep.name, version=version, tools=tool_names)
69
+ loaded.append(ep.name)
70
+ return loaded
@@ -0,0 +1,34 @@
1
+ """Registry of loaded plugins: name, version, tools registered."""
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+
6
+ @dataclass
7
+ class PluginInfo:
8
+ name: str
9
+ version: str = ""
10
+ tools_registered: list[str] = field(default_factory=list)
11
+
12
+
13
+ _plugins: dict[str, PluginInfo] = {}
14
+
15
+
16
+ def register_plugin(name: str, version: str = "", tools: list[str] | None = None) -> None:
17
+ _plugins[name] = PluginInfo(
18
+ name=name,
19
+ version=version,
20
+ tools_registered=list(tools or []),
21
+ )
22
+
23
+
24
+ def get_plugin(name: str) -> PluginInfo | None:
25
+ return _plugins.get(name)
26
+
27
+
28
+ def list_plugins() -> list[PluginInfo]:
29
+ return list(_plugins.values())
30
+
31
+
32
+ def clear_plugins() -> None:
33
+ """Clear registry (mainly for tests)."""
34
+ _plugins.clear()
@@ -0,0 +1,83 @@
1
+ """Shared HTTP client and token helpers for the devsper package registry."""
2
+
3
+ import os
4
+
5
+ import httpx
6
+
7
+ REGISTRY_URL = os.environ.get(
8
+ "DEVSPER_REGISTRY_URL",
9
+ "https://registry.devsper.com",
10
+ )
11
+
12
+ CREDENTIAL_SERVICE = "devsper_registry"
13
+ CREDENTIAL_USERNAME = "api_key"
14
+
15
+
16
+ def get_token() -> str | None:
17
+ """Read stored API key from OS keychain, falling back to env var."""
18
+ try:
19
+ from devsper.credentials import get_credential
20
+
21
+ token = get_credential(CREDENTIAL_SERVICE, CREDENTIAL_USERNAME)
22
+ if token:
23
+ return token
24
+ except Exception:
25
+ pass
26
+ return os.environ.get("DEVSPER_API_KEY")
27
+
28
+
29
+ def set_token(token: str) -> None:
30
+ """Store API key in OS keychain."""
31
+ from devsper.credentials import set_credential
32
+
33
+ set_credential(CREDENTIAL_SERVICE, CREDENTIAL_USERNAME, token)
34
+
35
+
36
+ def delete_token() -> None:
37
+ """Remove API key from OS keychain."""
38
+ try:
39
+ from devsper.credentials import delete_credential
40
+
41
+ delete_credential(CREDENTIAL_SERVICE, CREDENTIAL_USERNAME)
42
+ except Exception:
43
+ pass
44
+
45
+
46
+ def require_token() -> str:
47
+ """Get token or exit with helpful message."""
48
+ token = get_token()
49
+ if not token:
50
+ from rich.console import Console
51
+
52
+ Console().print(
53
+ "[red]Not logged in.[/red] Run [bold]devsper reg login[/bold] first.\n"
54
+ "Or set [bold]DEVSPER_API_KEY[/bold] env var for CI."
55
+ )
56
+ raise SystemExit(1)
57
+ return token
58
+
59
+
60
+ class RegistryClient:
61
+ """Thin HTTP wrapper for the devsper package registry API."""
62
+
63
+ def __init__(self, token: str | None = None):
64
+ self.base = REGISTRY_URL.rstrip("/")
65
+ self.token = token
66
+ self._client = httpx.Client(timeout=30)
67
+
68
+ def _headers(self) -> dict:
69
+ h: dict[str, str] = {"Content-Type": "application/json"}
70
+ if self.token:
71
+ h["X-API-Key"] = self.token
72
+ return h
73
+
74
+ def get(self, path: str, **kwargs) -> httpx.Response:
75
+ return self._client.get(f"{self.base}{path}", headers=self._headers(), **kwargs)
76
+
77
+ def post(self, path: str, **kwargs) -> httpx.Response:
78
+ return self._client.post(
79
+ f"{self.base}{path}", headers=self._headers(), **kwargs
80
+ )
81
+
82
+ def close(self) -> None:
83
+ self._client.close()
@@ -0,0 +1,6 @@
1
+ """Protocol version constants for MCP and A2A."""
2
+
3
+ MCP_PROTOCOL_VERSION = "2024-11-05"
4
+ A2A_PROTOCOL_VERSION = "0.2.1"
5
+
6
+ __all__ = ["MCP_PROTOCOL_VERSION", "A2A_PROTOCOL_VERSION"]
@@ -0,0 +1,17 @@
1
+ """Provider adapters: base interface, router, and implementations."""
2
+
3
+ from devsper.providers.base import BaseProvider, MockProvider
4
+ from devsper.providers.router import ProviderRouter, get_router
5
+ from devsper.providers.openai import OpenAIProvider
6
+ from devsper.providers.anthropic import AnthropicProvider
7
+ from devsper.providers.gemini import GeminiProvider
8
+
9
+ __all__ = [
10
+ "BaseProvider",
11
+ "MockProvider",
12
+ "ProviderRouter",
13
+ "get_router",
14
+ "OpenAIProvider",
15
+ "AnthropicProvider",
16
+ "GeminiProvider",
17
+ ]
@@ -0,0 +1,84 @@
1
+ """Anthropic provider adapter using LangChain. Supports native Anthropic and Azure (Foundry) Claude."""
2
+
3
+ import os
4
+ from typing import Iterator
5
+
6
+ from langchain_anthropic import ChatAnthropic
7
+ from langchain_core.messages import HumanMessage
8
+
9
+ from devsper.providers.base import BaseProvider
10
+
11
+
12
+ def _normalize_azure_base_url(url: str) -> str:
13
+ """Strip trailing /messages so base URL ends with /v1; ChatAnthropic will append /messages."""
14
+ if not url:
15
+ return url
16
+ url = url.rstrip("/")
17
+ if url.endswith("/messages"):
18
+ return url[: -len("/messages")]
19
+ return url
20
+
21
+
22
+ class AnthropicProvider(BaseProvider):
23
+ """
24
+ Anthropic API adapter. Uses ANTHROPIC_API_KEY (or pass api_key).
25
+ When AZURE_ANTHROPIC_* env vars are set, uses Azure Foundry Claude endpoint instead.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ api_key: str | None = None,
31
+ *,
32
+ azure: bool = False,
33
+ azure_endpoint: str | None = None,
34
+ azure_api_key: str | None = None,
35
+ azure_deployment: str | None = None,
36
+ ) -> None:
37
+ self.azure = azure or bool(
38
+ os.environ.get("AZURE_ANTHROPIC_ENDPOINT") or os.environ.get("AZURE_ANTHROPIC_API_KEY")
39
+ )
40
+ if self.azure:
41
+ self.azure_endpoint = _normalize_azure_base_url(
42
+ azure_endpoint or os.environ.get("AZURE_ANTHROPIC_ENDPOINT", "")
43
+ )
44
+ self.azure_api_key = azure_api_key or os.environ.get("AZURE_ANTHROPIC_API_KEY")
45
+ self.azure_deployment = (azure_deployment or os.environ.get("AZURE_ANTHROPIC_DEPLOYMENT_NAME") or "").strip()
46
+ if not self.azure_endpoint or not self.azure_api_key:
47
+ raise ValueError(
48
+ "Azure Anthropic requires AZURE_ANTHROPIC_ENDPOINT and AZURE_ANTHROPIC_API_KEY"
49
+ )
50
+ self.api_key = self.azure_api_key
51
+ else:
52
+ self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY")
53
+ self.azure_endpoint = ""
54
+ self.azure_api_key = ""
55
+ self.azure_deployment = ""
56
+ if not self.api_key:
57
+ raise ValueError("Anthropic requires api_key or ANTHROPIC_API_KEY")
58
+
59
+ def generate(self, model: str, prompt: str, stream: bool = False) -> str | Iterator[str]:
60
+ """Call Anthropic (or Azure Claude) API and return the model output text."""
61
+ if self.azure:
62
+ deployment = (model or self.azure_deployment or "").strip() or self.azure_deployment
63
+ if not deployment:
64
+ raise ValueError("Azure Anthropic requires model name or AZURE_ANTHROPIC_DEPLOYMENT_NAME")
65
+ llm = ChatAnthropic(
66
+ model=deployment,
67
+ anthropic_api_url=self.azure_endpoint,
68
+ anthropic_api_key=self.azure_api_key,
69
+ temperature=0,
70
+ )
71
+ else:
72
+ llm = ChatAnthropic(
73
+ model=model,
74
+ api_key=self.api_key,
75
+ temperature=0,
76
+ )
77
+ message = llm.invoke([HumanMessage(content=prompt)])
78
+ content = message.content
79
+ text = content if isinstance(content, str) else str(content)
80
+ if stream:
81
+ def _gen():
82
+ yield text
83
+ return _gen()
84
+ return text