open-swarm 0.1.1745275181__py3-none-any.whl → 0.1.1748636295__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.
- open_swarm-0.1.1748636295.dist-info/METADATA +257 -0
- open_swarm-0.1.1748636295.dist-info/RECORD +89 -0
- {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/WHEEL +2 -1
- open_swarm-0.1.1748636295.dist-info/entry_points.txt +3 -0
- open_swarm-0.1.1748636295.dist-info/top_level.txt +1 -0
- swarm/__init__.py +2 -0
- swarm/agent/agent.py +49 -0
- swarm/auth.py +48 -113
- swarm/consumers.py +0 -19
- swarm/core.py +411 -0
- swarm/extensions/blueprint/__init__.py +16 -30
- swarm/extensions/blueprint/agent_utils.py +45 -0
- swarm/extensions/blueprint/blueprint_base.py +562 -0
- swarm/extensions/blueprint/blueprint_discovery.py +112 -0
- swarm/extensions/blueprint/django_utils.py +79 -181
- swarm/extensions/blueprint/interactive_mode.py +72 -67
- swarm/extensions/blueprint/output_utils.py +82 -0
- swarm/{core → extensions/blueprint}/spinner.py +21 -30
- swarm/extensions/cli/cli_args.py +0 -6
- swarm/extensions/cli/commands/blueprint_management.py +9 -47
- swarm/extensions/cli/commands/config_management.py +6 -5
- swarm/extensions/cli/commands/edit_config.py +7 -16
- swarm/extensions/cli/commands/list_blueprints.py +1 -1
- swarm/extensions/cli/commands/validate_env.py +4 -11
- swarm/extensions/cli/commands/validate_envvars.py +6 -6
- swarm/extensions/cli/interactive_shell.py +2 -16
- swarm/extensions/config/config_loader.py +345 -107
- swarm/{core → extensions/config}/config_manager.py +38 -50
- swarm/{core → extensions/config}/server_config.py +0 -32
- swarm/extensions/launchers/build_launchers.py +14 -0
- swarm/{core → extensions/launchers}/build_swarm_wrapper.py +0 -0
- swarm/extensions/launchers/swarm_api.py +64 -8
- swarm/extensions/launchers/swarm_cli.py +300 -8
- swarm/extensions/mcp/__init__.py +1 -0
- swarm/extensions/mcp/cache_utils.py +32 -0
- swarm/extensions/mcp/mcp_client.py +233 -0
- swarm/extensions/mcp/mcp_tool_provider.py +135 -0
- swarm/extensions/mcp/mcp_utils.py +260 -0
- swarm/llm/chat_completion.py +166 -0
- swarm/serializers.py +5 -96
- swarm/settings.py +133 -85
- swarm/types.py +91 -0
- swarm/urls.py +74 -57
- swarm/utils/context_utils.py +4 -10
- swarm/utils/general_utils.py +0 -21
- swarm/utils/redact.py +36 -23
- swarm/views/api_views.py +39 -48
- swarm/views/chat_views.py +76 -236
- swarm/views/core_views.py +87 -80
- swarm/views/model_views.py +121 -64
- swarm/views/utils.py +439 -65
- swarm/views/web_views.py +2 -2
- open_swarm-0.1.1745275181.dist-info/METADATA +0 -874
- open_swarm-0.1.1745275181.dist-info/RECORD +0 -319
- open_swarm-0.1.1745275181.dist-info/entry_points.txt +0 -4
- swarm/blueprints/README.md +0 -68
- swarm/blueprints/blueprint_audit_status.json +0 -27
- swarm/blueprints/chatbot/README.md +0 -40
- swarm/blueprints/chatbot/blueprint_chatbot.py +0 -471
- swarm/blueprints/chatbot/metadata.json +0 -23
- swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -33
- swarm/blueprints/chucks_angels/README.md +0 -11
- swarm/blueprints/chucks_angels/blueprint_chucks_angels.py +0 -7
- swarm/blueprints/chucks_angels/test_basic.py +0 -3
- swarm/blueprints/codey/CODEY.md +0 -15
- swarm/blueprints/codey/README.md +0 -115
- swarm/blueprints/codey/blueprint_codey.py +0 -1072
- swarm/blueprints/codey/codey_cli.py +0 -373
- swarm/blueprints/codey/instructions.md +0 -17
- swarm/blueprints/codey/metadata.json +0 -23
- swarm/blueprints/common/operation_box_utils.py +0 -83
- swarm/blueprints/digitalbutlers/README.md +0 -11
- swarm/blueprints/digitalbutlers/__init__.py +0 -1
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -7
- swarm/blueprints/digitalbutlers/test_basic.py +0 -3
- swarm/blueprints/divine_code/README.md +0 -3
- swarm/blueprints/divine_code/__init__.py +0 -10
- swarm/blueprints/divine_code/apps.py +0 -11
- swarm/blueprints/divine_code/blueprint_divine_code.py +0 -270
- swarm/blueprints/django_chat/apps.py +0 -6
- swarm/blueprints/django_chat/blueprint_django_chat.py +0 -268
- swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -37
- swarm/blueprints/django_chat/urls.py +0 -8
- swarm/blueprints/django_chat/views.py +0 -32
- swarm/blueprints/echocraft/blueprint_echocraft.py +0 -384
- swarm/blueprints/flock/README.md +0 -11
- swarm/blueprints/flock/__init__.py +0 -8
- swarm/blueprints/flock/blueprint_flock.py +0 -7
- swarm/blueprints/flock/test_basic.py +0 -3
- swarm/blueprints/geese/README.md +0 -10
- swarm/blueprints/geese/__init__.py +0 -8
- swarm/blueprints/geese/blueprint_geese.py +0 -384
- swarm/blueprints/geese/geese_cli.py +0 -102
- swarm/blueprints/jeeves/README.md +0 -41
- swarm/blueprints/jeeves/blueprint_jeeves.py +0 -722
- swarm/blueprints/jeeves/jeeves_cli.py +0 -55
- swarm/blueprints/jeeves/metadata.json +0 -24
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -473
- swarm/blueprints/messenger/templates/messenger/messenger.html +0 -46
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -423
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -340
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -265
- swarm/blueprints/omniplex/blueprint_omniplex.py +0 -298
- swarm/blueprints/poets/blueprint_poets.py +0 -546
- swarm/blueprints/poets/poets_cli.py +0 -23
- swarm/blueprints/rue_code/README.md +0 -8
- swarm/blueprints/rue_code/blueprint_rue_code.py +0 -448
- swarm/blueprints/rue_code/rue_code_cli.py +0 -43
- swarm/blueprints/stewie/apps.py +0 -12
- swarm/blueprints/stewie/blueprint_family_ties.py +0 -349
- swarm/blueprints/stewie/models.py +0 -19
- swarm/blueprints/stewie/serializers.py +0 -10
- swarm/blueprints/stewie/settings.py +0 -17
- swarm/blueprints/stewie/urls.py +0 -11
- swarm/blueprints/stewie/views.py +0 -26
- swarm/blueprints/suggestion/blueprint_suggestion.py +0 -222
- swarm/blueprints/whinge_surf/README.md +0 -22
- swarm/blueprints/whinge_surf/__init__.py +0 -1
- swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +0 -565
- swarm/blueprints/whinge_surf/whinge_surf_cli.py +0 -99
- swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
- swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -11
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -339
- swarm/blueprints/zeus/__init__.py +0 -2
- swarm/blueprints/zeus/apps.py +0 -4
- swarm/blueprints/zeus/blueprint_zeus.py +0 -270
- swarm/blueprints/zeus/zeus_cli.py +0 -13
- swarm/cli/async_input.py +0 -65
- swarm/cli/async_input_demo.py +0 -32
- swarm/core/agent_utils.py +0 -21
- swarm/core/blueprint_base.py +0 -769
- swarm/core/blueprint_discovery.py +0 -125
- swarm/core/blueprint_runner.py +0 -59
- swarm/core/blueprint_ux.py +0 -109
- swarm/core/build_launchers.py +0 -15
- swarm/core/cli/__init__.py +0 -1
- swarm/core/cli/commands/__init__.py +0 -1
- swarm/core/cli/commands/blueprint_management.py +0 -7
- swarm/core/cli/interactive_shell.py +0 -14
- swarm/core/cli/main.py +0 -50
- swarm/core/cli/utils/__init__.py +0 -1
- swarm/core/cli/utils/discover_commands.py +0 -18
- swarm/core/config_loader.py +0 -122
- swarm/core/output_utils.py +0 -193
- swarm/core/session_logger.py +0 -42
- swarm/core/slash_commands.py +0 -89
- swarm/core/swarm_api.py +0 -68
- swarm/core/swarm_cli.py +0 -216
- swarm/core/utils/__init__.py +0 -0
- swarm/extensions/blueprint/cli_handler.py +0 -197
- swarm/extensions/blueprint/runnable_blueprint.py +0 -42
- swarm/extensions/cli/utils/__init__.py +0 -1
- swarm/extensions/cli/utils/async_input.py +0 -46
- swarm/extensions/cli/utils/prompt_user.py +0 -3
- swarm/management/__init__.py +0 -0
- swarm/management/commands/__init__.py +0 -0
- swarm/management/commands/runserver.py +0 -58
- swarm/middleware.py +0 -65
- swarm/permissions.py +0 -38
- swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -7
- swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -7
- swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -7
- swarm/static/contrib/markedjs/marked.min.js +0 -6
- swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -27
- swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -21
- swarm/static/contrib/tabler-icons/archive.svg +0 -21
- swarm/static/contrib/tabler-icons/artboard.svg +0 -27
- swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -23
- swarm/static/contrib/tabler-icons/box-multiple.svg +0 -19
- swarm/static/contrib/tabler-icons/carambola.svg +0 -19
- swarm/static/contrib/tabler-icons/copy.svg +0 -20
- swarm/static/contrib/tabler-icons/download.svg +0 -21
- swarm/static/contrib/tabler-icons/edit.svg +0 -21
- swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -13
- swarm/static/contrib/tabler-icons/filled/paint.svg +0 -13
- swarm/static/contrib/tabler-icons/headset.svg +0 -22
- swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -21
- swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -21
- swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -21
- swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -21
- swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -22
- swarm/static/contrib/tabler-icons/message-star.svg +0 -22
- swarm/static/contrib/tabler-icons/message-x.svg +0 -23
- swarm/static/contrib/tabler-icons/message.svg +0 -21
- swarm/static/contrib/tabler-icons/paperclip.svg +0 -18
- swarm/static/contrib/tabler-icons/playlist-add.svg +0 -22
- swarm/static/contrib/tabler-icons/robot.svg +0 -26
- swarm/static/contrib/tabler-icons/search.svg +0 -19
- swarm/static/contrib/tabler-icons/settings.svg +0 -20
- swarm/static/contrib/tabler-icons/thumb-down.svg +0 -19
- swarm/static/contrib/tabler-icons/thumb-up.svg +0 -19
- swarm/static/css/dropdown.css +0 -22
- swarm/static/htmx/htmx.min.js +0 -0
- swarm/static/js/dropdown.js +0 -23
- swarm/static/rest_mode/css/base.css +0 -470
- swarm/static/rest_mode/css/chat-history.css +0 -286
- swarm/static/rest_mode/css/chat.css +0 -251
- swarm/static/rest_mode/css/chatbot.css +0 -74
- swarm/static/rest_mode/css/chatgpt.css +0 -62
- swarm/static/rest_mode/css/colors/corporate.css +0 -74
- swarm/static/rest_mode/css/colors/pastel.css +0 -81
- swarm/static/rest_mode/css/colors/tropical.css +0 -82
- swarm/static/rest_mode/css/general.css +0 -142
- swarm/static/rest_mode/css/layout.css +0 -167
- swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -17
- swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -57
- swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -8
- swarm/static/rest_mode/css/messages.css +0 -84
- swarm/static/rest_mode/css/messenger.css +0 -135
- swarm/static/rest_mode/css/settings.css +0 -91
- swarm/static/rest_mode/css/simple.css +0 -44
- swarm/static/rest_mode/css/slack.css +0 -58
- swarm/static/rest_mode/css/style.css +0 -156
- swarm/static/rest_mode/css/theme.css +0 -30
- swarm/static/rest_mode/css/toast.css +0 -40
- swarm/static/rest_mode/js/auth.js +0 -9
- swarm/static/rest_mode/js/blueprint.js +0 -41
- swarm/static/rest_mode/js/blueprintUtils.js +0 -12
- swarm/static/rest_mode/js/chatLogic.js +0 -79
- swarm/static/rest_mode/js/debug.js +0 -63
- swarm/static/rest_mode/js/events.js +0 -98
- swarm/static/rest_mode/js/main.js +0 -19
- swarm/static/rest_mode/js/messages.js +0 -264
- swarm/static/rest_mode/js/messengerLogic.js +0 -355
- swarm/static/rest_mode/js/modules/apiService.js +0 -84
- swarm/static/rest_mode/js/modules/blueprintManager.js +0 -162
- swarm/static/rest_mode/js/modules/chatHistory.js +0 -110
- swarm/static/rest_mode/js/modules/debugLogger.js +0 -14
- swarm/static/rest_mode/js/modules/eventHandlers.js +0 -107
- swarm/static/rest_mode/js/modules/messageProcessor.js +0 -120
- swarm/static/rest_mode/js/modules/state.js +0 -7
- swarm/static/rest_mode/js/modules/userInteractions.js +0 -29
- swarm/static/rest_mode/js/modules/validation.js +0 -23
- swarm/static/rest_mode/js/rendering.js +0 -119
- swarm/static/rest_mode/js/settings.js +0 -130
- swarm/static/rest_mode/js/sidebar.js +0 -94
- swarm/static/rest_mode/js/simpleLogic.js +0 -37
- swarm/static/rest_mode/js/slackLogic.js +0 -66
- swarm/static/rest_mode/js/splash.js +0 -76
- swarm/static/rest_mode/js/theme.js +0 -111
- swarm/static/rest_mode/js/toast.js +0 -36
- swarm/static/rest_mode/js/ui.js +0 -265
- swarm/static/rest_mode/js/validation.js +0 -57
- swarm/static/rest_mode/svg/animated_spinner.svg +0 -12
- swarm/static/rest_mode/svg/arrow_down.svg +0 -5
- swarm/static/rest_mode/svg/arrow_left.svg +0 -5
- swarm/static/rest_mode/svg/arrow_right.svg +0 -5
- swarm/static/rest_mode/svg/arrow_up.svg +0 -5
- swarm/static/rest_mode/svg/attach.svg +0 -8
- swarm/static/rest_mode/svg/avatar.svg +0 -7
- swarm/static/rest_mode/svg/canvas.svg +0 -6
- swarm/static/rest_mode/svg/chat_history.svg +0 -4
- swarm/static/rest_mode/svg/close.svg +0 -5
- swarm/static/rest_mode/svg/copy.svg +0 -4
- swarm/static/rest_mode/svg/dark_mode.svg +0 -3
- swarm/static/rest_mode/svg/edit.svg +0 -5
- swarm/static/rest_mode/svg/layout.svg +0 -9
- swarm/static/rest_mode/svg/logo.svg +0 -29
- swarm/static/rest_mode/svg/logout.svg +0 -5
- swarm/static/rest_mode/svg/mobile.svg +0 -5
- swarm/static/rest_mode/svg/new_chat.svg +0 -4
- swarm/static/rest_mode/svg/not_visible.svg +0 -5
- swarm/static/rest_mode/svg/plus.svg +0 -7
- swarm/static/rest_mode/svg/run_code.svg +0 -6
- swarm/static/rest_mode/svg/save.svg +0 -4
- swarm/static/rest_mode/svg/search.svg +0 -6
- swarm/static/rest_mode/svg/settings.svg +0 -4
- swarm/static/rest_mode/svg/speaker.svg +0 -5
- swarm/static/rest_mode/svg/stop.svg +0 -6
- swarm/static/rest_mode/svg/thumbs_down.svg +0 -3
- swarm/static/rest_mode/svg/thumbs_up.svg +0 -3
- swarm/static/rest_mode/svg/toggle_off.svg +0 -6
- swarm/static/rest_mode/svg/toggle_on.svg +0 -6
- swarm/static/rest_mode/svg/trash.svg +0 -10
- swarm/static/rest_mode/svg/undo.svg +0 -3
- swarm/static/rest_mode/svg/visible.svg +0 -8
- swarm/static/rest_mode/svg/voice.svg +0 -10
- swarm/templates/account/login.html +0 -22
- swarm/templates/account/signup.html +0 -32
- swarm/templates/base.html +0 -30
- swarm/templates/chat.html +0 -43
- swarm/templates/index.html +0 -35
- swarm/templates/rest_mode/components/chat_sidebar.html +0 -55
- swarm/templates/rest_mode/components/header.html +0 -45
- swarm/templates/rest_mode/components/main_chat_pane.html +0 -41
- swarm/templates/rest_mode/components/settings_dialog.html +0 -97
- swarm/templates/rest_mode/components/splash_screen.html +0 -7
- swarm/templates/rest_mode/components/top_bar.html +0 -28
- swarm/templates/rest_mode/message_ui.html +0 -50
- swarm/templates/rest_mode/slackbot.html +0 -30
- swarm/templates/simple_blueprint_page.html +0 -24
- swarm/templates/websocket_partials/final_system_message.html +0 -3
- swarm/templates/websocket_partials/system_message.html +0 -4
- swarm/templates/websocket_partials/user_message.html +0 -5
- swarm/utils/ansi_box.py +0 -34
- swarm/utils/disable_tracing.py +0 -38
- swarm/utils/log_utils.py +0 -63
- swarm/utils/openai_patch.py +0 -33
- swarm/ux/ansi_box.py +0 -43
- swarm/ux/spinner.py +0 -53
- {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/licenses/LICENSE +0 -0
- /swarm/{core → extensions/blueprint}/blueprint_utils.py +0 -0
- /swarm/{core → extensions/blueprint}/common_utils.py +0 -0
- /swarm/{core → extensions/config}/setup_wizard.py +0 -0
- /swarm/{blueprints/rue_code → extensions/config/utils}/__init__.py +0 -0
- /swarm/{core → extensions/config}/utils/logger.py +0 -0
- /swarm/{core → extensions/launchers}/swarm_wrapper.py +0 -0
swarm/core.py
ADDED
@@ -0,0 +1,411 @@
|
|
1
|
+
"""
|
2
|
+
Swarm Core Module
|
3
|
+
|
4
|
+
This module defines the Swarm class, which orchestrates the Swarm framework by managing agents
|
5
|
+
and coordinating interactions with LLM endpoints and MCP servers. Modularized components live
|
6
|
+
in separate files for clarity.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import copy
|
11
|
+
import json
|
12
|
+
import logging
|
13
|
+
import uuid
|
14
|
+
from typing import List, Optional, Dict, Any
|
15
|
+
from types import SimpleNamespace # Needed for stream processing
|
16
|
+
|
17
|
+
import asyncio
|
18
|
+
from openai import AsyncOpenAI
|
19
|
+
|
20
|
+
# Internal imports for modular components
|
21
|
+
from .util import merge_chunk
|
22
|
+
from .types import Agent, Response, ChatCompletionMessageToolCall # Ensure necessary types are imported
|
23
|
+
from .extensions.config.config_loader import load_llm_config
|
24
|
+
# Use mcp_utils from the extensions directory
|
25
|
+
from .extensions.mcp.mcp_utils import discover_and_merge_agent_tools, discover_and_merge_agent_resources
|
26
|
+
from .settings import DEBUG
|
27
|
+
from .utils.redact import redact_sensitive_data
|
28
|
+
# Import chat completion logic
|
29
|
+
from .llm.chat_completion import get_chat_completion, get_chat_completion_message
|
30
|
+
# Import message and tool execution logic
|
31
|
+
from .messages import ChatMessage
|
32
|
+
from .tool_executor import handle_tool_calls
|
33
|
+
|
34
|
+
# Configure module-level logging
|
35
|
+
logger = logging.getLogger(__name__)
|
36
|
+
# Set level based on DEBUG setting or default to INFO
|
37
|
+
logger.setLevel(logging.DEBUG if DEBUG else logging.INFO)
|
38
|
+
# Ensure handler is added only once
|
39
|
+
if not logger.handlers:
|
40
|
+
stream_handler = logging.StreamHandler()
|
41
|
+
formatter = logging.Formatter("[%(levelname)s] %(asctime)s - %(name)s:%(lineno)d - %(message)s")
|
42
|
+
stream_handler.setFormatter(formatter)
|
43
|
+
logger.addHandler(stream_handler)
|
44
|
+
|
45
|
+
# Constants
|
46
|
+
__CTX_VARS_NAME__ = "context_variables" # Standard name for context injection
|
47
|
+
GLOBAL_DEFAULT_MAX_CONTEXT_TOKENS = int(os.getenv("SWARM_MAX_CONTEXT_TOKENS", 8000)) # Default from env
|
48
|
+
|
49
|
+
_discovery_locks: Dict[str, asyncio.Lock] = {} # Lock for async discovery
|
50
|
+
|
51
|
+
|
52
|
+
class Swarm:
|
53
|
+
"""
|
54
|
+
Core class managing agent interactions within the Swarm framework.
|
55
|
+
|
56
|
+
Attributes:
|
57
|
+
model (str): Default LLM model identifier.
|
58
|
+
temperature (float): Sampling temperature for LLM responses.
|
59
|
+
tool_choice (str): Strategy for selecting tools (e.g., "auto").
|
60
|
+
parallel_tool_calls (bool): Whether to execute tool calls in parallel.
|
61
|
+
agents (Dict[str, Agent]): Registered agents by name.
|
62
|
+
config (dict): Configuration for LLMs and MCP servers.
|
63
|
+
debug (bool): Enable detailed logging if True.
|
64
|
+
client (AsyncOpenAI): Client for OpenAI-compatible APIs.
|
65
|
+
current_llm_config (dict): Loaded config for the current default LLM.
|
66
|
+
max_context_messages (int): Max messages to keep in history.
|
67
|
+
max_context_tokens (int): Max tokens allowed in history.
|
68
|
+
"""
|
69
|
+
|
70
|
+
def __init__(self, client: Optional[AsyncOpenAI] = None, config: Optional[Dict] = None, debug: bool = False):
|
71
|
+
"""
|
72
|
+
Initialize the Swarm instance.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
client: Optional pre-initialized AsyncOpenAI client.
|
76
|
+
config: Configuration dictionary for LLMs and MCP servers.
|
77
|
+
debug: Enable detailed logging if True.
|
78
|
+
"""
|
79
|
+
self.model = os.getenv("DEFAULT_LLM", "default") # Default LLM profile name
|
80
|
+
self.temperature = 0.7 # Default temperature
|
81
|
+
self.tool_choice = "auto" # Default tool choice strategy
|
82
|
+
self.parallel_tool_calls = False # Default parallel tool call setting
|
83
|
+
self.agents: Dict[str, Agent] = {} # Dictionary to store registered agents
|
84
|
+
self.config = config or {} # Store provided or empty config
|
85
|
+
self.debug = debug or DEBUG # Use provided debug flag or global setting
|
86
|
+
|
87
|
+
# Context limits
|
88
|
+
self.max_context_messages = 50 # Default max messages
|
89
|
+
self.max_context_tokens = max(1, GLOBAL_DEFAULT_MAX_CONTEXT_TOKENS) # Ensure positive token limit
|
90
|
+
# Derived limits (optional, consider moving logic to truncation function)
|
91
|
+
# self.summarize_threshold_tokens = int(self.max_context_tokens * 0.75)
|
92
|
+
# self.keep_recent_tokens = int(self.max_context_tokens * 0.25)
|
93
|
+
logger.debug(f"Context limits set: max_messages={self.max_context_messages}, max_tokens={self.max_context_tokens}")
|
94
|
+
|
95
|
+
# Load LLM configuration for the default model
|
96
|
+
try:
|
97
|
+
self.current_llm_config = load_llm_config(self.config, self.model)
|
98
|
+
# Override API key from environment if using 'default' profile and key exists
|
99
|
+
if self.model == "default" and os.getenv("OPENAI_API_KEY"):
|
100
|
+
self.current_llm_config["api_key"] = os.getenv("OPENAI_API_KEY")
|
101
|
+
logger.debug(f"Overriding API key for model '{self.model}' from OPENAI_API_KEY env var.")
|
102
|
+
except ValueError as e:
|
103
|
+
logger.warning(f"LLM config for '{self.model}' not found: {e}. Falling back to loading 'default' profile.")
|
104
|
+
# Attempt to load the 'default' profile explicitly as fallback
|
105
|
+
try:
|
106
|
+
self.current_llm_config = load_llm_config(self.config, "default")
|
107
|
+
if os.getenv("OPENAI_API_KEY"): # Also check env var for fallback default
|
108
|
+
self.current_llm_config["api_key"] = os.getenv("OPENAI_API_KEY")
|
109
|
+
logger.debug("Overriding API key for fallback 'default' profile from OPENAI_API_KEY env var.")
|
110
|
+
except ValueError as e_default:
|
111
|
+
logger.error(f"Fallback 'default' LLM profile also not found: {e_default}. Swarm may not function correctly.")
|
112
|
+
self.current_llm_config = {} # Set empty config to avoid downstream errors
|
113
|
+
|
114
|
+
# Provide a dummy key if no real key is found and suppression is off
|
115
|
+
if not self.current_llm_config.get("api_key") and not os.getenv("SUPPRESS_DUMMY_KEY"):
|
116
|
+
self.current_llm_config["api_key"] = "sk-DUMMYKEY" # Use dummy key
|
117
|
+
logger.debug("No API key provided or found—using dummy key 'sk-DUMMYKEY'")
|
118
|
+
|
119
|
+
# Initialize AsyncOpenAI client using loaded config
|
120
|
+
# Filter out None values before passing to AsyncOpenAI constructor
|
121
|
+
client_kwargs = {
|
122
|
+
"api_key": self.current_llm_config.get("api_key"),
|
123
|
+
"base_url": self.current_llm_config.get("base_url")
|
124
|
+
}
|
125
|
+
client_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
126
|
+
|
127
|
+
# Log client initialization details (redacting API key)
|
128
|
+
redacted_kwargs_log = client_kwargs.copy()
|
129
|
+
if 'api_key' in redacted_kwargs_log:
|
130
|
+
redacted_kwargs_log['api_key'] = redact_sensitive_data(redacted_kwargs_log['api_key'])
|
131
|
+
logger.debug(f"Initializing AsyncOpenAI client with kwargs: {redacted_kwargs_log}")
|
132
|
+
|
133
|
+
# Use provided client or create a new one
|
134
|
+
self.client = client or AsyncOpenAI(**client_kwargs)
|
135
|
+
|
136
|
+
logger.info(f"Swarm initialized. Default LLM: '{self.model}', Max Tokens: {self.max_context_tokens}")
|
137
|
+
|
138
|
+
async def run_and_stream(
|
139
|
+
self,
|
140
|
+
agent: Agent,
|
141
|
+
messages: List[Dict[str, Any]],
|
142
|
+
context_variables: dict = {},
|
143
|
+
model_override: Optional[str] = None,
|
144
|
+
debug: bool = False,
|
145
|
+
max_turns: int = float("inf"), # Allow infinite turns by default
|
146
|
+
execute_tools: bool = True
|
147
|
+
):
|
148
|
+
"""
|
149
|
+
Run the swarm in streaming mode, yielding responses incrementally.
|
150
|
+
|
151
|
+
Args:
|
152
|
+
agent: Starting agent.
|
153
|
+
messages: Initial conversation history.
|
154
|
+
context_variables: Variables to include in the context.
|
155
|
+
model_override: Optional model to override default.
|
156
|
+
debug: If True, log detailed execution information.
|
157
|
+
max_turns: Maximum number of agent turns to process.
|
158
|
+
execute_tools: If True, execute tool calls requested by the agent.
|
159
|
+
|
160
|
+
Yields:
|
161
|
+
Dict: Streamed chunks (OpenAI format delta) or final response structure.
|
162
|
+
"""
|
163
|
+
if not agent:
|
164
|
+
logger.error("Cannot run in streaming mode: Agent is None")
|
165
|
+
yield {"error": "Agent is None"} # Yield an error structure
|
166
|
+
return
|
167
|
+
|
168
|
+
effective_debug = debug or self.debug # Use local debug flag or instance default
|
169
|
+
logger.debug(f"Starting streaming run for agent '{agent.name}' with {len(messages)} messages")
|
170
|
+
|
171
|
+
active_agent = agent
|
172
|
+
# Deep copy context and history to avoid modifying originals
|
173
|
+
context_variables = copy.deepcopy(context_variables)
|
174
|
+
history = copy.deepcopy(messages)
|
175
|
+
init_len = len(messages) # Store initial length to return only new messages
|
176
|
+
|
177
|
+
# Ensure context_variables is a dict and set active agent name
|
178
|
+
if not isinstance(context_variables, dict):
|
179
|
+
logger.warning(f"Invalid context_variables type: {type(context_variables)}. Using empty dict.")
|
180
|
+
context_variables = {}
|
181
|
+
context_variables["active_agent_name"] = active_agent.name
|
182
|
+
|
183
|
+
# Discover tools and resources for the initial agent if not already done
|
184
|
+
# Use flags to avoid repeated discovery within the same run instance
|
185
|
+
if not hasattr(active_agent, '_tools_discovered'):
|
186
|
+
active_agent.functions = await discover_and_merge_agent_tools(active_agent, self.config, effective_debug)
|
187
|
+
active_agent._tools_discovered = True
|
188
|
+
if not hasattr(active_agent, '_resources_discovered'):
|
189
|
+
active_agent.resources = await discover_and_merge_agent_resources(active_agent, self.config, effective_debug)
|
190
|
+
active_agent._resources_discovered = True
|
191
|
+
|
192
|
+
|
193
|
+
turn = 0
|
194
|
+
while turn < max_turns:
|
195
|
+
turn += 1
|
196
|
+
logger.debug(f"Turn {turn} starting with agent '{active_agent.name}'.")
|
197
|
+
# Prepare message object for accumulating response
|
198
|
+
message = ChatMessage(sender=active_agent.name)
|
199
|
+
|
200
|
+
# Get chat completion stream
|
201
|
+
completion_stream = await get_chat_completion(
|
202
|
+
self.client, active_agent, history, context_variables, self.current_llm_config,
|
203
|
+
self.max_context_tokens, self.max_context_messages, model_override, stream=True, debug=effective_debug
|
204
|
+
)
|
205
|
+
|
206
|
+
yield {"delim": "start"} # Signal start of response stream
|
207
|
+
current_tool_calls_data = [] # Accumulate tool call data from chunks
|
208
|
+
async for chunk in completion_stream:
|
209
|
+
if not chunk.choices: continue # Skip empty chunks
|
210
|
+
delta = chunk.choices[0].delta
|
211
|
+
# Update message object with content from the chunk
|
212
|
+
merge_chunk(message, delta)
|
213
|
+
# Accumulate tool call chunks
|
214
|
+
if delta.tool_calls:
|
215
|
+
for tc_chunk in delta.tool_calls:
|
216
|
+
# Find or create tool call entry in accumulator
|
217
|
+
found = False
|
218
|
+
for existing_tc_data in current_tool_calls_data:
|
219
|
+
if existing_tc_data['index'] == tc_chunk.index:
|
220
|
+
# Merge chunk into existing tool call data
|
221
|
+
if tc_chunk.id: existing_tc_data['id'] = existing_tc_data.get('id', "") + tc_chunk.id
|
222
|
+
if tc_chunk.type: existing_tc_data['type'] = tc_chunk.type
|
223
|
+
if tc_chunk.function:
|
224
|
+
if 'function' not in existing_tc_data: existing_tc_data['function'] = {'name': "", 'arguments': ""}
|
225
|
+
func_data = existing_tc_data['function']
|
226
|
+
if tc_chunk.function.name: func_data['name'] = func_data.get('name', "") + tc_chunk.function.name
|
227
|
+
if tc_chunk.function.arguments: func_data['arguments'] = func_data.get('arguments', "") + tc_chunk.function.arguments
|
228
|
+
found = True
|
229
|
+
break
|
230
|
+
if not found:
|
231
|
+
# Add new tool call data initialized from chunk
|
232
|
+
new_tc_data = {'index': tc_chunk.index}
|
233
|
+
if tc_chunk.id: new_tc_data['id'] = tc_chunk.id
|
234
|
+
if tc_chunk.type: new_tc_data['type'] = tc_chunk.type
|
235
|
+
if tc_chunk.function:
|
236
|
+
new_tc_data['function'] = {}
|
237
|
+
if tc_chunk.function.name: new_tc_data['function']['name'] = tc_chunk.function.name
|
238
|
+
if tc_chunk.function.arguments: new_tc_data['function']['arguments'] = tc_chunk.function.arguments
|
239
|
+
current_tool_calls_data.append(new_tc_data)
|
240
|
+
|
241
|
+
yield delta # Yield the raw chunk
|
242
|
+
yield {"delim": "end"} # Signal end of response stream
|
243
|
+
|
244
|
+
# Finalize tool calls for the completed message from accumulated data
|
245
|
+
if current_tool_calls_data:
|
246
|
+
message.tool_calls = [
|
247
|
+
ChatCompletionMessageToolCall(**tc_data) # Instantiate objects from data
|
248
|
+
for tc_data in current_tool_calls_data
|
249
|
+
# Ensure essential keys are present before instantiation
|
250
|
+
if 'id' in tc_data and 'type' in tc_data and 'function' in tc_data
|
251
|
+
]
|
252
|
+
else:
|
253
|
+
message.tool_calls = None
|
254
|
+
|
255
|
+
# Add the fully formed assistant message (potentially with tool calls) to history
|
256
|
+
history.append(json.loads(message.model_dump_json()))
|
257
|
+
|
258
|
+
# --- Tool Execution Phase ---
|
259
|
+
if message.tool_calls and execute_tools:
|
260
|
+
logger.debug(f"Turn {turn}: Agent '{active_agent.name}' requested {len(message.tool_calls)} tool calls.")
|
261
|
+
# Execute tools
|
262
|
+
partial_response = await handle_tool_calls(message.tool_calls, active_agent.functions, context_variables, effective_debug)
|
263
|
+
# Add tool results to history
|
264
|
+
history.extend(partial_response.messages)
|
265
|
+
# Update context variables from tool results
|
266
|
+
context_variables.update(partial_response.context_variables)
|
267
|
+
|
268
|
+
# Check for agent handoff
|
269
|
+
if partial_response.agent and partial_response.agent != active_agent:
|
270
|
+
active_agent = partial_response.agent
|
271
|
+
context_variables["active_agent_name"] = active_agent.name # Update context
|
272
|
+
logger.debug(f"Turn {turn}: Agent handoff to '{active_agent.name}' detected via tool call.")
|
273
|
+
# Discover tools/resources for the new agent if needed
|
274
|
+
if not hasattr(active_agent, '_tools_discovered'):
|
275
|
+
active_agent.functions = await discover_and_merge_agent_tools(active_agent, self.config, effective_debug)
|
276
|
+
active_agent._tools_discovered = True
|
277
|
+
if not hasattr(active_agent, '_resources_discovered'):
|
278
|
+
active_agent.resources = await discover_and_merge_agent_resources(active_agent, self.config, effective_debug)
|
279
|
+
active_agent._resources_discovered = True
|
280
|
+
|
281
|
+
# Continue the loop to get the next response from the (potentially new) agent
|
282
|
+
logger.debug(f"Turn {turn}: Continuing loop after tool execution.")
|
283
|
+
continue # Go to the start of the while loop for the next turn
|
284
|
+
|
285
|
+
else: # If no tool calls requested or execute_tools is False
|
286
|
+
logger.debug(f"Turn {turn}: No tool calls requested or execution disabled. Ending run.")
|
287
|
+
break # End the run
|
288
|
+
|
289
|
+
# After loop finishes (max_turns reached or break)
|
290
|
+
logger.debug(f"Streaming run completed after {turn} turns. Total history size: {len(history)} messages.")
|
291
|
+
# Yield the final aggregated response structure
|
292
|
+
yield {"response": Response(messages=history[init_len:], agent=active_agent, context_variables=context_variables)}
|
293
|
+
|
294
|
+
async def run(
|
295
|
+
self,
|
296
|
+
agent: Agent,
|
297
|
+
messages: List[Dict[str, Any]],
|
298
|
+
context_variables: dict = {},
|
299
|
+
model_override: Optional[str] = None,
|
300
|
+
stream: bool = False, # Default to non-streaming
|
301
|
+
debug: bool = False,
|
302
|
+
max_turns: int = float("inf"), # Allow infinite turns
|
303
|
+
execute_tools: bool = True
|
304
|
+
) -> Response:
|
305
|
+
"""
|
306
|
+
Execute the swarm run in streaming or non-streaming mode.
|
307
|
+
|
308
|
+
Args:
|
309
|
+
agent: Starting agent.
|
310
|
+
messages: Initial conversation history.
|
311
|
+
context_variables: Variables to include in the context.
|
312
|
+
model_override: Optional model to override default.
|
313
|
+
stream: If True, return an async generator; otherwise, return a single Response object.
|
314
|
+
debug: If True, log detailed execution information.
|
315
|
+
max_turns: Maximum number of agent turns to process.
|
316
|
+
execute_tools: If True, execute tool calls requested by the agent.
|
317
|
+
|
318
|
+
Returns:
|
319
|
+
Response or AsyncGenerator: Final response object, or an async generator if stream=True.
|
320
|
+
"""
|
321
|
+
if not agent:
|
322
|
+
logger.error("Cannot run: Agent is None")
|
323
|
+
raise ValueError("Agent is required")
|
324
|
+
|
325
|
+
effective_debug = debug or self.debug
|
326
|
+
logger.debug(f"Starting run for agent '{agent.name}' with {len(messages)} messages, stream={stream}")
|
327
|
+
|
328
|
+
# Handle streaming case by returning the generator
|
329
|
+
if stream:
|
330
|
+
# We return the async generator directly when stream=True
|
331
|
+
return self.run_and_stream(
|
332
|
+
agent=agent, messages=messages, context_variables=context_variables,
|
333
|
+
model_override=model_override, debug=effective_debug, max_turns=max_turns, execute_tools=execute_tools
|
334
|
+
)
|
335
|
+
|
336
|
+
# --- Non-Streaming Execution ---
|
337
|
+
active_agent = agent
|
338
|
+
context_variables = copy.deepcopy(context_variables)
|
339
|
+
history = copy.deepcopy(messages)
|
340
|
+
init_len = len(messages)
|
341
|
+
|
342
|
+
if not isinstance(context_variables, dict):
|
343
|
+
logger.warning(f"Invalid context_variables type: {type(context_variables)}. Using empty dict.")
|
344
|
+
context_variables = {}
|
345
|
+
context_variables["active_agent_name"] = active_agent.name
|
346
|
+
|
347
|
+
# Discover tools and resources for initial agent if not done
|
348
|
+
if not hasattr(active_agent, '_tools_discovered'):
|
349
|
+
active_agent.functions = await discover_and_merge_agent_tools(active_agent, self.config, effective_debug)
|
350
|
+
active_agent._tools_discovered = True
|
351
|
+
if not hasattr(active_agent, '_resources_discovered'):
|
352
|
+
active_agent.resources = await discover_and_merge_agent_resources(active_agent, self.config, effective_debug)
|
353
|
+
active_agent._resources_discovered = True
|
354
|
+
|
355
|
+
turn = 0
|
356
|
+
while turn < max_turns:
|
357
|
+
turn += 1
|
358
|
+
logger.debug(f"Turn {turn} starting with agent '{active_agent.name}'.")
|
359
|
+
# Get a single, complete chat completion message
|
360
|
+
message = await get_chat_completion_message(
|
361
|
+
self.client, active_agent, history, context_variables, self.current_llm_config,
|
362
|
+
self.max_context_tokens, self.max_context_messages, model_override, stream=False, debug=effective_debug
|
363
|
+
)
|
364
|
+
# Ensure message has sender info (might be redundant if get_chat_completion_message does it)
|
365
|
+
message.sender = active_agent.name
|
366
|
+
# Add the assistant's response to history
|
367
|
+
history.append(json.loads(message.model_dump_json()))
|
368
|
+
|
369
|
+
# --- Tool Execution Phase ---
|
370
|
+
if message.tool_calls and execute_tools:
|
371
|
+
logger.debug(f"Turn {turn}: Agent '{active_agent.name}' requested {len(message.tool_calls)} tool calls.")
|
372
|
+
# Execute tools
|
373
|
+
partial_response = await handle_tool_calls(message.tool_calls, active_agent.functions, context_variables, effective_debug)
|
374
|
+
# Add tool results to history
|
375
|
+
history.extend(partial_response.messages)
|
376
|
+
# Update context variables
|
377
|
+
context_variables.update(partial_response.context_variables)
|
378
|
+
|
379
|
+
# Check for agent handoff
|
380
|
+
if partial_response.agent and partial_response.agent != active_agent:
|
381
|
+
active_agent = partial_response.agent
|
382
|
+
context_variables["active_agent_name"] = active_agent.name # Update context
|
383
|
+
logger.debug(f"Turn {turn}: Agent handoff to '{active_agent.name}' detected.")
|
384
|
+
# Discover tools/resources for the new agent if needed
|
385
|
+
if not hasattr(active_agent, '_tools_discovered'):
|
386
|
+
active_agent.functions = await discover_and_merge_agent_tools(active_agent, self.config, effective_debug)
|
387
|
+
active_agent._tools_discovered = True
|
388
|
+
if not hasattr(active_agent, '_resources_discovered'):
|
389
|
+
active_agent.resources = await discover_and_merge_agent_resources(active_agent, self.config, effective_debug)
|
390
|
+
active_agent._resources_discovered = True
|
391
|
+
|
392
|
+
# Continue loop for next turn if tools were called
|
393
|
+
logger.debug(f"Turn {turn}: Continuing loop after tool execution.")
|
394
|
+
continue
|
395
|
+
|
396
|
+
else: # No tool calls or execution disabled
|
397
|
+
logger.debug(f"Turn {turn}: No tool calls requested or execution disabled. Ending run.")
|
398
|
+
break # End the run
|
399
|
+
|
400
|
+
# After loop finishes
|
401
|
+
logger.debug(f"Non-streaming run completed after {turn} turns. Total history size: {len(history)} messages.")
|
402
|
+
# Create the final Response object containing only the new messages
|
403
|
+
final_response = Response(
|
404
|
+
id=f"response-{uuid.uuid4()}", # Generate unique ID
|
405
|
+
messages=history[init_len:], # Only messages added during this run
|
406
|
+
agent=active_agent, # The agent that produced the last message
|
407
|
+
context_variables=context_variables # Final context state
|
408
|
+
)
|
409
|
+
if effective_debug:
|
410
|
+
logger.debug(f"Final Response ID: {final_response.id}, Messages count: {len(final_response.messages)}")
|
411
|
+
return final_response
|
@@ -1,49 +1,35 @@
|
|
1
1
|
"""
|
2
|
-
Blueprint
|
3
|
-
|
4
|
-
Provides the base class, discovery mechanisms, and utilities for creating
|
5
|
-
and running autonomous agent workflows (blueprints).
|
2
|
+
Blueprint discovery and management utilities.
|
6
3
|
"""
|
7
4
|
|
8
|
-
|
9
|
-
from swarm.core.blueprint_base import BlueprintBase
|
5
|
+
from .blueprint_base import BlueprintBase
|
10
6
|
from .blueprint_discovery import discover_blueprints
|
11
7
|
from .blueprint_utils import filter_blueprints
|
12
8
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
# from . import interactive_mode # If interactive mode is refactored out
|
17
|
-
# from . import output_utils # If output utils are used externally
|
18
|
-
|
19
|
-
# Re-export essential message utilities if they are part of the public API
|
20
|
-
# of this extension package. If they are purely internal utilities,
|
21
|
-
# they don't necessarily need to be re-exported here.
|
9
|
+
# Re-export the necessary message utilities from their new locations
|
10
|
+
# Note: The specific truncation functions like truncate_preserve_pairs might have been
|
11
|
+
# consolidated into truncate_message_history. Adjust if needed.
|
22
12
|
try:
|
23
13
|
from swarm.utils.message_sequence import repair_message_payload, validate_message_sequence
|
24
14
|
from swarm.utils.context_utils import truncate_message_history
|
15
|
+
# If specific old truncation functions are truly needed, they'd have to be
|
16
|
+
# re-implemented or their callers refactored to use truncate_message_history.
|
17
|
+
# Assuming truncate_message_history is the intended replacement for now.
|
18
|
+
# Define aliases if old names are required by downstream code:
|
19
|
+
# truncate_preserve_pairs = truncate_message_history # Example if needed
|
25
20
|
except ImportError as e:
|
21
|
+
# Log an error or warning if imports fail, helpful for debugging setup issues
|
26
22
|
import logging
|
27
|
-
logging.getLogger(__name__).
|
28
|
-
# Define dummy functions or
|
29
|
-
def repair_message_payload(m, **kwargs):
|
30
|
-
def validate_message_sequence(m):
|
31
|
-
def truncate_message_history(m, *args, **kwargs):
|
32
|
-
|
23
|
+
logging.getLogger(__name__).error(f"Failed to import core message utilities: {e}")
|
24
|
+
# Define dummy functions or raise error if critical
|
25
|
+
def repair_message_payload(m, **kwargs): return m
|
26
|
+
def validate_message_sequence(m): return m
|
27
|
+
def truncate_message_history(m, *args, **kwargs): return m
|
33
28
|
|
34
29
|
__all__ = [
|
35
|
-
# Core
|
36
30
|
"BlueprintBase",
|
37
31
|
"discover_blueprints",
|
38
32
|
"filter_blueprints",
|
39
|
-
|
40
|
-
# Helper Modules (Exporting for potential external use, though less common)
|
41
|
-
"config_loader",
|
42
|
-
"cli_handler",
|
43
|
-
# "interactive_mode",
|
44
|
-
# "output_utils",
|
45
|
-
|
46
|
-
# Utility Functions (If considered part of the public API)
|
47
33
|
"repair_message_payload",
|
48
34
|
"validate_message_sequence",
|
49
35
|
"truncate_message_history",
|
@@ -0,0 +1,45 @@
|
|
1
|
+
"""
|
2
|
+
Agent utility functions for Swarm blueprints
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
import os
|
7
|
+
from typing import Dict, List, Any, Callable, Optional
|
8
|
+
import asyncio
|
9
|
+
from swarm.types import Agent
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
def get_agent_name(agent: Agent) -> str:
|
14
|
+
"""Extract an agent's name, defaulting to its class name if not explicitly set."""
|
15
|
+
return getattr(agent, 'name', agent.__class__.__name__)
|
16
|
+
|
17
|
+
async def discover_tools_for_agent(agent: Agent, blueprint: Any) -> List[Any]:
|
18
|
+
"""Asynchronously discover tools available for an agent within a blueprint."""
|
19
|
+
return getattr(blueprint, '_discovered_tools', {}).get(get_agent_name(agent), [])
|
20
|
+
|
21
|
+
async def discover_resources_for_agent(agent: Agent, blueprint: Any) -> List[Any]:
|
22
|
+
"""Asynchronously discover resources available for an agent within a blueprint."""
|
23
|
+
return getattr(blueprint, '_discovered_resources', {}).get(get_agent_name(agent), [])
|
24
|
+
|
25
|
+
def initialize_agents(blueprint: Any) -> None:
|
26
|
+
"""Initialize agents defined in the blueprint's create_agents method."""
|
27
|
+
if not callable(getattr(blueprint, 'create_agents', None)):
|
28
|
+
logger.error(f"Blueprint {blueprint.__class__.__name__} has no callable create_agents method.")
|
29
|
+
return
|
30
|
+
|
31
|
+
agents = blueprint.create_agents()
|
32
|
+
if not isinstance(agents, dict):
|
33
|
+
logger.error(f"Blueprint {blueprint.__class__.__name__}.create_agents must return a dict, got {type(agents)}")
|
34
|
+
return
|
35
|
+
|
36
|
+
if hasattr(blueprint, 'swarm') and hasattr(blueprint.swarm, 'agents'):
|
37
|
+
blueprint.swarm.agents.update(agents)
|
38
|
+
else:
|
39
|
+
logger.error("Blueprint or its swarm instance lacks an 'agents' attribute to update.")
|
40
|
+
return
|
41
|
+
|
42
|
+
if not blueprint.starting_agent and agents:
|
43
|
+
first_agent_name = next(iter(agents.keys()))
|
44
|
+
blueprint.starting_agent = agents[first_agent_name]
|
45
|
+
logger.debug(f"Set default starting agent: {first_agent_name}")
|