open-swarm 0.1.1745275181__py3-none-any.whl → 0.1.1748636295__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. open_swarm-0.1.1748636295.dist-info/METADATA +257 -0
  2. open_swarm-0.1.1748636295.dist-info/RECORD +89 -0
  3. {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/WHEEL +2 -1
  4. open_swarm-0.1.1748636295.dist-info/entry_points.txt +3 -0
  5. open_swarm-0.1.1748636295.dist-info/top_level.txt +1 -0
  6. swarm/__init__.py +2 -0
  7. swarm/agent/agent.py +49 -0
  8. swarm/auth.py +48 -113
  9. swarm/consumers.py +0 -19
  10. swarm/core.py +411 -0
  11. swarm/extensions/blueprint/__init__.py +16 -30
  12. swarm/extensions/blueprint/agent_utils.py +45 -0
  13. swarm/extensions/blueprint/blueprint_base.py +562 -0
  14. swarm/extensions/blueprint/blueprint_discovery.py +112 -0
  15. swarm/extensions/blueprint/django_utils.py +79 -181
  16. swarm/extensions/blueprint/interactive_mode.py +72 -67
  17. swarm/extensions/blueprint/output_utils.py +82 -0
  18. swarm/{core → extensions/blueprint}/spinner.py +21 -30
  19. swarm/extensions/cli/cli_args.py +0 -6
  20. swarm/extensions/cli/commands/blueprint_management.py +9 -47
  21. swarm/extensions/cli/commands/config_management.py +6 -5
  22. swarm/extensions/cli/commands/edit_config.py +7 -16
  23. swarm/extensions/cli/commands/list_blueprints.py +1 -1
  24. swarm/extensions/cli/commands/validate_env.py +4 -11
  25. swarm/extensions/cli/commands/validate_envvars.py +6 -6
  26. swarm/extensions/cli/interactive_shell.py +2 -16
  27. swarm/extensions/config/config_loader.py +345 -107
  28. swarm/{core → extensions/config}/config_manager.py +38 -50
  29. swarm/{core → extensions/config}/server_config.py +0 -32
  30. swarm/extensions/launchers/build_launchers.py +14 -0
  31. swarm/{core → extensions/launchers}/build_swarm_wrapper.py +0 -0
  32. swarm/extensions/launchers/swarm_api.py +64 -8
  33. swarm/extensions/launchers/swarm_cli.py +300 -8
  34. swarm/extensions/mcp/__init__.py +1 -0
  35. swarm/extensions/mcp/cache_utils.py +32 -0
  36. swarm/extensions/mcp/mcp_client.py +233 -0
  37. swarm/extensions/mcp/mcp_tool_provider.py +135 -0
  38. swarm/extensions/mcp/mcp_utils.py +260 -0
  39. swarm/llm/chat_completion.py +166 -0
  40. swarm/serializers.py +5 -96
  41. swarm/settings.py +133 -85
  42. swarm/types.py +91 -0
  43. swarm/urls.py +74 -57
  44. swarm/utils/context_utils.py +4 -10
  45. swarm/utils/general_utils.py +0 -21
  46. swarm/utils/redact.py +36 -23
  47. swarm/views/api_views.py +39 -48
  48. swarm/views/chat_views.py +76 -236
  49. swarm/views/core_views.py +87 -80
  50. swarm/views/model_views.py +121 -64
  51. swarm/views/utils.py +439 -65
  52. swarm/views/web_views.py +2 -2
  53. open_swarm-0.1.1745275181.dist-info/METADATA +0 -874
  54. open_swarm-0.1.1745275181.dist-info/RECORD +0 -319
  55. open_swarm-0.1.1745275181.dist-info/entry_points.txt +0 -4
  56. swarm/blueprints/README.md +0 -68
  57. swarm/blueprints/blueprint_audit_status.json +0 -27
  58. swarm/blueprints/chatbot/README.md +0 -40
  59. swarm/blueprints/chatbot/blueprint_chatbot.py +0 -471
  60. swarm/blueprints/chatbot/metadata.json +0 -23
  61. swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -33
  62. swarm/blueprints/chucks_angels/README.md +0 -11
  63. swarm/blueprints/chucks_angels/blueprint_chucks_angels.py +0 -7
  64. swarm/blueprints/chucks_angels/test_basic.py +0 -3
  65. swarm/blueprints/codey/CODEY.md +0 -15
  66. swarm/blueprints/codey/README.md +0 -115
  67. swarm/blueprints/codey/blueprint_codey.py +0 -1072
  68. swarm/blueprints/codey/codey_cli.py +0 -373
  69. swarm/blueprints/codey/instructions.md +0 -17
  70. swarm/blueprints/codey/metadata.json +0 -23
  71. swarm/blueprints/common/operation_box_utils.py +0 -83
  72. swarm/blueprints/digitalbutlers/README.md +0 -11
  73. swarm/blueprints/digitalbutlers/__init__.py +0 -1
  74. swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -7
  75. swarm/blueprints/digitalbutlers/test_basic.py +0 -3
  76. swarm/blueprints/divine_code/README.md +0 -3
  77. swarm/blueprints/divine_code/__init__.py +0 -10
  78. swarm/blueprints/divine_code/apps.py +0 -11
  79. swarm/blueprints/divine_code/blueprint_divine_code.py +0 -270
  80. swarm/blueprints/django_chat/apps.py +0 -6
  81. swarm/blueprints/django_chat/blueprint_django_chat.py +0 -268
  82. swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -37
  83. swarm/blueprints/django_chat/urls.py +0 -8
  84. swarm/blueprints/django_chat/views.py +0 -32
  85. swarm/blueprints/echocraft/blueprint_echocraft.py +0 -384
  86. swarm/blueprints/flock/README.md +0 -11
  87. swarm/blueprints/flock/__init__.py +0 -8
  88. swarm/blueprints/flock/blueprint_flock.py +0 -7
  89. swarm/blueprints/flock/test_basic.py +0 -3
  90. swarm/blueprints/geese/README.md +0 -10
  91. swarm/blueprints/geese/__init__.py +0 -8
  92. swarm/blueprints/geese/blueprint_geese.py +0 -384
  93. swarm/blueprints/geese/geese_cli.py +0 -102
  94. swarm/blueprints/jeeves/README.md +0 -41
  95. swarm/blueprints/jeeves/blueprint_jeeves.py +0 -722
  96. swarm/blueprints/jeeves/jeeves_cli.py +0 -55
  97. swarm/blueprints/jeeves/metadata.json +0 -24
  98. swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -473
  99. swarm/blueprints/messenger/templates/messenger/messenger.html +0 -46
  100. swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -423
  101. swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -340
  102. swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -265
  103. swarm/blueprints/omniplex/blueprint_omniplex.py +0 -298
  104. swarm/blueprints/poets/blueprint_poets.py +0 -546
  105. swarm/blueprints/poets/poets_cli.py +0 -23
  106. swarm/blueprints/rue_code/README.md +0 -8
  107. swarm/blueprints/rue_code/blueprint_rue_code.py +0 -448
  108. swarm/blueprints/rue_code/rue_code_cli.py +0 -43
  109. swarm/blueprints/stewie/apps.py +0 -12
  110. swarm/blueprints/stewie/blueprint_family_ties.py +0 -349
  111. swarm/blueprints/stewie/models.py +0 -19
  112. swarm/blueprints/stewie/serializers.py +0 -10
  113. swarm/blueprints/stewie/settings.py +0 -17
  114. swarm/blueprints/stewie/urls.py +0 -11
  115. swarm/blueprints/stewie/views.py +0 -26
  116. swarm/blueprints/suggestion/blueprint_suggestion.py +0 -222
  117. swarm/blueprints/whinge_surf/README.md +0 -22
  118. swarm/blueprints/whinge_surf/__init__.py +0 -1
  119. swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +0 -565
  120. swarm/blueprints/whinge_surf/whinge_surf_cli.py +0 -99
  121. swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
  122. swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -11
  123. swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -339
  124. swarm/blueprints/zeus/__init__.py +0 -2
  125. swarm/blueprints/zeus/apps.py +0 -4
  126. swarm/blueprints/zeus/blueprint_zeus.py +0 -270
  127. swarm/blueprints/zeus/zeus_cli.py +0 -13
  128. swarm/cli/async_input.py +0 -65
  129. swarm/cli/async_input_demo.py +0 -32
  130. swarm/core/agent_utils.py +0 -21
  131. swarm/core/blueprint_base.py +0 -769
  132. swarm/core/blueprint_discovery.py +0 -125
  133. swarm/core/blueprint_runner.py +0 -59
  134. swarm/core/blueprint_ux.py +0 -109
  135. swarm/core/build_launchers.py +0 -15
  136. swarm/core/cli/__init__.py +0 -1
  137. swarm/core/cli/commands/__init__.py +0 -1
  138. swarm/core/cli/commands/blueprint_management.py +0 -7
  139. swarm/core/cli/interactive_shell.py +0 -14
  140. swarm/core/cli/main.py +0 -50
  141. swarm/core/cli/utils/__init__.py +0 -1
  142. swarm/core/cli/utils/discover_commands.py +0 -18
  143. swarm/core/config_loader.py +0 -122
  144. swarm/core/output_utils.py +0 -193
  145. swarm/core/session_logger.py +0 -42
  146. swarm/core/slash_commands.py +0 -89
  147. swarm/core/swarm_api.py +0 -68
  148. swarm/core/swarm_cli.py +0 -216
  149. swarm/core/utils/__init__.py +0 -0
  150. swarm/extensions/blueprint/cli_handler.py +0 -197
  151. swarm/extensions/blueprint/runnable_blueprint.py +0 -42
  152. swarm/extensions/cli/utils/__init__.py +0 -1
  153. swarm/extensions/cli/utils/async_input.py +0 -46
  154. swarm/extensions/cli/utils/prompt_user.py +0 -3
  155. swarm/management/__init__.py +0 -0
  156. swarm/management/commands/__init__.py +0 -0
  157. swarm/management/commands/runserver.py +0 -58
  158. swarm/middleware.py +0 -65
  159. swarm/permissions.py +0 -38
  160. swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -7
  161. swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -7
  162. swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -7
  163. swarm/static/contrib/markedjs/marked.min.js +0 -6
  164. swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -27
  165. swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -21
  166. swarm/static/contrib/tabler-icons/archive.svg +0 -21
  167. swarm/static/contrib/tabler-icons/artboard.svg +0 -27
  168. swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -23
  169. swarm/static/contrib/tabler-icons/box-multiple.svg +0 -19
  170. swarm/static/contrib/tabler-icons/carambola.svg +0 -19
  171. swarm/static/contrib/tabler-icons/copy.svg +0 -20
  172. swarm/static/contrib/tabler-icons/download.svg +0 -21
  173. swarm/static/contrib/tabler-icons/edit.svg +0 -21
  174. swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -13
  175. swarm/static/contrib/tabler-icons/filled/paint.svg +0 -13
  176. swarm/static/contrib/tabler-icons/headset.svg +0 -22
  177. swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -21
  178. swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -21
  179. swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -21
  180. swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -21
  181. swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -22
  182. swarm/static/contrib/tabler-icons/message-star.svg +0 -22
  183. swarm/static/contrib/tabler-icons/message-x.svg +0 -23
  184. swarm/static/contrib/tabler-icons/message.svg +0 -21
  185. swarm/static/contrib/tabler-icons/paperclip.svg +0 -18
  186. swarm/static/contrib/tabler-icons/playlist-add.svg +0 -22
  187. swarm/static/contrib/tabler-icons/robot.svg +0 -26
  188. swarm/static/contrib/tabler-icons/search.svg +0 -19
  189. swarm/static/contrib/tabler-icons/settings.svg +0 -20
  190. swarm/static/contrib/tabler-icons/thumb-down.svg +0 -19
  191. swarm/static/contrib/tabler-icons/thumb-up.svg +0 -19
  192. swarm/static/css/dropdown.css +0 -22
  193. swarm/static/htmx/htmx.min.js +0 -0
  194. swarm/static/js/dropdown.js +0 -23
  195. swarm/static/rest_mode/css/base.css +0 -470
  196. swarm/static/rest_mode/css/chat-history.css +0 -286
  197. swarm/static/rest_mode/css/chat.css +0 -251
  198. swarm/static/rest_mode/css/chatbot.css +0 -74
  199. swarm/static/rest_mode/css/chatgpt.css +0 -62
  200. swarm/static/rest_mode/css/colors/corporate.css +0 -74
  201. swarm/static/rest_mode/css/colors/pastel.css +0 -81
  202. swarm/static/rest_mode/css/colors/tropical.css +0 -82
  203. swarm/static/rest_mode/css/general.css +0 -142
  204. swarm/static/rest_mode/css/layout.css +0 -167
  205. swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -17
  206. swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -57
  207. swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -8
  208. swarm/static/rest_mode/css/messages.css +0 -84
  209. swarm/static/rest_mode/css/messenger.css +0 -135
  210. swarm/static/rest_mode/css/settings.css +0 -91
  211. swarm/static/rest_mode/css/simple.css +0 -44
  212. swarm/static/rest_mode/css/slack.css +0 -58
  213. swarm/static/rest_mode/css/style.css +0 -156
  214. swarm/static/rest_mode/css/theme.css +0 -30
  215. swarm/static/rest_mode/css/toast.css +0 -40
  216. swarm/static/rest_mode/js/auth.js +0 -9
  217. swarm/static/rest_mode/js/blueprint.js +0 -41
  218. swarm/static/rest_mode/js/blueprintUtils.js +0 -12
  219. swarm/static/rest_mode/js/chatLogic.js +0 -79
  220. swarm/static/rest_mode/js/debug.js +0 -63
  221. swarm/static/rest_mode/js/events.js +0 -98
  222. swarm/static/rest_mode/js/main.js +0 -19
  223. swarm/static/rest_mode/js/messages.js +0 -264
  224. swarm/static/rest_mode/js/messengerLogic.js +0 -355
  225. swarm/static/rest_mode/js/modules/apiService.js +0 -84
  226. swarm/static/rest_mode/js/modules/blueprintManager.js +0 -162
  227. swarm/static/rest_mode/js/modules/chatHistory.js +0 -110
  228. swarm/static/rest_mode/js/modules/debugLogger.js +0 -14
  229. swarm/static/rest_mode/js/modules/eventHandlers.js +0 -107
  230. swarm/static/rest_mode/js/modules/messageProcessor.js +0 -120
  231. swarm/static/rest_mode/js/modules/state.js +0 -7
  232. swarm/static/rest_mode/js/modules/userInteractions.js +0 -29
  233. swarm/static/rest_mode/js/modules/validation.js +0 -23
  234. swarm/static/rest_mode/js/rendering.js +0 -119
  235. swarm/static/rest_mode/js/settings.js +0 -130
  236. swarm/static/rest_mode/js/sidebar.js +0 -94
  237. swarm/static/rest_mode/js/simpleLogic.js +0 -37
  238. swarm/static/rest_mode/js/slackLogic.js +0 -66
  239. swarm/static/rest_mode/js/splash.js +0 -76
  240. swarm/static/rest_mode/js/theme.js +0 -111
  241. swarm/static/rest_mode/js/toast.js +0 -36
  242. swarm/static/rest_mode/js/ui.js +0 -265
  243. swarm/static/rest_mode/js/validation.js +0 -57
  244. swarm/static/rest_mode/svg/animated_spinner.svg +0 -12
  245. swarm/static/rest_mode/svg/arrow_down.svg +0 -5
  246. swarm/static/rest_mode/svg/arrow_left.svg +0 -5
  247. swarm/static/rest_mode/svg/arrow_right.svg +0 -5
  248. swarm/static/rest_mode/svg/arrow_up.svg +0 -5
  249. swarm/static/rest_mode/svg/attach.svg +0 -8
  250. swarm/static/rest_mode/svg/avatar.svg +0 -7
  251. swarm/static/rest_mode/svg/canvas.svg +0 -6
  252. swarm/static/rest_mode/svg/chat_history.svg +0 -4
  253. swarm/static/rest_mode/svg/close.svg +0 -5
  254. swarm/static/rest_mode/svg/copy.svg +0 -4
  255. swarm/static/rest_mode/svg/dark_mode.svg +0 -3
  256. swarm/static/rest_mode/svg/edit.svg +0 -5
  257. swarm/static/rest_mode/svg/layout.svg +0 -9
  258. swarm/static/rest_mode/svg/logo.svg +0 -29
  259. swarm/static/rest_mode/svg/logout.svg +0 -5
  260. swarm/static/rest_mode/svg/mobile.svg +0 -5
  261. swarm/static/rest_mode/svg/new_chat.svg +0 -4
  262. swarm/static/rest_mode/svg/not_visible.svg +0 -5
  263. swarm/static/rest_mode/svg/plus.svg +0 -7
  264. swarm/static/rest_mode/svg/run_code.svg +0 -6
  265. swarm/static/rest_mode/svg/save.svg +0 -4
  266. swarm/static/rest_mode/svg/search.svg +0 -6
  267. swarm/static/rest_mode/svg/settings.svg +0 -4
  268. swarm/static/rest_mode/svg/speaker.svg +0 -5
  269. swarm/static/rest_mode/svg/stop.svg +0 -6
  270. swarm/static/rest_mode/svg/thumbs_down.svg +0 -3
  271. swarm/static/rest_mode/svg/thumbs_up.svg +0 -3
  272. swarm/static/rest_mode/svg/toggle_off.svg +0 -6
  273. swarm/static/rest_mode/svg/toggle_on.svg +0 -6
  274. swarm/static/rest_mode/svg/trash.svg +0 -10
  275. swarm/static/rest_mode/svg/undo.svg +0 -3
  276. swarm/static/rest_mode/svg/visible.svg +0 -8
  277. swarm/static/rest_mode/svg/voice.svg +0 -10
  278. swarm/templates/account/login.html +0 -22
  279. swarm/templates/account/signup.html +0 -32
  280. swarm/templates/base.html +0 -30
  281. swarm/templates/chat.html +0 -43
  282. swarm/templates/index.html +0 -35
  283. swarm/templates/rest_mode/components/chat_sidebar.html +0 -55
  284. swarm/templates/rest_mode/components/header.html +0 -45
  285. swarm/templates/rest_mode/components/main_chat_pane.html +0 -41
  286. swarm/templates/rest_mode/components/settings_dialog.html +0 -97
  287. swarm/templates/rest_mode/components/splash_screen.html +0 -7
  288. swarm/templates/rest_mode/components/top_bar.html +0 -28
  289. swarm/templates/rest_mode/message_ui.html +0 -50
  290. swarm/templates/rest_mode/slackbot.html +0 -30
  291. swarm/templates/simple_blueprint_page.html +0 -24
  292. swarm/templates/websocket_partials/final_system_message.html +0 -3
  293. swarm/templates/websocket_partials/system_message.html +0 -4
  294. swarm/templates/websocket_partials/user_message.html +0 -5
  295. swarm/utils/ansi_box.py +0 -34
  296. swarm/utils/disable_tracing.py +0 -38
  297. swarm/utils/log_utils.py +0 -63
  298. swarm/utils/openai_patch.py +0 -33
  299. swarm/ux/ansi_box.py +0 -43
  300. swarm/ux/spinner.py +0 -53
  301. {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/licenses/LICENSE +0 -0
  302. /swarm/{core → extensions/blueprint}/blueprint_utils.py +0 -0
  303. /swarm/{core → extensions/blueprint}/common_utils.py +0 -0
  304. /swarm/{core → extensions/config}/setup_wizard.py +0 -0
  305. /swarm/{blueprints/rue_code → extensions/config/utils}/__init__.py +0 -0
  306. /swarm/{core → extensions/config}/utils/logger.py +0 -0
  307. /swarm/{core → extensions/launchers}/swarm_wrapper.py +0 -0
@@ -1,448 +0,0 @@
1
- """
2
- RueCode Blueprint
3
-
4
- Viral docstring update: Operational as of 2025-04-18T10:14:18Z (UTC).
5
- Self-healing, fileops-enabled, swarm-scalable.
6
- """
7
- import logging
8
- import os
9
- import sys
10
- import json
11
- import subprocess
12
- from typing import Dict, List, Any, AsyncGenerator, Optional
13
- from pathlib import Path
14
- import re
15
- from datetime import datetime
16
- import pytz
17
- from swarm.core.blueprint_ux import BlueprintUX
18
- from swarm.core.config_loader import load_full_configuration
19
- import time
20
- from swarm.blueprints.common.operation_box_utils import display_operation_box
21
-
22
- # Configure logging
23
- logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(asctime)s - %(name)s - %(message)s')
24
- logger = logging.getLogger(__name__)
25
-
26
- # Last swarm update: {{ datetime.now(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ') }}
27
- # Patch: Expose underlying fileops functions for direct testing
28
- class PatchedFunctionTool:
29
- def __init__(self, func, name):
30
- self.func = func
31
- self.name = name
32
-
33
- def read_file(path: str) -> str:
34
- try:
35
- with open(path, 'r') as f:
36
- return f.read()
37
- except Exception as e:
38
- return f"ERROR: {e}"
39
- def write_file(path: str, content: str) -> str:
40
- try:
41
- with open(path, 'w') as f:
42
- f.write(content)
43
- return "OK: file written"
44
- except Exception as e:
45
- return f"ERROR: {e}"
46
- def list_files(directory: str = '.') -> str:
47
- try:
48
- return '\n'.join(os.listdir(directory))
49
- except Exception as e:
50
- return f"ERROR: {e}"
51
- def execute_shell_command(command: str) -> str:
52
- """
53
- Executes a shell command and returns its stdout and stderr.
54
- Security Note: Ensure commands are properly sanitized or restricted.
55
- Timeout is configurable via SWARM_COMMAND_TIMEOUT (default: 60s).
56
- """
57
- logger.info(f"Executing shell command: {command}")
58
- try:
59
- import os
60
- timeout = int(os.getenv("SWARM_COMMAND_TIMEOUT", "60"))
61
- result = subprocess.run(
62
- command,
63
- shell=True,
64
- check=False, # Don't raise exception on non-zero exit code
65
- stdout=subprocess.PIPE,
66
- stderr=subprocess.PIPE,
67
- text=True,
68
- timeout=timeout
69
- )
70
- output = f"Exit Code: {result.returncode}\n"
71
- if result.stdout:
72
- output += f"STDOUT:\n{result.stdout}\n"
73
- if result.stderr:
74
- output += f"STDERR:\n{result.stderr}\n"
75
- logger.info(f"Command finished. Exit Code: {result.returncode}")
76
- return output.strip()
77
- except subprocess.TimeoutExpired:
78
- logger.error(f"Command timed out: {command}")
79
- return f"Error: Command timed out after {os.getenv('SWARM_COMMAND_TIMEOUT', '60')} seconds."
80
- except Exception as e:
81
- logger.error(f"Error executing command '{command}': {e}", exc_info=True)
82
- return f"Error executing command: {e}"
83
- read_file_tool = PatchedFunctionTool(read_file, 'read_file')
84
- write_file_tool = PatchedFunctionTool(write_file, 'write_file')
85
- list_files_tool = PatchedFunctionTool(list_files, 'list_files')
86
- execute_shell_command_tool = PatchedFunctionTool(execute_shell_command, 'execute_shell_command')
87
-
88
- # Attempt to import BlueprintBase, handle potential ImportError during early setup/testing
89
- try:
90
- from swarm.core.blueprint_base import BlueprintBase
91
- except ImportError as e:
92
- logger.error(f"Import failed: {e}. Check 'openai-agents' install and project structure.")
93
- # *** REMOVED sys.exit(1) ***
94
- # Define a dummy class if import fails, allowing module to load for inspection/debugging
95
- class BlueprintBase:
96
- metadata = {}
97
- def __init__(self, *args, **kwargs): pass
98
- async def run(self, *args, **kwargs): yield {}
99
-
100
- # --- Tool Definitions ---
101
-
102
- def execute_shell_command(command: str) -> str:
103
- """
104
- Executes a shell command and returns its stdout and stderr.
105
- Security Note: Ensure commands are properly sanitized or restricted.
106
- """
107
- logger.info(f"Executing shell command: {command}")
108
- try:
109
- result = subprocess.run(
110
- command,
111
- shell=True,
112
- check=False, # Don't raise exception on non-zero exit code
113
- stdout=subprocess.PIPE,
114
- stderr=subprocess.PIPE,
115
- text=True,
116
- timeout=60 # Add a timeout
117
- )
118
- output = f"Exit Code: {result.returncode}\n"
119
- if result.stdout:
120
- output += f"STDOUT:\n{result.stdout}\n"
121
- if result.stderr:
122
- output += f"STDERR:\n{result.stderr}\n"
123
- logger.info(f"Command finished. Exit Code: {result.returncode}")
124
- return output.strip()
125
- except subprocess.TimeoutExpired:
126
- logger.error(f"Command timed out: {command}")
127
- return "Error: Command timed out after 60 seconds."
128
- except Exception as e:
129
- logger.error(f"Error executing command '{command}': {e}", exc_info=True)
130
- return f"Error executing command: {e}"
131
-
132
- def read_file(file_path: str) -> str:
133
- """Reads the content of a specified file."""
134
- logger.info(f"📄 Reading file: {file_path}")
135
- try:
136
- if ".." in file_path:
137
- logger.warning(f"Attempted path traversal detected in read_file: {file_path}")
138
- return "\033[91m❌ Error: Invalid file path (potential traversal).\033[0m"
139
- path = Path(file_path)
140
- if not path.is_file():
141
- logger.warning(f"File not found: {file_path}")
142
- return f"\033[91m❌ Error: File not found at {file_path}\033[0m"
143
- content = path.read_text(encoding='utf-8')
144
- logger.info(f"Successfully read {len(content)} characters from {file_path}")
145
- max_len = 10000
146
- if len(content) > max_len:
147
- logger.warning(f"File {file_path} truncated to {max_len} characters.")
148
- return f"\033[93m⚠️ {content[:max_len]}\n... [File Truncated]\033[0m"
149
- return f"\033[92m✅ File read successfully!\033[0m\n\033[94m{content}\033[0m"
150
- except Exception as e:
151
- logger.error(f"Error reading file '{file_path}': {e}", exc_info=True)
152
- return f"\033[91m❌ Error reading file: {e}\033[0m"
153
-
154
- def write_file(file_path: str, content: str) -> str:
155
- """Writes content to a specified file, creating directories if needed."""
156
- logger.info(f"✏️ Writing to file: {file_path}")
157
- try:
158
- if ".." in file_path:
159
- logger.warning(f"Attempted path traversal detected in write_file: {file_path}")
160
- return "\033[91m❌ Error: Invalid file path (potential traversal).\033[0m"
161
- path = Path(file_path)
162
- path.parent.mkdir(parents=True, exist_ok=True)
163
- path.write_text(content, encoding='utf-8')
164
- logger.info(f"Successfully wrote {len(content)} characters to {file_path}")
165
- return f"\033[92m✅ Successfully wrote to {file_path}\033[0m"
166
- except Exception as e:
167
- logger.error(f"Error writing file '{file_path}': {e}", exc_info=True)
168
- return f"\033[91m❌ Error writing file: {e}\033[0m"
169
-
170
- def list_files(directory_path: str = ".") -> str:
171
- """Lists files and directories in a specified path."""
172
- logger.info(f"Listing files in directory: {directory_path}")
173
- try:
174
- # Basic path traversal check
175
- if ".." in directory_path:
176
- logger.warning(f"Attempted path traversal detected in list_files: {directory_path}")
177
- return "Error: Invalid directory path (potential traversal)."
178
- # Consider restricting base path
179
-
180
- path = Path(directory_path)
181
- if not path.is_dir():
182
- return f"Error: Directory not found at {directory_path}"
183
-
184
- entries = []
185
- for entry in path.iterdir():
186
- entry_type = "d" if entry.is_dir() else "f"
187
- entries.append(f"{entry_type} {entry.name}")
188
-
189
- logger.info(f"Found {len(entries)} entries in {directory_path}")
190
- return "\n".join(entries) if entries else "Directory is empty."
191
- except Exception as e:
192
- logger.error(f"Error listing files in '{directory_path}': {e}", exc_info=True)
193
- return f"Error listing files: {e}"
194
-
195
- # --- FileOps Tool Logic Definitions ---
196
- def read_file_fileops(path: str) -> str:
197
- try:
198
- with open(path, 'r') as f:
199
- return f.read()
200
- except Exception as e:
201
- return f"ERROR: {e}"
202
- def write_file_fileops(path: str, content: str) -> str:
203
- try:
204
- with open(path, 'w') as f:
205
- f.write(content)
206
- return "OK: file written"
207
- except Exception as e:
208
- return f"ERROR: {e}"
209
- def list_files_fileops(directory: str = '.') -> str:
210
- try:
211
- return '\n'.join(os.listdir(directory))
212
- except Exception as e:
213
- return f"ERROR: {e}"
214
- def execute_shell_command_fileops(command: str) -> str:
215
- import subprocess
216
- try:
217
- result = subprocess.run(command, shell=True, capture_output=True, text=True)
218
- return result.stdout + result.stderr
219
- except Exception as e:
220
- return f"ERROR: {e}"
221
-
222
- # --- LLM Cost Estimation Tool ---
223
- def calculate_llm_cost(model: str, prompt_tokens: int, completion_tokens: int = 0, config: dict = None) -> float:
224
- """
225
- Calculate the estimated cost (USD) for an LLM API call based on model and token usage.
226
- Pricing is pulled from swarm_config.json if available, else defaults.
227
- """
228
- # Default prices (per 1K tokens)
229
- default_price = {'prompt': 0.002, 'completion': 0.004}
230
- price = None
231
- model_key = model.lower()
232
- if config:
233
- llm_config = config.get('llm', {})
234
- for key, val in llm_config.items():
235
- m = val.get('model', '').lower()
236
- if m == model_key or key.lower() == model_key:
237
- # Support both single cost (same for prompt/completion) or dict
238
- if isinstance(val.get('cost'), dict):
239
- price = val['cost']
240
- elif 'cost' in val:
241
- price = {'prompt': float(val['cost']), 'completion': float(val['cost'])}
242
- break
243
- if price is None:
244
- price = default_price
245
- cost = (prompt_tokens / 1000.0) * price['prompt'] + (completion_tokens / 1000.0) * price['completion']
246
- return round(cost, 6)
247
-
248
- def llm_cost_tool(model: str, prompt_tokens: int, completion_tokens: int = 0, config: dict = None) -> str:
249
- try:
250
- cost = calculate_llm_cost(model, prompt_tokens, completion_tokens, config)
251
- return f"Estimated cost for {model}: ${cost} (prompt: {prompt_tokens}, completion: {completion_tokens} tokens)"
252
- except Exception as e:
253
- return f"Error: {e}"
254
-
255
- llm_cost_tool_fn = PatchedFunctionTool(llm_cost_tool, 'llm_cost')
256
-
257
- # --- RueCodeBlueprint Definition ---
258
- # === OpenAI GPT-4.1 Prompt Engineering Guide ===
259
- # See: https://github.com/openai/openai-cookbook/blob/main/examples/gpt4-1_prompting_guide.ipynb
260
- #
261
- # Agentic System Prompt Example (recommended for code generation/repair agents):
262
- SYS_PROMPT_AGENTIC = """
263
- You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved.
264
- If you are not sure about file content or codebase structure pertaining to the user’s request, use your tools to read files and gather the relevant information: do NOT guess or make up an answer.
265
- You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
266
- """
267
-
268
- class RueSpinner:
269
- FRAMES = ["Generating.", "Generating..", "Generating...", "Running..."]
270
- LONG_WAIT_MSG = "Generating... Taking longer than expected"
271
- INTERVAL = 0.12
272
- SLOW_THRESHOLD = 10
273
-
274
- def __init__(self):
275
- self._idx = 0
276
- self._start_time = None
277
- self._last_frame = self.FRAMES[0]
278
-
279
- def start(self):
280
- self._start_time = time.time()
281
- self._idx = 0
282
- self._last_frame = self.FRAMES[0]
283
-
284
- def _spin(self):
285
- self._idx = (self._idx + 1) % len(self.FRAMES)
286
- self._last_frame = self.FRAMES[self._idx]
287
-
288
- def current_spinner_state(self):
289
- if self._start_time and (time.time() - self._start_time) > self.SLOW_THRESHOLD:
290
- return self.LONG_WAIT_MSG
291
- return self._last_frame
292
-
293
- class RueCodeBlueprint(BlueprintBase):
294
- """
295
- A blueprint designed for code generation, execution, and file system interaction.
296
- Uses Jinja2 for templating prompts and provides tools for shell commands and file operations.
297
- """
298
- metadata = {
299
- "name": "RueCode",
300
- "description": "Generates, executes code, and interacts with the file system.",
301
- "author": "Matthew Hand",
302
- "version": "0.1.0",
303
- "tags": ["code", "execution", "filesystem", "developer"],
304
- "llm_profile": "default_dev" # Example: Suggests a profile suitable for coding
305
- }
306
-
307
- def __init__(self, blueprint_id: str = "rue_code", config=None, config_path=None, **kwargs):
308
- super().__init__(blueprint_id, config=config, config_path=config_path, **kwargs)
309
- self.blueprint_id = blueprint_id
310
- self.config_path = config_path
311
- self._config = config if config is not None else None
312
- self._llm_profile_name = None
313
- self._llm_profile_data = None
314
- self._markdown_output = None
315
- # Use standardized config loader
316
- if self._config is None:
317
- self._config = load_full_configuration(
318
- blueprint_class_name=self.__class__.__name__,
319
- default_config_path=Path(os.path.dirname(__file__)).parent.parent.parent / 'swarm_config.json',
320
- config_path_override=config_path,
321
- profile_override=None,
322
- cli_config_overrides=None
323
- )
324
- # Minimal LLM stub for demo
325
- class DummyLLM:
326
- def chat_completion_stream(self, messages, **_):
327
- class DummyStream:
328
- def __aiter__(self): return self
329
- async def __anext__(self):
330
- raise StopAsyncIteration
331
- return DummyStream()
332
- self.llm = DummyLLM()
333
- # Use silly style for RueCode
334
- self.ux = BlueprintUX(style="silly")
335
- self.spinner = RueSpinner()
336
-
337
- def render_prompt(self, template_name: str, context: dict) -> str:
338
- # Minimal fallback: just format the user request directly for now
339
- # (No Jinja2 dependency, just a stub for demo)
340
- return f"User request: {context.get('user_request', '')}\nHistory: {context.get('history', '')}\nAvailable tools: {', '.join(context.get('available_tools', []))}"
341
-
342
- def code_vs_semantic(self, label, results):
343
- """Format code or semantic results for display."""
344
- return f"{label.title()} Results:\n" + "\n".join(f"- {r}" for r in results)
345
-
346
- def summary(self, label, count, params):
347
- return f"{label} ({count} results) for: {params}"
348
-
349
- async def run(self, messages: List[Dict[str, str]]):
350
- logger.info("RueCodeBlueprint run method called.")
351
- last_user_message = next((m['content'] for m in reversed(messages) if m['role'] == 'user'), None)
352
- if not last_user_message:
353
- display_operation_box(
354
- title="RueCode Error",
355
- content="I need a user message to proceed.",
356
- emoji="📝"
357
- )
358
- spinner_frames = ["Generating.", "Generating..", "Generating...", "Running..."]
359
- for frame in spinner_frames:
360
- yield frame
361
- yield 'RueCode Error'
362
- yield {"messages": [{"role": "assistant", "content": self.ux.box("Error", "I need a user message to proceed.")}]}
363
- return
364
- prompt_context = {
365
- "user_request": last_user_message,
366
- "history": messages[:-1],
367
- "available_tools": ["rue_code"]
368
- }
369
- rendered_prompt = self.render_prompt("rue_code_prompt.j2", prompt_context)
370
- self.spinner.start()
371
- prompt_tokens = len(rendered_prompt) // 4
372
- completion_tokens = 64
373
- model = self._config.get('llm', {}).get('default', {}).get('model', 'gpt-3.5-turbo')
374
- cost_str = llm_cost_tool(model, prompt_tokens, completion_tokens, self._config)
375
- code_results = ["def foo(): ...", "def bar(): ..."]
376
- semantic_results = ["This function sorts a list.", "This function calculates a sum."]
377
- spinner_frames = ["Generating.", "Generating..", "Generating...", "Running..."]
378
- for frame in spinner_frames:
379
- yield frame
380
- yield 'RueCode Code Results'
381
- yield 'RueCode Semantic Results'
382
- yield 'RueCode Summary'
383
- for idx, label in enumerate(["code", "semantic"]):
384
- self.spinner._spin()
385
- display_operation_box(
386
- title=f"RueCode {label.title()} Results",
387
- content=self.code_vs_semantic(label, code_results if label=="code" else semantic_results),
388
- style="bold cyan" if label=="code" else "bold magenta",
389
- result_count=len(code_results if label=="code" else semantic_results),
390
- params={"user_request": prompt_context["user_request"]},
391
- spinner_state=self.spinner.current_spinner_state(),
392
- progress_line=idx+1,
393
- total_lines=2,
394
- emoji="📝"
395
- )
396
- display_operation_box(
397
- title="RueCode Summary",
398
- content=f"{self.summary('Analyzed codebase', 4, prompt_context['user_request'])}\n\n{cost_str}",
399
- emoji="📝"
400
- )
401
- yield {"messages": [{"role": "assistant", "content": self.ux.box(
402
- "RueCode Results",
403
- self.code_vs_semantic("code", code_results) + "\n" + self.code_vs_semantic("semantic", semantic_results) + f"\n\n{cost_str}",
404
- summary=self.summary("Analyzed codebase", 4, prompt_context["user_request"])
405
- )}]}
406
- logger.info("RueCodeBlueprint run finished.")
407
-
408
- if __name__ == "__main__":
409
- import asyncio
410
- import json
411
- print("\033[1;36m\n╔══════════════════════════════════════════════════════════════╗\n║ 📝 RUE CODE: SWARM TEMPLATING & EXECUTION DEMO ║\n╠══════════════════════════════════════════════════════════════╣\n║ This blueprint demonstrates viral doc propagation, ║\n║ code templating, and swarm-powered execution. ║\n║ Try running: python blueprint_rue_code.py ║\n╚══════════════════════════════════════════════════════════════╝\033[0m")
412
- messages = [
413
- {"role": "user", "content": "Show me how Rue Code does templating and swarm execution."}
414
- ]
415
- blueprint = RueCodeBlueprint(blueprint_id="demo-1")
416
- async def run_and_print():
417
- spinner = RueSpinner()
418
- spinner.start()
419
- try:
420
- all_results = []
421
- async for response in blueprint.run(messages):
422
- content = response["messages"][0]["content"] if (isinstance(response, dict) and "messages" in response and response["messages"]) else str(response)
423
- all_results.append(content)
424
- # Enhanced progressive output
425
- if isinstance(response, dict) and (response.get("progress") or response.get("matches")):
426
- display_operation_box(
427
- title="Progressive Operation",
428
- content="\n".join(response.get("matches", [])),
429
- style="bold cyan" if response.get("type") == "code_search" else "bold magenta",
430
- result_count=len(response.get("matches", [])) if response.get("matches") is not None else None,
431
- params={k: v for k, v in response.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done'}},
432
- progress_line=response.get('progress'),
433
- total_lines=response.get('total'),
434
- spinner_state=spinner.current_spinner_state() if hasattr(spinner, 'current_spinner_state') else None,
435
- op_type=response.get("type", "search"),
436
- emoji="🔍" if response.get("type") == "code_search" else "🧠"
437
- )
438
- finally:
439
- spinner.stop()
440
- display_operation_box(
441
- title="RueCode Output",
442
- content="\n".join(all_results),
443
- style="bold green",
444
- result_count=len(all_results),
445
- params={"prompt": messages[0]["content"]},
446
- op_type="rue_code"
447
- )
448
- asyncio.run(run_and_print())
@@ -1,43 +0,0 @@
1
- import argparse
2
- from swarm.blueprints.rue_code.blueprint_rue_code import RueCodeBlueprint
3
- from swarm.blueprints.common.operation_box_utils import display_operation_box
4
-
5
- def main():
6
- parser = argparse.ArgumentParser(description="RueCode: LLM code search/cost demo")
7
- parser.add_argument("--message", type=str, help="User message to process", default="Show me code cost demo")
8
- args = parser.parse_args()
9
- bp = RueCodeBlueprint(blueprint_id="cli")
10
- import asyncio
11
- async def run():
12
- async for result in bp.run([{"role": "user", "content": args.message}]):
13
- if isinstance(result, dict) and "messages" in result:
14
- print(result["messages"][0]["content"])
15
- elif isinstance(result, str):
16
- print(result)
17
- elif isinstance(result, dict) and (result.get("matches") or result.get("progress")):
18
- # Print the actual operation box for progressive output
19
- display_operation_box(
20
- title="Progressive Operation",
21
- content="\n".join(result.get("matches", [])),
22
- style="bold cyan" if result.get("type") == "code_search" else "bold magenta",
23
- result_count=len(result.get("matches", [])) if result.get("matches") is not None else None,
24
- params={k: v for k, v in result.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done'}},
25
- progress_line=result.get('progress'),
26
- total_lines=result.get('total'),
27
- spinner_state=result.get('spinner_state'),
28
- op_type=result.get("type", "search"),
29
- emoji="🔍" if result.get("type") == "code_search" else "🧠"
30
- )
31
- else:
32
- print(str(result))
33
- asyncio.run(run())
34
-
35
- if __name__ == "__main__":
36
- import sys
37
- if sys.argv[0].endswith("rue_code_cli.py") or sys.argv[0].endswith("rue_code_cli"): # legacy
38
- print("[INFO] For future use, invoke this CLI as 'rue' instead of 'rue_code_cli'.")
39
- main()
40
- elif sys.argv[0].endswith("rue"): # preferred new name
41
- main()
42
- else:
43
- main()
@@ -1,12 +0,0 @@
1
- from django.apps import AppConfig
2
- import logging
3
-
4
- logger = logging.getLogger(__name__)
5
-
6
- class StewieConfig(AppConfig):
7
- default_auto_field = 'django.db.models.BigAutoField'
8
- name = 'swarm.blueprints.stewie'
9
- verbose_name = "Family Ties Blueprint"
10
-
11
- def ready(self):
12
- logger.debug(f"Registering {self.name} via AppConfig")