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
devsper/swarm/swarm.py
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Swarm: entrypoint for users. Orchestrates planner → scheduler → executor → results.
|
|
3
|
+
|
|
4
|
+
User code:
|
|
5
|
+
swarm = Swarm(worker_count=4)
|
|
6
|
+
result = swarm.run("Analyze diffusion model research")
|
|
7
|
+
# Or with config file:
|
|
8
|
+
swarm = Swarm(config="devsper.toml")
|
|
9
|
+
result = swarm.run("analyze diffusion models")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import threading
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
|
|
19
|
+
from devsper.types.task import Task
|
|
20
|
+
from devsper.types.event import Event, events
|
|
21
|
+
from devsper.utils.event_logger import EventLog
|
|
22
|
+
from devsper.utils.models import resolve_model
|
|
23
|
+
|
|
24
|
+
from devsper.swarm.planner import Planner
|
|
25
|
+
from devsper.swarm.scheduler import Scheduler
|
|
26
|
+
from devsper.swarm.executor import Executor
|
|
27
|
+
from devsper.agents.agent import Agent
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _fake_config():
|
|
31
|
+
"""Minimal config for single-node when no config file loaded."""
|
|
32
|
+
class N:
|
|
33
|
+
mode = "single"
|
|
34
|
+
class C:
|
|
35
|
+
events_dir = ".devsper/events"
|
|
36
|
+
nodes = N()
|
|
37
|
+
return C()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _persist_dag(scheduler: Scheduler, event_log: EventLog) -> None:
|
|
41
|
+
"""Write task DAG to events dir as {run_id}_dag.json for graph export."""
|
|
42
|
+
run_id = getattr(event_log, "run_id", None)
|
|
43
|
+
if not run_id:
|
|
44
|
+
return
|
|
45
|
+
log_path = getattr(event_log, "log_path", None)
|
|
46
|
+
if not log_path:
|
|
47
|
+
return
|
|
48
|
+
events_dir = os.path.dirname(log_path)
|
|
49
|
+
nodes = [
|
|
50
|
+
{"id": t.id, "description": (t.description or "")[:200]}
|
|
51
|
+
for t in scheduler._tasks.values()
|
|
52
|
+
]
|
|
53
|
+
edges = list(scheduler._graph.edges())
|
|
54
|
+
path = os.path.join(events_dir, f"{run_id}_dag.json")
|
|
55
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
56
|
+
json.dump({"nodes": nodes, "edges": edges}, f, indent=0)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Swarm:
|
|
60
|
+
"""Orchestrates planner, scheduler, executor, and agent. Single entrypoint for running a task."""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
worker_count: int | None = None,
|
|
65
|
+
worker_model: str | None = None,
|
|
66
|
+
planner_model: str | None = None,
|
|
67
|
+
event_log: EventLog | None = None,
|
|
68
|
+
adaptive: bool | None = None,
|
|
69
|
+
memory_router=None,
|
|
70
|
+
store_swarm_memory: bool = True,
|
|
71
|
+
use_tools: bool | None = None,
|
|
72
|
+
config: str | Path | object | None = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
# Load from config file or config object if provided
|
|
75
|
+
cfg = None
|
|
76
|
+
if config is not None:
|
|
77
|
+
if isinstance(config, (str, Path)):
|
|
78
|
+
from devsper.config import get_config
|
|
79
|
+
|
|
80
|
+
cfg = get_config(config_path=str(config))
|
|
81
|
+
else:
|
|
82
|
+
cfg = config
|
|
83
|
+
if cfg is not None:
|
|
84
|
+
self.worker_count = (
|
|
85
|
+
worker_count
|
|
86
|
+
if worker_count is not None
|
|
87
|
+
else getattr(cfg.swarm, "workers", 4)
|
|
88
|
+
)
|
|
89
|
+
worker_raw = worker_model if worker_model is not None else cfg.models.worker
|
|
90
|
+
planner_raw = (
|
|
91
|
+
planner_model if planner_model is not None else cfg.models.planner
|
|
92
|
+
)
|
|
93
|
+
self.worker_model = resolve_model(worker_raw, "analysis")
|
|
94
|
+
self.planner_model = resolve_model(planner_raw, "planning")
|
|
95
|
+
self.adaptive = (
|
|
96
|
+
adaptive
|
|
97
|
+
if adaptive is not None
|
|
98
|
+
else getattr(cfg.swarm, "adaptive_planning", False)
|
|
99
|
+
or getattr(cfg.swarm, "adaptive_execution", False)
|
|
100
|
+
)
|
|
101
|
+
self.use_tools = use_tools if use_tools is not None else True
|
|
102
|
+
self.speculative_execution = getattr(
|
|
103
|
+
cfg.swarm, "speculative_execution", False
|
|
104
|
+
)
|
|
105
|
+
self.cache_enabled = getattr(cfg.swarm, "cache_enabled", False)
|
|
106
|
+
self.parallel_tools = getattr(cfg.swarm, "parallel_tools", True)
|
|
107
|
+
self.message_bus_enabled = getattr(cfg.swarm, "message_bus_enabled", True)
|
|
108
|
+
self.prefetch_enabled = getattr(cfg.swarm, "prefetch_enabled", True)
|
|
109
|
+
self._config = cfg
|
|
110
|
+
# v1.10.5: register MCP server tools from config
|
|
111
|
+
mcp_servers = getattr(getattr(cfg, "mcp", None), "servers", None) or []
|
|
112
|
+
for server_config in mcp_servers:
|
|
113
|
+
try:
|
|
114
|
+
from devsper.tools.mcp import register_mcp_server
|
|
115
|
+
register_mcp_server(server_config)
|
|
116
|
+
except Exception:
|
|
117
|
+
pass # don't fail Swarm init if MCP server unreachable
|
|
118
|
+
# v1.10.5: register A2A agent tools from config (auto_discover)
|
|
119
|
+
a2a_agents = getattr(getattr(cfg, "a2a", None), "agents", None) or []
|
|
120
|
+
for agent_config in a2a_agents:
|
|
121
|
+
if not getattr(agent_config, "auto_discover", True):
|
|
122
|
+
continue
|
|
123
|
+
try:
|
|
124
|
+
from devsper.agents.a2a.discovery import register_a2a_agent
|
|
125
|
+
register_a2a_agent(agent_config)
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
else:
|
|
129
|
+
self.worker_count = worker_count if worker_count is not None else 4
|
|
130
|
+
worker_raw = worker_model if worker_model is not None else "mock"
|
|
131
|
+
planner_raw = planner_model if planner_model is not None else "mock"
|
|
132
|
+
self.worker_model = resolve_model(worker_raw, "analysis")
|
|
133
|
+
self.planner_model = resolve_model(planner_raw, "planning")
|
|
134
|
+
self.adaptive = adaptive if adaptive is not None else False
|
|
135
|
+
self.use_tools = use_tools if use_tools is not None else False
|
|
136
|
+
self.speculative_execution = False
|
|
137
|
+
self.cache_enabled = False
|
|
138
|
+
self.parallel_tools = True
|
|
139
|
+
self.message_bus_enabled = True
|
|
140
|
+
self.prefetch_enabled = True
|
|
141
|
+
self._config = None
|
|
142
|
+
self.event_log = event_log or EventLog()
|
|
143
|
+
self.memory_router = memory_router
|
|
144
|
+
self.store_swarm_memory = store_swarm_memory
|
|
145
|
+
self._last_scheduler: Scheduler | None = None
|
|
146
|
+
self._last_reasoning_store = None
|
|
147
|
+
self._pause_event = threading.Event()
|
|
148
|
+
self._pause_event.set()
|
|
149
|
+
|
|
150
|
+
def pause(self) -> None:
|
|
151
|
+
"""Pause the executor: currently-running tasks finish, no new tasks start."""
|
|
152
|
+
self._pause_event.clear()
|
|
153
|
+
|
|
154
|
+
def resume(self) -> None:
|
|
155
|
+
"""Resume the executor so new tasks can be picked."""
|
|
156
|
+
self._pause_event.set()
|
|
157
|
+
|
|
158
|
+
def run(self, user_task: str, hitl_resolver: object = None) -> dict[str, str]:
|
|
159
|
+
"""
|
|
160
|
+
Create root task → plan subtasks → add to scheduler → run executor → return task_id → result.
|
|
161
|
+
hitl_resolver: optional (approval, policy) -> bool for in-process approval prompts.
|
|
162
|
+
"""
|
|
163
|
+
self._emit(events.SWARM_STARTED, {"user_task": user_task[:200]})
|
|
164
|
+
|
|
165
|
+
root = Task(id="root", description=user_task, dependencies=[])
|
|
166
|
+
from devsper.intelligence.strategy_selector import StrategySelector
|
|
167
|
+
from devsper.intelligence.strategies import get_strategy_for
|
|
168
|
+
|
|
169
|
+
knowledge_graph = None
|
|
170
|
+
if self._config is not None:
|
|
171
|
+
kg_cfg = getattr(self._config, "knowledge", None)
|
|
172
|
+
if kg_cfg and (getattr(kg_cfg, "guide_planning", False) or getattr(kg_cfg, "auto_extract", False)):
|
|
173
|
+
from devsper.knowledge.knowledge_graph import KnowledgeGraph
|
|
174
|
+
from devsper.memory.memory_store import get_default_store
|
|
175
|
+
knowledge_graph = KnowledgeGraph(store=get_default_store())
|
|
176
|
+
knowledge_graph.load()
|
|
177
|
+
knowledge_graph.build_from_memory(merge=True)
|
|
178
|
+
selector = StrategySelector()
|
|
179
|
+
selected = selector.select(root)
|
|
180
|
+
strategy_instance = get_strategy_for(selected)
|
|
181
|
+
prompt_suffix = selector.suggest_planner_prompt_suffix(selected)
|
|
182
|
+
guide_planning = False
|
|
183
|
+
min_confidence = 0.30
|
|
184
|
+
if self._config is not None and getattr(self._config, "knowledge", None):
|
|
185
|
+
guide_planning = getattr(self._config.knowledge, "guide_planning", False)
|
|
186
|
+
min_confidence = getattr(self._config.knowledge, "min_confidence", 0.30)
|
|
187
|
+
planner = Planner(
|
|
188
|
+
model_name=self.planner_model,
|
|
189
|
+
event_log=self.event_log,
|
|
190
|
+
strategy=strategy_instance,
|
|
191
|
+
prompt_suffix=prompt_suffix,
|
|
192
|
+
knowledge_graph=knowledge_graph,
|
|
193
|
+
guide_planning=guide_planning,
|
|
194
|
+
min_confidence=min_confidence,
|
|
195
|
+
)
|
|
196
|
+
subtasks = planner.plan(root)
|
|
197
|
+
|
|
198
|
+
scheduler = Scheduler()
|
|
199
|
+
scheduler.add_tasks(subtasks)
|
|
200
|
+
scheduler.run_id = getattr(self.event_log, "run_id", "") or ""
|
|
201
|
+
_persist_dag(scheduler, self.event_log)
|
|
202
|
+
|
|
203
|
+
from devsper.reasoning.store import ReasoningStore
|
|
204
|
+
from devsper.agents.message_bus import SwarmMessageBus
|
|
205
|
+
|
|
206
|
+
message_bus = None
|
|
207
|
+
if getattr(self, "message_bus_enabled", True):
|
|
208
|
+
message_bus = SwarmMessageBus(event_log=self.event_log)
|
|
209
|
+
|
|
210
|
+
# Build HITL once for both single-node and executor paths
|
|
211
|
+
hitl_enabled = False
|
|
212
|
+
hitl_escalation_checker = None
|
|
213
|
+
hitl_approval_store = None
|
|
214
|
+
hitl_notifier = None
|
|
215
|
+
if self._config is not None:
|
|
216
|
+
hitl_cfg = getattr(self._config, "hitl", None)
|
|
217
|
+
if hitl_cfg and getattr(hitl_cfg, "enabled", False):
|
|
218
|
+
from devsper.hitl.escalation import EscalationChecker, EscalationPolicy, EscalationTrigger
|
|
219
|
+
from devsper.hitl.approval import ApprovalStore, ApprovalNotifier
|
|
220
|
+
policies: list[EscalationPolicy] = []
|
|
221
|
+
for p in getattr(hitl_cfg, "policies", []) or []:
|
|
222
|
+
triggers = [
|
|
223
|
+
EscalationTrigger(type=getattr(t, "type", "confidence_below"), threshold=getattr(t, "threshold", 0.5))
|
|
224
|
+
for t in getattr(p, "triggers", []) or []
|
|
225
|
+
]
|
|
226
|
+
policies.append(
|
|
227
|
+
EscalationPolicy(
|
|
228
|
+
triggers=triggers,
|
|
229
|
+
approvers=getattr(p, "approvers", []) or [],
|
|
230
|
+
timeout_seconds=getattr(p, "timeout_seconds", 3600),
|
|
231
|
+
on_timeout=getattr(p, "on_timeout", "auto_approve"),
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
if policies:
|
|
235
|
+
hitl_enabled = True
|
|
236
|
+
hitl_escalation_checker = EscalationChecker(policies)
|
|
237
|
+
hitl_approval_store = ApprovalStore(getattr(self._config, "data_dir", ".devsper"))
|
|
238
|
+
hitl_notifier = ApprovalNotifier()
|
|
239
|
+
|
|
240
|
+
nodes_mode = "distributed" # no config -> use executor path (v1.9 behavior)
|
|
241
|
+
if self._config is not None:
|
|
242
|
+
nodes_cfg = getattr(self._config, "nodes", None)
|
|
243
|
+
nodes_mode = getattr(nodes_cfg, "mode", "single") if nodes_cfg else "single"
|
|
244
|
+
if nodes_mode == "single" and self._config is not None:
|
|
245
|
+
def _agent_factory(cfg):
|
|
246
|
+
rs = ReasoningStore()
|
|
247
|
+
mb = SwarmMessageBus(event_log=self.event_log) if getattr(self, "message_bus_enabled", True) else None
|
|
248
|
+
return Agent(
|
|
249
|
+
model_name=self.worker_model,
|
|
250
|
+
event_log=self.event_log,
|
|
251
|
+
memory_router=self.memory_router,
|
|
252
|
+
store_result_to_memory=False,
|
|
253
|
+
use_tools=self.use_tools,
|
|
254
|
+
reasoning_store=rs,
|
|
255
|
+
user_task=user_task,
|
|
256
|
+
parallel_tools=getattr(self, "parallel_tools", True),
|
|
257
|
+
message_bus=mb,
|
|
258
|
+
)
|
|
259
|
+
from devsper.nodes.single import create_single_node
|
|
260
|
+
single_node = create_single_node(
|
|
261
|
+
config=self._config or _fake_config(),
|
|
262
|
+
scheduler=scheduler,
|
|
263
|
+
event_log=self.event_log,
|
|
264
|
+
memory_router=self.memory_router,
|
|
265
|
+
agent_factory=_agent_factory,
|
|
266
|
+
user_task=user_task,
|
|
267
|
+
message_bus=message_bus,
|
|
268
|
+
hitl_enabled=hitl_enabled,
|
|
269
|
+
hitl_escalation_checker=hitl_escalation_checker,
|
|
270
|
+
hitl_approval_store=hitl_approval_store,
|
|
271
|
+
hitl_notifier=hitl_notifier,
|
|
272
|
+
hitl_resolver=hitl_resolver,
|
|
273
|
+
)
|
|
274
|
+
async def _run_single():
|
|
275
|
+
await single_node.start()
|
|
276
|
+
return await single_node.run_until_finished()
|
|
277
|
+
results = asyncio.run(_run_single())
|
|
278
|
+
self._last_scheduler = scheduler
|
|
279
|
+
self._last_reasoning_store = None
|
|
280
|
+
if self.store_swarm_memory and self.memory_router and results:
|
|
281
|
+
self._store_swarm_memory(user_task, scheduler)
|
|
282
|
+
self._emit(events.SWARM_FINISHED, {"task_count": len(results)})
|
|
283
|
+
try:
|
|
284
|
+
from devsper.intelligence.analysis.run_report import build_report_from_events
|
|
285
|
+
from devsper.runtime.run_history import RunHistory
|
|
286
|
+
log_path = getattr(self.event_log, "log_path", None)
|
|
287
|
+
if log_path:
|
|
288
|
+
events_dir = os.path.dirname(log_path)
|
|
289
|
+
run_id = getattr(self.event_log, "run_id", None)
|
|
290
|
+
if run_id:
|
|
291
|
+
report = build_report_from_events(run_id, events_dir)
|
|
292
|
+
RunHistory().record_run(report)
|
|
293
|
+
if self._config and getattr(getattr(self._config, "knowledge", None), "auto_extract", False):
|
|
294
|
+
from devsper.knowledge.knowledge_graph import KnowledgeGraph
|
|
295
|
+
from devsper.knowledge.extractor import KnowledgeExtractor
|
|
296
|
+
from devsper.memory.memory_store import get_default_store
|
|
297
|
+
kg = KnowledgeGraph(store=get_default_store())
|
|
298
|
+
kg.load()
|
|
299
|
+
kg.build_from_memory(merge=True)
|
|
300
|
+
completed_tasks = self.last_completed_tasks
|
|
301
|
+
min_conf = getattr(self._config.knowledge, "min_confidence", 0.60)
|
|
302
|
+
extractor = KnowledgeExtractor(min_confidence=min_conf)
|
|
303
|
+
try:
|
|
304
|
+
asyncio.get_event_loop().run_until_complete(
|
|
305
|
+
extractor.extract_from_run(
|
|
306
|
+
run_id, completed_tasks, kg, event_log=self.event_log
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
except Exception:
|
|
310
|
+
loop = asyncio.new_event_loop()
|
|
311
|
+
loop.run_until_complete(
|
|
312
|
+
extractor.extract_from_run(
|
|
313
|
+
run_id, completed_tasks, kg, event_log=self.event_log
|
|
314
|
+
)
|
|
315
|
+
)
|
|
316
|
+
except Exception:
|
|
317
|
+
pass
|
|
318
|
+
return results
|
|
319
|
+
|
|
320
|
+
reasoning_store = ReasoningStore()
|
|
321
|
+
agent = Agent(
|
|
322
|
+
model_name=self.worker_model,
|
|
323
|
+
event_log=self.event_log,
|
|
324
|
+
memory_router=self.memory_router,
|
|
325
|
+
store_result_to_memory=False,
|
|
326
|
+
use_tools=self.use_tools,
|
|
327
|
+
reasoning_store=reasoning_store,
|
|
328
|
+
user_task=user_task,
|
|
329
|
+
parallel_tools=getattr(self, "parallel_tools", True),
|
|
330
|
+
message_bus=message_bus,
|
|
331
|
+
)
|
|
332
|
+
audit_logger = None
|
|
333
|
+
if self._config and getattr(getattr(self._config, "compliance", None), "audit_logging", False):
|
|
334
|
+
run_id = getattr(self.event_log, "run_id", "") or ""
|
|
335
|
+
if run_id:
|
|
336
|
+
from devsper.audit.logger import AuditLogger
|
|
337
|
+
audit_logger = AuditLogger(
|
|
338
|
+
getattr(self._config, "data_dir", ".devsper"),
|
|
339
|
+
run_id=run_id,
|
|
340
|
+
)
|
|
341
|
+
agent.audit_logger = audit_logger
|
|
342
|
+
agent.audit_run_id = run_id
|
|
343
|
+
task_cache = None
|
|
344
|
+
semantic_cache = None
|
|
345
|
+
if getattr(self, "cache_enabled", False):
|
|
346
|
+
from devsper.cache import TaskCache
|
|
347
|
+
|
|
348
|
+
task_cache = TaskCache()
|
|
349
|
+
cfg = getattr(self, "_config", None)
|
|
350
|
+
if cfg and getattr(getattr(cfg, "cache", None), "semantic", False):
|
|
351
|
+
from devsper.cache.task_cache import SemanticTaskCache
|
|
352
|
+
|
|
353
|
+
cache_cfg = cfg.cache
|
|
354
|
+
semantic_cache = SemanticTaskCache(
|
|
355
|
+
similarity_threshold=getattr(cache_cfg, "similarity_threshold", 0.92),
|
|
356
|
+
max_age_hours=getattr(cache_cfg, "max_age_hours", 168.0),
|
|
357
|
+
)
|
|
358
|
+
complexity_router = None
|
|
359
|
+
models_config = None
|
|
360
|
+
if self._config is not None:
|
|
361
|
+
from devsper.providers.complexity_router import TaskComplexityRouter
|
|
362
|
+
|
|
363
|
+
complexity_router = TaskComplexityRouter()
|
|
364
|
+
models_config = self._config.models
|
|
365
|
+
|
|
366
|
+
critic_agent = None
|
|
367
|
+
critic_enabled = False
|
|
368
|
+
critic_roles: list[str] = []
|
|
369
|
+
fast_model = self.worker_model
|
|
370
|
+
if self._config is not None:
|
|
371
|
+
critic_enabled = getattr(self._config.swarm, "critic_enabled", False)
|
|
372
|
+
critic_roles = list(
|
|
373
|
+
getattr(self._config.swarm, "critic_roles", [])
|
|
374
|
+
or ["research", "analysis", "code"]
|
|
375
|
+
)
|
|
376
|
+
fast_model = getattr(self._config.models, "fast", None) or self.worker_model
|
|
377
|
+
if critic_enabled:
|
|
378
|
+
from devsper.agents.critic import CriticAgent
|
|
379
|
+
critic_agent = CriticAgent(event_log=self.event_log)
|
|
380
|
+
|
|
381
|
+
prefetcher = None
|
|
382
|
+
if getattr(self, "speculative_execution", False) and getattr(
|
|
383
|
+
self, "prefetch_enabled", True
|
|
384
|
+
):
|
|
385
|
+
from devsper.swarm.prefetcher import TaskPrefetcher
|
|
386
|
+
from devsper.tools.selector import get_tools_for_task
|
|
387
|
+
try:
|
|
388
|
+
from devsper.tools.scoring import get_default_score_store
|
|
389
|
+
score_store = get_default_score_store()
|
|
390
|
+
except Exception:
|
|
391
|
+
score_store = None
|
|
392
|
+
prefetch_max_age = 30.0
|
|
393
|
+
if self._config is not None:
|
|
394
|
+
prefetch_max_age = getattr(
|
|
395
|
+
self._config.swarm, "prefetch_max_age_seconds", 30.0
|
|
396
|
+
)
|
|
397
|
+
prefetcher = TaskPrefetcher(
|
|
398
|
+
memory_router=self.memory_router,
|
|
399
|
+
tool_selector=lambda desc, role=None, score_store=None: get_tools_for_task(
|
|
400
|
+
desc or "", role=role, score_store=score_store
|
|
401
|
+
),
|
|
402
|
+
score_store=score_store,
|
|
403
|
+
max_age_seconds=prefetch_max_age,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
bus = None
|
|
407
|
+
checkpointer = None
|
|
408
|
+
if self._config is not None:
|
|
409
|
+
try:
|
|
410
|
+
from devsper.bus import get_bus
|
|
411
|
+
bus = get_bus(self._config)
|
|
412
|
+
except Exception:
|
|
413
|
+
pass
|
|
414
|
+
if getattr(getattr(self._config, "swarm", None), "checkpoint_enabled", True):
|
|
415
|
+
from devsper.swarm.checkpointer import SchedulerCheckpointer
|
|
416
|
+
checkpointer = SchedulerCheckpointer(
|
|
417
|
+
events_dir=getattr(self._config, "events_dir", ".devsper/events"),
|
|
418
|
+
interval_tasks=getattr(
|
|
419
|
+
getattr(self._config, "swarm", None), "checkpoint_interval", 10
|
|
420
|
+
),
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
from devsper.config import get_config
|
|
424
|
+
sandbox_config = getattr(get_config(), "sandbox", None)
|
|
425
|
+
executor = Executor(
|
|
426
|
+
scheduler=scheduler,
|
|
427
|
+
agent=agent,
|
|
428
|
+
worker_count=self.worker_count,
|
|
429
|
+
event_log=self.event_log,
|
|
430
|
+
planner=planner if self.adaptive else None,
|
|
431
|
+
adaptive=self.adaptive,
|
|
432
|
+
speculative_execution=getattr(self, "speculative_execution", False),
|
|
433
|
+
task_cache=task_cache,
|
|
434
|
+
pause_event=self._pause_event,
|
|
435
|
+
semantic_cache=semantic_cache,
|
|
436
|
+
complexity_router=complexity_router,
|
|
437
|
+
models_config=models_config,
|
|
438
|
+
streaming_dag=True,
|
|
439
|
+
critic_agent=critic_agent,
|
|
440
|
+
critic_enabled=critic_enabled,
|
|
441
|
+
critic_roles=critic_roles,
|
|
442
|
+
fast_model=fast_model,
|
|
443
|
+
prefetcher=prefetcher,
|
|
444
|
+
bus=bus,
|
|
445
|
+
checkpointer=checkpointer,
|
|
446
|
+
sandbox_config=sandbox_config,
|
|
447
|
+
audit_logger=audit_logger,
|
|
448
|
+
hitl_enabled=hitl_enabled,
|
|
449
|
+
hitl_escalation_checker=hitl_escalation_checker,
|
|
450
|
+
hitl_approval_store=hitl_approval_store,
|
|
451
|
+
hitl_notifier=hitl_notifier,
|
|
452
|
+
hitl_resolver=hitl_resolver,
|
|
453
|
+
)
|
|
454
|
+
executor.run_sync()
|
|
455
|
+
|
|
456
|
+
self._last_scheduler = scheduler
|
|
457
|
+
self._last_reasoning_store = reasoning_store
|
|
458
|
+
results = scheduler.get_results()
|
|
459
|
+
if self.store_swarm_memory and self.memory_router and results:
|
|
460
|
+
self._store_swarm_memory(user_task, scheduler)
|
|
461
|
+
self._emit(events.SWARM_FINISHED, {"task_count": len(results)})
|
|
462
|
+
try:
|
|
463
|
+
from devsper.intelligence.analysis.run_report import build_report_from_events
|
|
464
|
+
from devsper.runtime.run_history import RunHistory
|
|
465
|
+
log_path = getattr(self.event_log, "log_path", None)
|
|
466
|
+
if log_path:
|
|
467
|
+
events_dir = os.path.dirname(log_path)
|
|
468
|
+
run_id = getattr(self.event_log, "run_id", None)
|
|
469
|
+
if run_id:
|
|
470
|
+
report = build_report_from_events(run_id, events_dir)
|
|
471
|
+
RunHistory().record_run(report)
|
|
472
|
+
if self._config and getattr(getattr(self._config, "knowledge", None), "auto_extract", False):
|
|
473
|
+
from devsper.knowledge.knowledge_graph import KnowledgeGraph
|
|
474
|
+
from devsper.knowledge.extractor import KnowledgeExtractor
|
|
475
|
+
from devsper.memory.memory_store import get_default_store
|
|
476
|
+
kg = KnowledgeGraph(store=get_default_store())
|
|
477
|
+
kg.load()
|
|
478
|
+
kg.build_from_memory(merge=True)
|
|
479
|
+
completed_tasks = self.last_completed_tasks
|
|
480
|
+
min_conf = getattr(self._config.knowledge, "min_confidence", 0.60)
|
|
481
|
+
extractor = KnowledgeExtractor(min_confidence=min_conf)
|
|
482
|
+
try:
|
|
483
|
+
asyncio.get_event_loop().run_until_complete(
|
|
484
|
+
extractor.extract_from_run(
|
|
485
|
+
run_id, completed_tasks, kg, event_log=self.event_log
|
|
486
|
+
)
|
|
487
|
+
)
|
|
488
|
+
except Exception:
|
|
489
|
+
loop = asyncio.new_event_loop()
|
|
490
|
+
loop.run_until_complete(
|
|
491
|
+
extractor.extract_from_run(
|
|
492
|
+
run_id, completed_tasks, kg, event_log=self.event_log
|
|
493
|
+
)
|
|
494
|
+
)
|
|
495
|
+
except Exception:
|
|
496
|
+
pass
|
|
497
|
+
return results
|
|
498
|
+
|
|
499
|
+
@property
|
|
500
|
+
def last_completed_tasks(self) -> list[Task]:
|
|
501
|
+
"""After run(), return completed tasks (id, description, result) for report building."""
|
|
502
|
+
if self._last_scheduler is None:
|
|
503
|
+
return []
|
|
504
|
+
return self._last_scheduler.get_completed_tasks()
|
|
505
|
+
|
|
506
|
+
def map_reduce(
|
|
507
|
+
self, dataset: list, map_fn, reduce_fn, worker_count: int | None = None
|
|
508
|
+
):
|
|
509
|
+
"""
|
|
510
|
+
First-class map-reduce: run map_fn on each item in parallel, then reduce_fn on results.
|
|
511
|
+
Uses the same worker pool pattern as the executor.
|
|
512
|
+
"""
|
|
513
|
+
from devsper.swarm.map_reduce import map_reduce as _map_reduce
|
|
514
|
+
|
|
515
|
+
workers = worker_count if worker_count is not None else self.worker_count
|
|
516
|
+
return _map_reduce(dataset, map_fn, reduce_fn, worker_count=workers)
|
|
517
|
+
|
|
518
|
+
def _store_swarm_memory(self, user_task: str, scheduler: Scheduler) -> None:
|
|
519
|
+
"""Store important outputs (research findings, summaries, results) into memory after run."""
|
|
520
|
+
from devsper.memory.memory_store import (
|
|
521
|
+
MemoryStore,
|
|
522
|
+
get_default_store,
|
|
523
|
+
generate_memory_id,
|
|
524
|
+
)
|
|
525
|
+
from devsper.memory.memory_types import MemoryRecord, MemoryType
|
|
526
|
+
from devsper.memory.memory_index import MemoryIndex
|
|
527
|
+
|
|
528
|
+
store = getattr(self.memory_router, "store", None)
|
|
529
|
+
if not isinstance(store, MemoryStore):
|
|
530
|
+
store = get_default_store()
|
|
531
|
+
index = getattr(self.memory_router, "index", None) or MemoryIndex(store)
|
|
532
|
+
for task in scheduler.get_completed_tasks():
|
|
533
|
+
content = (task.result or "").strip()
|
|
534
|
+
if not content or len(content) < 10:
|
|
535
|
+
continue
|
|
536
|
+
desc = (task.description or "").lower()
|
|
537
|
+
if "research" in desc or "paper" in desc or "literature" in desc:
|
|
538
|
+
mt = MemoryType.RESEARCH
|
|
539
|
+
elif "code" in desc or "codebase" in desc or "analyze" in desc:
|
|
540
|
+
mt = MemoryType.ARTIFACT
|
|
541
|
+
elif "data" in desc or "dataset" in desc or "experiment" in desc:
|
|
542
|
+
mt = MemoryType.SEMANTIC
|
|
543
|
+
else:
|
|
544
|
+
mt = MemoryType.EPISODIC
|
|
545
|
+
run_id = getattr(self.event_log, "run_id", "") or ""
|
|
546
|
+
record = MemoryRecord(
|
|
547
|
+
id=generate_memory_id(),
|
|
548
|
+
memory_type=mt,
|
|
549
|
+
source_task=task.id,
|
|
550
|
+
content=content[:15000],
|
|
551
|
+
tags=["swarm", "task", task.id, user_task[:100]],
|
|
552
|
+
run_id=run_id,
|
|
553
|
+
)
|
|
554
|
+
record = index.ensure_embedding(record)
|
|
555
|
+
store.store(record)
|
|
556
|
+
|
|
557
|
+
def _emit(self, event_type: events, payload: dict) -> None:
|
|
558
|
+
self.event_log.append_event(
|
|
559
|
+
Event(
|
|
560
|
+
timestamp=datetime.now(timezone.utc), type=event_type, payload=payload
|
|
561
|
+
)
|
|
562
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
devsper tool system: base, registry, tool_runner, and all categorized tools.
|
|
3
|
+
|
|
4
|
+
Importing this package triggers registration of all tools via category __init__.py.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from devsper.tools.base import Tool
|
|
8
|
+
from devsper.tools.registry import register, get, list_tools
|
|
9
|
+
from devsper.tools.tool_runner import run_tool
|
|
10
|
+
|
|
11
|
+
from devsper.tools import filesystem
|
|
12
|
+
from devsper.tools import research
|
|
13
|
+
from devsper.tools import coding
|
|
14
|
+
from devsper.tools import data
|
|
15
|
+
from devsper.tools import math as math_tools
|
|
16
|
+
from devsper.tools import system
|
|
17
|
+
from devsper.tools import documents
|
|
18
|
+
from devsper.tools import knowledge
|
|
19
|
+
from devsper.tools import research_advanced
|
|
20
|
+
from devsper.tools import code_intelligence
|
|
21
|
+
from devsper.tools import data_science
|
|
22
|
+
from devsper.tools import experiments
|
|
23
|
+
from devsper.tools import flagship
|
|
24
|
+
from devsper.tools import memory
|
|
25
|
+
|
|
26
|
+
# Load plugins from entry_points (devsper.plugins)
|
|
27
|
+
try:
|
|
28
|
+
from devsper.plugins.plugin_loader import load_plugins
|
|
29
|
+
load_plugins()
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
__all__ = ["Tool", "register", "get", "list_tools", "run_tool"]
|
devsper/tools/base.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base tool interface for the devsper tool system.
|
|
3
|
+
|
|
4
|
+
All tools are stateless and must return strings so agents can parse the output.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Tool(ABC):
|
|
11
|
+
"""
|
|
12
|
+
Base class for all devsper tools.
|
|
13
|
+
|
|
14
|
+
Tools are stateless. They accept keyword arguments matching input_schema
|
|
15
|
+
and return a string result for the agent to consume.
|
|
16
|
+
|
|
17
|
+
category: optional label for filtering (e.g. "research", "coding", "documents").
|
|
18
|
+
If unset, the tool selector may infer from the tool's module path.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
name: str = ""
|
|
22
|
+
description: str = ""
|
|
23
|
+
input_schema: dict = {}
|
|
24
|
+
category: str = ""
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def run(self, **kwargs) -> str:
|
|
28
|
+
"""Execute the tool with the given arguments. Returns a string result."""
|
|
29
|
+
...
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Codebase intelligence tools: indexing, dependencies, architecture, API, patterns."""
|
|
2
|
+
|
|
3
|
+
from devsper.tools.code_intelligence.codebase_indexer import CodebaseIndexerTool
|
|
4
|
+
from devsper.tools.code_intelligence.repository_semantic_index import RepositorySemanticIndexTool
|
|
5
|
+
from devsper.tools.code_intelligence.dependency_graph_builder import DependencyGraphBuilderTool
|
|
6
|
+
from devsper.tools.code_intelligence.architecture_analyzer import ArchitectureAnalyzerTool
|
|
7
|
+
from devsper.tools.code_intelligence.api_surface_extractor import ApiSurfaceExtractorTool
|
|
8
|
+
from devsper.tools.code_intelligence.test_coverage_estimator import TestCoverageEstimatorTool
|
|
9
|
+
from devsper.tools.code_intelligence.module_responsibility_mapper import ModuleResponsibilityMapperTool
|
|
10
|
+
from devsper.tools.code_intelligence.design_pattern_detector import DesignPatternDetectorTool
|
|
11
|
+
from devsper.tools.code_intelligence.refactor_candidate_detector import RefactorCandidateDetectorTool
|
|
12
|
+
from devsper.tools.code_intelligence.large_function_detector import LargeFunctionDetectorTool
|
|
13
|
+
from devsper.tools.code_intelligence.parallel_codebase_analysis import ParallelCodebaseAnalysisTool
|