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,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Self-optimizing swarm: adapt execution strategy in real time.
|
|
3
|
+
|
|
4
|
+
Detects slow tasks, failed tasks, and spawns alternative subtasks.
|
|
5
|
+
The adaptive planner can inject new tasks into the scheduler DAG.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from devsper.types.task import Task, TaskStatus
|
|
10
|
+
from devsper.swarm.scheduler import Scheduler
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def detect_slow_tasks(
|
|
14
|
+
scheduler: Scheduler,
|
|
15
|
+
task_start_times: dict[str, datetime],
|
|
16
|
+
threshold_seconds: float = 60.0,
|
|
17
|
+
*,
|
|
18
|
+
now: datetime | None = None,
|
|
19
|
+
) -> list[str]:
|
|
20
|
+
"""
|
|
21
|
+
Return task ids that are still RUNNING and have been running longer than threshold_seconds.
|
|
22
|
+
task_start_times maps task_id -> start time (e.g. when task was set to RUNNING).
|
|
23
|
+
"""
|
|
24
|
+
now = now or datetime.now(timezone.utc)
|
|
25
|
+
slow: list[str] = []
|
|
26
|
+
for task_id, task in scheduler._tasks.items():
|
|
27
|
+
if task.status != TaskStatus.RUNNING:
|
|
28
|
+
continue
|
|
29
|
+
start = task_start_times.get(task_id)
|
|
30
|
+
if start is None:
|
|
31
|
+
continue
|
|
32
|
+
elapsed = (now - start).total_seconds()
|
|
33
|
+
if elapsed >= threshold_seconds:
|
|
34
|
+
slow.append(task_id)
|
|
35
|
+
return slow
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def detect_failed_tasks(scheduler: Scheduler) -> list[str]:
|
|
39
|
+
"""Return task ids that have status FAILED."""
|
|
40
|
+
return [
|
|
41
|
+
task_id
|
|
42
|
+
for task_id, task in scheduler._tasks.items()
|
|
43
|
+
if task.status == TaskStatus.FAILED
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def suggest_alternative_tasks(
|
|
48
|
+
task: Task,
|
|
49
|
+
planner: object,
|
|
50
|
+
context: list[Task] | None = None,
|
|
51
|
+
) -> list[Task]:
|
|
52
|
+
"""
|
|
53
|
+
Ask the planner to suggest alternative follow-up tasks (e.g. after failure or timeout).
|
|
54
|
+
Returns new tasks that depend on the same dependencies as the given task, so they
|
|
55
|
+
can be injected as alternatives. The planner's expand_tasks is used with a synthetic
|
|
56
|
+
"failed" result to get alternatives.
|
|
57
|
+
"""
|
|
58
|
+
if not hasattr(planner, "expand_tasks"):
|
|
59
|
+
return []
|
|
60
|
+
# Create a minimal completed task with a note that original failed/timed out
|
|
61
|
+
synthetic = Task(
|
|
62
|
+
id=task.id + "_alt",
|
|
63
|
+
description=f"(Alternative to failed/slow task) {task.description}",
|
|
64
|
+
dependencies=task.dependencies.copy(),
|
|
65
|
+
status=TaskStatus.COMPLETED,
|
|
66
|
+
result="[Previous attempt failed or timed out. Suggest alternative approach.]",
|
|
67
|
+
)
|
|
68
|
+
return planner.expand_tasks(synthetic, context=context)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def create_alternative_subtasks_for_failed(
|
|
72
|
+
failed_task: Task,
|
|
73
|
+
planner: object,
|
|
74
|
+
scheduler: Scheduler,
|
|
75
|
+
) -> list[Task]:
|
|
76
|
+
"""
|
|
77
|
+
Generate alternative tasks for a failed task and return them (caller should
|
|
78
|
+
add to scheduler via scheduler.add_tasks). New tasks depend on the same
|
|
79
|
+
dependencies as the failed task so they can run in place of it.
|
|
80
|
+
"""
|
|
81
|
+
new_tasks = suggest_alternative_tasks(failed_task, planner)
|
|
82
|
+
if not new_tasks:
|
|
83
|
+
return []
|
|
84
|
+
# Ensure new tasks depend on same deps so they are runnable when failed_task's deps are done
|
|
85
|
+
for t in new_tasks:
|
|
86
|
+
if t.dependencies != failed_task.dependencies:
|
|
87
|
+
t.dependencies = failed_task.dependencies.copy()
|
|
88
|
+
return new_tasks
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Run analysis: build RunReport from events, cost estimation, LLM analysis, Rich formatting.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from devsper.intelligence.analysis.run_report import (
|
|
6
|
+
RunReport,
|
|
7
|
+
TaskSummary,
|
|
8
|
+
build_report_from_events,
|
|
9
|
+
)
|
|
10
|
+
from devsper.intelligence.analysis.analyzer import analyze
|
|
11
|
+
from devsper.intelligence.analysis.formatter import print_run_report
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"RunReport",
|
|
15
|
+
"TaskSummary",
|
|
16
|
+
"build_report_from_events",
|
|
17
|
+
"analyze",
|
|
18
|
+
"print_run_report",
|
|
19
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM-powered plain-English analysis of a RunReport.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from devsper.types.task import TaskStatus
|
|
8
|
+
from devsper.intelligence.analysis.run_report import RunReport
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def analyze(
|
|
12
|
+
report: RunReport,
|
|
13
|
+
worker_model: str,
|
|
14
|
+
stream_callback: Callable[[str], None] | None = None,
|
|
15
|
+
) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Call the LLM with a structured prompt; stream the response for progressive CLI display
|
|
18
|
+
when stream_callback is provided. On error return "Analysis unavailable."
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
from devsper.utils.models import generate
|
|
22
|
+
except ImportError:
|
|
23
|
+
return "Analysis unavailable."
|
|
24
|
+
|
|
25
|
+
bottleneck_desc = ""
|
|
26
|
+
if report.bottleneck_task_id:
|
|
27
|
+
for t in report.tasks:
|
|
28
|
+
if t.task_id == report.bottleneck_task_id:
|
|
29
|
+
bottleneck_desc = f"{t.description[:80]} ({t.duration_seconds:.1f}s)"
|
|
30
|
+
break
|
|
31
|
+
if not bottleneck_desc:
|
|
32
|
+
bottleneck_desc = report.bottleneck_task_id
|
|
33
|
+
|
|
34
|
+
failed_block = []
|
|
35
|
+
for t in report.tasks:
|
|
36
|
+
if t.status == TaskStatus.FAILED:
|
|
37
|
+
failed_block.append(
|
|
38
|
+
f"- {t.description[:100]}: error={t.error or 'unknown'}; tools_that_failed={t.tool_failures or 'none'}"
|
|
39
|
+
)
|
|
40
|
+
failed_text = "\n".join(failed_block) if failed_block else "(none)"
|
|
41
|
+
|
|
42
|
+
user_content = f"""Run ID: {report.run_id}
|
|
43
|
+
Root task: {report.root_task[:200]}
|
|
44
|
+
Duration: {report.total_duration_seconds:.1f}s
|
|
45
|
+
Tasks: {report.completed_tasks}/{report.total_tasks} completed, {report.failed_tasks} failed
|
|
46
|
+
Bottleneck: {bottleneck_desc}
|
|
47
|
+
Failed tasks:
|
|
48
|
+
{failed_text}
|
|
49
|
+
Tool success rate: {report.tool_success_rate:.1f}%
|
|
50
|
+
Peak parallelism: {report.peak_parallelism}"""
|
|
51
|
+
|
|
52
|
+
system_content = """You are analyzing a devsper swarm run. Be concise, specific, and actionable.
|
|
53
|
+
Focus on: what failed and why, what was slow, what could be improved.
|
|
54
|
+
Do not summarize what succeeded unless it's relevant to failures.
|
|
55
|
+
Max 200 words."""
|
|
56
|
+
|
|
57
|
+
prompt = f"{system_content}\n\n---\n\n{user_content}"
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
out = generate(worker_model, prompt, stream=bool(stream_callback))
|
|
61
|
+
if isinstance(out, str):
|
|
62
|
+
return out.strip() or "Analysis unavailable."
|
|
63
|
+
chunks = []
|
|
64
|
+
for chunk in out:
|
|
65
|
+
if chunk:
|
|
66
|
+
chunks.append(chunk)
|
|
67
|
+
if stream_callback:
|
|
68
|
+
stream_callback(chunk)
|
|
69
|
+
return "".join(chunks).strip() or "Analysis unavailable."
|
|
70
|
+
except Exception:
|
|
71
|
+
return "Analysis unavailable."
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cost estimation from token counts and model pricing.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from devsper.intelligence.analysis.run_report import TaskSummary
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
MODEL_PRICING = {
|
|
9
|
+
# (input_per_1k_tokens, output_per_1k_tokens) in USD
|
|
10
|
+
"gpt-4o": (0.0025, 0.010),
|
|
11
|
+
"gpt-4o-mini": (0.000150, 0.000600),
|
|
12
|
+
"claude-opus-4": (0.015, 0.075),
|
|
13
|
+
"claude-sonnet-4": (0.003, 0.015),
|
|
14
|
+
"claude-haiku-4": (0.00025, 0.00125),
|
|
15
|
+
"gemini-1.5-pro": (0.00125, 0.005),
|
|
16
|
+
"gemini-1.5-flash": (0.000075, 0.000300),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CostEstimator:
|
|
21
|
+
"""Estimate run cost from task token usage and model pricing."""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def estimate(
|
|
25
|
+
tasks: list[TaskSummary],
|
|
26
|
+
models_used: list[str],
|
|
27
|
+
) -> float | None:
|
|
28
|
+
"""
|
|
29
|
+
Compute total USD cost from token counts and model pricing.
|
|
30
|
+
Returns None if token counts are not available (don't guess).
|
|
31
|
+
"""
|
|
32
|
+
if not tasks and not models_used:
|
|
33
|
+
return None
|
|
34
|
+
total = 0.0
|
|
35
|
+
any_tokens = False
|
|
36
|
+
for t in tasks:
|
|
37
|
+
if t.tokens_used is None:
|
|
38
|
+
continue
|
|
39
|
+
any_tokens = True
|
|
40
|
+
model = models_used[0] if models_used else None
|
|
41
|
+
if not model:
|
|
42
|
+
return None
|
|
43
|
+
pricing = MODEL_PRICING.get(model)
|
|
44
|
+
if not pricing:
|
|
45
|
+
# Try prefix match for model variants
|
|
46
|
+
for key in MODEL_PRICING:
|
|
47
|
+
if model.startswith(key) or key in model:
|
|
48
|
+
pricing = MODEL_PRICING[key]
|
|
49
|
+
break
|
|
50
|
+
if not pricing:
|
|
51
|
+
return None
|
|
52
|
+
input_per_1k, output_per_1k = pricing
|
|
53
|
+
# Assume 50/50 split if we only have total; task summary has tokens_used as single int
|
|
54
|
+
inp = t.tokens_used // 2
|
|
55
|
+
out = t.tokens_used - inp
|
|
56
|
+
total += (inp / 1000.0) * input_per_1k + (out / 1000.0) * output_per_1k
|
|
57
|
+
if not any_tokens:
|
|
58
|
+
return None
|
|
59
|
+
return round(total, 4)
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def format_cost(usd: float | None) -> str:
|
|
63
|
+
"""Return '$0.0023' or 'unknown'."""
|
|
64
|
+
if usd is None:
|
|
65
|
+
return "unknown"
|
|
66
|
+
return f"${usd:.4f}"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rich CLI output for RunReport.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from devsper.types.task import TaskStatus
|
|
10
|
+
from devsper.intelligence.analysis.run_report import RunReport
|
|
11
|
+
from devsper.intelligence.analysis.cost_estimator import CostEstimator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def print_run_report(report: RunReport, console: Console) -> None:
|
|
15
|
+
"""Print report sections: header, overview, timeline table, critical path, tool usage, analysis panel, footer."""
|
|
16
|
+
# 1. Header
|
|
17
|
+
console.print()
|
|
18
|
+
console.print(f"[bold cyan]Run[/] [bold]{report.run_id}[/]")
|
|
19
|
+
console.print(f" Root task: [dim]{report.root_task[:120]}{'…' if len(report.root_task) > 120 else ''}[/]")
|
|
20
|
+
console.print(f" Strategy: [dim]{report.strategy}[/] Started: [dim]{report.started_at}[/] Finished: [dim]{report.finished_at}[/]")
|
|
21
|
+
console.print()
|
|
22
|
+
|
|
23
|
+
# 2. Overview panel
|
|
24
|
+
cost_str = CostEstimator.format_cost(report.estimated_cost_usd)
|
|
25
|
+
overview = (
|
|
26
|
+
f"Duration: [bold]{report.total_duration_seconds:.1f}s[/] "
|
|
27
|
+
f"Tasks: [green]{report.completed_tasks}[/]/[bold]{report.total_tasks}[/] completed, "
|
|
28
|
+
f"[red]{report.failed_tasks}[/] failed "
|
|
29
|
+
f"Cost: [dim]{cost_str}[/] "
|
|
30
|
+
f"Models: [dim]{', '.join(report.models_used) or '—'}[/]"
|
|
31
|
+
)
|
|
32
|
+
console.print(Panel(overview, title="Overview", border_style="dim"))
|
|
33
|
+
console.print()
|
|
34
|
+
|
|
35
|
+
# 3. Timeline table
|
|
36
|
+
table = Table(title="Timeline")
|
|
37
|
+
table.add_column("Task", style="dim", max_width=50, overflow="fold")
|
|
38
|
+
table.add_column("Role", style="dim", width=10)
|
|
39
|
+
table.add_column("Status", width=12)
|
|
40
|
+
table.add_column("Duration", justify="right", width=10)
|
|
41
|
+
table.add_column("Tools", style="dim", max_width=30, overflow="fold")
|
|
42
|
+
bottleneck_id = report.bottleneck_task_id
|
|
43
|
+
for t in report.tasks:
|
|
44
|
+
desc = (t.description or t.task_id)[:48]
|
|
45
|
+
role = (t.role or "—")
|
|
46
|
+
if t.status == TaskStatus.COMPLETED:
|
|
47
|
+
status = "[green]completed[/]"
|
|
48
|
+
elif t.status == TaskStatus.FAILED:
|
|
49
|
+
status = "[red]failed[/]"
|
|
50
|
+
elif t.status == TaskStatus.PENDING:
|
|
51
|
+
status = "[dim]skipped[/]"
|
|
52
|
+
else:
|
|
53
|
+
status = "[yellow]running[/]"
|
|
54
|
+
dur = f"{t.duration_seconds:.1f}s"
|
|
55
|
+
tools = ", ".join(t.tools_used[:5]) if t.tools_used else "—"
|
|
56
|
+
if len(t.tools_used) > 5:
|
|
57
|
+
tools += "…"
|
|
58
|
+
row_style = ""
|
|
59
|
+
if t.task_id == bottleneck_id:
|
|
60
|
+
row_style = "bold"
|
|
61
|
+
status = "[bold]⚡ bottleneck[/]"
|
|
62
|
+
elif t.status == TaskStatus.FAILED:
|
|
63
|
+
row_style = "red"
|
|
64
|
+
elif t.status == TaskStatus.PENDING:
|
|
65
|
+
row_style = "dim"
|
|
66
|
+
table.add_row(desc, role, status, dur, tools, style=row_style)
|
|
67
|
+
console.print(table)
|
|
68
|
+
console.print()
|
|
69
|
+
|
|
70
|
+
# 4. Critical path
|
|
71
|
+
if report.critical_path:
|
|
72
|
+
path_parts = [
|
|
73
|
+
tid + (" [bold](bottleneck)[/]" if tid == report.bottleneck_task_id else "")
|
|
74
|
+
for tid in report.critical_path
|
|
75
|
+
]
|
|
76
|
+
path_str = " → ".join(path_parts)
|
|
77
|
+
else:
|
|
78
|
+
path_str = "(none)"
|
|
79
|
+
console.print("[bold]Critical path:[/] [dim]" + path_str)
|
|
80
|
+
console.print()
|
|
81
|
+
|
|
82
|
+
# 5. Tool usage (aggregate)
|
|
83
|
+
tool_table = Table(title="Tool usage")
|
|
84
|
+
tool_table.add_column("Metric", style="dim")
|
|
85
|
+
tool_table.add_column("Value", justify="right")
|
|
86
|
+
tool_table.add_row("Tools called", str(report.tools_called))
|
|
87
|
+
tool_table.add_row("Success rate", f"{report.tool_success_rate:.1f}%")
|
|
88
|
+
console.print(tool_table)
|
|
89
|
+
console.print()
|
|
90
|
+
|
|
91
|
+
# 6. Plain-English Analysis (only if already set; CLI streams and prints after)
|
|
92
|
+
if report.plain_english_analysis:
|
|
93
|
+
console.print(
|
|
94
|
+
Panel(
|
|
95
|
+
report.plain_english_analysis,
|
|
96
|
+
title="Plain-English Analysis",
|
|
97
|
+
border_style="dim",
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
console.print()
|
|
101
|
+
|
|
102
|
+
# 7. Footer (caller may print again after streaming analysis)
|
|
103
|
+
console.print(f"[dim]Cost estimate: {CostEstimator.format_cost(report.estimated_cost_usd)}[/]")
|