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,53 @@
1
+ """Compute histogram (count per bin) for a list of numbers."""
2
+
3
+ from collections import defaultdict
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class HistogramTool(Tool):
10
+ """Compute histogram: bin edges and counts. Optional number of bins."""
11
+
12
+ name = "histogram"
13
+ description = "Compute histogram of values. Returns bin ranges and counts."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {
17
+ "values": {"type": "array", "description": "List of numbers"},
18
+ "bins": {"type": "integer", "description": "Number of bins (default 10)"},
19
+ },
20
+ "required": ["values"],
21
+ }
22
+
23
+ def run(self, **kwargs) -> str:
24
+ values = kwargs.get("values")
25
+ bins = kwargs.get("bins", 10)
26
+ if not isinstance(values, list):
27
+ return "Error: values must be an array"
28
+ if not isinstance(bins, int) or bins < 1:
29
+ bins = 10
30
+ try:
31
+ nums = [float(v) for v in values]
32
+ except (TypeError, ValueError):
33
+ return "Error: all elements must be numbers"
34
+ if not nums:
35
+ return "Error: empty list"
36
+ lo = min(nums)
37
+ hi = max(nums)
38
+ if hi == lo:
39
+ return f"All values equal: {lo}\ncount: {len(nums)}"
40
+ width = (hi - lo) / bins
41
+ counts = [0] * bins
42
+ for v in nums:
43
+ idx = min(int((v - lo) / width), bins - 1) if width > 0 else 0
44
+ counts[idx] += 1
45
+ lines = []
46
+ for i in range(bins):
47
+ left = lo + i * width
48
+ right = lo + (i + 1) * width
49
+ lines.append(f"[{left:.2f}, {right:.2f}): {counts[i]}")
50
+ return "\n".join(lines)
51
+
52
+
53
+ register(HistogramTool())
@@ -0,0 +1,47 @@
1
+ """Simple linear regression: y = mx + b from lists of x and y."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+
7
+ class LinearRegressionTool(Tool):
8
+ """Fit y = mx + b to x and y lists. Returns slope and intercept."""
9
+
10
+ name = "linear_regression"
11
+ description = "Fit linear regression to x and y data. Returns slope m and intercept b."
12
+ input_schema = {
13
+ "type": "object",
14
+ "properties": {
15
+ "x": {"type": "array", "description": "List of x values (numbers)"},
16
+ "y": {"type": "array", "description": "List of y values (numbers)"},
17
+ },
18
+ "required": ["x", "y"],
19
+ }
20
+
21
+ def run(self, **kwargs) -> str:
22
+ x = kwargs.get("x")
23
+ y = kwargs.get("y")
24
+ if not isinstance(x, list) or not isinstance(y, list):
25
+ return "Error: x and y must be arrays"
26
+ if len(x) != len(y) or len(x) < 2:
27
+ return "Error: x and y must have same length >= 2"
28
+ try:
29
+ import numpy as np
30
+ X = np.array(x, dtype=float)
31
+ Y = np.array(y, dtype=float)
32
+ n = len(X)
33
+ sx = X.sum()
34
+ sy = Y.sum()
35
+ sxy = (X * Y).sum()
36
+ sxx = (X * X).sum()
37
+ denom = n * sxx - sx * sx
38
+ if abs(denom) < 1e-12:
39
+ return "Error: singular (collinear) data"
40
+ m = (n * sxy - sx * sy) / denom
41
+ b = (sy - m * sx) / n
42
+ return f"slope m = {m}\nintercept b = {b}\ny = {m}*x + {b}"
43
+ except (ImportError, ValueError, TypeError) as e:
44
+ return f"Error: {e}"
45
+
46
+
47
+ register(LinearRegressionTool())
@@ -0,0 +1,38 @@
1
+ """Multiply two matrices (lists of lists). Uses numpy if available."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+
7
+ class MatrixMultiplyTool(Tool):
8
+ """Multiply two matrices given as JSON arrays. Uses numpy."""
9
+
10
+ name = "matrix_multiply"
11
+ description = "Multiply two matrices (list of lists). Requires numpy."
12
+ input_schema = {
13
+ "type": "object",
14
+ "properties": {
15
+ "matrix_a": {"type": "array", "description": "First matrix (list of rows)"},
16
+ "matrix_b": {"type": "array", "description": "Second matrix (list of rows)"},
17
+ },
18
+ "required": ["matrix_a", "matrix_b"],
19
+ }
20
+
21
+ def run(self, **kwargs) -> str:
22
+ a = kwargs.get("matrix_a")
23
+ b = kwargs.get("matrix_b")
24
+ if not isinstance(a, list) or not isinstance(b, list):
25
+ return "Error: matrix_a and matrix_b must be arrays"
26
+ try:
27
+ import numpy as np
28
+ A = np.array(a)
29
+ B = np.array(b)
30
+ C = A @ B
31
+ return str(C.tolist())
32
+ except ImportError:
33
+ return "Error: numpy required"
34
+ except Exception as e:
35
+ return f"Error: {e}"
36
+
37
+
38
+ register(MatrixMultiplyTool())
@@ -0,0 +1,35 @@
1
+ """Compute mean and standard deviation of a list of numbers."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+
7
+ class MeanStdTool(Tool):
8
+ """Compute mean and standard deviation of a list of numbers."""
9
+
10
+ name = "mean_std"
11
+ description = "Compute mean and standard deviation of a list of numbers."
12
+ input_schema = {
13
+ "type": "object",
14
+ "properties": {"values": {"type": "array", "description": "List of numbers"}},
15
+ "required": ["values"],
16
+ }
17
+
18
+ def run(self, **kwargs) -> str:
19
+ values = kwargs.get("values")
20
+ if not isinstance(values, list):
21
+ return "Error: values must be an array"
22
+ try:
23
+ nums = [float(v) for v in values]
24
+ except (TypeError, ValueError):
25
+ return "Error: all elements must be numbers"
26
+ if not nums:
27
+ return "Error: empty list"
28
+ n = len(nums)
29
+ mean = sum(nums) / n
30
+ variance = sum((x - mean) ** 2 for x in nums) / n
31
+ std = variance ** 0.5
32
+ return f"mean = {mean}\nstd = {std}\nn = {n}"
33
+
34
+
35
+ register(MeanStdTool())
@@ -0,0 +1,43 @@
1
+ """Simple Monte Carlo: sample random values and report mean/std."""
2
+
3
+ import random
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class MonteCarloSimulationTool(Tool):
10
+ """Run a simple Monte Carlo: N samples from uniform(a,b), return mean and std."""
11
+
12
+ name = "monte_carlo_simulation"
13
+ description = "Monte Carlo: N uniform samples in [a,b]. Returns mean and std."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {
17
+ "n": {"type": "integer", "description": "Number of samples"},
18
+ "a": {"type": "number", "description": "Lower bound"},
19
+ "b": {"type": "number", "description": "Upper bound"},
20
+ },
21
+ "required": ["n", "a", "b"],
22
+ }
23
+
24
+ def run(self, **kwargs) -> str:
25
+ n = kwargs.get("n")
26
+ a = kwargs.get("a")
27
+ b = kwargs.get("b")
28
+ if not isinstance(n, int) or n < 1:
29
+ return "Error: n must be a positive integer"
30
+ try:
31
+ a, b = float(a), float(b)
32
+ except (TypeError, ValueError):
33
+ return "Error: a and b must be numbers"
34
+ if a >= b:
35
+ return "Error: a must be less than b"
36
+ values = [random.uniform(a, b) for _ in range(n)]
37
+ mean = sum(values) / n
38
+ variance = sum((x - mean) ** 2 for x in values) / n
39
+ std = variance ** 0.5
40
+ return f"n = {n}\nmean = {mean}\nstd = {std}"
41
+
42
+
43
+ register(MonteCarloSimulationTool())
@@ -0,0 +1,40 @@
1
+ """Fit a polynomial of given degree to x, y data. Uses numpy."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+
7
+ class PolynomialFitTool(Tool):
8
+ """Fit polynomial coefficients to x and y. Returns coefficients (highest degree first)."""
9
+
10
+ name = "polynomial_fit"
11
+ description = "Fit polynomial of given degree to x, y. Returns coefficients."
12
+ input_schema = {
13
+ "type": "object",
14
+ "properties": {
15
+ "x": {"type": "array", "description": "x values"},
16
+ "y": {"type": "array", "description": "y values"},
17
+ "degree": {"type": "integer", "description": "Polynomial degree (default 2)"},
18
+ },
19
+ "required": ["x", "y"],
20
+ }
21
+
22
+ def run(self, **kwargs) -> str:
23
+ x = kwargs.get("x")
24
+ y = kwargs.get("y")
25
+ degree = kwargs.get("degree", 2)
26
+ if not isinstance(x, list) or not isinstance(y, list):
27
+ return "Error: x and y must be arrays"
28
+ if len(x) != len(y) or len(x) < 2:
29
+ return "Error: x and y must have same length >= 2"
30
+ if not isinstance(degree, int) or degree < 1:
31
+ degree = 2
32
+ try:
33
+ import numpy as np
34
+ coefs = np.polyfit(x, y, degree)
35
+ return "coefficients (high to low): " + str(coefs.tolist())
36
+ except (ImportError, Exception) as e:
37
+ return f"Error: {e}"
38
+
39
+
40
+ register(PolynomialFitTool())
@@ -0,0 +1,36 @@
1
+ """Draw a random sample from a list of values."""
2
+
3
+ import random
4
+
5
+ from devsper.tools.base import Tool
6
+ from devsper.tools.registry import register
7
+
8
+
9
+ class RandomSampleTool(Tool):
10
+ """Return k random items from a list (without replacement)."""
11
+
12
+ name = "random_sample"
13
+ description = "Draw k random elements from a list without replacement."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {
17
+ "values": {"type": "array", "description": "List of values"},
18
+ "k": {"type": "integer", "description": "Number of items to sample"},
19
+ },
20
+ "required": ["values", "k"],
21
+ }
22
+
23
+ def run(self, **kwargs) -> str:
24
+ values = kwargs.get("values")
25
+ k = kwargs.get("k")
26
+ if not isinstance(values, list):
27
+ return "Error: values must be an array"
28
+ if not isinstance(k, int) or k < 1:
29
+ return "Error: k must be a positive integer"
30
+ if k > len(values):
31
+ return "Error: k cannot exceed list length"
32
+ sample = random.sample(values, k)
33
+ return str(sample)
34
+
35
+
36
+ register(RandomSampleTool())
@@ -0,0 +1,23 @@
1
+ """
2
+ MCP (Model Context Protocol) integration: client, tool adapter, discovery.
3
+ """
4
+
5
+ from devsper.config.schema import MCPServerConfig
6
+ from devsper.tools.mcp.client import (
7
+ MCPClient,
8
+ MCPToolDefinition,
9
+ )
10
+ from devsper.tools.mcp.adapter import MCPToolAdapter
11
+ from devsper.tools.mcp.discovery import (
12
+ discover_mcp_tools,
13
+ register_mcp_server,
14
+ )
15
+
16
+ __all__ = [
17
+ "MCPClient",
18
+ "MCPToolAdapter",
19
+ "MCPServerConfig",
20
+ "MCPToolDefinition",
21
+ "discover_mcp_tools",
22
+ "register_mcp_server",
23
+ ]
@@ -0,0 +1,53 @@
1
+ """
2
+ MCPToolAdapter: wrap an MCP tool as a devsper Tool for the tool registry.
3
+ """
4
+
5
+ import asyncio
6
+
7
+ from devsper.tools.base import Tool
8
+ from devsper.tools.registry import register
9
+
10
+ from devsper.tools.mcp.client import MCPClient, MCPToolDefinition
11
+
12
+
13
+ class MCPToolAdapter(Tool):
14
+ """
15
+ Wraps an MCPToolDefinition as a devsper Tool so it appears in the
16
+ existing tool registry and selection pipeline unchanged.
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ server_name: str,
22
+ definition: MCPToolDefinition,
23
+ client: MCPClient,
24
+ ) -> None:
25
+ # "{server_name}.{tool_name}" to avoid collisions
26
+ self.name = f"{server_name}.{definition.name}"
27
+ self.description = definition.description or f"MCP tool: {definition.name}"
28
+ self.category = "mcp"
29
+ self.input_schema = definition.input_schema
30
+ self._client = client
31
+ self._mcp_tool_name = definition.name
32
+ # Register in tool registry on adapter creation
33
+ register(self)
34
+
35
+ def run(self, **kwargs) -> str:
36
+ """Execute the MCP tool via client.call_tool; sync wrapper around async."""
37
+ try:
38
+ loop = asyncio.get_event_loop()
39
+ except RuntimeError:
40
+ loop = asyncio.new_event_loop()
41
+ asyncio.set_event_loop(loop)
42
+ if loop.is_running():
43
+ # If we're already inside an async context, run in a new loop (e.g. sync call from sync code)
44
+ import concurrent.futures
45
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
46
+ future = pool.submit(
47
+ asyncio.run,
48
+ self._client.call_tool(self._mcp_tool_name, kwargs),
49
+ )
50
+ return future.result()
51
+ return loop.run_until_complete(
52
+ self._client.call_tool(self._mcp_tool_name, kwargs)
53
+ )
@@ -0,0 +1,235 @@
1
+ """
2
+ MCP client: connect to MCP servers over stdio, HTTP, or SSE.
3
+ Implements initialize handshake, tools/list, tools/call.
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ import os
9
+ from dataclasses import dataclass, field
10
+ from typing import Any
11
+
12
+ from devsper.protocols import MCP_PROTOCOL_VERSION
13
+ from devsper.types.exceptions import MCPToolError
14
+
15
+
16
+ @dataclass
17
+ class MCPToolDefinition:
18
+ """One tool as returned by MCP tools/list."""
19
+ name: str
20
+ description: str
21
+ input_schema: dict # JSON Schema as returned by MCP server
22
+
23
+
24
+ def _next_id() -> int:
25
+ """Simple monotonic id for JSON-RPC requests."""
26
+ _next_id._n = getattr(_next_id, "_n", 0) + 1
27
+ return _next_id._n
28
+
29
+
30
+ class MCPClient:
31
+ """
32
+ Connects to an MCP server over stdio, HTTP, or SSE transport.
33
+ Implements MCP protocol: initialize handshake, tools/list, tools/call.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ name: str,
39
+ transport: str,
40
+ *,
41
+ command: list[str] | None = None,
42
+ url: str | None = None,
43
+ env: dict[str, str] | None = None,
44
+ timeout_seconds: int = 30,
45
+ auto_reconnect: bool = True,
46
+ ) -> None:
47
+ self.name = name
48
+ self.transport = transport
49
+ self.command = command or []
50
+ self.url = (url or "").rstrip("/")
51
+ self.env = dict(env or {})
52
+ self.timeout_seconds = timeout_seconds
53
+ self.auto_reconnect = auto_reconnect
54
+ self._server_capabilities: dict[str, Any] = {}
55
+ self._stdio_process: asyncio.subprocess.Process | None = None
56
+ self._stdio_reader: asyncio.StreamReader | None = None
57
+ self._stdio_writer: asyncio.StreamWriter | None = None
58
+ self._connected = False
59
+
60
+ async def connect(self) -> None:
61
+ """Send initialize request, await initialized notification, cache server capabilities."""
62
+ if self.transport == "stdio":
63
+ await self._connect_stdio()
64
+ elif self.transport in ("http", "sse"):
65
+ await self._connect_http_sse()
66
+ else:
67
+ raise ValueError(f"Unsupported MCP transport: {self.transport}")
68
+
69
+ req = {
70
+ "jsonrpc": "2.0",
71
+ "id": _next_id(),
72
+ "method": "initialize",
73
+ "params": {
74
+ "protocolVersion": MCP_PROTOCOL_VERSION,
75
+ "capabilities": {},
76
+ "clientInfo": {"name": "devsper", "version": "1.10.5"},
77
+ },
78
+ }
79
+ resp = await self._request(req)
80
+ if "result" in resp:
81
+ self._server_capabilities = resp["result"].get("capabilities", {})
82
+ # Send initialized notification (no response expected)
83
+ await self._send_notification("notifications/initialized", {})
84
+ self._connected = True
85
+
86
+ async def _connect_stdio(self) -> None:
87
+ if not self.command:
88
+ raise ValueError("stdio transport requires command")
89
+ env = os.environ.copy()
90
+ env.update(self.env)
91
+ self._stdio_process = await asyncio.create_subprocess_exec(
92
+ *self.command,
93
+ stdin=asyncio.subprocess.PIPE,
94
+ stdout=asyncio.subprocess.PIPE,
95
+ stderr=asyncio.subprocess.DEVNULL,
96
+ env=env,
97
+ )
98
+ assert self._stdio_process.stdin and self._stdio_process.stdout
99
+ self._stdio_reader = self._stdio_process.stdout
100
+ self._stdio_writer = self._stdio_process.stdin
101
+
102
+ async def _connect_http_sse(self) -> None:
103
+ if not self.url:
104
+ raise ValueError("http/sse transport requires url")
105
+ # HTTP: no persistent connection for initialize; we'll use POST per request
106
+ self._connected = True
107
+
108
+ def _get_http_client(self):
109
+ import httpx
110
+ return httpx.AsyncClient(timeout=float(self.timeout_seconds)) # type: ignore[return-value]
111
+
112
+ async def _request(self, req: dict) -> dict:
113
+ if self.transport == "stdio":
114
+ return await self._request_stdio(req)
115
+ return await self._request_http(req)
116
+
117
+ async def _request_stdio(self, req: dict) -> dict:
118
+ if not self._stdio_writer or not self._stdio_reader:
119
+ raise RuntimeError("stdio not connected")
120
+ msg = json.dumps(req) + "\n"
121
+ self._stdio_writer.write(msg.encode("utf-8"))
122
+ await self._stdio_writer.drain()
123
+ line = await asyncio.wait_for(
124
+ self._stdio_reader.readline(),
125
+ timeout=self.timeout_seconds,
126
+ )
127
+ if not line:
128
+ raise ConnectionError("MCP server closed stdin")
129
+ data = json.loads(line.decode("utf-8").strip())
130
+ if "error" in data:
131
+ raise MCPToolError(data["error"].get("message", str(data["error"])))
132
+ return data
133
+
134
+ async def _request_http(self, req: dict) -> dict:
135
+ import httpx
136
+ client = self._get_http_client()
137
+ try:
138
+ # MCP over HTTP: POST JSON-RPC to endpoint
139
+ endpoint = self.url if self.url.endswith("/") else f"{self.url}/"
140
+ r = await client.post(endpoint, json=req)
141
+ r.raise_for_status()
142
+ out = r.json()
143
+ if "error" in out:
144
+ raise MCPToolError(out["error"].get("message", str(out["error"])))
145
+ return out
146
+ except httpx.HTTPError as e:
147
+ raise MCPToolError(str(e))
148
+ finally:
149
+ await client.aclose()
150
+
151
+ async def _send_notification(self, method: str, params: dict) -> None:
152
+ """Send a notification (no id, no response)."""
153
+ msg = {"jsonrpc": "2.0", "method": method, "params": params or {}}
154
+ if self.transport == "stdio" and self._stdio_writer:
155
+ self._stdio_writer.write((json.dumps(msg) + "\n").encode("utf-8"))
156
+ await self._stdio_writer.drain()
157
+ elif self.transport in ("http", "sse"):
158
+ async with self._get_http_client() as client:
159
+ await client.post(
160
+ self.url if self.url.endswith("/") else f"{self.url}/",
161
+ json=msg,
162
+ )
163
+
164
+ async def list_tools(self) -> list[MCPToolDefinition]:
165
+ """Send tools/list request; return list of MCPToolDefinition."""
166
+ req = {
167
+ "jsonrpc": "2.0",
168
+ "id": _next_id(),
169
+ "method": "tools/list",
170
+ "params": {},
171
+ }
172
+ resp = await self._request(req)
173
+ result = resp.get("result") or {}
174
+ tools_data = result.get("tools", [])
175
+ return [
176
+ MCPToolDefinition(
177
+ name=t.get("name", ""),
178
+ description=t.get("description", ""),
179
+ input_schema=t.get("inputSchema", {}),
180
+ )
181
+ for t in tools_data
182
+ ]
183
+
184
+ async def call_tool(self, name: str, arguments: dict) -> str:
185
+ """Send tools/call request; extract text from result.content; on error raise MCPToolError."""
186
+ req = {
187
+ "jsonrpc": "2.0",
188
+ "id": _next_id(),
189
+ "method": "tools/call",
190
+ "params": {"name": name, "arguments": arguments or {}},
191
+ }
192
+ resp = await self._request(req)
193
+ result = resp.get("result")
194
+ if result is None:
195
+ raise MCPToolError(resp.get("error", {}).get("message", "Unknown error"))
196
+ if result.get("isError"):
197
+ content = result.get("content", [])
198
+ parts = [
199
+ c.get("text", str(c))
200
+ for c in content
201
+ if isinstance(c, dict)
202
+ ]
203
+ raise MCPToolError("\n".join(parts) if parts else "Tool returned error")
204
+ content = result.get("content") or []
205
+ texts = [
206
+ c.get("text", "") if isinstance(c, dict) else str(c)
207
+ for c in content
208
+ ]
209
+ return "\n".join(texts)
210
+
211
+ async def disconnect(self) -> None:
212
+ """Close connection."""
213
+ if self.transport == "stdio" and self._stdio_writer:
214
+ try:
215
+ self._stdio_writer.close()
216
+ await self._stdio_writer.wait_closed()
217
+ except Exception:
218
+ pass
219
+ self._stdio_writer = None
220
+ self._stdio_reader = None
221
+ if self._stdio_process and self._stdio_process.returncode is None:
222
+ self._stdio_process.terminate()
223
+ try:
224
+ await asyncio.wait_for(self._stdio_process.wait(), timeout=2.0)
225
+ except asyncio.TimeoutError:
226
+ self._stdio_process.kill()
227
+ self._stdio_process = None
228
+ self._connected = False
229
+
230
+ async def reconnect(self) -> None:
231
+ """Disconnect and connect again."""
232
+ await self.disconnect()
233
+ await self.connect()
234
+
235
+
@@ -0,0 +1,53 @@
1
+ """
2
+ MCP discovery: connect to server, list tools, wrap as MCPToolAdapter and optionally register.
3
+ """
4
+
5
+ from devsper.config.schema import MCPServerConfig
6
+ from devsper.tools.mcp.client import MCPClient, MCPToolDefinition
7
+ from devsper.tools.mcp.adapter import MCPToolAdapter
8
+
9
+
10
+ def _config_to_client(config: MCPServerConfig) -> MCPClient:
11
+ """Build MCPClient from config model."""
12
+ return MCPClient(
13
+ name=config.name,
14
+ transport=config.transport,
15
+ command=config.command,
16
+ url=config.url,
17
+ env=config.env,
18
+ timeout_seconds=config.timeout_seconds,
19
+ auto_reconnect=config.auto_reconnect,
20
+ )
21
+
22
+
23
+ async def discover_mcp_tools_async(server_config: MCPServerConfig) -> list[MCPToolAdapter]:
24
+ """Connect client, list tools, wrap each as MCPToolAdapter, return list."""
25
+ client = _config_to_client(server_config)
26
+ await client.connect()
27
+ definitions = await client.list_tools()
28
+ adapters = [
29
+ MCPToolAdapter(server_config.name, defn, client)
30
+ for defn in definitions
31
+ ]
32
+ return adapters
33
+
34
+
35
+ def discover_mcp_tools(server_config: MCPServerConfig) -> list[MCPToolAdapter]:
36
+ """Synchronous wrapper: connect, list tools, wrap as MCPToolAdapter."""
37
+ import asyncio
38
+ return asyncio.run(discover_mcp_tools_async(server_config))
39
+
40
+
41
+ async def register_mcp_server_async(server_config: MCPServerConfig) -> int:
42
+ """Discover MCP tools and register each adapter in the tool registry. Return count."""
43
+ adapters = await discover_mcp_tools_async(server_config)
44
+ from devsper.tools.registry import register
45
+ for adapter in adapters:
46
+ register(adapter)
47
+ return len(adapters)
48
+
49
+
50
+ def register_mcp_server(server_config: MCPServerConfig) -> int:
51
+ """Synchronous: discover and register MCP tools; return count registered."""
52
+ import asyncio
53
+ return asyncio.run(register_mcp_server_async(server_config))
@@ -0,0 +1,16 @@
1
+ """Memory tools: store, search, list, delete, tag, summarize memory."""
2
+
3
+ from devsper.tools.memory.store_memory import StoreMemoryTool
4
+ from devsper.tools.memory.search_memory import SearchMemoryTool
5
+ from devsper.tools.memory.list_memory import ListMemoryTool
6
+ from devsper.tools.memory.delete_memory import DeleteMemoryTool
7
+ from devsper.tools.memory.tag_memory import TagMemoryTool
8
+ from devsper.tools.memory.summarize_memory import SummarizeMemoryTool
9
+ from devsper.tools.registry import register
10
+
11
+ register(StoreMemoryTool())
12
+ register(SearchMemoryTool())
13
+ register(ListMemoryTool())
14
+ register(DeleteMemoryTool())
15
+ register(TagMemoryTool())
16
+ register(SummarizeMemoryTool())