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,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Swarm visualization: produce a simple ASCII DAG of the scheduler graph.
|
|
3
|
+
|
|
4
|
+
Input: scheduler (or its graph). Output: ASCII DAG visualization.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from devsper.swarm.scheduler import Scheduler
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def visualize_scheduler_dag(scheduler: Scheduler) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Create a simple ASCII DAG visualization of the scheduler's task graph.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
task_1
|
|
16
|
+
|
|
|
17
|
+
task_2
|
|
18
|
+
|
|
|
19
|
+
task_3
|
|
20
|
+
/ \\
|
|
21
|
+
task_4 task_5
|
|
22
|
+
"""
|
|
23
|
+
graph = scheduler._graph
|
|
24
|
+
if graph.number_of_nodes() == 0:
|
|
25
|
+
return "(empty graph)"
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import networkx as nx
|
|
29
|
+
order = list(nx.topological_sort(graph))
|
|
30
|
+
except Exception:
|
|
31
|
+
order = list(graph.nodes())
|
|
32
|
+
|
|
33
|
+
roots = [n for n in order if graph.in_degree(n) == 0]
|
|
34
|
+
if not roots:
|
|
35
|
+
roots = [order[0]]
|
|
36
|
+
|
|
37
|
+
lines = []
|
|
38
|
+
|
|
39
|
+
def visit(node: str, print_name: bool) -> None:
|
|
40
|
+
if print_name:
|
|
41
|
+
lines.append(node)
|
|
42
|
+
succs = list(graph.successors(node))
|
|
43
|
+
if not succs:
|
|
44
|
+
return
|
|
45
|
+
if len(succs) == 1:
|
|
46
|
+
lines.append(" |")
|
|
47
|
+
lines.append(succs[0])
|
|
48
|
+
visit(succs[0], False)
|
|
49
|
+
else:
|
|
50
|
+
lines.append(" " + " / \\")
|
|
51
|
+
lines.append(" " + " ".join(succs))
|
|
52
|
+
for c in succs:
|
|
53
|
+
visit(c, False)
|
|
54
|
+
|
|
55
|
+
for r in roots:
|
|
56
|
+
visit(r, True)
|
|
57
|
+
|
|
58
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AgentSandbox: wraps agent execution with resource quotas and isolation.
|
|
3
|
+
|
|
4
|
+
Enforces: allowed_tool_categories, max_tool_calls, max_output_tokens;
|
|
5
|
+
tracks usage (wall time, tool calls, tokens).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from devsper.agents.agent import Agent, AgentRequest, AgentResponse
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from devsper.config.schema import SandboxConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ResourceQuota:
|
|
19
|
+
"""Limits and permissions for a sandboxed run."""
|
|
20
|
+
max_memory_mb: int = 512
|
|
21
|
+
max_cpu_seconds: int = 60
|
|
22
|
+
max_tool_calls: int = 20
|
|
23
|
+
max_output_tokens: int = 4096
|
|
24
|
+
allowed_tool_categories: list[str] | None = None # None = all
|
|
25
|
+
blocked_tool_categories: list[str] = field(default_factory=list)
|
|
26
|
+
network_access: bool = True
|
|
27
|
+
filesystem_write: bool = False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SandboxQuotaExceeded(Exception):
|
|
31
|
+
"""Raised when a quota limit is exceeded."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, quota_type: str, limit: int | float, actual: int | float) -> None:
|
|
34
|
+
self.quota_type = quota_type
|
|
35
|
+
self.limit = limit
|
|
36
|
+
self.actual = actual
|
|
37
|
+
super().__init__(f"Sandbox quota exceeded: {quota_type} (limit={limit}, actual={actual})")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _get_tool_categories(tool_name: str) -> list[str]:
|
|
41
|
+
"""Return categories for a tool (from registry if available)."""
|
|
42
|
+
try:
|
|
43
|
+
from devsper.tools.registry import get
|
|
44
|
+
t = get(tool_name)
|
|
45
|
+
if t is not None and getattr(t, "category", None):
|
|
46
|
+
return [t.category]
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _filter_tools_by_quota(tool_names: list[str], quota: ResourceQuota) -> list[str]:
|
|
53
|
+
"""Return tool names that are allowed by quota."""
|
|
54
|
+
if quota.allowed_tool_categories is None and not quota.blocked_tool_categories:
|
|
55
|
+
return list(tool_names)
|
|
56
|
+
out = []
|
|
57
|
+
for name in tool_names:
|
|
58
|
+
cats = _get_tool_categories(name)
|
|
59
|
+
if quota.blocked_tool_categories and any(c in quota.blocked_tool_categories for c in cats):
|
|
60
|
+
continue
|
|
61
|
+
if quota.allowed_tool_categories is not None and cats:
|
|
62
|
+
if not any(c in quota.allowed_tool_categories for c in cats):
|
|
63
|
+
continue
|
|
64
|
+
out.append(name)
|
|
65
|
+
return out
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AgentSandbox:
|
|
69
|
+
"""Wraps agent execution with resource quotas. Use run(request, quota) -> AgentResponse."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, agent: Agent) -> None:
|
|
72
|
+
self.agent = agent
|
|
73
|
+
|
|
74
|
+
def run(self, request: AgentRequest, quota: ResourceQuota) -> AgentResponse:
|
|
75
|
+
"""
|
|
76
|
+
Run the agent with quota enforcement: filter tools, enforce max_tool_calls and max_output_tokens.
|
|
77
|
+
Tracks usage and includes it in AgentResponse (via agent's response; we add quota_usage if needed).
|
|
78
|
+
"""
|
|
79
|
+
filtered_tools = _filter_tools_by_quota(request.tools, quota)
|
|
80
|
+
req = AgentRequest(
|
|
81
|
+
task=request.task,
|
|
82
|
+
memory_context=request.memory_context,
|
|
83
|
+
tools=filtered_tools,
|
|
84
|
+
model=request.model,
|
|
85
|
+
system_prompt=request.system_prompt,
|
|
86
|
+
prefetch_used=request.prefetch_used,
|
|
87
|
+
)
|
|
88
|
+
tool_call_count = [0]
|
|
89
|
+
max_tool_calls = quota.max_tool_calls
|
|
90
|
+
max_output_tokens = quota.max_output_tokens
|
|
91
|
+
|
|
92
|
+
from devsper.tools.tool_runner import run_tool as _run_tool_impl
|
|
93
|
+
|
|
94
|
+
def counting_run_tool(name: str, args: dict, task_type: str | None = None):
|
|
95
|
+
tool_call_count[0] += 1
|
|
96
|
+
if tool_call_count[0] > max_tool_calls:
|
|
97
|
+
raise SandboxQuotaExceeded("max_tool_calls", max_tool_calls, tool_call_count[0])
|
|
98
|
+
return _run_tool_impl(name, args, task_type=task_type)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
with _patch_tool_runner(counting_run_tool):
|
|
102
|
+
response = self.agent.run(req)
|
|
103
|
+
except SandboxQuotaExceeded:
|
|
104
|
+
raise
|
|
105
|
+
|
|
106
|
+
result = response.result or ""
|
|
107
|
+
if max_output_tokens and len(result) > max_output_tokens:
|
|
108
|
+
result = result[:max_output_tokens] + "\n\n[Output truncated: max_output_tokens exceeded.]"
|
|
109
|
+
return AgentResponse(
|
|
110
|
+
task_id=response.task_id,
|
|
111
|
+
result=result,
|
|
112
|
+
tools_called=response.tools_called,
|
|
113
|
+
broadcasts=response.broadcasts,
|
|
114
|
+
tokens_used=response.tokens_used,
|
|
115
|
+
duration_seconds=response.duration_seconds,
|
|
116
|
+
error=response.error,
|
|
117
|
+
success=response.success,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_quota_for_role(sandbox_config: "SandboxConfig", role: str | None) -> ResourceQuota:
|
|
122
|
+
"""Build ResourceQuota for a role from sandbox config (defaults + role overrides)."""
|
|
123
|
+
role = (role or "").strip()
|
|
124
|
+
q = ResourceQuota(
|
|
125
|
+
max_memory_mb=sandbox_config.default_max_memory_mb,
|
|
126
|
+
max_cpu_seconds=sandbox_config.default_max_cpu_seconds,
|
|
127
|
+
max_tool_calls=sandbox_config.default_max_tool_calls,
|
|
128
|
+
max_output_tokens=4096,
|
|
129
|
+
)
|
|
130
|
+
for r in sandbox_config.roles or []:
|
|
131
|
+
if (r.role or "").strip() == role:
|
|
132
|
+
if r.max_tool_calls is not None:
|
|
133
|
+
q.max_tool_calls = r.max_tool_calls
|
|
134
|
+
if r.allowed_tool_categories is not None:
|
|
135
|
+
q.allowed_tool_categories = r.allowed_tool_categories
|
|
136
|
+
if r.blocked_tool_categories:
|
|
137
|
+
q.blocked_tool_categories = list(r.blocked_tool_categories)
|
|
138
|
+
if r.filesystem_write:
|
|
139
|
+
q.filesystem_write = True
|
|
140
|
+
break
|
|
141
|
+
return q
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class _patch_tool_runner:
|
|
145
|
+
"""Temporarily patch the tool runner so we can count calls."""
|
|
146
|
+
|
|
147
|
+
def __init__(self, replacement):
|
|
148
|
+
self.replacement = replacement
|
|
149
|
+
self.orig = None
|
|
150
|
+
|
|
151
|
+
def __enter__(self):
|
|
152
|
+
import devsper.tools.tool_runner as tr
|
|
153
|
+
self.orig = getattr(tr, "run_tool", None)
|
|
154
|
+
tr.run_tool = self.replacement
|
|
155
|
+
return self
|
|
156
|
+
|
|
157
|
+
def __exit__(self, *args):
|
|
158
|
+
import devsper.tools.tool_runner as tr
|
|
159
|
+
if self.orig is not None:
|
|
160
|
+
tr.run_tool = self.orig
|
|
161
|
+
return False
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Scheduler checkpointer: write snapshot to disk periodically for resume and node sync.
|
|
3
|
+
|
|
4
|
+
v1.9: Writes to {events_dir}/{run_id}.checkpoint.json. Atomic write. Restore for resume.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from devsper.swarm.scheduler import Scheduler
|
|
11
|
+
from devsper.types.exceptions import CheckpointNotFoundError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SchedulerCheckpointer:
|
|
15
|
+
"""Writes scheduler.snapshot() to disk periodically. Atomic write. Restore by run_id."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
events_dir: str = ".devsper/events",
|
|
20
|
+
interval_tasks: int = 10,
|
|
21
|
+
) -> None:
|
|
22
|
+
self.events_dir = events_dir
|
|
23
|
+
self.interval_tasks = interval_tasks
|
|
24
|
+
self._completions_since_write = 0
|
|
25
|
+
|
|
26
|
+
def _path(self, run_id: str) -> str:
|
|
27
|
+
return os.path.join(self.events_dir, f"{run_id}.checkpoint.json")
|
|
28
|
+
|
|
29
|
+
def _write_atomic(self, path: str, data: dict) -> None:
|
|
30
|
+
tmp = path + ".tmp"
|
|
31
|
+
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
|
32
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
33
|
+
json.dump(data, f, indent=0)
|
|
34
|
+
os.replace(tmp, path)
|
|
35
|
+
|
|
36
|
+
def on_task_completed(self, scheduler: Scheduler) -> None:
|
|
37
|
+
"""Call after each task completion; writes checkpoint every interval_tasks."""
|
|
38
|
+
self._completions_since_write += 1
|
|
39
|
+
if self._completions_since_write >= self.interval_tasks:
|
|
40
|
+
self._completions_since_write = 0
|
|
41
|
+
self.write_now(scheduler)
|
|
42
|
+
|
|
43
|
+
def write_now(self, scheduler: Scheduler) -> None:
|
|
44
|
+
"""Write checkpoint once."""
|
|
45
|
+
run_id = getattr(scheduler, "run_id", "")
|
|
46
|
+
if not run_id:
|
|
47
|
+
return
|
|
48
|
+
path = self._path(run_id)
|
|
49
|
+
self._write_atomic(path, scheduler.snapshot())
|
|
50
|
+
|
|
51
|
+
def restore_latest(self, run_id: str) -> Scheduler | None:
|
|
52
|
+
"""Load checkpoint file; return restored Scheduler or None if not found."""
|
|
53
|
+
path = self._path(run_id)
|
|
54
|
+
if not os.path.isfile(path):
|
|
55
|
+
return None
|
|
56
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
57
|
+
data = json.load(f)
|
|
58
|
+
return Scheduler.restore(data)
|
|
59
|
+
|
|
60
|
+
def restore_or_raise(self, run_id: str) -> Scheduler:
|
|
61
|
+
"""Restore scheduler or raise CheckpointNotFoundError."""
|
|
62
|
+
s = self.restore_latest(run_id)
|
|
63
|
+
if s is None:
|
|
64
|
+
raise CheckpointNotFoundError(f"No checkpoint found for run_id: {run_id!r}")
|
|
65
|
+
return s
|