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/tui/app.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""
|
|
2
|
+
devsper TUI: prompt + output. Enter or r to run. Esc unfocuses input. q quit.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
from textual.app import App, ComposeResult
|
|
9
|
+
from textual.binding import Binding
|
|
10
|
+
from textual.widgets import Footer, Input
|
|
11
|
+
|
|
12
|
+
from devsper.tui.dashboard_screen import DashboardScreen
|
|
13
|
+
from devsper.tui.layout import devsperLayout
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class devsperTUI(App[None]):
|
|
17
|
+
"""Main screen: prompt + output. Run shows loading then result."""
|
|
18
|
+
|
|
19
|
+
TITLE = "devsper"
|
|
20
|
+
SUB_TITLE = "Distributed AI Swarm Runtime"
|
|
21
|
+
|
|
22
|
+
BINDINGS = [
|
|
23
|
+
Binding("r", "run_swarm", "Run", show=True),
|
|
24
|
+
Binding("d", "dashboard", "Dashboard", show=True),
|
|
25
|
+
Binding("p", "toggle_pause", "Pause", show=True),
|
|
26
|
+
Binding("i", "inject_note", "Inject", show=True),
|
|
27
|
+
Binding("escape", "unfocus_input", "Unfocus input", show=True),
|
|
28
|
+
Binding("o", "focus_output", "Output", show=True),
|
|
29
|
+
Binding("q", "quit", "Quit", show=True),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
CSS = """
|
|
33
|
+
/* Compact branding */
|
|
34
|
+
#branding {
|
|
35
|
+
height: auto;
|
|
36
|
+
padding: 0 2;
|
|
37
|
+
text-align: center;
|
|
38
|
+
}
|
|
39
|
+
#logo-line {
|
|
40
|
+
color: #6EE7B7;
|
|
41
|
+
text-style: bold;
|
|
42
|
+
}
|
|
43
|
+
/* Prompt box — full border so box is closed */
|
|
44
|
+
#prompt-box {
|
|
45
|
+
border: heavy #6EE7B7;
|
|
46
|
+
padding: 1 2;
|
|
47
|
+
margin: 0 2 0 2;
|
|
48
|
+
height: auto;
|
|
49
|
+
background: #0F172A;
|
|
50
|
+
}
|
|
51
|
+
#prompt-input {
|
|
52
|
+
border: none;
|
|
53
|
+
background: transparent;
|
|
54
|
+
padding: 0;
|
|
55
|
+
margin: 0;
|
|
56
|
+
width: 100%;
|
|
57
|
+
}
|
|
58
|
+
#action-hints {
|
|
59
|
+
color: #64748b;
|
|
60
|
+
padding: 0 2 1 2;
|
|
61
|
+
text-align: center;
|
|
62
|
+
}
|
|
63
|
+
/* Response area — full border, takes rest of screen */
|
|
64
|
+
#output-container {
|
|
65
|
+
height: 1fr;
|
|
66
|
+
min-height: 8;
|
|
67
|
+
border: heavy #6EE7B7;
|
|
68
|
+
padding: 1 2;
|
|
69
|
+
margin: 0 2 1 2;
|
|
70
|
+
}
|
|
71
|
+
.output-title {
|
|
72
|
+
text-style: bold;
|
|
73
|
+
color: #6EE7B7;
|
|
74
|
+
margin-bottom: 1;
|
|
75
|
+
}
|
|
76
|
+
#results-view {
|
|
77
|
+
scrollbar-size: 1 1;
|
|
78
|
+
overflow-y: auto;
|
|
79
|
+
height: 1fr;
|
|
80
|
+
}
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, events_folder: str = ".devsper/events", *args, **kwargs) -> None:
|
|
84
|
+
super().__init__(*args, **kwargs)
|
|
85
|
+
self._events_folder = events_folder
|
|
86
|
+
self._event_log_path: str | None = None
|
|
87
|
+
self._last_scheduler = None
|
|
88
|
+
self._last_reasoning_store = None
|
|
89
|
+
self._run_thread: threading.Thread | None = None
|
|
90
|
+
self._last_prompt = ""
|
|
91
|
+
self._loading_timer = None
|
|
92
|
+
self._current_swarm = None
|
|
93
|
+
self._paused = False
|
|
94
|
+
self._default_subtitle = "Distributed AI Swarm Runtime"
|
|
95
|
+
|
|
96
|
+
def compose(self) -> ComposeResult:
|
|
97
|
+
yield devsperLayout()
|
|
98
|
+
yield Footer()
|
|
99
|
+
|
|
100
|
+
def on_mount(self) -> None:
|
|
101
|
+
try:
|
|
102
|
+
self.set_focus(self.query_one("#prompt-input"))
|
|
103
|
+
except Exception:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
def _get_results_view(self):
|
|
107
|
+
return self.query_one("#results-view")
|
|
108
|
+
|
|
109
|
+
def _run_swarm_worker(self, prompt: str) -> None:
|
|
110
|
+
"""Run swarm in background thread."""
|
|
111
|
+
try:
|
|
112
|
+
from devsper.config import get_config
|
|
113
|
+
from devsper.utils.event_logger import EventLog
|
|
114
|
+
from devsper.swarm.swarm import Swarm
|
|
115
|
+
from devsper.memory.memory_router import MemoryRouter
|
|
116
|
+
from devsper.memory.memory_store import get_default_store
|
|
117
|
+
from devsper.memory.memory_index import MemoryIndex
|
|
118
|
+
|
|
119
|
+
cfg = get_config()
|
|
120
|
+
event_log = EventLog(events_folder_path=self._events_folder)
|
|
121
|
+
self._event_log_path = event_log.log_path
|
|
122
|
+
memory_router = MemoryRouter(
|
|
123
|
+
store=get_default_store(),
|
|
124
|
+
index=MemoryIndex(get_default_store()),
|
|
125
|
+
top_k=5,
|
|
126
|
+
)
|
|
127
|
+
swarm = Swarm(
|
|
128
|
+
worker_count=2,
|
|
129
|
+
worker_model=cfg.worker_model,
|
|
130
|
+
planner_model=cfg.planner_model,
|
|
131
|
+
event_log=event_log,
|
|
132
|
+
memory_router=memory_router,
|
|
133
|
+
use_tools=True,
|
|
134
|
+
)
|
|
135
|
+
self._current_swarm = swarm
|
|
136
|
+
swarm.run(prompt)
|
|
137
|
+
self._current_swarm = None
|
|
138
|
+
self._last_scheduler = swarm._last_scheduler
|
|
139
|
+
self._last_reasoning_store = getattr(swarm, "_last_reasoning_store", None)
|
|
140
|
+
self.call_from_thread(self._on_swarm_finished)
|
|
141
|
+
except Exception as err:
|
|
142
|
+
msg = str(err)
|
|
143
|
+
self.call_from_thread(lambda: self._on_swarm_error(msg))
|
|
144
|
+
|
|
145
|
+
def _on_swarm_finished(self) -> None:
|
|
146
|
+
self._update_ui_after_run()
|
|
147
|
+
|
|
148
|
+
def _on_swarm_error(self, msg: str) -> None:
|
|
149
|
+
self._run_thread = None
|
|
150
|
+
self._stop_loading_timer()
|
|
151
|
+
try:
|
|
152
|
+
rv = self._get_results_view()
|
|
153
|
+
if rv is not None and hasattr(rv, "set_loading"):
|
|
154
|
+
rv.set_loading(False)
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
self.notify(f"Swarm error: {msg}", severity="error")
|
|
158
|
+
|
|
159
|
+
def _update_ui_after_run(self) -> None:
|
|
160
|
+
self._run_thread = None
|
|
161
|
+
self._stop_loading_timer()
|
|
162
|
+
try:
|
|
163
|
+
rv = self._get_results_view()
|
|
164
|
+
if rv is not None and hasattr(rv, "set_loading"):
|
|
165
|
+
rv.set_loading(False)
|
|
166
|
+
if rv is None or not hasattr(rv, "set_exchange"):
|
|
167
|
+
return
|
|
168
|
+
response = ""
|
|
169
|
+
if self._last_scheduler is not None:
|
|
170
|
+
completed = self._last_scheduler.get_completed_tasks()
|
|
171
|
+
if completed:
|
|
172
|
+
last = completed[-1]
|
|
173
|
+
response = getattr(last, "result", None) or ""
|
|
174
|
+
if len(completed) > 1 and not response.strip():
|
|
175
|
+
response = "\n\n".join(
|
|
176
|
+
getattr(t, "result", "") or "" for t in completed
|
|
177
|
+
)
|
|
178
|
+
rv.set_exchange(self._last_prompt, response)
|
|
179
|
+
except Exception:
|
|
180
|
+
pass
|
|
181
|
+
self.notify("Done.", severity="information")
|
|
182
|
+
|
|
183
|
+
def _read_step_status(self) -> str:
|
|
184
|
+
"""Read event log and return a friendly, sequential step status for the current run."""
|
|
185
|
+
path = getattr(self, "_event_log_path", None)
|
|
186
|
+
if not path or not os.path.isfile(path):
|
|
187
|
+
return "Starting…"
|
|
188
|
+
events = []
|
|
189
|
+
try:
|
|
190
|
+
from devsper.types.event import Event
|
|
191
|
+
|
|
192
|
+
with open(path, "r") as f:
|
|
193
|
+
for line in f:
|
|
194
|
+
line = line.strip()
|
|
195
|
+
if not line:
|
|
196
|
+
continue
|
|
197
|
+
try:
|
|
198
|
+
events.append(Event.model_validate_json(line))
|
|
199
|
+
except Exception:
|
|
200
|
+
continue
|
|
201
|
+
except Exception:
|
|
202
|
+
return "Running…"
|
|
203
|
+
if not events:
|
|
204
|
+
return "Starting…"
|
|
205
|
+
|
|
206
|
+
# task_id → description so the UI can label each step
|
|
207
|
+
task_descriptions: dict[str, str] = {}
|
|
208
|
+
for e in events:
|
|
209
|
+
if getattr(e.type, "value", None) != "task_created":
|
|
210
|
+
continue
|
|
211
|
+
p = e.payload or {}
|
|
212
|
+
tid = p.get("task_id")
|
|
213
|
+
desc = p.get("description")
|
|
214
|
+
if tid and desc:
|
|
215
|
+
task_descriptions[tid] = (desc or "").strip()
|
|
216
|
+
|
|
217
|
+
def _truncate(s: str, max_len: int = 48) -> str:
|
|
218
|
+
s = (s or "").strip()
|
|
219
|
+
if len(s) <= max_len:
|
|
220
|
+
return s
|
|
221
|
+
return s[: max_len - 1].rstrip() + "…"
|
|
222
|
+
|
|
223
|
+
last = events[-1]
|
|
224
|
+
ev_type = getattr(last.type, "value", str(last.type))
|
|
225
|
+
payload = last.payload or {}
|
|
226
|
+
task_id = (payload.get("task_id") or "").strip()
|
|
227
|
+
desc = task_descriptions.get(task_id) if task_id else None
|
|
228
|
+
total_tasks = sum(1 for e in events if getattr(e.type, "value", None) == "task_created")
|
|
229
|
+
completed = sum(1 for e in events if getattr(e.type, "value", None) == "task_completed")
|
|
230
|
+
current_step = completed + 1 if ev_type in ("agent_started", "task_started") else completed
|
|
231
|
+
|
|
232
|
+
if ev_type == "swarm_started":
|
|
233
|
+
return "Starting…"
|
|
234
|
+
if ev_type == "planner_started":
|
|
235
|
+
return "Planning your request…"
|
|
236
|
+
if ev_type == "task_created":
|
|
237
|
+
return f"Planned {total_tasks} step(s)…"
|
|
238
|
+
if ev_type == "planner_finished":
|
|
239
|
+
n = payload.get("subtask_count", total_tasks) or total_tasks
|
|
240
|
+
return f"Ready. Executing step 1 of {n}…"
|
|
241
|
+
if ev_type == "executor_started":
|
|
242
|
+
if total_tasks > 0:
|
|
243
|
+
return f"Executing step 1 of {total_tasks}…"
|
|
244
|
+
return "Executing…"
|
|
245
|
+
if ev_type in ("agent_started", "task_started"):
|
|
246
|
+
if total_tasks > 0 and current_step <= total_tasks:
|
|
247
|
+
step_label = f"Step {current_step} of {total_tasks}: "
|
|
248
|
+
else:
|
|
249
|
+
step_label = ""
|
|
250
|
+
if desc:
|
|
251
|
+
return step_label + _truncate(desc)
|
|
252
|
+
return (step_label or "Working on task…").rstrip(": ") or "Working on task…"
|
|
253
|
+
if ev_type in ("task_completed", "agent_finished"):
|
|
254
|
+
if total_tasks > 0:
|
|
255
|
+
if completed < total_tasks:
|
|
256
|
+
next_n = completed + 1
|
|
257
|
+
return f"Finished step {completed} of {total_tasks}. Executing step {next_n}…"
|
|
258
|
+
return f"Finished step {completed} of {total_tasks}. Assembling result…"
|
|
259
|
+
return "Finished. Assembling result…"
|
|
260
|
+
if ev_type == "executor_finished":
|
|
261
|
+
return "Assembling final result…"
|
|
262
|
+
if ev_type == "swarm_finished":
|
|
263
|
+
return "Done. Here’s your result."
|
|
264
|
+
return "Running…"
|
|
265
|
+
|
|
266
|
+
def _start_loading_timer(self) -> None:
|
|
267
|
+
"""Tick spinner and update step status from event log."""
|
|
268
|
+
self._stop_loading_timer()
|
|
269
|
+
|
|
270
|
+
def tick() -> None:
|
|
271
|
+
if not (self._run_thread and self._run_thread.is_alive()):
|
|
272
|
+
self._stop_loading_timer()
|
|
273
|
+
return
|
|
274
|
+
try:
|
|
275
|
+
rv = self._get_results_view()
|
|
276
|
+
if rv is not None:
|
|
277
|
+
step = self._read_step_status()
|
|
278
|
+
if hasattr(rv, "set_loading"):
|
|
279
|
+
rv.set_loading(True, step)
|
|
280
|
+
if hasattr(rv, "tick_loading"):
|
|
281
|
+
rv.tick_loading()
|
|
282
|
+
except Exception:
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
self._loading_timer = self.set_interval(0.25, tick)
|
|
286
|
+
|
|
287
|
+
def _stop_loading_timer(self) -> None:
|
|
288
|
+
if getattr(self, "_loading_timer", None) is not None:
|
|
289
|
+
try:
|
|
290
|
+
self._loading_timer.stop()
|
|
291
|
+
except Exception:
|
|
292
|
+
pass
|
|
293
|
+
self._loading_timer = None
|
|
294
|
+
|
|
295
|
+
def _get_prompt_from_input(self) -> str:
|
|
296
|
+
try:
|
|
297
|
+
inp = self.query_one("#prompt-input", Input)
|
|
298
|
+
val = (inp.value or "").strip()
|
|
299
|
+
if val:
|
|
300
|
+
return val
|
|
301
|
+
except Exception:
|
|
302
|
+
pass
|
|
303
|
+
return "Summarize the concept of swarm intelligence in one paragraph."
|
|
304
|
+
|
|
305
|
+
def _start_swarm_run(self, prompt: str) -> None:
|
|
306
|
+
"""Start swarm with the given prompt (call only when not already running)."""
|
|
307
|
+
self._last_prompt = (prompt or "").strip()
|
|
308
|
+
try:
|
|
309
|
+
rv = self._get_results_view()
|
|
310
|
+
if rv is not None and hasattr(rv, "set_loading"):
|
|
311
|
+
rv.set_loading(True, "Starting…")
|
|
312
|
+
except Exception:
|
|
313
|
+
pass
|
|
314
|
+
self._run_thread = threading.Thread(
|
|
315
|
+
target=self._run_swarm_worker, args=(prompt,), daemon=True
|
|
316
|
+
)
|
|
317
|
+
self._run_thread.start()
|
|
318
|
+
self._start_loading_timer()
|
|
319
|
+
|
|
320
|
+
def action_run_swarm(self) -> None:
|
|
321
|
+
"""Run swarm with prompt from input (or default). Enter or r to run."""
|
|
322
|
+
if self._run_thread and self._run_thread.is_alive():
|
|
323
|
+
self.notify("Wait for current run to finish.", severity="warning")
|
|
324
|
+
return
|
|
325
|
+
prompt = self._get_prompt_from_input()
|
|
326
|
+
self._start_swarm_run(prompt)
|
|
327
|
+
|
|
328
|
+
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
329
|
+
"""Run swarm when user presses Enter in the prompt input."""
|
|
330
|
+
if event.input.id != "prompt-input":
|
|
331
|
+
return
|
|
332
|
+
if self._run_thread and self._run_thread.is_alive():
|
|
333
|
+
self.notify("Wait for current run to finish.", severity="warning")
|
|
334
|
+
return
|
|
335
|
+
prompt = (event.input.value or "").strip()
|
|
336
|
+
if not prompt:
|
|
337
|
+
self.notify("Type a task above, then Enter or r to run.", severity="warning")
|
|
338
|
+
return
|
|
339
|
+
self._start_swarm_run(prompt)
|
|
340
|
+
|
|
341
|
+
def action_focus_output(self) -> None:
|
|
342
|
+
try:
|
|
343
|
+
self.set_focus(self.query_one("#results-view"))
|
|
344
|
+
except Exception:
|
|
345
|
+
pass
|
|
346
|
+
|
|
347
|
+
def action_unfocus_input(self) -> None:
|
|
348
|
+
"""Move focus off the input so r / q work. Press Esc when stuck in the input."""
|
|
349
|
+
try:
|
|
350
|
+
focused = self.focused
|
|
351
|
+
if focused is not None and getattr(focused, "id", None) == "prompt-input":
|
|
352
|
+
out = self.query_one("#results-view")
|
|
353
|
+
if out.can_focus:
|
|
354
|
+
self.set_focus(out)
|
|
355
|
+
except Exception:
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
def action_dashboard(self) -> None:
|
|
359
|
+
"""Open dashboard screen (tasks, swarm graph, memory, logs)."""
|
|
360
|
+
self.push_screen(
|
|
361
|
+
DashboardScreen(
|
|
362
|
+
app_ref=self,
|
|
363
|
+
event_log_path=getattr(self, "_event_log_path", None),
|
|
364
|
+
)
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def action_toggle_pause(self) -> None:
|
|
368
|
+
"""Toggle pause/resume for the current swarm run."""
|
|
369
|
+
swarm = getattr(self, "_current_swarm", None)
|
|
370
|
+
if swarm is None:
|
|
371
|
+
self.notify("No run in progress.", severity="warning")
|
|
372
|
+
return
|
|
373
|
+
self._paused = not self._paused
|
|
374
|
+
if self._paused:
|
|
375
|
+
swarm.pause()
|
|
376
|
+
self.sub_title = "[yellow]⏸ PAUSED[/]"
|
|
377
|
+
self.notify("Paused: current tasks will finish, no new tasks start.", severity="information")
|
|
378
|
+
else:
|
|
379
|
+
swarm.resume()
|
|
380
|
+
self.sub_title = self._default_subtitle
|
|
381
|
+
self.notify("Resumed.", severity="information")
|
|
382
|
+
|
|
383
|
+
def action_inject_note(self) -> None:
|
|
384
|
+
"""Open overlay to inject a note to the swarm (stored as high-priority memory)."""
|
|
385
|
+
from devsper.tui.inject_screen import InjectScreen
|
|
386
|
+
self.push_screen(InjectScreen())
|
|
387
|
+
|
|
388
|
+
def action_quit(self) -> None:
|
|
389
|
+
self.exit()
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def run_tui(events_folder: str = ".devsper/events") -> None:
|
|
393
|
+
"""Entry point to run the TUI."""
|
|
394
|
+
app = devsperTUI(events_folder=events_folder)
|
|
395
|
+
app.run()
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dashboard screen: Tasks, Swarm Graph, Memory, Logs.
|
|
3
|
+
|
|
4
|
+
Shown when user presses `d`; Esc or q to return to main (prompt + output) view.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from textual.app import ComposeResult
|
|
8
|
+
from textual.containers import Container, Horizontal, Vertical
|
|
9
|
+
from textual.screen import Screen
|
|
10
|
+
from textual.widgets import Static
|
|
11
|
+
|
|
12
|
+
from devsper.tui.task_view import TaskView
|
|
13
|
+
from devsper.tui.task_detail_screen import TaskDetailScreen
|
|
14
|
+
from devsper.tui.swarm_view import SwarmView
|
|
15
|
+
from devsper.tui.memory_view import MemoryView
|
|
16
|
+
from devsper.tui.logs_view import LogsView
|
|
17
|
+
from devsper.tui.activity_feed_view import ActivityFeedView
|
|
18
|
+
from devsper.tui.knowledge_graph_view import KnowledgeGraphView
|
|
19
|
+
from devsper.tui.performance_view import PerformanceView
|
|
20
|
+
from devsper.tui.reasoning_graph_view import ReasoningGraphView
|
|
21
|
+
from devsper.tui.agent_role_view import AgentRoleActivityView
|
|
22
|
+
from devsper.tui.adaptive_tasks_view import AdaptiveTasksView
|
|
23
|
+
from devsper.tui.dev_view import DevView
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DashboardScreen(Screen[None]):
|
|
27
|
+
"""Full-screen dashboard: tasks, swarm graph, memory, logs. Esc to close."""
|
|
28
|
+
|
|
29
|
+
BINDINGS = [
|
|
30
|
+
("escape", "back", "Back to chat"),
|
|
31
|
+
("q", "back", "Back to chat"),
|
|
32
|
+
("enter", "open_task_detail", "Task detail"),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
CSS = """
|
|
36
|
+
#dashboard-container {
|
|
37
|
+
height: 100%;
|
|
38
|
+
padding: 0 2 1 2;
|
|
39
|
+
layout: vertical;
|
|
40
|
+
}
|
|
41
|
+
#dashboard-header {
|
|
42
|
+
color: #6EE7B7;
|
|
43
|
+
text-style: bold;
|
|
44
|
+
padding: 1 2;
|
|
45
|
+
margin-bottom: 1;
|
|
46
|
+
border: heavy #6EE7B7;
|
|
47
|
+
}
|
|
48
|
+
#dashboard-top {
|
|
49
|
+
height: 1fr;
|
|
50
|
+
min-height: 10;
|
|
51
|
+
}
|
|
52
|
+
#dashboard-mid {
|
|
53
|
+
height: 1fr;
|
|
54
|
+
min-height: 8;
|
|
55
|
+
}
|
|
56
|
+
.d-panel {
|
|
57
|
+
width: 1fr;
|
|
58
|
+
height: 1fr;
|
|
59
|
+
min-height: 6;
|
|
60
|
+
border: solid #6EE7B7;
|
|
61
|
+
padding: 1 2;
|
|
62
|
+
margin: 0 1 1 1;
|
|
63
|
+
}
|
|
64
|
+
.d-panel-title {
|
|
65
|
+
text-style: bold;
|
|
66
|
+
color: #6EE7B7;
|
|
67
|
+
margin-bottom: 1;
|
|
68
|
+
}
|
|
69
|
+
#dashboard-logs {
|
|
70
|
+
height: 1fr;
|
|
71
|
+
min-height: 6;
|
|
72
|
+
border: solid #6EE7B7;
|
|
73
|
+
padding: 1 2;
|
|
74
|
+
margin: 0 1 1 1;
|
|
75
|
+
}
|
|
76
|
+
# v1.2 panels
|
|
77
|
+
#dashboard-reasoning-row { height: auto; min-height: 6; }
|
|
78
|
+
TaskView, SwarmView, MemoryView, LogsView, ActivityFeedView, KnowledgeGraphView, PerformanceView,
|
|
79
|
+
ReasoningGraphView, AgentRoleActivityView, AdaptiveTasksView, DevView {
|
|
80
|
+
scrollbar-size: 1 1;
|
|
81
|
+
overflow-y: auto;
|
|
82
|
+
height: 1fr;
|
|
83
|
+
}
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
app_ref: object,
|
|
89
|
+
event_log_path: str | None = None,
|
|
90
|
+
*args,
|
|
91
|
+
**kwargs,
|
|
92
|
+
) -> None:
|
|
93
|
+
super().__init__(*args, **kwargs)
|
|
94
|
+
self._app_ref = app_ref
|
|
95
|
+
self._event_log_path = event_log_path
|
|
96
|
+
|
|
97
|
+
def compose(self) -> ComposeResult:
|
|
98
|
+
with Container(id="dashboard-container"):
|
|
99
|
+
yield Static(
|
|
100
|
+
" Dashboard — Tasks | Swarm | Memory | Activity | KG | Perf | Reasoning | Roles | Adaptive | Dev | Logs — Esc to back",
|
|
101
|
+
id="dashboard-header",
|
|
102
|
+
)
|
|
103
|
+
with Horizontal(id="dashboard-top"):
|
|
104
|
+
with Vertical(classes="d-panel"):
|
|
105
|
+
yield Static("Tasks", classes="d-panel-title")
|
|
106
|
+
yield TaskView(id="task-view")
|
|
107
|
+
with Vertical(classes="d-panel"):
|
|
108
|
+
yield Static("Swarm Graph", classes="d-panel-title")
|
|
109
|
+
yield SwarmView(id="swarm-view")
|
|
110
|
+
with Vertical(classes="d-panel"):
|
|
111
|
+
yield Static("Memory", classes="d-panel-title")
|
|
112
|
+
yield MemoryView(id="memory-view")
|
|
113
|
+
with Horizontal(id="dashboard-mid"):
|
|
114
|
+
with Vertical(classes="d-panel"):
|
|
115
|
+
yield Static("Activity Feed", classes="d-panel-title")
|
|
116
|
+
yield ActivityFeedView(id="activity-feed-view")
|
|
117
|
+
with Vertical(classes="d-panel"):
|
|
118
|
+
yield Static("Knowledge Graph", classes="d-panel-title")
|
|
119
|
+
yield KnowledgeGraphView(id="knowledge-graph-view")
|
|
120
|
+
with Vertical(classes="d-panel"):
|
|
121
|
+
yield Static(
|
|
122
|
+
"Performance (speculative | cache | tools)",
|
|
123
|
+
classes="d-panel-title",
|
|
124
|
+
)
|
|
125
|
+
yield PerformanceView(id="performance-view")
|
|
126
|
+
with Horizontal(id="dashboard-reasoning-row"):
|
|
127
|
+
with Vertical(classes="d-panel"):
|
|
128
|
+
yield Static("Reasoning Graph", classes="d-panel-title")
|
|
129
|
+
yield ReasoningGraphView(id="reasoning-graph-view")
|
|
130
|
+
with Vertical(classes="d-panel"):
|
|
131
|
+
yield Static("Agent Role Activity", classes="d-panel-title")
|
|
132
|
+
yield AgentRoleActivityView(id="agent-role-view")
|
|
133
|
+
with Vertical(classes="d-panel"):
|
|
134
|
+
yield Static("Adaptive Task Creation", classes="d-panel-title")
|
|
135
|
+
yield AdaptiveTasksView(id="adaptive-tasks-view")
|
|
136
|
+
with Horizontal(id="dashboard-dev-row"):
|
|
137
|
+
with Vertical(classes="d-panel"):
|
|
138
|
+
yield Static("Dev — Repository tree | Test results | File changes", classes="d-panel-title")
|
|
139
|
+
yield DevView(id="dev-view")
|
|
140
|
+
with Vertical(id="dashboard-logs"):
|
|
141
|
+
yield Static("Logs", classes="d-panel-title")
|
|
142
|
+
yield LogsView(id="logs-view")
|
|
143
|
+
|
|
144
|
+
def on_mount(self) -> None:
|
|
145
|
+
self._refresh_all()
|
|
146
|
+
|
|
147
|
+
def _refresh_all(self) -> None:
|
|
148
|
+
app = self._app_ref
|
|
149
|
+
events_folder = getattr(app, "_events_folder", ".devsper/events")
|
|
150
|
+
try:
|
|
151
|
+
lv = self.query_one("#logs-view", LogsView)
|
|
152
|
+
lv.set_events_folder(events_folder)
|
|
153
|
+
if getattr(self, "_event_log_path", None):
|
|
154
|
+
lv.set_log_path(self._event_log_path)
|
|
155
|
+
lv.refresh_logs()
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
158
|
+
try:
|
|
159
|
+
af = self.query_one("#activity-feed-view", ActivityFeedView)
|
|
160
|
+
af.set_events_folder(events_folder)
|
|
161
|
+
if getattr(self, "_event_log_path", None):
|
|
162
|
+
af.set_log_path(self._event_log_path)
|
|
163
|
+
af.refresh_events()
|
|
164
|
+
except Exception:
|
|
165
|
+
pass
|
|
166
|
+
try:
|
|
167
|
+
kg = self.query_one("#knowledge-graph-view", KnowledgeGraphView)
|
|
168
|
+
kg.load_from_memory()
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
171
|
+
try:
|
|
172
|
+
mv = self.query_one("#memory-view", MemoryView)
|
|
173
|
+
mv.load_from_store()
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
scheduler = getattr(app, "_last_scheduler", None)
|
|
177
|
+
if scheduler is not None:
|
|
178
|
+
try:
|
|
179
|
+
sv = self.query_one("#swarm-view", SwarmView)
|
|
180
|
+
sv.set_scheduler(scheduler)
|
|
181
|
+
except Exception:
|
|
182
|
+
pass
|
|
183
|
+
tasks = []
|
|
184
|
+
try:
|
|
185
|
+
graph = scheduler._graph
|
|
186
|
+
for nid in graph.nodes():
|
|
187
|
+
task = scheduler._tasks.get(nid)
|
|
188
|
+
if task:
|
|
189
|
+
status = getattr(task.status, "name", str(task.status))
|
|
190
|
+
tasks.append(
|
|
191
|
+
{
|
|
192
|
+
"task_id": task.id,
|
|
193
|
+
"description": getattr(task, "description", "") or "",
|
|
194
|
+
"status": status.lower(),
|
|
195
|
+
"runtime": "-",
|
|
196
|
+
"worker": "agent",
|
|
197
|
+
"result": getattr(task, "result", "") or "",
|
|
198
|
+
"error": getattr(task, "result", "") if status == "FAILED" else None,
|
|
199
|
+
"role": getattr(task, "role", None),
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
except Exception:
|
|
203
|
+
pass
|
|
204
|
+
if tasks:
|
|
205
|
+
try:
|
|
206
|
+
tv = self.query_one("#task-view", TaskView)
|
|
207
|
+
tv.set_tasks(tasks)
|
|
208
|
+
except Exception:
|
|
209
|
+
pass
|
|
210
|
+
try:
|
|
211
|
+
pv = self.query_one("#performance-view", PerformanceView)
|
|
212
|
+
speculative_count = 0
|
|
213
|
+
if scheduler is not None:
|
|
214
|
+
speculative_count = sum(
|
|
215
|
+
1
|
|
216
|
+
for t in scheduler._tasks.values()
|
|
217
|
+
if getattr(t, "speculative", False)
|
|
218
|
+
)
|
|
219
|
+
try:
|
|
220
|
+
from devsper.config import get_config
|
|
221
|
+
|
|
222
|
+
cfg = get_config()
|
|
223
|
+
speculative_enabled = getattr(cfg.swarm, "speculative_execution", False)
|
|
224
|
+
except Exception:
|
|
225
|
+
speculative_enabled = False
|
|
226
|
+
try:
|
|
227
|
+
from devsper.cache import TaskCache
|
|
228
|
+
|
|
229
|
+
cache_entries = TaskCache().stats()["entries"]
|
|
230
|
+
except Exception:
|
|
231
|
+
cache_entries = 0
|
|
232
|
+
try:
|
|
233
|
+
from devsper.analytics import get_default_analytics
|
|
234
|
+
|
|
235
|
+
tool_stats = get_default_analytics().get_stats()
|
|
236
|
+
except Exception:
|
|
237
|
+
tool_stats = None
|
|
238
|
+
pv.set_stats(
|
|
239
|
+
speculative_enabled=speculative_enabled,
|
|
240
|
+
speculative_count=speculative_count,
|
|
241
|
+
cache_entries=cache_entries,
|
|
242
|
+
tool_stats=tool_stats,
|
|
243
|
+
)
|
|
244
|
+
except Exception:
|
|
245
|
+
pass
|
|
246
|
+
# v1.2: reasoning graph, agent roles, adaptive tasks
|
|
247
|
+
try:
|
|
248
|
+
rgv = self.query_one("#reasoning-graph-view", ReasoningGraphView)
|
|
249
|
+
reasoning_store = getattr(app, "_last_reasoning_store", None)
|
|
250
|
+
rgv.set_reasoning_store(reasoning_store)
|
|
251
|
+
rgv.load_from_store()
|
|
252
|
+
except Exception:
|
|
253
|
+
pass
|
|
254
|
+
try:
|
|
255
|
+
arv = self.query_one("#agent-role-view", AgentRoleActivityView)
|
|
256
|
+
tasks_with_roles = []
|
|
257
|
+
if scheduler is not None:
|
|
258
|
+
for t in scheduler._tasks.values():
|
|
259
|
+
tasks_with_roles.append({
|
|
260
|
+
"task_id": t.id,
|
|
261
|
+
"role": getattr(t, "role", None),
|
|
262
|
+
"status": getattr(t.status, "name", str(t.status)),
|
|
263
|
+
})
|
|
264
|
+
arv.set_tasks_with_roles(tasks_with_roles)
|
|
265
|
+
except Exception:
|
|
266
|
+
pass
|
|
267
|
+
try:
|
|
268
|
+
atv = self.query_one("#adaptive-tasks-view", AdaptiveTasksView)
|
|
269
|
+
atv.set_events_folder(events_folder)
|
|
270
|
+
atv.set_log_path(getattr(self, "_event_log_path", None))
|
|
271
|
+
atv.refresh_adaptive_events()
|
|
272
|
+
except Exception:
|
|
273
|
+
pass
|
|
274
|
+
|
|
275
|
+
def action_back(self) -> None:
|
|
276
|
+
self.dismiss(None)
|
|
277
|
+
|
|
278
|
+
def action_open_task_detail(self) -> None:
|
|
279
|
+
"""Open task detail overlay for the selected task in TaskView."""
|
|
280
|
+
try:
|
|
281
|
+
tv = self.query_one("#task-view", TaskView)
|
|
282
|
+
task = tv.get_selected_task()
|
|
283
|
+
if task:
|
|
284
|
+
self.app.push_screen(TaskDetailScreen(task, self._app_ref))
|
|
285
|
+
except Exception:
|
|
286
|
+
pass
|
|
287
|
+
|
|
288
|
+
def on_task_view_task_selected(self, event: TaskView.TaskSelected) -> None:
|
|
289
|
+
"""When TaskView emits TaskSelected (e.g. Enter on list), open detail."""
|
|
290
|
+
self.app.push_screen(TaskDetailScreen(event.task, self._app_ref))
|