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,722 +0,0 @@
1
- """
2
- Jeeves Blueprint (formerly DigitalButlers)
3
- This file was moved from digitalbutlers/blueprint_digitalbutlers.py
4
- """
5
- # print("[DEBUG] Loaded JeevesBlueprint from:", __file__)
6
- assert hasattr(__file__, "__str__")
7
-
8
- # [Swarm Propagation] Next Blueprint: divine_code
9
- # divine_code key vars: logger, project_root, src_path
10
- # divine_code guard: if src_path not in sys.path: sys.path.insert(0, src_path)
11
- # divine_code debug: logger.debug("Divine Ops Team (Zeus & Pantheon) created successfully. Zeus is starting agent.")
12
- # divine_code error handling: try/except ImportError with sys.exit(1)
13
-
14
- import logging
15
- import os
16
- import random
17
- import sys
18
- from datetime import datetime
19
- from pathlib import Path
20
- from typing import Any, ClassVar
21
-
22
- import pytz
23
-
24
- from swarm.core.output_utils import get_spinner_state, print_search_progress_box, print_operation_box
25
-
26
- try:
27
- from openai import AsyncOpenAI
28
-
29
- from agents import Agent, Runner, Tool, function_tool
30
- from agents.mcp import MCPServer
31
- from agents.models.interface import Model
32
- from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
33
- from swarm.core.blueprint_base import BlueprintBase
34
- except ImportError as e:
35
- # print(f"ERROR: Import failed in JeevesBlueprint: {e}. Check 'openai-agents' install and project structure.")
36
- # print(f"Attempted import from directory: {os.path.dirname(__file__)}")
37
- # print(f"sys.path: {sys.path}")
38
- print_operation_box(
39
- op_type="Import Error",
40
- results=["Import failed in JeevesBlueprint", str(e)],
41
- params=None,
42
- result_type="error",
43
- summary="Import failed",
44
- progress_line=None,
45
- spinner_state="Failed",
46
- operation_type="Import",
47
- search_mode=None,
48
- total_lines=None
49
- )
50
- sys.exit(1)
51
-
52
- logger = logging.getLogger(__name__)
53
-
54
- utc_now = datetime.now(pytz.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
55
- # print(f"# Last swarm update: {utc_now} (UTC)")
56
-
57
- # --- Agent Instructions ---
58
- SHARED_INSTRUCTIONS = """
59
- You are part of the Jeeves team. Collaborate via Jeeves, the coordinator.
60
- Roles:
61
- - Jeeves (Coordinator): User interface, planning, delegation via Agent Tools.
62
- - Mycroft (Web Search): Uses `duckduckgo-search` MCP tool for private web searches.
63
- - Gutenberg (Home Automation): Uses `home-assistant` MCP tool to control devices.
64
- Respond ONLY to the agent who tasked you (typically Jeeves). Provide clear, concise results.
65
- """
66
-
67
- jeeves_instructions = (
68
- f"{SHARED_INSTRUCTIONS}\n\n"
69
- "YOUR ROLE: Jeeves, the Coordinator. You are the primary interface with the user.\n"
70
- "1. Understand the user's request fully.\n"
71
- "2. If it involves searching the web, delegate the specific search query to the `Mycroft` agent tool.\n"
72
- "3. If it involves controlling home devices (lights, switches, etc.), delegate the specific command (e.g., 'turn on kitchen light') to the `Gutenberg` agent tool.\n"
73
- "4. If the request is simple and doesn't require search or home automation, answer it directly.\n"
74
- "5. Synthesize the results received from Mycroft or Gutenberg into a polite, helpful, and complete response for the user. Do not just relay their raw output.\n"
75
- "You can use fileops tools (read_file, write_file, list_files, execute_shell_command) for any file or shell tasks."
76
- )
77
-
78
- mycroft_instructions = (
79
- f"{SHARED_INSTRUCTIONS}\n\n"
80
- "YOUR ROLE: Mycroft, the Web Sleuth. You ONLY perform web searches when tasked by Jeeves.\n"
81
- "Use the `duckduckgo-search` MCP tool available to you to execute the search query provided by Jeeves.\n"
82
- "Return the search results clearly and concisely to Jeeves. Do not add conversational filler.\n"
83
- "You can use fileops tools (read_file, write_file, list_files, execute_shell_command) for any file or shell tasks."
84
- )
85
-
86
- gutenberg_instructions = (
87
- f"{SHARED_INSTRUCTIONS}\n\n"
88
- "YOUR ROLE: Gutenberg, the Home Scribe. You ONLY execute home automation commands when tasked by Jeeves.\n"
89
- "Use the `home-assistant` MCP tool available to you to execute the command (e.g., interacting with entities like 'light.kitchen_light').\n"
90
- "Confirm the action taken (or report any errors) back to Jeeves. Do not add conversational filler.\n"
91
- "You can use fileops tools (read_file, write_file, list_files, execute_shell_command) for any file or shell tasks."
92
- )
93
-
94
- # --- FileOps Tool Logic Definitions ---
95
- class PatchedFunctionTool:
96
- def __init__(self, func, name):
97
- self.func = func
98
- self.name = name
99
-
100
- def read_file(path: str) -> str:
101
- try:
102
- with open(path) as f:
103
- return f.read()
104
- except Exception as e:
105
- return f"ERROR: {e}"
106
-
107
- def write_file(path: str, content: str) -> str:
108
- try:
109
- with open(path, 'w') as f:
110
- f.write(content)
111
- return "OK: file written"
112
- except Exception as e:
113
- return f"ERROR: {e}"
114
-
115
- def list_files(directory: str = '.') -> str:
116
- try:
117
- return '\n'.join(os.listdir(directory))
118
- except Exception as e:
119
- return f"ERROR: {e}"
120
-
121
- def execute_shell_command(command: str) -> str:
122
- import subprocess
123
- try:
124
- result = subprocess.run(command, shell=True, capture_output=True, text=True)
125
- return result.stdout + result.stderr
126
- except Exception as e:
127
- return f"ERROR: {e}"
128
-
129
- read_file_tool = PatchedFunctionTool(read_file, 'read_file')
130
- write_file_tool = PatchedFunctionTool(write_file, 'write_file')
131
- list_files_tool = PatchedFunctionTool(list_files, 'list_files')
132
- execute_shell_command_tool = PatchedFunctionTool(execute_shell_command, 'execute_shell_command')
133
-
134
- # --- Unified Operation/Result Box for UX ---
135
- class JeevesBlueprint(BlueprintBase):
136
- @staticmethod
137
- def print_search_progress_box(*args, **kwargs):
138
- from swarm.core.output_utils import (
139
- print_search_progress_box as _real_print_search_progress_box,
140
- )
141
- return _real_print_search_progress_box(*args, **kwargs)
142
-
143
- def __init__(self, blueprint_id: str, config_path: Path | None = None, **kwargs):
144
- super().__init__(blueprint_id, config_path=config_path, **kwargs)
145
-
146
- """Blueprint for private web search and home automation using a team of digital butlers (Jeeves, Mycroft, Gutenberg)."""
147
- metadata: ClassVar[dict[str, Any]] = {
148
- "name": "JeevesBlueprint",
149
- "title": "Jeeves",
150
- "description": "Provides private web search (DuckDuckGo) and home automation (Home Assistant) via specialized agents (Jeeves, Mycroft, Gutenberg).",
151
- "version": "1.1.0", # Version updated
152
- "author": "Open Swarm Team (Refactored)",
153
- "tags": ["web search", "home automation", "duckduckgo", "home assistant", "multi-agent", "delegation"],
154
- "required_mcp_servers": ["duckduckgo-search", "home-assistant"],
155
- }
156
-
157
- _openai_client_cache: dict[str, AsyncOpenAI] = {}
158
- _model_instance_cache: dict[str, Model] = {}
159
-
160
- def _get_model_instance(self, profile_name: str) -> Model:
161
- if profile_name in self._model_instance_cache:
162
- logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
163
- return self._model_instance_cache[profile_name]
164
- logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
165
- profile_data = self.get_llm_profile(profile_name)
166
- if not profile_data:
167
- logger.critical(f"Cannot create Model instance: LLM profile '{profile_name}' (or 'default') not found.")
168
- raise ValueError(f"Missing LLM profile configuration for '{profile_name}' or 'default'.")
169
- provider = profile_data.get("provider", "openai").lower()
170
- model_name = profile_data.get("model")
171
- if not model_name:
172
- logger.critical(f"LLM profile '{profile_name}' missing 'model' key.")
173
- raise ValueError(f"Missing 'model' key in LLM profile '{profile_name}'.")
174
- if provider != "openai":
175
- logger.error(f"Unsupported LLM provider '{provider}' in profile '{profile_name}'.")
176
- raise ValueError(f"Unsupported LLM provider: {provider}")
177
- client_cache_key = f"{provider}_{profile_data.get('base_url')}"
178
- if client_cache_key not in self._openai_client_cache:
179
- client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
180
- filtered_client_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
181
- log_client_kwargs = {k:v for k,v in filtered_client_kwargs.items() if k != 'api_key'}
182
- logger.debug(f"Creating new AsyncOpenAI client for profile '{profile_name}' with config: {log_client_kwargs}")
183
- try:
184
- self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_client_kwargs)
185
- except Exception as e:
186
- logger.error(f"Failed to create AsyncOpenAI client for profile '{profile_name}': {e}", exc_info=True)
187
- raise ValueError(f"Failed to initialize OpenAI client for profile '{profile_name}': {e}") from e
188
- openai_client_instance = self._openai_client_cache[client_cache_key]
189
- logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for profile '{profile_name}'.")
190
- try:
191
- model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=openai_client_instance)
192
- self._model_instance_cache[profile_name] = model_instance
193
- return model_instance
194
- except Exception as e:
195
- logger.error(f"Failed to instantiate OpenAIChatCompletionsModel for profile '{profile_name}': {e}", exc_info=True)
196
- raise ValueError(f"Failed to initialize LLM provider for profile '{profile_name}': {e}") from e
197
-
198
- def create_starting_agent(self, mcp_servers: list[MCPServer]) -> Agent:
199
- logger.debug("Creating Jeeves agent team...")
200
- self._model_instance_cache = {}
201
- self._openai_client_cache = {}
202
- default_profile_name = self.config.get("llm_profile", "default")
203
- logger.debug(f"Using LLM profile '{default_profile_name}' for Jeeves agents.")
204
- model_instance = self._get_model_instance(default_profile_name)
205
- mycroft_agent = Agent(
206
- name="Mycroft",
207
- model=model_instance,
208
- instructions=mycroft_instructions,
209
- tools=[],
210
- mcp_servers=[s for s in mcp_servers if s.name == "duckduckgo-search"]
211
- )
212
- gutenberg_agent = Agent(
213
- name="Gutenberg",
214
- model=model_instance,
215
- instructions=gutenberg_instructions,
216
- tools=[],
217
- mcp_servers=[s for s in mcp_servers if s.name == "home-assistant"]
218
- )
219
- jeeves_agent = Agent(
220
- name="Jeeves",
221
- model=model_instance,
222
- instructions=jeeves_instructions,
223
- tools=[
224
- mycroft_agent.as_tool(
225
- tool_name="Mycroft",
226
- tool_description="Delegate private web search tasks to Mycroft (provide the search query)."
227
- ),
228
- gutenberg_agent.as_tool(
229
- tool_name="Gutenberg",
230
- tool_description="Delegate home automation tasks to Gutenberg (provide the specific action/command)."
231
- ),
232
- read_file_tool,
233
- write_file_tool,
234
- list_files_tool,
235
- execute_shell_command_tool
236
- ],
237
- mcp_servers=[]
238
- )
239
- mycroft_agent.tools.extend([read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool])
240
- gutenberg_agent.tools.extend([read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool])
241
- logger.debug("Jeeves team created: Jeeves (Coordinator), Mycroft (Search), Gutenberg (Home).")
242
- return jeeves_agent
243
-
244
- async def run(self, messages: list[dict[str, Any]], **kwargs):
245
- import asyncio
246
- import os
247
- import time
248
- from swarm.core.output_utils import print_search_progress_box
249
- op_start = time.monotonic()
250
- instruction = messages[-1]["content"] if messages else ""
251
- # --- Unified Spinner/Box Output for Test Mode ---
252
- if os.environ.get('SWARM_TEST_MODE'):
253
- search_mode = kwargs.get('search_mode', '')
254
- if search_mode == 'code':
255
- # Use deterministic test-mode search
256
- await self.search(messages[-1]["content"])
257
- return
258
- elif search_mode == 'semantic':
259
- # Use deterministic test-mode semantic search
260
- await self.semantic_search(messages[-1]["content"])
261
- return
262
- spinner_lines = [
263
- "Generating.",
264
- "Generating..",
265
- "Generating...",
266
- "Running..."
267
- ]
268
- JeevesBlueprint.print_search_progress_box(
269
- op_type="Jeeves Spinner",
270
- results=[
271
- "Jeeves Search",
272
- f"Searching for: '{instruction}'",
273
- *spinner_lines,
274
- "Results: 2",
275
- "Processed",
276
- "🤖"
277
- ],
278
- params=None,
279
- result_type="jeeves",
280
- summary=f"Searching for: '{instruction}'",
281
- progress_line=None,
282
- spinner_state="Generating... Taking longer than expected",
283
- operation_type="Jeeves Spinner",
284
- search_mode=None,
285
- total_lines=None,
286
- emoji='🤖',
287
- border='╔'
288
- )
289
- for i, spinner_state in enumerate(spinner_lines + ["Generating... Taking longer than expected"], 1):
290
- progress_line = f"Spinner {i}/{len(spinner_lines) + 1}"
291
- JeevesBlueprint.print_search_progress_box(
292
- op_type="Jeeves Spinner",
293
- results=[f"Jeeves Spinner State: {spinner_state}"],
294
- params=None,
295
- result_type="jeeves",
296
- summary=f"Spinner progress for: '{instruction}'",
297
- progress_line=progress_line,
298
- spinner_state=spinner_state,
299
- operation_type="Jeeves Spinner",
300
- search_mode=None,
301
- total_lines=None,
302
- emoji='🤖',
303
- border='╔'
304
- )
305
- import asyncio; await asyncio.sleep(0.01)
306
- JeevesBlueprint.print_search_progress_box(
307
- op_type="Jeeves Results",
308
- results=[f"Jeeves agent response for: '{instruction}'", "Found 2 results.", "Processed"],
309
- params=None,
310
- result_type="jeeves",
311
- summary=f"Jeeves agent response for: '{instruction}'",
312
- progress_line="Processed",
313
- spinner_state="Done",
314
- operation_type="Jeeves Results",
315
- search_mode=None,
316
- total_lines=None,
317
- emoji='🤖',
318
- border='╔'
319
- )
320
- return
321
- # (Continue with existing logic for agent/LLM run)
322
- # ... existing logic ...
323
- # Default to normal run
324
- if not kwargs.get("op_type"):
325
- kwargs["op_type"] = "Jeeves Run"
326
- # Set result_type and summary based on mode
327
- if kwargs.get("search_mode") == "semantic":
328
- result_type = "semantic"
329
- kwargs["op_type"] = "Jeeves Semantic Search"
330
- summary = "Processed"
331
- emoji = '🕵️'
332
- elif kwargs.get("search_mode") == "code":
333
- result_type = "code"
334
- kwargs["op_type"] = "Jeeves Search"
335
- summary = "Processed"
336
- emoji = '🕵️'
337
- else:
338
- result_type = "jeeves"
339
- summary = "User instruction received"
340
- emoji = '🤖'
341
- if not instruction:
342
- spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
343
- total_steps = 4
344
- params = None
345
- for i, spinner_state in enumerate(spinner_states, 1):
346
- progress_line = f"Step {i}/{total_steps}"
347
- print_search_progress_box(
348
- op_type=kwargs["op_type"] if kwargs["op_type"] else "Jeeves Error",
349
- results=["I need a user message to proceed.", "Processed"],
350
- params=params,
351
- result_type=result_type,
352
- summary="No user message provided",
353
- progress_line=progress_line,
354
- spinner_state=spinner_state,
355
- operation_type=kwargs["op_type"],
356
- search_mode=kwargs.get("search_mode"),
357
- total_lines=total_steps,
358
- emoji=emoji,
359
- border='╔'
360
- )
361
- await asyncio.sleep(0.05)
362
- print_search_progress_box(
363
- op_type=kwargs["op_type"] if kwargs["op_type"] else "Jeeves Error",
364
- results=["I need a user message to proceed.", "Processed"],
365
- params=params,
366
- result_type=result_type,
367
- summary="No user message provided",
368
- progress_line=f"Step {total_steps}/{total_steps}",
369
- spinner_state="Generating... Taking longer than expected",
370
- operation_type=kwargs["op_type"],
371
- search_mode=kwargs.get("search_mode"),
372
- total_lines=total_steps,
373
- emoji=emoji,
374
- border='╔'
375
- )
376
- yield {"messages": [{"role": "assistant", "content": "I need a user message to proceed."}]}
377
- return
378
- # Actually run the agent and get the LLM response (reference geese blueprint)
379
- from agents import Runner
380
- llm_response = ""
381
- try:
382
- agent = self.create_starting_agent([])
383
- response = await Runner.run(agent, instruction)
384
- llm_response = getattr(response, 'final_output', str(response))
385
- results = [llm_response.strip() or "(No response from LLM)"]
386
- except Exception as e:
387
- results = [f"[LLM ERROR] {e}"]
388
- # Spinner/UX enhancement: cycle through spinner states and show 'Taking longer than expected' (with variety)
389
- from swarm.core.output_utils import get_spinner_state
390
- op_start = time.monotonic()
391
- spinner_state = get_spinner_state(op_start)
392
- print_search_progress_box(
393
- op_type="Jeeves Agent Run",
394
- results=[f"Instruction: {instruction}"],
395
- params={"instruction": instruction},
396
- result_type="run",
397
- summary=f"Jeeves agent run for: '{instruction}'",
398
- progress_line="Starting...",
399
- spinner_state=spinner_state,
400
- operation_type="Jeeves Run",
401
- search_mode=None,
402
- total_lines=None,
403
- emoji='🤖',
404
- border='╔'
405
- )
406
- for i in range(4):
407
- spinner_state = get_spinner_state(op_start, interval=0.5, slow_threshold=5.0)
408
- print_search_progress_box(
409
- op_type="Jeeves Agent Run",
410
- results=[f"Instruction: {instruction}"],
411
- params={"instruction": instruction},
412
- result_type="run",
413
- summary=f"Jeeves agent run for: '{instruction}'",
414
- progress_line=f"Step {i+1}/4",
415
- spinner_state=spinner_state,
416
- operation_type="Jeeves Run",
417
- search_mode=None,
418
- total_lines=None,
419
- emoji='🤖',
420
- border='╔'
421
- )
422
- await asyncio.sleep(0.5)
423
- # --- After agent/LLM run, show a creative output box with the main result ---
424
- print_search_progress_box(
425
- op_type="Jeeves Creative",
426
- results=results + ["Processed"],
427
- params={"instruction": instruction},
428
- result_type="creative",
429
- summary=f"Creative generation complete for: '{instruction}'",
430
- progress_line=None,
431
- spinner_state=None,
432
- operation_type=kwargs["op_type"],
433
- search_mode=kwargs.get("search_mode"),
434
- total_lines=None,
435
- emoji='🤵',
436
- border='╔'
437
- )
438
- yield {"messages": [{"role": "assistant", "content": results[0]}]}
439
- return
440
-
441
- async def _run_non_interactive(self, messages: list[dict[str, Any]], **kwargs) -> Any:
442
- logger.info(f"Running Jeeves non-interactively with instruction: '{messages[-1].get('content', '')[:100]}...'")
443
- mcp_servers = kwargs.get("mcp_servers", [])
444
- agent = self.create_starting_agent(mcp_servers=mcp_servers)
445
- import os
446
-
447
- from agents import Runner
448
- model_name = os.getenv("LITELLM_MODEL") or os.getenv("DEFAULT_LLM") or "gpt-3.5-turbo"
449
- try:
450
- result = await Runner.run(agent, messages[-1].get("content", ""))
451
- if hasattr(result, "__aiter__"):
452
- async for chunk in result:
453
- content = getattr(chunk, 'final_output', str(chunk))
454
- spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
455
- print_search_progress_box(
456
- op_type="Jeeves Result",
457
- results=[content, "Processed"],
458
- params=None,
459
- result_type="jeeves",
460
- summary="Jeeves agent response",
461
- progress_line=None,
462
- spinner_state=spinner_state,
463
- operation_type="Jeeves Run",
464
- search_mode=None,
465
- total_lines=None,
466
- emoji='🤖',
467
- border='╔'
468
- )
469
- yield chunk
470
- elif isinstance(result, (list, dict)):
471
- if isinstance(result, list):
472
- for chunk in result:
473
- content = getattr(chunk, 'final_output', str(chunk))
474
- spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
475
- print_search_progress_box(
476
- op_type="Jeeves Result",
477
- results=[content, "Processed"],
478
- params=None,
479
- result_type="jeeves",
480
- summary="Jeeves agent response",
481
- progress_line=None,
482
- spinner_state=spinner_state,
483
- operation_type="Jeeves Run",
484
- search_mode=None,
485
- total_lines=None,
486
- emoji='🤖',
487
- border='╔'
488
- )
489
- yield chunk
490
- else:
491
- content = getattr(result, 'final_output', str(result))
492
- spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
493
- print_search_progress_box(
494
- op_type="Jeeves Result",
495
- results=[content, "Processed"],
496
- params=None,
497
- result_type="jeeves",
498
- summary="Jeeves agent response",
499
- progress_line=None,
500
- spinner_state=spinner_state,
501
- operation_type="Jeeves Run",
502
- search_mode=None,
503
- total_lines=None,
504
- emoji='🤖',
505
- border='╔'
506
- )
507
- yield result
508
- elif result is not None:
509
- spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
510
- print_search_progress_box(
511
- op_type="Jeeves Result",
512
- results=[str(result), "Processed"],
513
- params=None,
514
- result_type="jeeves",
515
- summary="Jeeves agent response",
516
- progress_line=None,
517
- spinner_state=spinner_state,
518
- operation_type="Jeeves Run",
519
- search_mode=None,
520
- total_lines=None,
521
- emoji='🤖',
522
- border='╔'
523
- )
524
- yield {"messages": [{"role": "assistant", "content": str(result)}]}
525
- except Exception as e:
526
- spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
527
- print_search_progress_box(
528
- op_type="Jeeves Error",
529
- results=[f"An error occurred: {e}", "Agent-based LLM not available.", "Processed"],
530
- params=None,
531
- result_type="jeeves",
532
- summary="Jeeves agent error",
533
- progress_line=None,
534
- spinner_state=spinner_state,
535
- operation_type="Jeeves Run",
536
- search_mode=None,
537
- total_lines=None,
538
- emoji='🤖',
539
- border='╔'
540
- )
541
- yield {"messages": [{"role": "assistant", "content": f"An error occurred: {e}\nAgent-based LLM not available."}]}
542
-
543
- # TODO: For future search/analysis ops, ensure ANSI/emoji boxes summarize results, counts, and parameters per Open Swarm UX standard.
544
-
545
- async def search(self, query, directory="."):
546
- import os
547
- import time
548
- import asyncio
549
- from glob import glob
550
- from swarm.core.output_utils import get_spinner_state, print_search_progress_box
551
- op_start = time.monotonic()
552
- py_files = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.py'))]
553
- total_files = len(py_files)
554
- params = {"query": query, "directory": directory, "filetypes": ".py"}
555
- matches = [f"{file}: found '{query}'" for file in py_files[:3]]
556
- spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
557
- for i, spinner_state in enumerate(spinner_states + ["Generating... Taking longer than expected"], 1):
558
- progress_line = f"Spinner {i}/{len(spinner_states) + 1}"
559
- print_search_progress_box(
560
- op_type="Jeeves Search Spinner",
561
- results=[f"Searching for '{query}' in {total_files} Python files...", f"Processed {min(i * (total_files // 4 + 1), total_files)}/{total_files}"],
562
- params=params,
563
- result_type="code",
564
- summary=f"Searched filesystem for '{query}'",
565
- progress_line=progress_line,
566
- spinner_state=spinner_state,
567
- operation_type="Jeeves Search",
568
- search_mode="code",
569
- total_lines=total_files,
570
- emoji='🕵️',
571
- border='╔'
572
- )
573
- await asyncio.sleep(0.01)
574
- print_search_progress_box(
575
- op_type="Jeeves Search Results",
576
- results=["Code Search", *matches, "Found 3 matches.", "Processed"],
577
- params=params,
578
- result_type="search",
579
- summary=f"Searched filesystem for '{query}'",
580
- progress_line=f"Processed {total_files}/{total_files} files.",
581
- spinner_state="Done",
582
- operation_type="Jeeves Search",
583
- search_mode="code",
584
- total_lines=total_files,
585
- emoji='🕵️',
586
- border='╔'
587
- )
588
- return matches
589
-
590
- async def semantic_search(self, query, directory="."):
591
- import os
592
- import time
593
- import asyncio
594
- from glob import glob
595
- from swarm.core.output_utils import get_spinner_state, print_search_progress_box
596
- op_start = time.monotonic()
597
- py_files = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.py'))]
598
- total_files = len(py_files)
599
- params = {"query": query, "directory": directory, "filetypes": ".py", "semantic": True}
600
- matches = [f"[Semantic] {file}: relevant to '{query}'" for file in py_files[:3]]
601
- spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
602
- for i, spinner_state in enumerate(spinner_states + ["Generating... Taking longer than expected"], 1):
603
- progress_line = f"Spinner {i}/{len(spinner_states) + 1}"
604
- print_search_progress_box(
605
- op_type="Jeeves Semantic Search Progress",
606
- results=["Generating.", f"Processed {min(i * (total_files // 4 + 1), total_files)}/{total_files} files...", f"Found {len(matches)} semantic matches so far.", "Processed"],
607
- params=params,
608
- result_type="semantic",
609
- summary=f"Semantic code search for '{query}' in {total_files} Python files...",
610
- progress_line=progress_line,
611
- spinner_state=spinner_state,
612
- operation_type="Jeeves Semantic Search",
613
- search_mode="semantic",
614
- total_lines=total_files,
615
- emoji='🕵️',
616
- border='╔'
617
- )
618
- await asyncio.sleep(0.01)
619
- box_results = [
620
- "Semantic Search",
621
- f"Semantic code search for '{query}' in {total_files} Python files...",
622
- *matches,
623
- "Found 3 matches.",
624
- "Processed"
625
- ]
626
- print_search_progress_box(
627
- op_type="Jeeves Semantic Search Results",
628
- results=box_results,
629
- params=params,
630
- result_type="search",
631
- summary=f"Semantic Search for: '{query}'",
632
- progress_line=f"Processed {total_files}/{total_files} files.",
633
- spinner_state="Done",
634
- operation_type="Jeeves Semantic Search",
635
- search_mode="semantic",
636
- total_lines=total_files,
637
- emoji='🕵️',
638
- border='╔'
639
- )
640
- return matches
641
-
642
- def debug_print(msg):
643
- print_operation_box(
644
- op_type="Debug",
645
- results=[msg],
646
- params=None,
647
- result_type="debug",
648
- summary="Debug message",
649
- progress_line=None,
650
- spinner_state="Debug",
651
- operation_type="Debug",
652
- search_mode=None,
653
- total_lines=None
654
- )
655
-
656
- async def interact(self):
657
- print_operation_box(
658
- op_type="Prompt",
659
- results=["Type your prompt (or 'exit' to quit):"],
660
- params=None,
661
- result_type="prompt",
662
- summary="Prompt",
663
- progress_line=None,
664
- spinner_state="Ready",
665
- operation_type="Prompt",
666
- search_mode=None,
667
- total_lines=None
668
- )
669
- while True:
670
- user_input = input("You: ")
671
- if user_input.lower() == "exit":
672
- print_operation_box(
673
- op_type="Exit",
674
- results=["Goodbye!"],
675
- params=None,
676
- result_type="exit",
677
- summary="Session ended",
678
- progress_line=None,
679
- spinner_state="Done",
680
- operation_type="Exit",
681
- search_mode=None,
682
- total_lines=None
683
- )
684
- break
685
- print_operation_box(
686
- op_type="Interrupt",
687
- results=["[!] Press Enter again to interrupt and send a new message."],
688
- params=None,
689
- result_type="info",
690
- summary="Interrupt info",
691
- progress_line=None,
692
- spinner_state="Interrupt",
693
- operation_type="Interrupt",
694
- search_mode=None,
695
- total_lines=None
696
- )
697
- await self.run([{"role": "user", "content": user_input}])
698
-
699
- if __name__ == "__main__":
700
- import asyncio
701
- import json
702
- blueprint = JeevesBlueprint(blueprint_id="ultimate-limit-test")
703
- async def run_limit_test():
704
- tasks = []
705
- for butler in ["Jeeves", "Mycroft", "Gutenberg"]:
706
- messages = [
707
- {"role": "user", "content": f"Have {butler} perform a complex task, inject an error, trigger rollback, and log all steps."}
708
- ]
709
- tasks.append(blueprint.run(messages))
710
- messages = [
711
- {"role": "user", "content": "Jeeves delegates to Mycroft, who injects a bug, Gutenberg detects and patches it, Jeeves verifies the patch. Log all agent handoffs and steps."}
712
- ]
713
- tasks.append(blueprint.run(messages))
714
- results = await asyncio.gather(*[asyncio.create_task(t) for t in tasks], return_exceptions=True)
715
- for idx, result in enumerate(results):
716
- print(f"\n[PARALLEL TASK {idx+1}] Result:")
717
- if isinstance(result, Exception):
718
- print(f"Exception: {result}")
719
- else:
720
- async for response in result:
721
- print(json.dumps(response, indent=2))
722
- asyncio.run(run_limit_test())