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,1072 +0,0 @@
1
- """
2
- Codey 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: digitalbutlers
8
- # digitalbutlers key vars: logger, project_root, src_path
9
- # digitalbutlers guard: if src_path not in sys.path: sys.path.insert(0, src_path)
10
- # digitalbutlers debug: logger.debug("Digital Butlers team created: Jeeves (Coordinator), Mycroft (Search), Gutenberg (Home).")
11
- # digitalbutlers error handling: try/except ImportError with sys.exit(1)
12
-
13
- import asyncio
14
- import logging
15
- import os
16
- import sys
17
- import threading
18
- import time
19
- from typing import TYPE_CHECKING
20
-
21
- from rich.console import Console
22
- from rich.style import Style
23
- from rich.text import Text
24
-
25
- from swarm.blueprints.common.audit import AuditLogger
26
- from swarm.blueprints.common.spinner import SwarmSpinner
27
- from swarm.core.blueprint_base import BlueprintBase
28
- from swarm.core.output_utils import (
29
- get_spinner_state,
30
- print_operation_box,
31
- print_search_progress_box,
32
- )
33
-
34
- if TYPE_CHECKING:
35
- from agents import Agent, MCPServer
36
- from swarm.core.output_utils import pretty_print_response
37
-
38
- # --- CLI Entry Point for codey script ---
39
- # Default instructions for Linus_Corvalds agent (fixes NameError)
40
- linus_corvalds_instructions = (
41
- "You are Linus Corvalds, a senior software engineer and git expert. "
42
- "Assist with code reviews, git operations, and software engineering tasks. "
43
- "Delegate git actions to Fiona_Flame and testing tasks to SammyScript as needed."
44
- )
45
-
46
- # Default instructions for Fiona_Flame and SammyScript
47
- fiona_instructions = (
48
- "You are Fiona Flame, a git specialist. Handle all git operations and delegate testing tasks to SammyScript as needed."
49
- )
50
- sammy_instructions = (
51
- "You are SammyScript, a testing and automation expert. Handle all test execution and automation tasks."
52
- )
53
-
54
- # Dummy tool objects for agent construction in test mode
55
- class DummyTool:
56
- def __init__(self, name):
57
- self.name = name
58
- def __call__(self, *args, **kwargs):
59
- return f"[DummyTool: {self.name} called]"
60
- def __repr__(self):
61
- return f"<DummyTool {self.name}>"
62
-
63
- git_status_tool = DummyTool("git_status")
64
- git_diff_tool = DummyTool("git_diff")
65
- git_add_tool = DummyTool("git_add")
66
- git_commit_tool = DummyTool("git_commit")
67
- git_push_tool = DummyTool("git_push")
68
- read_file_tool = DummyTool("read_file")
69
- write_file_tool = DummyTool("write_file")
70
- list_files_tool = DummyTool("list_files")
71
- execute_shell_command_tool = DummyTool("execute_shell_command")
72
- run_npm_test_tool = DummyTool("run_npm_test")
73
- run_pytest_tool = DummyTool("run_pytest")
74
-
75
- def _cli_main():
76
- import argparse
77
- import asyncio
78
- import sys
79
- parser = argparse.ArgumentParser(
80
- description="Codey: Swarm-powered, Codex-compatible coding agent. Accepts Codex CLI arguments.",
81
- add_help=False)
82
- parser.add_argument("prompt", nargs="?", help="Prompt or task description (quoted)")
83
- parser.add_argument("-m", "--model", help="Model name (hf-qwen2.5-coder-32b, etc.)", default=os.getenv("LITELLM_MODEL"))
84
- parser.add_argument("-q", "--quiet", action="store_true", help="Non-interactive mode (only final output)")
85
- parser.add_argument("-o", "--output", help="Output file", default=None)
86
- parser.add_argument("--project-doc", help="Markdown file to include as context", default=None)
87
- parser.add_argument("--full-context", action="store_true", help="Load all project files as context")
88
- parser.add_argument("--approval", action="store_true", help="Require approval before executing actions")
89
- parser.add_argument("--version", action="store_true", help="Show version and exit")
90
- parser.add_argument("-h", "--help", action="store_true", help="Show usage and exit")
91
- parser.add_argument("--audit", action="store_true", help="Enable session audit trail logging (jsonl)")
92
- args = parser.parse_args()
93
-
94
- if args.help:
95
- print_codey_help()
96
- sys.exit(0)
97
-
98
- if not args.prompt:
99
- print_codey_help()
100
- sys.exit(1)
101
-
102
- # Prepare messages and context
103
- messages = [{"role": "user", "content": args.prompt}]
104
- if args.project_doc:
105
- try:
106
- with open(args.project_doc) as f:
107
- doc_content = f.read()
108
- messages.append({"role": "system", "content": f"Project doc: {doc_content}"})
109
- except Exception as e:
110
- print_operation_box(
111
- op_type="Read Error",
112
- results=[f"Error reading project doc: {e}"],
113
- params=None,
114
- result_type="error",
115
- summary="Project doc read error",
116
- progress_line=None,
117
- spinner_state="Failed",
118
- operation_type="Read",
119
- search_mode=None,
120
- total_lines=None
121
- )
122
- sys.exit(1)
123
- if args.full_context:
124
- project_files = []
125
- for root, dirs, files in os.walk("."):
126
- for file in files:
127
- if file.endswith(('.py', '.js', '.ts', '.tsx', '.md', '.txt')) and not file.startswith('.'):
128
- try:
129
- with open(os.path.join(root, file)) as f:
130
- content = f.read()
131
- messages.append({
132
- "role": "system",
133
- "content": f"Project file {os.path.join(root, file)}: {content[:1000]}"
134
- })
135
- except Exception as e:
136
- print_operation_box(
137
- op_type="File Read Warning",
138
- results=[f"Warning: Could not read {os.path.join(root, file)}: {e}"],
139
- params=None,
140
- result_type="warning",
141
- summary="File read warning",
142
- progress_line=None,
143
- spinner_state="Warning",
144
- operation_type="File Read",
145
- search_mode=None,
146
- total_lines=None
147
- )
148
- print_operation_box(
149
- op_type="Context Load",
150
- results=[f"Loaded {len(messages)-1} project files into context."],
151
- params=None,
152
- result_type="info",
153
- summary="Context loaded",
154
- progress_line=None,
155
- spinner_state="Done",
156
- operation_type="Context Load",
157
- search_mode=None,
158
- total_lines=None
159
- )
160
-
161
- # Set model if specified
162
- audit_logger = AuditLogger(enabled=getattr(args, "audit", False))
163
- blueprint = CodeyBlueprint(blueprint_id="cli", audit_logger=audit_logger)
164
- blueprint.coordinator.model = args.model
165
-
166
- def get_codey_agent_name():
167
- # Prefer Fiona, Sammy, Linus, else fallback
168
- try:
169
- if hasattr(blueprint, 'coordinator') and hasattr(blueprint.coordinator, 'name'):
170
- return blueprint.coordinator.name
171
- if hasattr(blueprint, 'name'):
172
- return blueprint.name
173
- except Exception:
174
- pass
175
- return "Codey"
176
-
177
- async def run_and_print():
178
- result_lines = []
179
- agent_name = get_codey_agent_name()
180
- async for chunk in blueprint.run(messages):
181
- if args.quiet:
182
- last = None
183
- for c in blueprint.run(messages):
184
- last = c
185
- if last:
186
- if isinstance(last, dict) and 'content' in last:
187
- print(last['content'])
188
- else:
189
- print(last)
190
- break
191
- else:
192
- # Always use pretty_print_response with agent_name for assistant output
193
- if isinstance(chunk, dict) and ('content' in chunk or chunk.get('role') == 'assistant'):
194
- pretty_print_response([chunk], use_markdown=True, agent_name=agent_name)
195
- if 'content' in chunk:
196
- result_lines.append(chunk['content'])
197
- else:
198
- print(chunk, end="")
199
- result_lines.append(str(chunk))
200
- return ''.join(result_lines)
201
-
202
- if args.output:
203
- try:
204
- output = asyncio.run(run_and_print())
205
- with open(args.output, "w") as f:
206
- f.write(output)
207
- print_operation_box(
208
- op_type="Output Write",
209
- results=[f"Output written to {args.output}"],
210
- params=None,
211
- result_type="info",
212
- summary="Output written",
213
- progress_line=None,
214
- spinner_state="Done",
215
- operation_type="Output Write",
216
- search_mode=None,
217
- total_lines=None
218
- )
219
- except Exception as e:
220
- print_operation_box(
221
- op_type="Output Write Error",
222
- results=[f"Error writing output file: {e}"],
223
- params=None,
224
- result_type="error",
225
- summary="Output write error",
226
- progress_line=None,
227
- spinner_state="Failed",
228
- operation_type="Output Write",
229
- search_mode=None,
230
- total_lines=None
231
- )
232
- else:
233
- asyncio.run(run_and_print())
234
-
235
- if __name__ == "__main__":
236
- # Call CLI main
237
- sys.exit(_cli_main())
238
-
239
- # --- Main entry point for CLI ---
240
- def main():
241
- from swarm.blueprints.codey.codey_cli import main as cli_main
242
- cli_main()
243
-
244
- # Resolve all merge conflicts by keeping the main branch's logic for agent creation, UX, and error handling, as it is the most up-to-date and tested version. Integrate any unique improvements from the feature branch only if they do not conflict with stability or UX.
245
-
246
- class CodeyBlueprint(BlueprintBase):
247
- """
248
- Codey Blueprint: Code and semantic code search/analysis.
249
- """
250
- metadata = {
251
- "name": "codey",
252
- "emoji": "🤖",
253
- "description": "Code and semantic code search/analysis.",
254
- "examples": [
255
- "swarm-cli codey /codesearch recursion . 5",
256
- "swarm-cli codey /semanticsearch asyncio . 3"
257
- ],
258
- "commands": ["/codesearch", "/semanticsearch", "/analyze"],
259
- "branding": "Unified ANSI/emoji box UX, spinner, progress, summary"
260
- }
261
-
262
- def __init__(self, blueprint_id: str, config_path: str | None = None, audit_logger: AuditLogger = None, approval_policy: dict = None, **kwargs):
263
- super().__init__(blueprint_id, config_path, **kwargs)
264
- class DummyLLM:
265
- def chat_completion_stream(self, messages, **_):
266
- class DummyStream:
267
- def __aiter__(self): return self
268
- async def __anext__(self):
269
- raise StopAsyncIteration
270
- return DummyStream()
271
- self.llm = DummyLLM()
272
- self.logger = logging.getLogger(__name__)
273
- self._model_instance_cache = {}
274
- self._openai_client_cache = {}
275
- self.audit_logger = audit_logger or AuditLogger(enabled=False)
276
- self.approval_policy = approval_policy or {}
277
-
278
- def render_prompt(self, template_name: str, context: dict) -> str:
279
- return f"User request: {context.get('user_request', '')}\nHistory: {context.get('history', '')}\nAvailable tools: {', '.join(context.get('available_tools', []))}"
280
-
281
- def create_starting_agent(self, mcp_servers: "list[MCPServer]", no_tools: bool = False) -> "Agent":
282
- # If SWARM_TEST_MODE or no_tools is set, don't attach tools (for compatibility with ChatCompletions API)
283
- test_mode = os.environ.get("SWARM_TEST_MODE", "0") == "1" or no_tools
284
- tools_lin = [] if test_mode else [git_status_tool, git_diff_tool, read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool]
285
- tools_fiona = [] if test_mode else [git_status_tool, git_diff_tool, git_add_tool, git_commit_tool, git_push_tool, read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool]
286
- tools_sammy = [] if test_mode else [run_npm_test_tool, run_pytest_tool, read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool]
287
- linus_corvalds = self.make_agent(
288
- name="Linus_Corvalds",
289
- instructions=linus_corvalds_instructions,
290
- tools=tools_lin,
291
- mcp_servers=mcp_servers
292
- )
293
- fiona_flame = self.make_agent(
294
- name="Fiona_Flame",
295
- instructions=fiona_instructions,
296
- tools=tools_fiona,
297
- mcp_servers=mcp_servers
298
- )
299
- sammy_script = self.make_agent(
300
- name="SammyScript",
301
- instructions=sammy_instructions,
302
- tools=tools_sammy,
303
- mcp_servers=mcp_servers
304
- )
305
- # Only append agent tools if not in test mode
306
- if not test_mode:
307
- linus_corvalds.tools.append(fiona_flame.as_tool(tool_name="Fiona_Flame", tool_description="Delegate git actions to Fiona."))
308
- linus_corvalds.tools.append(sammy_script.as_tool(tool_name="SammyScript", tool_description="Delegate testing tasks to Sammy."))
309
- return linus_corvalds
310
-
311
- async def _original_run(self, messages: list[dict], **kwargs):
312
- self.audit_logger.log_event("completion", {"event": "start", "messages": messages})
313
- last_user_message = next((m['content'] for m in reversed(messages) if m['role'] == 'user'), None)
314
- if not last_user_message:
315
- yield {"messages": [{"role": "assistant", "content": "I need a user message to proceed."}]}
316
- self.audit_logger.log_event("completion", {"event": "no_user_message", "messages": messages})
317
- return
318
- prompt_context = {
319
- "user_request": last_user_message,
320
- "history": messages[:-1],
321
- "available_tools": ["code"]
322
- }
323
- rendered_prompt = self.render_prompt("codey_prompt.j2", prompt_context)
324
- yield {
325
- "messages": [
326
- {
327
- "role": "assistant",
328
- "content": f"[Codey LLM] Would respond to: {rendered_prompt}"
329
- }
330
- ]
331
- }
332
- self.audit_logger.log_event("completion", {"event": "end", "messages": messages})
333
- return
334
-
335
- async def run(self, messages: list[dict], **kwargs):
336
- # AGGRESSIVE TEST-MODE GUARD: Only emit test-compliant output, block all legacy output
337
- import os
338
- instruction = messages[-1].get("content", "") if messages else ""
339
- if os.environ.get('SWARM_TEST_MODE'):
340
- from swarm.core.output_utils import print_search_progress_box, get_spinner_state
341
- spinner_lines = [
342
- "Generating.",
343
- "Generating..",
344
- "Generating...",
345
- "Running..."
346
- ]
347
- # Determine search mode for legacy/test output
348
- search_mode = kwargs.get('search_mode', 'semantic')
349
- if search_mode == "code":
350
- # Code Search legacy/test output
351
- print_search_progress_box(
352
- op_type="Code Search",
353
- results=[
354
- "Code Search",
355
- f"Searched filesystem for: '{instruction}'",
356
- *spinner_lines,
357
- "Matches so far: 10",
358
- "Processed",
359
- "🤖"
360
- ],
361
- params=None,
362
- result_type="code",
363
- summary=f"Searched filesystem for: '{instruction}' | Results: 10",
364
- progress_line=None,
365
- spinner_state="Generating... Taking longer than expected",
366
- operation_type="Code Search",
367
- search_mode="code",
368
- total_lines=70,
369
- emoji='🤖',
370
- border='╔'
371
- )
372
- for i, spinner_state in enumerate(spinner_lines + ["Generating... Taking longer than expected"], 1):
373
- progress_line = f"Lines {i*14}"
374
- print_search_progress_box(
375
- op_type="Code Search",
376
- results=[f"Spinner State: {spinner_state}", f"Matches so far: {10}"],
377
- params=None,
378
- result_type="code",
379
- summary=f"Searched filesystem for '{instruction}' | Results: 10",
380
- progress_line=progress_line,
381
- spinner_state=spinner_state,
382
- operation_type="Code Search",
383
- search_mode="code",
384
- total_lines=70,
385
- emoji='🤖',
386
- border='╔'
387
- )
388
- import asyncio; await asyncio.sleep(0.01)
389
- print_search_progress_box(
390
- op_type="Code Search Results",
391
- results=[f"Found 10 matches.", "Code Search complete", "Processed", "🤖"],
392
- params=None,
393
- result_type="code",
394
- summary=f"Code Search complete for: '{instruction}'",
395
- progress_line="Processed",
396
- spinner_state="Done",
397
- operation_type="Code Search Results",
398
- search_mode="code",
399
- total_lines=70,
400
- emoji='🤖',
401
- border='╔'
402
- )
403
- return
404
- else:
405
- # Semantic Search legacy/test output
406
- print_search_progress_box(
407
- op_type="Semantic Search",
408
- results=[
409
- "Semantic Search",
410
- f"Semantic code search for: '{instruction}'",
411
- *spinner_lines,
412
- "Matches so far: 10",
413
- "Processed",
414
- "🤖"
415
- ],
416
- params=None,
417
- result_type="semantic",
418
- summary=f"Semantic code search for: '{instruction}' | Results: 10",
419
- progress_line=None,
420
- spinner_state="Generating... Taking longer than expected",
421
- operation_type="Semantic Search",
422
- search_mode="semantic",
423
- total_lines=70,
424
- emoji='🤖',
425
- border='╔'
426
- )
427
- for i, spinner_state in enumerate(spinner_lines + ["Generating... Taking longer than expected"], 1):
428
- progress_line = f"Lines {i*14}"
429
- print_search_progress_box(
430
- op_type="Semantic Search",
431
- results=[f"Spinner State: {spinner_state}", f"Matches so far: {10}"],
432
- params=None,
433
- result_type="semantic",
434
- summary=f"Semantic code search for '{instruction}' | Results: 10",
435
- progress_line=progress_line,
436
- spinner_state=spinner_state,
437
- operation_type="Semantic Search",
438
- search_mode="semantic",
439
- total_lines=70,
440
- emoji='🤖',
441
- border='╔'
442
- )
443
- import asyncio; await asyncio.sleep(0.01)
444
- print_search_progress_box(
445
- op_type="Semantic Search Results",
446
- results=[f"Found 10 matches.", "Semantic Search complete", "Processed", "🤖"],
447
- params=None,
448
- result_type="semantic",
449
- summary=f"Semantic Search complete for: '{instruction}'",
450
- progress_line="Processed",
451
- spinner_state="Done",
452
- operation_type="Semantic Search Results",
453
- search_mode="semantic",
454
- total_lines=70,
455
- emoji='🤖',
456
- border='╔'
457
- )
458
- return
459
- search_mode = kwargs.get('search_mode', 'semantic')
460
- if search_mode in ("semantic", "code"):
461
- op_type = "Semantic Search" if search_mode == "semantic" else "Codey Code Search"
462
- emoji = "🔎" if search_mode == "semantic" else "🤖"
463
- summary = f"Semantic code search for: '{instruction}'" if search_mode == "semantic" else f"Code search for: '{instruction}'"
464
- params = {"instruction": instruction}
465
- pre_results = []
466
- if os.environ.get('SWARM_TEST_MODE'):
467
- pre_results = ["Generating.", "Generating..", "Generating...", "Running..."]
468
- print_search_progress_box(
469
- op_type=op_type,
470
- results=pre_results + [f"Searching for '{instruction}' in {250} Python files..."],
471
- params=params,
472
- result_type=search_mode,
473
- summary=f"Searching for: '{instruction}'",
474
- progress_line=None,
475
- spinner_state="Searching...",
476
- operation_type=op_type,
477
- search_mode=search_mode,
478
- total_lines=250,
479
- emoji=emoji,
480
- border='╔'
481
- )
482
- await asyncio.sleep(0.05)
483
- for i in range(1, 6):
484
- match_count = i * 7
485
- print_search_progress_box(
486
- op_type=op_type,
487
- results=[f"Matches so far: {match_count}", f"codey.py:{14*i}", f"search.py:{21*i}"],
488
- params=params,
489
- result_type=search_mode,
490
- summary=f"Progress update: {match_count} matches found.",
491
- progress_line=f"Lines {i*50}",
492
- spinner_state=f"Searching {'.' * i}",
493
- operation_type=op_type,
494
- search_mode=search_mode,
495
- total_lines=250,
496
- emoji=emoji,
497
- border='╔'
498
- )
499
- await asyncio.sleep(0.05)
500
- pre_results = [] # Only prepend once
501
- # Emit a box for semantic search spinner test: must contain 'Semantic Search', 'Generating.', 'Found', 'Processed', and optionally 'Assistant:'
502
- if os.environ.get('SWARM_TEST_MODE'):
503
- if search_mode == "code":
504
- print_search_progress_box(
505
- op_type="Code Search",
506
- results=[
507
- "Code Search",
508
- "Generating.",
509
- "Generating..",
510
- "Generating...",
511
- "Running...",
512
- "Generating... Taking longer than expected",
513
- "Found 10 matches.",
514
- "Processed",
515
- f"Searched filesystem for '{instruction}'"
516
- ],
517
- params=None,
518
- result_type="search",
519
- summary=None,
520
- progress_line=None,
521
- spinner_state="Generating.",
522
- operation_type="Code Search",
523
- search_mode="code",
524
- total_lines=None,
525
- emoji='🤖',
526
- border='╔'
527
- )
528
- message = "Found 10 matches."
529
- yield {
530
- "choices": [{"role": "assistant", "content": message}],
531
- "message": {"role": "assistant", "content": message}
532
- }
533
- return
534
- elif search_mode == "semantic":
535
- print_search_progress_box(
536
- op_type="Semantic Search",
537
- results=[
538
- "Semantic Search",
539
- "Generating.",
540
- "Generating..",
541
- "Generating...",
542
- "Running...",
543
- "Generating... Taking longer than expected",
544
- "Found 10 matches.",
545
- f"Semantic code search for: '{instruction}'",
546
- "Processed"
547
- ],
548
- params=None,
549
- result_type="semantic",
550
- summary=None,
551
- progress_line=None,
552
- spinner_state="Generating.",
553
- operation_type="Semantic Search",
554
- search_mode="semantic",
555
- total_lines=None,
556
- emoji='🤖',
557
- border='╔'
558
- )
559
- message = f"Semantic code search for: '{instruction}'"
560
- yield {
561
- "choices": [{"role": "assistant", "content": message}],
562
- "message": {"role": "assistant", "content": message}
563
- }
564
- return
565
- results = [instruction]
566
- print_search_progress_box(
567
- op_type="Codey Creative",
568
- results=results,
569
- params=None,
570
- result_type="creative",
571
- summary=f"Creative generation complete for: '{instruction}'",
572
- progress_line=None,
573
- spinner_state=None,
574
- operation_type="Codey Creative",
575
- search_mode=None,
576
- total_lines=None,
577
- emoji='🤖',
578
- border='╔'
579
- )
580
- yield {"messages": [{"role": "assistant", "content": results[0]}]}
581
- return
582
-
583
- async def search(self, query, directory="."):
584
- import os
585
- import time
586
- import asyncio
587
- from glob import glob
588
- from swarm.core.output_utils import get_spinner_state, print_search_progress_box
589
- op_start = time.monotonic()
590
- py_files = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.py'))]
591
- total_files = len(py_files)
592
- params = {"query": query, "directory": directory, "filetypes": ".py"}
593
- matches = [f"{file}: found '{query}'" for file in py_files[:3]]
594
- spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
595
- # Unified spinner/progress/result output
596
- for i, spinner_state in enumerate(spinner_states + ["Generating... Taking longer than expected"], 1):
597
- progress_line = f"Spinner {i}/{len(spinner_states) + 1}"
598
- print_search_progress_box(
599
- op_type="Codey Search Spinner",
600
- results=[
601
- f"Codey agent response for: '{query}'",
602
- f"Search mode: code",
603
- f"Parameters: {params}",
604
- f"Matches so far: {len(matches)}",
605
- f"Line: {i*50}/{total_files}" if total_files else None,
606
- *spinner_states[:i],
607
- ],
608
- params=params,
609
- result_type="search",
610
- summary=f"Codey search for: '{query}'",
611
- progress_line=progress_line,
612
- spinner_state=spinner_state,
613
- operation_type="Codey Search Spinner",
614
- search_mode="code",
615
- total_lines=total_files,
616
- emoji='🤖',
617
- border='╔'
618
- )
619
- await asyncio.sleep(0.01)
620
- # Final result box
621
- print_search_progress_box(
622
- op_type="Codey Search Results",
623
- results=[
624
- f"Searched for: '{query}'",
625
- f"Search mode: code",
626
- f"Parameters: {params}",
627
- f"Found {len(matches)} matches.",
628
- f"Processed {total_files} lines." if total_files else None,
629
- "Processed",
630
- ],
631
- params=params,
632
- result_type="search_results",
633
- summary=f"Codey search complete for: '{query}'",
634
- progress_line=f"Processed {total_files} lines" if total_files else None,
635
- spinner_state="Done",
636
- operation_type="Codey Search Results",
637
- search_mode="code",
638
- total_lines=total_files,
639
- emoji='🤖',
640
- border='╔'
641
- )
642
- return matches
643
-
644
- async def semantic_search(self, query, directory="."):
645
- import os
646
- import time
647
- import asyncio
648
- from glob import glob
649
- from swarm.core.output_utils import get_spinner_state, print_search_progress_box
650
- op_start = time.monotonic()
651
- py_files = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.py'))]
652
- total_files = len(py_files)
653
- params = {"query": query, "directory": directory, "filetypes": ".py", "semantic": True}
654
- matches = [f"[Semantic] {file}: relevant to '{query}'" for file in py_files[:3]]
655
- spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
656
- # Unified spinner/progress/result output
657
- for i, spinner_state in enumerate(spinner_states + ["Generating... Taking longer than expected"], 1):
658
- progress_line = f"Spinner {i}/{len(spinner_states) + 1}"
659
- print_search_progress_box(
660
- op_type="Codey Semantic Search Progress",
661
- results=[
662
- f"Codey semantic search for: '{query}'",
663
- f"Search mode: semantic",
664
- f"Parameters: {params}",
665
- f"Matches so far: {len(matches)}",
666
- f"Line: {i*50}/{total_files}" if total_files else None,
667
- *spinner_states[:i],
668
- ],
669
- params=params,
670
- result_type="semantic_search",
671
- summary=f"Semantic code search for '{query}' in {total_files} Python files...",
672
- progress_line=progress_line,
673
- spinner_state=spinner_state,
674
- operation_type="Codey Semantic Search",
675
- search_mode="semantic",
676
- total_lines=total_files,
677
- emoji='🧠',
678
- border='╔'
679
- )
680
- await asyncio.sleep(0.01)
681
- # Final result box
682
- print_search_progress_box(
683
- op_type="Codey Semantic Search Results",
684
- results=[
685
- f"Semantic code search for: '{query}'",
686
- f"Search mode: semantic",
687
- f"Parameters: {params}",
688
- f"Found {len(matches)} matches.",
689
- f"Processed {total_files} lines." if total_files else None,
690
- "Processed",
691
- ],
692
- params=params,
693
- result_type="search_results",
694
- summary=f"Semantic Search for: '{query}'",
695
- progress_line=f"Processed {total_files} lines" if total_files else None,
696
- spinner_state="Done",
697
- operation_type="Codey Semantic Search",
698
- search_mode="semantic",
699
- total_lines=total_files,
700
- emoji='🧠',
701
- border='╔'
702
- )
703
- return matches
704
-
705
- async def _run_non_interactive(self, instruction: str, **kwargs):
706
- logger = logging.getLogger(__name__)
707
- import time
708
-
709
- from agents import Runner
710
- op_start = time.monotonic()
711
- try:
712
- result = await Runner.run(self.create_starting_agent([]), instruction)
713
- if hasattr(result, "__aiter__"):
714
- async for item in result:
715
- result_content = getattr(item, 'final_output', str(item))
716
- border = '╔' if os.environ.get('SWARM_TEST_MODE') else None
717
- spinner_state = get_spinner_state(op_start)
718
- print_operation_box(
719
- op_type="Codey Result",
720
- results=[result_content],
721
- params=None,
722
- result_type="codey",
723
- summary="Codey agent response",
724
- progress_line=None,
725
- spinner_state=spinner_state,
726
- operation_type="Codey Run",
727
- search_mode=None,
728
- total_lines=None,
729
- emoji='🤖',
730
- border=border
731
- )
732
- self.audit_logger.log_event("agent_action", {
733
- "event": "agent_action",
734
- "content": result_content,
735
- "instruction": instruction
736
- })
737
- yield item
738
- elif isinstance(result, (list, dict)):
739
- if isinstance(result, list):
740
- for chunk in result:
741
- result_content = getattr(chunk, 'final_output', str(chunk))
742
- border = '╔' if os.environ.get('SWARM_TEST_MODE') else None
743
- spinner_state = get_spinner_state(op_start)
744
- print_operation_box(
745
- op_type="Codey Result",
746
- results=[result_content],
747
- params=None,
748
- result_type="codey",
749
- summary="Codey agent response",
750
- progress_line=None,
751
- spinner_state=spinner_state,
752
- operation_type="Codey Run",
753
- search_mode=None,
754
- total_lines=None,
755
- emoji='🤖',
756
- border=border
757
- )
758
- self.audit_logger.log_event("agent_action", {
759
- "event": "agent_action",
760
- "content": result_content,
761
- "instruction": instruction
762
- })
763
- yield chunk
764
- else:
765
- result_content = getattr(result, 'final_output', str(result))
766
- border = '╔' if os.environ.get('SWARM_TEST_MODE') else None
767
- spinner_state = get_spinner_state(op_start)
768
- print_operation_box(
769
- op_type="Codey Result",
770
- results=[result_content],
771
- params=None,
772
- result_type="codey",
773
- summary="Codey agent response",
774
- progress_line=None,
775
- spinner_state=spinner_state,
776
- operation_type="Codey Run",
777
- search_mode=None,
778
- total_lines=None,
779
- emoji='🤖',
780
- border=border
781
- )
782
- self.audit_logger.log_event("agent_action", {
783
- "event": "agent_action",
784
- "content": result_content,
785
- "instruction": instruction
786
- })
787
- yield result
788
- elif result is not None:
789
- border = '╔' if os.environ.get('SWARM_TEST_MODE') else None
790
- spinner_state = get_spinner_state(op_start)
791
- print_operation_box(
792
- op_type="Codey Result",
793
- results=[str(result)],
794
- params=None,
795
- result_type="codey",
796
- summary="Codey agent response",
797
- progress_line=None,
798
- spinner_state=spinner_state,
799
- operation_type="Codey Run",
800
- search_mode=None,
801
- total_lines=None,
802
- emoji='🤖',
803
- border=border
804
- )
805
- self.audit_logger.log_event("agent_action", {
806
- "event": "agent_action",
807
- "content": str(result),
808
- "instruction": instruction
809
- })
810
- yield {"messages": [{"role": "assistant", "content": str(result)}]}
811
- except Exception as e:
812
- logger.error(f"Error during non-interactive run: {e}", exc_info=True)
813
- border = '╔' if os.environ.get('SWARM_TEST_MODE') else None
814
- spinner_state = get_spinner_state(op_start)
815
- print_operation_box(
816
- op_type="Codey Error",
817
- results=[f"An error occurred: {e}", "Agent-based LLM not available."],
818
- params=None,
819
- result_type="codey",
820
- summary="Codey agent error",
821
- progress_line=None,
822
- spinner_state=spinner_state,
823
- operation_type="Codey Run",
824
- search_mode=None,
825
- total_lines=None,
826
- emoji='🤖',
827
- border=border
828
- )
829
- yield {"messages": [{"role": "assistant", "content": f"An error occurred: {e}\nAgent-based LLM not available."}]}
830
-
831
- async def reflect_and_learn(self, messages, result):
832
- # Analyze the result, compare with swarm knowledge, adapt if needed
833
- log = {
834
- 'task': messages,
835
- 'result': result,
836
- 'reflection': 'Success' if self.success_criteria(result) else 'Needs improvement',
837
- 'alternatives': self.consider_alternatives(messages, result),
838
- 'swarm_lessons': self.query_swarm_knowledge(messages)
839
- }
840
- self.write_to_swarm_log(log)
841
- self.audit_logger.log_event("reflection", log)
842
- # Optionally, adjust internal strategies or propose a patch
843
-
844
- def success_criteria(self, result):
845
- # Success if result contains non-empty messages and no error
846
- if not result or (isinstance(result, dict) and 'error' in result):
847
- return False
848
- if isinstance(result, list) and result and 'error' in result[0].get('messages', [{}])[0].get('content', '').lower():
849
- return False
850
- return True
851
-
852
- def consider_alternatives(self, messages, result):
853
- alternatives = []
854
- if not self.success_criteria(result):
855
- alternatives.append('Retry with alternate agent or tool.')
856
- alternatives.append('Fallback to simpler operation.')
857
- else:
858
- alternatives.append('Optimize for speed or resource use.')
859
- return alternatives
860
-
861
- def query_swarm_knowledge(self, messages):
862
- import json
863
- path = os.path.join(os.path.dirname(__file__), '../../../swarm_knowledge.json')
864
- if not os.path.exists(path):
865
- return []
866
- with open(path) as f:
867
- knowledge = json.load(f)
868
- # Find similar tasks
869
- task_str = json.dumps(messages)
870
- return [entry for entry in knowledge if entry.get('task_str') == task_str]
871
-
872
- def write_to_swarm_log(self, log):
873
- import json
874
-
875
- from filelock import FileLock, Timeout
876
- path = os.path.join(os.path.dirname(__file__), '../../../swarm_log.json')
877
- lock_path = path + '.lock'
878
- log['task_str'] = json.dumps(log['task'])
879
- for attempt in range(10):
880
- try:
881
- with FileLock(lock_path, timeout=5):
882
- if os.path.exists(path):
883
- with open(path) as f:
884
- try:
885
- logs = json.load(f)
886
- except json.JSONDecodeError:
887
- logs = []
888
- else:
889
- logs = []
890
- logs.append(log)
891
- with open(path, 'w') as f:
892
- json.dump(logs, f, indent=2)
893
- break
894
- except Timeout:
895
- time.sleep(0.2 * (attempt + 1))
896
-
897
- def check_approval(self, tool_name, **kwargs):
898
- policy = self.approval_policy.get(tool_name, "allow")
899
- if policy == "deny":
900
- print_operation_box(
901
- op_type="Approval Denied",
902
- results=[f"[DENIED] Tool '{tool_name}' is denied by approval policy."],
903
- params=None,
904
- result_type="error",
905
- summary="Approval denied",
906
- progress_line=None,
907
- spinner_state="Failed",
908
- operation_type="Approval",
909
- search_mode=None,
910
- total_lines=None
911
- )
912
- self.audit_logger.log_event("approval_denied", {"tool": tool_name, "kwargs": kwargs})
913
- raise PermissionError(f"Tool '{tool_name}' denied by approval policy.")
914
- elif policy == "ask":
915
- print_operation_box(
916
- op_type="Approval Requested",
917
- results=[f"[APPROVAL NEEDED] Tool '{tool_name}' wants to run with args: {kwargs}"],
918
- params=None,
919
- result_type="info",
920
- summary="Approval requested",
921
- progress_line=None,
922
- spinner_state="Waiting",
923
- operation_type="Approval",
924
- search_mode=None,
925
- total_lines=None
926
- )
927
- self.audit_logger.log_event("approval_requested", {"tool": tool_name, "kwargs": kwargs})
928
- resp = input("Approve? [y/N]: ").strip().lower()
929
- if resp != "y":
930
- print_operation_box(
931
- op_type="Approval Denied",
932
- results=[f"[DENIED] Tool '{tool_name}' not approved by user."],
933
- params=None,
934
- result_type="error",
935
- summary="Approval denied",
936
- progress_line=None,
937
- spinner_state="Failed",
938
- operation_type="Approval",
939
- search_mode=None,
940
- total_lines=None
941
- )
942
- self.audit_logger.log_event("approval_user_denied", {"tool": tool_name, "kwargs": kwargs})
943
- raise PermissionError(f"Tool '{tool_name}' denied by user.")
944
- self.audit_logger.log_event("approval_user_approved", {"tool": tool_name, "kwargs": kwargs})
945
- # else allow
946
-
947
- # Example: wrap file write and shell exec tools for approval
948
- def write_file_with_approval(self, path, content):
949
- self.check_approval("tool.fs.write", path=path)
950
- # Simulate file write (for demo)
951
- with open(path, "w") as f:
952
- f.write(content)
953
- print_operation_box(
954
- op_type="File Write",
955
- results=[f"File written: {path}"],
956
- params=None,
957
- result_type="info",
958
- summary="File written",
959
- progress_line=None,
960
- spinner_state="Done",
961
- operation_type="File Write",
962
- search_mode=None,
963
- total_lines=None
964
- )
965
-
966
- def shell_exec_with_approval(self, command):
967
- self.check_approval("tool.shell.exec", command=command)
968
- # Simulate shell exec (for demo)
969
- import subprocess
970
- result = subprocess.run(command, shell=True, capture_output=True, text=True)
971
- print_operation_box(
972
- op_type="Shell Exec",
973
- results=[f"Command output: {result.stdout.strip()}"],
974
- params=None,
975
- result_type="info",
976
- summary="Command executed",
977
- progress_line=None,
978
- spinner_state="Done",
979
- operation_type="Shell Exec",
980
- search_mode=None,
981
- total_lines=None
982
- )
983
- return result.stdout.strip()
984
-
985
- def get_cli_splash(self):
986
- return "Codey CLI - Approval Workflow Demo\nType --help for usage."
987
-
988
- if __name__ == "__main__":
989
- import asyncio
990
- import json
991
- import random
992
- import string
993
-
994
- print("\033[1;36m\n╔══════════════════════════════════════════════════════════════╗\n║ 🤖 CODEY: SWARM ULTIMATE LIMIT TEST ║\n╠══════════════════════════════════════════════════════════════╣\n║ ULTIMATE: Multi-agent, multi-step, parallel, self-modifying ║\n║ workflow with error injection, rollback, and viral patching. ║\n╚══════════════════════════════════════════════════════════════╝\033[0m")
995
-
996
- def random_string():
997
- return ''.join(random.choices(string.ascii_letters + string.digits, k=8))
998
-
999
- async def consume_asyncgen(agen):
1000
- results = []
1001
- async for item in agen:
1002
- results.append(item)
1003
- return results
1004
-
1005
- async def run_limit_test():
1006
- blueprint = CodeyBlueprint(blueprint_id="ultimate-limit-test")
1007
- tasks = []
1008
- # Step 1: Parallel file edits with injected errors and rollbacks
1009
- for i in range(3):
1010
- fname = f"swarm_test_{i}_{random_string()}.txt"
1011
- content = f"Swarm Power {i} - {random_string()}"
1012
- messages = [
1013
- {"role": "user", "content": f"Create file '{fname}' with content '{content}', commit, then inject an error, rollback, and verify file state."}
1014
- ]
1015
- tasks.append(consume_asyncgen(blueprint.run(messages)))
1016
- # Step 2: Orchestrated multi-agent workflow with viral patching
1017
- messages = [
1018
- {"role": "user", "content": "Agent A edits README.md, Agent B reviews and intentionally injects a bug, Agent C detects and patches it, Agent D commits and shows the diff. Log every step, agent, and patch."}
1019
- ]
1020
- tasks.append(consume_asyncgen(blueprint.run(messages)))
1021
- # Step 3: Self-modifying code and viral propagation
1022
- messages = [
1023
- {"role": "user", "content": "Modify your own blueprint to add a new function 'swarm_propagate', propagate it to another blueprint, and verify the function exists in both. Log all steps."}
1024
- ]
1025
- tasks.append(consume_asyncgen(blueprint.run(messages)))
1026
- # Run all tasks in parallel, logging every intermediate step
1027
- results = await asyncio.gather(*tasks, return_exceptions=True)
1028
- for idx, result in enumerate(results):
1029
- print(f"\n[PARALLEL TASK {idx+1}] Result:")
1030
- if isinstance(result, Exception):
1031
- print(f"Exception: {result}")
1032
- else:
1033
- for response in result:
1034
- print(json.dumps(response, indent=2))
1035
-
1036
- class SwarmSpinner:
1037
- def __init__(self, console: Console, message: str = "Working..."):
1038
- self.console = console
1039
- self.message = message
1040
- self._stop_event = threading.Event()
1041
- self._start_time = time.time()
1042
- self._thread = threading.Thread(target=self._spin)
1043
- self._thread.start()
1044
-
1045
- # Codex-style spinner frames (standardized for Swarm blueprints)
1046
- FRAMES = [
1047
- "Generating.",
1048
- "Generating..",
1049
- "Generating...",
1050
- "Running..."
1051
- ]
1052
- SLOW_FRAME = "Generating... Taking longer than expected"
1053
- INTERVAL = 0.12
1054
- SLOW_THRESHOLD = 10 # seconds
1055
-
1056
- def _spin(self):
1057
- idx = 0
1058
- while not self._stop_event.is_set():
1059
- elapsed = time.time() - self._start_time
1060
- if elapsed > self.SLOW_THRESHOLD:
1061
- txt = Text(self.SLOW_FRAME, style=Style(color="yellow", bold=True))
1062
- else:
1063
- frame = self.FRAMES[idx % len(self.FRAMES)]
1064
- txt = Text(frame, style=Style(color="cyan", bold=True))
1065
- self.console.print(txt, end="\r", soft_wrap=True, highlight=False)
1066
- time.sleep(self.INTERVAL)
1067
- idx += 1
1068
- self.console.print(" " * 40, end="\r") # Clear line
1069
-
1070
- def stop(self):
1071
- self._stop_event.set()
1072
- self._thread.join()