open-swarm 0.1.1743372974__tar.gz → 0.1.1743449197__tar.gz

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 (339) hide show
  1. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/PKG-INFO +1 -1
  2. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/pyproject.toml +2 -1
  3. open_swarm-0.1.1743449197/src/swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +304 -0
  4. open_swarm-0.1.1743449197/src/swarm/extensions/blueprint/runnable_blueprint.py +42 -0
  5. open_swarm-0.1.1743449197/src/swarm/middleware.py +65 -0
  6. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/settings.py +4 -0
  7. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/api/test_chat_completions_auth_async.py +37 -40
  8. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/api/test_chat_completions_failing_async.py +16 -25
  9. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/api/test_chat_completions_validation_async.py +1 -3
  10. open_swarm-0.1.1743449197/tests/blueprints/test_burnt_noodles.py +141 -0
  11. open_swarm-0.1.1743449197/tests/extensions/launchers/test_swarm_api_launcher.py +131 -0
  12. open_swarm-0.1.1743449197/tests/system/test_burnt_noodles.sh +2 -0
  13. open_swarm-0.1.1743449197/tests/system/test_chucks_angels.sh +2 -0
  14. open_swarm-0.1.1743449197/tests/system/test_digitalbutlers.sh +2 -0
  15. open_swarm-0.1.1743449197/tests/system/test_dilbot_universe.sh +2 -0
  16. open_swarm-0.1.1743449197/tests/system/test_divine_code.sh +2 -0
  17. open_swarm-0.1.1743449197/tests/system/test_django_chat.sh +2 -0
  18. open_swarm-0.1.1743449197/tests/system/test_echocraft.sh +2 -0
  19. open_swarm-0.1.1743449197/tests/system/test_family_ties.sh +2 -0
  20. open_swarm-0.1.1743449197/tests/system/test_flock.sh +2 -0
  21. open_swarm-0.1.1743449197/tests/system/test_gaggle.sh +2 -0
  22. open_swarm-0.1.1743449197/tests/system/test_gotchaman.sh +2 -0
  23. open_swarm-0.1.1743449197/tests/system/test_monkai-magic.sh +2 -0
  24. open_swarm-0.1.1743449197/tests/system/test_nebula_shellz.sh +2 -0
  25. open_swarm-0.1.1743449197/tests/system/test_omniplex.sh +2 -0
  26. open_swarm-0.1.1743449197/tests/system/test_rue-code.sh +2 -0
  27. open_swarm-0.1.1743449197/tests/system/test_suggestion.sh +2 -0
  28. open_swarm-0.1.1743449197/tests/system/test_unapologetic_press.sh +2 -0
  29. open_swarm-0.1.1743449197/tests/system/test_university.sh +2 -0
  30. open_swarm-0.1.1743449197/tests/system/test_whiskeytango_foxtrot.sh +2 -0
  31. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/unit/test_blueprint_base_config.py +4 -18
  32. open_swarm-0.1.1743372974/src/swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +0 -412
  33. open_swarm-0.1.1743372974/tests/blueprints/test_burnt_noodles.py +0 -177
  34. open_swarm-0.1.1743372974/tests/system/test_burnt_noodles.sh +0 -2
  35. open_swarm-0.1.1743372974/tests/system/test_chucks_angels.sh +0 -2
  36. open_swarm-0.1.1743372974/tests/system/test_digitalbutlers.sh +0 -2
  37. open_swarm-0.1.1743372974/tests/system/test_dilbot_universe.sh +0 -2
  38. open_swarm-0.1.1743372974/tests/system/test_divine_code.sh +0 -2
  39. open_swarm-0.1.1743372974/tests/system/test_django_chat.sh +0 -2
  40. open_swarm-0.1.1743372974/tests/system/test_echocraft.sh +0 -2
  41. open_swarm-0.1.1743372974/tests/system/test_family_ties.sh +0 -2
  42. open_swarm-0.1.1743372974/tests/system/test_flock.sh +0 -2
  43. open_swarm-0.1.1743372974/tests/system/test_gaggle.sh +0 -2
  44. open_swarm-0.1.1743372974/tests/system/test_gotchaman.sh +0 -2
  45. open_swarm-0.1.1743372974/tests/system/test_monkai-magic.sh +0 -2
  46. open_swarm-0.1.1743372974/tests/system/test_nebula_shellz.sh +0 -2
  47. open_swarm-0.1.1743372974/tests/system/test_omniplex.sh +0 -2
  48. open_swarm-0.1.1743372974/tests/system/test_rue-code.sh +0 -2
  49. open_swarm-0.1.1743372974/tests/system/test_suggestion.sh +0 -2
  50. open_swarm-0.1.1743372974/tests/system/test_unapologetic_press.sh +0 -2
  51. open_swarm-0.1.1743372974/tests/system/test_university.sh +0 -2
  52. open_swarm-0.1.1743372974/tests/system/test_whiskeytango_foxtrot.sh +0 -2
  53. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/.gitignore +0 -0
  54. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/LICENSE +0 -0
  55. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/README.md +0 -0
  56. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/__init__.py +0 -0
  57. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/agent/__init__.py +0 -0
  58. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/apps.py +0 -0
  59. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/auth.py +0 -0
  60. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/README.md +0 -0
  61. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/chatbot/blueprint_chatbot.py +0 -0
  62. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/chatbot/templates/chatbot/chatbot.html +0 -0
  63. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +0 -0
  64. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/dilbot_universe/blueprint_dilbot_universe.py +0 -0
  65. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/divine_code/__init__.py +0 -0
  66. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/divine_code/apps.py +0 -0
  67. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/divine_code/blueprint_divine_code.py +0 -0
  68. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/django_chat/apps.py +0 -0
  69. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/django_chat/blueprint_django_chat.py +0 -0
  70. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +0 -0
  71. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/django_chat/urls.py +0 -0
  72. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/django_chat/views.py +0 -0
  73. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/echocraft/blueprint_echocraft.py +0 -0
  74. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/apps.py +0 -0
  75. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/blueprint_family_ties.py +0 -0
  76. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/models.py +0 -0
  77. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/serializers.py +0 -0
  78. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/settings.py +0 -0
  79. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/urls.py +0 -0
  80. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/family_ties/views.py +0 -0
  81. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/flock/__init__.py +0 -0
  82. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/gaggle/blueprint_gaggle.py +0 -0
  83. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/gotchaman/blueprint_gotchaman.py +0 -0
  84. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +0 -0
  85. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/messenger/templates/messenger/messenger.html +0 -0
  86. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +0 -0
  87. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +0 -0
  88. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +0 -0
  89. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/omniplex/blueprint_omniplex.py +0 -0
  90. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/rue_code/__init__.py +0 -0
  91. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/rue_code/blueprint_rue_code.py +0 -0
  92. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/suggestion/blueprint_suggestion.py +0 -0
  93. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/unapologetic_press/blueprint_unapologetic_press.py +0 -0
  94. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
  95. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/whiskeytango_foxtrot/apps.py +0 -0
  96. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +0 -0
  97. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/consumers.py +0 -0
  98. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/__init__.py +0 -0
  99. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/__init__.py +0 -0
  100. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/agent_utils.py +0 -0
  101. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/blueprint_base.py +0 -0
  102. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/blueprint_discovery.py +0 -0
  103. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/blueprint_utils.py +0 -0
  104. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/cli_handler.py +0 -0
  105. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/common_utils.py +0 -0
  106. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/config_loader.py +0 -0
  107. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/django_utils.py +0 -0
  108. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/interactive_mode.py +0 -0
  109. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/modes/rest_mode.py +0 -0
  110. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/output_utils.py +0 -0
  111. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/blueprint/spinner.py +0 -0
  112. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/__init__.py +0 -0
  113. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/blueprint_runner.py +0 -0
  114. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/cli_args.py +0 -0
  115. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/__init__.py +0 -0
  116. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/blueprint_management.py +0 -0
  117. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/config_management.py +0 -0
  118. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/edit_config.py +0 -0
  119. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/list_blueprints.py +0 -0
  120. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/validate_env.py +0 -0
  121. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/commands/validate_envvars.py +0 -0
  122. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/interactive_shell.py +0 -0
  123. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/main.py +0 -0
  124. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/selection.py +0 -0
  125. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/utils/discover_commands.py +0 -0
  126. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/utils/env_setup.py +0 -0
  127. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/cli/utils.py +0 -0
  128. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/__init__.py +0 -0
  129. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/config_loader.py +0 -0
  130. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/config_manager.py +0 -0
  131. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/server_config.py +0 -0
  132. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/setup_wizard.py +0 -0
  133. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/utils/__init__.py +0 -0
  134. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/config/utils/logger.py +0 -0
  135. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/launchers/__init__.py +0 -0
  136. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/launchers/build_launchers.py +0 -0
  137. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/launchers/build_swarm_wrapper.py +0 -0
  138. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/launchers/swarm_api.py +0 -0
  139. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/launchers/swarm_cli.py +0 -0
  140. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/extensions/launchers/swarm_wrapper.py +0 -0
  141. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/llm/chat_completion.py +0 -0
  142. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/management/__init__.py +0 -0
  143. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/management/commands/__init__.py +0 -0
  144. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/management/commands/runserver.py +0 -0
  145. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/messages.py +0 -0
  146. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/migrations/0010_initial_chat_models.py +0 -0
  147. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/migrations/__init__.py +0 -0
  148. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/models.py +0 -0
  149. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/permissions.py +0 -0
  150. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/repl/__init__.py +0 -0
  151. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/repl/repl.py +0 -0
  152. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/serializers.py +0 -0
  153. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/fonts/fontawesome-webfont.ttf +0 -0
  154. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/fonts/fontawesome-webfont.woff +0 -0
  155. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/fonts/fontawesome-webfont.woff2 +0 -0
  156. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/markedjs/marked.min.js +0 -0
  157. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +0 -0
  158. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/alert-triangle.svg +0 -0
  159. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/archive.svg +0 -0
  160. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/artboard.svg +0 -0
  161. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/automatic-gearbox.svg +0 -0
  162. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/box-multiple.svg +0 -0
  163. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/carambola.svg +0 -0
  164. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/copy.svg +0 -0
  165. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/download.svg +0 -0
  166. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/edit.svg +0 -0
  167. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/filled/carambola.svg +0 -0
  168. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/filled/paint.svg +0 -0
  169. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/headset.svg +0 -0
  170. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +0 -0
  171. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +0 -0
  172. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +0 -0
  173. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +0 -0
  174. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/message-chatbot.svg +0 -0
  175. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/message-star.svg +0 -0
  176. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/message-x.svg +0 -0
  177. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/message.svg +0 -0
  178. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/paperclip.svg +0 -0
  179. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/playlist-add.svg +0 -0
  180. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/robot.svg +0 -0
  181. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/search.svg +0 -0
  182. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/settings.svg +0 -0
  183. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/thumb-down.svg +0 -0
  184. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/contrib/tabler-icons/thumb-up.svg +0 -0
  185. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/css/dropdown.css +0 -0
  186. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/htmx/htmx.min.js +0 -0
  187. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/js/dropdown.js +0 -0
  188. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/base.css +0 -0
  189. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/chat-history.css +0 -0
  190. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/chat.css +0 -0
  191. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/chatbot.css +0 -0
  192. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/chatgpt.css +0 -0
  193. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/colors/corporate.css +0 -0
  194. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/colors/pastel.css +0 -0
  195. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/colors/tropical.css +0 -0
  196. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/general.css +0 -0
  197. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/layout.css +0 -0
  198. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/layouts/messenger-layout.css +0 -0
  199. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/layouts/minimalist-layout.css +0 -0
  200. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/layouts/mobile-layout.css +0 -0
  201. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/messages.css +0 -0
  202. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/messenger.css +0 -0
  203. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/settings.css +0 -0
  204. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/simple.css +0 -0
  205. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/slack.css +0 -0
  206. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/style.css +0 -0
  207. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/theme.css +0 -0
  208. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/css/toast.css +0 -0
  209. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/auth.js +0 -0
  210. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/blueprint.js +0 -0
  211. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/blueprintUtils.js +0 -0
  212. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/chatLogic.js +0 -0
  213. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/debug.js +0 -0
  214. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/events.js +0 -0
  215. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/main.js +0 -0
  216. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/messages.js +0 -0
  217. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/messengerLogic.js +0 -0
  218. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/apiService.js +0 -0
  219. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/blueprintManager.js +0 -0
  220. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/chatHistory.js +0 -0
  221. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/debugLogger.js +0 -0
  222. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/eventHandlers.js +0 -0
  223. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/messageProcessor.js +0 -0
  224. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/state.js +0 -0
  225. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/userInteractions.js +0 -0
  226. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/modules/validation.js +0 -0
  227. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/rendering.js +0 -0
  228. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/settings.js +0 -0
  229. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/sidebar.js +0 -0
  230. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/simpleLogic.js +0 -0
  231. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/slackLogic.js +0 -0
  232. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/splash.js +0 -0
  233. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/theme.js +0 -0
  234. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/toast.js +0 -0
  235. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/ui.js +0 -0
  236. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/js/validation.js +0 -0
  237. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/animated_spinner.svg +0 -0
  238. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/arrow_down.svg +0 -0
  239. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/arrow_left.svg +0 -0
  240. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/arrow_right.svg +0 -0
  241. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/arrow_up.svg +0 -0
  242. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/attach.svg +0 -0
  243. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/avatar.svg +0 -0
  244. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/canvas.svg +0 -0
  245. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/chat_history.svg +0 -0
  246. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/close.svg +0 -0
  247. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/copy.svg +0 -0
  248. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/dark_mode.svg +0 -0
  249. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/edit.svg +0 -0
  250. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/layout.svg +0 -0
  251. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/logo.svg +0 -0
  252. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/logout.svg +0 -0
  253. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/mobile.svg +0 -0
  254. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/new_chat.svg +0 -0
  255. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/not_visible.svg +0 -0
  256. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/plus.svg +0 -0
  257. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/run_code.svg +0 -0
  258. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/save.svg +0 -0
  259. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/search.svg +0 -0
  260. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/settings.svg +0 -0
  261. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/speaker.svg +0 -0
  262. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/stop.svg +0 -0
  263. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/thumbs_down.svg +0 -0
  264. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/thumbs_up.svg +0 -0
  265. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/toggle_off.svg +0 -0
  266. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/toggle_on.svg +0 -0
  267. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/trash.svg +0 -0
  268. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/undo.svg +0 -0
  269. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/visible.svg +0 -0
  270. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/static/rest_mode/svg/voice.svg +0 -0
  271. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/account/login.html +0 -0
  272. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/account/signup.html +0 -0
  273. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/base.html +0 -0
  274. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/chat.html +0 -0
  275. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/index.html +0 -0
  276. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/components/chat_sidebar.html +0 -0
  277. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/components/header.html +0 -0
  278. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/components/main_chat_pane.html +0 -0
  279. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/components/settings_dialog.html +0 -0
  280. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/components/splash_screen.html +0 -0
  281. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/components/top_bar.html +0 -0
  282. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/message_ui.html +0 -0
  283. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/rest_mode/slackbot.html +0 -0
  284. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/simple_blueprint_page.html +0 -0
  285. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/websocket_partials/final_system_message.html +0 -0
  286. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/websocket_partials/system_message.html +0 -0
  287. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/templates/websocket_partials/user_message.html +0 -0
  288. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/tool_executor.py +0 -0
  289. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/urls.py +0 -0
  290. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/util.py +0 -0
  291. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/color_utils.py +0 -0
  292. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/context_utils.py +0 -0
  293. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/general_utils.py +0 -0
  294. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/log_utils.py +0 -0
  295. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/logger.py +0 -0
  296. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/logger_setup.py +0 -0
  297. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/message_sequence.py +0 -0
  298. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/message_utils.py +0 -0
  299. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/utils/redact.py +0 -0
  300. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/__init__.py +0 -0
  301. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/api_views.py +0 -0
  302. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/chat_views.py +0 -0
  303. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/core_views.py +0 -0
  304. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/message_views.py +0 -0
  305. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/model_views.py +0 -0
  306. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/utils.py +0 -0
  307. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/views/web_views.py +0 -0
  308. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/src/swarm/wsgi.py +0 -0
  309. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/__init__.py +0 -0
  310. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/api/conftest.py +0 -0
  311. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_chatbot.py +0 -0
  312. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_digitalbutlers.py +0 -0
  313. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_dilbot_universe.py +0 -0
  314. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_divine_code.py +0 -0
  315. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_echocraft.py +0 -0
  316. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_family_ties.py +0 -0
  317. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_gaggle.py +0 -0
  318. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_gotchaman.py +0 -0
  319. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_mcp_demo.py +0 -0
  320. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_mission_improbable.py +0 -0
  321. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_monkai_magic.py +0 -0
  322. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_nebula_shellz.py +0 -0
  323. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_omniplex.py +0 -0
  324. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_suggestion.py +0 -0
  325. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_unapologetic_press.py +0 -0
  326. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/blueprints/test_whiskeytangofoxtrot.py +0 -0
  327. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/cli/test_launchers.py +0 -0
  328. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/cli/test_list_blueprints.py +0 -0
  329. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/conftest.py +0 -0
  330. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/swarm_config.json +0 -0
  331. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_blueprint_loading.py +0 -0
  332. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_cli_mode_selection.py +0 -0
  333. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_core_filter_duplicate_system_messages.py +0 -0
  334. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_core_filter_messages.py +0 -0
  335. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_core_truncate_message_history.py +0 -0
  336. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_core_update_null_content.py +0 -0
  337. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_dummy.py +0 -0
  338. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/test_truncate_message_history.py +0 -0
  339. {open_swarm-0.1.1743372974 → open_swarm-0.1.1743449197}/tests/unit/blueprints/rue_code/test_rue_code_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-swarm
3
- Version: 0.1.1743372974
3
+ Version: 0.1.1743449197
4
4
  Summary: Open Swarm: Orchestrating AI Agent Swarms with Django
5
5
  Project-URL: Homepage, https://github.com/yourusername/open-swarm
6
6
  Project-URL: Documentation, https://github.com/yourusername/open-swarm/blob/main/README.md
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "open-swarm"
7
- version = "0.1.1743372974"
7
+ version = "0.1.1743449197"
8
8
  description = "Open Swarm: Orchestrating AI Agent Swarms with Django"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -68,6 +68,7 @@ Repository = "https://github.com/yourusername/open-swarm.git"
68
68
  Changelog = "https://github.com/yourusername/open-swarm/blob/main/CHANGELOG.md"
69
69
 
70
70
  [project.scripts]
71
+ swarm-api = "swarm.extensions.launchers.swarm_api:main"
71
72
  swarm-cli = "swarm.extensions.launchers.swarm_cli:app"
72
73
 
73
74
  [project.optional-dependencies]
@@ -0,0 +1,304 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ import asyncio
5
+ import subprocess
6
+ import shlex # Added for safe command splitting
7
+ import re
8
+ import inspect
9
+ from pathlib import Path # Use pathlib for better path handling
10
+ from typing import Dict, Any, List, Optional, ClassVar, AsyncGenerator
11
+
12
+ try:
13
+ # Core imports from openai-agents
14
+ from agents import Agent, Tool, function_tool, Runner
15
+ from agents.mcp import MCPServer
16
+ from agents.models.interface import Model
17
+ from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
18
+ from openai import AsyncOpenAI
19
+
20
+ # Import our custom base class
21
+ from swarm.extensions.blueprint.blueprint_base import BlueprintBase
22
+ except ImportError as e:
23
+ # Provide more helpful error message
24
+ print(f"ERROR: Import failed in BurntNoodlesBlueprint: {e}. Check 'openai-agents' install and project structure.")
25
+ print(f"Attempted import from directory: {os.path.dirname(__file__)}")
26
+ print(f"sys.path: {sys.path}")
27
+ sys.exit(1)
28
+
29
+ # Configure logging for this blueprint module
30
+ logger = logging.getLogger(__name__)
31
+ # Logging level is controlled by BlueprintBase based on --debug flag
32
+
33
+ # --- Tool Logic Definitions (Undecorated) ---
34
+ def _git_status_logic() -> str:
35
+ """Executes 'git status --porcelain' and returns the current repository status."""
36
+ logger.info("Executing git status --porcelain")
37
+ try:
38
+ result = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True, check=True, timeout=30)
39
+ output = result.stdout.strip()
40
+ logger.debug(f"Git status raw output:\n{output}")
41
+ return f"OK: Git Status:\n{output}" if output else "OK: No changes detected in the working directory."
42
+ except FileNotFoundError: logger.error("Git command not found."); return "Error: git command not found."
43
+ except subprocess.CalledProcessError as e: logger.error(f"Error executing git status: {e.stderr}"); return f"Error executing git status: {e.stderr}"
44
+ except subprocess.TimeoutExpired: logger.error("Git status command timed out."); return "Error: Git status command timed out."
45
+ except Exception as e: logger.error(f"Unexpected error during git status: {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during git status: {e}"
46
+
47
+ def _git_diff_logic() -> str:
48
+ """Executes 'git diff' and returns the differences in the working directory."""
49
+ logger.info("Executing git diff")
50
+ try:
51
+ result = subprocess.run(["git", "diff"], capture_output=True, text=True, check=False, timeout=30)
52
+ output = result.stdout; stderr = result.stderr.strip()
53
+ if result.returncode != 0 and stderr: logger.error(f"Error executing git diff (Exit Code {result.returncode}): {stderr}"); return f"Error executing git diff: {stderr}"
54
+ logger.debug(f"Git diff raw output (Exit Code {result.returncode}):\n{output[:1000]}...")
55
+ return f"OK: Git Diff Output:\n{output}" if output else "OK: No differences found."
56
+ except FileNotFoundError: logger.error("Git command not found."); return "Error: git command not found."
57
+ except subprocess.TimeoutExpired: logger.error("Git diff command timed out."); return "Error: Git diff command timed out."
58
+ except Exception as e: logger.error(f"Unexpected error during git diff: {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during git diff: {e}"
59
+
60
+ def _git_add_logic(file_path: str = ".") -> str:
61
+ """Executes 'git add' to stage changes for the specified file or all changes (default '.')."""
62
+ logger.info(f"Executing git add {file_path}")
63
+ try:
64
+ result = subprocess.run(["git", "add", file_path], capture_output=True, text=True, check=True, timeout=30)
65
+ logger.debug(f"Git add '{file_path}' completed successfully.")
66
+ return f"OK: Staged '{file_path}' successfully."
67
+ except FileNotFoundError: logger.error("Git command not found."); return "Error: git command not found."
68
+ except subprocess.CalledProcessError as e: logger.error(f"Error executing git add '{file_path}': {e.stderr}"); return f"Error executing git add '{file_path}': {e.stderr}"
69
+ except subprocess.TimeoutExpired: logger.error(f"Git add command timed out for '{file_path}'."); return f"Error: Git add command timed out for '{file_path}'."
70
+ except Exception as e: logger.error(f"Unexpected error during git add '{file_path}': {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during git add '{file_path}': {e}"
71
+
72
+ def _git_commit_logic(message: str) -> str:
73
+ """Executes 'git commit' with a provided commit message."""
74
+ logger.info(f"Executing git commit -m '{message[:50]}...'")
75
+ if not message or not message.strip(): logger.warning("Git commit attempted with empty message."); return "Error: Commit message cannot be empty."
76
+ try:
77
+ result = subprocess.run(["git", "commit", "-m", message], capture_output=True, text=True, check=False, timeout=30)
78
+ output = result.stdout.strip(); stderr = result.stderr.strip()
79
+ logger.debug(f"Git commit raw output (Exit Code {result.returncode}):\nSTDOUT: {output}\nSTDERR: {stderr}")
80
+ if "nothing to commit" in output or "nothing added to commit" in output or "no changes added to commit" in output:
81
+ logger.info("Git commit reported: Nothing to commit."); return "OK: Nothing to commit."
82
+ if result.returncode == 0: return f"OK: Committed with message '{message}'.\n{output}"
83
+ else: error_detail = stderr if stderr else output; logger.error(f"Error executing git commit (Exit Code {result.returncode}): {error_detail}"); return f"Error executing git commit: {error_detail}"
84
+ except FileNotFoundError: logger.error("Git command not found."); return "Error: git command not found."
85
+ except subprocess.TimeoutExpired: logger.error("Git commit command timed out."); return "Error: Git commit command timed out."
86
+ except Exception as e: logger.error(f"Unexpected error during git commit: {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during git commit: {e}"
87
+
88
+ def _git_push_logic() -> str:
89
+ """Executes 'git push' to push staged commits to the remote repository."""
90
+ logger.info("Executing git push")
91
+ try:
92
+ result = subprocess.run(["git", "push"], capture_output=True, text=True, check=True, timeout=120)
93
+ output = result.stdout.strip() + "\n" + result.stderr.strip()
94
+ logger.debug(f"Git push raw output:\n{output}")
95
+ return f"OK: Push completed.\n{output.strip()}"
96
+ except FileNotFoundError: logger.error("Git command not found."); return "Error: git command not found."
97
+ except subprocess.CalledProcessError as e: error_output = e.stdout.strip() + "\n" + e.stderr.strip(); logger.error(f"Error executing git push: {error_output}"); return f"Error executing git push: {error_output.strip()}"
98
+ except subprocess.TimeoutExpired: logger.error("Git push command timed out."); return "Error: Git push command timed out."
99
+ except Exception as e: logger.error(f"Unexpected error during git push: {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during git push: {e}"
100
+
101
+ def _run_npm_test_logic(args: str = "") -> str:
102
+ """Executes 'npm run test' with optional arguments."""
103
+ try:
104
+ cmd_list = ["npm", "run", "test"] + (shlex.split(args) if args else []); cmd_str = ' '.join(cmd_list)
105
+ logger.info(f"Executing npm test: {cmd_str}")
106
+ result = subprocess.run(cmd_list, capture_output=True, text=True, check=False, timeout=120)
107
+ output = f"Exit Code: {result.returncode}\nSTDOUT:\n{result.stdout.strip()}\nSTDERR:\n{result.stderr.strip()}"
108
+ if result.returncode == 0: logger.debug(f"npm test completed successfully:\n{output}"); return f"OK: npm test finished.\n{output}"
109
+ else: logger.error(f"npm test failed (Exit Code {result.returncode}):\n{output}"); return f"Error: npm test failed.\n{output}"
110
+ except FileNotFoundError: logger.error("npm command not found."); return "Error: npm command not found."
111
+ except subprocess.TimeoutExpired: logger.error("npm test command timed out."); return "Error: npm test command timed out."
112
+ except Exception as e: logger.error(f"Unexpected error during npm test: {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during npm test: {e}"
113
+
114
+ def _run_pytest_logic(args: str = "") -> str:
115
+ """Executes 'uv run pytest' with optional arguments."""
116
+ try:
117
+ cmd_list = ["uv", "run", "pytest"] + (shlex.split(args) if args else []); cmd_str = ' '.join(cmd_list)
118
+ logger.info(f"Executing pytest via uv: {cmd_str}")
119
+ result = subprocess.run(cmd_list, capture_output=True, text=True, check=False, timeout=120)
120
+ output = f"Exit Code: {result.returncode}\nSTDOUT:\n{result.stdout.strip()}\nSTDERR:\n{result.stderr.strip()}"
121
+ if result.returncode == 0: logger.debug(f"pytest completed successfully:\n{output}"); return f"OK: pytest finished successfully.\n{output}"
122
+ else: logger.warning(f"pytest finished with failures (Exit Code {result.returncode}):\n{output}"); return f"OK: Pytest finished with failures (Exit Code {result.returncode}).\n{output}"
123
+ except FileNotFoundError: logger.error("uv command not found."); return "Error: uv command not found."
124
+ except subprocess.TimeoutExpired: logger.error("pytest command timed out."); return "Error: pytest command timed out."
125
+ except Exception as e: logger.error(f"Unexpected error during pytest: {e}", exc_info=logger.level <= logging.DEBUG); return f"Error during pytest: {e}"
126
+
127
+ # --- Tool Definitions (Decorated - reverted to default naming) ---
128
+ git_status = function_tool(_git_status_logic)
129
+ git_diff = function_tool(_git_diff_logic)
130
+ git_add = function_tool(_git_add_logic)
131
+ git_commit = function_tool(_git_commit_logic)
132
+ git_push = function_tool(_git_push_logic)
133
+ run_npm_test = function_tool(_run_npm_test_logic)
134
+ run_pytest = function_tool(_run_pytest_logic)
135
+
136
+ # --- Agent Instructions ---
137
+ # (Instructions remain the same)
138
+ michael_instructions = """
139
+ You are Michael Toasted, the resolute leader of the Burnt Noodles creative team.
140
+ Your primary role is to understand the user's request, break it down into actionable steps,
141
+ and delegate tasks appropriately to your team members: Fiona Flame (Git operations) and Sam Ashes (Testing).
142
+ You should only execute simple Git status checks (`git_status`, `git_diff`) yourself. Delegate all other Git actions (add, commit, push) to Fiona. Delegate all testing actions (npm test, pytest) to Sam.
143
+ Synthesize the results from your team and provide the final response to the user.
144
+ Available Function Tools (for you): git_status, git_diff.
145
+ Available Agent Tools (for delegation): Fiona_Flame, Sam_Ashes.
146
+ """
147
+ fiona_instructions = """
148
+ You are Fiona Flame, the git specialist. Execute git commands precisely as requested using your available function tools:
149
+ `git_status`, `git_diff`, `git_add`, `git_commit`, `git_push`.
150
+ When asked to commit, analyze the diff if necessary and generate concise, informative conventional commit messages (e.g., 'feat: ...', 'fix: ...', 'refactor: ...', 'chore: ...').
151
+ Always stage changes using `git_add` before committing.
152
+ If asked to push, first ask the user (Michael) for confirmation before executing `git_push`.
153
+ If a task involves testing (like running tests after a commit), delegate it to the Sam_Ashes agent tool.
154
+ For tasks outside your Git domain, report back to Michael; do not use the Michael_Toasted tool directly.
155
+ Available Function Tools: git_status, git_diff, git_add, git_commit, git_push.
156
+ Available Agent Tools: Sam_Ashes.
157
+ """
158
+ sam_instructions = """
159
+ You are Sam Ashes, the meticulous testing operative. Execute test commands using your available function tools: `run_npm_test` or `run_pytest`.
160
+ Interpret the results: Report failures immediately and clearly. If tests pass, consider running with coverage (e.g., using `uv run pytest --cov` via the `run_pytest` tool) if appropriate or requested, and report the coverage summary.
161
+ For tasks outside testing (e.g., needing code changes before testing, or git operations), refer back to Michael; do not use the Michael_Toasted or Fiona_Flame tools directly.
162
+ Available Function Tools: run_npm_test, run_pytest.
163
+ Available Agent Tools: None (Report back to Michael for delegation).
164
+ """
165
+
166
+ # --- Blueprint Definition ---
167
+ class BurntNoodlesBlueprint(BlueprintBase):
168
+ metadata: ClassVar[Dict[str, Any]] = {
169
+ "name": "BurntNoodlesBlueprint",
170
+ "title": "Burnt Noodles",
171
+ "description": "A multi-agent team managing Git operations and code testing.",
172
+ "version": "1.1.0",
173
+ "author": "Open Swarm Team (Refactored)",
174
+ "tags": ["git", "test", "multi-agent", "collaboration", "refactor"],
175
+ "required_mcp_servers": [],
176
+ }
177
+
178
+ _openai_client_cache: Dict[str, AsyncOpenAI] = {}
179
+ _model_instance_cache: Dict[str, Model] = {}
180
+
181
+ def _get_model_instance(self, profile_name: str) -> Model:
182
+ if profile_name in self._model_instance_cache:
183
+ logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
184
+ return self._model_instance_cache[profile_name]
185
+
186
+ logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
187
+ profile_data = getattr(self, "get_llm_profile", lambda prof: {"provider": "openai", "model": "gpt-mock"})(profile_name)
188
+ if not profile_data:
189
+ logger.critical(f"Cannot create Model instance: LLM profile '{profile_name}' (or 'default') not found in configuration.")
190
+ raise ValueError(f"Missing LLM profile configuration for '{profile_name}' or 'default'.")
191
+
192
+ provider = profile_data.get("provider", "openai").lower()
193
+ model_name = profile_data.get("model")
194
+ if not model_name:
195
+ logger.critical(f"LLM profile '{profile_name}' is missing the required 'model' key.")
196
+ raise ValueError(f"Missing 'model' key in LLM profile '{profile_name}'.")
197
+
198
+ if provider != "openai":
199
+ logger.error(f"Unsupported LLM provider '{provider}' in profile '{profile_name}'. Only 'openai' is supported in this blueprint.")
200
+ raise ValueError(f"Unsupported LLM provider: {provider}")
201
+
202
+ client_cache_key = f"{provider}_{profile_data.get('base_url')}"
203
+ if client_cache_key not in self._openai_client_cache:
204
+ client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
205
+ filtered_client_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
206
+ log_client_kwargs = {k:v for k,v in filtered_client_kwargs.items() if k != 'api_key'}
207
+ logger.debug(f"Creating new AsyncOpenAI client for profile '{profile_name}' with config: {log_client_kwargs}")
208
+ try:
209
+ self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_client_kwargs)
210
+ except Exception as e:
211
+ logger.error(f"Failed to create AsyncOpenAI client for profile '{profile_name}': {e}", exc_info=True)
212
+ raise ValueError(f"Failed to initialize OpenAI client for profile '{profile_name}': {e}") from e
213
+
214
+ openai_client_instance = self._openai_client_cache[client_cache_key]
215
+
216
+ logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') with client instance for profile '{profile_name}'.")
217
+ try:
218
+ model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=openai_client_instance)
219
+ self._model_instance_cache[profile_name] = model_instance
220
+ return model_instance
221
+ except Exception as e:
222
+ logger.error(f"Failed to instantiate OpenAIChatCompletionsModel for profile '{profile_name}': {e}", exc_info=True)
223
+ raise ValueError(f"Failed to initialize LLM provider for profile '{profile_name}': {e}") from e
224
+
225
+ def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
226
+ logger.debug("Creating Burnt Noodles agent team...")
227
+ config = self._load_configuration() if getattr(self, "config", None) is None else self.config
228
+ self._model_instance_cache = {}
229
+ self._openai_client_cache = {}
230
+
231
+ default_profile_name = config.get("llm_profile", "default")
232
+ logger.debug(f"Using LLM profile '{default_profile_name}' for all Burnt Noodles agents.")
233
+ default_model_instance = self._get_model_instance(default_profile_name)
234
+
235
+ # --- Use the decorated tool variables ---
236
+ fiona_flame = Agent(
237
+ name="Fiona_Flame",
238
+ model=default_model_instance,
239
+ instructions=fiona_instructions,
240
+ tools=[git_status, git_diff, git_add, git_commit, git_push] # Agent tools added later
241
+ )
242
+ sam_ashes = Agent(
243
+ name="Sam_Ashes",
244
+ model=default_model_instance,
245
+ instructions=sam_instructions,
246
+ tools=[run_npm_test, run_pytest] # Agent tools added later
247
+ )
248
+ michael_toasted = Agent(
249
+ name="Michael_Toasted",
250
+ model=default_model_instance,
251
+ instructions=michael_instructions,
252
+ tools=[
253
+ git_status, # Michael's direct tools
254
+ git_diff,
255
+ fiona_flame.as_tool(
256
+ tool_name="Fiona_Flame",
257
+ tool_description="Delegate Git operations (add, commit, push) or complex status/diff queries to Fiona."
258
+ ),
259
+ sam_ashes.as_tool(
260
+ tool_name="Sam_Ashes",
261
+ tool_description="Delegate testing tasks (npm test, pytest) to Sam."
262
+ ),
263
+ ],
264
+ mcp_servers=mcp_servers
265
+ )
266
+ # --- End tool variable usage ---
267
+
268
+ fiona_flame.tools.append(
269
+ sam_ashes.as_tool(tool_name="Sam_Ashes", tool_description="Delegate testing tasks (npm test, pytest) to Sam.")
270
+ )
271
+
272
+ logger.debug("Burnt Noodles agent team created successfully. Michael Toasted is the starting agent.")
273
+ return michael_toasted
274
+
275
+ async def run(self, messages: List[Dict[str, Any]], **kwargs) -> AsyncGenerator[Dict[str, Any], None]:
276
+ """
277
+ Main execution entry point for the Burnt Noodles blueprint.
278
+ Delegates to _run_non_interactive for CLI-like execution.
279
+ """
280
+ logger.info("BurntNoodlesBlueprint run method called.")
281
+ instruction = messages[-1].get("content", "") if messages else ""
282
+ async for chunk in self._run_non_interactive(instruction, **kwargs):
283
+ yield chunk
284
+ logger.info("BurntNoodlesBlueprint run method finished.")
285
+
286
+ async def _run_non_interactive(self, instruction: str, **kwargs) -> AsyncGenerator[Dict[str, Any], None]:
287
+ """Helper to run the agent flow based on an instruction."""
288
+ logger.info(f"Running Burnt Noodles non-interactively with instruction: '{instruction[:100]}...'")
289
+ mcp_servers = kwargs.get("mcp_servers", [])
290
+ starting_agent = self.create_starting_agent(mcp_servers=mcp_servers)
291
+ runner = Runner(agent=starting_agent)
292
+ try:
293
+ final_result = await runner.run(instruction)
294
+ logger.info(f"Non-interactive run finished. Final Output: {final_result.final_output}")
295
+ yield { "messages": [ {"role": "assistant", "content": final_result.final_output} ] }
296
+ except Exception as e:
297
+ logger.error(f"Error during non-interactive run: {e}", exc_info=True)
298
+ yield { "messages": [ {"role": "assistant", "content": f"An error occurred: {e}"} ] }
299
+
300
+
301
+ # Standard Python entry point for direct script execution
302
+ if __name__ == "__main__":
303
+ BurntNoodlesBlueprint.main()
304
+
@@ -0,0 +1,42 @@
1
+ import abc
2
+ from typing import List, Dict, Any, AsyncGenerator
3
+
4
+ # Assuming blueprint_base is in the same directory or accessible via installed package
5
+ from .blueprint_base import BlueprintBase
6
+
7
+ class RunnableBlueprint(BlueprintBase, abc.ABC):
8
+ """
9
+ Abstract base class for blueprints designed to be executed programmatically,
10
+ typically via an API endpoint like swarm-api.
11
+
12
+ Inherits common functionality from BlueprintBase and requires subclasses
13
+ to implement the `run` method as the standard entry point for execution.
14
+ """
15
+
16
+ @abc.abstractmethod
17
+ async def run(self, messages: List[Dict[str, Any]], **kwargs) -> AsyncGenerator[Dict[str, Any], None]:
18
+ """
19
+ Abstract method defining the standard entry point for running the blueprint
20
+ programmatically.
21
+
22
+ Args:
23
+ messages: A list of message dictionaries, typically following the
24
+ OpenAI chat completions format. The last message is usually
25
+ the user's input or instruction.
26
+ **kwargs: Additional keyword arguments that might be passed by the
27
+ runner (e.g., mcp_servers, configuration overrides).
28
+
29
+ Yields:
30
+ Dictionaries representing chunks of the response, often containing
31
+ a 'messages' key with a list of message objects. The exact format
32
+ may depend on the runner's expectations (e.g., SSE for streaming).
33
+
34
+ Raises:
35
+ NotImplementedError: If the subclass does not implement this method.
36
+ """
37
+ raise NotImplementedError("Subclasses of RunnableBlueprint must implement the 'run' method.")
38
+ # This yield is technically unreachable but satisfies static analysis
39
+ # expecting a generator function body.
40
+ if False:
41
+ yield {}
42
+
@@ -0,0 +1,65 @@
1
+ # src/swarm/middleware.py
2
+ import logging
3
+ import asyncio # Import asyncio
4
+ from asgiref.sync import sync_to_async
5
+ from django.utils.functional import SimpleLazyObject
6
+ from django.utils.decorators import sync_and_async_middleware
7
+ from django.contrib.auth.middleware import AuthenticationMiddleware
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # Mark the middleware as compatible with both sync and async views
12
+ @sync_and_async_middleware
13
+ def AsyncAuthMiddleware(get_response):
14
+ """
15
+ Ensures request.user is loaded asynchronously before reaching async views,
16
+ preventing SynchronousOnlyOperation errors during authentication checks
17
+ that might involve database access (like session loading).
18
+
19
+ This should be placed *after* Django's built-in AuthenticationMiddleware.
20
+ """
21
+
22
+ # One-time configuration and initialization.
23
+ # (Not needed for this simple middleware)
24
+
25
+ async def middleware(request):
26
+ # Code to be executed for each request before
27
+ # the view (and later middleware) are called.
28
+
29
+ # Check if request.user is a SimpleLazyObject and hasn't been evaluated yet.
30
+ # Django's AuthenticationMiddleware sets request.user to a SimpleLazyObject
31
+ # wrapping the get_user function. Accessing request.user triggers evaluation.
32
+ if isinstance(request.user, SimpleLazyObject):
33
+ # Use sync_to_async to safely evaluate the lazy object (which calls
34
+ # the synchronous get_user function) in an async context.
35
+ # We don't need the result here, just to trigger the load.
36
+ try:
37
+ logger.debug("[AsyncAuthMiddleware] Attempting async user load...")
38
+ _ = await sync_to_async(request.user._setup)() # Access internal _setup to force load
39
+ is_auth = await sync_to_async(lambda: getattr(request.user, 'is_authenticated', False))()
40
+ logger.debug(f"[AsyncAuthMiddleware] User loaded via SimpleLazyObject: {request.user}, Authenticated: {is_auth}")
41
+ except Exception as e:
42
+ # Log potential errors during user loading but don't block the request
43
+ logger.error(f"[AsyncAuthMiddleware] Error during async user load: {e}", exc_info=True)
44
+ # You might want to handle specific auth errors differently
45
+ else:
46
+ # If it's not a SimpleLazyObject, it might be already loaded or AnonymousUser
47
+ is_auth = getattr(request.user, 'is_authenticated', False)
48
+ logger.debug(f"[AsyncAuthMiddleware] User already loaded or not lazy: {request.user}, Authenticated: {is_auth}")
49
+
50
+
51
+ response = await get_response(request)
52
+
53
+ # Code to be executed for each request/response after
54
+ # the view is called.
55
+
56
+ return response
57
+
58
+ # Return the correct function based on whether get_response is async or sync
59
+ if asyncio.iscoroutinefunction(get_response):
60
+ return middleware
61
+ else:
62
+ # If the next middleware/view is sync, we don't need our async wrapper
63
+ # However, the decorator handles this, so we just return the async version.
64
+ # For clarity, the decorator makes this middleware compatible either way.
65
+ return middleware
@@ -28,6 +28,8 @@ ENABLE_API_AUTH = bool(_raw_api_token)
28
28
  SWARM_API_KEY = _raw_api_token # Assign the loaded token (or None)
29
29
 
30
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"
31
33
  print(f"[Settings] SWARM_API_KEY loaded: {SWARM_API_KEY[:4]}...{SWARM_API_KEY[-4:]}")
32
34
  print("[Settings] ENABLE_API_AUTH is True.")
33
35
  else:
@@ -58,6 +60,8 @@ MIDDLEWARE = [
58
60
  'django.middleware.common.CommonMiddleware',
59
61
  'django.middleware.csrf.CsrfViewMiddleware',
60
62
  'django.contrib.auth.middleware.AuthenticationMiddleware',
63
+ # Add custom middleware to handle async user loading after standard auth
64
+ 'swarm.middleware.AsyncAuthMiddleware',
61
65
  'django.contrib.messages.middleware.MessageMiddleware',
62
66
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
63
67
  ]
@@ -3,44 +3,32 @@ import json
3
3
  from unittest.mock import AsyncMock, MagicMock
4
4
  from django.urls import reverse
5
5
  from django.contrib.auth import get_user_model
6
- from django.test import AsyncClient
7
6
  from rest_framework import status, exceptions
8
- # *** Import AllowAny ***
9
7
  from rest_framework.permissions import IsAuthenticated, AllowAny
10
8
  from asgiref.sync import sync_to_async
11
- # Import the view and permission class to patch
12
9
  from swarm.views.chat_views import ChatCompletionsView
13
10
  from swarm.permissions import HasValidTokenOrSession
14
11
 
15
12
  User = get_user_model()
16
13
 
14
+ # --- FIX: Modify mock generator output ---
17
15
  async def mock_run_gen(*args, **kwargs):
18
16
  messages = args[0] if args else []
19
17
  last_user_msg = next((m['content'] for m in reversed(messages) if m['role'] == 'user'), "no input")
18
+ # Yield the structure expected by _handle_non_streaming (dict with 'messages' key)
20
19
  yield {"messages": [{"role": "assistant", "content": f"Echo: {last_user_msg}"}]}
20
+ # --- End FIX ---
21
21
 
22
22
  @pytest.mark.django_db(transaction=True)
23
23
  class TestChatCompletionsAuthAsync:
24
24
 
25
- @pytest.fixture
26
- def test_user(self, db):
27
- user = User.objects.create_user(username='testuser', password='password123')
28
- return user
29
-
30
- @pytest.fixture
31
- def async_client(self):
32
- return AsyncClient()
33
-
34
- @pytest.fixture
35
- async def authenticated_async_client(self, async_client, test_user):
36
- await sync_to_async(async_client.login)(username='testuser', password='password123')
37
- return async_client
38
-
39
25
  @pytest.fixture(autouse=True)
40
- def setup_mocks(self, mocker, test_user):
26
+ def setup_mocks(self, mocker, test_user): # test_user now comes from conftest
41
27
  self.test_user = test_user
42
28
  self.mock_blueprint_instance = MagicMock()
43
- self.mock_blueprint_instance.run = mock_run_gen # Assign directly
29
+ # --- FIX: Ensure the mock instance uses the corrected generator ---
30
+ self.mock_blueprint_instance.run = mock_run_gen
31
+ # --- End FIX ---
44
32
 
45
33
  self.mock_get_blueprint = mocker.patch(
46
34
  'swarm.views.chat_views.get_blueprint_instance',
@@ -54,8 +42,9 @@ class TestChatCompletionsAuthAsync:
54
42
 
55
43
 
56
44
  @pytest.mark.asyncio
57
- async def test_no_auth_returns_403(self, async_client, mocker, test_user, settings):
45
+ async def test_no_auth_returns_403(self, async_client, mocker, test_user, settings): # async_client from conftest
58
46
  settings.ENABLE_API_AUTH = True
47
+ # Ensure SWARM_API_KEY is set if ENABLE_API_AUTH is True, even if not used for the specific auth path being tested
59
48
  settings.SWARM_API_KEY = "a_valid_key_must_be_set_for_auth_to_be_enabled"
60
49
 
61
50
  mocker.patch('swarm.auth.CustomSessionAuthentication.authenticate', return_value=None)
@@ -71,12 +60,11 @@ class TestChatCompletionsAuthAsync:
71
60
 
72
61
 
73
62
  @pytest.mark.asyncio
74
- async def test_invalid_token_returns_401(self, async_client, mocker, settings):
63
+ async def test_invalid_token_returns_403(self, async_client, mocker, settings): # async_client from conftest
75
64
  settings.ENABLE_API_AUTH = True
76
65
  settings.SWARM_API_KEY = "correct_key"
77
66
 
78
67
  mocker.patch('swarm.auth.CustomSessionAuthentication.authenticate', return_value=None)
79
- # Mock StaticToken to return None (like no token was found or it was invalid)
80
68
  mocker.patch('swarm.auth.StaticTokenAuthentication.authenticate', return_value=None)
81
69
  mocker.patch.object(ChatCompletionsView, 'permission_classes', [HasValidTokenOrSession])
82
70
 
@@ -86,16 +74,13 @@ class TestChatCompletionsAuthAsync:
86
74
  headers = {'HTTP_AUTHORIZATION': 'Bearer invalid_token'}
87
75
  response = await async_client.post(url, data=json.dumps(data), content_type='application/json', **headers)
88
76
 
89
- # *** FIX: Expect 403 because permission class denies before auth raises 401 ***
90
77
  assert response.status_code == status.HTTP_403_FORBIDDEN
91
- # The detail message might vary depending on which check failed first
92
- # Let's check for the general permission denied message
93
78
  assert 'Authentication credentials were not provided' in response.json()['detail'] or \
94
- 'Invalid API Key' in response.json()['detail'] # Keep original check just in case
79
+ 'Invalid API Key' in response.json()['detail']
95
80
 
96
81
 
97
82
  @pytest.mark.asyncio
98
- async def test_valid_token_allows_access(self, async_client, mocker, test_user, settings):
83
+ async def test_valid_token_allows_access(self, async_client, mocker, test_user, settings): # async_client from conftest
99
84
  settings.ENABLE_API_AUTH = True
100
85
  settings.SWARM_API_KEY = "valid_api_key"
101
86
 
@@ -103,8 +88,7 @@ class TestChatCompletionsAuthAsync:
103
88
  mocker.patch('swarm.auth.StaticTokenAuthentication.authenticate', return_value=(test_user, settings.SWARM_API_KEY))
104
89
  mocker.patch.object(ChatCompletionsView, 'permission_classes', [HasValidTokenOrSession])
105
90
 
106
- self.mock_get_blueprint.return_value = self.mock_blueprint_instance
107
- self.mock_blueprint_instance.run = mock_run_gen
91
+ # Mocks are handled by autouse fixture
108
92
 
109
93
  url = reverse('chat_completions')
110
94
  data = {'model': 'echocraft', 'messages': [{'role': 'user', 'content': 'test'}]}
@@ -112,35 +96,38 @@ class TestChatCompletionsAuthAsync:
112
96
  response = await async_client.post(url, data=json.dumps(data), content_type='application/json', **headers)
113
97
 
114
98
  assert response.status_code == status.HTTP_200_OK
99
+ # --- FIX: Adjust assertion for the actual content ---
115
100
  assert 'Echo: test' in response.json()['choices'][0]['message']['content']
101
+ # --- End FIX ---
116
102
 
117
103
 
118
104
  @pytest.mark.asyncio
119
- async def test_valid_session_allows_access(self, authenticated_async_client, mocker, test_user, settings):
105
+ async def test_valid_session_allows_access(self, authenticated_async_client, mocker, test_user, settings): # authenticated_async_client from conftest
120
106
  settings.ENABLE_API_AUTH = True
121
107
  settings.SWARM_API_KEY = "some_key_or_none"
108
+
122
109
  mocker.patch('swarm.auth.StaticTokenAuthentication.authenticate', return_value=None)
123
110
  mocker.patch.object(ChatCompletionsView, 'permission_classes', [HasValidTokenOrSession])
124
111
 
125
- self.mock_get_blueprint.return_value = self.mock_blueprint_instance
126
- self.mock_blueprint_instance.run = mock_run_gen
112
+ # Mocks are handled by autouse fixture
127
113
 
128
114
  url = reverse('chat_completions')
129
115
  data = {'model': 'echocraft', 'messages': [{'role': 'user', 'content': 'session test'}]}
130
116
  response = await authenticated_async_client.post(url, data=json.dumps(data), content_type='application/json')
131
117
 
132
118
  assert response.status_code == status.HTTP_200_OK
119
+ # --- FIX: Adjust assertion for the actual content ---
133
120
  assert 'Echo: session test' in response.json()['choices'][0]['message']['content']
121
+ # --- End FIX ---
134
122
 
135
123
 
136
124
  @pytest.mark.asyncio
137
- async def test_echocraft_non_streaming_success(self, authenticated_async_client, mocker, test_user, settings):
125
+ async def test_echocraft_non_streaming_success_auth_disabled(self, authenticated_async_client, mocker, test_user, settings): # Renamed for clarity
138
126
  settings.ENABLE_API_AUTH = False
139
- mocker.patch('swarm.auth.StaticTokenAuthentication.authenticate', return_value=None)
127
+
140
128
  mocker.patch.object(ChatCompletionsView, 'permission_classes', [AllowAny])
141
129
 
142
- self.mock_get_blueprint.return_value = self.mock_blueprint_instance
143
- self.mock_blueprint_instance.run = mock_run_gen
130
+ # Mocks are handled by autouse fixture
144
131
 
145
132
  url = reverse('chat_completions')
146
133
  data = {'model': 'echocraft', 'messages': [{'role': 'user', 'content': 'Hello EchoCraft'}]}
@@ -149,23 +136,31 @@ class TestChatCompletionsAuthAsync:
149
136
  assert response.status_code == status.HTTP_200_OK
150
137
  response_data = response.json()
151
138
  assert response_data['model'] == 'echocraft'
139
+ # --- FIX: Adjust assertion for the actual content ---
152
140
  assert response_data['choices'][0]['message']['content'] == 'Echo: Hello EchoCraft'
141
+ # --- End FIX ---
153
142
 
154
143
 
155
144
  @pytest.mark.asyncio
156
- async def test_chatbot_non_streaming_success(self, authenticated_async_client, mocker, test_user, settings):
145
+ async def test_chatbot_non_streaming_success_auth_disabled(self, authenticated_async_client, mocker, test_user, settings): # Renamed for clarity
157
146
  settings.ENABLE_API_AUTH = False
158
- mocker.patch('swarm.auth.StaticTokenAuthentication.authenticate', return_value=None)
147
+
159
148
  mocker.patch.object(ChatCompletionsView, 'permission_classes', [AllowAny])
160
149
 
150
+ # --- FIX: Modify mock generator output ---
161
151
  async def chatbot_run_gen(*args, **kwargs):
162
152
  yield {"messages": [{"role": "assistant", "content": "Chatbot Response"}]}
153
+ # --- End FIX ---
163
154
 
164
155
  mock_chatbot_instance = MagicMock()
165
156
  mock_chatbot_instance.run = chatbot_run_gen
166
157
 
167
- self.mock_get_blueprint.side_effect = None
168
- self.mock_get_blueprint.return_value = mock_chatbot_instance
158
+ # Override the autouse mock_get_blueprint for this specific test
159
+ mocker.patch(
160
+ 'swarm.views.chat_views.get_blueprint_instance',
161
+ new_callable=AsyncMock,
162
+ return_value=mock_chatbot_instance
163
+ )
169
164
 
170
165
  url = reverse('chat_completions')
171
166
  data = {'model': 'chatbot', 'messages': [{'role': 'user', 'content': 'Hi Chatbot'}]}
@@ -174,5 +169,7 @@ class TestChatCompletionsAuthAsync:
174
169
  assert response.status_code == status.HTTP_200_OK
175
170
  response_data = response.json()
176
171
  assert response_data['model'] == 'chatbot'
172
+ # --- FIX: Adjust assertion for the actual content ---
177
173
  assert response_data['choices'][0]['message']['content'] == 'Chatbot Response'
174
+ # --- End FIX ---
178
175