open-swarm 0.1.1745274976__py3-none-any.whl → 0.1.1748636259__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. open_swarm-0.1.1748636259.dist-info/METADATA +188 -0
  2. open_swarm-0.1.1748636259.dist-info/RECORD +82 -0
  3. {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1748636259.dist-info}/WHEEL +2 -1
  4. open_swarm-0.1.1748636259.dist-info/entry_points.txt +3 -0
  5. open_swarm-0.1.1748636259.dist-info/top_level.txt +1 -0
  6. swarm/agent/agent.py +49 -0
  7. swarm/auth.py +48 -113
  8. swarm/consumers.py +0 -19
  9. swarm/extensions/blueprint/__init__.py +16 -30
  10. swarm/{core → extensions/blueprint}/agent_utils.py +1 -1
  11. swarm/extensions/blueprint/blueprint_base.py +458 -0
  12. swarm/extensions/blueprint/blueprint_discovery.py +112 -0
  13. swarm/extensions/blueprint/output_utils.py +95 -0
  14. swarm/{core → extensions/blueprint}/spinner.py +21 -30
  15. swarm/extensions/cli/cli_args.py +0 -6
  16. swarm/extensions/cli/commands/blueprint_management.py +9 -47
  17. swarm/extensions/cli/commands/config_management.py +6 -5
  18. swarm/extensions/cli/commands/edit_config.py +7 -16
  19. swarm/extensions/cli/commands/list_blueprints.py +1 -1
  20. swarm/extensions/cli/commands/validate_env.py +4 -11
  21. swarm/extensions/cli/commands/validate_envvars.py +6 -6
  22. swarm/extensions/cli/interactive_shell.py +2 -16
  23. swarm/extensions/config/config_loader.py +201 -107
  24. swarm/{core → extensions/config}/config_manager.py +38 -50
  25. swarm/{core → extensions/config}/server_config.py +0 -32
  26. swarm/extensions/launchers/build_launchers.py +14 -0
  27. swarm/{core → extensions/launchers}/build_swarm_wrapper.py +0 -0
  28. swarm/extensions/launchers/swarm_api.py +64 -8
  29. swarm/extensions/launchers/swarm_cli.py +300 -8
  30. swarm/llm/chat_completion.py +195 -0
  31. swarm/serializers.py +5 -96
  32. swarm/settings.py +111 -99
  33. swarm/urls.py +74 -57
  34. swarm/utils/context_utils.py +4 -10
  35. swarm/utils/general_utils.py +0 -21
  36. swarm/utils/redact.py +36 -23
  37. swarm/views/api_views.py +39 -48
  38. swarm/views/chat_views.py +70 -237
  39. swarm/views/core_views.py +87 -80
  40. swarm/views/model_views.py +121 -64
  41. swarm/views/utils.py +441 -65
  42. swarm/views/web_views.py +2 -2
  43. open_swarm-0.1.1745274976.dist-info/METADATA +0 -874
  44. open_swarm-0.1.1745274976.dist-info/RECORD +0 -318
  45. open_swarm-0.1.1745274976.dist-info/entry_points.txt +0 -4
  46. swarm/blueprints/README.md +0 -68
  47. swarm/blueprints/blueprint_audit_status.json +0 -27
  48. swarm/blueprints/chatbot/README.md +0 -40
  49. swarm/blueprints/chatbot/blueprint_chatbot.py +0 -471
  50. swarm/blueprints/chatbot/metadata.json +0 -23
  51. swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -33
  52. swarm/blueprints/chucks_angels/README.md +0 -11
  53. swarm/blueprints/chucks_angels/blueprint_chucks_angels.py +0 -7
  54. swarm/blueprints/chucks_angels/test_basic.py +0 -3
  55. swarm/blueprints/codey/CODEY.md +0 -15
  56. swarm/blueprints/codey/README.md +0 -115
  57. swarm/blueprints/codey/blueprint_codey.py +0 -1072
  58. swarm/blueprints/codey/codey_cli.py +0 -373
  59. swarm/blueprints/codey/instructions.md +0 -17
  60. swarm/blueprints/codey/metadata.json +0 -23
  61. swarm/blueprints/common/operation_box_utils.py +0 -83
  62. swarm/blueprints/digitalbutlers/README.md +0 -11
  63. swarm/blueprints/digitalbutlers/__init__.py +0 -1
  64. swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -7
  65. swarm/blueprints/digitalbutlers/test_basic.py +0 -3
  66. swarm/blueprints/divine_code/README.md +0 -3
  67. swarm/blueprints/divine_code/__init__.py +0 -10
  68. swarm/blueprints/divine_code/apps.py +0 -11
  69. swarm/blueprints/divine_code/blueprint_divine_code.py +0 -270
  70. swarm/blueprints/django_chat/apps.py +0 -6
  71. swarm/blueprints/django_chat/blueprint_django_chat.py +0 -268
  72. swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -37
  73. swarm/blueprints/django_chat/urls.py +0 -8
  74. swarm/blueprints/django_chat/views.py +0 -32
  75. swarm/blueprints/echocraft/blueprint_echocraft.py +0 -384
  76. swarm/blueprints/flock/README.md +0 -11
  77. swarm/blueprints/flock/__init__.py +0 -8
  78. swarm/blueprints/flock/blueprint_flock.py +0 -7
  79. swarm/blueprints/flock/test_basic.py +0 -3
  80. swarm/blueprints/geese/README.md +0 -97
  81. swarm/blueprints/geese/blueprint_geese.py +0 -803
  82. swarm/blueprints/geese/geese_cli.py +0 -102
  83. swarm/blueprints/jeeves/README.md +0 -41
  84. swarm/blueprints/jeeves/blueprint_jeeves.py +0 -722
  85. swarm/blueprints/jeeves/jeeves_cli.py +0 -55
  86. swarm/blueprints/jeeves/metadata.json +0 -24
  87. swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -473
  88. swarm/blueprints/messenger/templates/messenger/messenger.html +0 -46
  89. swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -423
  90. swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -340
  91. swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -265
  92. swarm/blueprints/omniplex/blueprint_omniplex.py +0 -298
  93. swarm/blueprints/poets/blueprint_poets.py +0 -546
  94. swarm/blueprints/poets/poets_cli.py +0 -23
  95. swarm/blueprints/rue_code/README.md +0 -8
  96. swarm/blueprints/rue_code/blueprint_rue_code.py +0 -448
  97. swarm/blueprints/rue_code/rue_code_cli.py +0 -43
  98. swarm/blueprints/stewie/apps.py +0 -12
  99. swarm/blueprints/stewie/blueprint_family_ties.py +0 -349
  100. swarm/blueprints/stewie/models.py +0 -19
  101. swarm/blueprints/stewie/serializers.py +0 -10
  102. swarm/blueprints/stewie/settings.py +0 -17
  103. swarm/blueprints/stewie/urls.py +0 -11
  104. swarm/blueprints/stewie/views.py +0 -26
  105. swarm/blueprints/suggestion/blueprint_suggestion.py +0 -222
  106. swarm/blueprints/whinge_surf/README.md +0 -22
  107. swarm/blueprints/whinge_surf/__init__.py +0 -1
  108. swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +0 -565
  109. swarm/blueprints/whinge_surf/whinge_surf_cli.py +0 -99
  110. swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
  111. swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -11
  112. swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -339
  113. swarm/blueprints/zeus/__init__.py +0 -2
  114. swarm/blueprints/zeus/apps.py +0 -4
  115. swarm/blueprints/zeus/blueprint_zeus.py +0 -270
  116. swarm/blueprints/zeus/zeus_cli.py +0 -13
  117. swarm/cli/async_input.py +0 -65
  118. swarm/cli/async_input_demo.py +0 -32
  119. swarm/core/blueprint_base.py +0 -769
  120. swarm/core/blueprint_discovery.py +0 -125
  121. swarm/core/blueprint_runner.py +0 -59
  122. swarm/core/blueprint_ux.py +0 -109
  123. swarm/core/build_launchers.py +0 -15
  124. swarm/core/cli/__init__.py +0 -1
  125. swarm/core/cli/commands/__init__.py +0 -1
  126. swarm/core/cli/commands/blueprint_management.py +0 -7
  127. swarm/core/cli/interactive_shell.py +0 -14
  128. swarm/core/cli/main.py +0 -50
  129. swarm/core/cli/utils/__init__.py +0 -1
  130. swarm/core/cli/utils/discover_commands.py +0 -18
  131. swarm/core/config_loader.py +0 -122
  132. swarm/core/output_utils.py +0 -193
  133. swarm/core/session_logger.py +0 -42
  134. swarm/core/slash_commands.py +0 -89
  135. swarm/core/swarm_api.py +0 -68
  136. swarm/core/swarm_cli.py +0 -216
  137. swarm/core/utils/__init__.py +0 -0
  138. swarm/extensions/blueprint/cli_handler.py +0 -197
  139. swarm/extensions/blueprint/runnable_blueprint.py +0 -42
  140. swarm/extensions/cli/utils/__init__.py +0 -1
  141. swarm/extensions/cli/utils/async_input.py +0 -46
  142. swarm/extensions/cli/utils/prompt_user.py +0 -3
  143. swarm/management/__init__.py +0 -0
  144. swarm/management/commands/__init__.py +0 -0
  145. swarm/management/commands/runserver.py +0 -58
  146. swarm/middleware.py +0 -65
  147. swarm/permissions.py +0 -38
  148. swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -7
  149. swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -7
  150. swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -7
  151. swarm/static/contrib/markedjs/marked.min.js +0 -6
  152. swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -27
  153. swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -21
  154. swarm/static/contrib/tabler-icons/archive.svg +0 -21
  155. swarm/static/contrib/tabler-icons/artboard.svg +0 -27
  156. swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -23
  157. swarm/static/contrib/tabler-icons/box-multiple.svg +0 -19
  158. swarm/static/contrib/tabler-icons/carambola.svg +0 -19
  159. swarm/static/contrib/tabler-icons/copy.svg +0 -20
  160. swarm/static/contrib/tabler-icons/download.svg +0 -21
  161. swarm/static/contrib/tabler-icons/edit.svg +0 -21
  162. swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -13
  163. swarm/static/contrib/tabler-icons/filled/paint.svg +0 -13
  164. swarm/static/contrib/tabler-icons/headset.svg +0 -22
  165. swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -21
  166. swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -21
  167. swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -21
  168. swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -21
  169. swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -22
  170. swarm/static/contrib/tabler-icons/message-star.svg +0 -22
  171. swarm/static/contrib/tabler-icons/message-x.svg +0 -23
  172. swarm/static/contrib/tabler-icons/message.svg +0 -21
  173. swarm/static/contrib/tabler-icons/paperclip.svg +0 -18
  174. swarm/static/contrib/tabler-icons/playlist-add.svg +0 -22
  175. swarm/static/contrib/tabler-icons/robot.svg +0 -26
  176. swarm/static/contrib/tabler-icons/search.svg +0 -19
  177. swarm/static/contrib/tabler-icons/settings.svg +0 -20
  178. swarm/static/contrib/tabler-icons/thumb-down.svg +0 -19
  179. swarm/static/contrib/tabler-icons/thumb-up.svg +0 -19
  180. swarm/static/css/dropdown.css +0 -22
  181. swarm/static/htmx/htmx.min.js +0 -0
  182. swarm/static/js/dropdown.js +0 -23
  183. swarm/static/rest_mode/css/base.css +0 -470
  184. swarm/static/rest_mode/css/chat-history.css +0 -286
  185. swarm/static/rest_mode/css/chat.css +0 -251
  186. swarm/static/rest_mode/css/chatbot.css +0 -74
  187. swarm/static/rest_mode/css/chatgpt.css +0 -62
  188. swarm/static/rest_mode/css/colors/corporate.css +0 -74
  189. swarm/static/rest_mode/css/colors/pastel.css +0 -81
  190. swarm/static/rest_mode/css/colors/tropical.css +0 -82
  191. swarm/static/rest_mode/css/general.css +0 -142
  192. swarm/static/rest_mode/css/layout.css +0 -167
  193. swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -17
  194. swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -57
  195. swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -8
  196. swarm/static/rest_mode/css/messages.css +0 -84
  197. swarm/static/rest_mode/css/messenger.css +0 -135
  198. swarm/static/rest_mode/css/settings.css +0 -91
  199. swarm/static/rest_mode/css/simple.css +0 -44
  200. swarm/static/rest_mode/css/slack.css +0 -58
  201. swarm/static/rest_mode/css/style.css +0 -156
  202. swarm/static/rest_mode/css/theme.css +0 -30
  203. swarm/static/rest_mode/css/toast.css +0 -40
  204. swarm/static/rest_mode/js/auth.js +0 -9
  205. swarm/static/rest_mode/js/blueprint.js +0 -41
  206. swarm/static/rest_mode/js/blueprintUtils.js +0 -12
  207. swarm/static/rest_mode/js/chatLogic.js +0 -79
  208. swarm/static/rest_mode/js/debug.js +0 -63
  209. swarm/static/rest_mode/js/events.js +0 -98
  210. swarm/static/rest_mode/js/main.js +0 -19
  211. swarm/static/rest_mode/js/messages.js +0 -264
  212. swarm/static/rest_mode/js/messengerLogic.js +0 -355
  213. swarm/static/rest_mode/js/modules/apiService.js +0 -84
  214. swarm/static/rest_mode/js/modules/blueprintManager.js +0 -162
  215. swarm/static/rest_mode/js/modules/chatHistory.js +0 -110
  216. swarm/static/rest_mode/js/modules/debugLogger.js +0 -14
  217. swarm/static/rest_mode/js/modules/eventHandlers.js +0 -107
  218. swarm/static/rest_mode/js/modules/messageProcessor.js +0 -120
  219. swarm/static/rest_mode/js/modules/state.js +0 -7
  220. swarm/static/rest_mode/js/modules/userInteractions.js +0 -29
  221. swarm/static/rest_mode/js/modules/validation.js +0 -23
  222. swarm/static/rest_mode/js/rendering.js +0 -119
  223. swarm/static/rest_mode/js/settings.js +0 -130
  224. swarm/static/rest_mode/js/sidebar.js +0 -94
  225. swarm/static/rest_mode/js/simpleLogic.js +0 -37
  226. swarm/static/rest_mode/js/slackLogic.js +0 -66
  227. swarm/static/rest_mode/js/splash.js +0 -76
  228. swarm/static/rest_mode/js/theme.js +0 -111
  229. swarm/static/rest_mode/js/toast.js +0 -36
  230. swarm/static/rest_mode/js/ui.js +0 -265
  231. swarm/static/rest_mode/js/validation.js +0 -57
  232. swarm/static/rest_mode/svg/animated_spinner.svg +0 -12
  233. swarm/static/rest_mode/svg/arrow_down.svg +0 -5
  234. swarm/static/rest_mode/svg/arrow_left.svg +0 -5
  235. swarm/static/rest_mode/svg/arrow_right.svg +0 -5
  236. swarm/static/rest_mode/svg/arrow_up.svg +0 -5
  237. swarm/static/rest_mode/svg/attach.svg +0 -8
  238. swarm/static/rest_mode/svg/avatar.svg +0 -7
  239. swarm/static/rest_mode/svg/canvas.svg +0 -6
  240. swarm/static/rest_mode/svg/chat_history.svg +0 -4
  241. swarm/static/rest_mode/svg/close.svg +0 -5
  242. swarm/static/rest_mode/svg/copy.svg +0 -4
  243. swarm/static/rest_mode/svg/dark_mode.svg +0 -3
  244. swarm/static/rest_mode/svg/edit.svg +0 -5
  245. swarm/static/rest_mode/svg/layout.svg +0 -9
  246. swarm/static/rest_mode/svg/logo.svg +0 -29
  247. swarm/static/rest_mode/svg/logout.svg +0 -5
  248. swarm/static/rest_mode/svg/mobile.svg +0 -5
  249. swarm/static/rest_mode/svg/new_chat.svg +0 -4
  250. swarm/static/rest_mode/svg/not_visible.svg +0 -5
  251. swarm/static/rest_mode/svg/plus.svg +0 -7
  252. swarm/static/rest_mode/svg/run_code.svg +0 -6
  253. swarm/static/rest_mode/svg/save.svg +0 -4
  254. swarm/static/rest_mode/svg/search.svg +0 -6
  255. swarm/static/rest_mode/svg/settings.svg +0 -4
  256. swarm/static/rest_mode/svg/speaker.svg +0 -5
  257. swarm/static/rest_mode/svg/stop.svg +0 -6
  258. swarm/static/rest_mode/svg/thumbs_down.svg +0 -3
  259. swarm/static/rest_mode/svg/thumbs_up.svg +0 -3
  260. swarm/static/rest_mode/svg/toggle_off.svg +0 -6
  261. swarm/static/rest_mode/svg/toggle_on.svg +0 -6
  262. swarm/static/rest_mode/svg/trash.svg +0 -10
  263. swarm/static/rest_mode/svg/undo.svg +0 -3
  264. swarm/static/rest_mode/svg/visible.svg +0 -8
  265. swarm/static/rest_mode/svg/voice.svg +0 -10
  266. swarm/templates/account/login.html +0 -22
  267. swarm/templates/account/signup.html +0 -32
  268. swarm/templates/base.html +0 -30
  269. swarm/templates/chat.html +0 -43
  270. swarm/templates/index.html +0 -35
  271. swarm/templates/rest_mode/components/chat_sidebar.html +0 -55
  272. swarm/templates/rest_mode/components/header.html +0 -45
  273. swarm/templates/rest_mode/components/main_chat_pane.html +0 -41
  274. swarm/templates/rest_mode/components/settings_dialog.html +0 -97
  275. swarm/templates/rest_mode/components/splash_screen.html +0 -7
  276. swarm/templates/rest_mode/components/top_bar.html +0 -28
  277. swarm/templates/rest_mode/message_ui.html +0 -50
  278. swarm/templates/rest_mode/slackbot.html +0 -30
  279. swarm/templates/simple_blueprint_page.html +0 -24
  280. swarm/templates/websocket_partials/final_system_message.html +0 -3
  281. swarm/templates/websocket_partials/system_message.html +0 -4
  282. swarm/templates/websocket_partials/user_message.html +0 -5
  283. swarm/utils/ansi_box.py +0 -34
  284. swarm/utils/disable_tracing.py +0 -38
  285. swarm/utils/log_utils.py +0 -63
  286. swarm/utils/openai_patch.py +0 -33
  287. swarm/ux/ansi_box.py +0 -43
  288. swarm/ux/spinner.py +0 -53
  289. {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1748636259.dist-info}/licenses/LICENSE +0 -0
  290. /swarm/{core → extensions/blueprint}/blueprint_utils.py +0 -0
  291. /swarm/{core → extensions/blueprint}/common_utils.py +0 -0
  292. /swarm/{core → extensions/config}/setup_wizard.py +0 -0
  293. /swarm/{blueprints/rue_code → extensions/config/utils}/__init__.py +0 -0
  294. /swarm/{core → extensions/config}/utils/logger.py +0 -0
  295. /swarm/{core → extensions/launchers}/swarm_wrapper.py +0 -0
swarm/serializers.py CHANGED
@@ -1,103 +1,12 @@
1
1
  from rest_framework import serializers
2
- from swarm.models import ChatMessage
3
- import logging
4
-
5
- logger = logging.getLogger(__name__)
6
- print_logger = logging.getLogger('print_debug')
7
-
8
- class MessageSerializer(serializers.Serializer):
9
- role = serializers.ChoiceField(choices=["system", "user", "assistant", "tool"])
10
- # Content is CharField, allows null/blank by default
11
- content = serializers.CharField(allow_null=True, required=False, allow_blank=True)
12
- name = serializers.CharField(required=False, allow_blank=True)
13
-
14
- # Removed validate_content
15
-
16
- def validate(self, data):
17
- """Validate message structure based on role."""
18
- print_logger.debug(f"MessageSerializer.validate received data: {data}")
19
- role = data.get('role')
20
- content = data.get('content', None)
21
- name = data.get('name')
22
-
23
- # Role validation
24
- if 'role' not in data:
25
- raise serializers.ValidationError({"role": ["This field is required."]})
26
-
27
- # Content requiredness validation (based on role)
28
- content_required = role in ['system', 'user', 'assistant', 'tool']
29
- content_present = 'content' in data
30
-
31
- if content_required:
32
- if not content_present:
33
- raise serializers.ValidationError({"content": ["This field is required."]})
34
- # Null/Blank checks are handled by field definition (allow_null/allow_blank)
35
- # Type check will happen in ChatCompletionRequestSerializer.validate_messages
36
-
37
- # Name validation for tool role
38
- if role == 'tool' and not name:
39
- raise serializers.ValidationError({"name": ["This field is required for role 'tool'."]})
40
-
41
- print_logger.debug(f"MessageSerializer.validate PASSED for data: {data}")
42
- return data
43
-
44
- class ChatCompletionRequestSerializer(serializers.Serializer):
45
- model = serializers.CharField(max_length=255)
46
- messages = MessageSerializer(many=True, min_length=1)
47
- stream = serializers.BooleanField(default=False)
48
- params = serializers.JSONField(required=False, allow_null=True)
49
-
50
- def validate(self, data):
51
- """Perform object-level validation."""
52
- model_value = self.initial_data.get('model')
53
- logger.debug(f"Top-level validate checking model type. Got: {type(model_value)}, value: {model_value}")
54
- if model_value is not None and not isinstance(model_value, str):
55
- raise serializers.ValidationError({"model": ["Field 'model' must be a string."]})
56
- # Messages validation (including content type) happens in validate_messages
57
- return data
58
-
59
- def validate_messages(self, value):
60
- """
61
- Validate the messages list itself and perform raw type checks.
62
- 'value' here is the list *after* MessageSerializer has run on each item.
63
- We need to inspect `self.initial_data` for the raw types.
64
- """
65
- if not value:
66
- raise serializers.ValidationError("Messages list cannot be empty.")
67
-
68
- # Access raw message data from initial_data for type checking
69
- raw_messages = self.initial_data.get('messages', [])
70
- if not isinstance(raw_messages, list):
71
- # This case is handled by ListField implicitly, but good to be explicit
72
- raise serializers.ValidationError("Expected a list of message items.")
73
-
74
- errors = []
75
- for i, raw_msg in enumerate(raw_messages):
76
- msg_errors = {}
77
- if not isinstance(raw_msg, dict):
78
- # If the item itself isn't a dict, add error and skip further checks for it
79
- errors.append({f"item_{i}": "Each message must be a dictionary."})
80
- continue
81
-
82
- # *** Check raw content type here ***
83
- content = raw_msg.get('content', None)
84
- if 'content' in raw_msg and content is not None and not isinstance(content, str):
85
- msg_errors['content'] = ["Content must be a string or null."] # Match test assertion
86
-
87
- # Add other raw checks if needed (e.g., role type)
88
-
89
- if msg_errors:
90
- errors.append(msg_errors) # Append errors for this specific message index
91
-
92
- if errors:
93
- # Raise a single validation error containing all message-specific errors
94
- raise serializers.ValidationError(errors)
95
-
96
- # Return the processed 'value' which passed MessageSerializer validation
97
- return value
2
+ from swarm.models import ChatMessage, ChatConversation
98
3
 
99
4
  class ChatMessageSerializer(serializers.ModelSerializer):
100
5
  class Meta:
101
6
  model = ChatMessage
102
7
  fields = '__all__'
103
8
 
9
+ class ChatConversationSerializer(serializers.ModelSerializer):
10
+ class Meta:
11
+ model = ChatConversation
12
+ fields = '__all__'
swarm/settings.py CHANGED
@@ -1,46 +1,48 @@
1
1
  """
2
2
  Django settings for swarm project.
3
+ Includes Pydantic base settings for Swarm Core.
3
4
  """
4
5
 
6
+ import logging
5
7
  import os
8
+ import sys
9
+ from enum import Enum
6
10
  from pathlib import Path
7
- from dotenv import load_dotenv
8
- import logging
11
+ from pydantic import Field # Import Field from Pydantic v2+
12
+ from pydantic_settings import BaseSettings, SettingsConfigDict
13
+
14
+ # --- Pydantic Settings for Swarm Core ---
15
+ class LogFormat(str, Enum):
16
+ standard = "[%(levelname)s] %(asctime)s - %(name)s:%(lineno)d - %(message)s"
17
+ simple = "[%(levelname)s] %(name)s - %(message)s"
9
18
 
10
- BASE_DIR = Path(__file__).resolve().parent.parent # Points to src/
19
+ class Settings(BaseSettings):
20
+ model_config = SettingsConfigDict(env_prefix='SWARM_', case_sensitive=False)
11
21
 
12
- # --- Load .env file ---
13
- dotenv_path = BASE_DIR.parent / '.env'
14
- load_dotenv(dotenv_path=dotenv_path)
15
- # print(f"[Settings] Attempted to load .env from: {dotenv_path}")
16
- # ---
22
+ log_level: str = Field(default='INFO', description="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)")
23
+ log_format: LogFormat = Field(default=LogFormat.standard, description="Logging format")
24
+ debug: bool = Field(default=False, description="Global debug flag")
17
25
 
18
- SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'django-insecure-fallback-key-for-dev')
19
- DEBUG = os.getenv('DJANGO_DEBUG', 'True').lower() in ('true', '1', 't')
20
- ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
26
+ # --- Standard Django Settings ---
21
27
 
22
- # --- Custom Swarm Settings ---
23
- # Load the token from environment
24
- _raw_api_token = os.getenv('API_AUTH_TOKEN')
28
+ # Build paths inside the project like this: BASE_DIR / 'subdir'.
29
+ BASE_DIR = Path(__file__).resolve().parent.parent
30
+ PROJECT_ROOT = BASE_DIR.parent
31
+ if str(PROJECT_ROOT) not in sys.path:
32
+ sys.path.insert(0, str(PROJECT_ROOT))
25
33
 
26
- # *** Only enable API auth if the token is actually set ***
27
- ENABLE_API_AUTH = bool(_raw_api_token)
28
- SWARM_API_KEY = _raw_api_token # Assign the loaded token (or None)
34
+ BLUEPRINTS_DIR = PROJECT_ROOT / 'blueprints'
29
35
 
30
- if ENABLE_API_AUTH:
31
- # Add assertion to satisfy type checkers within this block
32
- assert SWARM_API_KEY is not None, "SWARM_API_KEY cannot be None when ENABLE_API_AUTH is True"
33
- # print(f"[Settings] SWARM_API_KEY loaded: {SWARM_API_KEY[:4]}...{SWARM_API_KEY[-4:]}")
34
- # print("[Settings] ENABLE_API_AUTH is True.")
35
- else:
36
- # print("[Settings] API_AUTH_TOKEN env var not set. SWARM_API_KEY is None.")
37
- # print("[Settings] ENABLE_API_AUTH is False.")
38
- pass
36
+ # --- Determine if running under pytest ---
37
+ TESTING = 'pytest' in sys.modules
39
38
 
40
- SWARM_CONFIG_PATH = os.getenv('SWARM_CONFIG_PATH', str(BASE_DIR.parent / 'swarm_config.json'))
41
- BLUEPRINT_DIRECTORY = os.getenv('BLUEPRINT_DIRECTORY', str(BASE_DIR / 'swarm' / 'blueprints'))
42
- # --- End Custom Swarm Settings ---
39
+ # Quick-start development settings - unsuitable for production
40
+ SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'django-insecure-YOUR_FALLBACK_KEY_HERE_CHANGE_ME')
41
+ # Use the Pydantic setting value for Django's DEBUG
42
+ DEBUG = Settings().debug # Read from instantiated Pydantic settings
43
+ ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',')
43
44
 
45
+ # --- Application definition ---
44
46
  INSTALLED_APPS = [
45
47
  'django.contrib.admin',
46
48
  'django.contrib.auth',
@@ -48,20 +50,61 @@ INSTALLED_APPS = [
48
50
  'django.contrib.sessions',
49
51
  'django.contrib.messages',
50
52
  'django.contrib.staticfiles',
53
+ # Third-party apps
51
54
  'rest_framework',
52
55
  'rest_framework.authtoken',
53
56
  'drf_spectacular',
54
- 'swarm',
57
+ # Local apps
58
+ 'swarm.apps.SwarmConfig',
55
59
  ]
56
60
 
61
+ # # --- Conditionally add blueprint apps for TESTING ---
62
+ # if TESTING:
63
+ # _test_apps_to_add = ['blueprints.university']
64
+ # for app in _test_apps_to_add:
65
+ # if app not in INSTALLED_APPS:
66
+ # INSTALLED_APPS.insert(0, app)
67
+ # logging.info(f"Settings [TESTING]: Added '{app}' to INSTALLED_APPS.")
68
+ # if 'SWARM_BLUEPRINTS' not in os.environ:
69
+ # os.environ['SWARM_BLUEPRINTS'] = 'university'
70
+ # logging.info(f"Settings [TESTING]: Set SWARM_BLUEPRINTS='university'")
71
+ # else:
72
+ # # --- Dynamic App Loading for Production/Development ---
73
+ # _INITIAL_BLUEPRINT_APPS = []
74
+ # _swarm_blueprints_env = os.getenv('SWARM_BLUEPRINTS')
75
+ # _log_source = "Not Set"
76
+ # if _swarm_blueprints_env:
77
+ # _blueprint_names = [name.strip() for name in _swarm_blueprints_env.split(',') if name.strip()]
78
+ # _INITIAL_BLUEPRINT_APPS = [f'blueprints.{name}' for name in _blueprint_names if name.replace('_', '').isidentifier()]
79
+ # _log_source = "SWARM_BLUEPRINTS env var"
80
+ # logging.info(f"Settings: Found blueprints from env var: {_INITIAL_BLUEPRINT_APPS}")
81
+ # else:
82
+ # _log_source = "directory scan"
83
+ # try:
84
+ # if BLUEPRINTS_DIR.is_dir():
85
+ # for item in BLUEPRINTS_DIR.iterdir():
86
+ # if item.is_dir() and (item / '__init__.py').exists():
87
+ # if item.name.replace('_', '').isidentifier():
88
+ # _INITIAL_BLUEPRINT_APPS.append(f'blueprints.{item.name}')
89
+ # logging.info(f"Settings: Found blueprints from directory scan: {_INITIAL_BLUEPRINT_APPS}")
90
+ # except Exception as e:
91
+ # logging.error(f"Settings: Error discovering blueprint apps during initial load: {e}")
92
+
93
+ # for app in _INITIAL_BLUEPRINT_APPS:
94
+ # if app not in INSTALLED_APPS:
95
+ # INSTALLED_APPS.append(app)
96
+ # logging.info(f"Settings [{_log_source}]: Added '{app}' to INSTALLED_APPS.")
97
+ # # --- End App Loading Logic ---
98
+
99
+ if isinstance(INSTALLED_APPS, tuple): INSTALLED_APPS = list(INSTALLED_APPS)
100
+ logging.info(f"Settings: Final INSTALLED_APPS = {INSTALLED_APPS}")
101
+
57
102
  MIDDLEWARE = [
58
103
  'django.middleware.security.SecurityMiddleware',
59
104
  'django.contrib.sessions.middleware.SessionMiddleware',
60
105
  'django.middleware.common.CommonMiddleware',
61
106
  'django.middleware.csrf.CsrfViewMiddleware',
62
107
  'django.contrib.auth.middleware.AuthenticationMiddleware',
63
- # Add custom middleware to handle async user loading after standard auth
64
- 'swarm.middleware.AsyncAuthMiddleware',
65
108
  'django.contrib.messages.middleware.MessageMiddleware',
66
109
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
67
110
  ]
@@ -71,7 +114,7 @@ ROOT_URLCONF = 'swarm.urls'
71
114
  TEMPLATES = [
72
115
  {
73
116
  'BACKEND': 'django.template.backends.django.DjangoTemplates',
74
- 'DIRS': [BASE_DIR.parent / 'templates'],
117
+ 'DIRS': [BASE_DIR / 'templates'],
75
118
  'APP_DIRS': True,
76
119
  'OPTIONS': {
77
120
  'context_processors': [
@@ -85,93 +128,62 @@ TEMPLATES = [
85
128
  ]
86
129
 
87
130
  WSGI_APPLICATION = 'swarm.wsgi.application'
88
- ASGI_APPLICATION = 'swarm.asgi.application'
89
-
90
- DATABASES = {
91
- 'default': {
92
- 'ENGINE': 'django.db.backends.sqlite3',
93
- 'NAME': os.environ.get('DJANGO_DB_NAME', '/tmp/db.sqlite3'),
94
- 'TEST': {
95
- 'NAME': os.environ.get('DJANGO_TEST_DB_NAME', '/tmp/test_db.sqlite3'),
96
- 'OPTIONS': {
97
- 'timeout': 20,
98
- 'init_command': "PRAGMA journal_mode=WAL;",
99
- },
100
- },
101
- }
102
- }
131
+
132
+ SQLITE_DB_PATH = os.getenv('SQLITE_DB_PATH', BASE_DIR / 'db.sqlite3')
133
+ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': SQLITE_DB_PATH } }
134
+ DJANGO_DATABASE = DATABASES['default']
103
135
 
104
136
  AUTH_PASSWORD_VALIDATORS = [
105
- {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},
106
- {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},
107
- {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},
108
- {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
137
+ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
138
+ {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
139
+ {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
140
+ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
109
141
  ]
110
142
 
111
- LANGUAGE_CODE = 'en-us'
112
- TIME_ZONE = 'UTC'
113
- USE_I18N = True
114
- USE_TZ = True
143
+ LANGUAGE_CODE = 'en-us'; TIME_ZONE = 'UTC'; USE_I18N = True; USE_TZ = True
115
144
 
116
- STATIC_URL = 'static/'
117
- STATIC_ROOT = BASE_DIR.parent / 'staticfiles'
118
- STATICFILES_DIRS = [ BASE_DIR / "swarm" / "static", ]
145
+ STATIC_URL = '/static/'; STATIC_ROOT = BASE_DIR / 'staticfiles'
146
+ STATICFILES_DIRS = [ BASE_DIR / 'static', BASE_DIR / 'assets' ]
119
147
 
120
148
  DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
121
149
 
122
150
  REST_FRAMEWORK = {
123
- 'DEFAULT_AUTHENTICATION_CLASSES': [
124
- 'swarm.auth.StaticTokenAuthentication',
125
- 'swarm.auth.CustomSessionAuthentication',
126
- ],
127
- # *** IMPORTANT: Add DEFAULT_PERMISSION_CLASSES ***
128
- # If ENABLE_API_AUTH is False, we might want to allow any access for testing.
129
- # If ENABLE_API_AUTH is True, we require HasValidTokenOrSession.
130
- # We need to set this dynamically based on ENABLE_API_AUTH.
131
- # A simple way is to set it here, but a cleaner way might involve middleware
132
- # or overriding get_permissions in views. For now, let's adjust this:
133
- 'DEFAULT_PERMISSION_CLASSES': [
134
- # If auth is enabled, require our custom permission
135
- 'swarm.permissions.HasValidTokenOrSession' if ENABLE_API_AUTH else
136
- # Otherwise, allow anyone (useful for dev when token isn't set)
137
- 'rest_framework.permissions.AllowAny'
138
- ],
139
151
  'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
152
+ 'DEFAULT_AUTHENTICATION_CLASSES': (
153
+ 'swarm.auth.EnvOrTokenAuthentication',
154
+ 'rest_framework.authentication.TokenAuthentication',
155
+ 'rest_framework.authentication.SessionAuthentication',
156
+ ),
157
+ 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',)
140
158
  }
141
159
 
142
160
  SPECTACULAR_SETTINGS = {
143
161
  'TITLE': 'Open Swarm API',
144
- 'DESCRIPTION': 'API for managing autonomous agent swarms',
145
- 'VERSION': '0.2.0',
162
+ 'DESCRIPTION': 'API for the Open Swarm multi-agent collaboration framework.',
163
+ 'VERSION': '1.0.0',
146
164
  'SERVE_INCLUDE_SCHEMA': False,
165
+ 'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'],
147
166
  }
148
167
 
149
168
  LOGGING = {
150
- 'version': 1,
151
- 'disable_existing_loggers': False,
152
- 'formatters': {
153
- 'verbose': { 'format': '[{levelname}] {asctime} - {name}:{lineno} - {message}', 'style': '{', },
154
- 'simple': { 'format': '[{levelname}] {message}', 'style': '{', },
155
- },
156
- 'handlers': {
157
- 'console': { 'class': 'logging.StreamHandler', 'formatter': 'verbose', },
158
- },
169
+ 'version': 1, 'disable_existing_loggers': False,
170
+ 'formatters': { 'standard': { 'format': '[%(levelname)s] %(asctime)s - %(name)s:%(lineno)d - %(message)s' }, },
171
+ 'handlers': { 'console': { 'level': 'DEBUG' if DEBUG else 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'standard', }, },
159
172
  'loggers': {
160
- 'django': { 'handlers': ['console'], 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 'propagate': False, },
161
- 'swarm': { 'handlers': ['console'], 'level': os.getenv('SWARM_LOG_LEVEL', 'DEBUG'), 'propagate': False, },
162
- 'swarm.auth': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, },
163
- 'swarm.views': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, },
164
- 'swarm.extensions': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, },
165
- 'blueprint_django_chat': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, },
166
- 'print_debug': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, },
173
+ 'django': { 'handlers': ['console'], 'level': 'INFO', 'propagate': False, },
174
+ 'django.request': { 'handlers': ['console'], 'level': 'WARNING', 'propagate': False, },
175
+ 'swarm': { 'handlers': ['console'], 'level': 'DEBUG' if DEBUG else 'INFO', 'propagate': False, },
176
+ 'swarm.extensions': { 'handlers': ['console'], 'level': 'DEBUG' if DEBUG else 'INFO', 'propagate': False, },
177
+ 'blueprints': { 'handlers': ['console'], 'level': 'DEBUG' if DEBUG else 'INFO', 'propagate': False, },
167
178
  },
168
- 'root': { 'handlers': ['console'], 'level': 'WARNING', },
169
179
  }
170
180
 
181
+ AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
182
+ LOGIN_URL = '/accounts/login/'; LOGIN_REDIRECT_URL = '/chatbot/'
183
+
171
184
  REDIS_HOST = os.getenv('REDIS_HOST', 'localhost')
172
- REDIS_PORT = int(os.getenv('REDIS_PORT', '6379'))
185
+ REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
173
186
 
174
- LOGIN_URL = '/login/'
175
- LOGIN_REDIRECT_URL = '/'
176
- LOGOUT_REDIRECT_URL = '/'
177
- CSRF_TRUSTED_ORIGINS = os.getenv('DJANGO_CSRF_TRUSTED_ORIGINS', 'http://localhost:8000,http://127.0.0.1:8000').split(',')
187
+ if TESTING:
188
+ print("Pytest detected: Adjusting settings for testing.")
189
+ DATABASES['default']['NAME'] = ':memory:'
swarm/urls.py CHANGED
@@ -1,72 +1,89 @@
1
- """
2
- Swarm URL Configuration
3
- """
4
- import logging
5
1
  from django.contrib import admin
6
- from django.urls import path, include, reverse_lazy
2
+ from django.urls import path, re_path, include
3
+ from django.http import HttpResponse
7
4
  from django.conf import settings
8
- from django.views.generic import RedirectView
9
- from django.contrib.auth import views as auth_views
10
-
11
- from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
5
+ from django.conf.urls.static import static
6
+ import os
7
+ import logging
12
8
 
13
- from swarm.views.chat_views import ChatCompletionsView, HealthCheckView
14
- # *** Uncomment and correct the import path ***
15
- from swarm.views.api_views import ModelsListView
16
- # from swarm.views.webui_views import index as webui_index # Rename to avoid conflict
9
+ # Import specific views from their modules
10
+ from swarm.views.core_views import index as core_index_view, serve_swarm_config, custom_login
11
+ from swarm.views.chat_views import chat_completions
12
+ from swarm.views.model_views import list_models
13
+ from swarm.views.message_views import ChatMessageViewSet
14
+ from drf_spectacular.views import SpectacularSwaggerView, SpectacularAPIView as HiddenSpectacularAPIView
15
+ from rest_framework.routers import DefaultRouter
17
16
 
18
17
  logger = logging.getLogger(__name__)
19
18
 
20
- # ==============================================================================
21
- # API URL Patterns (v1)
22
- # ==============================================================================
23
- api_urlpatterns = [
24
- path('chat/completions', ChatCompletionsView.as_view(), name='chat_completions'),
25
- # *** Uncomment this URL pattern ***
26
- path('models', ModelsListView.as_view(), name='list_models'),
27
- path('health', HealthCheckView.as_view(), name='health_check'),
28
- # Add other v1 API endpoints here
29
- ]
30
-
31
- # ==============================================================================
32
- # Schema URL Patterns
33
- # ==============================================================================
34
- schema_urlpatterns = [
35
- path('schema/', SpectacularAPIView.as_view(), name='schema'),
36
- path('schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
37
- path('schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
38
- ]
19
+ def favicon(request):
20
+ favicon_path = settings.BASE_DIR / 'assets' / 'images' / 'favicon.ico'
21
+ try:
22
+ with open(favicon_path, 'rb') as f:
23
+ favicon_data = f.read()
24
+ return HttpResponse(favicon_data, content_type="image/x-icon")
25
+ except FileNotFoundError:
26
+ logger.warning("Favicon not found.")
27
+ return HttpResponse(status=404)
39
28
 
40
- # ==============================================================================
41
- # Main URL Patterns
42
- # ==============================================================================
43
- urlpatterns = [
44
- # Redirect root based on DEBUG setting
45
- path('', RedirectView.as_view(pattern_name='swagger-ui', permanent=False) if settings.DEBUG else RedirectView.as_view(pattern_name='login', permanent=False)),
29
+ ENABLE_ADMIN = os.getenv("ENABLE_ADMIN", "false").lower() in ("true", "1", "t")
30
+ ENABLE_WEBUI = os.getenv("ENABLE_WEBUI", "true").lower() in ("true", "1", "t")
46
31
 
47
- # API v1 endpoints
48
- path('v1/', include(api_urlpatterns)),
32
+ logger.debug(f"ENABLE_WEBUI={'true' if ENABLE_WEBUI else 'false'}")
33
+ logger.debug(f"ENABLE_ADMIN={'true' if ENABLE_ADMIN else 'false'}")
49
34
 
50
- # Schema endpoints
51
- path('api/', include(schema_urlpatterns)),
35
+ router = DefaultRouter()
36
+ # Ensure ChatMessageViewSet is available before registering
37
+ if ChatMessageViewSet:
38
+ router.register(r'v1/chat/messages', ChatMessageViewSet, basename='chatmessage')
39
+ else:
40
+ logger.warning("ChatMessageViewSet not imported correctly, skipping API registration.")
52
41
 
53
- # Django Admin (Optional)
54
- path('admin/', admin.site.urls) if getattr(settings, 'ENABLE_ADMIN', False) else path('admin/', RedirectView.as_view(url=reverse_lazy('login'))),
42
+ # Base URL patterns required by Swarm core
43
+ # Use the imported view functions directly
44
+ base_urlpatterns = [
45
+ re_path(r'^health/?$', lambda request: HttpResponse("OK"), name='health_check'),
46
+ re_path(r'^v1/chat/completions/?$', chat_completions, name='chat_completions'),
47
+ re_path(r'^v1/models/?$', list_models, name='list_models'),
48
+ re_path(r'^schema/?$', HiddenSpectacularAPIView.as_view(), name='schema'),
49
+ re_path(r'^swagger-ui/?$', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
50
+ ]
55
51
 
56
- # Authentication Views (Django Built-in)
57
- path('login/', auth_views.LoginView.as_view(template_name='swarm/login.html'), name='login'),
58
- path('logout/', auth_views.LogoutView.as_view(next_page=reverse_lazy('login')), name='logout'),
52
+ # Optional Admin URLs
53
+ admin_urlpatterns = [path('admin/', admin.site.urls)] if ENABLE_ADMIN else []
59
54
 
60
- # Web UI (Optional) - Conditionally include based on settings
61
- # *** Ensure this line remains commented out or removed if webui_index is not defined ***
62
- # path('webui/', webui_index, name='webui_index') if getattr(settings, 'ENABLE_WEBUI', False) else path('webui/', RedirectView.as_view(url=reverse_lazy('login'))),
63
- ]
55
+ # Optional Web UI URLs
56
+ webui_urlpatterns = []
57
+ if ENABLE_WEBUI:
58
+ webui_urlpatterns = [
59
+ path('', core_index_view, name='index'),
60
+ path('favicon.ico', favicon, name='favicon'),
61
+ path('config/swarm_config.json', serve_swarm_config, name='serve_swarm_config'),
62
+ path('accounts/login/', custom_login, name='custom_login'),
63
+ ]
64
+ if settings.DEBUG:
65
+ if settings.STATIC_URL and settings.STATIC_ROOT:
66
+ webui_urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
67
+ else:
68
+ logger.warning("STATIC_URL or STATIC_ROOT not configured, static files may not serve correctly in DEBUG mode.")
64
69
 
65
- # Debug logging (optional)
66
- logger.debug(f"ENABLE_ADMIN={getattr(settings, 'ENABLE_ADMIN', False)}")
67
- logger.debug(f"ENABLE_WEBUI={getattr(settings, 'ENABLE_WEBUI', False)}")
70
+ # --- Blueprint URLs are now added dynamically via blueprint_base.py -> django_utils.py ---
71
+ blueprint_urlpatterns = [] # Start with empty list, populated by utils
68
72
 
69
- # Example of how to conditionally add URLs based on settings
70
- # if getattr(settings, 'ENABLE_SOMETHING', False):
71
- # urlpatterns.append(path('something/', include('something.urls')))
73
+ # Combine all URL patterns
74
+ urlpatterns = webui_urlpatterns + admin_urlpatterns + base_urlpatterns + blueprint_urlpatterns + router.urls
72
75
 
76
+ # Log final URL patterns (consider moving this to where patterns are finalized if issues persist)
77
+ if settings.DEBUG:
78
+ try:
79
+ from django.urls import get_resolver
80
+ # Note: get_resolver(None) might not reflect dynamically added URLs perfectly here.
81
+ # Logging within django_utils might be more accurate for dynamic additions.
82
+ final_patterns = get_resolver(None).url_patterns
83
+ logger.debug(f"Initial resolved URL patterns ({len(final_patterns)} total):")
84
+ # for pattern in final_patterns:
85
+ # try: pattern_repr = str(pattern)
86
+ # except: pattern_repr = f"[Pattern for {getattr(pattern, 'name', 'unnamed')}]"
87
+ # logger.debug(f" {pattern_repr}")
88
+ except Exception as e:
89
+ logger.error(f"Could not log initial URL patterns: {e}")
@@ -23,16 +23,10 @@ def _is_valid_message(msg: Any) -> bool:
23
23
  role = msg.get("role")
24
24
  if not role or not isinstance(role, str): logger.warning(f"Skipping msg missing role: {str(msg)[:150]}"); return False
25
25
  content = msg.get("content"); tool_calls = msg.get("tool_calls"); tool_call_id = msg.get("tool_call_id")
26
- # Validate based on role: content must be a string for system/user/tool; assistant may have tool calls
27
- if role == "system":
28
- is_valid = isinstance(content, str)
29
- elif role == "user":
30
- is_valid = isinstance(content, str)
31
- elif role == "assistant":
32
- # Assistant valid if it has string content or at least one tool call
33
- is_valid = isinstance(content, str) or (isinstance(tool_calls, list) and len(tool_calls) > 0)
34
- elif role == "tool":
35
- is_valid = isinstance(content, str) and tool_call_id is not None
26
+ if role == "system": is_valid = content is not None
27
+ elif role == "user": is_valid = content is not None
28
+ elif role == "assistant": is_valid = content is not None or (isinstance(tool_calls, list) and len(tool_calls) > 0)
29
+ elif role == "tool": is_valid = content is not None and tool_call_id is not None
36
30
  else: is_valid = False
37
31
  if not is_valid: logger.warning(f"Skipping msg failing validity check for role '{role}': {str(msg)[:150]}")
38
32
  return is_valid
@@ -118,27 +118,6 @@ def _search_and_process_jmespath(expression: str, payload: dict) -> str:
118
118
 
119
119
  return str(chat_id) if chat_id is not None else ""
120
120
 
121
- # ---------------------------------------------------------------------------
122
- # Debug utilities
123
- # ---------------------------------------------------------------------------
124
-
125
- _DEBUG_ENV_VARS = ("SWARM_DEBUG", "DEBUG", "OPEN_SWARM_DEBUG")
126
-
127
-
128
- def is_debug_enabled() -> bool:
129
- """Return True if any recognised debug environment variable is truthy.
130
-
131
- A value is considered *truthy* when it is set and **not** one of
132
- "0", "false", "off" (case‑insensitive).
133
- """
134
- import os
135
-
136
- for var in _DEBUG_ENV_VARS:
137
- val = os.getenv(var)
138
- if val and val.lower() not in ("0", "false", "off"):
139
- return True
140
- return False
141
-
142
121
 
143
122
  def extract_chat_id(payload: dict) -> str:
144
123
  """