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,51 @@
|
|
|
1
|
+
"""Run a shell command and return stdout/stderr."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import shlex
|
|
5
|
+
|
|
6
|
+
from devsper.tools.base import Tool
|
|
7
|
+
from devsper.tools.registry import register
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RunShellCommandTool(Tool):
|
|
11
|
+
"""Execute a shell command and return combined stdout and stderr."""
|
|
12
|
+
|
|
13
|
+
name = "run_shell_command"
|
|
14
|
+
description = "Run a shell command. Returns stdout and stderr. Timeout 60s."
|
|
15
|
+
input_schema = {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"command": {"type": "string", "description": "Shell command to run"},
|
|
19
|
+
"timeout_seconds": {"type": "integer", "description": "Timeout in seconds (default 60)"},
|
|
20
|
+
},
|
|
21
|
+
"required": ["command"],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def run(self, **kwargs) -> str:
|
|
25
|
+
command = kwargs.get("command")
|
|
26
|
+
timeout = kwargs.get("timeout_seconds", 60)
|
|
27
|
+
if not command or not isinstance(command, str):
|
|
28
|
+
return "Error: command must be a non-empty string"
|
|
29
|
+
if not isinstance(timeout, int) or timeout < 1:
|
|
30
|
+
timeout = 60
|
|
31
|
+
try:
|
|
32
|
+
result = subprocess.run(
|
|
33
|
+
shlex.split(command) if isinstance(command, str) else [command],
|
|
34
|
+
capture_output=True,
|
|
35
|
+
text=True,
|
|
36
|
+
timeout=timeout,
|
|
37
|
+
)
|
|
38
|
+
out = result.stdout or ""
|
|
39
|
+
err = result.stderr or ""
|
|
40
|
+
if err:
|
|
41
|
+
out = out + "\n--- stderr ---\n" + err
|
|
42
|
+
if result.returncode != 0:
|
|
43
|
+
out = f"[exit {result.returncode}]\n" + out
|
|
44
|
+
return out.strip() or "(no output)"
|
|
45
|
+
except subprocess.TimeoutExpired:
|
|
46
|
+
return f"Error: command timed out after {timeout}s"
|
|
47
|
+
except Exception as e:
|
|
48
|
+
return f"Error: {e}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
register(RunShellCommandTool())
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Return basic system information."""
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from devsper.tools.base import Tool
|
|
7
|
+
from devsper.tools.registry import register
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SystemInfoTool(Tool):
|
|
11
|
+
"""Return system info: OS, machine, Python version."""
|
|
12
|
+
|
|
13
|
+
name = "system_info"
|
|
14
|
+
description = "Get system information: OS, machine, Python version."
|
|
15
|
+
input_schema = {"type": "object", "properties": {}, "required": []}
|
|
16
|
+
|
|
17
|
+
def run(self, **kwargs) -> str:
|
|
18
|
+
return (
|
|
19
|
+
f"system = {platform.system()}\n"
|
|
20
|
+
f"machine = {platform.machine()}\n"
|
|
21
|
+
f"processor = {platform.processor() or 'N/A'}\n"
|
|
22
|
+
f"python = {sys.version}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
register(SystemInfoTool())
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool runner: execute a tool by name with validated arguments and safe error handling.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from devsper.tools.registry import get_with_mcp_fallback
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _validate_args(args: dict, schema: dict) -> str | None:
|
|
11
|
+
"""
|
|
12
|
+
Validate args against JSON Schema-style input_schema.
|
|
13
|
+
Returns None if valid, or an error message string if invalid.
|
|
14
|
+
"""
|
|
15
|
+
if not isinstance(args, dict):
|
|
16
|
+
return "args must be a dict"
|
|
17
|
+
required = schema.get("required", [])
|
|
18
|
+
properties = schema.get("properties", {})
|
|
19
|
+
for key in required:
|
|
20
|
+
if key not in args:
|
|
21
|
+
return f"Missing required argument: {key}"
|
|
22
|
+
for key, value in args.items():
|
|
23
|
+
if key not in properties:
|
|
24
|
+
continue
|
|
25
|
+
prop = properties[key]
|
|
26
|
+
expected_type = prop.get("type")
|
|
27
|
+
if expected_type == "string" and not isinstance(value, str):
|
|
28
|
+
return f"Argument '{key}' must be a string"
|
|
29
|
+
if expected_type == "number" and not isinstance(value, (int, float)):
|
|
30
|
+
return f"Argument '{key}' must be a number"
|
|
31
|
+
if expected_type == "integer" and not isinstance(value, int):
|
|
32
|
+
return f"Argument '{key}' must be an integer"
|
|
33
|
+
if expected_type == "boolean" and not isinstance(value, bool):
|
|
34
|
+
return f"Argument '{key}' must be a boolean"
|
|
35
|
+
if expected_type == "array" and not isinstance(value, list):
|
|
36
|
+
return f"Argument '{key}' must be an array"
|
|
37
|
+
if expected_type == "object" and not isinstance(value, dict):
|
|
38
|
+
return f"Argument '{key}' must be an object"
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run_tool(
|
|
43
|
+
name: str,
|
|
44
|
+
args: dict,
|
|
45
|
+
task_type: str | None = None,
|
|
46
|
+
) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Execute the tool by name with the given arguments.
|
|
49
|
+
|
|
50
|
+
Validates args against the tool's input_schema, runs the tool, and returns
|
|
51
|
+
its string output. On validation failure or exception, returns a formatted error string.
|
|
52
|
+
Records usage to tool analytics and tool scoring when available.
|
|
53
|
+
"""
|
|
54
|
+
start = time.perf_counter()
|
|
55
|
+
tool = get_with_mcp_fallback(name)
|
|
56
|
+
if tool is None:
|
|
57
|
+
_record_analytics(name, False, start)
|
|
58
|
+
_record_scoring(name, task_type, False, start, error_type="ToolNotFound")
|
|
59
|
+
return f"Tool not found: {name}"
|
|
60
|
+
resolved_name = tool.name
|
|
61
|
+
err = _validate_args(args, tool.input_schema)
|
|
62
|
+
if err is not None:
|
|
63
|
+
_record_analytics(resolved_name, False, start)
|
|
64
|
+
_record_scoring(resolved_name, task_type, False, start, error_type="ValidationError")
|
|
65
|
+
return f"Validation error: {err}"
|
|
66
|
+
try:
|
|
67
|
+
result = tool.run(**args)
|
|
68
|
+
latency_ms = int((time.monotonic() - start) * 1000)
|
|
69
|
+
success = not (isinstance(result, str) and result.startswith("Tool error:"))
|
|
70
|
+
_record_analytics(resolved_name, success, start)
|
|
71
|
+
_record_scoring(resolved_name, task_type, success, start, latency_ms=latency_ms)
|
|
72
|
+
return result
|
|
73
|
+
except Exception as e:
|
|
74
|
+
latency_ms = int((time.monotonic() - start) * 1000)
|
|
75
|
+
_record_analytics(resolved_name, False, start)
|
|
76
|
+
_record_scoring(
|
|
77
|
+
resolved_name,
|
|
78
|
+
task_type,
|
|
79
|
+
False,
|
|
80
|
+
start,
|
|
81
|
+
latency_ms=latency_ms,
|
|
82
|
+
error_type=type(e).__name__,
|
|
83
|
+
)
|
|
84
|
+
return f"Tool error: {type(e).__name__}: {e}"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _record_analytics(tool_name: str, success: bool, start_time: float) -> None:
|
|
88
|
+
"""Record tool invocation to analytics if available."""
|
|
89
|
+
try:
|
|
90
|
+
from devsper.analytics import get_default_analytics
|
|
91
|
+
|
|
92
|
+
latency_ms = (time.perf_counter() - start_time) * 1000
|
|
93
|
+
get_default_analytics().record(tool_name, success, latency_ms)
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _record_scoring(
|
|
99
|
+
tool_name: str,
|
|
100
|
+
task_type: str | None,
|
|
101
|
+
success: bool,
|
|
102
|
+
start_time: float,
|
|
103
|
+
latency_ms: int | None = None,
|
|
104
|
+
error_type: str | None = None,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""Record tool result to scoring store if available."""
|
|
107
|
+
try:
|
|
108
|
+
from devsper.tools.scoring import record_tool_result
|
|
109
|
+
except Exception:
|
|
110
|
+
return
|
|
111
|
+
if latency_ms is None:
|
|
112
|
+
latency_ms = int((time.perf_counter() - start_time) * 1000)
|
|
113
|
+
try:
|
|
114
|
+
record_tool_result(
|
|
115
|
+
tool_name,
|
|
116
|
+
task_type or "general",
|
|
117
|
+
success=success,
|
|
118
|
+
latency_ms=latency_ms,
|
|
119
|
+
error_type=error_type,
|
|
120
|
+
)
|
|
121
|
+
except Exception:
|
|
122
|
+
pass
|
devsper/tui/__init__.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent activity feed: chronological feed of agent actions (task started/ended, tool calls).
|
|
3
|
+
Reads from EventLog when log path is set.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from textual.widgets import Static
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ActivityFeedView(Static):
|
|
10
|
+
"""Shows recent events: TASK_STARTED, TASK_COMPLETED, TOOL_CALLED, etc."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
14
|
+
self._events_folder: str = ".devsper/events"
|
|
15
|
+
self._log_path: str | None = None
|
|
16
|
+
self._lines: list[str] = []
|
|
17
|
+
|
|
18
|
+
def set_events_folder(self, folder: str) -> None:
|
|
19
|
+
self._events_folder = folder
|
|
20
|
+
|
|
21
|
+
def set_log_path(self, path: str | None) -> None:
|
|
22
|
+
self._log_path = path
|
|
23
|
+
|
|
24
|
+
def refresh_events(self, limit: int = 50) -> None:
|
|
25
|
+
"""Load recent events from the event log and display as feed."""
|
|
26
|
+
self._lines = []
|
|
27
|
+
try:
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
import json
|
|
30
|
+
|
|
31
|
+
path = self._log_path
|
|
32
|
+
if not path and self._events_folder:
|
|
33
|
+
folder = Path(self._events_folder)
|
|
34
|
+
if folder.is_dir():
|
|
35
|
+
files = sorted(folder.glob("*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
36
|
+
path = str(files[0]) if files else None
|
|
37
|
+
if not path or not Path(path).is_file():
|
|
38
|
+
self._lines = ["(no event log)\nRun swarm to see activity."]
|
|
39
|
+
self.update("\n".join(self._lines))
|
|
40
|
+
return
|
|
41
|
+
with open(path) as f:
|
|
42
|
+
raw_lines = f.readlines()
|
|
43
|
+
for line in raw_lines[-limit:]:
|
|
44
|
+
line = line.strip()
|
|
45
|
+
if not line:
|
|
46
|
+
continue
|
|
47
|
+
try:
|
|
48
|
+
obj = json.loads(line)
|
|
49
|
+
ev = obj.get("type", "?")
|
|
50
|
+
payload = obj.get("payload", {})
|
|
51
|
+
task_id = payload.get("task_id", "")
|
|
52
|
+
tool = payload.get("tool", "")
|
|
53
|
+
if ev == "task_started":
|
|
54
|
+
self._lines.append(f"[dim]▶ task {task_id}[/dim]")
|
|
55
|
+
elif ev == "task_completed":
|
|
56
|
+
self._lines.append(f"[dim]✔ task {task_id}[/dim]")
|
|
57
|
+
elif ev == "tool_called":
|
|
58
|
+
self._lines.append(f"[dim]🔧 {tool} (task {task_id})[/dim]")
|
|
59
|
+
elif ev == "user_injection":
|
|
60
|
+
msg = (payload.get("message") or "")[:60]
|
|
61
|
+
if len(payload.get("message") or "") > 60:
|
|
62
|
+
msg += "…"
|
|
63
|
+
self._lines.append(f"📌 [bold]User injected:[/] [dim]{msg}[/]")
|
|
64
|
+
else:
|
|
65
|
+
self._lines.append(f"[dim]{ev}[/dim]")
|
|
66
|
+
except Exception:
|
|
67
|
+
self._lines.append(line[:80])
|
|
68
|
+
except Exception as e:
|
|
69
|
+
self._lines = [f"(error: {e})"]
|
|
70
|
+
self.update("\n".join(self._lines[-limit:]))
|
|
71
|
+
|
|
72
|
+
def on_mount(self) -> None:
|
|
73
|
+
self.update("(activity feed)\n\nRun swarm to see events.")
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Adaptive task creation: show when alternative tasks were injected (e.g. after failure).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from textual.widgets import Static
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AdaptiveTasksView(Static):
|
|
9
|
+
"""Shows adaptive events: failed tasks and newly created alternative tasks."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
12
|
+
super().__init__(*args, **kwargs)
|
|
13
|
+
self._events_folder: str = ".devsper/events"
|
|
14
|
+
self._log_path: str | None = None
|
|
15
|
+
|
|
16
|
+
def set_events_folder(self, folder: str) -> None:
|
|
17
|
+
self._events_folder = folder
|
|
18
|
+
|
|
19
|
+
def set_log_path(self, path: str | None) -> None:
|
|
20
|
+
self._log_path = path
|
|
21
|
+
|
|
22
|
+
def refresh_adaptive_events(self, limit: int = 20) -> None:
|
|
23
|
+
"""Load task_failed and subsequent task_created from event log."""
|
|
24
|
+
lines = ["(Adaptive task creation)", ""]
|
|
25
|
+
try:
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
import json
|
|
28
|
+
|
|
29
|
+
path = self._log_path
|
|
30
|
+
if not path and self._events_folder:
|
|
31
|
+
folder = Path(self._events_folder)
|
|
32
|
+
if folder.is_dir():
|
|
33
|
+
files = sorted(
|
|
34
|
+
folder.glob("*.jsonl"),
|
|
35
|
+
key=lambda p: p.stat().st_mtime,
|
|
36
|
+
reverse=True,
|
|
37
|
+
)
|
|
38
|
+
path = str(files[0]) if files else None
|
|
39
|
+
if not path or not Path(path).is_file():
|
|
40
|
+
self.update("(Adaptive tasks)\n\nNo event log.\nRun with adaptive_execution=true to see alternatives.")
|
|
41
|
+
return
|
|
42
|
+
with open(path) as f:
|
|
43
|
+
raw = f.readlines()
|
|
44
|
+
failed_ids = set()
|
|
45
|
+
created_after_fail = []
|
|
46
|
+
for line in raw:
|
|
47
|
+
line = line.strip()
|
|
48
|
+
if not line:
|
|
49
|
+
continue
|
|
50
|
+
try:
|
|
51
|
+
obj = json.loads(line)
|
|
52
|
+
ev = obj.get("type", "?")
|
|
53
|
+
payload = obj.get("payload", {})
|
|
54
|
+
if ev == "task_failed":
|
|
55
|
+
failed_ids.add(payload.get("task_id", ""))
|
|
56
|
+
if ev == "task_created" and failed_ids:
|
|
57
|
+
created_after_fail.append(
|
|
58
|
+
(payload.get("task_id", ""), payload.get("description", "")[:60])
|
|
59
|
+
)
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
if failed_ids:
|
|
63
|
+
lines.append("Failed tasks:")
|
|
64
|
+
for tid in list(failed_ids)[:5]:
|
|
65
|
+
lines.append(f" • {tid}")
|
|
66
|
+
lines.append("")
|
|
67
|
+
if created_after_fail:
|
|
68
|
+
lines.append("New tasks (after failure):")
|
|
69
|
+
for tid, desc in created_after_fail[-limit:]:
|
|
70
|
+
lines.append(f" • {tid}: {desc}")
|
|
71
|
+
elif not failed_ids:
|
|
72
|
+
lines.append("No adaptive events in this run.")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
lines.append(f"Error: {e}")
|
|
75
|
+
self.update("\n".join(lines))
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent role activity: show which roles are active and for which tasks.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from textual.widgets import Static
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AgentRoleActivityView(Static):
|
|
9
|
+
"""Shows agent role per task: research_agent, code_agent, analysis_agent, critic_agent."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
12
|
+
super().__init__(*args, **kwargs)
|
|
13
|
+
self._tasks_with_roles: list[dict] = []
|
|
14
|
+
|
|
15
|
+
def set_tasks_with_roles(self, tasks: list[dict]) -> None:
|
|
16
|
+
"""Update with list of {task_id, role, status}. Role can be None."""
|
|
17
|
+
self._tasks_with_roles = tasks
|
|
18
|
+
self._refresh_display()
|
|
19
|
+
|
|
20
|
+
def _refresh_display(self) -> None:
|
|
21
|
+
if not self._tasks_with_roles:
|
|
22
|
+
self.update("(Agent roles)\n\nNo task/role data yet.")
|
|
23
|
+
return
|
|
24
|
+
by_role: dict[str, list[str]] = {}
|
|
25
|
+
for t in self._tasks_with_roles:
|
|
26
|
+
role = t.get("role") or "general"
|
|
27
|
+
tid = t.get("task_id", "?")
|
|
28
|
+
by_role.setdefault(role, []).append(tid)
|
|
29
|
+
lines = ["(Agent role activity)", ""]
|
|
30
|
+
for role in ["research_agent", "code_agent", "analysis_agent", "critic_agent", "general"]:
|
|
31
|
+
if role not in by_role:
|
|
32
|
+
continue
|
|
33
|
+
tasks = by_role[role][:10]
|
|
34
|
+
lines.append(f" {role}: {', '.join(tasks)}")
|
|
35
|
+
self.update("\n".join(lines))
|