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/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
devsper: distributed AI swarm runtime.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
from devsper import Swarm
|
|
6
|
+
|
|
7
|
+
swarm = Swarm(config="devsper.toml")
|
|
8
|
+
result = swarm.run("analyze diffusion models")
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from devsper.config import get_config
|
|
12
|
+
from devsper.swarm.swarm import Swarm
|
|
13
|
+
|
|
14
|
+
__all__ = ["Swarm", "get_config"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A (Agent-to-Agent) integration: client, server, tool adapter.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from devsper.agents.a2a.types import (
|
|
6
|
+
AgentCard,
|
|
7
|
+
AgentSkill,
|
|
8
|
+
A2ATaskRequest,
|
|
9
|
+
A2ATaskResponse,
|
|
10
|
+
)
|
|
11
|
+
from devsper.agents.a2a.client import A2AClient
|
|
12
|
+
from devsper.agents.a2a.server import create_a2a_app, run_a2a_server
|
|
13
|
+
from devsper.agents.a2a.tool_adapter import A2AAgentTool
|
|
14
|
+
|
|
15
|
+
# Alias for task spec: "A2AServer" = run_a2a_server / create_a2a_app
|
|
16
|
+
A2AServer = create_a2a_app
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"A2AClient",
|
|
20
|
+
"A2AServer",
|
|
21
|
+
"AgentCard",
|
|
22
|
+
"A2ATaskRequest",
|
|
23
|
+
"A2ATaskResponse",
|
|
24
|
+
"A2AAgentTool",
|
|
25
|
+
"create_a2a_app",
|
|
26
|
+
"run_a2a_server",
|
|
27
|
+
]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A client: call external A2A-compliant agents (get AgentCard, send_task, stream_task).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from typing import AsyncIterator
|
|
7
|
+
|
|
8
|
+
from devsper.agents.a2a.types import (
|
|
9
|
+
AgentCard,
|
|
10
|
+
AgentSkill,
|
|
11
|
+
A2ATaskRequest,
|
|
12
|
+
A2ATaskResponse,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _parse_agent_card(data: dict) -> AgentCard:
|
|
17
|
+
"""Build AgentCard from JSON response."""
|
|
18
|
+
skills_data = data.get("skills") or []
|
|
19
|
+
skills = [
|
|
20
|
+
AgentSkill(
|
|
21
|
+
id=s.get("id", ""),
|
|
22
|
+
name=s.get("name", ""),
|
|
23
|
+
description=s.get("description", ""),
|
|
24
|
+
input_modes=list(s.get("inputModes") or []),
|
|
25
|
+
output_modes=list(s.get("outputModes") or []),
|
|
26
|
+
)
|
|
27
|
+
for s in skills_data
|
|
28
|
+
if isinstance(s, dict)
|
|
29
|
+
]
|
|
30
|
+
return AgentCard(
|
|
31
|
+
name=data.get("name", ""),
|
|
32
|
+
description=data.get("description", ""),
|
|
33
|
+
url=data.get("url", ""),
|
|
34
|
+
version=data.get("version", ""),
|
|
35
|
+
capabilities=list(data.get("capabilities") or []),
|
|
36
|
+
skills=skills,
|
|
37
|
+
authentication=data.get("authentication"),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _parse_task_response(data: dict) -> A2ATaskResponse:
|
|
42
|
+
"""Build A2ATaskResponse from JSON."""
|
|
43
|
+
status = data.get("status", "failed")
|
|
44
|
+
if status not in ("submitted", "working", "completed", "failed", "canceled"):
|
|
45
|
+
status = "failed"
|
|
46
|
+
return A2ATaskResponse(
|
|
47
|
+
id=data.get("id", ""),
|
|
48
|
+
status=status,
|
|
49
|
+
result=data.get("result"),
|
|
50
|
+
artifacts=data.get("artifacts") or [],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class A2AClient:
|
|
55
|
+
"""Calls external A2A-compliant agents as if they were local tools."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, timeout_seconds: float = 60.0) -> None:
|
|
58
|
+
self.timeout_seconds = timeout_seconds
|
|
59
|
+
|
|
60
|
+
def _client(self):
|
|
61
|
+
import httpx
|
|
62
|
+
return httpx.AsyncClient(timeout=self.timeout_seconds)
|
|
63
|
+
|
|
64
|
+
async def get_agent_card(self, base_url: str) -> AgentCard:
|
|
65
|
+
"""GET {base_url}/.well-known/agent.json"""
|
|
66
|
+
url = base_url.rstrip("/") + "/.well-known/agent.json"
|
|
67
|
+
async with self._client() as client:
|
|
68
|
+
r = await client.get(url)
|
|
69
|
+
r.raise_for_status()
|
|
70
|
+
data = r.json()
|
|
71
|
+
return _parse_agent_card(data)
|
|
72
|
+
|
|
73
|
+
async def send_task(
|
|
74
|
+
self,
|
|
75
|
+
base_url: str,
|
|
76
|
+
request: A2ATaskRequest,
|
|
77
|
+
poll_interval: float = 0.5,
|
|
78
|
+
) -> A2ATaskResponse:
|
|
79
|
+
"""POST {base_url}/tasks/send, then poll GET /tasks/{id} until completed/failed."""
|
|
80
|
+
import asyncio
|
|
81
|
+
url = base_url.rstrip("/") + "/tasks/send"
|
|
82
|
+
body = {
|
|
83
|
+
"id": request.id,
|
|
84
|
+
"message": request.message,
|
|
85
|
+
"sessionId": request.session_id,
|
|
86
|
+
}
|
|
87
|
+
async with self._client() as client:
|
|
88
|
+
r = await client.post(url, json=body)
|
|
89
|
+
r.raise_for_status()
|
|
90
|
+
data = r.json()
|
|
91
|
+
resp = _parse_task_response(data)
|
|
92
|
+
if resp.status in ("completed", "failed", "canceled"):
|
|
93
|
+
return resp
|
|
94
|
+
task_url = base_url.rstrip("/") + f"/tasks/{resp.id}"
|
|
95
|
+
while resp.status in ("submitted", "working"):
|
|
96
|
+
await asyncio.sleep(poll_interval)
|
|
97
|
+
async with self._client() as client:
|
|
98
|
+
r = await client.get(task_url)
|
|
99
|
+
r.raise_for_status()
|
|
100
|
+
data = r.json()
|
|
101
|
+
resp = _parse_task_response(data)
|
|
102
|
+
if resp.status in ("completed", "failed", "canceled"):
|
|
103
|
+
return resp
|
|
104
|
+
return resp
|
|
105
|
+
|
|
106
|
+
async def stream_task(
|
|
107
|
+
self,
|
|
108
|
+
base_url: str,
|
|
109
|
+
request: A2ATaskRequest,
|
|
110
|
+
) -> AsyncIterator[str]:
|
|
111
|
+
"""POST {base_url}/tasks/sendSubscribe, consume SSE stream, yield text chunks."""
|
|
112
|
+
import httpx
|
|
113
|
+
url = base_url.rstrip("/") + "/tasks/sendSubscribe"
|
|
114
|
+
body = {
|
|
115
|
+
"id": request.id,
|
|
116
|
+
"message": request.message,
|
|
117
|
+
"sessionId": request.session_id,
|
|
118
|
+
}
|
|
119
|
+
async with httpx.AsyncClient(timeout=self.timeout_seconds) as client:
|
|
120
|
+
async with client.stream("POST", url, json=body) as response:
|
|
121
|
+
response.raise_for_status()
|
|
122
|
+
async for line in response.aiter_lines():
|
|
123
|
+
if line.startswith("data:"):
|
|
124
|
+
data = line[5:].strip()
|
|
125
|
+
if data and data != "[DONE]":
|
|
126
|
+
yield data
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A discovery: fetch AgentCard from URL, register skills as A2AAgentTool.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
from devsper.config.schema import A2AAgentConfig
|
|
8
|
+
from devsper.agents.a2a.client import A2AClient
|
|
9
|
+
from devsper.agents.a2a.tool_adapter import A2AAgentTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def discover_a2a_tools_async(agent_config: A2AAgentConfig) -> int:
|
|
13
|
+
"""Fetch AgentCard from agent URL, create A2AAgentTool per skill, register. Return count."""
|
|
14
|
+
client = A2AClient()
|
|
15
|
+
card = await client.get_agent_card(agent_config.url)
|
|
16
|
+
name = agent_config.name or card.name or "a2a-agent"
|
|
17
|
+
for skill in card.skills:
|
|
18
|
+
A2AAgentTool(agent_name=name, skill=skill, base_url=agent_config.url, client=client)
|
|
19
|
+
return len(card.skills)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def register_a2a_agent(agent_config: A2AAgentConfig) -> int:
|
|
23
|
+
"""Sync: discover and register A2A agent tools; return count."""
|
|
24
|
+
return asyncio.run(discover_a2a_tools_async(agent_config))
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A server: expose devsper agents as A2A-compliant endpoints (FastAPI).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from devsper.agents.a2a.types import AgentCard, AgentSkill, A2ATaskRequest, A2ATaskResponse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _build_agent_card(host: str, port: int, swarm_name: str) -> dict:
|
|
11
|
+
"""Build AgentCard JSON for GET /.well-known/agent.json."""
|
|
12
|
+
from devsper.intelligence.strategy_selector import ExecutionStrategy
|
|
13
|
+
url = f"http://{host}:{port}"
|
|
14
|
+
name = swarm_name or "devsper"
|
|
15
|
+
skills = [
|
|
16
|
+
{
|
|
17
|
+
"id": s.value,
|
|
18
|
+
"name": s.value.replace("_", " ").title(),
|
|
19
|
+
"description": f"Execute task using {s.value} strategy.",
|
|
20
|
+
"inputModes": ["text"],
|
|
21
|
+
"outputModes": ["text"],
|
|
22
|
+
}
|
|
23
|
+
for s in ExecutionStrategy
|
|
24
|
+
if s != ExecutionStrategy.GENERAL
|
|
25
|
+
]
|
|
26
|
+
return {
|
|
27
|
+
"name": name,
|
|
28
|
+
"description": "devsper swarm orchestration as A2A agent.",
|
|
29
|
+
"url": url,
|
|
30
|
+
"version": "1.10.5",
|
|
31
|
+
"capabilities": ["streaming"],
|
|
32
|
+
"skills": skills,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def create_a2a_app(host: str = "localhost", port: int = 8080, swarm_name: str = "") -> Any:
|
|
37
|
+
"""Create FastAPI app with A2A routes. Requires fastapi, uvicorn, sse-starlette."""
|
|
38
|
+
from fastapi import FastAPI
|
|
39
|
+
from fastapi.responses import StreamingResponse
|
|
40
|
+
import asyncio
|
|
41
|
+
import uuid
|
|
42
|
+
|
|
43
|
+
app = FastAPI(title="devsper A2A Server")
|
|
44
|
+
|
|
45
|
+
@app.get("/.well-known/agent.json")
|
|
46
|
+
def agent_card() -> dict:
|
|
47
|
+
return _build_agent_card(host, port, swarm_name)
|
|
48
|
+
|
|
49
|
+
@app.post("/tasks/send")
|
|
50
|
+
async def tasks_send(body: dict) -> dict:
|
|
51
|
+
"""Accept A2A task, run via Swarm, return result."""
|
|
52
|
+
task_id = body.get("id") or str(uuid.uuid4())
|
|
53
|
+
message = body.get("message") or {}
|
|
54
|
+
text = message.get("text", "") if isinstance(message, dict) else ""
|
|
55
|
+
if not text:
|
|
56
|
+
return {
|
|
57
|
+
"id": task_id,
|
|
58
|
+
"status": "failed",
|
|
59
|
+
"result": "Missing message.text",
|
|
60
|
+
"artifacts": [],
|
|
61
|
+
}
|
|
62
|
+
try:
|
|
63
|
+
from devsper.config import get_config
|
|
64
|
+
from devsper.swarm.swarm import Swarm
|
|
65
|
+
cfg = get_config()
|
|
66
|
+
swarm = Swarm(config=cfg)
|
|
67
|
+
result = swarm.run(text)
|
|
68
|
+
out = "\n".join(f"{k}: {v[:2000]}" for k, v in result.items()) if result else ""
|
|
69
|
+
return {
|
|
70
|
+
"id": task_id,
|
|
71
|
+
"status": "completed",
|
|
72
|
+
"result": out,
|
|
73
|
+
"artifacts": [],
|
|
74
|
+
}
|
|
75
|
+
except Exception as e:
|
|
76
|
+
return {
|
|
77
|
+
"id": task_id,
|
|
78
|
+
"status": "failed",
|
|
79
|
+
"result": str(e),
|
|
80
|
+
"artifacts": [],
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@app.post("/tasks/sendSubscribe")
|
|
84
|
+
async def tasks_send_subscribe(body: dict):
|
|
85
|
+
"""SSE streaming task execution."""
|
|
86
|
+
task_id = body.get("id") or str(uuid.uuid4())
|
|
87
|
+
message = body.get("message") or {}
|
|
88
|
+
text = message.get("text", "") if isinstance(message, dict) else ""
|
|
89
|
+
|
|
90
|
+
async def stream():
|
|
91
|
+
if not text:
|
|
92
|
+
yield f"data: {__import__('json').dumps({'error': 'Missing message.text'})}\n\n"
|
|
93
|
+
return
|
|
94
|
+
try:
|
|
95
|
+
from devsper.config import get_config
|
|
96
|
+
from devsper.swarm.swarm import Swarm
|
|
97
|
+
cfg = get_config()
|
|
98
|
+
swarm = Swarm(config=cfg)
|
|
99
|
+
result = swarm.run(text)
|
|
100
|
+
out = "\n".join(f"{k}: {v[:2000]}" for k, v in result.items()) if result else ""
|
|
101
|
+
yield f"data: {__import__('json').dumps({'id': task_id, 'status': 'completed', 'result': out})}\n\n"
|
|
102
|
+
except Exception as e:
|
|
103
|
+
yield f"data: {__import__('json').dumps({'id': task_id, 'status': 'failed', 'result': str(e)})}\n\n"
|
|
104
|
+
|
|
105
|
+
return StreamingResponse(
|
|
106
|
+
stream(),
|
|
107
|
+
media_type="text/event-stream",
|
|
108
|
+
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@app.get("/tasks/{task_id}")
|
|
112
|
+
def task_status(task_id: str) -> dict:
|
|
113
|
+
"""Task status (stub: we don't persist task state for now)."""
|
|
114
|
+
return {"id": task_id, "status": "completed", "result": None, "artifacts": []}
|
|
115
|
+
|
|
116
|
+
@app.post("/tasks/{task_id}/cancel")
|
|
117
|
+
def task_cancel(task_id: str) -> dict:
|
|
118
|
+
"""Cancel running task (stub)."""
|
|
119
|
+
return {"id": task_id, "status": "canceled", "result": None, "artifacts": []}
|
|
120
|
+
|
|
121
|
+
return app
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def run_a2a_server(host: str = "localhost", port: int = 8080, swarm_name: str = "") -> None:
|
|
125
|
+
"""Run A2A server with uvicorn."""
|
|
126
|
+
import uvicorn
|
|
127
|
+
app = create_a2a_app(host=host, port=port, swarm_name=swarm_name)
|
|
128
|
+
uvicorn.run(app, host=host, port=port)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2AAgentTool: wrap an external A2A agent skill as a devsper Tool.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
8
|
+
from devsper.tools.base import Tool
|
|
9
|
+
from devsper.tools.registry import register
|
|
10
|
+
|
|
11
|
+
from devsper.agents.a2a.types import AgentCard, AgentSkill, A2ATaskRequest, A2ATaskResponse
|
|
12
|
+
from devsper.agents.a2a.client import A2AClient
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class A2AAgentTool(Tool):
|
|
16
|
+
"""
|
|
17
|
+
Wraps an external A2A agent skill as a devsper tool.
|
|
18
|
+
Discovery: fetch AgentCard, create one tool per skill.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
agent_name: str,
|
|
24
|
+
skill: AgentSkill,
|
|
25
|
+
base_url: str,
|
|
26
|
+
client: A2AClient | None = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
self.name = f"{agent_name}.{skill.id}"
|
|
29
|
+
self.description = skill.description or f"A2A skill: {skill.name}"
|
|
30
|
+
self.category = "a2a"
|
|
31
|
+
self.input_schema = {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {"task": {"type": "string", "description": "Task or prompt for the agent"}},
|
|
34
|
+
"required": ["task"],
|
|
35
|
+
}
|
|
36
|
+
self._base_url = base_url.rstrip("/")
|
|
37
|
+
self._client = client or A2AClient()
|
|
38
|
+
self._skill_id = skill.id
|
|
39
|
+
register(self)
|
|
40
|
+
|
|
41
|
+
def run(self, task: str = "", **kwargs) -> str:
|
|
42
|
+
"""Send task to external A2A agent via send_task."""
|
|
43
|
+
text = task or kwargs.get("task", "")
|
|
44
|
+
if not text:
|
|
45
|
+
return "Error: missing 'task' argument"
|
|
46
|
+
request = A2ATaskRequest(
|
|
47
|
+
id=str(uuid.uuid4()),
|
|
48
|
+
message={"text": text},
|
|
49
|
+
session_id=None,
|
|
50
|
+
)
|
|
51
|
+
try:
|
|
52
|
+
loop = asyncio.get_event_loop()
|
|
53
|
+
except RuntimeError:
|
|
54
|
+
loop = asyncio.new_event_loop()
|
|
55
|
+
asyncio.set_event_loop(loop)
|
|
56
|
+
if loop.is_running():
|
|
57
|
+
import concurrent.futures
|
|
58
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
59
|
+
future = pool.submit(
|
|
60
|
+
asyncio.run,
|
|
61
|
+
self._client.send_task(self._base_url, request),
|
|
62
|
+
)
|
|
63
|
+
resp = future.result()
|
|
64
|
+
else:
|
|
65
|
+
resp = loop.run_until_complete(self._client.send_task(self._base_url, request))
|
|
66
|
+
if resp.status == "completed" and resp.result is not None:
|
|
67
|
+
return resp.result
|
|
68
|
+
return f"Error: status={resp.status}, result={resp.result}"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A (Agent-to-Agent) protocol types — AgentCard, TaskRequest/Response, etc.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class AgentSkill:
|
|
11
|
+
"""One skill exposed by an A2A agent."""
|
|
12
|
+
id: str
|
|
13
|
+
name: str
|
|
14
|
+
description: str
|
|
15
|
+
input_modes: list[str] # e.g. ["text", "file"]
|
|
16
|
+
output_modes: list[str]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class AgentCard:
|
|
21
|
+
"""A2A agent descriptor — follows A2A spec."""
|
|
22
|
+
name: str
|
|
23
|
+
description: str
|
|
24
|
+
url: str # this agent's A2A endpoint
|
|
25
|
+
version: str
|
|
26
|
+
capabilities: list[str] # e.g. ["streaming", "pushNotifications"]
|
|
27
|
+
skills: list[AgentSkill]
|
|
28
|
+
authentication: dict | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class A2ATaskRequest:
|
|
33
|
+
"""A2A task send request."""
|
|
34
|
+
id: str # uuid4
|
|
35
|
+
message: dict # A2A Message object
|
|
36
|
+
session_id: str | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class A2ATaskResponse:
|
|
41
|
+
"""A2A task response."""
|
|
42
|
+
id: str
|
|
43
|
+
status: Literal["submitted", "working", "completed", "failed", "canceled"]
|
|
44
|
+
result: str | None = None
|
|
45
|
+
artifacts: list[dict] | None = None
|
|
46
|
+
|
|
47
|
+
def __post_init__(self) -> None:
|
|
48
|
+
if self.artifacts is None:
|
|
49
|
+
self.artifacts = []
|