open-swarm 0.1.1745274976__py3-none-any.whl → 0.1.1748636259__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 (295) hide show
  1. open_swarm-0.1.1748636259.dist-info/METADATA +188 -0
  2. open_swarm-0.1.1748636259.dist-info/RECORD +82 -0
  3. {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1748636259.dist-info}/WHEEL +2 -1
  4. open_swarm-0.1.1748636259.dist-info/entry_points.txt +3 -0
  5. open_swarm-0.1.1748636259.dist-info/top_level.txt +1 -0
  6. swarm/agent/agent.py +49 -0
  7. swarm/auth.py +48 -113
  8. swarm/consumers.py +0 -19
  9. swarm/extensions/blueprint/__init__.py +16 -30
  10. swarm/{core → extensions/blueprint}/agent_utils.py +1 -1
  11. swarm/extensions/blueprint/blueprint_base.py +458 -0
  12. swarm/extensions/blueprint/blueprint_discovery.py +112 -0
  13. swarm/extensions/blueprint/output_utils.py +95 -0
  14. swarm/{core → extensions/blueprint}/spinner.py +21 -30
  15. swarm/extensions/cli/cli_args.py +0 -6
  16. swarm/extensions/cli/commands/blueprint_management.py +9 -47
  17. swarm/extensions/cli/commands/config_management.py +6 -5
  18. swarm/extensions/cli/commands/edit_config.py +7 -16
  19. swarm/extensions/cli/commands/list_blueprints.py +1 -1
  20. swarm/extensions/cli/commands/validate_env.py +4 -11
  21. swarm/extensions/cli/commands/validate_envvars.py +6 -6
  22. swarm/extensions/cli/interactive_shell.py +2 -16
  23. swarm/extensions/config/config_loader.py +201 -107
  24. swarm/{core → extensions/config}/config_manager.py +38 -50
  25. swarm/{core → extensions/config}/server_config.py +0 -32
  26. swarm/extensions/launchers/build_launchers.py +14 -0
  27. swarm/{core → extensions/launchers}/build_swarm_wrapper.py +0 -0
  28. swarm/extensions/launchers/swarm_api.py +64 -8
  29. swarm/extensions/launchers/swarm_cli.py +300 -8
  30. swarm/llm/chat_completion.py +195 -0
  31. swarm/serializers.py +5 -96
  32. swarm/settings.py +111 -99
  33. swarm/urls.py +74 -57
  34. swarm/utils/context_utils.py +4 -10
  35. swarm/utils/general_utils.py +0 -21
  36. swarm/utils/redact.py +36 -23
  37. swarm/views/api_views.py +39 -48
  38. swarm/views/chat_views.py +70 -237
  39. swarm/views/core_views.py +87 -80
  40. swarm/views/model_views.py +121 -64
  41. swarm/views/utils.py +441 -65
  42. swarm/views/web_views.py +2 -2
  43. open_swarm-0.1.1745274976.dist-info/METADATA +0 -874
  44. open_swarm-0.1.1745274976.dist-info/RECORD +0 -318
  45. open_swarm-0.1.1745274976.dist-info/entry_points.txt +0 -4
  46. swarm/blueprints/README.md +0 -68
  47. swarm/blueprints/blueprint_audit_status.json +0 -27
  48. swarm/blueprints/chatbot/README.md +0 -40
  49. swarm/blueprints/chatbot/blueprint_chatbot.py +0 -471
  50. swarm/blueprints/chatbot/metadata.json +0 -23
  51. swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -33
  52. swarm/blueprints/chucks_angels/README.md +0 -11
  53. swarm/blueprints/chucks_angels/blueprint_chucks_angels.py +0 -7
  54. swarm/blueprints/chucks_angels/test_basic.py +0 -3
  55. swarm/blueprints/codey/CODEY.md +0 -15
  56. swarm/blueprints/codey/README.md +0 -115
  57. swarm/blueprints/codey/blueprint_codey.py +0 -1072
  58. swarm/blueprints/codey/codey_cli.py +0 -373
  59. swarm/blueprints/codey/instructions.md +0 -17
  60. swarm/blueprints/codey/metadata.json +0 -23
  61. swarm/blueprints/common/operation_box_utils.py +0 -83
  62. swarm/blueprints/digitalbutlers/README.md +0 -11
  63. swarm/blueprints/digitalbutlers/__init__.py +0 -1
  64. swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -7
  65. swarm/blueprints/digitalbutlers/test_basic.py +0 -3
  66. swarm/blueprints/divine_code/README.md +0 -3
  67. swarm/blueprints/divine_code/__init__.py +0 -10
  68. swarm/blueprints/divine_code/apps.py +0 -11
  69. swarm/blueprints/divine_code/blueprint_divine_code.py +0 -270
  70. swarm/blueprints/django_chat/apps.py +0 -6
  71. swarm/blueprints/django_chat/blueprint_django_chat.py +0 -268
  72. swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -37
  73. swarm/blueprints/django_chat/urls.py +0 -8
  74. swarm/blueprints/django_chat/views.py +0 -32
  75. swarm/blueprints/echocraft/blueprint_echocraft.py +0 -384
  76. swarm/blueprints/flock/README.md +0 -11
  77. swarm/blueprints/flock/__init__.py +0 -8
  78. swarm/blueprints/flock/blueprint_flock.py +0 -7
  79. swarm/blueprints/flock/test_basic.py +0 -3
  80. swarm/blueprints/geese/README.md +0 -97
  81. swarm/blueprints/geese/blueprint_geese.py +0 -803
  82. swarm/blueprints/geese/geese_cli.py +0 -102
  83. swarm/blueprints/jeeves/README.md +0 -41
  84. swarm/blueprints/jeeves/blueprint_jeeves.py +0 -722
  85. swarm/blueprints/jeeves/jeeves_cli.py +0 -55
  86. swarm/blueprints/jeeves/metadata.json +0 -24
  87. swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -473
  88. swarm/blueprints/messenger/templates/messenger/messenger.html +0 -46
  89. swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -423
  90. swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -340
  91. swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -265
  92. swarm/blueprints/omniplex/blueprint_omniplex.py +0 -298
  93. swarm/blueprints/poets/blueprint_poets.py +0 -546
  94. swarm/blueprints/poets/poets_cli.py +0 -23
  95. swarm/blueprints/rue_code/README.md +0 -8
  96. swarm/blueprints/rue_code/blueprint_rue_code.py +0 -448
  97. swarm/blueprints/rue_code/rue_code_cli.py +0 -43
  98. swarm/blueprints/stewie/apps.py +0 -12
  99. swarm/blueprints/stewie/blueprint_family_ties.py +0 -349
  100. swarm/blueprints/stewie/models.py +0 -19
  101. swarm/blueprints/stewie/serializers.py +0 -10
  102. swarm/blueprints/stewie/settings.py +0 -17
  103. swarm/blueprints/stewie/urls.py +0 -11
  104. swarm/blueprints/stewie/views.py +0 -26
  105. swarm/blueprints/suggestion/blueprint_suggestion.py +0 -222
  106. swarm/blueprints/whinge_surf/README.md +0 -22
  107. swarm/blueprints/whinge_surf/__init__.py +0 -1
  108. swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +0 -565
  109. swarm/blueprints/whinge_surf/whinge_surf_cli.py +0 -99
  110. swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
  111. swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -11
  112. swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -339
  113. swarm/blueprints/zeus/__init__.py +0 -2
  114. swarm/blueprints/zeus/apps.py +0 -4
  115. swarm/blueprints/zeus/blueprint_zeus.py +0 -270
  116. swarm/blueprints/zeus/zeus_cli.py +0 -13
  117. swarm/cli/async_input.py +0 -65
  118. swarm/cli/async_input_demo.py +0 -32
  119. swarm/core/blueprint_base.py +0 -769
  120. swarm/core/blueprint_discovery.py +0 -125
  121. swarm/core/blueprint_runner.py +0 -59
  122. swarm/core/blueprint_ux.py +0 -109
  123. swarm/core/build_launchers.py +0 -15
  124. swarm/core/cli/__init__.py +0 -1
  125. swarm/core/cli/commands/__init__.py +0 -1
  126. swarm/core/cli/commands/blueprint_management.py +0 -7
  127. swarm/core/cli/interactive_shell.py +0 -14
  128. swarm/core/cli/main.py +0 -50
  129. swarm/core/cli/utils/__init__.py +0 -1
  130. swarm/core/cli/utils/discover_commands.py +0 -18
  131. swarm/core/config_loader.py +0 -122
  132. swarm/core/output_utils.py +0 -193
  133. swarm/core/session_logger.py +0 -42
  134. swarm/core/slash_commands.py +0 -89
  135. swarm/core/swarm_api.py +0 -68
  136. swarm/core/swarm_cli.py +0 -216
  137. swarm/core/utils/__init__.py +0 -0
  138. swarm/extensions/blueprint/cli_handler.py +0 -197
  139. swarm/extensions/blueprint/runnable_blueprint.py +0 -42
  140. swarm/extensions/cli/utils/__init__.py +0 -1
  141. swarm/extensions/cli/utils/async_input.py +0 -46
  142. swarm/extensions/cli/utils/prompt_user.py +0 -3
  143. swarm/management/__init__.py +0 -0
  144. swarm/management/commands/__init__.py +0 -0
  145. swarm/management/commands/runserver.py +0 -58
  146. swarm/middleware.py +0 -65
  147. swarm/permissions.py +0 -38
  148. swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -7
  149. swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -7
  150. swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -7
  151. swarm/static/contrib/markedjs/marked.min.js +0 -6
  152. swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -27
  153. swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -21
  154. swarm/static/contrib/tabler-icons/archive.svg +0 -21
  155. swarm/static/contrib/tabler-icons/artboard.svg +0 -27
  156. swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -23
  157. swarm/static/contrib/tabler-icons/box-multiple.svg +0 -19
  158. swarm/static/contrib/tabler-icons/carambola.svg +0 -19
  159. swarm/static/contrib/tabler-icons/copy.svg +0 -20
  160. swarm/static/contrib/tabler-icons/download.svg +0 -21
  161. swarm/static/contrib/tabler-icons/edit.svg +0 -21
  162. swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -13
  163. swarm/static/contrib/tabler-icons/filled/paint.svg +0 -13
  164. swarm/static/contrib/tabler-icons/headset.svg +0 -22
  165. swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -21
  166. swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -21
  167. swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -21
  168. swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -21
  169. swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -22
  170. swarm/static/contrib/tabler-icons/message-star.svg +0 -22
  171. swarm/static/contrib/tabler-icons/message-x.svg +0 -23
  172. swarm/static/contrib/tabler-icons/message.svg +0 -21
  173. swarm/static/contrib/tabler-icons/paperclip.svg +0 -18
  174. swarm/static/contrib/tabler-icons/playlist-add.svg +0 -22
  175. swarm/static/contrib/tabler-icons/robot.svg +0 -26
  176. swarm/static/contrib/tabler-icons/search.svg +0 -19
  177. swarm/static/contrib/tabler-icons/settings.svg +0 -20
  178. swarm/static/contrib/tabler-icons/thumb-down.svg +0 -19
  179. swarm/static/contrib/tabler-icons/thumb-up.svg +0 -19
  180. swarm/static/css/dropdown.css +0 -22
  181. swarm/static/htmx/htmx.min.js +0 -0
  182. swarm/static/js/dropdown.js +0 -23
  183. swarm/static/rest_mode/css/base.css +0 -470
  184. swarm/static/rest_mode/css/chat-history.css +0 -286
  185. swarm/static/rest_mode/css/chat.css +0 -251
  186. swarm/static/rest_mode/css/chatbot.css +0 -74
  187. swarm/static/rest_mode/css/chatgpt.css +0 -62
  188. swarm/static/rest_mode/css/colors/corporate.css +0 -74
  189. swarm/static/rest_mode/css/colors/pastel.css +0 -81
  190. swarm/static/rest_mode/css/colors/tropical.css +0 -82
  191. swarm/static/rest_mode/css/general.css +0 -142
  192. swarm/static/rest_mode/css/layout.css +0 -167
  193. swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -17
  194. swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -57
  195. swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -8
  196. swarm/static/rest_mode/css/messages.css +0 -84
  197. swarm/static/rest_mode/css/messenger.css +0 -135
  198. swarm/static/rest_mode/css/settings.css +0 -91
  199. swarm/static/rest_mode/css/simple.css +0 -44
  200. swarm/static/rest_mode/css/slack.css +0 -58
  201. swarm/static/rest_mode/css/style.css +0 -156
  202. swarm/static/rest_mode/css/theme.css +0 -30
  203. swarm/static/rest_mode/css/toast.css +0 -40
  204. swarm/static/rest_mode/js/auth.js +0 -9
  205. swarm/static/rest_mode/js/blueprint.js +0 -41
  206. swarm/static/rest_mode/js/blueprintUtils.js +0 -12
  207. swarm/static/rest_mode/js/chatLogic.js +0 -79
  208. swarm/static/rest_mode/js/debug.js +0 -63
  209. swarm/static/rest_mode/js/events.js +0 -98
  210. swarm/static/rest_mode/js/main.js +0 -19
  211. swarm/static/rest_mode/js/messages.js +0 -264
  212. swarm/static/rest_mode/js/messengerLogic.js +0 -355
  213. swarm/static/rest_mode/js/modules/apiService.js +0 -84
  214. swarm/static/rest_mode/js/modules/blueprintManager.js +0 -162
  215. swarm/static/rest_mode/js/modules/chatHistory.js +0 -110
  216. swarm/static/rest_mode/js/modules/debugLogger.js +0 -14
  217. swarm/static/rest_mode/js/modules/eventHandlers.js +0 -107
  218. swarm/static/rest_mode/js/modules/messageProcessor.js +0 -120
  219. swarm/static/rest_mode/js/modules/state.js +0 -7
  220. swarm/static/rest_mode/js/modules/userInteractions.js +0 -29
  221. swarm/static/rest_mode/js/modules/validation.js +0 -23
  222. swarm/static/rest_mode/js/rendering.js +0 -119
  223. swarm/static/rest_mode/js/settings.js +0 -130
  224. swarm/static/rest_mode/js/sidebar.js +0 -94
  225. swarm/static/rest_mode/js/simpleLogic.js +0 -37
  226. swarm/static/rest_mode/js/slackLogic.js +0 -66
  227. swarm/static/rest_mode/js/splash.js +0 -76
  228. swarm/static/rest_mode/js/theme.js +0 -111
  229. swarm/static/rest_mode/js/toast.js +0 -36
  230. swarm/static/rest_mode/js/ui.js +0 -265
  231. swarm/static/rest_mode/js/validation.js +0 -57
  232. swarm/static/rest_mode/svg/animated_spinner.svg +0 -12
  233. swarm/static/rest_mode/svg/arrow_down.svg +0 -5
  234. swarm/static/rest_mode/svg/arrow_left.svg +0 -5
  235. swarm/static/rest_mode/svg/arrow_right.svg +0 -5
  236. swarm/static/rest_mode/svg/arrow_up.svg +0 -5
  237. swarm/static/rest_mode/svg/attach.svg +0 -8
  238. swarm/static/rest_mode/svg/avatar.svg +0 -7
  239. swarm/static/rest_mode/svg/canvas.svg +0 -6
  240. swarm/static/rest_mode/svg/chat_history.svg +0 -4
  241. swarm/static/rest_mode/svg/close.svg +0 -5
  242. swarm/static/rest_mode/svg/copy.svg +0 -4
  243. swarm/static/rest_mode/svg/dark_mode.svg +0 -3
  244. swarm/static/rest_mode/svg/edit.svg +0 -5
  245. swarm/static/rest_mode/svg/layout.svg +0 -9
  246. swarm/static/rest_mode/svg/logo.svg +0 -29
  247. swarm/static/rest_mode/svg/logout.svg +0 -5
  248. swarm/static/rest_mode/svg/mobile.svg +0 -5
  249. swarm/static/rest_mode/svg/new_chat.svg +0 -4
  250. swarm/static/rest_mode/svg/not_visible.svg +0 -5
  251. swarm/static/rest_mode/svg/plus.svg +0 -7
  252. swarm/static/rest_mode/svg/run_code.svg +0 -6
  253. swarm/static/rest_mode/svg/save.svg +0 -4
  254. swarm/static/rest_mode/svg/search.svg +0 -6
  255. swarm/static/rest_mode/svg/settings.svg +0 -4
  256. swarm/static/rest_mode/svg/speaker.svg +0 -5
  257. swarm/static/rest_mode/svg/stop.svg +0 -6
  258. swarm/static/rest_mode/svg/thumbs_down.svg +0 -3
  259. swarm/static/rest_mode/svg/thumbs_up.svg +0 -3
  260. swarm/static/rest_mode/svg/toggle_off.svg +0 -6
  261. swarm/static/rest_mode/svg/toggle_on.svg +0 -6
  262. swarm/static/rest_mode/svg/trash.svg +0 -10
  263. swarm/static/rest_mode/svg/undo.svg +0 -3
  264. swarm/static/rest_mode/svg/visible.svg +0 -8
  265. swarm/static/rest_mode/svg/voice.svg +0 -10
  266. swarm/templates/account/login.html +0 -22
  267. swarm/templates/account/signup.html +0 -32
  268. swarm/templates/base.html +0 -30
  269. swarm/templates/chat.html +0 -43
  270. swarm/templates/index.html +0 -35
  271. swarm/templates/rest_mode/components/chat_sidebar.html +0 -55
  272. swarm/templates/rest_mode/components/header.html +0 -45
  273. swarm/templates/rest_mode/components/main_chat_pane.html +0 -41
  274. swarm/templates/rest_mode/components/settings_dialog.html +0 -97
  275. swarm/templates/rest_mode/components/splash_screen.html +0 -7
  276. swarm/templates/rest_mode/components/top_bar.html +0 -28
  277. swarm/templates/rest_mode/message_ui.html +0 -50
  278. swarm/templates/rest_mode/slackbot.html +0 -30
  279. swarm/templates/simple_blueprint_page.html +0 -24
  280. swarm/templates/websocket_partials/final_system_message.html +0 -3
  281. swarm/templates/websocket_partials/system_message.html +0 -4
  282. swarm/templates/websocket_partials/user_message.html +0 -5
  283. swarm/utils/ansi_box.py +0 -34
  284. swarm/utils/disable_tracing.py +0 -38
  285. swarm/utils/log_utils.py +0 -63
  286. swarm/utils/openai_patch.py +0 -33
  287. swarm/ux/ansi_box.py +0 -43
  288. swarm/ux/spinner.py +0 -53
  289. {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1748636259.dist-info}/licenses/LICENSE +0 -0
  290. /swarm/{core → extensions/blueprint}/blueprint_utils.py +0 -0
  291. /swarm/{core → extensions/blueprint}/common_utils.py +0 -0
  292. /swarm/{core → extensions/config}/setup_wizard.py +0 -0
  293. /swarm/{blueprints/rue_code → extensions/config/utils}/__init__.py +0 -0
  294. /swarm/{core → extensions/config}/utils/logger.py +0 -0
  295. /swarm/{core → extensions/launchers}/swarm_wrapper.py +0 -0
@@ -1,55 +0,0 @@
1
- import argparse
2
- import asyncio
3
- from swarm.blueprints.jeeves.blueprint_jeeves import JeevesBlueprint, JeevesSpinner, display_operation_box, SPINNER_STATES
4
-
5
- def main():
6
- parser = argparse.ArgumentParser(description="Jeeves: Home automation and web search butler")
7
- parser.add_argument("--instruction", type=str, help="Instruction for Jeeves to execute", default=None)
8
- parser.add_argument("--message", dest='instruction', type=str, help="Instruction for Jeeves agent (alias --message)")
9
- args = parser.parse_args()
10
- bp = JeevesBlueprint(blueprint_id="jeeves")
11
-
12
- async def run_instruction(instruction):
13
- spinner = JeevesSpinner()
14
- spinner.start()
15
- try:
16
- messages = [{"role": "user", "content": instruction}]
17
- spinner_idx = 0
18
- import time
19
- spinner_start = time.time()
20
- async for chunk in bp.run(messages):
21
- if isinstance(chunk, dict) and (chunk.get("progress") or chunk.get("matches")):
22
- elapsed = time.time() - spinner_start
23
- spinner_state = spinner.current_spinner_state()
24
- display_operation_box(
25
- title="Progressive Operation",
26
- content="\n".join(chunk.get("matches", [])),
27
- style="bold cyan" if chunk.get("type") == "code_search" else "bold magenta",
28
- result_count=len(chunk.get('matches', [])) if chunk.get("matches") is not None else None,
29
- params={k: v for k, v in chunk.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done'}},
30
- progress_line=chunk.get('progress'),
31
- total_lines=chunk.get('total'),
32
- spinner_state=spinner_state,
33
- op_type=chunk.get("type", "search"),
34
- emoji="🔍" if chunk.get("type") == "code_search" else "🧠"
35
- )
36
- else:
37
- print(chunk)
38
- finally:
39
- spinner.stop()
40
-
41
- if args.instruction:
42
- messages = [{"role": "user", "content": args.instruction}]
43
- asyncio.run(run_instruction(args.instruction))
44
- else:
45
- print("[Jeeves CLI] Type your instruction and press Enter. Ctrl+C to exit.")
46
- try:
47
- while True:
48
- user_input = input("You: ")
49
- if user_input.strip():
50
- asyncio.run(run_instruction(user_input.strip()))
51
- except (KeyboardInterrupt, EOFError):
52
- print("\nExiting Jeeves CLI.")
53
-
54
- if __name__ == "__main__":
55
- main()
@@ -1,24 +0,0 @@
1
- {
2
- "name": "JeevesBlueprint",
3
- "title": "Jeeves: Multi-Agent Home & Web Orchestration",
4
- "description": "Demonstrates agent-based delegation for web search and home automation, with ANSI/emoji UX, spinner feedback, and robust fallback for agent/LLM errors.",
5
- "author": "Open Swarm Team",
6
- "version": "1.1.0",
7
- "tags": ["agentic", "multi-agent", "home automation", "web search", "UX", "fallback", "demo"],
8
- "demonstrates": [
9
- "Multi-agent delegation and orchestration",
10
- "Web search and home automation via agents",
11
- "LLM fallback and error handling",
12
- "Unified ANSI/emoji output and spinner",
13
- "Result summaries and fallback",
14
- "Test mode for robust testing"
15
- ],
16
- "compliance": {
17
- "agentic": true,
18
- "ux_ansi_emoji": true,
19
- "spinner": true,
20
- "fallback": true,
21
- "test_coverage": true
22
- },
23
- "last_updated": "2025-04-21T04:44:16Z"
24
- }
@@ -1,473 +0,0 @@
1
- """
2
- MCPDemo Blueprint
3
-
4
- Viral docstring update: Operational as of 2025-04-18T10:14:18Z (UTC).
5
- Self-healing, fileops-enabled, swarm-scalable.
6
- """
7
- # [Swarm Propagation] Next Blueprint: mission_improbable
8
- # mission_improbable key vars: logger, project_root, src_path
9
- # mission_improbable guard: if src_path not in sys.path: sys.path.insert(0, src_path)
10
- # mission_improbable debug: logger.debug("Mission Improbable agent created: JimFlimsy (Coordinator)")
11
- # mission_improbable error handling: try/except ImportError with sys.exit(1)
12
-
13
- import logging
14
- import os
15
- import sys
16
- import glob
17
- import json
18
- import concurrent.futures
19
- from typing import Dict, Any, List, ClassVar, Optional
20
- from datetime import datetime
21
- import pytz
22
-
23
- # Ensure src is in path for BlueprintBase import
24
- project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
25
- src_path = os.path.join(project_root, 'src')
26
- if src_path not in sys.path: sys.path.insert(0, src_path)
27
-
28
- try:
29
- from agents import Agent, Tool, function_tool, Runner
30
- from agents.mcp import MCPServer
31
- from agents.models.interface import Model
32
- from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
33
- from openai import AsyncOpenAI
34
- from swarm.core.blueprint_base import BlueprintBase
35
- from swarm.core.blueprint_discovery import discover_blueprints
36
- except ImportError as e:
37
- print(f"ERROR: Import failed in MCPDemoBlueprint: {e}. Check dependencies.")
38
- print(f"sys.path: {sys.path}")
39
- sys.exit(1)
40
-
41
- logger = logging.getLogger(__name__)
42
-
43
- # Last swarm update: 2025-04-18T10:15:21Z (UTC)
44
- last_swarm_update = datetime.now(pytz.utc).strftime("%Y-%m-%dT%H:%M:%SZ (UTC)")
45
- logger.info(f"Last swarm update: {last_swarm_update}")
46
-
47
- # --- Agent Instructions ---
48
-
49
- sage_instructions_template = """
50
- You are Sage, an agent demonstrating capabilities provided by MCP servers.
51
- You have access to the following external capabilities via implicitly available MCP tools:
52
- {mcp_tool_descriptions}
53
-
54
- Your goal is to understand the user's request and utilize the appropriate MCP tool to fulfill it.
55
- For example:
56
- - To write to a file, use the 'filesystem' tool's 'write' function.
57
- - To read from memory, use the 'memory' tool's 'get' function.
58
- - To store in memory, use the 'memory' tool's 'set' function.
59
- - To perform viral file operations, provide a comma-separated list of paths or wildcard patterns.
60
-
61
- You can scale file operations horizontally across multiple targets for performance.
62
- Explain what action you are taking via which tool and report the result.
63
- """
64
-
65
- # --- FileOps Tool Logic Definitions ---
66
- # Patch: Expose underlying fileops functions for direct testing
67
- class PatchedFunctionTool:
68
- def __init__(self, func, name):
69
- self.func = func
70
- self.name = name
71
- def read_file(path: str) -> str:
72
- """
73
- Read contents of one or more files.
74
- Supports wildcard patterns (e.g., '*.txt') and comma-separated lists of paths.
75
- Returns a JSON mapping of paths to contents or error messages.
76
- """
77
- try:
78
- # Determine file paths
79
- if ',' in path:
80
- paths = [p.strip() for p in path.split(',')]
81
- elif any(pat in path for pat in ['*', '?', '[']):
82
- paths = glob.glob(path)
83
- else:
84
- paths = [path]
85
- results: Dict[str, str] = {}
86
- for p in paths:
87
- try:
88
- with open(p, 'r') as f:
89
- results[p] = f.read()
90
- except Exception as e:
91
- results[p] = f"ERROR: {e}"
92
- return json.dumps(results)
93
- except Exception as e:
94
- return f"ERROR: {e}"
95
- def write_file(path: str, content: str) -> str:
96
- """
97
- Write content to one or more files.
98
- Supports wildcard patterns and comma-separated lists for viral file operations.
99
- Returns a JSON mapping of paths to status ('OK' or error message).
100
- """
101
- try:
102
- # Determine file paths
103
- if ',' in path:
104
- paths = [p.strip() for p in path.split(',')]
105
- elif any(pat in path for pat in ['*', '?', '[']):
106
- paths = glob.glob(path)
107
- else:
108
- paths = [path]
109
- results: Dict[str, str] = {}
110
- # Write to all targets concurrently
111
- def _write_single(p: str):
112
- try:
113
- with open(p, 'w') as f:
114
- f.write(content)
115
- return p, 'OK'
116
- except Exception as e:
117
- return p, f"ERROR: {e}"
118
- with concurrent.futures.ThreadPoolExecutor() as executor:
119
- futures = {executor.submit(_write_single, p): p for p in paths}
120
- for fut in concurrent.futures.as_completed(futures):
121
- p, status = fut.result()
122
- results[p] = status
123
- return json.dumps(results)
124
- except Exception as e:
125
- return f"ERROR: {e}"
126
- def list_files(directory: str = '.') -> str:
127
- """
128
- List files in one or more directories.
129
- Supports wildcard patterns and comma-separated directory lists.
130
- Returns a JSON mapping of directory to list of entries or error message.
131
- """
132
- try:
133
- # Determine directories
134
- if ',' in directory:
135
- dirs = [d.strip() for d in directory.split(',')]
136
- elif any(pat in directory for pat in ['*', '?', '[']):
137
- dirs = glob.glob(directory)
138
- else:
139
- dirs = [directory]
140
- results: Dict[str, Any] = {}
141
- for d in dirs:
142
- try:
143
- results[d] = os.listdir(d)
144
- except Exception as e:
145
- results[d] = f"ERROR: {e}"
146
- return json.dumps(results)
147
- except Exception as e:
148
- return f"ERROR: {e}"
149
- def execute_shell_command(command: str) -> str:
150
- """
151
- Executes a shell command and returns its stdout and stderr.
152
- Timeout is configurable via SWARM_COMMAND_TIMEOUT (default: 60s).
153
- """
154
- logger.info(f"Executing shell command: {command}")
155
- try:
156
- import os
157
- timeout = int(os.getenv("SWARM_COMMAND_TIMEOUT", "60"))
158
- result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=timeout)
159
- output = f"Exit Code: {result.returncode}\n"
160
- if result.stdout:
161
- output += f"STDOUT:\n{result.stdout}\n"
162
- if result.stderr:
163
- output += f"STDERR:\n{result.stderr}\n"
164
- logger.info(f"Command finished. Exit Code: {result.returncode}")
165
- return output.strip()
166
- except subprocess.TimeoutExpired:
167
- logger.error(f"Command timed out: {command}")
168
- return f"Error: Command timed out after {os.getenv('SWARM_COMMAND_TIMEOUT', '60')} seconds."
169
- except Exception as e:
170
- logger.error(f"Error executing command '{command}': {e}", exc_info=True)
171
- return f"Error executing command: {e}"
172
- read_file_tool = PatchedFunctionTool(read_file, 'read_file')
173
- write_file_tool = PatchedFunctionTool(write_file, 'write_file')
174
- list_files_tool = PatchedFunctionTool(list_files, 'list_files')
175
- execute_shell_command_tool = PatchedFunctionTool(execute_shell_command, 'execute_shell_command')
176
-
177
- # --- Spinner and ANSI/emoji operation box for unified UX (for CLI/dev runs) ---
178
- from swarm.ux.ansi_box import ansi_box
179
- from rich.console import Console
180
- from rich.style import Style
181
- from rich.text import Text
182
- import threading
183
- import time
184
-
185
- class MCPDemoSpinner:
186
- FRAMES = [
187
- "Generating.", "Generating..", "Generating...", "Running...",
188
- "⠋ Generating...", "⠙ Generating...", "⠹ Generating...", "⠸ Generating...",
189
- "⠼ Generating...", "⠴ Generating...", "⠦ Generating...", "⠧ Generating...",
190
- "⠇ Generating...", "⠏ Generating...", "🤖 Generating...", "💡 Generating...", "✨ Generating..."
191
- ]
192
- SLOW_FRAME = "Generating... Taking longer than expected"
193
- INTERVAL = 0.12
194
- SLOW_THRESHOLD = 10 # seconds
195
-
196
- def __init__(self):
197
- self._stop_event = threading.Event()
198
- self._thread = None
199
- self._start_time = None
200
- self.console = Console()
201
- self._last_frame = None
202
- self._last_slow = False
203
-
204
- def start(self):
205
- self._stop_event.clear()
206
- self._start_time = time.time()
207
- self._thread = threading.Thread(target=self._spin, daemon=True)
208
- self._thread.start()
209
-
210
- def _spin(self):
211
- idx = 0
212
- while not self._stop_event.is_set():
213
- elapsed = time.time() - self._start_time
214
- if elapsed > self.SLOW_THRESHOLD:
215
- txt = Text(self.SLOW_FRAME, style=Style(color="yellow", bold=True))
216
- self._last_frame = self.SLOW_FRAME
217
- self._last_slow = True
218
- else:
219
- frame = self.FRAMES[idx % len(self.FRAMES)]
220
- txt = Text(frame, style=Style(color="cyan", bold=True))
221
- self._last_frame = frame
222
- self._last_slow = False
223
- self.console.print(txt, end="\r", soft_wrap=True, highlight=False)
224
- time.sleep(self.INTERVAL)
225
- idx += 1
226
- self.console.print(" " * 40, end="\r") # Clear line
227
-
228
- def stop(self, final_message="Done!"):
229
- self._stop_event.set()
230
- if self._thread:
231
- self._thread.join()
232
- self.console.print(Text(final_message, style=Style(color="green", bold=True)))
233
-
234
- def current_spinner_state(self):
235
- if self._last_slow:
236
- return self.SLOW_FRAME
237
- return self._last_frame or self.FRAMES[0]
238
-
239
-
240
- def print_operation_box(op_type, results, params=None, result_type="mcp", taking_long=False):
241
- emoji = "🧠" if result_type == "mcp" else "🔍"
242
- style = 'success' if result_type == "mcp" else 'default'
243
- box_title = op_type if op_type else ("MCPDemo Output" if result_type == "mcp" else "Results")
244
- summary_lines = []
245
- count = len(results) if isinstance(results, list) else 0
246
- summary_lines.append(f"Results: {count}")
247
- if params:
248
- for k, v in params.items():
249
- summary_lines.append(f"{k.capitalize()}: {v}")
250
- box_content = "\n".join(summary_lines + ["\n".join(map(str, results))])
251
- ansi_box(box_title, box_content, count=count, params=params, style=style if not taking_long else 'warning', emoji=emoji)
252
-
253
- # --- Define the Blueprint ---
254
- class MCPDemoBlueprint(BlueprintBase):
255
- """Demonstrates using filesystem and memory MCP servers."""
256
- metadata: ClassVar[Dict[str, Any]] = {
257
- "name": "MCPDemoBlueprint",
258
- "title": "MCP Demo (Filesystem & Memory, Scalable & Viral FileOps)",
259
- "description": "A scalable agent (Sage) demonstrating interaction with filesystem and memory MCP servers, supporting horizontal scaling and viral file operations.",
260
- "version": "1.2.0", # Updated for scaling & viral fileops
261
- "author": "Open Swarm Team (Refactored)",
262
- "tags": ["mcp", "filesystem", "memory", "demo", "scaling", "viral-fileops"],
263
- "required_mcp_servers": ["filesystem", "memory"],
264
- "env_vars": ["ALLOWED_PATH"], # For filesystem MCP
265
- }
266
-
267
- # Caches
268
- _openai_client_cache: Dict[str, AsyncOpenAI] = {}
269
- _model_instance_cache: Dict[str, Model] = {}
270
-
271
- def __init__(self, *args, **kwargs):
272
- super().__init__(*args, **kwargs)
273
- class DummyLLM:
274
- def chat_completion_stream(self, messages, **_):
275
- class DummyStream:
276
- def __aiter__(self): return self
277
- async def __anext__(self):
278
- raise StopAsyncIteration
279
- return DummyStream()
280
- self.llm = DummyLLM()
281
-
282
- # --- Model Instantiation Helper --- (Standard helper)
283
- def _get_model_instance(self, profile_name: str) -> Model:
284
- """Retrieves or creates an LLM Model instance."""
285
- # ... (Implementation is the same as in previous refactors) ...
286
- if profile_name in self._model_instance_cache:
287
- logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
288
- return self._model_instance_cache[profile_name]
289
- logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
290
- profile_data = self.get_llm_profile(profile_name)
291
- if not profile_data: raise ValueError(f"Missing LLM profile '{profile_name}'.")
292
- provider = profile_data.get("provider", "openai").lower()
293
- model_name = profile_data.get("model")
294
- if not model_name: raise ValueError(f"Missing 'model' in profile '{profile_name}'.")
295
- if provider != "openai": raise ValueError(f"Unsupported provider: {provider}")
296
- client_cache_key = f"{provider}_{profile_data.get('base_url')}"
297
- if client_cache_key not in self._openai_client_cache:
298
- client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
299
- filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
300
- log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
301
- logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
302
- try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
303
- except Exception as e: raise ValueError(f"Failed to init client: {e}") from e
304
- client = self._openai_client_cache[client_cache_key]
305
- logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
306
- try:
307
- model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
308
- self._model_instance_cache[profile_name] = model_instance
309
- return model_instance
310
- except Exception as e: raise ValueError(f"Failed to init LLM: {e}") from e
311
-
312
- def render_prompt(self, template_name: str, context: dict) -> str:
313
- return f"User request: {context.get('user_request', '')}\nHistory: {context.get('history', '')}\nAvailable tools: {', '.join(context.get('available_tools', []))}"
314
-
315
- # --- Agent Creation ---
316
- def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
317
- """Creates the Sage agent, dynamically adding MCP server descriptions to its prompt."""
318
- logger.debug("Creating MCP Demo agent (Sage)...")
319
- self._model_instance_cache = {}
320
- self._openai_client_cache = {}
321
-
322
- default_profile_name = self.config.get("llm_profile", "default")
323
- logger.debug(f"Using LLM profile '{default_profile_name}' for Sage.")
324
- model_instance = self._get_model_instance(default_profile_name)
325
-
326
- # Filter for required MCPs and get descriptions
327
- required_names = self.metadata["required_mcp_servers"]
328
- agent_mcps: List[MCPServer] = []
329
- mcp_descriptions = []
330
- for server in mcp_servers:
331
- if server.name in required_names:
332
- agent_mcps.append(server)
333
- description = self.get_mcp_server_description(server.name)
334
- mcp_descriptions.append(f"- {server.name}: {description or 'No description available.'}")
335
-
336
- if len(agent_mcps) != len(required_names):
337
- missing = set(required_names) - {s.name for s in agent_mcps}
338
- logger.warning(f"Sage agent created, but missing required MCP server(s): {', '.join(missing)}. Functionality will be limited.")
339
- # Continue with available servers
340
-
341
- # Format descriptions for the prompt
342
- mcp_tool_desc_str = "\n".join(mcp_descriptions) if mcp_descriptions else "No external tools available."
343
- sage_instructions = sage_instructions_template.format(mcp_tool_descriptions=mcp_tool_desc_str)
344
- logger.debug(f"Sage instructions generated:\n{sage_instructions}")
345
-
346
- # Instantiate Sage
347
- sage_agent = Agent(
348
- name="Sage",
349
- model=model_instance,
350
- instructions=sage_instructions,
351
- tools=[read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool], # Tools come implicitly from assigned MCP servers
352
- mcp_servers=agent_mcps # Pass the list of *started* server objects
353
- )
354
-
355
- logger.debug("Sage agent created.")
356
- return sage_agent
357
-
358
- async def _original_run(self, messages: List[dict]) -> object:
359
- last_user_message = next((m['content'] for m in reversed(messages) if m['role'] == 'user'), None)
360
- if not last_user_message:
361
- yield {"messages": [{"role": "assistant", "content": "I need a user message to proceed."}]}
362
- return
363
- prompt_context = {
364
- "user_request": last_user_message,
365
- "history": messages[:-1],
366
- "available_tools": ["demo"]
367
- }
368
- rendered_prompt = self.render_prompt("mcp_demo_prompt.j2", prompt_context)
369
- yield {
370
- "messages": [
371
- {
372
- "role": "assistant",
373
- "content": f"[MCPDemo LLM] Would respond to: {rendered_prompt}"
374
- }
375
- ]
376
- }
377
- return
378
-
379
- async def run(self, messages: List[dict]) -> object:
380
- last_result = None
381
- async for result in self._original_run(messages):
382
- last_result = result
383
- yield result
384
- if last_result is not None:
385
- await self.reflect_and_learn(messages, last_result)
386
- return
387
-
388
- async def reflect_and_learn(self, messages, result):
389
- log = {
390
- 'task': messages,
391
- 'result': result,
392
- 'reflection': 'Success' if self.success_criteria(result) else 'Needs improvement',
393
- 'alternatives': self.consider_alternatives(messages, result),
394
- 'swarm_lessons': self.query_swarm_knowledge(messages)
395
- }
396
- self.write_to_swarm_log(log)
397
-
398
- def success_criteria(self, result):
399
- if not result or (isinstance(result, dict) and 'error' in result):
400
- return False
401
- if isinstance(result, list) and result and 'error' in result[0].get('messages', [{}])[0].get('content', '').lower():
402
- return False
403
- return True
404
-
405
- def consider_alternatives(self, messages, result):
406
- alternatives = []
407
- if not self.success_criteria(result):
408
- alternatives.append('Try a different agent for the task.')
409
- alternatives.append('Fallback to a simpler command.')
410
- else:
411
- alternatives.append('Add more agent-to-agent coordination.')
412
- return alternatives
413
-
414
- def query_swarm_knowledge(self, messages):
415
- import json, os
416
- path = os.path.join(os.path.dirname(__file__), '../../../swarm_knowledge.json')
417
- if not os.path.exists(path):
418
- return []
419
- with open(path, 'r') as f:
420
- knowledge = json.load(f)
421
- task_str = json.dumps(messages)
422
- return [entry for entry in knowledge if entry.get('task_str') == task_str]
423
-
424
- def write_to_swarm_log(self, log):
425
- import json, os, time
426
- from filelock import FileLock, Timeout
427
- path = os.path.join(os.path.dirname(__file__), '../../../swarm_log.json')
428
- lock_path = path + '.lock'
429
- log['task_str'] = json.dumps(log['task'])
430
- for attempt in range(10):
431
- try:
432
- with FileLock(lock_path, timeout=5):
433
- if os.path.exists(path):
434
- with open(path, 'r') as f:
435
- try:
436
- logs = json.load(f)
437
- except json.JSONDecodeError:
438
- logs = []
439
- else:
440
- logs = []
441
- logs.append(log)
442
- with open(path, 'w') as f:
443
- json.dump(logs, f, indent=2)
444
- break
445
- except Timeout:
446
- time.sleep(0.2 * (attempt + 1))
447
-
448
- # Standard Python entry point
449
- if __name__ == "__main__":
450
- import asyncio
451
- import json
452
- print("\033[1;36m\n╔══════════════════════════════════════════════════════════════╗\n║ 🧠 MCP DEMO: AGENT INTERACTION & SWARM DEBUG DEMO ║\n╠══════════════════════════════════════════════════════════════╣\n║ This blueprint showcases viral swarm propagation, ║\n║ agent-to-agent interaction, and advanced debug logging. ║\n║ Try running: python blueprint_mcp_demo.py ║\n╚══════════════════════════════════════════════════════════════╝\033[0m")
453
- messages = [
454
- {"role": "user", "content": "Show me how MCP Demo enables agent interaction and swarm debug logging."}
455
- ]
456
- blueprint = MCPDemoBlueprint(blueprint_id="demo-1")
457
- async def run_and_print():
458
- spinner = MCPDemoSpinner()
459
- spinner.start()
460
- try:
461
- all_results = []
462
- async for response in blueprint.run(messages):
463
- content = response["messages"][0]["content"]
464
- all_results.append(content)
465
- finally:
466
- spinner.stop()
467
- print_operation_box(
468
- op_type="MCPDemo Output",
469
- results=all_results,
470
- params={"prompt": messages[0]["content"]},
471
- result_type="mcp"
472
- )
473
- asyncio.run(run_and_print())
@@ -1,46 +0,0 @@
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>