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.
Files changed (217) hide show
  1. open_swarm-0.1.1743362777.dist-info/METADATA +217 -0
  2. open_swarm-0.1.1743362777.dist-info/RECORD +260 -0
  3. {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/WHEEL +1 -2
  4. open_swarm-0.1.1743362777.dist-info/entry_points.txt +2 -0
  5. swarm/__init__.py +0 -2
  6. swarm/auth.py +53 -49
  7. swarm/blueprints/README.md +67 -0
  8. swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +412 -0
  9. swarm/blueprints/chatbot/blueprint_chatbot.py +98 -0
  10. swarm/blueprints/chatbot/templates/chatbot/chatbot.html +33 -0
  11. swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +183 -0
  12. swarm/blueprints/dilbot_universe/blueprint_dilbot_universe.py +285 -0
  13. swarm/blueprints/divine_code/__init__.py +0 -0
  14. swarm/blueprints/divine_code/apps.py +11 -0
  15. swarm/blueprints/divine_code/blueprint_divine_code.py +219 -0
  16. swarm/blueprints/django_chat/apps.py +6 -0
  17. swarm/blueprints/django_chat/blueprint_django_chat.py +84 -0
  18. swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +37 -0
  19. swarm/blueprints/django_chat/urls.py +8 -0
  20. swarm/blueprints/django_chat/views.py +32 -0
  21. swarm/blueprints/echocraft/blueprint_echocraft.py +44 -0
  22. swarm/blueprints/family_ties/apps.py +11 -0
  23. swarm/blueprints/family_ties/blueprint_family_ties.py +152 -0
  24. swarm/blueprints/family_ties/models.py +19 -0
  25. swarm/blueprints/family_ties/serializers.py +7 -0
  26. swarm/blueprints/family_ties/settings.py +16 -0
  27. swarm/blueprints/family_ties/urls.py +10 -0
  28. swarm/blueprints/family_ties/views.py +26 -0
  29. swarm/blueprints/flock/__init__.py +0 -0
  30. swarm/blueprints/gaggle/blueprint_gaggle.py +184 -0
  31. swarm/blueprints/gotchaman/blueprint_gotchaman.py +232 -0
  32. swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +133 -0
  33. swarm/blueprints/messenger/templates/messenger/messenger.html +46 -0
  34. swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +234 -0
  35. swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +248 -0
  36. swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +156 -0
  37. swarm/blueprints/omniplex/blueprint_omniplex.py +221 -0
  38. swarm/blueprints/rue_code/__init__.py +0 -0
  39. swarm/blueprints/rue_code/blueprint_rue_code.py +291 -0
  40. swarm/blueprints/suggestion/blueprint_suggestion.py +110 -0
  41. swarm/blueprints/unapologetic_press/blueprint_unapologetic_press.py +298 -0
  42. swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
  43. swarm/blueprints/whiskeytango_foxtrot/apps.py +11 -0
  44. swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +256 -0
  45. swarm/extensions/blueprint/__init__.py +30 -15
  46. swarm/extensions/blueprint/agent_utils.py +16 -40
  47. swarm/extensions/blueprint/blueprint_base.py +141 -543
  48. swarm/extensions/blueprint/blueprint_discovery.py +112 -98
  49. swarm/extensions/blueprint/cli_handler.py +185 -0
  50. swarm/extensions/blueprint/config_loader.py +122 -0
  51. swarm/extensions/blueprint/django_utils.py +181 -79
  52. swarm/extensions/blueprint/interactive_mode.py +1 -1
  53. swarm/extensions/config/config_loader.py +83 -200
  54. swarm/extensions/launchers/build_swarm_wrapper.py +0 -0
  55. swarm/extensions/launchers/swarm_cli.py +199 -287
  56. swarm/llm/chat_completion.py +26 -55
  57. swarm/management/__init__.py +0 -0
  58. swarm/management/commands/__init__.py +0 -0
  59. swarm/management/commands/runserver.py +58 -0
  60. swarm/permissions.py +38 -0
  61. swarm/serializers.py +96 -5
  62. swarm/settings.py +95 -110
  63. swarm/static/contrib/fonts/fontawesome-webfont.ttf +7 -0
  64. swarm/static/contrib/fonts/fontawesome-webfont.woff +7 -0
  65. swarm/static/contrib/fonts/fontawesome-webfont.woff2 +7 -0
  66. swarm/static/contrib/markedjs/marked.min.js +6 -0
  67. swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +27 -0
  68. swarm/static/contrib/tabler-icons/alert-triangle.svg +21 -0
  69. swarm/static/contrib/tabler-icons/archive.svg +21 -0
  70. swarm/static/contrib/tabler-icons/artboard.svg +27 -0
  71. swarm/static/contrib/tabler-icons/automatic-gearbox.svg +23 -0
  72. swarm/static/contrib/tabler-icons/box-multiple.svg +19 -0
  73. swarm/static/contrib/tabler-icons/carambola.svg +19 -0
  74. swarm/static/contrib/tabler-icons/copy.svg +20 -0
  75. swarm/static/contrib/tabler-icons/download.svg +21 -0
  76. swarm/static/contrib/tabler-icons/edit.svg +21 -0
  77. swarm/static/contrib/tabler-icons/filled/carambola.svg +13 -0
  78. swarm/static/contrib/tabler-icons/filled/paint.svg +13 -0
  79. swarm/static/contrib/tabler-icons/headset.svg +22 -0
  80. swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +21 -0
  81. swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +21 -0
  82. swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +21 -0
  83. swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +21 -0
  84. swarm/static/contrib/tabler-icons/message-chatbot.svg +22 -0
  85. swarm/static/contrib/tabler-icons/message-star.svg +22 -0
  86. swarm/static/contrib/tabler-icons/message-x.svg +23 -0
  87. swarm/static/contrib/tabler-icons/message.svg +21 -0
  88. swarm/static/contrib/tabler-icons/paperclip.svg +18 -0
  89. swarm/static/contrib/tabler-icons/playlist-add.svg +22 -0
  90. swarm/static/contrib/tabler-icons/robot.svg +26 -0
  91. swarm/static/contrib/tabler-icons/search.svg +19 -0
  92. swarm/static/contrib/tabler-icons/settings.svg +20 -0
  93. swarm/static/contrib/tabler-icons/thumb-down.svg +19 -0
  94. swarm/static/contrib/tabler-icons/thumb-up.svg +19 -0
  95. swarm/static/css/dropdown.css +22 -0
  96. swarm/static/htmx/htmx.min.js +0 -0
  97. swarm/static/js/dropdown.js +23 -0
  98. swarm/static/rest_mode/css/base.css +470 -0
  99. swarm/static/rest_mode/css/chat-history.css +286 -0
  100. swarm/static/rest_mode/css/chat.css +251 -0
  101. swarm/static/rest_mode/css/chatbot.css +74 -0
  102. swarm/static/rest_mode/css/chatgpt.css +62 -0
  103. swarm/static/rest_mode/css/colors/corporate.css +74 -0
  104. swarm/static/rest_mode/css/colors/pastel.css +81 -0
  105. swarm/static/rest_mode/css/colors/tropical.css +82 -0
  106. swarm/static/rest_mode/css/general.css +142 -0
  107. swarm/static/rest_mode/css/layout.css +167 -0
  108. swarm/static/rest_mode/css/layouts/messenger-layout.css +17 -0
  109. swarm/static/rest_mode/css/layouts/minimalist-layout.css +57 -0
  110. swarm/static/rest_mode/css/layouts/mobile-layout.css +8 -0
  111. swarm/static/rest_mode/css/messages.css +84 -0
  112. swarm/static/rest_mode/css/messenger.css +135 -0
  113. swarm/static/rest_mode/css/settings.css +91 -0
  114. swarm/static/rest_mode/css/simple.css +44 -0
  115. swarm/static/rest_mode/css/slack.css +58 -0
  116. swarm/static/rest_mode/css/style.css +156 -0
  117. swarm/static/rest_mode/css/theme.css +30 -0
  118. swarm/static/rest_mode/css/toast.css +40 -0
  119. swarm/static/rest_mode/js/auth.js +9 -0
  120. swarm/static/rest_mode/js/blueprint.js +41 -0
  121. swarm/static/rest_mode/js/blueprintUtils.js +12 -0
  122. swarm/static/rest_mode/js/chatLogic.js +79 -0
  123. swarm/static/rest_mode/js/debug.js +63 -0
  124. swarm/static/rest_mode/js/events.js +98 -0
  125. swarm/static/rest_mode/js/main.js +19 -0
  126. swarm/static/rest_mode/js/messages.js +264 -0
  127. swarm/static/rest_mode/js/messengerLogic.js +355 -0
  128. swarm/static/rest_mode/js/modules/apiService.js +84 -0
  129. swarm/static/rest_mode/js/modules/blueprintManager.js +162 -0
  130. swarm/static/rest_mode/js/modules/chatHistory.js +110 -0
  131. swarm/static/rest_mode/js/modules/debugLogger.js +14 -0
  132. swarm/static/rest_mode/js/modules/eventHandlers.js +107 -0
  133. swarm/static/rest_mode/js/modules/messageProcessor.js +120 -0
  134. swarm/static/rest_mode/js/modules/state.js +7 -0
  135. swarm/static/rest_mode/js/modules/userInteractions.js +29 -0
  136. swarm/static/rest_mode/js/modules/validation.js +23 -0
  137. swarm/static/rest_mode/js/rendering.js +119 -0
  138. swarm/static/rest_mode/js/settings.js +130 -0
  139. swarm/static/rest_mode/js/sidebar.js +94 -0
  140. swarm/static/rest_mode/js/simpleLogic.js +37 -0
  141. swarm/static/rest_mode/js/slackLogic.js +66 -0
  142. swarm/static/rest_mode/js/splash.js +76 -0
  143. swarm/static/rest_mode/js/theme.js +111 -0
  144. swarm/static/rest_mode/js/toast.js +36 -0
  145. swarm/static/rest_mode/js/ui.js +265 -0
  146. swarm/static/rest_mode/js/validation.js +57 -0
  147. swarm/static/rest_mode/svg/animated_spinner.svg +12 -0
  148. swarm/static/rest_mode/svg/arrow_down.svg +5 -0
  149. swarm/static/rest_mode/svg/arrow_left.svg +5 -0
  150. swarm/static/rest_mode/svg/arrow_right.svg +5 -0
  151. swarm/static/rest_mode/svg/arrow_up.svg +5 -0
  152. swarm/static/rest_mode/svg/attach.svg +8 -0
  153. swarm/static/rest_mode/svg/avatar.svg +7 -0
  154. swarm/static/rest_mode/svg/canvas.svg +6 -0
  155. swarm/static/rest_mode/svg/chat_history.svg +4 -0
  156. swarm/static/rest_mode/svg/close.svg +5 -0
  157. swarm/static/rest_mode/svg/copy.svg +4 -0
  158. swarm/static/rest_mode/svg/dark_mode.svg +3 -0
  159. swarm/static/rest_mode/svg/edit.svg +5 -0
  160. swarm/static/rest_mode/svg/layout.svg +9 -0
  161. swarm/static/rest_mode/svg/logo.svg +29 -0
  162. swarm/static/rest_mode/svg/logout.svg +5 -0
  163. swarm/static/rest_mode/svg/mobile.svg +5 -0
  164. swarm/static/rest_mode/svg/new_chat.svg +4 -0
  165. swarm/static/rest_mode/svg/not_visible.svg +5 -0
  166. swarm/static/rest_mode/svg/plus.svg +7 -0
  167. swarm/static/rest_mode/svg/run_code.svg +6 -0
  168. swarm/static/rest_mode/svg/save.svg +4 -0
  169. swarm/static/rest_mode/svg/search.svg +6 -0
  170. swarm/static/rest_mode/svg/settings.svg +4 -0
  171. swarm/static/rest_mode/svg/speaker.svg +5 -0
  172. swarm/static/rest_mode/svg/stop.svg +6 -0
  173. swarm/static/rest_mode/svg/thumbs_down.svg +3 -0
  174. swarm/static/rest_mode/svg/thumbs_up.svg +3 -0
  175. swarm/static/rest_mode/svg/toggle_off.svg +6 -0
  176. swarm/static/rest_mode/svg/toggle_on.svg +6 -0
  177. swarm/static/rest_mode/svg/trash.svg +10 -0
  178. swarm/static/rest_mode/svg/undo.svg +3 -0
  179. swarm/static/rest_mode/svg/visible.svg +8 -0
  180. swarm/static/rest_mode/svg/voice.svg +10 -0
  181. swarm/templates/account/login.html +22 -0
  182. swarm/templates/account/signup.html +32 -0
  183. swarm/templates/base.html +30 -0
  184. swarm/templates/chat.html +43 -0
  185. swarm/templates/index.html +35 -0
  186. swarm/templates/rest_mode/components/chat_sidebar.html +55 -0
  187. swarm/templates/rest_mode/components/header.html +45 -0
  188. swarm/templates/rest_mode/components/main_chat_pane.html +41 -0
  189. swarm/templates/rest_mode/components/settings_dialog.html +97 -0
  190. swarm/templates/rest_mode/components/splash_screen.html +7 -0
  191. swarm/templates/rest_mode/components/top_bar.html +28 -0
  192. swarm/templates/rest_mode/message_ui.html +50 -0
  193. swarm/templates/rest_mode/slackbot.html +30 -0
  194. swarm/templates/simple_blueprint_page.html +24 -0
  195. swarm/templates/websocket_partials/final_system_message.html +3 -0
  196. swarm/templates/websocket_partials/system_message.html +4 -0
  197. swarm/templates/websocket_partials/user_message.html +5 -0
  198. swarm/urls.py +57 -74
  199. swarm/utils/log_utils.py +63 -0
  200. swarm/views/api_views.py +48 -39
  201. swarm/views/chat_views.py +156 -70
  202. swarm/views/core_views.py +85 -90
  203. swarm/views/model_views.py +64 -121
  204. swarm/views/utils.py +65 -441
  205. open_swarm-0.1.1743070217.dist-info/METADATA +0 -258
  206. open_swarm-0.1.1743070217.dist-info/RECORD +0 -89
  207. open_swarm-0.1.1743070217.dist-info/entry_points.txt +0 -3
  208. open_swarm-0.1.1743070217.dist-info/top_level.txt +0 -1
  209. swarm/agent/agent.py +0 -49
  210. swarm/core.py +0 -326
  211. swarm/extensions/mcp/__init__.py +0 -1
  212. swarm/extensions/mcp/cache_utils.py +0 -36
  213. swarm/extensions/mcp/mcp_client.py +0 -341
  214. swarm/extensions/mcp/mcp_constants.py +0 -7
  215. swarm/extensions/mcp/mcp_tool_provider.py +0 -110
  216. swarm/types.py +0 -126
  217. {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,10 @@
1
+ from django.urls import path, include
2
+ from rest_framework.routers import DefaultRouter
3
+ from .views import AgentInstructionViewSet
4
+
5
+ router = DefaultRouter()
6
+ router.register(r'instructions', AgentInstructionViewSet, basename='instructions')
7
+
8
+ urlpatterns = [
9
+ path('', include(router.urls)),
10
+ ]
@@ -0,0 +1,26 @@
1
+ from rest_framework.viewsets import ModelViewSet
2
+ from rest_framework.permissions import AllowAny
3
+ import os
4
+ from swarm.auth import EnvOrTokenAuthentication
5
+ from blueprints.chc.models import AgentInstruction
6
+ from blueprints.chc.serializers import AgentInstructionSerializer
7
+
8
+ class AgentInstructionViewSet(ModelViewSet):
9
+ authentication_classes = [EnvOrTokenAuthentication]
10
+ permission_classes = [AllowAny]
11
+ queryset = AgentInstruction.objects.all()
12
+ serializer_class = AgentInstructionSerializer
13
+
14
+ def get_permissions(self):
15
+ if os.getenv("ENABLE_API_AUTH", "false").lower() in ("true", "1", "t"):
16
+ from rest_framework.permissions import IsAuthenticated
17
+ return [IsAuthenticated()]
18
+ return [AllowAny()]
19
+
20
+ def perform_authentication(self, request):
21
+ super().perform_authentication(request)
22
+ if not request.user or not request.user.is_authenticated:
23
+ from rest_framework.exceptions import AuthenticationFailed
24
+ raise AuthenticationFailed("Invalid token.")
25
+
26
+ __all__ = ["AgentInstructionViewSet"]
File without changes
@@ -0,0 +1,184 @@
1
+ import logging
2
+ import os # Added os import
3
+ import sys
4
+ from typing import List, Dict, Any, Optional, ClassVar
5
+
6
+ # Ensure src is in path for BlueprintBase import (if needed, adjust path)
7
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
8
+ src_path = os.path.join(project_root, 'src')
9
+ if src_path not in sys.path: sys.path.insert(0, src_path)
10
+
11
+ try:
12
+ from agents import Agent, Tool, function_tool, Runner
13
+ from agents.mcp import MCPServer
14
+ from agents.models.interface import Model
15
+ from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
16
+ from openai import AsyncOpenAI
17
+ # Corrected Import: Relative path within the package
18
+ from swarm.extensions.blueprint.blueprint_base import BlueprintBase
19
+ except ImportError as e:
20
+ print(f"ERROR: Import failed in blueprint_gaggle: {e}. Check 'openai-agents' install and project structure.")
21
+ print(f"sys.path: {sys.path}")
22
+ sys.exit(1)
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ # --- Tools ---
27
+ @function_tool
28
+ def create_story_outline(topic: str) -> str:
29
+ """Generates a basic story outline based on a topic."""
30
+ logger.info(f"Tool: Generating outline for: {topic}") # Keep INFO for tool exec start
31
+ # In a real scenario, this might involve more logic or an LLM call.
32
+ # Adding slight variation for clarity
33
+ outline = f"Story Outline for '{topic}':\n1. Beginning: Introduce characters and setting.\n2. Middle: Develop conflict and rising action.\n3. Climax: The peak of the conflict.\n4. End: Resolution and aftermath."
34
+ logger.debug(f"Generated outline: {outline}")
35
+ return outline
36
+
37
+ @function_tool
38
+ def write_story_part(part_name: str, outline: str, previous_parts: str) -> str:
39
+ """Writes a specific part of the story using the outline and previous context."""
40
+ logger.info(f"Tool: Writing story part: {part_name}") # Keep INFO for tool exec start
41
+ # Simple placeholder implementation
42
+ content = f"## {part_name}\n\nThis is the draft content for the '{part_name}' section. It follows:\n'{previous_parts[:100]}...' \nIt should align with the outline:\n'{outline}'"
43
+ logger.debug(f"Generated content for {part_name}: {content[:100]}...")
44
+ return content
45
+
46
+ @function_tool
47
+ def edit_story(full_story: str, edit_instructions: str) -> str:
48
+ """Edits the complete story based on instructions."""
49
+ logger.info(f"Tool: Editing story with instructions: {edit_instructions}") # Keep INFO for tool exec start
50
+ # Simple placeholder for editing
51
+ edited_content = f"*** Edited Story Draft ***\n(Based on instructions: '{edit_instructions}')\n\n{full_story}\n\n[Editor's Notes: Minor tweaks applied for flow.]"
52
+ logger.debug("Editing complete.")
53
+ return edited_content
54
+
55
+ # --- Blueprint Definition ---
56
+ class GaggleBlueprint(BlueprintBase):
57
+ """A multi-agent blueprint using a Coordinator, Planner, Writer, and Editor for collaborative story writing."""
58
+ metadata: ClassVar[Dict[str, Any]] = {
59
+ "name": "GaggleBlueprint",
60
+ "title": "Gaggle Story Writing Team",
61
+ "description": "A multi-agent blueprint for collaborative story writing using Planner, Writer, and Editor roles coordinated by a central agent.",
62
+ "version": "1.2.0", # Updated version
63
+ "author": "Open Swarm Team (Refactored)",
64
+ "tags": ["writing", "collaboration", "multi-agent", "storytelling"],
65
+ "required_mcp_servers": [],
66
+ "env_vars": [],
67
+ }
68
+
69
+ # Caches
70
+ _openai_client_cache: Dict[str, AsyncOpenAI] = {}
71
+ _model_instance_cache: Dict[str, Model] = {}
72
+
73
+ # --- Model Instantiation Helper --- (Standard helper)
74
+ def _get_model_instance(self, profile_name: str) -> Model:
75
+ """Retrieves or creates an LLM Model instance."""
76
+ # ... (Implementation is the same as in previous refactors, e.g., Dilbot's) ...
77
+ if profile_name in self._model_instance_cache:
78
+ logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
79
+ return self._model_instance_cache[profile_name]
80
+ logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
81
+ profile_data = self.get_llm_profile(profile_name)
82
+ if not profile_data:
83
+ logger.critical(f"LLM profile '{profile_name}' (or 'default') not found.")
84
+ raise ValueError(f"Missing LLM profile configuration for '{profile_name}' or 'default'.")
85
+ provider = profile_data.get("provider", "openai").lower()
86
+ model_name = profile_data.get("model")
87
+ if not model_name:
88
+ logger.critical(f"LLM profile '{profile_name}' missing 'model' key.")
89
+ raise ValueError(f"Missing 'model' key in LLM profile '{profile_name}'.")
90
+ if provider != "openai":
91
+ logger.error(f"Unsupported LLM provider '{provider}'.")
92
+ raise ValueError(f"Unsupported LLM provider: {provider}")
93
+ client_cache_key = f"{provider}_{profile_data.get('base_url')}"
94
+ if client_cache_key not in self._openai_client_cache:
95
+ client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
96
+ filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
97
+ log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
98
+ logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
99
+ try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
100
+ except Exception as e: raise ValueError(f"Failed to init OpenAI client: {e}") from e
101
+ client = self._openai_client_cache[client_cache_key]
102
+ logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
103
+ try:
104
+ model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
105
+ self._model_instance_cache[profile_name] = model_instance
106
+ return model_instance
107
+ except Exception as e: raise ValueError(f"Failed to init LLM provider: {e}") from e
108
+
109
+
110
+ def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
111
+ """Creates the story writing team and returns the Coordinator."""
112
+ logger.debug("Creating Gaggle Story Writing Team...")
113
+ self._model_instance_cache = {}
114
+ self._openai_client_cache = {}
115
+
116
+ default_profile_name = self.config.get("llm_profile", "default")
117
+ logger.debug(f"Using LLM profile '{default_profile_name}' for Gaggle agents.")
118
+ model_instance = self._get_model_instance(default_profile_name)
119
+
120
+ # --- Define Agent Instructions ---
121
+ planner_instructions = "You are the Planner. Your goal is to take a user's story topic and create a coherent outline using the 'create_story_outline' tool. Respond ONLY with the generated outline string."
122
+ writer_instructions = "You are a Writer. You receive a story part name (e.g., 'Introduction', 'Climax'), the full outline, and any previously written parts. Write the content for ONLY your assigned part using the 'write_story_part' tool, ensuring it flows logically from previous parts and fits the outline. Respond ONLY with the text generated for your part."
123
+ editor_instructions = "You are the Editor. You receive the complete draft of the story and editing instructions (e.g., 'make it funnier', 'check for consistency'). Use the 'edit_story' tool to revise the text. Respond ONLY with the final, edited story string."
124
+ coordinator_instructions = (
125
+ "You are the Coordinator for a team of writing agents (Planner, Writer, Editor).\n"
126
+ "1. Receive the user's story topic.\n"
127
+ "2. Delegate to the Planner tool to get a story outline.\n"
128
+ "3. Identify the story parts from the outline (e.g., Beginning, Middle, Climax, End).\n"
129
+ "4. Sequentially delegate writing each part to the Writer tool. Provide the part name, the full outline, and all previously written parts as context for the Writer.\n"
130
+ "5. Accumulate the written parts into a full draft.\n"
131
+ "6. Delegate the complete draft to the Editor tool with simple instructions like 'Ensure coherence and flow'.\n"
132
+ "7. Return the final, edited story as the result."
133
+ )
134
+
135
+ # Instantiate agents, passing their specific function tools
136
+ planner_agent = Agent(
137
+ name="Planner",
138
+ instructions=planner_instructions,
139
+ model=model_instance,
140
+ tools=[create_story_outline],
141
+ mcp_servers=mcp_servers
142
+ )
143
+ writer_agent = Agent(
144
+ name="Writer",
145
+ instructions=writer_instructions,
146
+ model=model_instance,
147
+ tools=[write_story_part],
148
+ mcp_servers=mcp_servers
149
+ )
150
+ editor_agent = Agent(
151
+ name="Editor",
152
+ instructions=editor_instructions,
153
+ model=model_instance,
154
+ tools=[edit_story],
155
+ mcp_servers=mcp_servers
156
+ )
157
+
158
+ # Instantiate Coordinator, giving it the other agents as tools
159
+ coordinator_agent = Agent(
160
+ name="Coordinator",
161
+ instructions=coordinator_instructions,
162
+ model=model_instance, # Coordinator also needs a model
163
+ tools=[
164
+ planner_agent.as_tool(
165
+ tool_name="Planner",
166
+ tool_description="Delegate creating a story outline based on a topic."
167
+ ),
168
+ writer_agent.as_tool(
169
+ tool_name="Writer",
170
+ tool_description="Delegate writing a specific part of the story. Requires part_name, outline, and previous_parts."
171
+ ),
172
+ editor_agent.as_tool(
173
+ tool_name="Editor",
174
+ tool_description="Delegate editing the full story draft. Requires full_story and edit_instructions."
175
+ ),
176
+ ],
177
+ mcp_servers=mcp_servers
178
+ )
179
+
180
+ logger.debug("Gaggle Story Writing Team created. Coordinator is the starting agent.")
181
+ return coordinator_agent
182
+
183
+ if __name__ == "__main__":
184
+ GaggleBlueprint.main()
@@ -0,0 +1,232 @@
1
+ """
2
+ Gotchaman: CLI Automation Blueprint
3
+
4
+ This blueprint provides CLI automation capabilities using a team of agents:
5
+ - Ken (Coordinator)
6
+ - Joe (Runner - executes commands/file ops)
7
+ - Jun (Logger - hypothetical monitoring via MCP)
8
+ - Jinpei (Advisor - hypothetical suggestion via MCP)
9
+ - Ryu (Reviewer - hypothetical insights via MCP)
10
+
11
+ Uses BlueprintBase, @function_tool for local commands, and agent-as-tool delegation.
12
+ """
13
+
14
+ import os
15
+ import sys
16
+ import logging
17
+ import subprocess
18
+ import shlex # For safe command splitting
19
+ from pathlib import Path # Use pathlib
20
+ from typing import Dict, Any, List, ClassVar, Optional
21
+
22
+ # Ensure src is in path for BlueprintBase import
23
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
24
+ src_path = os.path.join(project_root, 'src')
25
+ if src_path not in sys.path: sys.path.insert(0, src_path)
26
+
27
+ try:
28
+ from agents import Agent, Tool, function_tool, Runner
29
+ from agents.mcp import MCPServer
30
+ from agents.models.interface import Model
31
+ from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
32
+ from openai import AsyncOpenAI
33
+ from swarm.extensions.blueprint.blueprint_base import BlueprintBase
34
+ except ImportError as e:
35
+ print(f"ERROR: Import failed in GotchamanBlueprint: {e}. Check dependencies.")
36
+ print(f"sys.path: {sys.path}")
37
+ sys.exit(1)
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+ # --- Function Tools ---
42
+ @function_tool
43
+ def execute_command(command: str) -> str:
44
+ """Executes a shell command and returns its stdout and stderr."""
45
+ if not command: return "Error: No command provided."
46
+ logger.info(f"Tool: Executing command: {command}")
47
+ try:
48
+ # Use shell=True cautiously, consider splitting if possible for safer execution
49
+ result = subprocess.run(
50
+ command,
51
+ shell=True, # Be cautious with shell=True
52
+ check=False, # Capture output even on error
53
+ capture_output=True,
54
+ text=True,
55
+ timeout=120
56
+ )
57
+ output = f"Exit Code: {result.returncode}\nSTDOUT:\n{result.stdout.strip()}\nSTDERR:\n{result.stderr.strip()}"
58
+ if result.returncode == 0:
59
+ logger.debug(f"Command successful:\n{output}")
60
+ return f"OK: Command executed.\n{output}"
61
+ else:
62
+ logger.error(f"Command failed:\n{output}")
63
+ return f"Error: Command failed.\n{output}"
64
+ except FileNotFoundError:
65
+ # This error is less likely with shell=True unless the shell itself is missing
66
+ logger.error(f"Error executing command '{command}': Shell or command not found.")
67
+ return f"Error: Shell or command '{command.split()[0]}' not found."
68
+ except subprocess.TimeoutExpired:
69
+ logger.error(f"Command '{command}' timed out.")
70
+ return f"Error: Command '{command}' timed out."
71
+ except Exception as e:
72
+ logger.error(f"Unexpected error executing command '{command}': {e}", exc_info=logger.level <= logging.DEBUG)
73
+ return f"Error: Unexpected error during command execution: {e}"
74
+
75
+ @function_tool
76
+ def read_file(path: str) -> str:
77
+ """Reads the content of a file at the specified path."""
78
+ if not path: return "Error: No file path provided."
79
+ logger.info(f"Tool: Reading file at: {path}")
80
+ try:
81
+ file_path = Path(path).resolve()
82
+ # Optional: Add security check to ensure path is within allowed bounds if needed
83
+ # cwd = Path.cwd()
84
+ # if not str(file_path).startswith(str(cwd)):
85
+ # logger.warning(f"Attempt to read file outside current working directory: {path}")
86
+ # return f"Error: Access denied to path: {path}"
87
+ if not file_path.is_file():
88
+ logger.error(f"File not found at: {file_path}")
89
+ return f"Error: File not found at path: {path}"
90
+ content = file_path.read_text(encoding="utf-8")
91
+ logger.debug(f"Read {len(content)} characters from {file_path}.")
92
+ return f"OK: Content of {path}:\n{content}"
93
+ except Exception as e:
94
+ logger.error(f"Error reading file at {path}: {e}", exc_info=logger.level <= logging.DEBUG)
95
+ return f"Error reading file '{path}': {e}"
96
+
97
+ @function_tool
98
+ def write_file(path: str, content: str) -> str:
99
+ """Writes content to a file at the specified path, overwriting if it exists."""
100
+ if not path: return "Error: No file path provided."
101
+ logger.info(f"Tool: Writing {len(content)} characters to file at: {path}")
102
+ try:
103
+ file_path = Path(path).resolve()
104
+ # Optional: Add security check
105
+ # cwd = Path.cwd()
106
+ # if not str(file_path).startswith(str(cwd)):
107
+ # logger.warning(f"Attempt to write file outside current working directory: {path}")
108
+ # return f"Error: Access denied to path: {path}"
109
+
110
+ file_path.parent.mkdir(parents=True, exist_ok=True) # Ensure directory exists
111
+ file_path.write_text(content, encoding="utf-8")
112
+ logger.debug(f"Successfully wrote to {file_path}.")
113
+ return f"OK: Successfully wrote to {path}."
114
+ except Exception as e:
115
+ logger.error(f"Error writing file at {path}: {e}", exc_info=logger.level <= logging.DEBUG)
116
+ return f"Error writing file '{path}': {e}"
117
+
118
+ # --- Define the Blueprint ---
119
+ class GotchamanBlueprint(BlueprintBase):
120
+ """Gotchaman: CLI Automation Blueprint using BlueprintBase."""
121
+
122
+ metadata: ClassVar[Dict[str, Any]] = {
123
+ "name": "GotchamanBlueprint",
124
+ "title": "Gotchaman: CLI Automation",
125
+ "description": (
126
+ "A blueprint for automating CLI tasks using a team of agents (Ken, Joe, Jun, Jinpei, Ryu) "
127
+ "with specific roles and MCP/tool access."
128
+ ),
129
+ "version": "1.1.0", # Refactored version
130
+ "author": "Open Swarm Team (Refactored)",
131
+ "tags": ["cli", "automation", "multi-agent", "mcp", "slack", "monday"],
132
+ # List only servers directly used by refactored agents
133
+ "required_mcp_servers": ["slack", "mondayDotCom", "basic-memory", "mcp-npx-fetch"],
134
+ "env_vars": ["SLACK_API_KEY", "MONDAY_API_KEY"]
135
+ }
136
+
137
+ # Caches
138
+ _openai_client_cache: Dict[str, AsyncOpenAI] = {}
139
+ _model_instance_cache: Dict[str, Model] = {}
140
+
141
+ # --- Model Instantiation Helper --- (Standard helper)
142
+ def _get_model_instance(self, profile_name: str) -> Model:
143
+ """Retrieves or creates an LLM Model instance."""
144
+ # ... (Implementation is the same as previous refactors) ...
145
+ if profile_name in self._model_instance_cache:
146
+ logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
147
+ return self._model_instance_cache[profile_name]
148
+ logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
149
+ profile_data = self.get_llm_profile(profile_name)
150
+ if not profile_data: raise ValueError(f"Missing LLM profile '{profile_name}'.")
151
+ provider = profile_data.get("provider", "openai").lower()
152
+ model_name = profile_data.get("model")
153
+ if not model_name: raise ValueError(f"Missing 'model' in profile '{profile_name}'.")
154
+ if provider != "openai": raise ValueError(f"Unsupported provider: {provider}")
155
+ client_cache_key = f"{provider}_{profile_data.get('base_url')}"
156
+ if client_cache_key not in self._openai_client_cache:
157
+ client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
158
+ filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
159
+ log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
160
+ logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
161
+ try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
162
+ except Exception as e: raise ValueError(f"Failed to init client: {e}") from e
163
+ client = self._openai_client_cache[client_cache_key]
164
+ logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
165
+ try:
166
+ model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
167
+ self._model_instance_cache[profile_name] = model_instance
168
+ return model_instance
169
+ except Exception as e: raise ValueError(f"Failed to init LLM: {e}") from e
170
+
171
+
172
+ def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
173
+ """Creates the Gotchaman agent team and returns Ken (Coordinator)."""
174
+ logger.debug("Creating Gotchaman agent team...")
175
+ self._model_instance_cache = {}
176
+ self._openai_client_cache = {}
177
+
178
+ default_profile_name = self.config.get("llm_profile", "default")
179
+ logger.debug(f"Using LLM profile '{default_profile_name}' for Gotchaman agents.")
180
+ model_instance = self._get_model_instance(default_profile_name)
181
+
182
+ # Helper to filter MCP servers
183
+ def get_agent_mcps(names: List[str]) -> List[MCPServer]:
184
+ return [s for s in mcp_servers if s.name in names]
185
+
186
+ # --- Agent Instructions ---
187
+ ken_instructions = "You are Ken, the Coordinator for Gotchaman team. Your team: Joe (Runner), Jun (Logger), Jinpei (Advisor), and Ryu (Reviewer). Analyze the user request and delegate tasks to the appropriate agent using their Agent Tool. Synthesize their responses for the final output."
188
+ joe_instructions = "You are Joe, the Runner. Execute shell commands, read files, or write files using your function tools (`execute_command`, `read_file`, `write_file`) as requested by Ken. Report the outcome."
189
+ jun_instructions = "You are Jun, the Logger. Receive information or instructions from Ken. Use the `slack` MCP tool to log messages or feedback to a designated channel (details provided by Ken or pre-configured). Report success/failure of logging back to Ken."
190
+ jinpei_instructions = "You are Jinpei, the Advisor. Receive context from Ken. Use the `mcp-npx-fetch` MCP tool to fetch relevant documentation or examples based on the context. Provide concise suggestions or relevant snippets back to Ken."
191
+ ryu_instructions = "You are Ryu, the Reviewer. Receive outputs or code snippets from Ken. Use the `basic-memory` MCP tool to recall previous related outputs or guidelines if necessary. Provide insightful review comments or quality checks back to Ken."
192
+
193
+ # Instantiate agents
194
+ joe_agent = Agent(
195
+ name="Joe", model=model_instance, instructions=joe_instructions,
196
+ tools=[execute_command, read_file, write_file], # Joe has the function tools
197
+ mcp_servers=[] # Joe doesn't directly use MCPs listed
198
+ )
199
+ jun_agent = Agent(
200
+ name="Jun", model=model_instance, instructions=jun_instructions,
201
+ tools=[], # Jun uses MCP
202
+ mcp_servers=get_agent_mcps(["slack"])
203
+ )
204
+ jinpei_agent = Agent(
205
+ name="Jinpei", model=model_instance, instructions=jinpei_instructions,
206
+ tools=[], # Jinpei uses MCP
207
+ mcp_servers=get_agent_mcps(["mcp-npx-fetch"])
208
+ )
209
+ ryu_agent = Agent(
210
+ name="Ryu", model=model_instance, instructions=ryu_instructions,
211
+ tools=[], # Ryu uses MCP
212
+ mcp_servers=get_agent_mcps(["basic-memory"])
213
+ )
214
+ # Coordinator - Ken
215
+ ken_agent = Agent(
216
+ name="Ken", model=model_instance, instructions=ken_instructions,
217
+ tools=[ # Ken delegates to others via agent tools
218
+ joe_agent.as_tool(tool_name="Joe", tool_description="Delegate command execution or file operations to Joe."),
219
+ jun_agent.as_tool(tool_name="Jun", tool_description="Delegate logging tasks via Slack to Jun."),
220
+ jinpei_agent.as_tool(tool_name="Jinpei", tool_description="Delegate fetching docs/examples to Jinpei."),
221
+ ryu_agent.as_tool(tool_name="Ryu", tool_description="Delegate review tasks or recall past info via Ryu."),
222
+ ],
223
+ # Ken might use memory directly, or coordinate access via Ryu? Assigning for potential direct use.
224
+ mcp_servers=get_agent_mcps(["basic-memory"])
225
+ )
226
+
227
+ logger.debug("Gotchaman agents created. Starting with Ken.")
228
+ return ken_agent
229
+
230
+ # Standard Python entry point
231
+ if __name__ == "__main__":
232
+ GotchamanBlueprint.main()
@@ -0,0 +1,133 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from typing import Dict, Any, List, ClassVar, Optional
5
+
6
+ # Ensure src is in path for BlueprintBase import
7
+ project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
8
+ src_path = os.path.join(project_root, 'src')
9
+ if src_path not in sys.path: sys.path.insert(0, src_path)
10
+
11
+ try:
12
+ from agents import Agent, Tool, function_tool, Runner
13
+ from agents.mcp import MCPServer
14
+ from agents.models.interface import Model
15
+ from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
16
+ from openai import AsyncOpenAI
17
+ from swarm.extensions.blueprint.blueprint_base import BlueprintBase
18
+ except ImportError as e:
19
+ print(f"ERROR: Import failed in MCPDemoBlueprint: {e}. Check dependencies.")
20
+ print(f"sys.path: {sys.path}")
21
+ sys.exit(1)
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # --- Agent Instructions ---
26
+
27
+ sage_instructions_template = """
28
+ You are Sage, an agent demonstrating capabilities provided by MCP servers.
29
+ You have access to the following external capabilities via implicitly available MCP tools:
30
+ {mcp_tool_descriptions}
31
+
32
+ Your goal is to understand the user's request and utilize the appropriate MCP tool to fulfill it.
33
+ For example:
34
+ - To write to a file, use the 'filesystem' tool's 'write' function.
35
+ - To read from memory, use the 'memory' tool's 'get' function.
36
+ - To store in memory, use the 'memory' tool's 'set' function.
37
+ Explain what action you are taking via which tool and report the result.
38
+ """
39
+
40
+ # --- Define the Blueprint ---
41
+ class MCPDemoBlueprint(BlueprintBase):
42
+ """Demonstrates using filesystem and memory MCP servers."""
43
+ metadata: ClassVar[Dict[str, Any]] = {
44
+ "name": "MCPDemoBlueprint",
45
+ "title": "MCP Demo (Filesystem & Memory)",
46
+ "description": "A simple agent (Sage) demonstrating interaction with filesystem and memory MCP servers.",
47
+ "version": "1.1.0", # Refactored version
48
+ "author": "Open Swarm Team (Refactored)",
49
+ "tags": ["mcp", "filesystem", "memory", "demo"],
50
+ "required_mcp_servers": ["filesystem", "memory"],
51
+ "env_vars": ["ALLOWED_PATH"], # For filesystem MCP
52
+ }
53
+
54
+ # Caches
55
+ _openai_client_cache: Dict[str, AsyncOpenAI] = {}
56
+ _model_instance_cache: Dict[str, Model] = {}
57
+
58
+ # --- Model Instantiation Helper --- (Standard helper)
59
+ def _get_model_instance(self, profile_name: str) -> Model:
60
+ """Retrieves or creates an LLM Model instance."""
61
+ # ... (Implementation is the same as in previous refactors) ...
62
+ if profile_name in self._model_instance_cache:
63
+ logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
64
+ return self._model_instance_cache[profile_name]
65
+ logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
66
+ profile_data = self.get_llm_profile(profile_name)
67
+ if not profile_data: raise ValueError(f"Missing LLM profile '{profile_name}'.")
68
+ provider = profile_data.get("provider", "openai").lower()
69
+ model_name = profile_data.get("model")
70
+ if not model_name: raise ValueError(f"Missing 'model' in profile '{profile_name}'.")
71
+ if provider != "openai": raise ValueError(f"Unsupported provider: {provider}")
72
+ client_cache_key = f"{provider}_{profile_data.get('base_url')}"
73
+ if client_cache_key not in self._openai_client_cache:
74
+ client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
75
+ filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
76
+ log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
77
+ logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
78
+ try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
79
+ except Exception as e: raise ValueError(f"Failed to init client: {e}") from e
80
+ client = self._openai_client_cache[client_cache_key]
81
+ logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
82
+ try:
83
+ model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
84
+ self._model_instance_cache[profile_name] = model_instance
85
+ return model_instance
86
+ except Exception as e: raise ValueError(f"Failed to init LLM: {e}") from e
87
+
88
+ # --- Agent Creation ---
89
+ def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
90
+ """Creates the Sage agent, dynamically adding MCP server descriptions to its prompt."""
91
+ logger.debug("Creating MCP Demo agent (Sage)...")
92
+ self._model_instance_cache = {}
93
+ self._openai_client_cache = {}
94
+
95
+ default_profile_name = self.config.get("llm_profile", "default")
96
+ logger.debug(f"Using LLM profile '{default_profile_name}' for Sage.")
97
+ model_instance = self._get_model_instance(default_profile_name)
98
+
99
+ # Filter for required MCPs and get descriptions
100
+ required_names = self.metadata["required_mcp_servers"]
101
+ agent_mcps: List[MCPServer] = []
102
+ mcp_descriptions = []
103
+ for server in mcp_servers:
104
+ if server.name in required_names:
105
+ agent_mcps.append(server)
106
+ description = self.get_mcp_server_description(server.name)
107
+ mcp_descriptions.append(f"- {server.name}: {description or 'No description available.'}")
108
+
109
+ if len(agent_mcps) != len(required_names):
110
+ missing = set(required_names) - {s.name for s in agent_mcps}
111
+ logger.warning(f"Sage agent created, but missing required MCP server(s): {', '.join(missing)}. Functionality will be limited.")
112
+ # Continue with available servers
113
+
114
+ # Format descriptions for the prompt
115
+ mcp_tool_desc_str = "\n".join(mcp_descriptions) if mcp_descriptions else "No external tools available."
116
+ sage_instructions = sage_instructions_template.format(mcp_tool_descriptions=mcp_tool_desc_str)
117
+ logger.debug(f"Sage instructions generated:\n{sage_instructions}")
118
+
119
+ # Instantiate Sage
120
+ sage_agent = Agent(
121
+ name="Sage",
122
+ model=model_instance,
123
+ instructions=sage_instructions,
124
+ tools=[], # Tools come implicitly from assigned MCP servers
125
+ mcp_servers=agent_mcps # Pass the list of *started* server objects
126
+ )
127
+
128
+ logger.debug("Sage agent created.")
129
+ return sage_agent
130
+
131
+ # Standard Python entry point
132
+ if __name__ == "__main__":
133
+ MCPDemoBlueprint.main()
@@ -0,0 +1,46 @@
1
+ {% load static %}
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ {% include 'rest_mode/components/header.html' %}
6
+ <link rel="stylesheet" href="{% static 'rest_mode/css/messenger.css' %}">
7
+ <script>
8
+ window.STATIC_URLS = {
9
+ layoutSidebarLeftExpand: "{% static 'contrib/tabler-icons/layout-sidebar-left-expand.svg' %}",
10
+ layoutSidebarLeftCollapse: "{% static 'contrib/tabler-icons/layout-sidebar-left-collapse.svg' %}"
11
+ };
12
+ </script>
13
+ </head>
14
+ <body>
15
+ {% include 'rest_mode/components/top_bar.html' %}
16
+ {% include 'rest_mode/components/settings_dialog.html' %}
17
+ <div class="container" data-theme-color="corporate" data-theme-dark="false" data-theme-layout="messenger-layout">
18
+ <div class="side-panes chat-history-pane" id="channelPane">
19
+ <div class="chat-history-header">
20
+ <button class="input-button chat-history-button" id="toggleSidebar" aria-label="Toggle Sidebar">
21
+ <img src="{% static 'contrib/tabler-icons/layout-sidebar-left-collapse.svg' %}" alt="Collapse Sidebar">
22
+ </button>
23
+ <button class="input-button upload-button" id="settingsToggleButton" aria-label="Toggle Settings">
24
+ <img src="{% static 'contrib/tabler-icons/settings.svg' %}" alt="Settings">
25
+ </button>
26
+ <button class="input-button search-button" title="Search" aria-label="Search Channels">
27
+ <img src="{% static 'contrib/tabler-icons/search.svg' %}" alt="Search">
28
+ </button>
29
+ </div>
30
+ <h3>Channels</h3>
31
+ <ul id="channelList" class="chat-history-list">
32
+ <li data-blueprint-id="welcome">#Welcome to Open-Swarm</li>
33
+ </ul>
34
+ </div>
35
+ <div class="vertical-divider" id="divider-left"></div>
36
+ <div class="main-pane">
37
+ {% include 'rest_mode/components/main_chat_pane.html' %}
38
+ </div>
39
+ <div id="toastContainer"></div>
40
+ </div>
41
+ <div class="settings-overlay"></div>
42
+ <script type="module" src="{% static 'contrib/markedjs/marked.min.js' %}"></script>
43
+ <script type="module" src="{% static 'rest_mode/js/settings.js' %}"></script>
44
+ <script type="module" src="{% static 'rest_mode/js/messengerLogic.js' %}"></script>
45
+ </body>
46
+ </html>