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,69 @@
1
+ """Analyze trends in papers by extracting years and term frequency over time (by document)."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+ from devsper.tools.documents._docproc import run_docproc_to_markdown
9
+
10
+ YEAR_PAT = re.compile(r"\b(19\d{2}|20\d{2})\b")
11
+ STOP = frozenset("a an the and or but in on at to for of with by from as is was are were been be have has had do does did will would could should may might must can this that it its".split())
12
+
13
+
14
+ class PaperTrendAnalyzerTool(Tool):
15
+ """
16
+ Extract publication years from documents and aggregate term frequency by year (filename or content).
17
+ """
18
+
19
+ name = "paper_trend_analyzer"
20
+ description = "Analyze trends: years and term frequency across papers."
21
+ input_schema = {
22
+ "type": "object",
23
+ "properties": {
24
+ "file_paths": {
25
+ "type": "array",
26
+ "items": {"type": "string"},
27
+ "description": "Paths to papers",
28
+ },
29
+ "top_terms": {"type": "integer", "description": "Top N terms overall (default 15)"},
30
+ },
31
+ "required": ["file_paths"],
32
+ }
33
+
34
+ def run(self, **kwargs) -> str:
35
+ file_paths = kwargs.get("file_paths")
36
+ top_terms = kwargs.get("top_terms", 15)
37
+ if not file_paths or not isinstance(file_paths, list):
38
+ return "Error: file_paths must be a non-empty list"
39
+ if not isinstance(top_terms, int) or top_terms < 1:
40
+ top_terms = 15
41
+ year_to_terms = {}
42
+ global_count = {}
43
+ for path in file_paths:
44
+ if not isinstance(path, str) or not path.strip():
45
+ continue
46
+ p = Path(path.strip()).resolve()
47
+ if not p.exists() or not p.is_file():
48
+ continue
49
+ content, err = run_docproc_to_markdown(str(p))
50
+ if err:
51
+ continue
52
+ text = content or ""
53
+ years = YEAR_PAT.findall(text)
54
+ year = years[0] if years else "unknown"
55
+ if year not in year_to_terms:
56
+ year_to_terms[year] = []
57
+ words = [w for w in re.findall(r"[a-z]+", text.lower()) if len(w) >= 4 and w not in STOP]
58
+ from collections import Counter
59
+
60
+ year_to_terms[year].extend(words)
61
+ for w in words:
62
+ global_count[w] = global_count.get(w, 0) + 1
63
+ sorted_terms = sorted(global_count.items(), key=lambda x: -x[1])[:top_terms]
64
+ lines = ["Trend analysis", "=" * 40, "Documents by year: " + ", ".join(sorted(year_to_terms.keys()))]
65
+ lines.append("Global top terms: " + ", ".join(t for t, _ in sorted_terms))
66
+ return "\n".join(lines)
67
+
68
+
69
+ register(PaperTrendAnalyzerTool())
@@ -0,0 +1,56 @@
1
+ """Analyze multiple documents in batches (swarm-friendly: split into chunks for parallel workers)."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+ from devsper.tools.documents._docproc import run_docproc_to_markdown, DOCPROC_EXTENSIONS
9
+
10
+
11
+ class ParallelDocumentAnalyzerTool(Tool):
12
+ """
13
+ Split a list of documents into batches for parallel analysis (e.g. by a swarm).
14
+ Returns batch assignments and per-batch file lists; does not run docproc itself.
15
+ """
16
+
17
+ name = "parallel_document_analyzer"
18
+ description = "Split documents into batches for parallel/swarm analysis. Returns batch plan."
19
+ input_schema = {
20
+ "type": "object",
21
+ "properties": {
22
+ "file_paths": {
23
+ "type": "array",
24
+ "items": {"type": "string"},
25
+ "description": "Paths to documents",
26
+ },
27
+ "directory": {"type": "string", "description": "Or directory to scan for docs"},
28
+ "batch_size": {"type": "integer", "description": "Documents per batch (default 5)"},
29
+ },
30
+ "required": [],
31
+ }
32
+
33
+ def run(self, **kwargs) -> str:
34
+ file_paths = kwargs.get("file_paths")
35
+ directory = kwargs.get("directory")
36
+ batch_size = kwargs.get("batch_size", 5)
37
+ if not isinstance(batch_size, int) or batch_size < 1:
38
+ batch_size = 5
39
+ paths = []
40
+ if file_paths and isinstance(file_paths, list):
41
+ paths = [p for p in file_paths if isinstance(p, str) and p.strip()]
42
+ if directory and isinstance(directory, str):
43
+ d = Path(directory).resolve()
44
+ if d.exists() and d.is_dir():
45
+ for ext in DOCPROC_EXTENSIONS:
46
+ paths.extend(str(p) for p in d.rglob(f"*{ext}"))
47
+ if not paths:
48
+ return "Error: provide file_paths or directory"
49
+ batches = []
50
+ for i in range(0, len(paths), batch_size):
51
+ batches.append(paths[i : i + batch_size])
52
+ result = {"total_documents": len(paths), "batch_size": batch_size, "num_batches": len(batches), "batches": batches}
53
+ return json.dumps(result, indent=2)
54
+
55
+
56
+ register(ParallelDocumentAnalyzerTool())
@@ -0,0 +1,71 @@
1
+ """Identify potential research gaps by comparing question-like sentences and missing terms across papers."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+ from devsper.tools.documents._docproc import run_docproc_to_markdown
9
+
10
+ STOP = frozenset("a an the and or but in on at to for of with by from as is was are were been be have has had do does did will would could should may might must can this that it its we they".split())
11
+
12
+
13
+ class ResearchGapFinderTool(Tool):
14
+ """
15
+ Suggest research gaps: question phrases in papers and terms that appear in few documents.
16
+ """
17
+
18
+ name = "research_gap_finder"
19
+ description = "Identify potential research gaps from question phrases and rare terms across papers."
20
+ input_schema = {
21
+ "type": "object",
22
+ "properties": {
23
+ "file_paths": {
24
+ "type": "array",
25
+ "items": {"type": "string"},
26
+ "description": "Paths to papers",
27
+ },
28
+ "min_doc_freq": {"type": "integer", "description": "Term in fewer than N docs = gap candidate (default 2)"},
29
+ },
30
+ "required": ["file_paths"],
31
+ }
32
+
33
+ def run(self, **kwargs) -> str:
34
+ file_paths = kwargs.get("file_paths")
35
+ min_doc_freq = kwargs.get("min_doc_freq", 2)
36
+ if not file_paths or not isinstance(file_paths, list):
37
+ return "Error: file_paths must be a non-empty list"
38
+ if not isinstance(min_doc_freq, int) or min_doc_freq < 1:
39
+ min_doc_freq = 2
40
+ question_pat = re.compile(r"[^.!?]*\?+[^.!?]*")
41
+ doc_terms = []
42
+ all_questions = []
43
+ for path in file_paths:
44
+ if not isinstance(path, str) or not path.strip():
45
+ continue
46
+ p = Path(path.strip()).resolve()
47
+ if not p.exists() or not p.is_file():
48
+ continue
49
+ content, err = run_docproc_to_markdown(str(p))
50
+ if err:
51
+ continue
52
+ text = content or ""
53
+ questions = question_pat.findall(text)
54
+ all_questions.extend(q.strip()[:100] for q in questions if len(q.strip()) > 10)
55
+ words = {w for w in re.findall(r"[a-z]+", text.lower()) if len(w) >= 4 and w not in STOP}
56
+ doc_terms.append((p.name, words))
57
+ term_doc_count = {}
58
+ for _, words in doc_terms:
59
+ for w in words:
60
+ term_doc_count[w] = term_doc_count.get(w, 0) + 1
61
+ gap_terms = [w for w, c in term_doc_count.items() if c < min_doc_freq][:20]
62
+ lines = ["Research gap indicators", "=" * 40]
63
+ if all_questions:
64
+ lines.append("Question phrases (research questions):")
65
+ for q in all_questions[:10]:
66
+ lines.append(f" - {q}")
67
+ lines.append("\nRare terms (in few documents): " + ", ".join(gap_terms[:15]))
68
+ return "\n".join(lines)
69
+
70
+
71
+ register(ResearchGapFinderTool())
@@ -0,0 +1,69 @@
1
+ """Map research topics across documents: term frequency and co-occurrence style groups."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+ from devsper.tools.documents._docproc import run_docproc_to_markdown
9
+
10
+ STOP = frozenset("a an the and or but in on at to for of with by from as is was are were been be have has had do does did will would could should may might must can this that it its".split())
11
+
12
+
13
+ class ResearchTopicMapperTool(Tool):
14
+ """
15
+ Map topics across documents: high-frequency terms per doc and shared terms.
16
+ """
17
+
18
+ name = "research_topic_mapper"
19
+ description = "Map research topics across documents (term frequency and shared terms)."
20
+ input_schema = {
21
+ "type": "object",
22
+ "properties": {
23
+ "file_paths": {
24
+ "type": "array",
25
+ "items": {"type": "string"},
26
+ "description": "Paths to papers",
27
+ },
28
+ "top_per_doc": {"type": "integer", "description": "Top N terms per doc (default 10)"},
29
+ },
30
+ "required": ["file_paths"],
31
+ }
32
+
33
+ def run(self, **kwargs) -> str:
34
+ file_paths = kwargs.get("file_paths")
35
+ top_per_doc = kwargs.get("top_per_doc", 10)
36
+ if not file_paths or not isinstance(file_paths, list):
37
+ return "Error: file_paths must be a non-empty list"
38
+ if not isinstance(top_per_doc, int) or top_per_doc < 1:
39
+ top_per_doc = 10
40
+ doc_topics = {}
41
+ all_terms = set()
42
+ for path in file_paths:
43
+ if not isinstance(path, str) or not path.strip():
44
+ continue
45
+ p = Path(path.strip()).resolve()
46
+ if not p.exists() or not p.is_file():
47
+ continue
48
+ content, err = run_docproc_to_markdown(str(p))
49
+ if err:
50
+ continue
51
+ words = [w for w in re.findall(r"[a-z]+", (content or "").lower()) if len(w) >= 4 and w not in STOP]
52
+ from collections import Counter
53
+
54
+ top = [w for w, _ in Counter(words).most_common(top_per_doc)]
55
+ doc_topics[p.name] = top
56
+ all_terms.update(top)
57
+ shared = []
58
+ for t in all_terms:
59
+ count = sum(1 for tops in doc_topics.values() if t in tops)
60
+ if count > 1:
61
+ shared.append(t)
62
+ lines = ["Topic map", "=" * 40]
63
+ for name, topics in doc_topics.items():
64
+ lines.append(f"{name}: " + ", ".join(topics))
65
+ lines.append("\nShared topics: " + ", ".join(shared[:20]))
66
+ return "\n".join(lines)
67
+
68
+
69
+ register(ResearchTopicMapperTool())
@@ -0,0 +1,58 @@
1
+ """Plan a swarm-based literature review: assign document subsets and themes to batches."""
2
+
3
+ import json
4
+ import re
5
+ from pathlib import Path
6
+
7
+ from devsper.tools.base import Tool
8
+ from devsper.tools.registry import register
9
+
10
+ STOP = frozenset("a an the and or but in on at to for of with by from as is was are were been be have has had do does did will would could should may might must can this that it its".split())
11
+
12
+
13
+ class SwarmLiteratureReviewTool(Tool):
14
+ """
15
+ Plan a literature review for a swarm: split documents into batches and suggest theme keywords per batch.
16
+ Accepts raw text list (e.g. from prior extraction); does not read files.
17
+ """
18
+
19
+ name = "swarm_literature_review"
20
+ description = "Plan swarm-based literature review: batch assignments and theme keywords per batch."
21
+ input_schema = {
22
+ "type": "object",
23
+ "properties": {
24
+ "texts": {
25
+ "type": "array",
26
+ "items": {"type": "string"},
27
+ "description": "Document texts or abstracts (one per item)",
28
+ },
29
+ "batch_size": {"type": "integer", "description": "Texts per batch (default 3)"},
30
+ "top_keywords_per_batch": {"type": "integer", "description": "Theme keywords per batch (default 5)"},
31
+ },
32
+ "required": ["texts"],
33
+ }
34
+
35
+ def run(self, **kwargs) -> str:
36
+ texts = kwargs.get("texts")
37
+ batch_size = kwargs.get("batch_size", 3)
38
+ top_k = kwargs.get("top_keywords_per_batch", 5)
39
+ if not texts or not isinstance(texts, list):
40
+ return "Error: texts must be a non-empty list of strings"
41
+ if not isinstance(batch_size, int) or batch_size < 1:
42
+ batch_size = 3
43
+ if not isinstance(top_k, int) or top_k < 1:
44
+ top_k = 5
45
+ batches = []
46
+ for i in range(0, len(texts), batch_size):
47
+ batch_texts = [t for t in texts[i : i + batch_size] if isinstance(t, str) and t.strip()]
48
+ combined = " ".join(batch_texts).lower()
49
+ words = [w for w in re.findall(r"[a-z]+", combined) if len(w) >= 4 and w not in STOP]
50
+ from collections import Counter
51
+
52
+ keywords = [w for w, _ in Counter(words).most_common(top_k)]
53
+ batches.append({"batch_index": len(batches), "num_docs": len(batch_texts), "theme_keywords": keywords})
54
+ result = {"total_documents": len(texts), "batch_size": batch_size, "batches": batches}
55
+ return json.dumps(result, indent=2)
56
+
57
+
58
+ register(SwarmLiteratureReviewTool())
@@ -0,0 +1,52 @@
1
+ """
2
+ Tool reliability scoring: record results, get scores, blended selection.
3
+ """
4
+
5
+ from devsper.tools.scoring.store import ToolScore, ToolScoreStore
6
+
7
+ _default_store: ToolScoreStore | None = None
8
+
9
+
10
+ def get_default_score_store() -> ToolScoreStore:
11
+ """Return the default tool score store (singleton)."""
12
+ global _default_store
13
+ if _default_store is None:
14
+ _default_store = ToolScoreStore()
15
+ return _default_store
16
+
17
+
18
+ def record_tool_result(
19
+ tool_name: str,
20
+ task_type: str | None,
21
+ success: bool,
22
+ latency_ms: int | None = None,
23
+ error_type: str | None = None,
24
+ ) -> None:
25
+ """Record one tool execution result (success/failure, latency) into the score store."""
26
+ try:
27
+ get_default_score_store().record(
28
+ tool_name=tool_name,
29
+ task_type=task_type,
30
+ success=success,
31
+ latency_ms=latency_ms,
32
+ error_type=error_type,
33
+ )
34
+ except Exception:
35
+ pass
36
+
37
+
38
+ def get_tool_score(tool_name: str) -> ToolScore | None:
39
+ """Return the current ToolScore for a tool, or None if not tracked."""
40
+ try:
41
+ return get_default_score_store().get_score(tool_name)
42
+ except Exception:
43
+ return None
44
+
45
+
46
+ __all__ = [
47
+ "record_tool_result",
48
+ "get_tool_score",
49
+ "get_default_score_store",
50
+ "ToolScoreStore",
51
+ "ToolScore",
52
+ ]
@@ -0,0 +1,44 @@
1
+ """
2
+ Formatting for devsper tools CLI output and analytics summary.
3
+ """
4
+
5
+ from devsper.tools.scoring.scorer import score_label
6
+ from devsper.tools.scoring.store import ToolScore
7
+
8
+
9
+ def generate_tools_report(scores: list[ToolScore]) -> str:
10
+ """
11
+ Summary header: total tools, % excellent/good/degraded/poor;
12
+ highlight top 3 and bottom 3 tools.
13
+ """
14
+ if not scores:
15
+ return "No tool scores recorded."
16
+ total = len(scores)
17
+ excellent = sum(1 for s in scores if s.composite_score >= 0.85)
18
+ good = sum(1 for s in scores if 0.65 <= s.composite_score < 0.85)
19
+ degraded = sum(1 for s in scores if 0.40 <= s.composite_score < 0.65)
20
+ poor = sum(1 for s in scores if s.composite_score < 0.40)
21
+ p_ex = (excellent / total * 100) if total else 0
22
+ p_good = (good / total * 100) if total else 0
23
+ p_deg = (degraded / total * 100) if total else 0
24
+ p_poor = (poor / total * 100) if total else 0
25
+ lines = [
26
+ f"Tool reliability: {total} tools tracked",
27
+ f" excellent: {p_ex:.0f}% good: {p_good:.0f}% degraded: {p_deg:.0f}% poor: {p_poor:.0f}%",
28
+ "",
29
+ ]
30
+ sorted_scores = sorted(scores, key=lambda s: -s.composite_score)
31
+ top3 = sorted_scores[:3]
32
+ bottom3 = sorted_scores[-3:] if len(sorted_scores) >= 3 else sorted_scores
33
+ if top3:
34
+ lines.append("Top 3:")
35
+ for s in top3:
36
+ label = score_label(s.composite_score)
37
+ lines.append(f" {s.tool_name}: {s.composite_score:.2f} ({label})")
38
+ lines.append("")
39
+ if bottom3 and (len(bottom3) < len(top3) or bottom3[0].tool_name != top3[0].tool_name):
40
+ lines.append("Bottom 3:")
41
+ for s in reversed(bottom3):
42
+ label = score_label(s.composite_score)
43
+ lines.append(f" {s.tool_name}: {s.composite_score:.2f} ({label})")
44
+ return "\n".join(lines)
@@ -0,0 +1,39 @@
1
+ """
2
+ ToolScorer: compute composite score from raw stats.
3
+ """
4
+
5
+
6
+ def _clamp(x: float, lo: float, hi: float) -> float:
7
+ return max(lo, min(hi, x))
8
+
9
+
10
+ def compute_composite_score(stats: dict) -> float:
11
+ """
12
+ Compute composite reliability score from stats dict with keys:
13
+ success_rate, avg_latency_ms, total_calls, recent_failures.
14
+ Returns 0.05--1.0.
15
+ """
16
+ total_calls = stats.get("total_calls", 0)
17
+ if total_calls < 5:
18
+ return 0.75
19
+ success_rate = stats.get("success_rate", 0.0)
20
+ if success_rate == 0.0 and total_calls >= 10:
21
+ return 0.05
22
+ avg_latency_ms = stats.get("avg_latency_ms", 0.0)
23
+ recent_failures = stats.get("recent_failures", 0)
24
+ reliability = success_rate
25
+ speed = 1.0 - _clamp(avg_latency_ms / 10000.0, 0.0, 1.0)
26
+ recency = 1.0 - (recent_failures / 20.0)
27
+ composite = (reliability * 0.50) + (speed * 0.30) + (recency * 0.20)
28
+ return _clamp(composite, 0.05, 1.0)
29
+
30
+
31
+ def score_label(score: float) -> str:
32
+ """Return label for a composite score."""
33
+ if score >= 0.85:
34
+ return "excellent"
35
+ if score >= 0.65:
36
+ return "good"
37
+ if score >= 0.40:
38
+ return "degraded"
39
+ return "poor"
@@ -0,0 +1,61 @@
1
+ """
2
+ Updated top-k selection: similarity × reliability (blended score).
3
+ """
4
+
5
+ import os
6
+
7
+ from devsper.memory.embeddings import embed_text
8
+ from devsper.tools.base import Tool
9
+
10
+ from devsper.tools.scoring.store import ToolScoreStore, ToolScore
11
+
12
+
13
+ def _cosine_similarity(a: list[float], b: list[float]) -> float:
14
+ if not a or not b or len(a) != len(b):
15
+ return 0.0
16
+ dot = sum(x * y for x, y in zip(a, b))
17
+ na = sum(x * x for x in a) ** 0.5
18
+ nb = sum(x * x for x in b) ** 0.5
19
+ if na == 0 or nb == 0:
20
+ return 0.0
21
+ return dot / (na * nb)
22
+
23
+
24
+ def select_tools_scored(
25
+ task_description: str,
26
+ available_tools: list[Tool],
27
+ top_k: int,
28
+ score_store: ToolScoreStore | None,
29
+ ) -> list[Tool]:
30
+ """
31
+ Select top_k tools by blended score: 70% similarity + 30% reliability.
32
+ New tools (< 5 calls) get neutral 0.75 reliability. If DEVSPER_DISABLE_TOOL_SCORING=1
33
+ or score_store is None, ranking is similarity-only.
34
+ """
35
+ if not available_tools:
36
+ return []
37
+ if top_k <= 0:
38
+ return available_tools
39
+
40
+ disable = os.environ.get("DEVSPER_DISABLE_TOOL_SCORING", "").strip() == "1"
41
+ use_reliability = not disable and score_store is not None
42
+
43
+ task_embedding = embed_text(task_description or " ")
44
+ tool_texts = [f"{t.name}: {t.description}" for t in available_tools]
45
+ tool_embeddings = [embed_text(t) for t in tool_texts]
46
+
47
+ scored: list[tuple[Tool, float]] = []
48
+ for tool, te in zip(available_tools, tool_embeddings):
49
+ sim = _cosine_similarity(task_embedding, te)
50
+ if use_reliability:
51
+ ts = score_store.get_score(tool.name)
52
+ reliability = (
53
+ ts.composite_score if ts and not ts.is_new else 0.75
54
+ )
55
+ blended = (sim * 0.70) + (reliability * 0.30)
56
+ else:
57
+ blended = sim
58
+ scored.append((tool, blended))
59
+
60
+ scored.sort(key=lambda x: -x[1])
61
+ return [t for t, _ in scored[:top_k]]