open-swarm 0.1.1743070217__py3-none-any.whl → 0.1.1743362777__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.1743362777.dist-info/METADATA +217 -0
- open_swarm-0.1.1743362777.dist-info/RECORD +260 -0
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/WHEEL +1 -2
- open_swarm-0.1.1743362777.dist-info/entry_points.txt +2 -0
- swarm/__init__.py +0 -2
- swarm/auth.py +53 -49
- swarm/blueprints/README.md +67 -0
- swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +412 -0
- swarm/blueprints/chatbot/blueprint_chatbot.py +98 -0
- swarm/blueprints/chatbot/templates/chatbot/chatbot.html +33 -0
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +183 -0
- swarm/blueprints/dilbot_universe/blueprint_dilbot_universe.py +285 -0
- swarm/blueprints/divine_code/__init__.py +0 -0
- swarm/blueprints/divine_code/apps.py +11 -0
- swarm/blueprints/divine_code/blueprint_divine_code.py +219 -0
- swarm/blueprints/django_chat/apps.py +6 -0
- swarm/blueprints/django_chat/blueprint_django_chat.py +84 -0
- swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +37 -0
- swarm/blueprints/django_chat/urls.py +8 -0
- swarm/blueprints/django_chat/views.py +32 -0
- swarm/blueprints/echocraft/blueprint_echocraft.py +44 -0
- swarm/blueprints/family_ties/apps.py +11 -0
- swarm/blueprints/family_ties/blueprint_family_ties.py +152 -0
- swarm/blueprints/family_ties/models.py +19 -0
- swarm/blueprints/family_ties/serializers.py +7 -0
- swarm/blueprints/family_ties/settings.py +16 -0
- swarm/blueprints/family_ties/urls.py +10 -0
- swarm/blueprints/family_ties/views.py +26 -0
- swarm/blueprints/flock/__init__.py +0 -0
- swarm/blueprints/gaggle/blueprint_gaggle.py +184 -0
- swarm/blueprints/gotchaman/blueprint_gotchaman.py +232 -0
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +133 -0
- swarm/blueprints/messenger/templates/messenger/messenger.html +46 -0
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +234 -0
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +248 -0
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +156 -0
- swarm/blueprints/omniplex/blueprint_omniplex.py +221 -0
- swarm/blueprints/rue_code/__init__.py +0 -0
- swarm/blueprints/rue_code/blueprint_rue_code.py +291 -0
- swarm/blueprints/suggestion/blueprint_suggestion.py +110 -0
- swarm/blueprints/unapologetic_press/blueprint_unapologetic_press.py +298 -0
- swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
- swarm/blueprints/whiskeytango_foxtrot/apps.py +11 -0
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +256 -0
- swarm/extensions/blueprint/__init__.py +30 -15
- swarm/extensions/blueprint/agent_utils.py +16 -40
- swarm/extensions/blueprint/blueprint_base.py +141 -543
- swarm/extensions/blueprint/blueprint_discovery.py +112 -98
- swarm/extensions/blueprint/cli_handler.py +185 -0
- swarm/extensions/blueprint/config_loader.py +122 -0
- swarm/extensions/blueprint/django_utils.py +181 -79
- swarm/extensions/blueprint/interactive_mode.py +1 -1
- swarm/extensions/config/config_loader.py +83 -200
- swarm/extensions/launchers/build_swarm_wrapper.py +0 -0
- swarm/extensions/launchers/swarm_cli.py +199 -287
- swarm/llm/chat_completion.py +26 -55
- swarm/management/__init__.py +0 -0
- swarm/management/commands/__init__.py +0 -0
- swarm/management/commands/runserver.py +58 -0
- swarm/permissions.py +38 -0
- swarm/serializers.py +96 -5
- swarm/settings.py +95 -110
- swarm/static/contrib/fonts/fontawesome-webfont.ttf +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff2 +7 -0
- swarm/static/contrib/markedjs/marked.min.js +6 -0
- swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +27 -0
- swarm/static/contrib/tabler-icons/alert-triangle.svg +21 -0
- swarm/static/contrib/tabler-icons/archive.svg +21 -0
- swarm/static/contrib/tabler-icons/artboard.svg +27 -0
- swarm/static/contrib/tabler-icons/automatic-gearbox.svg +23 -0
- swarm/static/contrib/tabler-icons/box-multiple.svg +19 -0
- swarm/static/contrib/tabler-icons/carambola.svg +19 -0
- swarm/static/contrib/tabler-icons/copy.svg +20 -0
- swarm/static/contrib/tabler-icons/download.svg +21 -0
- swarm/static/contrib/tabler-icons/edit.svg +21 -0
- swarm/static/contrib/tabler-icons/filled/carambola.svg +13 -0
- swarm/static/contrib/tabler-icons/filled/paint.svg +13 -0
- swarm/static/contrib/tabler-icons/headset.svg +22 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/message-chatbot.svg +22 -0
- swarm/static/contrib/tabler-icons/message-star.svg +22 -0
- swarm/static/contrib/tabler-icons/message-x.svg +23 -0
- swarm/static/contrib/tabler-icons/message.svg +21 -0
- swarm/static/contrib/tabler-icons/paperclip.svg +18 -0
- swarm/static/contrib/tabler-icons/playlist-add.svg +22 -0
- swarm/static/contrib/tabler-icons/robot.svg +26 -0
- swarm/static/contrib/tabler-icons/search.svg +19 -0
- swarm/static/contrib/tabler-icons/settings.svg +20 -0
- swarm/static/contrib/tabler-icons/thumb-down.svg +19 -0
- swarm/static/contrib/tabler-icons/thumb-up.svg +19 -0
- swarm/static/css/dropdown.css +22 -0
- swarm/static/htmx/htmx.min.js +0 -0
- swarm/static/js/dropdown.js +23 -0
- swarm/static/rest_mode/css/base.css +470 -0
- swarm/static/rest_mode/css/chat-history.css +286 -0
- swarm/static/rest_mode/css/chat.css +251 -0
- swarm/static/rest_mode/css/chatbot.css +74 -0
- swarm/static/rest_mode/css/chatgpt.css +62 -0
- swarm/static/rest_mode/css/colors/corporate.css +74 -0
- swarm/static/rest_mode/css/colors/pastel.css +81 -0
- swarm/static/rest_mode/css/colors/tropical.css +82 -0
- swarm/static/rest_mode/css/general.css +142 -0
- swarm/static/rest_mode/css/layout.css +167 -0
- swarm/static/rest_mode/css/layouts/messenger-layout.css +17 -0
- swarm/static/rest_mode/css/layouts/minimalist-layout.css +57 -0
- swarm/static/rest_mode/css/layouts/mobile-layout.css +8 -0
- swarm/static/rest_mode/css/messages.css +84 -0
- swarm/static/rest_mode/css/messenger.css +135 -0
- swarm/static/rest_mode/css/settings.css +91 -0
- swarm/static/rest_mode/css/simple.css +44 -0
- swarm/static/rest_mode/css/slack.css +58 -0
- swarm/static/rest_mode/css/style.css +156 -0
- swarm/static/rest_mode/css/theme.css +30 -0
- swarm/static/rest_mode/css/toast.css +40 -0
- swarm/static/rest_mode/js/auth.js +9 -0
- swarm/static/rest_mode/js/blueprint.js +41 -0
- swarm/static/rest_mode/js/blueprintUtils.js +12 -0
- swarm/static/rest_mode/js/chatLogic.js +79 -0
- swarm/static/rest_mode/js/debug.js +63 -0
- swarm/static/rest_mode/js/events.js +98 -0
- swarm/static/rest_mode/js/main.js +19 -0
- swarm/static/rest_mode/js/messages.js +264 -0
- swarm/static/rest_mode/js/messengerLogic.js +355 -0
- swarm/static/rest_mode/js/modules/apiService.js +84 -0
- swarm/static/rest_mode/js/modules/blueprintManager.js +162 -0
- swarm/static/rest_mode/js/modules/chatHistory.js +110 -0
- swarm/static/rest_mode/js/modules/debugLogger.js +14 -0
- swarm/static/rest_mode/js/modules/eventHandlers.js +107 -0
- swarm/static/rest_mode/js/modules/messageProcessor.js +120 -0
- swarm/static/rest_mode/js/modules/state.js +7 -0
- swarm/static/rest_mode/js/modules/userInteractions.js +29 -0
- swarm/static/rest_mode/js/modules/validation.js +23 -0
- swarm/static/rest_mode/js/rendering.js +119 -0
- swarm/static/rest_mode/js/settings.js +130 -0
- swarm/static/rest_mode/js/sidebar.js +94 -0
- swarm/static/rest_mode/js/simpleLogic.js +37 -0
- swarm/static/rest_mode/js/slackLogic.js +66 -0
- swarm/static/rest_mode/js/splash.js +76 -0
- swarm/static/rest_mode/js/theme.js +111 -0
- swarm/static/rest_mode/js/toast.js +36 -0
- swarm/static/rest_mode/js/ui.js +265 -0
- swarm/static/rest_mode/js/validation.js +57 -0
- swarm/static/rest_mode/svg/animated_spinner.svg +12 -0
- swarm/static/rest_mode/svg/arrow_down.svg +5 -0
- swarm/static/rest_mode/svg/arrow_left.svg +5 -0
- swarm/static/rest_mode/svg/arrow_right.svg +5 -0
- swarm/static/rest_mode/svg/arrow_up.svg +5 -0
- swarm/static/rest_mode/svg/attach.svg +8 -0
- swarm/static/rest_mode/svg/avatar.svg +7 -0
- swarm/static/rest_mode/svg/canvas.svg +6 -0
- swarm/static/rest_mode/svg/chat_history.svg +4 -0
- swarm/static/rest_mode/svg/close.svg +5 -0
- swarm/static/rest_mode/svg/copy.svg +4 -0
- swarm/static/rest_mode/svg/dark_mode.svg +3 -0
- swarm/static/rest_mode/svg/edit.svg +5 -0
- swarm/static/rest_mode/svg/layout.svg +9 -0
- swarm/static/rest_mode/svg/logo.svg +29 -0
- swarm/static/rest_mode/svg/logout.svg +5 -0
- swarm/static/rest_mode/svg/mobile.svg +5 -0
- swarm/static/rest_mode/svg/new_chat.svg +4 -0
- swarm/static/rest_mode/svg/not_visible.svg +5 -0
- swarm/static/rest_mode/svg/plus.svg +7 -0
- swarm/static/rest_mode/svg/run_code.svg +6 -0
- swarm/static/rest_mode/svg/save.svg +4 -0
- swarm/static/rest_mode/svg/search.svg +6 -0
- swarm/static/rest_mode/svg/settings.svg +4 -0
- swarm/static/rest_mode/svg/speaker.svg +5 -0
- swarm/static/rest_mode/svg/stop.svg +6 -0
- swarm/static/rest_mode/svg/thumbs_down.svg +3 -0
- swarm/static/rest_mode/svg/thumbs_up.svg +3 -0
- swarm/static/rest_mode/svg/toggle_off.svg +6 -0
- swarm/static/rest_mode/svg/toggle_on.svg +6 -0
- swarm/static/rest_mode/svg/trash.svg +10 -0
- swarm/static/rest_mode/svg/undo.svg +3 -0
- swarm/static/rest_mode/svg/visible.svg +8 -0
- swarm/static/rest_mode/svg/voice.svg +10 -0
- swarm/templates/account/login.html +22 -0
- swarm/templates/account/signup.html +32 -0
- swarm/templates/base.html +30 -0
- swarm/templates/chat.html +43 -0
- swarm/templates/index.html +35 -0
- swarm/templates/rest_mode/components/chat_sidebar.html +55 -0
- swarm/templates/rest_mode/components/header.html +45 -0
- swarm/templates/rest_mode/components/main_chat_pane.html +41 -0
- swarm/templates/rest_mode/components/settings_dialog.html +97 -0
- swarm/templates/rest_mode/components/splash_screen.html +7 -0
- swarm/templates/rest_mode/components/top_bar.html +28 -0
- swarm/templates/rest_mode/message_ui.html +50 -0
- swarm/templates/rest_mode/slackbot.html +30 -0
- swarm/templates/simple_blueprint_page.html +24 -0
- swarm/templates/websocket_partials/final_system_message.html +3 -0
- swarm/templates/websocket_partials/system_message.html +4 -0
- swarm/templates/websocket_partials/user_message.html +5 -0
- swarm/urls.py +57 -74
- swarm/utils/log_utils.py +63 -0
- swarm/views/api_views.py +48 -39
- swarm/views/chat_views.py +156 -70
- swarm/views/core_views.py +85 -90
- swarm/views/model_views.py +64 -121
- swarm/views/utils.py +65 -441
- open_swarm-0.1.1743070217.dist-info/METADATA +0 -258
- open_swarm-0.1.1743070217.dist-info/RECORD +0 -89
- open_swarm-0.1.1743070217.dist-info/entry_points.txt +0 -3
- open_swarm-0.1.1743070217.dist-info/top_level.txt +0 -1
- swarm/agent/agent.py +0 -49
- swarm/core.py +0 -326
- swarm/extensions/mcp/__init__.py +0 -1
- swarm/extensions/mcp/cache_utils.py +0 -36
- swarm/extensions/mcp/mcp_client.py +0 -341
- swarm/extensions/mcp/mcp_constants.py +0 -7
- swarm/extensions/mcp/mcp_tool_provider.py +0 -110
- swarm/types.py +0 -126
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/licenses/LICENSE +0 -0
@@ -1,562 +1,160 @@
|
|
1
|
-
"""
|
2
|
-
Swarm Blueprint Base Module (Sync Interactive Mode)
|
3
|
-
"""
|
4
|
-
|
5
|
-
import asyncio
|
6
|
-
import json
|
7
1
|
import logging
|
8
|
-
from src.swarm.utils.message_sequence import repair_message_payload, validate_message_sequence
|
9
|
-
from src.swarm.utils.context_utils import truncate_message_history, get_token_count
|
10
2
|
import os
|
11
|
-
import
|
12
|
-
import
|
3
|
+
import inspect
|
4
|
+
from typing import Dict, List, Any, AsyncGenerator, Optional, Type
|
13
5
|
from abc import ABC, abstractmethod
|
14
|
-
from
|
15
|
-
|
6
|
+
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
16
7
|
from pathlib import Path
|
17
|
-
|
18
|
-
|
19
|
-
from
|
20
|
-
from swarm.utils.redact import redact_sensitive_data
|
21
|
-
from swarm.utils.context_utils import get_token_count, truncate_message_history
|
22
|
-
from swarm.extensions.blueprint.agent_utils import (
|
23
|
-
get_agent_name,
|
24
|
-
discover_tools_for_agent,
|
25
|
-
discover_resources_for_agent,
|
26
|
-
initialize_agents
|
27
|
-
)
|
28
|
-
from swarm.extensions.blueprint.django_utils import register_django_components
|
29
|
-
from swarm.extensions.blueprint.spinner import Spinner
|
30
|
-
from swarm.extensions.blueprint.output_utils import pretty_print_response
|
31
|
-
from dotenv import load_dotenv
|
32
|
-
import argparse
|
33
|
-
from swarm.types import Agent, Response
|
34
|
-
|
35
|
-
logger = logging.getLogger(__name__)
|
36
|
-
|
37
|
-
class BlueprintBase(ABC):
|
38
|
-
"""Base class for Swarm blueprints with sync interactive mode and Django integration."""
|
39
|
-
|
40
|
-
def __init__(
|
41
|
-
self,
|
42
|
-
config: dict,
|
43
|
-
auto_complete_task: bool = False,
|
44
|
-
update_user_goal: bool = False,
|
45
|
-
update_user_goal_frequency: int = 5,
|
46
|
-
skip_django_registration: bool = False,
|
47
|
-
record_chat: bool = False,
|
48
|
-
log_file_path: Optional[str] = None,
|
49
|
-
debug: bool = False,
|
50
|
-
use_markdown: bool = False,
|
51
|
-
**kwargs
|
52
|
-
):
|
53
|
-
self.auto_complete_task = auto_complete_task
|
54
|
-
self.update_user_goal = update_user_goal
|
55
|
-
self.update_user_goal_frequency = max(1, update_user_goal_frequency)
|
56
|
-
self.last_goal_update_count = 0
|
57
|
-
self.record_chat = record_chat
|
58
|
-
self.conversation_id = str(uuid.uuid4()) if record_chat else None
|
59
|
-
self.log_file_path = log_file_path
|
60
|
-
self.debug = debug or DEBUG
|
61
|
-
self.use_markdown = use_markdown
|
62
|
-
self._urls_registered = False
|
63
|
-
|
64
|
-
if self.use_markdown:
|
65
|
-
logger.debug("Markdown rendering enabled (if rich is available).")
|
66
|
-
logger.debug(f"Initializing {self.__class__.__name__} with config: {redact_sensitive_data(config)}")
|
67
|
-
if not hasattr(self, 'metadata') or not isinstance(self.metadata, dict):
|
68
|
-
raise AssertionError(f"{self.__class__.__name__} must define a 'metadata' property returning a dictionary.")
|
8
|
+
import copy
|
9
|
+
# *** Import Django settings ***
|
10
|
+
from django.conf import settings
|
69
11
|
|
70
|
-
|
71
|
-
|
72
|
-
self.max_context_messages = max(1, self.metadata.get("max_context_messages", 50))
|
73
|
-
logger.debug(f"Truncation settings: mode={self.truncation_mode}, max_tokens={self.max_context_tokens}, max_messages={self.max_context_messages}")
|
12
|
+
# *** REMOVE SwarmConfig import from apps.py ***
|
13
|
+
# from swarm.apps import SwarmConfig
|
74
14
|
|
75
|
-
|
76
|
-
|
15
|
+
# Import helpers from config_loader if needed directly
|
16
|
+
# from swarm.extensions.config.config_loader import find_config_file, DEFAULT_CONFIG_FILENAME
|
77
17
|
|
78
|
-
|
79
|
-
|
80
|
-
self.swarm = kwargs.get('swarm_instance') or Swarm(config=self.config, debug=self.debug)
|
81
|
-
logger.debug("Swarm instance initialized.")
|
18
|
+
# *** LLMRegistry doesn't exist yet - Comment out import ***
|
19
|
+
# from swarm.llm.llm_registry import LLMRegistry # Example path - CHANGE ME!
|
82
20
|
|
83
|
-
|
84
|
-
self.starting_agent = None
|
85
|
-
self._discovered_tools: Dict[str, List[Any]] = {}
|
86
|
-
self._discovered_resources: Dict[str, List[Any]] = {}
|
87
|
-
self.spinner = Spinner(interactive=not kwargs.get('non_interactive', False))
|
88
|
-
|
89
|
-
required_env_vars = set(self.metadata.get('env_vars', []))
|
90
|
-
missing_vars = [var for var in required_env_vars if not os.getenv(var)]
|
91
|
-
if missing_vars:
|
92
|
-
logger.warning(f"Missing environment variables for {self.metadata.get('title', self.__class__.__name__)}: {', '.join(missing_vars)}")
|
93
|
-
|
94
|
-
self.required_mcp_servers = self.metadata.get('required_mcp_servers', [])
|
95
|
-
logger.debug(f"Required MCP servers: {self.required_mcp_servers}")
|
96
|
-
|
97
|
-
if self._is_create_agents_overridden():
|
98
|
-
initialize_agents(self)
|
99
|
-
register_django_components(self)
|
100
|
-
|
101
|
-
def _is_create_agents_overridden(self) -> bool:
|
102
|
-
"""Check if the 'create_agents' method is overridden in the subclass."""
|
103
|
-
return self.__class__.create_agents is not BlueprintBase.create_agents
|
21
|
+
logger = logging.getLogger(__name__)
|
104
22
|
|
105
|
-
|
106
|
-
|
107
|
-
|
23
|
+
class BlueprintBase(ABC):
|
24
|
+
"""
|
25
|
+
Abstract base class for all blueprints.
|
26
|
+
Ensures common interface and configuration handling.
|
27
|
+
"""
|
28
|
+
metadata: Dict[str, Any] = {}
|
108
29
|
|
109
|
-
@property
|
110
30
|
@abstractmethod
|
111
|
-
def
|
112
|
-
"""
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def set_starting_agent(self, agent: Agent) -> None:
|
120
|
-
"""Set the starting agent and trigger initial asset discovery."""
|
121
|
-
agent_name = get_agent_name(agent)
|
122
|
-
logger.debug(f"Setting starting agent to: {agent_name}")
|
123
|
-
self.starting_agent = agent
|
124
|
-
self.context_variables["active_agent_name"] = agent_name
|
125
|
-
|
126
|
-
try:
|
127
|
-
loop = asyncio.get_event_loop()
|
128
|
-
is_running = loop.is_running()
|
129
|
-
except RuntimeError:
|
130
|
-
loop = None
|
131
|
-
is_running = False
|
132
|
-
|
133
|
-
# Corrected calls: Pass only agent and config
|
134
|
-
if loop and is_running:
|
135
|
-
logger.debug(f"Scheduling async asset discovery for starting agent {agent_name}.")
|
136
|
-
asyncio.ensure_future(discover_tools_for_agent(agent, self.swarm.config))
|
137
|
-
asyncio.ensure_future(discover_resources_for_agent(agent, self.swarm.config))
|
138
|
-
else:
|
139
|
-
logger.debug(f"Running sync asset discovery for starting agent {agent_name}.")
|
140
|
-
try:
|
141
|
-
asyncio.run(discover_tools_for_agent(agent, self.swarm.config))
|
142
|
-
asyncio.run(discover_resources_for_agent(agent, self.swarm.config))
|
143
|
-
except RuntimeError as e:
|
144
|
-
if "cannot be called from a running event loop" in str(e): logger.error("Nested asyncio.run detected during sync discovery.")
|
145
|
-
else: raise e
|
146
|
-
|
147
|
-
async def determine_active_agent(self) -> Optional[Agent]:
|
148
|
-
"""Determine the currently active agent."""
|
149
|
-
active_agent_name = self.context_variables.get("active_agent_name")
|
150
|
-
agent_to_use = None
|
151
|
-
|
152
|
-
if active_agent_name and active_agent_name in self.swarm.agents:
|
153
|
-
agent_to_use = self.swarm.agents[active_agent_name]
|
154
|
-
logger.debug(f"Determined active agent from context: {active_agent_name}")
|
155
|
-
elif self.starting_agent:
|
156
|
-
agent_to_use = self.starting_agent
|
157
|
-
active_agent_name = get_agent_name(agent_to_use)
|
158
|
-
if self.context_variables.get("active_agent_name") != active_agent_name:
|
159
|
-
self.context_variables["active_agent_name"] = active_agent_name
|
160
|
-
logger.debug(f"Falling back to starting agent: {active_agent_name} and updating context.")
|
161
|
-
else:
|
162
|
-
logger.debug(f"Using starting agent: {active_agent_name}")
|
163
|
-
else:
|
164
|
-
logger.error("Cannot determine active agent: No agent name in context and no starting agent set.")
|
165
|
-
return None
|
166
|
-
|
167
|
-
agent_name_cache_key = get_agent_name(agent_to_use)
|
168
|
-
# Corrected calls: Pass only agent and config
|
169
|
-
if agent_name_cache_key not in self._discovered_tools:
|
170
|
-
logger.debug(f"Cache miss for tools of agent {agent_name_cache_key}. Discovering...")
|
171
|
-
discovered_tools = await discover_tools_for_agent(agent_to_use, self.swarm.config)
|
172
|
-
self._discovered_tools[agent_name_cache_key] = discovered_tools
|
173
|
-
|
174
|
-
if agent_name_cache_key not in self._discovered_resources:
|
175
|
-
logger.debug(f"Cache miss for resources of agent {agent_name_cache_key}. Discovering...")
|
176
|
-
discovered_resources = await discover_resources_for_agent(agent_to_use, self.swarm.config)
|
177
|
-
self._discovered_resources[agent_name_cache_key] = discovered_resources
|
178
|
-
|
179
|
-
return agent_to_use
|
180
|
-
|
181
|
-
# --- Core Execution Logic ---
|
182
|
-
def run_with_context(self, messages: List[Dict[str, str]], context_variables: dict) -> dict:
|
183
|
-
"""Synchronous wrapper for the async execution logic."""
|
184
|
-
return asyncio.run(self.run_with_context_async(messages, context_variables))
|
185
|
-
|
186
|
-
async def run_with_context_async(self, messages: List[Dict[str, str]], context_variables: dict) -> dict:
|
187
|
-
"""Asynchronously run the blueprint's logic."""
|
188
|
-
self.context_variables.update(context_variables)
|
189
|
-
logger.debug(f"Context variables updated: {self.context_variables}")
|
190
|
-
|
191
|
-
active_agent = await self.determine_active_agent()
|
192
|
-
if not active_agent:
|
193
|
-
logger.error("No active agent could be determined. Cannot proceed.")
|
194
|
-
return {"response": Response(messages=[{"role": "assistant", "content": "Error: No active agent available."}], agent=None, context_variables=self.context_variables), "context_variables": self.context_variables}
|
195
|
-
|
196
|
-
model = getattr(active_agent, 'model', None) or self.swarm.current_llm_config.get("model", "default")
|
197
|
-
logger.debug(f"Using model: {model} for agent {get_agent_name(active_agent)}")
|
198
|
-
|
199
|
-
truncated_messages = self.truncate_message_history(messages, model)
|
200
|
-
validated_messages = validate_message_sequence(truncated_messages)
|
201
|
-
repaired_messages = repair_message_payload(validated_messages, debug=self.debug)
|
202
|
-
|
203
|
-
if not self.swarm.agents:
|
204
|
-
logger.warning("No agents registered; returning default response.")
|
205
|
-
return {"response": Response(messages=[{"role": "assistant", "content": "No agents available in Swarm."}], agent=None, context_variables=self.context_variables), "context_variables": self.context_variables}
|
206
|
-
|
207
|
-
logger.debug(f"Running Swarm core with agent: {get_agent_name(active_agent)}")
|
208
|
-
self.spinner.start(f"Generating response from {get_agent_name(active_agent)}")
|
209
|
-
response_obj = None
|
210
|
-
try:
|
211
|
-
response_obj = await self.swarm.run(
|
212
|
-
agent=active_agent, messages=repaired_messages, context_variables=self.context_variables,
|
213
|
-
stream=False, debug=self.debug,
|
214
|
-
)
|
215
|
-
except Exception as e:
|
216
|
-
logger.error(f"Swarm run failed: {e}", exc_info=True)
|
217
|
-
response_obj = Response(messages=[{"role": "assistant", "content": f"An error occurred: {str(e)}"}], agent=active_agent, context_variables=self.context_variables)
|
218
|
-
finally:
|
219
|
-
self.spinner.stop()
|
220
|
-
|
221
|
-
final_agent = active_agent
|
222
|
-
updated_context = self.context_variables.copy()
|
223
|
-
|
224
|
-
if response_obj:
|
225
|
-
if hasattr(response_obj, 'agent') and response_obj.agent and get_agent_name(response_obj.agent) != get_agent_name(active_agent):
|
226
|
-
final_agent = response_obj.agent
|
227
|
-
new_agent_name = get_agent_name(final_agent)
|
228
|
-
updated_context["active_agent_name"] = new_agent_name
|
229
|
-
logger.debug(f"Agent handoff occurred. New active agent: {new_agent_name}")
|
230
|
-
# Corrected calls: Pass only agent and config
|
231
|
-
asyncio.ensure_future(discover_tools_for_agent(final_agent, self.swarm.config))
|
232
|
-
asyncio.ensure_future(discover_resources_for_agent(final_agent, self.swarm.config))
|
233
|
-
if hasattr(response_obj, 'context_variables'):
|
234
|
-
updated_context.update(response_obj.context_variables)
|
235
|
-
else:
|
236
|
-
logger.error("Swarm run returned None or invalid response structure.")
|
237
|
-
response_obj = Response(messages=[{"role": "assistant", "content": "Error processing the request."}], agent=active_agent, context_variables=updated_context)
|
238
|
-
|
239
|
-
return {"response": response_obj, "context_variables": updated_context}
|
240
|
-
|
241
|
-
def set_active_agent(self, agent_name: str) -> None:
|
242
|
-
"""Explicitly set the active agent by name and trigger asset discovery."""
|
243
|
-
if agent_name in self.swarm.agents:
|
244
|
-
self.context_variables["active_agent_name"] = agent_name
|
245
|
-
agent = self.swarm.agents[agent_name]
|
246
|
-
logger.debug(f"Explicitly setting active agent to: {agent_name}")
|
247
|
-
# Corrected calls: Pass only agent and config
|
248
|
-
if agent_name not in self._discovered_tools:
|
249
|
-
logger.debug(f"Discovering tools for explicitly set agent {agent_name}.")
|
250
|
-
try: asyncio.run(discover_tools_for_agent(agent, self.swarm.config))
|
251
|
-
except RuntimeError as e:
|
252
|
-
if "cannot be called from a running event loop" in str(e): logger.error("Cannot run sync discovery from within an async context (set_active_agent).")
|
253
|
-
else: raise e
|
254
|
-
if agent_name not in self._discovered_resources:
|
255
|
-
logger.debug(f"Discovering resources for explicitly set agent {agent_name}.")
|
256
|
-
try: asyncio.run(discover_resources_for_agent(agent, self.swarm.config))
|
257
|
-
except RuntimeError as e:
|
258
|
-
if "cannot be called from a running event loop" in str(e): logger.error("Cannot run sync discovery from within an async context (set_active_agent).")
|
259
|
-
else: raise e
|
260
|
-
else:
|
261
|
-
logger.error(f"Attempted to set active agent to '{agent_name}', but agent not found.")
|
262
|
-
|
263
|
-
# --- Task Completion & Goal Update Logic ---
|
264
|
-
async def _is_task_done_async(self, user_goal: str, conversation_summary: str, last_assistant_message: str) -> bool:
|
265
|
-
"""Check if the task defined by user_goal is complete using an LLM call."""
|
266
|
-
if not user_goal:
|
267
|
-
logger.warning("Cannot check task completion: user_goal is empty.")
|
268
|
-
return False
|
269
|
-
|
270
|
-
system_prompt = os.getenv("TASK_DONE_PROMPT", "You are a completion checker. Respond with ONLY 'YES' or 'NO'.")
|
271
|
-
user_prompt = os.getenv(
|
272
|
-
"TASK_DONE_USER_PROMPT",
|
273
|
-
"User's goal: {user_goal}\nConversation summary: {conversation_summary}\nLast assistant message: {last_assistant_message}\nIs the task fully complete? Answer only YES or NO."
|
274
|
-
).format(user_goal=user_goal, conversation_summary=conversation_summary, last_assistant_message=last_assistant_message)
|
275
|
-
|
276
|
-
check_prompt = [{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}]
|
277
|
-
client = self.swarm.client
|
278
|
-
model_to_use = self.swarm.current_llm_config.get("model", self.swarm.model)
|
279
|
-
|
280
|
-
try:
|
281
|
-
response = await client.chat.completions.create(
|
282
|
-
model=model_to_use, messages=check_prompt, max_tokens=5, temperature=0
|
283
|
-
)
|
284
|
-
if response.choices:
|
285
|
-
result_content = response.choices[0].message.content.strip().upper()
|
286
|
-
is_done = result_content.startswith("YES")
|
287
|
-
logger.debug(f"Task completion check (Goal: '{user_goal}', LLM Raw: '{result_content}'): {is_done}")
|
288
|
-
return is_done
|
289
|
-
else:
|
290
|
-
logger.warning("LLM response for task completion check had no choices.")
|
291
|
-
return False
|
292
|
-
except Exception as e:
|
293
|
-
logger.error(f"Task completion check LLM call failed: {e}", exc_info=True)
|
294
|
-
return False
|
295
|
-
|
296
|
-
async def _update_user_goal_async(self, messages: List[Dict[str, str]]) -> None:
|
297
|
-
"""Update the 'user_goal' in context_variables based on conversation history using an LLM call."""
|
298
|
-
if not messages:
|
299
|
-
logger.debug("Cannot update goal: No messages provided.")
|
300
|
-
return
|
301
|
-
|
302
|
-
system_prompt = os.getenv(
|
303
|
-
"UPDATE_GOAL_PROMPT",
|
304
|
-
"You are an assistant that summarizes the user's primary objective from the conversation. Provide a concise, one-sentence summary."
|
305
|
-
)
|
306
|
-
conversation_text = "\n".join(f"{m['role']}: {m.get('content', '')}" for m in messages if m.get('content') or m.get('tool_calls'))
|
307
|
-
if not conversation_text:
|
308
|
-
logger.debug("Cannot update goal: No content in messages.")
|
309
|
-
return
|
310
|
-
|
311
|
-
user_prompt = os.getenv(
|
312
|
-
"UPDATE_GOAL_USER_PROMPT",
|
313
|
-
"Summarize the user's main goal based on this conversation:\n{conversation}"
|
314
|
-
).format(conversation=conversation_text)
|
315
|
-
|
316
|
-
prompt = [{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}]
|
317
|
-
client = self.swarm.client
|
318
|
-
model_to_use = self.swarm.current_llm_config.get("model", self.swarm.model)
|
31
|
+
async def run(self, messages: List[Dict[str, str]]) -> AsyncGenerator[Dict[str, Any], None]:
|
32
|
+
"""
|
33
|
+
Asynchronously runs the blueprint logic with the given messages.
|
34
|
+
Must be implemented by subclasses.
|
35
|
+
"""
|
36
|
+
if False: yield {}
|
37
|
+
pass
|
319
38
|
|
39
|
+
def __init__(
|
40
|
+
self,
|
41
|
+
# config_override: Optional[Dict] = None, # Override logic needs rethink
|
42
|
+
params: Optional[Dict] = None,
|
43
|
+
):
|
44
|
+
logger.debug(f"BlueprintBase.__init__ starting for {self.__class__.__name__}")
|
45
|
+
self.params = params if params else {}
|
46
|
+
# self._config_override = config_override # Store override for use in _configure
|
47
|
+
# self._resolved_config_path = None # Initialize path tracker
|
48
|
+
|
49
|
+
# Configuration loading needs rethink - access via settings for now
|
50
|
+
self._configure() # This sets self.config attributes from settings
|
51
|
+
|
52
|
+
# Now setup others that might depend on config
|
53
|
+
self._setup_llm() # Uses self.config attributes
|
54
|
+
self._setup_jinja() # Uses class path
|
55
|
+
|
56
|
+
# Logging Adjustment
|
57
|
+
# Check config *after* it has been loaded by _configure
|
58
|
+
# Use self.is_debug which should be set in _configure
|
59
|
+
if hasattr(self, 'is_debug') and self.is_debug:
|
60
|
+
log_level_int = logging.DEBUG
|
61
|
+
if logging.root.level > log_level_int:
|
62
|
+
logger.info(f"Root logger level is {logging.getLevelName(logging.root.level)}. Lowering to DEBUG due to config.")
|
63
|
+
logging.root.setLevel(log_level_int)
|
64
|
+
|
65
|
+
# Ensure llm_profile_name is set by _setup_llm before logging it
|
66
|
+
# Add check if llm_profile_name exists before logging
|
67
|
+
profile_name_to_log = getattr(self, 'llm_profile_name', 'N/A')
|
68
|
+
logger.info(f"Initialized blueprint '{self.metadata.get('name', self.__class__.__name__)}' with profile '{profile_name_to_log}'")
|
69
|
+
|
70
|
+
def _configure(self):
|
71
|
+
"""Placeholder: Sets config attributes based on django.conf.settings."""
|
72
|
+
logger.debug("BlueprintBase._configure accessing django.conf.settings")
|
73
|
+
# *** Access settings directly (temporary) ***
|
74
|
+
# This assumes swarm_config.json content is NOT yet loaded into settings
|
75
|
+
# We'll rely on defaults or potentially fail in _setup_llm
|
76
|
+
self.is_debug = getattr(settings, 'DEBUG', False)
|
77
|
+
# Store blueprint-specific parts for convenience - these won't exist in settings yet!
|
78
|
+
self.blueprint_config = {} # Placeholder
|
79
|
+
self.blueprint_defaults = {} # Placeholder
|
80
|
+
logger.warning("_configure is using placeholders. Swarm config file content is not loaded here.")
|
81
|
+
|
82
|
+
|
83
|
+
def _setup_llm(self):
|
84
|
+
"""Sets up the LLM provider based on configuration stored in self.config."""
|
85
|
+
# *** Access settings directly (temporary) ***
|
86
|
+
# Get default profile name from settings if defined, else 'default'
|
87
|
+
default_profile = getattr(settings, 'DEFAULT_LLM_PROFILE', 'default')
|
88
|
+
self.llm_profile_name = self.blueprint_config.get('llm_profile', default_profile) # Use placeholder blueprint_config
|
89
|
+
logger.debug(f"Getting LLM profile details for '{self.llm_profile_name}'.")
|
90
|
+
|
91
|
+
# *** This part will likely fail or use defaults as swarm_config isn't in settings ***
|
92
|
+
# Attempt to get profiles from settings if they were somehow loaded there
|
93
|
+
all_llm_profiles = getattr(settings, 'LLM_PROFILES', {})
|
94
|
+
profile_data = all_llm_profiles.get(self.llm_profile_name)
|
95
|
+
|
96
|
+
if profile_data is None:
|
97
|
+
logger.warning(f"LLM profile '{self.llm_profile_name}' not found in django settings. LLM will not be available.")
|
98
|
+
self.llm_profile = {} # Set empty profile
|
99
|
+
self.llm = None # Set LLM to None
|
100
|
+
return # Exit setup early
|
101
|
+
|
102
|
+
logger.debug(f"Using LLM profile '{self.llm_profile_name}' from settings.")
|
103
|
+
self.llm_profile = profile_data
|
104
|
+
# *** LLMRegistry doesn't exist yet - Comment out usage and set placeholder ***
|
105
|
+
# self.llm = LLMRegistry.get_llm(self.llm_profile)
|
106
|
+
self.llm = None # Placeholder until registry is implemented
|
107
|
+
logger.warning("LLMRegistry not implemented. self.llm set to None.")
|
108
|
+
|
109
|
+
|
110
|
+
@staticmethod
|
111
|
+
def _substitute_env_vars(data: Any) -> Any:
|
112
|
+
"""Recursively substitutes environment variables in strings."""
|
113
|
+
if isinstance(data, dict):
|
114
|
+
return {k: BlueprintBase._substitute_env_vars(v) for k, v in data.items()}
|
115
|
+
if isinstance(data, list):
|
116
|
+
return [BlueprintBase._substitute_env_vars(item) for item in data]
|
117
|
+
if isinstance(data, str):
|
118
|
+
return os.path.expandvars(data)
|
119
|
+
return data
|
120
|
+
|
121
|
+
def _setup_jinja(self):
|
122
|
+
"""Sets up Jinja2 environment."""
|
320
123
|
try:
|
321
|
-
|
322
|
-
|
323
|
-
)
|
324
|
-
if
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
logger.warning("LLM goal update returned empty response.")
|
124
|
+
blueprint_file_path = inspect.getfile(self.__class__)
|
125
|
+
blueprint_dir = os.path.dirname(blueprint_file_path)
|
126
|
+
template_dir = os.path.join(blueprint_dir, 'templates')
|
127
|
+
if os.path.isdir(template_dir):
|
128
|
+
logger.debug(f"Setting up Jinja env with loader at: {template_dir}")
|
129
|
+
self.jinja_env = Environment(
|
130
|
+
loader=FileSystemLoader(template_dir),
|
131
|
+
autoescape=select_autoescape(['html', 'xml'])
|
132
|
+
)
|
331
133
|
else:
|
332
|
-
logger.
|
134
|
+
logger.debug(f"No 'templates' directory found for blueprint at {template_dir}. Jinja env not created.")
|
135
|
+
self.jinja_env = None
|
333
136
|
except Exception as e:
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
"""Return the custom prompt string, potentially from the active agent."""
|
344
|
-
active_agent = self.swarm.agents.get(self.context_variables.get("active_agent_name"))
|
345
|
-
return getattr(active_agent, 'prompt', getattr(self, "custom_user_prompt", "User: "))
|
346
|
-
|
347
|
-
# --- Interactive & Non-Interactive Modes ---
|
348
|
-
def interactive_mode(self, stream: bool = False) -> None:
|
349
|
-
"""Run the blueprint in interactive command-line mode."""
|
350
|
-
from .interactive_mode import run_interactive_mode
|
351
|
-
run_interactive_mode(self, stream)
|
352
|
-
|
353
|
-
def non_interactive_mode(self, instruction: str, stream: bool = False) -> None:
|
354
|
-
"""Run the blueprint non-interactively with a single instruction."""
|
355
|
-
logger.debug(f"Starting non-interactive mode with instruction: {instruction}, stream={stream}")
|
356
|
-
try:
|
357
|
-
asyncio.run(self.non_interactive_mode_async(instruction, stream=stream))
|
358
|
-
except RuntimeError as e:
|
359
|
-
if "cannot be called from a running event loop" in str(e):
|
360
|
-
logger.error("Cannot start non_interactive_mode with asyncio.run from an existing event loop.")
|
361
|
-
else: raise e
|
362
|
-
|
363
|
-
async def non_interactive_mode_async(self, instruction: str, stream: bool = False) -> None:
|
364
|
-
"""Asynchronously run the blueprint non-interactively."""
|
365
|
-
logger.debug(f"Starting async non-interactive mode with instruction: {instruction}, stream={stream}")
|
366
|
-
if not self.swarm:
|
367
|
-
logger.error("Swarm instance not initialized.")
|
368
|
-
print("Error: Swarm framework not ready.")
|
369
|
-
return
|
370
|
-
|
371
|
-
print(f"--- {self.metadata.get('title', 'Blueprint')} Non-Interactive Mode ---")
|
372
|
-
instructions = [line.strip() for line in instruction.splitlines() if line.strip()]
|
373
|
-
if not instructions:
|
374
|
-
print("No valid instruction provided.")
|
375
|
-
return
|
376
|
-
messages = [{"role": "user", "content": line} for line in instructions]
|
377
|
-
|
378
|
-
if not self.starting_agent:
|
379
|
-
if self.swarm.agents:
|
380
|
-
first_agent_name = next(iter(self.swarm.agents.keys()))
|
381
|
-
logger.warning(f"No starting agent explicitly set. Defaulting to first agent: {first_agent_name}")
|
382
|
-
self.set_starting_agent(self.swarm.agents[first_agent_name])
|
383
|
-
else:
|
384
|
-
logger.error("No starting agent set and no agents defined.")
|
385
|
-
print("Error: No agent available to handle the instruction.")
|
386
|
-
return
|
387
|
-
|
388
|
-
self.context_variables["user_goal"] = instruction
|
389
|
-
self.context_variables["active_agent_name"] = get_agent_name(self.starting_agent)
|
390
|
-
|
391
|
-
if stream:
|
392
|
-
logger.debug("Running non-interactive in streaming mode.")
|
393
|
-
response_generator = self.swarm.run(
|
394
|
-
agent=self.starting_agent, messages=messages, context_variables=self.context_variables,
|
395
|
-
stream=True, debug=self.debug,
|
396
|
-
)
|
397
|
-
final_response_data = await self._process_and_print_streaming_response_async(response_generator)
|
398
|
-
if self.auto_complete_task:
|
399
|
-
logger.warning("Auto-completion is not fully supported with streaming in non-interactive mode.")
|
400
|
-
else:
|
401
|
-
logger.debug("Running non-interactive in non-streaming mode.")
|
402
|
-
result = await self.run_with_context_async(messages, self.context_variables)
|
403
|
-
swarm_response = result.get("response")
|
404
|
-
self.context_variables = result.get("context_variables", self.context_variables)
|
405
|
-
|
406
|
-
response_messages = []
|
407
|
-
if hasattr(swarm_response, 'messages'): response_messages = swarm_response.messages
|
408
|
-
elif isinstance(swarm_response, dict) and 'messages' in swarm_response: response_messages = swarm_response.get('messages', [])
|
409
|
-
|
410
|
-
self._pretty_print_response(response_messages)
|
411
|
-
if self.auto_complete_task and self.swarm.agents:
|
412
|
-
logger.debug("Starting auto-completion task.")
|
413
|
-
current_history = messages + response_messages
|
414
|
-
await self._auto_complete_task_async(current_history, stream=False)
|
415
|
-
|
416
|
-
print("--- Execution Completed ---")
|
417
|
-
|
418
|
-
async def _process_and_print_streaming_response_async(self, response_generator):
|
419
|
-
"""Async helper to process and print streaming response chunks."""
|
420
|
-
content = ""
|
421
|
-
last_sender = self.context_variables.get("active_agent_name", "Assistant")
|
422
|
-
final_response_chunk_data = None
|
137
|
+
logger.warning(f"Could not determine template path for {self.__class__.__name__}: {e}. Jinja env not created.")
|
138
|
+
self.jinja_env = None
|
139
|
+
|
140
|
+
def render_prompt(self, template_name: str, context: Dict = None) -> str:
|
141
|
+
"""Renders a Jinja2 template."""
|
142
|
+
if not self.jinja_env:
|
143
|
+
raise RuntimeError(f"Jinja environment not set up for blueprint {self.__class__.__name__}.")
|
144
|
+
if context is None:
|
145
|
+
context = {}
|
423
146
|
try:
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
print(f"\033[94m{last_sender}\033[0m: ", end="", flush=True)
|
428
|
-
elif chunk["delim"] == "end" and content:
|
429
|
-
print()
|
430
|
-
content = ""
|
431
|
-
elif hasattr(chunk, 'choices') and chunk.choices:
|
432
|
-
delta = chunk.choices[0].delta
|
433
|
-
if delta and delta.content:
|
434
|
-
print(delta.content, end="", flush=True)
|
435
|
-
content += delta.content
|
436
|
-
elif isinstance(chunk, dict) and "response" in chunk:
|
437
|
-
final_response_chunk_data = chunk["response"]
|
438
|
-
if hasattr(final_response_chunk_data, 'agent'):
|
439
|
-
last_sender = get_agent_name(final_response_chunk_data.agent)
|
440
|
-
if hasattr(final_response_chunk_data, 'context_variables'):
|
441
|
-
self.context_variables.update(final_response_chunk_data.context_variables)
|
442
|
-
logger.debug("Received final aggregated response chunk in stream.")
|
443
|
-
elif isinstance(chunk, dict) and "error" in chunk:
|
444
|
-
logger.error(f"Error received during stream: {chunk['error']}")
|
445
|
-
print(f"\n[Stream Error: {chunk['error']}]")
|
147
|
+
template = self.jinja_env.get_template(template_name)
|
148
|
+
full_context = {**self.params, **context}
|
149
|
+
return template.render(full_context)
|
446
150
|
except Exception as e:
|
447
|
-
|
448
|
-
|
449
|
-
finally:
|
450
|
-
if content: print()
|
451
|
-
return final_response_chunk_data
|
452
|
-
|
453
|
-
async def _auto_complete_task_async(self, current_history: List[Dict[str, str]], stream: bool) -> None:
|
454
|
-
"""Async helper for task auto-completion loop (non-streaming)."""
|
455
|
-
max_auto_turns = 10
|
456
|
-
auto_turn = 0
|
457
|
-
while auto_turn < max_auto_turns:
|
458
|
-
auto_turn += 1
|
459
|
-
logger.debug(f"Auto-completion Turn: {auto_turn}/{max_auto_turns}")
|
460
|
-
conversation_summary = " ".join(m.get("content", "") for m in current_history[-4:] if m.get("content"))
|
461
|
-
last_assistant_msg = next((m.get("content", "") for m in reversed(current_history) if m.get("role") == "assistant" and m.get("content")), "")
|
462
|
-
user_goal = self.context_variables.get("user_goal", "")
|
463
|
-
|
464
|
-
# Call the renamed async method
|
465
|
-
if await self._is_task_done_async(user_goal, conversation_summary, last_assistant_msg):
|
466
|
-
print("\033[93m[System]\033[0m: Task detected as complete.")
|
467
|
-
break
|
468
|
-
|
469
|
-
logger.debug("Task not complete, running next auto-completion turn.")
|
470
|
-
result = await self.run_with_context_async(current_history, self.context_variables)
|
471
|
-
swarm_response = result.get("response")
|
472
|
-
self.context_variables = result.get("context_variables", self.context_variables)
|
473
|
-
|
474
|
-
new_messages = []
|
475
|
-
if hasattr(swarm_response, 'messages'): new_messages = swarm_response.messages
|
476
|
-
elif isinstance(swarm_response, dict) and 'messages' in swarm_response: new_messages = swarm_response.get('messages', [])
|
477
|
-
|
478
|
-
if not new_messages:
|
479
|
-
logger.warning("Auto-completion turn yielded no new messages. Stopping.")
|
480
|
-
break
|
481
|
-
|
482
|
-
self._pretty_print_response(new_messages)
|
483
|
-
current_history.extend(new_messages)
|
484
|
-
|
485
|
-
if auto_turn >= max_auto_turns:
|
486
|
-
logger.warning("Auto-completion reached maximum turns limit.")
|
487
|
-
print("\033[93m[System]\033[0m: Reached max auto-completion turns.")
|
151
|
+
logger.error(f"Error rendering template '{template_name}': {e}", exc_info=True)
|
152
|
+
raise
|
488
153
|
|
489
|
-
def
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
try:
|
496
|
-
asyncio.run(self._auto_complete_task_async(messages, stream=False))
|
497
|
-
except RuntimeError as e:
|
498
|
-
if "cannot be called from a running event loop" in str(e):
|
499
|
-
logger.error("Cannot start _auto_complete_task with asyncio.run from an existing event loop.")
|
500
|
-
else: raise e
|
501
|
-
|
502
|
-
# --- Class Method for Entry Point ---
|
503
|
-
@classmethod
|
504
|
-
def main(cls):
|
505
|
-
parser = argparse.ArgumentParser(description=f"Run the {cls.__name__} blueprint.")
|
506
|
-
parser.add_argument("--config", default="./swarm_config.json", help="Path to the swarm_config.json file.")
|
507
|
-
parser.add_argument("--instruction", help="Single instruction for non-interactive mode.")
|
508
|
-
parser.add_argument("--stream", action="store_true", help="Enable streaming output in non-interactive mode.")
|
509
|
-
parser.add_argument("--auto-complete-task", action="store_true", help="Enable task auto-completion in non-interactive mode.")
|
510
|
-
parser.add_argument("--update-user-goal", action="store_true", help="Enable dynamic goal updates using LLM.")
|
511
|
-
parser.add_argument("--update-user-goal-frequency", type=int, default=5, help="Frequency (in messages) for updating user goal.")
|
512
|
-
parser.add_argument("--log-file-path", help="Path for logging output (default: ~/.swarm/logs/<blueprint_name>.log).")
|
513
|
-
parser.add_argument("--debug", action="store_true", help="Enable debug logging to console instead of file.")
|
514
|
-
parser.add_argument("--use-markdown", action="store_true", help="Enable markdown rendering for assistant responses.")
|
515
|
-
args = parser.parse_args()
|
516
|
-
|
517
|
-
root_logger = logging.getLogger()
|
518
|
-
log_level = logging.DEBUG if args.debug or DEBUG else logging.INFO
|
519
|
-
root_logger.setLevel(log_level)
|
520
|
-
|
521
|
-
if root_logger.hasHandlers(): root_logger.handlers.clear()
|
522
|
-
|
523
|
-
log_formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(name)s:%(lineno)d - %(message)s")
|
524
|
-
log_handler = logging.StreamHandler(sys.stdout) if args.debug else logging.FileHandler(
|
525
|
-
Path(args.log_file_path or Path.home() / ".swarm" / "logs" / f"{cls.__name__.lower()}.log").resolve(), mode='a'
|
526
|
-
)
|
527
|
-
log_handler.setFormatter(log_formatter)
|
528
|
-
root_logger.addHandler(log_handler)
|
529
|
-
logger.info(f"Logging initialized. Level: {logging.getLevelName(log_level)}. Destination: {getattr(log_handler, 'baseFilename', 'console')}")
|
530
|
-
|
531
|
-
original_stderr = sys.stderr
|
532
|
-
dev_null = None
|
533
|
-
if not args.debug:
|
534
|
-
try:
|
535
|
-
dev_null = open(os.devnull, "w")
|
536
|
-
sys.stderr = dev_null
|
537
|
-
logger.info(f"Redirected stderr to {os.devnull}")
|
538
|
-
except OSError as e: logger.warning(f"Could not redirect stderr: {e}")
|
539
|
-
|
540
|
-
try:
|
541
|
-
config_data = load_server_config(args.config)
|
542
|
-
blueprint_instance = cls(
|
543
|
-
config=config_data, auto_complete_task=args.auto_complete_task, update_user_goal=args.update_user_goal,
|
544
|
-
update_user_goal_frequency=args.update_user_goal_frequency, log_file_path=str(getattr(log_handler, 'baseFilename', None)),
|
545
|
-
debug=args.debug, use_markdown=args.use_markdown, non_interactive=bool(args.instruction)
|
546
|
-
)
|
547
|
-
if args.instruction:
|
548
|
-
asyncio.run(blueprint_instance.non_interactive_mode_async(args.instruction, stream=args.stream))
|
549
|
-
else:
|
550
|
-
blueprint_instance.interactive_mode(stream=args.stream)
|
551
|
-
except Exception as e:
|
552
|
-
logger.critical(f"Blueprint execution failed: {e}", exc_info=True)
|
553
|
-
print(f"Critical Error: {e}", file=original_stderr)
|
554
|
-
finally:
|
555
|
-
if not args.debug and dev_null is not None:
|
556
|
-
sys.stderr = original_stderr
|
557
|
-
dev_null.close()
|
558
|
-
logger.debug("Restored stderr.")
|
559
|
-
logger.info("Blueprint execution finished.")
|
154
|
+
def get_llm_profile(self) -> Dict:
|
155
|
+
"""Returns the configuration for the LLM profile being used."""
|
156
|
+
if not hasattr(self, 'llm_profile'):
|
157
|
+
logger.warning("LLM profile was not set during initialization, returning empty dict.")
|
158
|
+
return {}
|
159
|
+
return self.llm_profile
|
560
160
|
|
561
|
-
if __name__ == "__main__":
|
562
|
-
BlueprintBase.main()
|