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,65 @@
1
+ """Detect outliers using IQR or z-score (simple heuristic)."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+ try:
7
+ import numpy as np
8
+ except ImportError:
9
+ np = None
10
+
11
+
12
+ class DatasetOutlierDetectorTool(Tool):
13
+ """
14
+ Detect outliers in a numeric array using IQR (Q1 - 1.5*IQR, Q3 + 1.5*IQR) or z-score.
15
+ """
16
+
17
+ name = "dataset_outlier_detector"
18
+ description = "Detect outliers in numeric data using IQR or z-score method."
19
+ input_schema = {
20
+ "type": "object",
21
+ "properties": {
22
+ "values": {
23
+ "type": "array",
24
+ "items": {"type": "number"},
25
+ "description": "Numeric array",
26
+ },
27
+ "method": {"type": "string", "description": "iqr or zscore (default iqr)"},
28
+ "z_threshold": {"type": "number", "description": "Z-score threshold (default 3.0)"},
29
+ },
30
+ "required": ["values"],
31
+ }
32
+
33
+ def run(self, **kwargs) -> str:
34
+ values = kwargs.get("values")
35
+ method = kwargs.get("method", "iqr")
36
+ z_threshold = kwargs.get("z_threshold", 3.0)
37
+ if not values or not isinstance(values, list):
38
+ return "Error: values must be a non-empty list of numbers"
39
+ if np is None:
40
+ return "Error: numpy is required for this tool"
41
+ try:
42
+ arr = np.array(values, dtype=float)
43
+ arr = arr[~np.isnan(arr)]
44
+ except (ValueError, TypeError):
45
+ return "Error: all values must be numeric"
46
+ if len(arr) < 4:
47
+ return "Error: need at least 4 values"
48
+ if method == "zscore":
49
+ mean, std = np.mean(arr), np.std(arr)
50
+ if std == 0:
51
+ return "No variance; no outliers."
52
+ z = np.abs((arr - mean) / std)
53
+ outliers = arr[z > z_threshold]
54
+ else:
55
+ q1, q3 = np.percentile(arr, 25), np.percentile(arr, 75)
56
+ iqr = q3 - q1
57
+ if iqr == 0:
58
+ return "IQR is 0; no outliers."
59
+ low, high = q1 - 1.5 * iqr, q3 + 1.5 * iqr
60
+ outliers = arr[(arr < low) | (arr > high)]
61
+ count = len(outliers)
62
+ return f"Outliers: {count} ({100*count/len(arr):.1f}%). Sample (first 10): {outliers[:10].tolist()}"
63
+
64
+
65
+ register(DatasetOutlierDetectorTool())
@@ -0,0 +1,76 @@
1
+ """Generate a dataset profile: shape, dtypes, nulls, and basic stats. Uses CSV or numpy array description."""
2
+
3
+ import csv
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+ try:
10
+ import numpy as np
11
+ except ImportError:
12
+ np = None
13
+
14
+
15
+ class DatasetProfileTool(Tool):
16
+ """
17
+ Profile a dataset from CSV or from a list of column arrays: shape, nulls, basic stats.
18
+ """
19
+
20
+ name = "dataset_profile"
21
+ description = "Profile a dataset (CSV path or column lists): shape, nulls, basic stats."
22
+ input_schema = {
23
+ "type": "object",
24
+ "properties": {
25
+ "path": {"type": "string", "description": "Path to CSV file"},
26
+ "sample_rows": {"type": "integer", "description": "Max rows to sample for CSV (default 10000)"},
27
+ },
28
+ "required": ["path"],
29
+ }
30
+
31
+ def run(self, **kwargs) -> str:
32
+ path = kwargs.get("path")
33
+ sample_rows = kwargs.get("sample_rows", 10000)
34
+ if not path or not isinstance(path, str):
35
+ return "Error: path must be a non-empty string"
36
+ if not isinstance(sample_rows, int) or sample_rows < 1:
37
+ sample_rows = 10000
38
+ p = Path(path).resolve()
39
+ if not p.exists() or not p.is_file():
40
+ return f"Error: file not found: {path}"
41
+ try:
42
+ with p.open(encoding="utf-8", errors="replace", newline="") as f:
43
+ reader = csv.reader(f)
44
+ rows = list(reader)
45
+ if not rows:
46
+ return "Empty CSV."
47
+ header = rows[0]
48
+ data = rows[1:][:sample_rows]
49
+ n_rows, n_cols = len(data), len(header)
50
+ null_counts = []
51
+ for j in range(n_cols):
52
+ col = [row[j] if j < len(row) else "" for row in data]
53
+ nulls = sum(1 for c in col if c.strip() == "" or c.lower() in ("na", "nan", "none", "null"))
54
+ null_counts.append(nulls)
55
+ lines = [
56
+ "Dataset profile",
57
+ "=" * 40,
58
+ f"Shape: {n_rows} rows, {n_cols} columns",
59
+ f"Columns: {', '.join(header[:15])}" + ("..." if n_cols > 15 else ""),
60
+ "Null-like counts per column:",
61
+ ]
62
+ for i, (h, nc) in enumerate(zip(header[:20], null_counts[:20])):
63
+ lines.append(f" {h}: {nc}")
64
+ if np is not None and data:
65
+ try:
66
+ arr = np.array(data, dtype=float, order="C")
67
+ mask = np.isnan(arr)
68
+ lines.append(f"\nNumeric stats (if applicable): min={np.nanmin(arr):.4f}, max={np.nanmax(arr):.4f}")
69
+ except (ValueError, TypeError):
70
+ pass
71
+ return "\n".join(lines)
72
+ except Exception as e:
73
+ return f"Error: {e}"
74
+
75
+
76
+ register(DatasetProfileTool())
@@ -0,0 +1,54 @@
1
+ """Split a dataset (row indices or file) into batches for distributed/swarm processing."""
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
+
9
+
10
+ class DistributedDatasetProcessorTool(Tool):
11
+ """
12
+ Split dataset processing into batches: given total rows or a CSV path, return batch ranges.
13
+ """
14
+
15
+ name = "distributed_dataset_processor"
16
+ description = "Split dataset into batches for distributed processing. Returns batch plan."
17
+ input_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "path": {"type": "string", "description": "Path to CSV (to count rows) or use total_rows"},
21
+ "total_rows": {"type": "integer", "description": "Total rows if known"},
22
+ "batch_size": {"type": "integer", "description": "Rows per batch (default 1000)"},
23
+ },
24
+ "required": [],
25
+ }
26
+
27
+ def run(self, **kwargs) -> str:
28
+ path = kwargs.get("path")
29
+ total_rows = kwargs.get("total_rows")
30
+ batch_size = kwargs.get("batch_size", 1000)
31
+ if not isinstance(batch_size, int) or batch_size < 1:
32
+ batch_size = 1000
33
+ if total_rows is not None and isinstance(total_rows, int) and total_rows > 0:
34
+ n = total_rows
35
+ elif path and isinstance(path, str):
36
+ p = Path(path).resolve()
37
+ if not p.exists() or not p.is_file():
38
+ return f"Error: file not found: {path}"
39
+ try:
40
+ n = sum(1 for _ in p.open(encoding="utf-8", errors="replace")) - 1
41
+ n = max(0, n)
42
+ except Exception as e:
43
+ return f"Error: {e}"
44
+ else:
45
+ return "Error: provide path (to CSV) or total_rows"
46
+ batches = []
47
+ for start in range(0, n, batch_size):
48
+ end = min(start + batch_size, n)
49
+ batches.append({"start": start, "end": end, "size": end - start})
50
+ result = {"total_rows": n, "batch_size": batch_size, "num_batches": len(batches), "batches": batches}
51
+ return json.dumps(result, indent=2)
52
+
53
+
54
+ register(DistributedDatasetProcessorTool())
@@ -0,0 +1,69 @@
1
+ """Suggest simple feature engineering: log, sqrt, squared, binning for numeric columns."""
2
+
3
+ import csv
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+ try:
10
+ import numpy as np
11
+ except ImportError:
12
+ np = None
13
+
14
+
15
+ class FeatureEngineeringSuggestionsTool(Tool):
16
+ """
17
+ Suggest feature engineering based on column stats: log (if positive), sqrt, squared, binning.
18
+ """
19
+
20
+ name = "feature_engineering_suggestions"
21
+ description = "Suggest feature engineering: log, sqrt, squared, binning for numeric columns."
22
+ input_schema = {
23
+ "type": "object",
24
+ "properties": {
25
+ "path": {"type": "string", "description": "Path to CSV"},
26
+ "max_columns": {"type": "integer", "description": "Max columns (default 15)"},
27
+ },
28
+ "required": ["path"],
29
+ }
30
+
31
+ def run(self, **kwargs) -> str:
32
+ path = kwargs.get("path")
33
+ max_columns = kwargs.get("max_columns", 15)
34
+ if not path or not isinstance(path, str):
35
+ return "Error: path must be a non-empty string"
36
+ if np is None:
37
+ return "Error: numpy is required for this tool"
38
+ p = Path(path).resolve()
39
+ if not p.exists() or not p.is_file():
40
+ return f"Error: file not found: {path}"
41
+ try:
42
+ with p.open(encoding="utf-8", errors="replace", newline="") as f:
43
+ reader = csv.DictReader(f)
44
+ rows = list(reader)
45
+ if not rows:
46
+ return "Empty CSV."
47
+ lines = ["Feature engineering suggestions", "=" * 40]
48
+ for col in list(rows[0].keys())[:max_columns]:
49
+ try:
50
+ arr = np.array([float(row.get(col, np.nan)) for row in rows], dtype=float)
51
+ arr = arr[~np.isnan(arr)]
52
+ if len(arr) < 3:
53
+ continue
54
+ min_v, max_v = np.min(arr), np.max(arr)
55
+ sugs = []
56
+ if min_v > 0:
57
+ sugs.append("log1p")
58
+ sugs.append("sqrt(abs(x))")
59
+ sugs.append("squared")
60
+ sugs.append("binned (e.g. quintiles)")
61
+ lines.append(f" {col}: " + ", ".join(sugs))
62
+ except (ValueError, TypeError):
63
+ continue
64
+ return "\n".join(lines) if len(lines) > 1 else "No numeric columns found."
65
+ except Exception as e:
66
+ return f"Error: {e}"
67
+
68
+
69
+ register(FeatureEngineeringSuggestionsTool())
@@ -0,0 +1,82 @@
1
+ """Estimate feature importance using simple correlation with a target column (no model)."""
2
+
3
+ import csv
4
+ from pathlib import Path
5
+
6
+ from devsper.tools.base import Tool
7
+ from devsper.tools.registry import register
8
+
9
+ try:
10
+ import numpy as np
11
+ except ImportError:
12
+ np = None
13
+
14
+
15
+ class FeatureImportanceEstimatorTool(Tool):
16
+ """
17
+ Estimate feature importance as absolute correlation with target (numeric). No model training.
18
+ """
19
+
20
+ name = "feature_importance_estimator"
21
+ description = "Estimate feature importance via correlation with target column (numeric)."
22
+ input_schema = {
23
+ "type": "object",
24
+ "properties": {
25
+ "path": {"type": "string", "description": "Path to CSV"},
26
+ "target_column": {"type": "string", "description": "Name of target column"},
27
+ "top_n": {"type": "integer", "description": "Top N features (default 15)"},
28
+ },
29
+ "required": ["path", "target_column"],
30
+ }
31
+
32
+ def run(self, **kwargs) -> str:
33
+ path = kwargs.get("path")
34
+ target_column = kwargs.get("target_column")
35
+ top_n = kwargs.get("top_n", 15)
36
+ if not path or not isinstance(path, str):
37
+ return "Error: path must be a non-empty string"
38
+ if not target_column or not isinstance(target_column, str):
39
+ return "Error: target_column must be a non-empty string"
40
+ if not isinstance(top_n, int) or top_n < 1:
41
+ top_n = 15
42
+ if np is None:
43
+ return "Error: numpy is required for this tool"
44
+ p = Path(path).resolve()
45
+ if not p.exists() or not p.is_file():
46
+ return f"Error: file not found: {path}"
47
+ try:
48
+ with p.open(encoding="utf-8", errors="replace", newline="") as f:
49
+ reader = csv.DictReader(f)
50
+ rows = list(reader)
51
+ if not rows:
52
+ return "Empty CSV."
53
+ headers = list(rows[0].keys())
54
+ if target_column not in headers:
55
+ return f"Error: target column '{target_column}' not in {headers}"
56
+ try:
57
+ y = np.array([float(row.get(target_column, np.nan)) for row in rows], dtype=float)
58
+ except (ValueError, TypeError):
59
+ return "Error: target column must be numeric"
60
+ importance = []
61
+ for col in headers:
62
+ if col == target_column:
63
+ continue
64
+ try:
65
+ x = np.array([float(row.get(col, np.nan)) for row in rows], dtype=float)
66
+ mask = ~(np.isnan(x) | np.isnan(y))
67
+ if mask.sum() < 3:
68
+ continue
69
+ c = np.corrcoef(x[mask], y[mask])[0, 1]
70
+ importance.append((col, abs(float(c))))
71
+ except (ValueError, TypeError):
72
+ continue
73
+ importance.sort(key=lambda t: -t[1])
74
+ lines = ["Feature importance (|correlation| with target)", "=" * 40]
75
+ for name, val in importance[:top_n]:
76
+ lines.append(f" {name}: {val:.4f}")
77
+ return "\n".join(lines) if lines else "No numeric features to correlate."
78
+ except Exception as e:
79
+ return f"Error: {e}"
80
+
81
+
82
+ register(FeatureImportanceEstimatorTool())
@@ -0,0 +1,59 @@
1
+ """Validate model input: check shapes, nulls, and value ranges for a list of arrays."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+ try:
7
+ import numpy as np
8
+ except ImportError:
9
+ np = None
10
+
11
+
12
+ class ModelInputValidatorTool(Tool):
13
+ """
14
+ Validate model inputs: check for NaNs, shapes, and optional value range.
15
+ Accepts a single list of numbers (one array) or a JSON-like structure; validates and reports.
16
+ """
17
+
18
+ name = "model_input_validator"
19
+ description = "Validate model input array: NaNs, shape, value range."
20
+ input_schema = {
21
+ "type": "object",
22
+ "properties": {
23
+ "values": {
24
+ "type": "array",
25
+ "items": {"type": "number"},
26
+ "description": "Single feature array",
27
+ },
28
+ "min_value": {"type": "number", "description": "Expected min (optional)"},
29
+ "max_value": {"type": "number", "description": "Expected max (optional)"},
30
+ },
31
+ "required": ["values"],
32
+ }
33
+
34
+ def run(self, **kwargs) -> str:
35
+ values = kwargs.get("values")
36
+ min_value = kwargs.get("min_value")
37
+ max_value = kwargs.get("max_value")
38
+ if not values or not isinstance(values, list):
39
+ return "Error: values must be a non-empty list of numbers"
40
+ if np is None:
41
+ return "Error: numpy is required for this tool"
42
+ try:
43
+ arr = np.array(values, dtype=float)
44
+ except (ValueError, TypeError):
45
+ return "Error: all values must be numeric"
46
+ issues = []
47
+ nan_count = int(np.isnan(arr).sum())
48
+ if nan_count > 0:
49
+ issues.append(f"NaNs: {nan_count}")
50
+ if min_value is not None and np.nanmin(arr) < min_value:
51
+ issues.append(f"Values below min {min_value}")
52
+ if max_value is not None and np.nanmax(arr) > max_value:
53
+ issues.append(f"Values above max {max_value}")
54
+ if not issues:
55
+ return f"Valid. Shape: {arr.shape}, no NaNs, range [{np.nanmin(arr):.4f}, {np.nanmax(arr):.4f}]"
56
+ return "Validation issues: " + "; ".join(issues)
57
+
58
+
59
+ register(ModelInputValidatorTool())
@@ -0,0 +1,57 @@
1
+ """Basic time series analysis: trend (linear slope), simple stats. Assumes numeric column as series."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+
6
+ try:
7
+ import numpy as np
8
+ except ImportError:
9
+ np = None
10
+
11
+
12
+ class TimeSeriesAnalyzerTool(Tool):
13
+ """
14
+ Analyze a time series (numeric list): linear trend slope, mean, std, min, max.
15
+ """
16
+
17
+ name = "time_series_analyzer"
18
+ description = "Analyze time series: trend slope, mean, std, min, max."
19
+ input_schema = {
20
+ "type": "object",
21
+ "properties": {
22
+ "values": {
23
+ "type": "array",
24
+ "items": {"type": "number"},
25
+ "description": "Time series values (ordered)",
26
+ },
27
+ },
28
+ "required": ["values"],
29
+ }
30
+
31
+ def run(self, **kwargs) -> str:
32
+ values = kwargs.get("values")
33
+ if not values or not isinstance(values, list):
34
+ return "Error: values must be a non-empty list of numbers"
35
+ if np is None:
36
+ return "Error: numpy is required for this tool"
37
+ try:
38
+ arr = np.array(values, dtype=float)
39
+ arr = arr[~np.isnan(arr)]
40
+ except (ValueError, TypeError):
41
+ return "Error: all values must be numeric"
42
+ if len(arr) < 2:
43
+ return "Error: need at least 2 values"
44
+ x = np.arange(len(arr), dtype=float)
45
+ slope = np.polyfit(x, arr, 1)[0]
46
+ lines = [
47
+ "Time series summary",
48
+ "=" * 40,
49
+ f"Length: {len(arr)}",
50
+ f"Mean: {np.mean(arr):.4f}, Std: {np.std(arr):.4f}",
51
+ f"Min: {np.min(arr):.4f}, Max: {np.max(arr):.4f}",
52
+ f"Linear trend slope: {slope:.6f}",
53
+ ]
54
+ return "\n".join(lines)
55
+
56
+
57
+ register(TimeSeriesAnalyzerTool())
@@ -0,0 +1,11 @@
1
+ """Document tools: extract text, images, equations, tables from PDF/DOCX/PPTX/XLSX via docproc; write LaTeX, Word, Markdown with citations."""
2
+
3
+ from devsper.tools.documents.extract_document_text import ExtractDocumentTextTool
4
+ from devsper.tools.documents.extract_document_images import ExtractDocumentImagesTool
5
+ from devsper.tools.documents.extract_equations import ExtractEquationsTool
6
+ from devsper.tools.documents.extract_tables import ExtractTablesTool
7
+ from devsper.tools.documents.document_to_markdown import DocumentToMarkdownTool
8
+ from devsper.tools.documents.summarize_document import SummarizeDocumentTool
9
+ from devsper.tools.documents.write_latex_document import WriteLaTeXDocumentTool
10
+ from devsper.tools.documents.write_markdown_document import WriteMarkdownDocumentTool
11
+ from devsper.tools.documents.write_word_document import WriteWordDocumentTool
@@ -0,0 +1,56 @@
1
+ """Shared helper for docproc CLI: run docproc and read output. Optional dependency."""
2
+
3
+ import subprocess
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+
8
+ DOCPROC_EXTENSIONS = {".pdf", ".docx", ".pptx", ".xlsx"}
9
+
10
+
11
+ def docproc_available() -> bool:
12
+ """Return True if docproc CLI is available."""
13
+ try:
14
+ subprocess.run(
15
+ ["docproc", "--help"],
16
+ capture_output=True,
17
+ timeout=5,
18
+ )
19
+ return True
20
+ except (FileNotFoundError, subprocess.TimeoutExpired):
21
+ return False
22
+
23
+
24
+ def run_docproc_to_markdown(file_path: str) -> tuple[str, str | None]:
25
+ """
26
+ Run docproc --file <path> -o <tmp>.md and read the markdown.
27
+ Returns (content, None) on success or ("", error_message) on failure.
28
+ """
29
+ path = Path(file_path).resolve()
30
+ if not path.exists() or not path.is_file():
31
+ return "", f"File not found: {file_path}"
32
+ if path.suffix.lower() not in DOCPROC_EXTENSIONS:
33
+ return "", f"Unsupported format. Use one of: {DOCPROC_EXTENSIONS}"
34
+ if not docproc_available():
35
+ return "", "docproc not installed. Install with: pip install docproc"
36
+ try:
37
+ with tempfile.NamedTemporaryFile(suffix=".md", delete=False) as f:
38
+ out_path = f.name
39
+ try:
40
+ result = subprocess.run(
41
+ ["docproc", "--file", str(path), "-o", out_path],
42
+ capture_output=True,
43
+ text=True,
44
+ timeout=120,
45
+ )
46
+ if result.returncode != 0:
47
+ err = result.stderr or result.stdout or "Unknown error"
48
+ return "", f"docproc failed: {err}"
49
+ content = Path(out_path).read_text(encoding="utf-8", errors="replace")
50
+ return content, None
51
+ finally:
52
+ Path(out_path).unlink(missing_ok=True)
53
+ except subprocess.TimeoutExpired:
54
+ return "", "docproc timed out"
55
+ except Exception as e:
56
+ return "", str(e)
@@ -0,0 +1,29 @@
1
+ """Convert document to markdown using docproc."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+ from devsper.tools.documents._docproc import run_docproc_to_markdown
6
+
7
+
8
+ class DocumentToMarkdownTool(Tool):
9
+ """Convert PDF, DOCX, PPTX, or XLSX to markdown via docproc. Returns the markdown content."""
10
+
11
+ name = "document_to_markdown"
12
+ description = "Convert document to markdown. Returns full markdown text."
13
+ input_schema = {
14
+ "type": "object",
15
+ "properties": {"file_path": {"type": "string", "description": "Path to the document"}},
16
+ "required": ["file_path"],
17
+ }
18
+
19
+ def run(self, **kwargs) -> str:
20
+ file_path = kwargs.get("file_path")
21
+ if not file_path or not isinstance(file_path, str):
22
+ return "Error: file_path must be a non-empty string"
23
+ content, err = run_docproc_to_markdown(file_path)
24
+ if err:
25
+ return err
26
+ return content.strip() or "(empty markdown)"
27
+
28
+
29
+ register(DocumentToMarkdownTool())
@@ -0,0 +1,39 @@
1
+ """Extract image references/descriptions from docproc markdown output."""
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
+
11
+ class ExtractDocumentImagesTool(Tool):
12
+ """Extract image blocks (markdown image syntax or alt text) from document via docproc."""
13
+
14
+ name = "extract_document_images"
15
+ description = "Extract image references and descriptions from document (docproc markdown)."
16
+ input_schema = {
17
+ "type": "object",
18
+ "properties": {"file_path": {"type": "string", "description": "Path to the document"}},
19
+ "required": ["file_path"],
20
+ }
21
+
22
+ def run(self, **kwargs) -> str:
23
+ file_path = kwargs.get("file_path")
24
+ if not file_path or not isinstance(file_path, str):
25
+ return "Error: file_path must be a non-empty string"
26
+ content, err = run_docproc_to_markdown(file_path)
27
+ if err:
28
+ return err
29
+ images = re.findall(r"!\[([^\]]*)\]\(([^)]+)\)", content)
30
+ if not images:
31
+ alt_lines = re.findall(r"(?m)^.*[Ii]mage[^:\n]*:\s*.+$", content)
32
+ if alt_lines:
33
+ return "Image descriptions:\n" + "\n".join(alt_lines[:20])
34
+ return "No image blocks found in extracted markdown. Docproc may embed images as text descriptions."
35
+ lines = [f"alt: {a}\nurl: {u}" for a, u in images[:30]]
36
+ return "\n---\n".join(lines)
37
+
38
+
39
+ register(ExtractDocumentImagesTool())
@@ -0,0 +1,29 @@
1
+ """Extract full text from a document (PDF, DOCX, etc.) using docproc."""
2
+
3
+ from devsper.tools.base import Tool
4
+ from devsper.tools.registry import register
5
+ from devsper.tools.documents._docproc import run_docproc_to_markdown
6
+
7
+
8
+ class ExtractDocumentTextTool(Tool):
9
+ """Extract all text from a document (PDF, DOCX, PPTX, XLSX) via docproc."""
10
+
11
+ name = "extract_document_text"
12
+ description = "Extract full text from PDF, DOCX, PPTX, or XLSX using docproc."
13
+ input_schema = {
14
+ "type": "object",
15
+ "properties": {"file_path": {"type": "string", "description": "Path to the document"}},
16
+ "required": ["file_path"],
17
+ }
18
+
19
+ def run(self, **kwargs) -> str:
20
+ file_path = kwargs.get("file_path")
21
+ if not file_path or not isinstance(file_path, str):
22
+ return "Error: file_path must be a non-empty string"
23
+ content, err = run_docproc_to_markdown(file_path)
24
+ if err:
25
+ return err
26
+ return content.strip() or "(no text extracted)"
27
+
28
+
29
+ register(ExtractDocumentTextTool())
@@ -0,0 +1,36 @@
1
+ """Extract equation-like content (LaTeX, code blocks) from docproc markdown."""
2
+
3
+ import re
4
+ from devsper.tools.base import Tool
5
+ from devsper.tools.registry import register
6
+ from devsper.tools.documents._docproc import run_docproc_to_markdown
7
+
8
+
9
+ class ExtractEquationsTool(Tool):
10
+ """Extract equations from document: LaTeX ($...$) and math code blocks."""
11
+
12
+ name = "extract_equations"
13
+ description = "Extract equations (LaTeX and math blocks) from document via docproc."
14
+ input_schema = {
15
+ "type": "object",
16
+ "properties": {"file_path": {"type": "string", "description": "Path to the document"}},
17
+ "required": ["file_path"],
18
+ }
19
+
20
+ def run(self, **kwargs) -> str:
21
+ file_path = kwargs.get("file_path")
22
+ if not file_path or not isinstance(file_path, str):
23
+ return "Error: file_path must be a non-empty string"
24
+ content, err = run_docproc_to_markdown(file_path)
25
+ if err:
26
+ return err
27
+ inline = re.findall(r"\$([^$]+)\$", content)
28
+ block = re.findall(r"\$\$([^$]+)\$\$", content)
29
+ code_blocks = re.findall(r"```(?:\w*)\n(.*?)```", content, re.DOTALL)
30
+ equations = block + inline + [c.strip() for c in code_blocks if "\\" in c or "=" in c][:10]
31
+ if not equations:
32
+ return "No equations found in extracted markdown."
33
+ return "Equations:\n" + "\n---\n".join(equations[:30])
34
+
35
+
36
+ register(ExtractEquationsTool())