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