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.
- devsper/__init__.py +14 -0
- devsper/agents/a2a/__init__.py +27 -0
- devsper/agents/a2a/client.py +126 -0
- devsper/agents/a2a/discovery.py +24 -0
- devsper/agents/a2a/server.py +128 -0
- devsper/agents/a2a/tool_adapter.py +68 -0
- devsper/agents/a2a/types.py +49 -0
- devsper/agents/agent.py +602 -0
- devsper/agents/critic.py +80 -0
- devsper/agents/message_bus.py +124 -0
- devsper/agents/roles.py +181 -0
- devsper/agents/run_agent.py +78 -0
- devsper/analytics/__init__.py +5 -0
- devsper/analytics/tool_analytics.py +78 -0
- devsper/audit/__init__.py +5 -0
- devsper/audit/logger.py +214 -0
- devsper/bus/__init__.py +29 -0
- devsper/bus/backends/__init__.py +5 -0
- devsper/bus/backends/base.py +38 -0
- devsper/bus/backends/memory.py +55 -0
- devsper/bus/backends/redis.py +146 -0
- devsper/bus/message.py +56 -0
- devsper/bus/schema_version.py +3 -0
- devsper/bus/topics.py +19 -0
- devsper/cache/__init__.py +6 -0
- devsper/cache/embedding_index.py +98 -0
- devsper/cache/hashing.py +24 -0
- devsper/cache/store.py +153 -0
- devsper/cache/task_cache.py +191 -0
- devsper/cli/__init__.py +6 -0
- devsper/cli/commands/reg.py +733 -0
- devsper/cli/github_oauth.py +157 -0
- devsper/cli/init.py +637 -0
- devsper/cli/main.py +2956 -0
- devsper/cli/run_progress.py +103 -0
- devsper/cli/ui/__init__.py +65 -0
- devsper/cli/ui/components.py +94 -0
- devsper/cli/ui/errors.py +104 -0
- devsper/cli/ui/logging.py +120 -0
- devsper/cli/ui/onboarding.py +102 -0
- devsper/cli/ui/progress.py +43 -0
- devsper/cli/ui/run_view.py +308 -0
- devsper/cli/ui/theme.py +40 -0
- devsper/cluster/__init__.py +29 -0
- devsper/cluster/election.py +84 -0
- devsper/cluster/local.py +97 -0
- devsper/cluster/node_info.py +77 -0
- devsper/cluster/registry.py +71 -0
- devsper/cluster/router.py +117 -0
- devsper/cluster/state_backend.py +105 -0
- devsper/compliance/__init__.py +5 -0
- devsper/compliance/pii.py +147 -0
- devsper/config/__init__.py +52 -0
- devsper/config/config_loader.py +121 -0
- devsper/config/defaults.py +77 -0
- devsper/config/resolver.py +342 -0
- devsper/config/schema.py +237 -0
- devsper/credentials/__init__.py +19 -0
- devsper/credentials/cli.py +197 -0
- devsper/credentials/migration.py +124 -0
- devsper/credentials/store.py +142 -0
- devsper/dashboard/__init__.py +9 -0
- devsper/dashboard/dashboard.py +87 -0
- devsper/dev/__init__.py +25 -0
- devsper/dev/builder.py +195 -0
- devsper/dev/debugger.py +95 -0
- devsper/dev/repo_index.py +138 -0
- devsper/dev/sandbox.py +203 -0
- devsper/dev/scaffold.py +122 -0
- devsper/embeddings/__init__.py +5 -0
- devsper/embeddings/service.py +36 -0
- devsper/explainability/__init__.py +14 -0
- devsper/explainability/decision_tree.py +104 -0
- devsper/explainability/rationale.py +38 -0
- devsper/explainability/simulation.py +56 -0
- devsper/hitl/__init__.py +13 -0
- devsper/hitl/approval.py +160 -0
- devsper/hitl/escalation.py +95 -0
- devsper/intelligence/__init__.py +9 -0
- devsper/intelligence/adaptation.py +88 -0
- devsper/intelligence/analysis/__init__.py +19 -0
- devsper/intelligence/analysis/analyzer.py +71 -0
- devsper/intelligence/analysis/cost_estimator.py +66 -0
- devsper/intelligence/analysis/formatter.py +103 -0
- devsper/intelligence/analysis/run_report.py +402 -0
- devsper/intelligence/learning_engine.py +92 -0
- devsper/intelligence/strategies/__init__.py +23 -0
- devsper/intelligence/strategies/base.py +14 -0
- devsper/intelligence/strategies/code_analysis_strategy.py +33 -0
- devsper/intelligence/strategies/data_science_strategy.py +33 -0
- devsper/intelligence/strategies/document_pipeline_strategy.py +33 -0
- devsper/intelligence/strategies/experiment_strategy.py +33 -0
- devsper/intelligence/strategies/research_strategy.py +34 -0
- devsper/intelligence/strategy_selector.py +84 -0
- devsper/intelligence/synthesis.py +132 -0
- devsper/intelligence/task_optimizer.py +92 -0
- devsper/knowledge/__init__.py +5 -0
- devsper/knowledge/extractor.py +204 -0
- devsper/knowledge/knowledge_graph.py +184 -0
- devsper/knowledge/query.py +285 -0
- devsper/memory/__init__.py +35 -0
- devsper/memory/consolidation.py +138 -0
- devsper/memory/embeddings.py +60 -0
- devsper/memory/memory_index.py +97 -0
- devsper/memory/memory_router.py +62 -0
- devsper/memory/memory_store.py +221 -0
- devsper/memory/memory_types.py +54 -0
- devsper/memory/namespaces.py +45 -0
- devsper/memory/scoring.py +77 -0
- devsper/memory/summarizer.py +52 -0
- devsper/nodes/__init__.py +5 -0
- devsper/nodes/controller.py +449 -0
- devsper/nodes/rpc.py +127 -0
- devsper/nodes/single.py +161 -0
- devsper/nodes/worker.py +506 -0
- devsper/orchestration/__init__.py +19 -0
- devsper/orchestration/meta_planner.py +239 -0
- devsper/orchestration/priority_queue.py +61 -0
- devsper/plugins/__init__.py +19 -0
- devsper/plugins/marketplace/__init__.py +0 -0
- devsper/plugins/plugin_loader.py +70 -0
- devsper/plugins/plugin_registry.py +34 -0
- devsper/plugins/registry.py +83 -0
- devsper/protocols/__init__.py +6 -0
- devsper/providers/__init__.py +17 -0
- devsper/providers/anthropic.py +84 -0
- devsper/providers/base.py +75 -0
- devsper/providers/complexity_router.py +94 -0
- devsper/providers/gemini.py +36 -0
- devsper/providers/github.py +180 -0
- devsper/providers/model_router.py +40 -0
- devsper/providers/openai.py +105 -0
- devsper/providers/router/__init__.py +21 -0
- devsper/providers/router/backends/__init__.py +19 -0
- devsper/providers/router/backends/anthropic_backend.py +111 -0
- devsper/providers/router/backends/custom_backend.py +138 -0
- devsper/providers/router/backends/gemini_backend.py +89 -0
- devsper/providers/router/backends/github_backend.py +165 -0
- devsper/providers/router/backends/ollama_backend.py +104 -0
- devsper/providers/router/backends/openai_backend.py +142 -0
- devsper/providers/router/backends/vllm_backend.py +35 -0
- devsper/providers/router/base.py +60 -0
- devsper/providers/router/factory.py +92 -0
- devsper/providers/router/legacy.py +101 -0
- devsper/providers/router/router.py +135 -0
- devsper/reasoning/__init__.py +12 -0
- devsper/reasoning/graph.py +59 -0
- devsper/reasoning/nodes.py +20 -0
- devsper/reasoning/store.py +67 -0
- devsper/runtime/__init__.py +12 -0
- devsper/runtime/health.py +88 -0
- devsper/runtime/replay.py +53 -0
- devsper/runtime/replay_engine.py +142 -0
- devsper/runtime/run_history.py +204 -0
- devsper/runtime/telemetry.py +116 -0
- devsper/runtime/visualize.py +58 -0
- devsper/sandbox/__init__.py +13 -0
- devsper/sandbox/sandbox.py +161 -0
- devsper/swarm/checkpointer.py +65 -0
- devsper/swarm/executor.py +558 -0
- devsper/swarm/map_reduce.py +44 -0
- devsper/swarm/planner.py +197 -0
- devsper/swarm/prefetcher.py +91 -0
- devsper/swarm/scheduler.py +153 -0
- devsper/swarm/speculation.py +47 -0
- devsper/swarm/swarm.py +562 -0
- devsper/tools/__init__.py +33 -0
- devsper/tools/base.py +29 -0
- devsper/tools/code_intelligence/__init__.py +13 -0
- devsper/tools/code_intelligence/api_surface_extractor.py +73 -0
- devsper/tools/code_intelligence/architecture_analyzer.py +65 -0
- devsper/tools/code_intelligence/codebase_indexer.py +71 -0
- devsper/tools/code_intelligence/dependency_graph_builder.py +67 -0
- devsper/tools/code_intelligence/design_pattern_detector.py +62 -0
- devsper/tools/code_intelligence/large_function_detector.py +68 -0
- devsper/tools/code_intelligence/module_responsibility_mapper.py +56 -0
- devsper/tools/code_intelligence/parallel_codebase_analysis.py +44 -0
- devsper/tools/code_intelligence/refactor_candidate_detector.py +81 -0
- devsper/tools/code_intelligence/repository_semantic_index.py +61 -0
- devsper/tools/code_intelligence/test_coverage_estimator.py +62 -0
- devsper/tools/coding/__init__.py +12 -0
- devsper/tools/coding/analyze_code_complexity.py +48 -0
- devsper/tools/coding/dependency_analyzer.py +42 -0
- devsper/tools/coding/extract_functions.py +38 -0
- devsper/tools/coding/format_python.py +50 -0
- devsper/tools/coding/generate_docstrings.py +40 -0
- devsper/tools/coding/generate_unit_tests.py +42 -0
- devsper/tools/coding/lint_python.py +51 -0
- devsper/tools/coding/refactor_function.py +41 -0
- devsper/tools/coding/repo_structure_map.py +54 -0
- devsper/tools/coding/run_python.py +53 -0
- devsper/tools/data/__init__.py +12 -0
- devsper/tools/data/column_type_detection.py +64 -0
- devsper/tools/data/csv_summary.py +52 -0
- devsper/tools/data/dataframe_filter.py +51 -0
- devsper/tools/data/dataframe_groupby.py +47 -0
- devsper/tools/data/dataframe_stats.py +38 -0
- devsper/tools/data/dataset_sampling.py +55 -0
- devsper/tools/data/dataset_schema.py +45 -0
- devsper/tools/data/json_pretty_print.py +37 -0
- devsper/tools/data/json_query.py +46 -0
- devsper/tools/data/missing_value_report.py +47 -0
- devsper/tools/data_science/__init__.py +13 -0
- devsper/tools/data_science/correlation_heatmap.py +72 -0
- devsper/tools/data_science/dataset_bias_detector.py +49 -0
- devsper/tools/data_science/dataset_distribution_report.py +64 -0
- devsper/tools/data_science/dataset_drift_detector.py +64 -0
- devsper/tools/data_science/dataset_outlier_detector.py +65 -0
- devsper/tools/data_science/dataset_profile.py +76 -0
- devsper/tools/data_science/distributed_dataset_processor.py +54 -0
- devsper/tools/data_science/feature_engineering_suggestions.py +69 -0
- devsper/tools/data_science/feature_importance_estimator.py +82 -0
- devsper/tools/data_science/model_input_validator.py +59 -0
- devsper/tools/data_science/time_series_analyzer.py +57 -0
- devsper/tools/documents/__init__.py +11 -0
- devsper/tools/documents/_docproc.py +56 -0
- devsper/tools/documents/document_to_markdown.py +29 -0
- devsper/tools/documents/extract_document_images.py +39 -0
- devsper/tools/documents/extract_document_text.py +29 -0
- devsper/tools/documents/extract_equations.py +36 -0
- devsper/tools/documents/extract_tables.py +47 -0
- devsper/tools/documents/summarize_document.py +42 -0
- devsper/tools/documents/write_latex_document.py +133 -0
- devsper/tools/documents/write_markdown_document.py +89 -0
- devsper/tools/documents/write_word_document.py +149 -0
- devsper/tools/experiments/__init__.py +13 -0
- devsper/tools/experiments/bootstrap_estimator.py +54 -0
- devsper/tools/experiments/experiment_report_generator.py +50 -0
- devsper/tools/experiments/experiment_tracker.py +36 -0
- devsper/tools/experiments/grid_search_runner.py +50 -0
- devsper/tools/experiments/model_benchmark_runner.py +45 -0
- devsper/tools/experiments/monte_carlo_experiment.py +38 -0
- devsper/tools/experiments/parameter_sweep_runner.py +51 -0
- devsper/tools/experiments/result_comparator.py +58 -0
- devsper/tools/experiments/simulation_runner.py +43 -0
- devsper/tools/experiments/statistical_significance_test.py +56 -0
- devsper/tools/experiments/swarm_map_reduce.py +42 -0
- devsper/tools/filesystem/__init__.py +12 -0
- devsper/tools/filesystem/append_file.py +42 -0
- devsper/tools/filesystem/file_hash.py +40 -0
- devsper/tools/filesystem/file_line_count.py +36 -0
- devsper/tools/filesystem/file_metadata.py +38 -0
- devsper/tools/filesystem/file_preview.py +55 -0
- devsper/tools/filesystem/find_large_files.py +50 -0
- devsper/tools/filesystem/list_directory.py +39 -0
- devsper/tools/filesystem/read_file.py +35 -0
- devsper/tools/filesystem/search_files.py +60 -0
- devsper/tools/filesystem/write_file.py +41 -0
- devsper/tools/flagship/__init__.py +15 -0
- devsper/tools/flagship/distributed_document_analysis.py +77 -0
- devsper/tools/flagship/docproc_corpus_pipeline.py +91 -0
- devsper/tools/flagship/repository_semantic_map.py +99 -0
- devsper/tools/flagship/research_graph_builder.py +111 -0
- devsper/tools/flagship/swarm_experiment_runner.py +86 -0
- devsper/tools/knowledge/__init__.py +10 -0
- devsper/tools/knowledge/citation_graph_builder.py +69 -0
- devsper/tools/knowledge/concept_frequency_analyzer.py +74 -0
- devsper/tools/knowledge/corpus_builder.py +66 -0
- devsper/tools/knowledge/cross_document_entity_linker.py +71 -0
- devsper/tools/knowledge/document_corpus_summary.py +68 -0
- devsper/tools/knowledge/document_topic_extractor.py +58 -0
- devsper/tools/knowledge/knowledge_graph_extractor.py +58 -0
- devsper/tools/knowledge/timeline_extractor.py +59 -0
- devsper/tools/math/__init__.py +12 -0
- devsper/tools/math/calculate_expression.py +52 -0
- devsper/tools/math/correlation.py +44 -0
- devsper/tools/math/distribution_summary.py +39 -0
- devsper/tools/math/histogram.py +53 -0
- devsper/tools/math/linear_regression.py +47 -0
- devsper/tools/math/matrix_multiply.py +38 -0
- devsper/tools/math/mean_std.py +35 -0
- devsper/tools/math/monte_carlo_simulation.py +43 -0
- devsper/tools/math/polynomial_fit.py +40 -0
- devsper/tools/math/random_sample.py +36 -0
- devsper/tools/mcp/__init__.py +23 -0
- devsper/tools/mcp/adapter.py +53 -0
- devsper/tools/mcp/client.py +235 -0
- devsper/tools/mcp/discovery.py +53 -0
- devsper/tools/memory/__init__.py +16 -0
- devsper/tools/memory/delete_memory.py +25 -0
- devsper/tools/memory/list_memory.py +34 -0
- devsper/tools/memory/search_memory.py +36 -0
- devsper/tools/memory/store_memory.py +47 -0
- devsper/tools/memory/summarize_memory.py +41 -0
- devsper/tools/memory/tag_memory.py +47 -0
- devsper/tools/pipelines.py +92 -0
- devsper/tools/registry.py +39 -0
- devsper/tools/research/__init__.py +12 -0
- devsper/tools/research/arxiv_download.py +55 -0
- devsper/tools/research/arxiv_search.py +58 -0
- devsper/tools/research/citation_extractor.py +35 -0
- devsper/tools/research/duckduckgo_search.py +42 -0
- devsper/tools/research/paper_metadata_extractor.py +45 -0
- devsper/tools/research/paper_summarizer.py +41 -0
- devsper/tools/research/research_question_generator.py +39 -0
- devsper/tools/research/topic_cluster.py +46 -0
- devsper/tools/research/web_search.py +47 -0
- devsper/tools/research/wikipedia_lookup.py +50 -0
- devsper/tools/research_advanced/__init__.py +14 -0
- devsper/tools/research_advanced/citation_context_extractor.py +60 -0
- devsper/tools/research_advanced/literature_review_generator.py +79 -0
- devsper/tools/research_advanced/methodology_extractor.py +58 -0
- devsper/tools/research_advanced/paper_contribution_extractor.py +50 -0
- devsper/tools/research_advanced/paper_dataset_identifier.py +49 -0
- devsper/tools/research_advanced/paper_method_comparator.py +62 -0
- devsper/tools/research_advanced/paper_similarity_search.py +69 -0
- devsper/tools/research_advanced/paper_trend_analyzer.py +69 -0
- devsper/tools/research_advanced/parallel_document_analyzer.py +56 -0
- devsper/tools/research_advanced/research_gap_finder.py +71 -0
- devsper/tools/research_advanced/research_topic_mapper.py +69 -0
- devsper/tools/research_advanced/swarm_literature_review.py +58 -0
- devsper/tools/scoring/__init__.py +52 -0
- devsper/tools/scoring/report.py +44 -0
- devsper/tools/scoring/scorer.py +39 -0
- devsper/tools/scoring/selector.py +61 -0
- devsper/tools/scoring/store.py +267 -0
- devsper/tools/selector.py +130 -0
- devsper/tools/system/__init__.py +12 -0
- devsper/tools/system/cpu_usage.py +22 -0
- devsper/tools/system/disk_usage.py +35 -0
- devsper/tools/system/environment_variables.py +29 -0
- devsper/tools/system/memory_usage.py +23 -0
- devsper/tools/system/pip_install.py +44 -0
- devsper/tools/system/pip_search.py +29 -0
- devsper/tools/system/process_list.py +34 -0
- devsper/tools/system/python_package_list.py +40 -0
- devsper/tools/system/run_shell_command.py +51 -0
- devsper/tools/system/system_info.py +26 -0
- devsper/tools/tool_runner.py +122 -0
- devsper/tui/__init__.py +5 -0
- devsper/tui/activity_feed_view.py +73 -0
- devsper/tui/adaptive_tasks_view.py +75 -0
- devsper/tui/agent_role_view.py +35 -0
- devsper/tui/app.py +395 -0
- devsper/tui/dashboard_screen.py +290 -0
- devsper/tui/dev_view.py +99 -0
- devsper/tui/inject_screen.py +73 -0
- devsper/tui/knowledge_graph_view.py +46 -0
- devsper/tui/layout.py +43 -0
- devsper/tui/logs_view.py +83 -0
- devsper/tui/memory_view.py +58 -0
- devsper/tui/performance_view.py +33 -0
- devsper/tui/reasoning_graph_view.py +39 -0
- devsper/tui/results_view.py +139 -0
- devsper/tui/swarm_view.py +37 -0
- devsper/tui/task_detail_screen.py +55 -0
- devsper/tui/task_view.py +103 -0
- devsper/types/event.py +97 -0
- devsper/types/exceptions.py +21 -0
- devsper/types/swarm.py +41 -0
- devsper/types/task.py +80 -0
- devsper/upgrade/__init__.py +21 -0
- devsper/upgrade/changelog.py +124 -0
- devsper/upgrade/cli.py +145 -0
- devsper/upgrade/installer.py +103 -0
- devsper/upgrade/notifier.py +52 -0
- devsper/upgrade/version_check.py +121 -0
- devsper/utils/event_logger.py +88 -0
- devsper/utils/http.py +43 -0
- devsper/utils/models.py +54 -0
- devsper/visualization/__init__.py +5 -0
- devsper/visualization/dag_export.py +67 -0
- devsper/workflow/__init__.py +18 -0
- devsper/workflow/conditions.py +157 -0
- devsper/workflow/context.py +108 -0
- devsper/workflow/loader.py +156 -0
- devsper/workflow/resolver.py +109 -0
- devsper/workflow/runner.py +562 -0
- devsper/workflow/schema.py +63 -0
- devsper/workflow/validator.py +128 -0
- devsper-2.1.6.dist-info/METADATA +346 -0
- devsper-2.1.6.dist-info/RECORD +375 -0
- devsper-2.1.6.dist-info/WHEEL +4 -0
- devsper-2.1.6.dist-info/entry_points.txt +3 -0
- devsper-2.1.6.dist-info/licenses/LICENSE +639 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Run a simple benchmark: execute a function N times and report mean/std of runtimes (mock or real)."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from devsper.tools.base import Tool
|
|
7
|
+
from devsper.tools.registry import register
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ModelBenchmarkRunnerTool(Tool):
|
|
11
|
+
"""
|
|
12
|
+
Run a lightweight benchmark: repeat a no-op or fixed delay N times, report mean/std (for pipeline demos).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
name = "model_benchmark_runner"
|
|
16
|
+
description = "Run a simple timing benchmark (N iterations, optional delay) and report mean/std."
|
|
17
|
+
input_schema = {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"iterations": {"type": "integer", "description": "Number of iterations (default 5)"},
|
|
21
|
+
"delay_seconds": {"type": "number", "description": "Optional delay per iteration (default 0)"},
|
|
22
|
+
},
|
|
23
|
+
"required": [],
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def run(self, **kwargs) -> str:
|
|
27
|
+
iterations = kwargs.get("iterations", 5)
|
|
28
|
+
delay_seconds = kwargs.get("delay_seconds", 0)
|
|
29
|
+
if not isinstance(iterations, int) or iterations < 1:
|
|
30
|
+
iterations = 5
|
|
31
|
+
if not isinstance(delay_seconds, (int, float)) or delay_seconds < 0:
|
|
32
|
+
delay_seconds = 0
|
|
33
|
+
times = []
|
|
34
|
+
for _ in range(iterations):
|
|
35
|
+
t0 = time.perf_counter()
|
|
36
|
+
if delay_seconds > 0:
|
|
37
|
+
time.sleep(delay_seconds)
|
|
38
|
+
times.append(time.perf_counter() - t0)
|
|
39
|
+
mean_t = sum(times) / len(times)
|
|
40
|
+
variance = sum((t - mean_t) ** 2 for t in times) / len(times)
|
|
41
|
+
std_t = variance ** 0.5
|
|
42
|
+
return json.dumps({"iterations": iterations, "mean_seconds": round(mean_t, 4), "std_seconds": round(std_t, 4), "times": [round(t, 4) for t in times]}, indent=2)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
register(ModelBenchmarkRunnerTool())
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Run a Monte Carlo experiment: sample N times from a simple distribution and report stats."""
|
|
2
|
+
|
|
3
|
+
from devsper.tools.base import Tool
|
|
4
|
+
from devsper.tools.registry import register
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MonteCarloExperimentTool(Tool):
|
|
9
|
+
"""
|
|
10
|
+
Run a Monte Carlo experiment: N random samples (e.g. uniform or from a list) and report mean/std.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
name = "monte_carlo_experiment"
|
|
14
|
+
description = "Run Monte Carlo: N samples, report mean, std, min, max."
|
|
15
|
+
input_schema = {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"n_samples": {"type": "integer", "description": "Number of samples (default 100)"},
|
|
19
|
+
"low": {"type": "number", "description": "Uniform low (default 0)"},
|
|
20
|
+
"high": {"type": "number", "description": "Uniform high (default 1)"},
|
|
21
|
+
},
|
|
22
|
+
"required": [],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
def run(self, **kwargs) -> str:
|
|
26
|
+
n_samples = kwargs.get("n_samples", 100)
|
|
27
|
+
low = kwargs.get("low", 0.0)
|
|
28
|
+
high = kwargs.get("high", 1.0)
|
|
29
|
+
if not isinstance(n_samples, int) or n_samples < 1:
|
|
30
|
+
n_samples = 100
|
|
31
|
+
samples = [random.uniform(low, high) for _ in range(n_samples)]
|
|
32
|
+
mean = sum(samples) / len(samples)
|
|
33
|
+
variance = sum((x - mean) ** 2 for x in samples) / len(samples)
|
|
34
|
+
std = variance ** 0.5
|
|
35
|
+
return f"Monte Carlo (n={n_samples}, uniform [{low},{high}]): mean={mean:.4f}, std={std:.4f}, min={min(samples):.4f}, max={max(samples):.4f}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
register(MonteCarloExperimentTool())
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Run a parameter sweep: generate (param_name, value) combinations and return as batch plan."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ParameterSweepRunnerTool(Tool):
|
|
10
|
+
"""
|
|
11
|
+
Generate parameter sweep combinations from named parameter lists (no actual execution).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
name = "parameter_sweep_runner"
|
|
15
|
+
description = "Generate parameter sweep combinations from named parameter lists."
|
|
16
|
+
input_schema = {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"params": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "Map of param name -> list of values, e.g. {\"lr\": [0.01, 0.1], \"epochs\": [5, 10]}",
|
|
22
|
+
},
|
|
23
|
+
"max_combinations": {"type": "integer", "description": "Cap total combinations (default 100)"},
|
|
24
|
+
},
|
|
25
|
+
"required": ["params"],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def run(self, **kwargs) -> str:
|
|
29
|
+
params = kwargs.get("params")
|
|
30
|
+
max_combinations = kwargs.get("max_combinations", 100)
|
|
31
|
+
if not params or not isinstance(params, dict):
|
|
32
|
+
return "Error: params must be a dict of param name -> list of values"
|
|
33
|
+
if not isinstance(max_combinations, int) or max_combinations < 1:
|
|
34
|
+
max_combinations = 100
|
|
35
|
+
names = list(params.keys())
|
|
36
|
+
values = []
|
|
37
|
+
for n in names:
|
|
38
|
+
v = params[n]
|
|
39
|
+
if not isinstance(v, list):
|
|
40
|
+
v = [v]
|
|
41
|
+
values.append(v)
|
|
42
|
+
result = [{}]
|
|
43
|
+
for i, vlist in enumerate(values):
|
|
44
|
+
result = [r | {names[i]: x} for r in result for x in vlist]
|
|
45
|
+
if len(result) > max_combinations:
|
|
46
|
+
result = result[:max_combinations]
|
|
47
|
+
break
|
|
48
|
+
return json.dumps({"combinations": result, "total": len(result)}, indent=2)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
register(ParameterSweepRunnerTool())
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Compare two or more experiment results (metrics) and report best by a chosen metric."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ResultComparatorTool(Tool):
|
|
10
|
+
"""
|
|
11
|
+
Compare experiment results: pass list of {run_id, metrics}; choose metric and direction (max/min).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
name = "result_comparator"
|
|
15
|
+
description = "Compare experiment results by a chosen metric; return best run and summary."
|
|
16
|
+
input_schema = {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"results": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"items": {"type": "object"},
|
|
22
|
+
"description": "List of {run_id, metrics}",
|
|
23
|
+
},
|
|
24
|
+
"metric": {"type": "string", "description": "Metric name to compare (e.g. accuracy)"},
|
|
25
|
+
"higher_is_better": {"type": "boolean", "description": "True for accuracy, False for loss (default True)"},
|
|
26
|
+
},
|
|
27
|
+
"required": ["results", "metric"],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def run(self, **kwargs) -> str:
|
|
31
|
+
results = kwargs.get("results")
|
|
32
|
+
metric = kwargs.get("metric")
|
|
33
|
+
higher_is_better = kwargs.get("higher_is_better", True)
|
|
34
|
+
if not results or not isinstance(results, list):
|
|
35
|
+
return "Error: results must be a non-empty list of {run_id, metrics}"
|
|
36
|
+
if not metric or not isinstance(metric, str):
|
|
37
|
+
return "Error: metric must be a non-empty string"
|
|
38
|
+
comparable = []
|
|
39
|
+
for r in results:
|
|
40
|
+
if not isinstance(r, dict):
|
|
41
|
+
continue
|
|
42
|
+
run_id = r.get("run_id", "?")
|
|
43
|
+
metrics = r.get("metrics") or r
|
|
44
|
+
if metric not in metrics:
|
|
45
|
+
continue
|
|
46
|
+
try:
|
|
47
|
+
val = float(metrics[metric])
|
|
48
|
+
comparable.append((run_id, val, r))
|
|
49
|
+
except (TypeError, ValueError):
|
|
50
|
+
continue
|
|
51
|
+
if not comparable:
|
|
52
|
+
return f"No results with metric '{metric}' found."
|
|
53
|
+
comparable.sort(key=lambda x: x[1], reverse=higher_is_better)
|
|
54
|
+
best = comparable[0]
|
|
55
|
+
return json.dumps({"best_run_id": best[0], "best_value": best[1], "all_ranked": [{"run_id": x[0], "value": x[1]} for x in comparable]}, indent=2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
register(ResultComparatorTool())
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Run a simple simulation: e.g. N steps of a deterministic recurrence and return final state."""
|
|
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 SimulationRunnerTool(Tool):
|
|
13
|
+
"""
|
|
14
|
+
Run a simple numeric simulation: x_{n+1} = f(x_n) for N steps (e.g. linear growth).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
name = "simulation_runner"
|
|
18
|
+
description = "Run a simple simulation: recurrence for N steps (e.g. linear growth)."
|
|
19
|
+
input_schema = {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"initial_value": {"type": "number", "description": "Starting value (default 1)"},
|
|
23
|
+
"steps": {"type": "integer", "description": "Number of steps (default 10)"},
|
|
24
|
+
"growth_rate": {"type": "number", "description": "Multiplier per step (default 1.1)"},
|
|
25
|
+
},
|
|
26
|
+
"required": [],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def run(self, **kwargs) -> str:
|
|
30
|
+
initial = kwargs.get("initial_value", 1.0)
|
|
31
|
+
steps = kwargs.get("steps", 10)
|
|
32
|
+
growth_rate = kwargs.get("growth_rate", 1.1)
|
|
33
|
+
if not isinstance(steps, int) or steps < 1:
|
|
34
|
+
steps = 10
|
|
35
|
+
x = initial
|
|
36
|
+
trajectory = [x]
|
|
37
|
+
for _ in range(steps - 1):
|
|
38
|
+
x = x * growth_rate
|
|
39
|
+
trajectory.append(x)
|
|
40
|
+
return f"Simulation: initial={initial}, steps={steps}, growth_rate={growth_rate}. Final value: {x}. Trajectory (first 5, last 2): {trajectory[:5]} ... {trajectory[-2:]}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
register(SimulationRunnerTool())
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Simple statistical significance: compare two samples with a t-statistic (no scipy)."""
|
|
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 StatisticalSignificanceTestTool(Tool):
|
|
13
|
+
"""
|
|
14
|
+
Compute pooled t-statistic for two samples (no p-value; report t and approximate interpretation).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
name = "statistical_significance_test"
|
|
18
|
+
description = "Compare two samples with t-statistic (pooled)."
|
|
19
|
+
input_schema = {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"sample_a": {"type": "array", "items": {"type": "number"}, "description": "First sample"},
|
|
23
|
+
"sample_b": {"type": "array", "items": {"type": "number"}, "description": "Second sample"},
|
|
24
|
+
},
|
|
25
|
+
"required": ["sample_a", "sample_b"],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def run(self, **kwargs) -> str:
|
|
29
|
+
sample_a = kwargs.get("sample_a")
|
|
30
|
+
sample_b = kwargs.get("sample_b")
|
|
31
|
+
if not sample_a or not isinstance(sample_a, list):
|
|
32
|
+
return "Error: sample_a must be a non-empty list of numbers"
|
|
33
|
+
if not sample_b or not isinstance(sample_b, list):
|
|
34
|
+
return "Error: sample_b 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
|
+
a = np.array(sample_a, dtype=float)
|
|
39
|
+
b = np.array(sample_b, dtype=float)
|
|
40
|
+
a, b = a[~np.isnan(a)], b[~np.isnan(b)]
|
|
41
|
+
except (ValueError, TypeError):
|
|
42
|
+
return "Error: values must be numeric"
|
|
43
|
+
if len(a) < 2 or len(b) < 2:
|
|
44
|
+
return "Error: need at least 2 values per sample"
|
|
45
|
+
n1, n2 = len(a), len(b)
|
|
46
|
+
m1, m2 = np.mean(a), np.mean(b)
|
|
47
|
+
s1, s2 = np.std(a, ddof=1), np.std(b, ddof=1)
|
|
48
|
+
if s1 == 0 and s2 == 0:
|
|
49
|
+
return "No variance in one or both samples; t-test not defined."
|
|
50
|
+
pooled = ((n1 - 1) * s1**2 + (n2 - 1) * s2**2) / (n1 + n2 - 2)
|
|
51
|
+
pooled = max(pooled ** 0.5, 1e-10)
|
|
52
|
+
t = (m1 - m2) / (pooled * (1 / n1 + 1 / n2) ** 0.5)
|
|
53
|
+
return f"Pooled t-statistic: {t:.4f}. Mean A: {m1:.4f}, Mean B: {m2:.4f}. |t|>2 often suggests difference at ~5% level."
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
register(StatisticalSignificanceTestTool())
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Map-reduce plan for swarm: split items into batches (map), then aggregate keys (reduce)."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SwarmMapReduceTool(Tool):
|
|
10
|
+
"""
|
|
11
|
+
Plan a map-reduce for swarm: split a list of items into map batches; suggest reduce keys.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
name = "swarm_map_reduce"
|
|
15
|
+
description = "Plan map-reduce for swarm: split items into map batches; return batch plan."
|
|
16
|
+
input_schema = {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"items": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"description": "List of items to process (e.g. file paths or IDs)",
|
|
22
|
+
},
|
|
23
|
+
"batch_size": {"type": "integer", "description": "Items per map batch (default 5)"},
|
|
24
|
+
"reduce_key": {"type": "string", "description": "Suggested key for reduce phase (e.g. sum, concat)"},
|
|
25
|
+
},
|
|
26
|
+
"required": ["items"],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def run(self, **kwargs) -> str:
|
|
30
|
+
items = kwargs.get("items")
|
|
31
|
+
batch_size = kwargs.get("batch_size", 5)
|
|
32
|
+
reduce_key = kwargs.get("reduce_key") or "concat"
|
|
33
|
+
if not items or not isinstance(items, list):
|
|
34
|
+
return "Error: items must be a non-empty list"
|
|
35
|
+
if not isinstance(batch_size, int) or batch_size < 1:
|
|
36
|
+
batch_size = 5
|
|
37
|
+
batches = [items[i : i + batch_size] for i in range(0, len(items), batch_size)]
|
|
38
|
+
result = {"total_items": len(items), "batch_size": batch_size, "map_batches": len(batches), "batches": batches, "reduce_key": reduce_key}
|
|
39
|
+
return json.dumps(result, indent=2)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
register(SwarmMapReduceTool())
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Filesystem tools: read, write, list, search, metadata, etc."""
|
|
2
|
+
|
|
3
|
+
from devsper.tools.filesystem.read_file import ReadFileTool
|
|
4
|
+
from devsper.tools.filesystem.write_file import WriteFileTool
|
|
5
|
+
from devsper.tools.filesystem.append_file import AppendFileTool
|
|
6
|
+
from devsper.tools.filesystem.list_directory import ListDirectoryTool
|
|
7
|
+
from devsper.tools.filesystem.search_files import SearchFilesTool
|
|
8
|
+
from devsper.tools.filesystem.find_large_files import FindLargeFilesTool
|
|
9
|
+
from devsper.tools.filesystem.file_metadata import FileMetadataTool
|
|
10
|
+
from devsper.tools.filesystem.file_hash import FileHashTool
|
|
11
|
+
from devsper.tools.filesystem.file_line_count import FileLineCountTool
|
|
12
|
+
from devsper.tools.filesystem.file_preview import FilePreviewTool
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Append content to a file."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AppendFileTool(Tool):
|
|
10
|
+
"""Append text to the end of a file. Creates the file if it does not exist."""
|
|
11
|
+
|
|
12
|
+
name = "append_file"
|
|
13
|
+
description = "Append content to a file. Creates file if it does not exist."
|
|
14
|
+
input_schema = {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"path": {"type": "string", "description": "Path to the file"},
|
|
18
|
+
"content": {"type": "string", "description": "Content to append"},
|
|
19
|
+
},
|
|
20
|
+
"required": ["path", "content"],
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def run(self, **kwargs) -> str:
|
|
24
|
+
path = kwargs.get("path")
|
|
25
|
+
content = kwargs.get("content")
|
|
26
|
+
if not path or not isinstance(path, str):
|
|
27
|
+
return "Error: path must be a non-empty string"
|
|
28
|
+
if content is None:
|
|
29
|
+
return "Error: content is required"
|
|
30
|
+
if not isinstance(content, str):
|
|
31
|
+
content = str(content)
|
|
32
|
+
p = Path(path)
|
|
33
|
+
try:
|
|
34
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
with open(p, "a", encoding="utf-8") as f:
|
|
36
|
+
f.write(content)
|
|
37
|
+
return f"Appended {len(content)} characters to {path}"
|
|
38
|
+
except Exception as e:
|
|
39
|
+
return f"Error appending to file: {e}"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
register(AppendFileTool())
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Compute a hash of a file's contents."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from devsper.tools.base import Tool
|
|
7
|
+
from devsper.tools.registry import register
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FileHashTool(Tool):
|
|
11
|
+
"""Compute SHA-256 hash of a file's contents."""
|
|
12
|
+
|
|
13
|
+
name = "file_hash"
|
|
14
|
+
description = "Compute SHA-256 hash of a file. Returns hex digest."
|
|
15
|
+
input_schema = {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {"path": {"type": "string", "description": "Path to the file"}},
|
|
18
|
+
"required": ["path"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def run(self, **kwargs) -> str:
|
|
22
|
+
path = kwargs.get("path")
|
|
23
|
+
if not path or not isinstance(path, str):
|
|
24
|
+
return "Error: path must be a non-empty string"
|
|
25
|
+
p = Path(path)
|
|
26
|
+
if not p.exists():
|
|
27
|
+
return f"Error: file not found: {path}"
|
|
28
|
+
if not p.is_file():
|
|
29
|
+
return f"Error: not a file: {path}"
|
|
30
|
+
try:
|
|
31
|
+
h = hashlib.sha256()
|
|
32
|
+
with open(p, "rb") as f:
|
|
33
|
+
for chunk in iter(lambda: f.read(65536), b""):
|
|
34
|
+
h.update(chunk)
|
|
35
|
+
return h.hexdigest()
|
|
36
|
+
except Exception as e:
|
|
37
|
+
return f"Error: {e}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
register(FileHashTool())
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Count lines in a text file."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FileLineCountTool(Tool):
|
|
10
|
+
"""Count the number of lines in a text file."""
|
|
11
|
+
|
|
12
|
+
name = "file_line_count"
|
|
13
|
+
description = "Count lines in a file. Uses universal newlines."
|
|
14
|
+
input_schema = {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {"path": {"type": "string", "description": "Path to the file"}},
|
|
17
|
+
"required": ["path"],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def run(self, **kwargs) -> str:
|
|
21
|
+
path = kwargs.get("path")
|
|
22
|
+
if not path or not isinstance(path, str):
|
|
23
|
+
return "Error: path must be a non-empty string"
|
|
24
|
+
p = Path(path)
|
|
25
|
+
if not p.exists():
|
|
26
|
+
return f"Error: file not found: {path}"
|
|
27
|
+
if not p.is_file():
|
|
28
|
+
return f"Error: not a file: {path}"
|
|
29
|
+
try:
|
|
30
|
+
count = sum(1 for _ in p.open(encoding="utf-8", errors="replace"))
|
|
31
|
+
return str(count)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
return f"Error: {e}"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
register(FileLineCountTool())
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Get file metadata (size, mtime, etc.)."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
|
|
6
|
+
from devsper.tools.base import Tool
|
|
7
|
+
from devsper.tools.registry import register
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FileMetadataTool(Tool):
|
|
11
|
+
"""Return metadata for a file: size, modification time, and whether it is a file/dir."""
|
|
12
|
+
|
|
13
|
+
name = "file_metadata"
|
|
14
|
+
description = "Get file metadata: size in bytes, modification time, type."
|
|
15
|
+
input_schema = {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {"path": {"type": "string", "description": "Path to the file or directory"}},
|
|
18
|
+
"required": ["path"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def run(self, **kwargs) -> str:
|
|
22
|
+
path = kwargs.get("path")
|
|
23
|
+
if not path or not isinstance(path, str):
|
|
24
|
+
return "Error: path must be a non-empty string"
|
|
25
|
+
p = Path(path)
|
|
26
|
+
if not p.exists():
|
|
27
|
+
return f"Error: path not found: {path}"
|
|
28
|
+
try:
|
|
29
|
+
st = p.stat()
|
|
30
|
+
mtime = datetime.fromtimestamp(st.st_mtime, tz=timezone.utc).isoformat()
|
|
31
|
+
kind = "directory" if p.is_dir() else "file"
|
|
32
|
+
size = st.st_size if p.is_file() else 0
|
|
33
|
+
return f"path: {path}\ntype: {kind}\nsize: {size} bytes\nmtime: {mtime}"
|
|
34
|
+
except Exception as e:
|
|
35
|
+
return f"Error: {e}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
register(FileMetadataTool())
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Preview the first N lines or bytes of a file."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FilePreviewTool(Tool):
|
|
10
|
+
"""Return the first N lines (default 20) or first N bytes of a file."""
|
|
11
|
+
|
|
12
|
+
name = "file_preview"
|
|
13
|
+
description = "Preview the start of a file: first N lines or first N bytes."
|
|
14
|
+
input_schema = {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"path": {"type": "string", "description": "Path to the file"},
|
|
18
|
+
"lines": {"type": "integer", "description": "Number of lines to show (default 20). Ignored if bytes is set."},
|
|
19
|
+
"bytes": {"type": "integer", "description": "If set, show first N bytes instead of lines"},
|
|
20
|
+
},
|
|
21
|
+
"required": ["path"],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def run(self, **kwargs) -> str:
|
|
25
|
+
path = kwargs.get("path")
|
|
26
|
+
lines = kwargs.get("lines", 20)
|
|
27
|
+
bytes_limit = kwargs.get("bytes")
|
|
28
|
+
if not path or not isinstance(path, str):
|
|
29
|
+
return "Error: path must be a non-empty string"
|
|
30
|
+
if not isinstance(lines, int) or lines < 1:
|
|
31
|
+
lines = 20
|
|
32
|
+
p = Path(path)
|
|
33
|
+
if not p.exists():
|
|
34
|
+
return f"Error: file not found: {path}"
|
|
35
|
+
if not p.is_file():
|
|
36
|
+
return f"Error: not a file: {path}"
|
|
37
|
+
try:
|
|
38
|
+
if bytes_limit is not None:
|
|
39
|
+
if not isinstance(bytes_limit, int) or bytes_limit < 1:
|
|
40
|
+
return "Error: bytes must be a positive integer"
|
|
41
|
+
data = p.read_bytes()[:bytes_limit]
|
|
42
|
+
return data.decode("utf-8", errors="replace")
|
|
43
|
+
with p.open(encoding="utf-8", errors="replace") as f:
|
|
44
|
+
out = []
|
|
45
|
+
for _ in range(lines):
|
|
46
|
+
line = f.readline()
|
|
47
|
+
if not line:
|
|
48
|
+
break
|
|
49
|
+
out.append(line.rstrip("\n"))
|
|
50
|
+
return "\n".join(out)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
return f"Error: {e}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
register(FilePreviewTool())
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Find files larger than a given size."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FindLargeFilesTool(Tool):
|
|
10
|
+
"""Find files under a directory that are larger than a given size in bytes."""
|
|
11
|
+
|
|
12
|
+
name = "find_large_files"
|
|
13
|
+
description = "Find files larger than min_size bytes in a directory. Returns paths and sizes."
|
|
14
|
+
input_schema = {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"path": {"type": "string", "description": "Root directory to search"},
|
|
18
|
+
"min_size": {"type": "integer", "description": "Minimum file size in bytes"},
|
|
19
|
+
},
|
|
20
|
+
"required": ["path", "min_size"],
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def run(self, **kwargs) -> str:
|
|
24
|
+
path = kwargs.get("path")
|
|
25
|
+
min_size = kwargs.get("min_size")
|
|
26
|
+
if not path or not isinstance(path, str):
|
|
27
|
+
return "Error: path must be a non-empty string"
|
|
28
|
+
if not isinstance(min_size, int) or min_size < 0:
|
|
29
|
+
return "Error: min_size must be a non-negative integer"
|
|
30
|
+
p = Path(path)
|
|
31
|
+
if not p.exists() or not p.is_dir():
|
|
32
|
+
return f"Error: path must be an existing directory: {path}"
|
|
33
|
+
try:
|
|
34
|
+
results = []
|
|
35
|
+
for f in p.rglob("*"):
|
|
36
|
+
if f.is_file():
|
|
37
|
+
try:
|
|
38
|
+
size = f.stat().st_size
|
|
39
|
+
if size >= min_size:
|
|
40
|
+
results.append((str(f), size))
|
|
41
|
+
except OSError:
|
|
42
|
+
pass
|
|
43
|
+
results.sort(key=lambda x: -x[1])
|
|
44
|
+
lines = [f"{size}\t{fp}" for fp, size in results]
|
|
45
|
+
return "\n".join(lines) if lines else "No files found"
|
|
46
|
+
except Exception as e:
|
|
47
|
+
return f"Error: {e}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
register(FindLargeFilesTool())
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""List contents of a directory."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from devsper.tools.base import Tool
|
|
6
|
+
from devsper.tools.registry import register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ListDirectoryTool(Tool):
|
|
10
|
+
"""List files and subdirectories in a directory."""
|
|
11
|
+
|
|
12
|
+
name = "list_directory"
|
|
13
|
+
description = "List entries in a directory. Returns names of files and subdirs."
|
|
14
|
+
input_schema = {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"path": {"type": "string", "description": "Path to the directory"},
|
|
18
|
+
},
|
|
19
|
+
"required": ["path"],
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def run(self, **kwargs) -> str:
|
|
23
|
+
path = kwargs.get("path")
|
|
24
|
+
if not path or not isinstance(path, str):
|
|
25
|
+
return "Error: path must be a non-empty string"
|
|
26
|
+
p = Path(path)
|
|
27
|
+
if not p.exists():
|
|
28
|
+
return f"Error: path not found: {path}"
|
|
29
|
+
if not p.is_dir():
|
|
30
|
+
return f"Error: not a directory: {path}"
|
|
31
|
+
try:
|
|
32
|
+
entries = sorted(p.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower()))
|
|
33
|
+
lines = [f"{'[dir] ' if e.is_dir() else ''}{e.name}" for e in entries]
|
|
34
|
+
return "\n".join(lines) if lines else "(empty)"
|
|
35
|
+
except Exception as e:
|
|
36
|
+
return f"Error listing directory: {e}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
register(ListDirectoryTool())
|