open-swarm 0.1.1745275181__py3-none-any.whl → 0.1.1748636295__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- open_swarm-0.1.1748636295.dist-info/METADATA +257 -0
- open_swarm-0.1.1748636295.dist-info/RECORD +89 -0
- {open_swarm-0.1.1745275181.dist-info → open_swarm-0.1.1748636295.dist-info}/WHEEL +2 -1
- open_swarm-0.1.1748636295.dist-info/entry_points.txt +3 -0
- open_swarm-0.1.1748636295.dist-info/top_level.txt +1 -0
- swarm/__init__.py +2 -0
- swarm/agent/agent.py +49 -0
- swarm/auth.py +48 -113
- swarm/consumers.py +0 -19
- swarm/core.py +411 -0
- swarm/extensions/blueprint/__init__.py +16 -30
- swarm/extensions/blueprint/agent_utils.py +45 -0
- swarm/extensions/blueprint/blueprint_base.py +562 -0
- swarm/extensions/blueprint/blueprint_discovery.py +112 -0
- swarm/extensions/blueprint/django_utils.py +79 -181
- swarm/extensions/blueprint/interactive_mode.py +72 -67
- swarm/extensions/blueprint/output_utils.py +82 -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 +345 -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/extensions/mcp/__init__.py +1 -0
- swarm/extensions/mcp/cache_utils.py +32 -0
- swarm/extensions/mcp/mcp_client.py +233 -0
- swarm/extensions/mcp/mcp_tool_provider.py +135 -0
- swarm/extensions/mcp/mcp_utils.py +260 -0
- swarm/llm/chat_completion.py +166 -0
- swarm/serializers.py +5 -96
- swarm/settings.py +133 -85
- swarm/types.py +91 -0
- 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 +76 -236
- swarm/views/core_views.py +87 -80
- swarm/views/model_views.py +121 -64
- swarm/views/utils.py +439 -65
- swarm/views/web_views.py +2 -2
- open_swarm-0.1.1745275181.dist-info/METADATA +0 -874
- open_swarm-0.1.1745275181.dist-info/RECORD +0 -319
- open_swarm-0.1.1745275181.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 -10
- swarm/blueprints/geese/__init__.py +0 -8
- swarm/blueprints/geese/blueprint_geese.py +0 -384
- 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/agent_utils.py +0 -21
- 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.1745275181.dist-info → open_swarm-0.1.1748636295.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,384 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from dotenv import load_dotenv; load_dotenv(override=True)
|
3
|
-
|
4
|
-
import logging
|
5
|
-
|
6
|
-
logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(name)s: %(message)s')
|
7
|
-
import asyncio
|
8
|
-
import sys
|
9
|
-
|
10
|
-
|
11
|
-
def force_info_logging():
|
12
|
-
root = logging.getLogger()
|
13
|
-
for handler in root.handlers[:]:
|
14
|
-
root.removeHandler(handler)
|
15
|
-
loglevel = os.environ.get('LOGLEVEL', None)
|
16
|
-
debug_env = os.environ.get('SWARM_DEBUG', '0') == '1'
|
17
|
-
debug_arg = '--debug' in sys.argv
|
18
|
-
if debug_arg or debug_env or (loglevel and loglevel.upper() == 'DEBUG'):
|
19
|
-
level = logging.DEBUG
|
20
|
-
else:
|
21
|
-
level = logging.INFO
|
22
|
-
logging.basicConfig(level=level, format='[%(levelname)s] %(name)s: %(message)s')
|
23
|
-
root.setLevel(level)
|
24
|
-
|
25
|
-
force_info_logging()
|
26
|
-
|
27
|
-
import argparse
|
28
|
-
|
29
|
-
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
30
|
-
src_path = os.path.join(project_root, 'src')
|
31
|
-
if src_path not in sys.path: sys.path.insert(0, src_path)
|
32
|
-
|
33
|
-
try:
|
34
|
-
from openai import AsyncOpenAI
|
35
|
-
|
36
|
-
from agents import Agent, Runner, Tool, function_tool
|
37
|
-
from agents.mcp import MCPServer
|
38
|
-
from agents.models.interface import Model
|
39
|
-
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
40
|
-
from swarm.core.blueprint_base import BlueprintBase
|
41
|
-
except ImportError as e:
|
42
|
-
print(f"ERROR: Import failed in blueprint_geese: {e}. Check 'openai-agents' install and project structure.")
|
43
|
-
print(f"sys.path: {sys.path}")
|
44
|
-
sys.exit(1)
|
45
|
-
|
46
|
-
|
47
|
-
def setup_logging():
|
48
|
-
parser = argparse.ArgumentParser(add_help=False)
|
49
|
-
parser.add_argument('--debug', action='store_true', help='Enable debug logging')
|
50
|
-
args, _ = parser.parse_known_args()
|
51
|
-
loglevel = os.environ.get('LOGLEVEL', None)
|
52
|
-
if args.debug or os.environ.get('SWARM_DEBUG', '0') == '1' or (loglevel and loglevel.upper() == 'DEBUG'):
|
53
|
-
logging.basicConfig(level=logging.DEBUG)
|
54
|
-
else:
|
55
|
-
logging.basicConfig(level=logging.INFO)
|
56
|
-
return args
|
57
|
-
|
58
|
-
args = setup_logging()
|
59
|
-
|
60
|
-
logger = logging.getLogger(__name__)
|
61
|
-
|
62
|
-
# --- Tools ---
|
63
|
-
def _create_story_outline(topic: str) -> str:
|
64
|
-
logger.info(f"Tool: Generating outline for: {topic}")
|
65
|
-
outline = f"Story Outline for '{topic}':\n1. Beginning: Introduce characters and setting.\n2. Middle: Develop conflict and rising action.\n3. Climax: The peak of the conflict.\n4. End: Resolution and aftermath."
|
66
|
-
logger.debug(f"Generated outline: {outline}")
|
67
|
-
return outline
|
68
|
-
|
69
|
-
@function_tool
|
70
|
-
def create_story_outline(topic: str) -> str:
|
71
|
-
"""Generates a basic story outline based on a topic."""
|
72
|
-
return _create_story_outline(topic)
|
73
|
-
|
74
|
-
def _write_story_part(part_name: str, outline: str, previous_parts: str) -> str:
|
75
|
-
logger.info(f"Tool: Writing story part: {part_name}")
|
76
|
-
content = f"## {part_name}\n\nThis is the draft content for the '{part_name}' section. It follows:\n'{previous_parts[:100]}...' \nIt should align with the outline:\n'{outline}'"
|
77
|
-
logger.debug(f"Generated content for {part_name}: {content[:100]}...")
|
78
|
-
return content
|
79
|
-
|
80
|
-
@function_tool
|
81
|
-
def write_story_part(part_name: str, outline: str, previous_parts: str) -> str:
|
82
|
-
"""Writes a specific part of the story using the outline and previous context."""
|
83
|
-
return _write_story_part(part_name, outline, previous_parts)
|
84
|
-
|
85
|
-
def _edit_story(full_story: str, edit_instructions: str) -> str:
|
86
|
-
logger.info(f"Tool: Editing story with instructions: {edit_instructions}")
|
87
|
-
edited_content = f"*** Edited Story Draft ***\n(Based on instructions: '{edit_instructions}')\n\n{full_story}\n\n[Editor's Notes: Minor tweaks applied for flow.]"
|
88
|
-
logger.debug("Editing complete.")
|
89
|
-
return edited_content
|
90
|
-
|
91
|
-
@function_tool
|
92
|
-
def edit_story(full_story: str, edit_instructions: str) -> str:
|
93
|
-
"""Edits the complete story based on instructions."""
|
94
|
-
return _edit_story(full_story, edit_instructions)
|
95
|
-
|
96
|
-
from rich.console import Console
|
97
|
-
from rich.panel import Panel
|
98
|
-
|
99
|
-
from swarm.core.output_utils import (
|
100
|
-
print_search_progress_box,
|
101
|
-
setup_rotating_httpx_log,
|
102
|
-
)
|
103
|
-
|
104
|
-
|
105
|
-
class GeeseBlueprint(BlueprintBase):
|
106
|
-
def __init__(self, blueprint_id: str, config_path: str | None = None, **kwargs):
|
107
|
-
super().__init__(blueprint_id, config_path, **kwargs)
|
108
|
-
from agents import Agent
|
109
|
-
# --- Setup OpenAI LLM Model ---
|
110
|
-
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
111
|
-
openai_client = AsyncOpenAI(api_key=openai_api_key) if openai_api_key else None
|
112
|
-
llm_model_name = kwargs.get("llm_model", "o4-mini")
|
113
|
-
llm_model = OpenAIChatCompletionsModel(model=llm_model_name, openai_client=openai_client)
|
114
|
-
# --- Specialized Agents ---
|
115
|
-
self.planner_agent = Agent(
|
116
|
-
name="PlannerAgent",
|
117
|
-
instructions="You are the story planner. Break down the story into sections and assign tasks.",
|
118
|
-
tools=[],
|
119
|
-
model=llm_model
|
120
|
-
).as_tool("Planner", "Plan and outline stories.")
|
121
|
-
self.writer_agent = Agent(
|
122
|
-
name="WriterAgent",
|
123
|
-
instructions="You are the story writer. Write detailed sections of the story based on the plan.",
|
124
|
-
tools=[],
|
125
|
-
model=llm_model
|
126
|
-
).as_tool("Writer", "Write story sections.")
|
127
|
-
self.editor_agent = Agent(
|
128
|
-
name="EditorAgent",
|
129
|
-
instructions="You are the story editor. Edit and improve the story for clarity and engagement.",
|
130
|
-
tools=[],
|
131
|
-
model=llm_model
|
132
|
-
).as_tool("Editor", "Edit and improve stories.")
|
133
|
-
# --- Coordinator Agent ---
|
134
|
-
self.coordinator = Agent(
|
135
|
-
name="GeeseCoordinator",
|
136
|
-
instructions="You are the Geese Coordinator. Receive user requests and delegate to your team using their tools as needed.",
|
137
|
-
tools=[self.planner_agent, self.writer_agent, self.editor_agent],
|
138
|
-
model=llm_model
|
139
|
-
)
|
140
|
-
self.logger = logging.getLogger(__name__)
|
141
|
-
self._model_instance_cache = {}
|
142
|
-
self._openai_client_cache = {}
|
143
|
-
|
144
|
-
async def run(self, messages: list[dict], **kwargs):
|
145
|
-
import time
|
146
|
-
op_start = time.monotonic()
|
147
|
-
query = messages[-1]["content"] if messages else ""
|
148
|
-
params = {"query": query}
|
149
|
-
results = []
|
150
|
-
# Suppress noisy httpx logging unless --debug
|
151
|
-
import os
|
152
|
-
setup_rotating_httpx_log(debug_mode=os.environ.get('SWARM_DEBUG') == '1')
|
153
|
-
# --- Unified UX/Test Mode Spinner & Box Output ---
|
154
|
-
if os.environ.get("SWARM_TEST_MODE"):
|
155
|
-
from swarm.core.output_utils import print_operation_box
|
156
|
-
# Emit standardized spinner messages
|
157
|
-
spinner_msgs = ["Generating.", "Generating..", "Generating...", "Running...", "Generating... Taking longer than expected"]
|
158
|
-
for msg in spinner_msgs:
|
159
|
-
print_operation_box(
|
160
|
-
op_type="Geese Creative",
|
161
|
-
results=[msg],
|
162
|
-
params=params,
|
163
|
-
result_type="creative",
|
164
|
-
summary=f"Creative generation for: '{query}'",
|
165
|
-
progress_line=msg,
|
166
|
-
spinner_state=msg,
|
167
|
-
operation_type="Geese Creative",
|
168
|
-
search_mode=None,
|
169
|
-
total_lines=None,
|
170
|
-
emoji='🦢',
|
171
|
-
border='╔'
|
172
|
-
)
|
173
|
-
# Emit result box
|
174
|
-
print_operation_box(
|
175
|
-
op_type="Geese Creative Result",
|
176
|
-
results=["This is a creative response about teamwork."],
|
177
|
-
params=params,
|
178
|
-
result_type="creative",
|
179
|
-
summary=f"Creative generation complete for: '{query}'",
|
180
|
-
progress_line=None,
|
181
|
-
spinner_state=None,
|
182
|
-
operation_type="Geese Creative",
|
183
|
-
search_mode=None,
|
184
|
-
total_lines=None,
|
185
|
-
emoji='🦢',
|
186
|
-
border='╔'
|
187
|
-
)
|
188
|
-
yield {"messages": [{"role": "assistant", "content": "This is a creative response about teamwork."}]}
|
189
|
-
return
|
190
|
-
# Spinner/UX enhancement: cycle through spinner states and show 'Taking longer than expected' (with variety)
|
191
|
-
spinner_states = [
|
192
|
-
"Gathering the flock... 🦢",
|
193
|
-
"Herding geese... 🪿",
|
194
|
-
"Honking in unison... 🎶",
|
195
|
-
"Flying in formation... 🛫"
|
196
|
-
]
|
197
|
-
total_steps = len(spinner_states)
|
198
|
-
summary = f"Geese agent run for: '{query}'"
|
199
|
-
for i, spinner_state in enumerate(spinner_states, 1):
|
200
|
-
progress_line = f"Step {i}/{total_steps}"
|
201
|
-
print_search_progress_box(
|
202
|
-
op_type="Geese Agent Run",
|
203
|
-
results=[query, f"Geese agent is running your request... (Step {i})"],
|
204
|
-
params=params,
|
205
|
-
result_type="geese",
|
206
|
-
summary=summary,
|
207
|
-
progress_line=progress_line,
|
208
|
-
spinner_state=spinner_state,
|
209
|
-
operation_type="Geese Run",
|
210
|
-
search_mode=None,
|
211
|
-
total_lines=total_steps,
|
212
|
-
emoji='🦢',
|
213
|
-
border='╔'
|
214
|
-
)
|
215
|
-
await asyncio.sleep(0.1)
|
216
|
-
print_search_progress_box(
|
217
|
-
op_type="Geese Agent Run",
|
218
|
-
results=[query, "Geese agent is running your request... (Taking longer than expected)", "Still honking..."],
|
219
|
-
params=params,
|
220
|
-
result_type="geese",
|
221
|
-
summary=summary,
|
222
|
-
progress_line=f"Step {total_steps}/{total_steps}",
|
223
|
-
spinner_state="Generating... Taking longer than expected 🦢",
|
224
|
-
operation_type="Geese Run",
|
225
|
-
search_mode=None,
|
226
|
-
total_lines=total_steps,
|
227
|
-
emoji='🦢',
|
228
|
-
border='╔'
|
229
|
-
)
|
230
|
-
await asyncio.sleep(0.2)
|
231
|
-
|
232
|
-
# Actually run the agent and get the LLM response
|
233
|
-
agent = self.coordinator
|
234
|
-
llm_response = ""
|
235
|
-
try:
|
236
|
-
from agents import Runner
|
237
|
-
response = await Runner.run(agent, query)
|
238
|
-
llm_response = getattr(response, 'final_output', str(response))
|
239
|
-
results = [llm_response.strip() or "(No response from LLM)"]
|
240
|
-
except Exception as e:
|
241
|
-
results = [f"[LLM ERROR] {e}"]
|
242
|
-
|
243
|
-
search_mode = kwargs.get('search_mode', 'semantic')
|
244
|
-
if search_mode in ("semantic", "code"):
|
245
|
-
from swarm.core.output_utils import print_search_progress_box
|
246
|
-
op_type = "Geese Semantic Search" if search_mode == "semantic" else "Geese Code Search"
|
247
|
-
emoji = "🔎" if search_mode == "semantic" else "🦢"
|
248
|
-
summary = f"Analyzed ({search_mode}) for: '{query}'"
|
249
|
-
params = {"instruction": query}
|
250
|
-
# Simulate progressive search with line numbers and results
|
251
|
-
for i in range(1, 6):
|
252
|
-
match_count = i * 13
|
253
|
-
print_search_progress_box(
|
254
|
-
op_type=op_type,
|
255
|
-
results=[f"Matches so far: {match_count}", f"geese.py:{26*i}", f"story.py:{39*i}"],
|
256
|
-
params=params,
|
257
|
-
result_type=search_mode,
|
258
|
-
summary=f"Searched codebase for '{query}' | Results: {match_count} | Params: {params}",
|
259
|
-
progress_line=f"Lines {i*120}",
|
260
|
-
spinner_state=f"Searching {'.' * i}",
|
261
|
-
operation_type=op_type,
|
262
|
-
search_mode=search_mode,
|
263
|
-
total_lines=600,
|
264
|
-
emoji=emoji,
|
265
|
-
border='╔'
|
266
|
-
)
|
267
|
-
await asyncio.sleep(0.05)
|
268
|
-
print_search_progress_box(
|
269
|
-
op_type=op_type,
|
270
|
-
results=[f"{search_mode.title()} search complete. Found 65 results for '{query}'.", "geese.py:130", "story.py:195"],
|
271
|
-
params=params,
|
272
|
-
result_type=search_mode,
|
273
|
-
summary=summary,
|
274
|
-
progress_line="Lines 600",
|
275
|
-
spinner_state="Search complete!",
|
276
|
-
operation_type=op_type,
|
277
|
-
search_mode=search_mode,
|
278
|
-
total_lines=600,
|
279
|
-
emoji=emoji,
|
280
|
-
border='╔'
|
281
|
-
)
|
282
|
-
yield {"messages": [{"role": "assistant", "content": f"{search_mode.title()} search complete. Found 65 results for '{query}'."}]}
|
283
|
-
return
|
284
|
-
# After LLM/agent run, show a creative output box with the main result
|
285
|
-
results = [llm_response]
|
286
|
-
print_search_progress_box(
|
287
|
-
op_type="Geese Creative",
|
288
|
-
results=results,
|
289
|
-
params=None,
|
290
|
-
result_type="creative",
|
291
|
-
summary=f"Creative generation complete for: '{query}'",
|
292
|
-
progress_line=None,
|
293
|
-
spinner_state=None,
|
294
|
-
operation_type="Geese Creative",
|
295
|
-
search_mode=None,
|
296
|
-
total_lines=None,
|
297
|
-
emoji='🦢',
|
298
|
-
border='╔'
|
299
|
-
)
|
300
|
-
yield {"messages": [{"role": "assistant", "content": results[0]}]}
|
301
|
-
return
|
302
|
-
|
303
|
-
def display_splash_screen(self, animated: bool = False):
|
304
|
-
console = Console()
|
305
|
-
splash = r'''
|
306
|
-
[bold magenta]
|
307
|
-
____ _ _ ____ _ _
|
308
|
-
/ ___| __ _ _ __ __ _| | ___| |__ / ___|| |_ __ _ _ __| |_ ___
|
309
|
-
| | _ / _` | '_ \ / _` | |/ _ \ '_ \ \___ \| __/ _` | '__| __/ _ \
|
310
|
-
| |_| | (_| | | | | (_| | | __/ | | | ___) | || (_| | | | || __/
|
311
|
-
\____|\__,_|_| |_|\__, |_|\___|_| |_|____/ \__\__,_|_| \__\___|
|
312
|
-
|___/
|
313
|
-
[/bold magenta]
|
314
|
-
[white]Collaborative Story Writing Blueprint[/white]
|
315
|
-
'''
|
316
|
-
panel = Panel(splash, title="[bold magenta]Geese Blueprint[/]", border_style="magenta", expand=False)
|
317
|
-
console.print(panel)
|
318
|
-
console.print() # Blank line for spacing
|
319
|
-
|
320
|
-
def create_starting_agent(self, mcp_servers: list[MCPServer]) -> Agent:
|
321
|
-
"""Returns the coordinator agent for GeeseBlueprint."""
|
322
|
-
# mcp_servers not used in this blueprint
|
323
|
-
return self.coordinator
|
324
|
-
|
325
|
-
def main():
|
326
|
-
import argparse
|
327
|
-
import asyncio
|
328
|
-
import sys
|
329
|
-
parser = argparse.ArgumentParser(description="Geese: Swarm-powered collaborative story writing agent (formerly Gaggle).")
|
330
|
-
parser.add_argument("prompt", nargs="?", help="Prompt or story topic (quoted)")
|
331
|
-
parser.add_argument("-i", "--input", help="Input file or directory", default=None)
|
332
|
-
parser.add_argument("-o", "--output", help="Output file", default=None)
|
333
|
-
parser.add_argument("--model", help="Model name (codex, gpt, etc.)", default=None)
|
334
|
-
parser.add_argument("--temperature", type=float, help="Sampling temperature", default=0.1)
|
335
|
-
parser.add_argument("--max-tokens", type=int, help="Max tokens", default=2048)
|
336
|
-
parser.add_argument("--mode", choices=["generate", "edit", "explain", "docstring"], default="generate", help="Operation mode")
|
337
|
-
parser.add_argument("--language", help="Programming language", default=None)
|
338
|
-
parser.add_argument("--stop", help="Stop sequence", default=None)
|
339
|
-
parser.add_argument("--interactive", action="store_true", help="Interactive mode")
|
340
|
-
parser.add_argument("--version", action="version", version="geese 1.0.0")
|
341
|
-
args = parser.parse_args()
|
342
|
-
|
343
|
-
# Print help if no prompt and no input
|
344
|
-
if not args.prompt and not args.input:
|
345
|
-
parser.print_help()
|
346
|
-
sys.exit(1)
|
347
|
-
|
348
|
-
blueprint = GeeseBlueprint(blueprint_id="cli")
|
349
|
-
messages = []
|
350
|
-
if args.prompt:
|
351
|
-
messages.append({"role": "user", "content": args.prompt})
|
352
|
-
if args.input:
|
353
|
-
try:
|
354
|
-
with open(args.input) as f:
|
355
|
-
file_content = f.read()
|
356
|
-
messages.append({"role": "user", "content": file_content})
|
357
|
-
except Exception as e:
|
358
|
-
print(f"Error reading input file: {e}")
|
359
|
-
sys.exit(1)
|
360
|
-
|
361
|
-
async def run_and_print():
|
362
|
-
result_lines = []
|
363
|
-
async for chunk in blueprint.run(messages):
|
364
|
-
if isinstance(chunk, dict) and 'content' in chunk:
|
365
|
-
print(chunk['content'], end="")
|
366
|
-
result_lines.append(chunk['content'])
|
367
|
-
else:
|
368
|
-
print(chunk, end="")
|
369
|
-
result_lines.append(str(chunk))
|
370
|
-
return ''.join(result_lines)
|
371
|
-
|
372
|
-
if args.output:
|
373
|
-
try:
|
374
|
-
output = asyncio.run(run_and_print())
|
375
|
-
with open(args.output, "w") as f:
|
376
|
-
f.write(output)
|
377
|
-
print(f"\nOutput written to {args.output}")
|
378
|
-
except Exception as e:
|
379
|
-
print(f"Error writing output file: {e}")
|
380
|
-
else:
|
381
|
-
asyncio.run(run_and_print())
|
382
|
-
|
383
|
-
if __name__ == "__main__":
|
384
|
-
main()
|
@@ -1,102 +0,0 @@
|
|
1
|
-
import argparse
|
2
|
-
from swarm.blueprints.geese.blueprint_geese import GeeseBlueprint
|
3
|
-
|
4
|
-
def main():
|
5
|
-
parser = argparse.ArgumentParser(description="Run the Geese Blueprint")
|
6
|
-
parser.add_argument('--message', dest='prompt', nargs='?', default=None, help='Prompt for the agent (optional, aliased as --message for compatibility)')
|
7
|
-
parser.add_argument('--config', type=str, help='Path to config file', default=None)
|
8
|
-
parser.add_argument('--agent-mcp', action='append', help='Agent to MCP assignment, e.g. --agent-mcp agent1:mcpA,mcpB')
|
9
|
-
parser.add_argument('--model', type=str, help='Model name (overrides DEFAULT_LLM envvar)', default=None)
|
10
|
-
args = parser.parse_args()
|
11
|
-
|
12
|
-
agent_mcp_assignments = None
|
13
|
-
if args.agent_mcp:
|
14
|
-
agent_mcp_assignments = {}
|
15
|
-
for assignment in args.agent_mcp:
|
16
|
-
agent, mcps = assignment.split(':', 1)
|
17
|
-
agent_mcp_assignments[agent] = [m.strip() for m in mcps.split(',')]
|
18
|
-
|
19
|
-
blueprint = GeeseBlueprint(
|
20
|
-
blueprint_id='geese',
|
21
|
-
config_path=args.config,
|
22
|
-
agent_mcp_assignments=agent_mcp_assignments
|
23
|
-
)
|
24
|
-
import asyncio
|
25
|
-
messages = []
|
26
|
-
if args.prompt:
|
27
|
-
# PATCH: Always use blueprint.run for progressive UX
|
28
|
-
messages = [{"role": "user", "content": args.prompt}]
|
29
|
-
import asyncio
|
30
|
-
from swarm.blueprints.geese.blueprint_geese import SPINNER_STATES, SLOW_SPINNER, display_operation_box
|
31
|
-
import time
|
32
|
-
async def run_and_print():
|
33
|
-
spinner_idx = 0
|
34
|
-
spinner_start = time.time()
|
35
|
-
async for chunk in blueprint.run(messages, model=args.model):
|
36
|
-
if isinstance(chunk, dict) and (chunk.get("progress") or chunk.get("matches") or chunk.get("spinner_state")):
|
37
|
-
elapsed = time.time() - spinner_start
|
38
|
-
spinner_state = chunk.get("spinner_state")
|
39
|
-
if not spinner_state:
|
40
|
-
spinner_state = SLOW_SPINNER if elapsed > 10 else SPINNER_STATES[spinner_idx % len(SPINNER_STATES)]
|
41
|
-
spinner_idx += 1
|
42
|
-
op_type = chunk.get("type", "search")
|
43
|
-
result_count = len(chunk.get("matches", [])) if chunk.get("matches") is not None else None
|
44
|
-
box_content = f"Matches so far: {result_count}" if result_count is not None else str(chunk)
|
45
|
-
display_operation_box(
|
46
|
-
title="Searching Filesystem" if chunk.get("progress") else "Geese Output",
|
47
|
-
content=box_content,
|
48
|
-
result_count=result_count,
|
49
|
-
params={k: v for k, v in chunk.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done', 'spinner_state'}},
|
50
|
-
progress_line=chunk.get('progress'),
|
51
|
-
total_lines=chunk.get('total'),
|
52
|
-
spinner_state=spinner_state,
|
53
|
-
emoji="🔍" if chunk.get("progress") else "💡"
|
54
|
-
)
|
55
|
-
else:
|
56
|
-
if isinstance(chunk, dict) and 'content' in chunk:
|
57
|
-
print(chunk['content'], end="")
|
58
|
-
else:
|
59
|
-
print(chunk, end="")
|
60
|
-
asyncio.run(run_and_print())
|
61
|
-
return
|
62
|
-
|
63
|
-
# Set DEFAULT_LLM envvar if --model is given
|
64
|
-
import os
|
65
|
-
if args.model:
|
66
|
-
os.environ['DEFAULT_LLM'] = args.model
|
67
|
-
|
68
|
-
async def run_and_print():
|
69
|
-
spinner_idx = 0
|
70
|
-
spinner_start = time.time()
|
71
|
-
from swarm.blueprints.geese.blueprint_geese import GeeseBlueprint, SPINNER_STATES, SLOW_SPINNER, display_operation_box
|
72
|
-
import time
|
73
|
-
async for chunk in blueprint.run(messages, model=args.model):
|
74
|
-
# If chunk is a dict with progress info, show operation box
|
75
|
-
if isinstance(chunk, dict) and (chunk.get("progress") or chunk.get("matches") or chunk.get("spinner_state")):
|
76
|
-
elapsed = time.time() - spinner_start
|
77
|
-
spinner_state = chunk.get("spinner_state")
|
78
|
-
if not spinner_state:
|
79
|
-
spinner_state = SLOW_SPINNER if elapsed > 10 else SPINNER_STATES[spinner_idx % len(SPINNER_STATES)]
|
80
|
-
spinner_idx += 1
|
81
|
-
op_type = chunk.get("type", "search")
|
82
|
-
result_count = len(chunk.get("matches", [])) if chunk.get("matches") is not None else None
|
83
|
-
box_content = f"Matches so far: {result_count}" if result_count is not None else str(chunk)
|
84
|
-
display_operation_box(
|
85
|
-
title="Searching Filesystem" if chunk.get("progress") else "Geese Output",
|
86
|
-
content=box_content,
|
87
|
-
result_count=result_count,
|
88
|
-
params={k: v for k, v in chunk.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done', 'spinner_state'}},
|
89
|
-
progress_line=chunk.get('progress'),
|
90
|
-
total_lines=chunk.get('total'),
|
91
|
-
spinner_state=spinner_state,
|
92
|
-
emoji="🔍" if chunk.get("progress") else "💡"
|
93
|
-
)
|
94
|
-
else:
|
95
|
-
if isinstance(chunk, dict) and 'content' in chunk:
|
96
|
-
print(chunk['content'], end="")
|
97
|
-
else:
|
98
|
-
print(chunk, end="")
|
99
|
-
asyncio.run(run_and_print())
|
100
|
-
|
101
|
-
if __name__ == "__main__":
|
102
|
-
main()
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# Jeeves Blueprint
|
2
|
-
|
3
|
-
**Jeeves** is a multi-agent home and web orchestration blueprint for Open Swarm, demonstrating multi-agent delegation for web search and home automation, robust fallback for LLM/agent errors, and unified ANSI/emoji UX with spinner feedback.
|
4
|
-
|
5
|
-
---
|
6
|
-
|
7
|
-
## What This Blueprint Demonstrates
|
8
|
-
- **Multi-agent delegation and orchestration** for web search and home automation
|
9
|
-
- **LLM fallback and error handling** with user-friendly messages
|
10
|
-
- **Unified ANSI/emoji boxes** for operation results, including summaries, counts, and parameters
|
11
|
-
- **Custom spinner messages**: 'Generating.', 'Generating..', 'Generating...', 'Running...'
|
12
|
-
- **Progress updates** for long-running operations (result counts, summaries)
|
13
|
-
- **Test mode** for robust, deterministic testing
|
14
|
-
|
15
|
-
## Usage
|
16
|
-
Run with the CLI:
|
17
|
-
```sh
|
18
|
-
swarm-cli run jeeves --instruction "Turn off the living room lights and search for pizza recipes."
|
19
|
-
```
|
20
|
-
|
21
|
-
## Test
|
22
|
-
```sh
|
23
|
-
uv run pytest -v tests/blueprints/test_jeeves.py
|
24
|
-
```
|
25
|
-
|
26
|
-
## Compliance
|
27
|
-
- Agentic:
|
28
|
-
- UX (ANSI/emoji):
|
29
|
-
- Spinner:
|
30
|
-
- Fallback:
|
31
|
-
- Test Coverage:
|
32
|
-
|
33
|
-
## Required Env Vars
|
34
|
-
- `SWARM_TEST_MODE` (optional): Enables test mode for deterministic output.
|
35
|
-
|
36
|
-
## Extending
|
37
|
-
- See `blueprint_jeeves.py` for agent logic and UX hooks.
|
38
|
-
- Extend agent capabilities or UX by modifying the `_run_non_interactive` and agent orchestration methods.
|
39
|
-
|
40
|
-
---
|
41
|
-
_Last updated: 2025-04-21_
|