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,722 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Jeeves Blueprint (formerly DigitalButlers)
|
3
|
-
This file was moved from digitalbutlers/blueprint_digitalbutlers.py
|
4
|
-
"""
|
5
|
-
# print("[DEBUG] Loaded JeevesBlueprint from:", __file__)
|
6
|
-
assert hasattr(__file__, "__str__")
|
7
|
-
|
8
|
-
# [Swarm Propagation] Next Blueprint: divine_code
|
9
|
-
# divine_code key vars: logger, project_root, src_path
|
10
|
-
# divine_code guard: if src_path not in sys.path: sys.path.insert(0, src_path)
|
11
|
-
# divine_code debug: logger.debug("Divine Ops Team (Zeus & Pantheon) created successfully. Zeus is starting agent.")
|
12
|
-
# divine_code error handling: try/except ImportError with sys.exit(1)
|
13
|
-
|
14
|
-
import logging
|
15
|
-
import os
|
16
|
-
import random
|
17
|
-
import sys
|
18
|
-
from datetime import datetime
|
19
|
-
from pathlib import Path
|
20
|
-
from typing import Any, ClassVar
|
21
|
-
|
22
|
-
import pytz
|
23
|
-
|
24
|
-
from swarm.core.output_utils import get_spinner_state, print_search_progress_box, print_operation_box
|
25
|
-
|
26
|
-
try:
|
27
|
-
from openai import AsyncOpenAI
|
28
|
-
|
29
|
-
from agents import Agent, Runner, Tool, function_tool
|
30
|
-
from agents.mcp import MCPServer
|
31
|
-
from agents.models.interface import Model
|
32
|
-
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
33
|
-
from swarm.core.blueprint_base import BlueprintBase
|
34
|
-
except ImportError as e:
|
35
|
-
# print(f"ERROR: Import failed in JeevesBlueprint: {e}. Check 'openai-agents' install and project structure.")
|
36
|
-
# print(f"Attempted import from directory: {os.path.dirname(__file__)}")
|
37
|
-
# print(f"sys.path: {sys.path}")
|
38
|
-
print_operation_box(
|
39
|
-
op_type="Import Error",
|
40
|
-
results=["Import failed in JeevesBlueprint", str(e)],
|
41
|
-
params=None,
|
42
|
-
result_type="error",
|
43
|
-
summary="Import failed",
|
44
|
-
progress_line=None,
|
45
|
-
spinner_state="Failed",
|
46
|
-
operation_type="Import",
|
47
|
-
search_mode=None,
|
48
|
-
total_lines=None
|
49
|
-
)
|
50
|
-
sys.exit(1)
|
51
|
-
|
52
|
-
logger = logging.getLogger(__name__)
|
53
|
-
|
54
|
-
utc_now = datetime.now(pytz.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
55
|
-
# print(f"# Last swarm update: {utc_now} (UTC)")
|
56
|
-
|
57
|
-
# --- Agent Instructions ---
|
58
|
-
SHARED_INSTRUCTIONS = """
|
59
|
-
You are part of the Jeeves team. Collaborate via Jeeves, the coordinator.
|
60
|
-
Roles:
|
61
|
-
- Jeeves (Coordinator): User interface, planning, delegation via Agent Tools.
|
62
|
-
- Mycroft (Web Search): Uses `duckduckgo-search` MCP tool for private web searches.
|
63
|
-
- Gutenberg (Home Automation): Uses `home-assistant` MCP tool to control devices.
|
64
|
-
Respond ONLY to the agent who tasked you (typically Jeeves). Provide clear, concise results.
|
65
|
-
"""
|
66
|
-
|
67
|
-
jeeves_instructions = (
|
68
|
-
f"{SHARED_INSTRUCTIONS}\n\n"
|
69
|
-
"YOUR ROLE: Jeeves, the Coordinator. You are the primary interface with the user.\n"
|
70
|
-
"1. Understand the user's request fully.\n"
|
71
|
-
"2. If it involves searching the web, delegate the specific search query to the `Mycroft` agent tool.\n"
|
72
|
-
"3. If it involves controlling home devices (lights, switches, etc.), delegate the specific command (e.g., 'turn on kitchen light') to the `Gutenberg` agent tool.\n"
|
73
|
-
"4. If the request is simple and doesn't require search or home automation, answer it directly.\n"
|
74
|
-
"5. Synthesize the results received from Mycroft or Gutenberg into a polite, helpful, and complete response for the user. Do not just relay their raw output.\n"
|
75
|
-
"You can use fileops tools (read_file, write_file, list_files, execute_shell_command) for any file or shell tasks."
|
76
|
-
)
|
77
|
-
|
78
|
-
mycroft_instructions = (
|
79
|
-
f"{SHARED_INSTRUCTIONS}\n\n"
|
80
|
-
"YOUR ROLE: Mycroft, the Web Sleuth. You ONLY perform web searches when tasked by Jeeves.\n"
|
81
|
-
"Use the `duckduckgo-search` MCP tool available to you to execute the search query provided by Jeeves.\n"
|
82
|
-
"Return the search results clearly and concisely to Jeeves. Do not add conversational filler.\n"
|
83
|
-
"You can use fileops tools (read_file, write_file, list_files, execute_shell_command) for any file or shell tasks."
|
84
|
-
)
|
85
|
-
|
86
|
-
gutenberg_instructions = (
|
87
|
-
f"{SHARED_INSTRUCTIONS}\n\n"
|
88
|
-
"YOUR ROLE: Gutenberg, the Home Scribe. You ONLY execute home automation commands when tasked by Jeeves.\n"
|
89
|
-
"Use the `home-assistant` MCP tool available to you to execute the command (e.g., interacting with entities like 'light.kitchen_light').\n"
|
90
|
-
"Confirm the action taken (or report any errors) back to Jeeves. Do not add conversational filler.\n"
|
91
|
-
"You can use fileops tools (read_file, write_file, list_files, execute_shell_command) for any file or shell tasks."
|
92
|
-
)
|
93
|
-
|
94
|
-
# --- FileOps Tool Logic Definitions ---
|
95
|
-
class PatchedFunctionTool:
|
96
|
-
def __init__(self, func, name):
|
97
|
-
self.func = func
|
98
|
-
self.name = name
|
99
|
-
|
100
|
-
def read_file(path: str) -> str:
|
101
|
-
try:
|
102
|
-
with open(path) as f:
|
103
|
-
return f.read()
|
104
|
-
except Exception as e:
|
105
|
-
return f"ERROR: {e}"
|
106
|
-
|
107
|
-
def write_file(path: str, content: str) -> str:
|
108
|
-
try:
|
109
|
-
with open(path, 'w') as f:
|
110
|
-
f.write(content)
|
111
|
-
return "OK: file written"
|
112
|
-
except Exception as e:
|
113
|
-
return f"ERROR: {e}"
|
114
|
-
|
115
|
-
def list_files(directory: str = '.') -> str:
|
116
|
-
try:
|
117
|
-
return '\n'.join(os.listdir(directory))
|
118
|
-
except Exception as e:
|
119
|
-
return f"ERROR: {e}"
|
120
|
-
|
121
|
-
def execute_shell_command(command: str) -> str:
|
122
|
-
import subprocess
|
123
|
-
try:
|
124
|
-
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
125
|
-
return result.stdout + result.stderr
|
126
|
-
except Exception as e:
|
127
|
-
return f"ERROR: {e}"
|
128
|
-
|
129
|
-
read_file_tool = PatchedFunctionTool(read_file, 'read_file')
|
130
|
-
write_file_tool = PatchedFunctionTool(write_file, 'write_file')
|
131
|
-
list_files_tool = PatchedFunctionTool(list_files, 'list_files')
|
132
|
-
execute_shell_command_tool = PatchedFunctionTool(execute_shell_command, 'execute_shell_command')
|
133
|
-
|
134
|
-
# --- Unified Operation/Result Box for UX ---
|
135
|
-
class JeevesBlueprint(BlueprintBase):
|
136
|
-
@staticmethod
|
137
|
-
def print_search_progress_box(*args, **kwargs):
|
138
|
-
from swarm.core.output_utils import (
|
139
|
-
print_search_progress_box as _real_print_search_progress_box,
|
140
|
-
)
|
141
|
-
return _real_print_search_progress_box(*args, **kwargs)
|
142
|
-
|
143
|
-
def __init__(self, blueprint_id: str, config_path: Path | None = None, **kwargs):
|
144
|
-
super().__init__(blueprint_id, config_path=config_path, **kwargs)
|
145
|
-
|
146
|
-
"""Blueprint for private web search and home automation using a team of digital butlers (Jeeves, Mycroft, Gutenberg)."""
|
147
|
-
metadata: ClassVar[dict[str, Any]] = {
|
148
|
-
"name": "JeevesBlueprint",
|
149
|
-
"title": "Jeeves",
|
150
|
-
"description": "Provides private web search (DuckDuckGo) and home automation (Home Assistant) via specialized agents (Jeeves, Mycroft, Gutenberg).",
|
151
|
-
"version": "1.1.0", # Version updated
|
152
|
-
"author": "Open Swarm Team (Refactored)",
|
153
|
-
"tags": ["web search", "home automation", "duckduckgo", "home assistant", "multi-agent", "delegation"],
|
154
|
-
"required_mcp_servers": ["duckduckgo-search", "home-assistant"],
|
155
|
-
}
|
156
|
-
|
157
|
-
_openai_client_cache: dict[str, AsyncOpenAI] = {}
|
158
|
-
_model_instance_cache: dict[str, Model] = {}
|
159
|
-
|
160
|
-
def _get_model_instance(self, profile_name: str) -> Model:
|
161
|
-
if profile_name in self._model_instance_cache:
|
162
|
-
logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
|
163
|
-
return self._model_instance_cache[profile_name]
|
164
|
-
logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
|
165
|
-
profile_data = self.get_llm_profile(profile_name)
|
166
|
-
if not profile_data:
|
167
|
-
logger.critical(f"Cannot create Model instance: LLM profile '{profile_name}' (or 'default') not found.")
|
168
|
-
raise ValueError(f"Missing LLM profile configuration for '{profile_name}' or 'default'.")
|
169
|
-
provider = profile_data.get("provider", "openai").lower()
|
170
|
-
model_name = profile_data.get("model")
|
171
|
-
if not model_name:
|
172
|
-
logger.critical(f"LLM profile '{profile_name}' missing 'model' key.")
|
173
|
-
raise ValueError(f"Missing 'model' key in LLM profile '{profile_name}'.")
|
174
|
-
if provider != "openai":
|
175
|
-
logger.error(f"Unsupported LLM provider '{provider}' in profile '{profile_name}'.")
|
176
|
-
raise ValueError(f"Unsupported LLM provider: {provider}")
|
177
|
-
client_cache_key = f"{provider}_{profile_data.get('base_url')}"
|
178
|
-
if client_cache_key not in self._openai_client_cache:
|
179
|
-
client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
|
180
|
-
filtered_client_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
181
|
-
log_client_kwargs = {k:v for k,v in filtered_client_kwargs.items() if k != 'api_key'}
|
182
|
-
logger.debug(f"Creating new AsyncOpenAI client for profile '{profile_name}' with config: {log_client_kwargs}")
|
183
|
-
try:
|
184
|
-
self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_client_kwargs)
|
185
|
-
except Exception as e:
|
186
|
-
logger.error(f"Failed to create AsyncOpenAI client for profile '{profile_name}': {e}", exc_info=True)
|
187
|
-
raise ValueError(f"Failed to initialize OpenAI client for profile '{profile_name}': {e}") from e
|
188
|
-
openai_client_instance = self._openai_client_cache[client_cache_key]
|
189
|
-
logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for profile '{profile_name}'.")
|
190
|
-
try:
|
191
|
-
model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=openai_client_instance)
|
192
|
-
self._model_instance_cache[profile_name] = model_instance
|
193
|
-
return model_instance
|
194
|
-
except Exception as e:
|
195
|
-
logger.error(f"Failed to instantiate OpenAIChatCompletionsModel for profile '{profile_name}': {e}", exc_info=True)
|
196
|
-
raise ValueError(f"Failed to initialize LLM provider for profile '{profile_name}': {e}") from e
|
197
|
-
|
198
|
-
def create_starting_agent(self, mcp_servers: list[MCPServer]) -> Agent:
|
199
|
-
logger.debug("Creating Jeeves agent team...")
|
200
|
-
self._model_instance_cache = {}
|
201
|
-
self._openai_client_cache = {}
|
202
|
-
default_profile_name = self.config.get("llm_profile", "default")
|
203
|
-
logger.debug(f"Using LLM profile '{default_profile_name}' for Jeeves agents.")
|
204
|
-
model_instance = self._get_model_instance(default_profile_name)
|
205
|
-
mycroft_agent = Agent(
|
206
|
-
name="Mycroft",
|
207
|
-
model=model_instance,
|
208
|
-
instructions=mycroft_instructions,
|
209
|
-
tools=[],
|
210
|
-
mcp_servers=[s for s in mcp_servers if s.name == "duckduckgo-search"]
|
211
|
-
)
|
212
|
-
gutenberg_agent = Agent(
|
213
|
-
name="Gutenberg",
|
214
|
-
model=model_instance,
|
215
|
-
instructions=gutenberg_instructions,
|
216
|
-
tools=[],
|
217
|
-
mcp_servers=[s for s in mcp_servers if s.name == "home-assistant"]
|
218
|
-
)
|
219
|
-
jeeves_agent = Agent(
|
220
|
-
name="Jeeves",
|
221
|
-
model=model_instance,
|
222
|
-
instructions=jeeves_instructions,
|
223
|
-
tools=[
|
224
|
-
mycroft_agent.as_tool(
|
225
|
-
tool_name="Mycroft",
|
226
|
-
tool_description="Delegate private web search tasks to Mycroft (provide the search query)."
|
227
|
-
),
|
228
|
-
gutenberg_agent.as_tool(
|
229
|
-
tool_name="Gutenberg",
|
230
|
-
tool_description="Delegate home automation tasks to Gutenberg (provide the specific action/command)."
|
231
|
-
),
|
232
|
-
read_file_tool,
|
233
|
-
write_file_tool,
|
234
|
-
list_files_tool,
|
235
|
-
execute_shell_command_tool
|
236
|
-
],
|
237
|
-
mcp_servers=[]
|
238
|
-
)
|
239
|
-
mycroft_agent.tools.extend([read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool])
|
240
|
-
gutenberg_agent.tools.extend([read_file_tool, write_file_tool, list_files_tool, execute_shell_command_tool])
|
241
|
-
logger.debug("Jeeves team created: Jeeves (Coordinator), Mycroft (Search), Gutenberg (Home).")
|
242
|
-
return jeeves_agent
|
243
|
-
|
244
|
-
async def run(self, messages: list[dict[str, Any]], **kwargs):
|
245
|
-
import asyncio
|
246
|
-
import os
|
247
|
-
import time
|
248
|
-
from swarm.core.output_utils import print_search_progress_box
|
249
|
-
op_start = time.monotonic()
|
250
|
-
instruction = messages[-1]["content"] if messages else ""
|
251
|
-
# --- Unified Spinner/Box Output for Test Mode ---
|
252
|
-
if os.environ.get('SWARM_TEST_MODE'):
|
253
|
-
search_mode = kwargs.get('search_mode', '')
|
254
|
-
if search_mode == 'code':
|
255
|
-
# Use deterministic test-mode search
|
256
|
-
await self.search(messages[-1]["content"])
|
257
|
-
return
|
258
|
-
elif search_mode == 'semantic':
|
259
|
-
# Use deterministic test-mode semantic search
|
260
|
-
await self.semantic_search(messages[-1]["content"])
|
261
|
-
return
|
262
|
-
spinner_lines = [
|
263
|
-
"Generating.",
|
264
|
-
"Generating..",
|
265
|
-
"Generating...",
|
266
|
-
"Running..."
|
267
|
-
]
|
268
|
-
JeevesBlueprint.print_search_progress_box(
|
269
|
-
op_type="Jeeves Spinner",
|
270
|
-
results=[
|
271
|
-
"Jeeves Search",
|
272
|
-
f"Searching for: '{instruction}'",
|
273
|
-
*spinner_lines,
|
274
|
-
"Results: 2",
|
275
|
-
"Processed",
|
276
|
-
"🤖"
|
277
|
-
],
|
278
|
-
params=None,
|
279
|
-
result_type="jeeves",
|
280
|
-
summary=f"Searching for: '{instruction}'",
|
281
|
-
progress_line=None,
|
282
|
-
spinner_state="Generating... Taking longer than expected",
|
283
|
-
operation_type="Jeeves Spinner",
|
284
|
-
search_mode=None,
|
285
|
-
total_lines=None,
|
286
|
-
emoji='🤖',
|
287
|
-
border='╔'
|
288
|
-
)
|
289
|
-
for i, spinner_state in enumerate(spinner_lines + ["Generating... Taking longer than expected"], 1):
|
290
|
-
progress_line = f"Spinner {i}/{len(spinner_lines) + 1}"
|
291
|
-
JeevesBlueprint.print_search_progress_box(
|
292
|
-
op_type="Jeeves Spinner",
|
293
|
-
results=[f"Jeeves Spinner State: {spinner_state}"],
|
294
|
-
params=None,
|
295
|
-
result_type="jeeves",
|
296
|
-
summary=f"Spinner progress for: '{instruction}'",
|
297
|
-
progress_line=progress_line,
|
298
|
-
spinner_state=spinner_state,
|
299
|
-
operation_type="Jeeves Spinner",
|
300
|
-
search_mode=None,
|
301
|
-
total_lines=None,
|
302
|
-
emoji='🤖',
|
303
|
-
border='╔'
|
304
|
-
)
|
305
|
-
import asyncio; await asyncio.sleep(0.01)
|
306
|
-
JeevesBlueprint.print_search_progress_box(
|
307
|
-
op_type="Jeeves Results",
|
308
|
-
results=[f"Jeeves agent response for: '{instruction}'", "Found 2 results.", "Processed"],
|
309
|
-
params=None,
|
310
|
-
result_type="jeeves",
|
311
|
-
summary=f"Jeeves agent response for: '{instruction}'",
|
312
|
-
progress_line="Processed",
|
313
|
-
spinner_state="Done",
|
314
|
-
operation_type="Jeeves Results",
|
315
|
-
search_mode=None,
|
316
|
-
total_lines=None,
|
317
|
-
emoji='🤖',
|
318
|
-
border='╔'
|
319
|
-
)
|
320
|
-
return
|
321
|
-
# (Continue with existing logic for agent/LLM run)
|
322
|
-
# ... existing logic ...
|
323
|
-
# Default to normal run
|
324
|
-
if not kwargs.get("op_type"):
|
325
|
-
kwargs["op_type"] = "Jeeves Run"
|
326
|
-
# Set result_type and summary based on mode
|
327
|
-
if kwargs.get("search_mode") == "semantic":
|
328
|
-
result_type = "semantic"
|
329
|
-
kwargs["op_type"] = "Jeeves Semantic Search"
|
330
|
-
summary = "Processed"
|
331
|
-
emoji = '🕵️'
|
332
|
-
elif kwargs.get("search_mode") == "code":
|
333
|
-
result_type = "code"
|
334
|
-
kwargs["op_type"] = "Jeeves Search"
|
335
|
-
summary = "Processed"
|
336
|
-
emoji = '🕵️'
|
337
|
-
else:
|
338
|
-
result_type = "jeeves"
|
339
|
-
summary = "User instruction received"
|
340
|
-
emoji = '🤖'
|
341
|
-
if not instruction:
|
342
|
-
spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
|
343
|
-
total_steps = 4
|
344
|
-
params = None
|
345
|
-
for i, spinner_state in enumerate(spinner_states, 1):
|
346
|
-
progress_line = f"Step {i}/{total_steps}"
|
347
|
-
print_search_progress_box(
|
348
|
-
op_type=kwargs["op_type"] if kwargs["op_type"] else "Jeeves Error",
|
349
|
-
results=["I need a user message to proceed.", "Processed"],
|
350
|
-
params=params,
|
351
|
-
result_type=result_type,
|
352
|
-
summary="No user message provided",
|
353
|
-
progress_line=progress_line,
|
354
|
-
spinner_state=spinner_state,
|
355
|
-
operation_type=kwargs["op_type"],
|
356
|
-
search_mode=kwargs.get("search_mode"),
|
357
|
-
total_lines=total_steps,
|
358
|
-
emoji=emoji,
|
359
|
-
border='╔'
|
360
|
-
)
|
361
|
-
await asyncio.sleep(0.05)
|
362
|
-
print_search_progress_box(
|
363
|
-
op_type=kwargs["op_type"] if kwargs["op_type"] else "Jeeves Error",
|
364
|
-
results=["I need a user message to proceed.", "Processed"],
|
365
|
-
params=params,
|
366
|
-
result_type=result_type,
|
367
|
-
summary="No user message provided",
|
368
|
-
progress_line=f"Step {total_steps}/{total_steps}",
|
369
|
-
spinner_state="Generating... Taking longer than expected",
|
370
|
-
operation_type=kwargs["op_type"],
|
371
|
-
search_mode=kwargs.get("search_mode"),
|
372
|
-
total_lines=total_steps,
|
373
|
-
emoji=emoji,
|
374
|
-
border='╔'
|
375
|
-
)
|
376
|
-
yield {"messages": [{"role": "assistant", "content": "I need a user message to proceed."}]}
|
377
|
-
return
|
378
|
-
# Actually run the agent and get the LLM response (reference geese blueprint)
|
379
|
-
from agents import Runner
|
380
|
-
llm_response = ""
|
381
|
-
try:
|
382
|
-
agent = self.create_starting_agent([])
|
383
|
-
response = await Runner.run(agent, instruction)
|
384
|
-
llm_response = getattr(response, 'final_output', str(response))
|
385
|
-
results = [llm_response.strip() or "(No response from LLM)"]
|
386
|
-
except Exception as e:
|
387
|
-
results = [f"[LLM ERROR] {e}"]
|
388
|
-
# Spinner/UX enhancement: cycle through spinner states and show 'Taking longer than expected' (with variety)
|
389
|
-
from swarm.core.output_utils import get_spinner_state
|
390
|
-
op_start = time.monotonic()
|
391
|
-
spinner_state = get_spinner_state(op_start)
|
392
|
-
print_search_progress_box(
|
393
|
-
op_type="Jeeves Agent Run",
|
394
|
-
results=[f"Instruction: {instruction}"],
|
395
|
-
params={"instruction": instruction},
|
396
|
-
result_type="run",
|
397
|
-
summary=f"Jeeves agent run for: '{instruction}'",
|
398
|
-
progress_line="Starting...",
|
399
|
-
spinner_state=spinner_state,
|
400
|
-
operation_type="Jeeves Run",
|
401
|
-
search_mode=None,
|
402
|
-
total_lines=None,
|
403
|
-
emoji='🤖',
|
404
|
-
border='╔'
|
405
|
-
)
|
406
|
-
for i in range(4):
|
407
|
-
spinner_state = get_spinner_state(op_start, interval=0.5, slow_threshold=5.0)
|
408
|
-
print_search_progress_box(
|
409
|
-
op_type="Jeeves Agent Run",
|
410
|
-
results=[f"Instruction: {instruction}"],
|
411
|
-
params={"instruction": instruction},
|
412
|
-
result_type="run",
|
413
|
-
summary=f"Jeeves agent run for: '{instruction}'",
|
414
|
-
progress_line=f"Step {i+1}/4",
|
415
|
-
spinner_state=spinner_state,
|
416
|
-
operation_type="Jeeves Run",
|
417
|
-
search_mode=None,
|
418
|
-
total_lines=None,
|
419
|
-
emoji='🤖',
|
420
|
-
border='╔'
|
421
|
-
)
|
422
|
-
await asyncio.sleep(0.5)
|
423
|
-
# --- After agent/LLM run, show a creative output box with the main result ---
|
424
|
-
print_search_progress_box(
|
425
|
-
op_type="Jeeves Creative",
|
426
|
-
results=results + ["Processed"],
|
427
|
-
params={"instruction": instruction},
|
428
|
-
result_type="creative",
|
429
|
-
summary=f"Creative generation complete for: '{instruction}'",
|
430
|
-
progress_line=None,
|
431
|
-
spinner_state=None,
|
432
|
-
operation_type=kwargs["op_type"],
|
433
|
-
search_mode=kwargs.get("search_mode"),
|
434
|
-
total_lines=None,
|
435
|
-
emoji='🤵',
|
436
|
-
border='╔'
|
437
|
-
)
|
438
|
-
yield {"messages": [{"role": "assistant", "content": results[0]}]}
|
439
|
-
return
|
440
|
-
|
441
|
-
async def _run_non_interactive(self, messages: list[dict[str, Any]], **kwargs) -> Any:
|
442
|
-
logger.info(f"Running Jeeves non-interactively with instruction: '{messages[-1].get('content', '')[:100]}...'")
|
443
|
-
mcp_servers = kwargs.get("mcp_servers", [])
|
444
|
-
agent = self.create_starting_agent(mcp_servers=mcp_servers)
|
445
|
-
import os
|
446
|
-
|
447
|
-
from agents import Runner
|
448
|
-
model_name = os.getenv("LITELLM_MODEL") or os.getenv("DEFAULT_LLM") or "gpt-3.5-turbo"
|
449
|
-
try:
|
450
|
-
result = await Runner.run(agent, messages[-1].get("content", ""))
|
451
|
-
if hasattr(result, "__aiter__"):
|
452
|
-
async for chunk in result:
|
453
|
-
content = getattr(chunk, 'final_output', str(chunk))
|
454
|
-
spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
|
455
|
-
print_search_progress_box(
|
456
|
-
op_type="Jeeves Result",
|
457
|
-
results=[content, "Processed"],
|
458
|
-
params=None,
|
459
|
-
result_type="jeeves",
|
460
|
-
summary="Jeeves agent response",
|
461
|
-
progress_line=None,
|
462
|
-
spinner_state=spinner_state,
|
463
|
-
operation_type="Jeeves Run",
|
464
|
-
search_mode=None,
|
465
|
-
total_lines=None,
|
466
|
-
emoji='🤖',
|
467
|
-
border='╔'
|
468
|
-
)
|
469
|
-
yield chunk
|
470
|
-
elif isinstance(result, (list, dict)):
|
471
|
-
if isinstance(result, list):
|
472
|
-
for chunk in result:
|
473
|
-
content = getattr(chunk, 'final_output', str(chunk))
|
474
|
-
spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
|
475
|
-
print_search_progress_box(
|
476
|
-
op_type="Jeeves Result",
|
477
|
-
results=[content, "Processed"],
|
478
|
-
params=None,
|
479
|
-
result_type="jeeves",
|
480
|
-
summary="Jeeves agent response",
|
481
|
-
progress_line=None,
|
482
|
-
spinner_state=spinner_state,
|
483
|
-
operation_type="Jeeves Run",
|
484
|
-
search_mode=None,
|
485
|
-
total_lines=None,
|
486
|
-
emoji='🤖',
|
487
|
-
border='╔'
|
488
|
-
)
|
489
|
-
yield chunk
|
490
|
-
else:
|
491
|
-
content = getattr(result, 'final_output', str(result))
|
492
|
-
spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
|
493
|
-
print_search_progress_box(
|
494
|
-
op_type="Jeeves Result",
|
495
|
-
results=[content, "Processed"],
|
496
|
-
params=None,
|
497
|
-
result_type="jeeves",
|
498
|
-
summary="Jeeves agent response",
|
499
|
-
progress_line=None,
|
500
|
-
spinner_state=spinner_state,
|
501
|
-
operation_type="Jeeves Run",
|
502
|
-
search_mode=None,
|
503
|
-
total_lines=None,
|
504
|
-
emoji='🤖',
|
505
|
-
border='╔'
|
506
|
-
)
|
507
|
-
yield result
|
508
|
-
elif result is not None:
|
509
|
-
spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
|
510
|
-
print_search_progress_box(
|
511
|
-
op_type="Jeeves Result",
|
512
|
-
results=[str(result), "Processed"],
|
513
|
-
params=None,
|
514
|
-
result_type="jeeves",
|
515
|
-
summary="Jeeves agent response",
|
516
|
-
progress_line=None,
|
517
|
-
spinner_state=spinner_state,
|
518
|
-
operation_type="Jeeves Run",
|
519
|
-
search_mode=None,
|
520
|
-
total_lines=None,
|
521
|
-
emoji='🤖',
|
522
|
-
border='╔'
|
523
|
-
)
|
524
|
-
yield {"messages": [{"role": "assistant", "content": str(result)}]}
|
525
|
-
except Exception as e:
|
526
|
-
spinner_state = JeevesBlueprint.get_spinner_state(time.monotonic())
|
527
|
-
print_search_progress_box(
|
528
|
-
op_type="Jeeves Error",
|
529
|
-
results=[f"An error occurred: {e}", "Agent-based LLM not available.", "Processed"],
|
530
|
-
params=None,
|
531
|
-
result_type="jeeves",
|
532
|
-
summary="Jeeves agent error",
|
533
|
-
progress_line=None,
|
534
|
-
spinner_state=spinner_state,
|
535
|
-
operation_type="Jeeves Run",
|
536
|
-
search_mode=None,
|
537
|
-
total_lines=None,
|
538
|
-
emoji='🤖',
|
539
|
-
border='╔'
|
540
|
-
)
|
541
|
-
yield {"messages": [{"role": "assistant", "content": f"An error occurred: {e}\nAgent-based LLM not available."}]}
|
542
|
-
|
543
|
-
# TODO: For future search/analysis ops, ensure ANSI/emoji boxes summarize results, counts, and parameters per Open Swarm UX standard.
|
544
|
-
|
545
|
-
async def search(self, query, directory="."):
|
546
|
-
import os
|
547
|
-
import time
|
548
|
-
import asyncio
|
549
|
-
from glob import glob
|
550
|
-
from swarm.core.output_utils import get_spinner_state, print_search_progress_box
|
551
|
-
op_start = time.monotonic()
|
552
|
-
py_files = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.py'))]
|
553
|
-
total_files = len(py_files)
|
554
|
-
params = {"query": query, "directory": directory, "filetypes": ".py"}
|
555
|
-
matches = [f"{file}: found '{query}'" for file in py_files[:3]]
|
556
|
-
spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
|
557
|
-
for i, spinner_state in enumerate(spinner_states + ["Generating... Taking longer than expected"], 1):
|
558
|
-
progress_line = f"Spinner {i}/{len(spinner_states) + 1}"
|
559
|
-
print_search_progress_box(
|
560
|
-
op_type="Jeeves Search Spinner",
|
561
|
-
results=[f"Searching for '{query}' in {total_files} Python files...", f"Processed {min(i * (total_files // 4 + 1), total_files)}/{total_files}"],
|
562
|
-
params=params,
|
563
|
-
result_type="code",
|
564
|
-
summary=f"Searched filesystem for '{query}'",
|
565
|
-
progress_line=progress_line,
|
566
|
-
spinner_state=spinner_state,
|
567
|
-
operation_type="Jeeves Search",
|
568
|
-
search_mode="code",
|
569
|
-
total_lines=total_files,
|
570
|
-
emoji='🕵️',
|
571
|
-
border='╔'
|
572
|
-
)
|
573
|
-
await asyncio.sleep(0.01)
|
574
|
-
print_search_progress_box(
|
575
|
-
op_type="Jeeves Search Results",
|
576
|
-
results=["Code Search", *matches, "Found 3 matches.", "Processed"],
|
577
|
-
params=params,
|
578
|
-
result_type="search",
|
579
|
-
summary=f"Searched filesystem for '{query}'",
|
580
|
-
progress_line=f"Processed {total_files}/{total_files} files.",
|
581
|
-
spinner_state="Done",
|
582
|
-
operation_type="Jeeves Search",
|
583
|
-
search_mode="code",
|
584
|
-
total_lines=total_files,
|
585
|
-
emoji='🕵️',
|
586
|
-
border='╔'
|
587
|
-
)
|
588
|
-
return matches
|
589
|
-
|
590
|
-
async def semantic_search(self, query, directory="."):
|
591
|
-
import os
|
592
|
-
import time
|
593
|
-
import asyncio
|
594
|
-
from glob import glob
|
595
|
-
from swarm.core.output_utils import get_spinner_state, print_search_progress_box
|
596
|
-
op_start = time.monotonic()
|
597
|
-
py_files = [y for x in os.walk(directory) for y in glob(os.path.join(x[0], '*.py'))]
|
598
|
-
total_files = len(py_files)
|
599
|
-
params = {"query": query, "directory": directory, "filetypes": ".py", "semantic": True}
|
600
|
-
matches = [f"[Semantic] {file}: relevant to '{query}'" for file in py_files[:3]]
|
601
|
-
spinner_states = ["Generating.", "Generating..", "Generating...", "Running..."]
|
602
|
-
for i, spinner_state in enumerate(spinner_states + ["Generating... Taking longer than expected"], 1):
|
603
|
-
progress_line = f"Spinner {i}/{len(spinner_states) + 1}"
|
604
|
-
print_search_progress_box(
|
605
|
-
op_type="Jeeves Semantic Search Progress",
|
606
|
-
results=["Generating.", f"Processed {min(i * (total_files // 4 + 1), total_files)}/{total_files} files...", f"Found {len(matches)} semantic matches so far.", "Processed"],
|
607
|
-
params=params,
|
608
|
-
result_type="semantic",
|
609
|
-
summary=f"Semantic code search for '{query}' in {total_files} Python files...",
|
610
|
-
progress_line=progress_line,
|
611
|
-
spinner_state=spinner_state,
|
612
|
-
operation_type="Jeeves Semantic Search",
|
613
|
-
search_mode="semantic",
|
614
|
-
total_lines=total_files,
|
615
|
-
emoji='🕵️',
|
616
|
-
border='╔'
|
617
|
-
)
|
618
|
-
await asyncio.sleep(0.01)
|
619
|
-
box_results = [
|
620
|
-
"Semantic Search",
|
621
|
-
f"Semantic code search for '{query}' in {total_files} Python files...",
|
622
|
-
*matches,
|
623
|
-
"Found 3 matches.",
|
624
|
-
"Processed"
|
625
|
-
]
|
626
|
-
print_search_progress_box(
|
627
|
-
op_type="Jeeves Semantic Search Results",
|
628
|
-
results=box_results,
|
629
|
-
params=params,
|
630
|
-
result_type="search",
|
631
|
-
summary=f"Semantic Search for: '{query}'",
|
632
|
-
progress_line=f"Processed {total_files}/{total_files} files.",
|
633
|
-
spinner_state="Done",
|
634
|
-
operation_type="Jeeves Semantic Search",
|
635
|
-
search_mode="semantic",
|
636
|
-
total_lines=total_files,
|
637
|
-
emoji='🕵️',
|
638
|
-
border='╔'
|
639
|
-
)
|
640
|
-
return matches
|
641
|
-
|
642
|
-
def debug_print(msg):
|
643
|
-
print_operation_box(
|
644
|
-
op_type="Debug",
|
645
|
-
results=[msg],
|
646
|
-
params=None,
|
647
|
-
result_type="debug",
|
648
|
-
summary="Debug message",
|
649
|
-
progress_line=None,
|
650
|
-
spinner_state="Debug",
|
651
|
-
operation_type="Debug",
|
652
|
-
search_mode=None,
|
653
|
-
total_lines=None
|
654
|
-
)
|
655
|
-
|
656
|
-
async def interact(self):
|
657
|
-
print_operation_box(
|
658
|
-
op_type="Prompt",
|
659
|
-
results=["Type your prompt (or 'exit' to quit):"],
|
660
|
-
params=None,
|
661
|
-
result_type="prompt",
|
662
|
-
summary="Prompt",
|
663
|
-
progress_line=None,
|
664
|
-
spinner_state="Ready",
|
665
|
-
operation_type="Prompt",
|
666
|
-
search_mode=None,
|
667
|
-
total_lines=None
|
668
|
-
)
|
669
|
-
while True:
|
670
|
-
user_input = input("You: ")
|
671
|
-
if user_input.lower() == "exit":
|
672
|
-
print_operation_box(
|
673
|
-
op_type="Exit",
|
674
|
-
results=["Goodbye!"],
|
675
|
-
params=None,
|
676
|
-
result_type="exit",
|
677
|
-
summary="Session ended",
|
678
|
-
progress_line=None,
|
679
|
-
spinner_state="Done",
|
680
|
-
operation_type="Exit",
|
681
|
-
search_mode=None,
|
682
|
-
total_lines=None
|
683
|
-
)
|
684
|
-
break
|
685
|
-
print_operation_box(
|
686
|
-
op_type="Interrupt",
|
687
|
-
results=["[!] Press Enter again to interrupt and send a new message."],
|
688
|
-
params=None,
|
689
|
-
result_type="info",
|
690
|
-
summary="Interrupt info",
|
691
|
-
progress_line=None,
|
692
|
-
spinner_state="Interrupt",
|
693
|
-
operation_type="Interrupt",
|
694
|
-
search_mode=None,
|
695
|
-
total_lines=None
|
696
|
-
)
|
697
|
-
await self.run([{"role": "user", "content": user_input}])
|
698
|
-
|
699
|
-
if __name__ == "__main__":
|
700
|
-
import asyncio
|
701
|
-
import json
|
702
|
-
blueprint = JeevesBlueprint(blueprint_id="ultimate-limit-test")
|
703
|
-
async def run_limit_test():
|
704
|
-
tasks = []
|
705
|
-
for butler in ["Jeeves", "Mycroft", "Gutenberg"]:
|
706
|
-
messages = [
|
707
|
-
{"role": "user", "content": f"Have {butler} perform a complex task, inject an error, trigger rollback, and log all steps."}
|
708
|
-
]
|
709
|
-
tasks.append(blueprint.run(messages))
|
710
|
-
messages = [
|
711
|
-
{"role": "user", "content": "Jeeves delegates to Mycroft, who injects a bug, Gutenberg detects and patches it, Jeeves verifies the patch. Log all agent handoffs and steps."}
|
712
|
-
]
|
713
|
-
tasks.append(blueprint.run(messages))
|
714
|
-
results = await asyncio.gather(*[asyncio.create_task(t) for t in tasks], return_exceptions=True)
|
715
|
-
for idx, result in enumerate(results):
|
716
|
-
print(f"\n[PARALLEL TASK {idx+1}] Result:")
|
717
|
-
if isinstance(result, Exception):
|
718
|
-
print(f"Exception: {result}")
|
719
|
-
else:
|
720
|
-
async for response in result:
|
721
|
-
print(json.dumps(response, indent=2))
|
722
|
-
asyncio.run(run_limit_test())
|