open-swarm 0.1.1743070217__py3-none-any.whl → 0.1.1743362777__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.1743362777.dist-info/METADATA +217 -0
- open_swarm-0.1.1743362777.dist-info/RECORD +260 -0
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/WHEEL +1 -2
- open_swarm-0.1.1743362777.dist-info/entry_points.txt +2 -0
- swarm/__init__.py +0 -2
- swarm/auth.py +53 -49
- swarm/blueprints/README.md +67 -0
- swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +412 -0
- swarm/blueprints/chatbot/blueprint_chatbot.py +98 -0
- swarm/blueprints/chatbot/templates/chatbot/chatbot.html +33 -0
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +183 -0
- swarm/blueprints/dilbot_universe/blueprint_dilbot_universe.py +285 -0
- swarm/blueprints/divine_code/__init__.py +0 -0
- swarm/blueprints/divine_code/apps.py +11 -0
- swarm/blueprints/divine_code/blueprint_divine_code.py +219 -0
- swarm/blueprints/django_chat/apps.py +6 -0
- swarm/blueprints/django_chat/blueprint_django_chat.py +84 -0
- swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +37 -0
- swarm/blueprints/django_chat/urls.py +8 -0
- swarm/blueprints/django_chat/views.py +32 -0
- swarm/blueprints/echocraft/blueprint_echocraft.py +44 -0
- swarm/blueprints/family_ties/apps.py +11 -0
- swarm/blueprints/family_ties/blueprint_family_ties.py +152 -0
- swarm/blueprints/family_ties/models.py +19 -0
- swarm/blueprints/family_ties/serializers.py +7 -0
- swarm/blueprints/family_ties/settings.py +16 -0
- swarm/blueprints/family_ties/urls.py +10 -0
- swarm/blueprints/family_ties/views.py +26 -0
- swarm/blueprints/flock/__init__.py +0 -0
- swarm/blueprints/gaggle/blueprint_gaggle.py +184 -0
- swarm/blueprints/gotchaman/blueprint_gotchaman.py +232 -0
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +133 -0
- swarm/blueprints/messenger/templates/messenger/messenger.html +46 -0
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +234 -0
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +248 -0
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +156 -0
- swarm/blueprints/omniplex/blueprint_omniplex.py +221 -0
- swarm/blueprints/rue_code/__init__.py +0 -0
- swarm/blueprints/rue_code/blueprint_rue_code.py +291 -0
- swarm/blueprints/suggestion/blueprint_suggestion.py +110 -0
- swarm/blueprints/unapologetic_press/blueprint_unapologetic_press.py +298 -0
- swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
- swarm/blueprints/whiskeytango_foxtrot/apps.py +11 -0
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +256 -0
- swarm/extensions/blueprint/__init__.py +30 -15
- swarm/extensions/blueprint/agent_utils.py +16 -40
- swarm/extensions/blueprint/blueprint_base.py +141 -543
- swarm/extensions/blueprint/blueprint_discovery.py +112 -98
- swarm/extensions/blueprint/cli_handler.py +185 -0
- swarm/extensions/blueprint/config_loader.py +122 -0
- swarm/extensions/blueprint/django_utils.py +181 -79
- swarm/extensions/blueprint/interactive_mode.py +1 -1
- swarm/extensions/config/config_loader.py +83 -200
- swarm/extensions/launchers/build_swarm_wrapper.py +0 -0
- swarm/extensions/launchers/swarm_cli.py +199 -287
- swarm/llm/chat_completion.py +26 -55
- swarm/management/__init__.py +0 -0
- swarm/management/commands/__init__.py +0 -0
- swarm/management/commands/runserver.py +58 -0
- swarm/permissions.py +38 -0
- swarm/serializers.py +96 -5
- swarm/settings.py +95 -110
- swarm/static/contrib/fonts/fontawesome-webfont.ttf +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff2 +7 -0
- swarm/static/contrib/markedjs/marked.min.js +6 -0
- swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +27 -0
- swarm/static/contrib/tabler-icons/alert-triangle.svg +21 -0
- swarm/static/contrib/tabler-icons/archive.svg +21 -0
- swarm/static/contrib/tabler-icons/artboard.svg +27 -0
- swarm/static/contrib/tabler-icons/automatic-gearbox.svg +23 -0
- swarm/static/contrib/tabler-icons/box-multiple.svg +19 -0
- swarm/static/contrib/tabler-icons/carambola.svg +19 -0
- swarm/static/contrib/tabler-icons/copy.svg +20 -0
- swarm/static/contrib/tabler-icons/download.svg +21 -0
- swarm/static/contrib/tabler-icons/edit.svg +21 -0
- swarm/static/contrib/tabler-icons/filled/carambola.svg +13 -0
- swarm/static/contrib/tabler-icons/filled/paint.svg +13 -0
- swarm/static/contrib/tabler-icons/headset.svg +22 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/message-chatbot.svg +22 -0
- swarm/static/contrib/tabler-icons/message-star.svg +22 -0
- swarm/static/contrib/tabler-icons/message-x.svg +23 -0
- swarm/static/contrib/tabler-icons/message.svg +21 -0
- swarm/static/contrib/tabler-icons/paperclip.svg +18 -0
- swarm/static/contrib/tabler-icons/playlist-add.svg +22 -0
- swarm/static/contrib/tabler-icons/robot.svg +26 -0
- swarm/static/contrib/tabler-icons/search.svg +19 -0
- swarm/static/contrib/tabler-icons/settings.svg +20 -0
- swarm/static/contrib/tabler-icons/thumb-down.svg +19 -0
- swarm/static/contrib/tabler-icons/thumb-up.svg +19 -0
- swarm/static/css/dropdown.css +22 -0
- swarm/static/htmx/htmx.min.js +0 -0
- swarm/static/js/dropdown.js +23 -0
- swarm/static/rest_mode/css/base.css +470 -0
- swarm/static/rest_mode/css/chat-history.css +286 -0
- swarm/static/rest_mode/css/chat.css +251 -0
- swarm/static/rest_mode/css/chatbot.css +74 -0
- swarm/static/rest_mode/css/chatgpt.css +62 -0
- swarm/static/rest_mode/css/colors/corporate.css +74 -0
- swarm/static/rest_mode/css/colors/pastel.css +81 -0
- swarm/static/rest_mode/css/colors/tropical.css +82 -0
- swarm/static/rest_mode/css/general.css +142 -0
- swarm/static/rest_mode/css/layout.css +167 -0
- swarm/static/rest_mode/css/layouts/messenger-layout.css +17 -0
- swarm/static/rest_mode/css/layouts/minimalist-layout.css +57 -0
- swarm/static/rest_mode/css/layouts/mobile-layout.css +8 -0
- swarm/static/rest_mode/css/messages.css +84 -0
- swarm/static/rest_mode/css/messenger.css +135 -0
- swarm/static/rest_mode/css/settings.css +91 -0
- swarm/static/rest_mode/css/simple.css +44 -0
- swarm/static/rest_mode/css/slack.css +58 -0
- swarm/static/rest_mode/css/style.css +156 -0
- swarm/static/rest_mode/css/theme.css +30 -0
- swarm/static/rest_mode/css/toast.css +40 -0
- swarm/static/rest_mode/js/auth.js +9 -0
- swarm/static/rest_mode/js/blueprint.js +41 -0
- swarm/static/rest_mode/js/blueprintUtils.js +12 -0
- swarm/static/rest_mode/js/chatLogic.js +79 -0
- swarm/static/rest_mode/js/debug.js +63 -0
- swarm/static/rest_mode/js/events.js +98 -0
- swarm/static/rest_mode/js/main.js +19 -0
- swarm/static/rest_mode/js/messages.js +264 -0
- swarm/static/rest_mode/js/messengerLogic.js +355 -0
- swarm/static/rest_mode/js/modules/apiService.js +84 -0
- swarm/static/rest_mode/js/modules/blueprintManager.js +162 -0
- swarm/static/rest_mode/js/modules/chatHistory.js +110 -0
- swarm/static/rest_mode/js/modules/debugLogger.js +14 -0
- swarm/static/rest_mode/js/modules/eventHandlers.js +107 -0
- swarm/static/rest_mode/js/modules/messageProcessor.js +120 -0
- swarm/static/rest_mode/js/modules/state.js +7 -0
- swarm/static/rest_mode/js/modules/userInteractions.js +29 -0
- swarm/static/rest_mode/js/modules/validation.js +23 -0
- swarm/static/rest_mode/js/rendering.js +119 -0
- swarm/static/rest_mode/js/settings.js +130 -0
- swarm/static/rest_mode/js/sidebar.js +94 -0
- swarm/static/rest_mode/js/simpleLogic.js +37 -0
- swarm/static/rest_mode/js/slackLogic.js +66 -0
- swarm/static/rest_mode/js/splash.js +76 -0
- swarm/static/rest_mode/js/theme.js +111 -0
- swarm/static/rest_mode/js/toast.js +36 -0
- swarm/static/rest_mode/js/ui.js +265 -0
- swarm/static/rest_mode/js/validation.js +57 -0
- swarm/static/rest_mode/svg/animated_spinner.svg +12 -0
- swarm/static/rest_mode/svg/arrow_down.svg +5 -0
- swarm/static/rest_mode/svg/arrow_left.svg +5 -0
- swarm/static/rest_mode/svg/arrow_right.svg +5 -0
- swarm/static/rest_mode/svg/arrow_up.svg +5 -0
- swarm/static/rest_mode/svg/attach.svg +8 -0
- swarm/static/rest_mode/svg/avatar.svg +7 -0
- swarm/static/rest_mode/svg/canvas.svg +6 -0
- swarm/static/rest_mode/svg/chat_history.svg +4 -0
- swarm/static/rest_mode/svg/close.svg +5 -0
- swarm/static/rest_mode/svg/copy.svg +4 -0
- swarm/static/rest_mode/svg/dark_mode.svg +3 -0
- swarm/static/rest_mode/svg/edit.svg +5 -0
- swarm/static/rest_mode/svg/layout.svg +9 -0
- swarm/static/rest_mode/svg/logo.svg +29 -0
- swarm/static/rest_mode/svg/logout.svg +5 -0
- swarm/static/rest_mode/svg/mobile.svg +5 -0
- swarm/static/rest_mode/svg/new_chat.svg +4 -0
- swarm/static/rest_mode/svg/not_visible.svg +5 -0
- swarm/static/rest_mode/svg/plus.svg +7 -0
- swarm/static/rest_mode/svg/run_code.svg +6 -0
- swarm/static/rest_mode/svg/save.svg +4 -0
- swarm/static/rest_mode/svg/search.svg +6 -0
- swarm/static/rest_mode/svg/settings.svg +4 -0
- swarm/static/rest_mode/svg/speaker.svg +5 -0
- swarm/static/rest_mode/svg/stop.svg +6 -0
- swarm/static/rest_mode/svg/thumbs_down.svg +3 -0
- swarm/static/rest_mode/svg/thumbs_up.svg +3 -0
- swarm/static/rest_mode/svg/toggle_off.svg +6 -0
- swarm/static/rest_mode/svg/toggle_on.svg +6 -0
- swarm/static/rest_mode/svg/trash.svg +10 -0
- swarm/static/rest_mode/svg/undo.svg +3 -0
- swarm/static/rest_mode/svg/visible.svg +8 -0
- swarm/static/rest_mode/svg/voice.svg +10 -0
- swarm/templates/account/login.html +22 -0
- swarm/templates/account/signup.html +32 -0
- swarm/templates/base.html +30 -0
- swarm/templates/chat.html +43 -0
- swarm/templates/index.html +35 -0
- swarm/templates/rest_mode/components/chat_sidebar.html +55 -0
- swarm/templates/rest_mode/components/header.html +45 -0
- swarm/templates/rest_mode/components/main_chat_pane.html +41 -0
- swarm/templates/rest_mode/components/settings_dialog.html +97 -0
- swarm/templates/rest_mode/components/splash_screen.html +7 -0
- swarm/templates/rest_mode/components/top_bar.html +28 -0
- swarm/templates/rest_mode/message_ui.html +50 -0
- swarm/templates/rest_mode/slackbot.html +30 -0
- swarm/templates/simple_blueprint_page.html +24 -0
- swarm/templates/websocket_partials/final_system_message.html +3 -0
- swarm/templates/websocket_partials/system_message.html +4 -0
- swarm/templates/websocket_partials/user_message.html +5 -0
- swarm/urls.py +57 -74
- swarm/utils/log_utils.py +63 -0
- swarm/views/api_views.py +48 -39
- swarm/views/chat_views.py +156 -70
- swarm/views/core_views.py +85 -90
- swarm/views/model_views.py +64 -121
- swarm/views/utils.py +65 -441
- open_swarm-0.1.1743070217.dist-info/METADATA +0 -258
- open_swarm-0.1.1743070217.dist-info/RECORD +0 -89
- open_swarm-0.1.1743070217.dist-info/entry_points.txt +0 -3
- open_swarm-0.1.1743070217.dist-info/top_level.txt +0 -1
- swarm/agent/agent.py +0 -49
- swarm/core.py +0 -326
- swarm/extensions/mcp/__init__.py +0 -1
- swarm/extensions/mcp/cache_utils.py +0 -36
- swarm/extensions/mcp/mcp_client.py +0 -341
- swarm/extensions/mcp/mcp_constants.py +0 -7
- swarm/extensions/mcp/mcp_tool_provider.py +0 -110
- swarm/types.py +0 -126
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743362777.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,219 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
from typing import Dict, Any, List, ClassVar, Optional
|
5
|
+
|
6
|
+
# Ensure src is in path for BlueprintBase import
|
7
|
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
8
|
+
src_path = os.path.join(project_root, 'src')
|
9
|
+
if src_path not in sys.path: sys.path.insert(0, src_path)
|
10
|
+
|
11
|
+
try:
|
12
|
+
from agents import Agent, Tool, function_tool, Runner
|
13
|
+
from agents.mcp import MCPServer
|
14
|
+
from agents.models.interface import Model
|
15
|
+
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
16
|
+
from openai import AsyncOpenAI
|
17
|
+
from swarm.extensions.blueprint.blueprint_base import BlueprintBase
|
18
|
+
except ImportError as e:
|
19
|
+
print(f"ERROR: Import failed in DivineOpsBlueprint: {e}. Check 'openai-agents' install and project structure.")
|
20
|
+
print(f"sys.path: {sys.path}")
|
21
|
+
sys.exit(1)
|
22
|
+
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
# --- Agent Instructions ---
|
26
|
+
# Refined for clarity on tool usage (MCP vs Agent-as-Tool)
|
27
|
+
|
28
|
+
zeus_instructions = """
|
29
|
+
You are Zeus, Product Owner and Coordinator of the Divine Ops team.
|
30
|
+
Your goal is to manage the software development lifecycle based on user requests.
|
31
|
+
1. Understand the user's request (e.g., "design a user login system", "deploy the latest changes", "fix bug X").
|
32
|
+
2. Delegate tasks to the appropriate specialist agent using their respective Agent Tool:
|
33
|
+
- Odin: For high-level architecture, design, research.
|
34
|
+
- Hermes: For breaking down features into technical tasks, system checks.
|
35
|
+
- Hephaestus: For primary coding and implementation.
|
36
|
+
- Hecate: For specific coding assistance requested by Hephaestus (via you).
|
37
|
+
- Thoth: For database schema/data changes, code updates related to DB.
|
38
|
+
- Mnemosyne: For DevOps, deployment, infrastructure tasks.
|
39
|
+
- Chronos: For writing documentation.
|
40
|
+
3. Provide clear context and requirements when delegating.
|
41
|
+
4. Synthesize the results and progress reports from your team.
|
42
|
+
5. Provide the final update or result to the user.
|
43
|
+
Available Agent Tools: Odin, Hermes, Hephaestus, Hecate, Thoth, Mnemosyne, Chronos.
|
44
|
+
"""
|
45
|
+
|
46
|
+
odin_instructions = """
|
47
|
+
You are Odin, Software Architect. Your task is to design scalable and robust systems based on requirements provided by Zeus.
|
48
|
+
- Analyze the requirements carefully.
|
49
|
+
- Use the `brave-search` MCP tool to research technologies, patterns, or existing solutions if needed and explicitly available. Otherwise, rely on your internal knowledge.
|
50
|
+
- Produce detailed technical specifications, diagrams (descriptively), or design documents.
|
51
|
+
- Report your design back to Zeus. Do not delegate tasks.
|
52
|
+
Available MCP Tools (if provided): brave-search.
|
53
|
+
"""
|
54
|
+
|
55
|
+
hermes_instructions = """
|
56
|
+
You are Hermes, the Tech Lead. Your tasks involve planning and system interaction based on architecture specs received from Zeus.
|
57
|
+
- Receive architecture specifications or feature requests.
|
58
|
+
- Break down features into specific, actionable technical tasks suitable for Hephaestus, Hecate, or Thoth.
|
59
|
+
- Use the `mcp-shell` MCP tool for necessary system checks (e.g., check tool versions, list files briefly) or simple setup commands *if required and available*. Be cautious with shell commands.
|
60
|
+
- Clearly define the tasks and report the breakdown back to Zeus for delegation. Do not delegate directly.
|
61
|
+
Available MCP Tools (if provided): mcp-shell.
|
62
|
+
"""
|
63
|
+
|
64
|
+
hephaestus_instructions = """
|
65
|
+
You are Hephaestus, Full Stack Implementer. You write the core code based on tasks assigned by Zeus (originating from Hermes).
|
66
|
+
- Receive specific coding tasks.
|
67
|
+
- Use the `filesystem` MCP tool to read existing code, write new code, or modify files as required for your task.
|
68
|
+
- If you need assistance on a specific sub-part, report back to Zeus requesting Hecate's help.
|
69
|
+
- Report code completion, issues, or the need for Hecate's help back to Zeus.
|
70
|
+
Available MCP Tools (if provided): filesystem.
|
71
|
+
"""
|
72
|
+
|
73
|
+
hecate_instructions = """
|
74
|
+
You are Hecate, Code Assistant. You assist Hephaestus with specific, well-defined coding sub-tasks when requested by Zeus.
|
75
|
+
- Receive a very specific coding task (e.g., "write a function to validate email format", "refactor this specific loop").
|
76
|
+
- Use the `filesystem` MCP tool to read relevant code snippets and write the required code.
|
77
|
+
- Report the completed code snippet or function back to Zeus.
|
78
|
+
Available MCP Tools (if provided): filesystem.
|
79
|
+
"""
|
80
|
+
|
81
|
+
thoth_instructions = """
|
82
|
+
You are Thoth, Code Updater & DB Manager. You handle tasks related to database changes and code updates associated with them, assigned by Zeus.
|
83
|
+
- Receive tasks like "update the user schema", "add an index to the orders table", "apply database migrations".
|
84
|
+
- Use the `sqlite` MCP tool to execute necessary SQL commands or interact with the database.
|
85
|
+
- Use the `filesystem` MCP tool if needed to update code related to database interactions (e.g., ORM models).
|
86
|
+
- Report task completion status or any errors back to Zeus.
|
87
|
+
Available MCP Tools (if provided): sqlite, filesystem.
|
88
|
+
"""
|
89
|
+
|
90
|
+
mnemosyne_instructions = """
|
91
|
+
You are Mnemosyne, DevOps Engineer. You handle deployment, infrastructure configuration, and CI/CD tasks assigned by Zeus.
|
92
|
+
- Receive tasks like "deploy version 1.2 to production", "set up staging environment", "configure CI pipeline".
|
93
|
+
- Use the `mcp-shell` MCP tool (if available) for deployment scripts, server commands, or infrastructure setup.
|
94
|
+
- Use the `memory` MCP tool (if available) to potentially store/retrieve deployment status or simple configuration details if instructed.
|
95
|
+
- Report deployment success, failures, or infrastructure status back to Zeus.
|
96
|
+
Available MCP Tools (if provided): mcp-shell, memory.
|
97
|
+
"""
|
98
|
+
|
99
|
+
chronos_instructions = """
|
100
|
+
You are Chronos, Technical Writer. You create documentation based on requests from Zeus.
|
101
|
+
- Receive requests like "document the new API endpoint", "write user guide for feature X".
|
102
|
+
- Use the `sequential-thinking` MCP tool (if available) to help structure complex documentation logically.
|
103
|
+
- Use the `filesystem` MCP tool (if available) to write documentation files (e.g., Markdown).
|
104
|
+
- Report the completed documentation or its location back to Zeus.
|
105
|
+
Available MCP Tools (if provided): sequential-thinking, filesystem.
|
106
|
+
"""
|
107
|
+
|
108
|
+
# --- Define the Blueprint ---
|
109
|
+
class DivineOpsBlueprint(BlueprintBase):
|
110
|
+
""" Divine Ops: Streamlined Software Dev & Sysadmin Team Blueprint using openai-agents """
|
111
|
+
metadata: ClassVar[Dict[str, Any]] = {
|
112
|
+
"name": "DivineOpsBlueprint",
|
113
|
+
"title": "Divine Ops: Streamlined Software Dev & Sysadmin Team",
|
114
|
+
"description": "Zeus leads a pantheon for software dev & sysadmin tasks, coordinating via agent-as-tool delegation.",
|
115
|
+
"version": "1.1.0", # Refactored version
|
116
|
+
"author": "Open Swarm Team (Refactored)",
|
117
|
+
"tags": ["software development", "sysadmin", "devops", "multi-agent", "collaboration", "delegation"],
|
118
|
+
"required_mcp_servers": [ # List ALL servers ANY agent might potentially use
|
119
|
+
"memory",
|
120
|
+
"filesystem",
|
121
|
+
"mcp-shell",
|
122
|
+
"sqlite",
|
123
|
+
"sequential-thinking",
|
124
|
+
"brave-search", # Odin might use this
|
125
|
+
],
|
126
|
+
"env_vars": [ # Vars needed by MCP servers or tools directly
|
127
|
+
"ALLOWED_PATH", # Often needed for filesystem server
|
128
|
+
"SQLITE_DB_PATH", # For sqlite server
|
129
|
+
"BRAVE_API_KEY" # For brave search server
|
130
|
+
]
|
131
|
+
}
|
132
|
+
|
133
|
+
# Caches
|
134
|
+
_openai_client_cache: Dict[str, AsyncOpenAI] = {}
|
135
|
+
_model_instance_cache: Dict[str, Model] = {}
|
136
|
+
|
137
|
+
# --- Model Instantiation Helper --- (Standard helper)
|
138
|
+
def _get_model_instance(self, profile_name: str) -> Model:
|
139
|
+
"""Retrieves or creates an LLM Model instance."""
|
140
|
+
# ... (Implementation is the same as in previous refactors) ...
|
141
|
+
if profile_name in self._model_instance_cache:
|
142
|
+
logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
|
143
|
+
return self._model_instance_cache[profile_name]
|
144
|
+
logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
|
145
|
+
profile_data = self.get_llm_profile(profile_name)
|
146
|
+
if not profile_data:
|
147
|
+
logger.critical(f"LLM profile '{profile_name}' (or 'default') not found.")
|
148
|
+
raise ValueError(f"Missing LLM profile configuration for '{profile_name}' or 'default'.")
|
149
|
+
provider = profile_data.get("provider", "openai").lower()
|
150
|
+
model_name = profile_data.get("model")
|
151
|
+
if not model_name:
|
152
|
+
logger.critical(f"LLM profile '{profile_name}' missing 'model' key.")
|
153
|
+
raise ValueError(f"Missing 'model' key in LLM profile '{profile_name}'.")
|
154
|
+
if provider != "openai":
|
155
|
+
logger.error(f"Unsupported LLM provider '{provider}'.")
|
156
|
+
raise ValueError(f"Unsupported LLM provider: {provider}")
|
157
|
+
client_cache_key = f"{provider}_{profile_data.get('base_url')}"
|
158
|
+
if client_cache_key not in self._openai_client_cache:
|
159
|
+
client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
|
160
|
+
filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
161
|
+
log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
|
162
|
+
logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
|
163
|
+
try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
|
164
|
+
except Exception as e: raise ValueError(f"Failed to init OpenAI client: {e}") from e
|
165
|
+
client = self._openai_client_cache[client_cache_key]
|
166
|
+
logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
|
167
|
+
try:
|
168
|
+
model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
|
169
|
+
self._model_instance_cache[profile_name] = model_instance
|
170
|
+
return model_instance
|
171
|
+
except Exception as e: raise ValueError(f"Failed to init LLM provider: {e}") from e
|
172
|
+
|
173
|
+
# --- Agent Creation ---
|
174
|
+
def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
|
175
|
+
"""Creates the Divine Ops agent team and returns Zeus (Coordinator)."""
|
176
|
+
logger.debug("Creating Divine Ops agent team...")
|
177
|
+
self._model_instance_cache = {}
|
178
|
+
self._openai_client_cache = {}
|
179
|
+
|
180
|
+
default_profile_name = self.config.get("llm_profile", "default")
|
181
|
+
logger.debug(f"Using LLM profile '{default_profile_name}' for Divine Ops agents.")
|
182
|
+
model_instance = self._get_model_instance(default_profile_name)
|
183
|
+
|
184
|
+
# Helper to filter MCP servers for specific agents
|
185
|
+
def get_agent_mcps(names: List[str]) -> List[MCPServer]:
|
186
|
+
return [s for s in mcp_servers if s.name in names]
|
187
|
+
|
188
|
+
# Instantiate specialist agents, passing only the relevant MCP servers
|
189
|
+
odin_agent = Agent(name="Odin", model=model_instance, instructions=odin_instructions, tools=[], mcp_servers=get_agent_mcps(["brave-search"]))
|
190
|
+
hermes_agent = Agent(name="Hermes", model=model_instance, instructions=hermes_instructions, tools=[], mcp_servers=get_agent_mcps(["mcp-shell"]))
|
191
|
+
hephaestus_agent = Agent(name="Hephaestus", model=model_instance, instructions=hephaestus_instructions, tools=[], mcp_servers=get_agent_mcps(["filesystem"]))
|
192
|
+
hecate_agent = Agent(name="Hecate", model=model_instance, instructions=hecate_instructions, tools=[], mcp_servers=get_agent_mcps(["filesystem"]))
|
193
|
+
thoth_agent = Agent(name="Thoth", model=model_instance, instructions=thoth_instructions, tools=[], mcp_servers=get_agent_mcps(["sqlite", "filesystem"]))
|
194
|
+
mnemosyne_agent = Agent(name="Mnemosyne", model=model_instance, instructions=mnemosyne_instructions, tools=[], mcp_servers=get_agent_mcps(["mcp-shell", "memory"]))
|
195
|
+
chronos_agent = Agent(name="Chronos", model=model_instance, instructions=chronos_instructions, tools=[], mcp_servers=get_agent_mcps(["sequential-thinking", "filesystem"]))
|
196
|
+
|
197
|
+
# Instantiate Zeus (Coordinator), giving it the other agents as tools
|
198
|
+
zeus_agent = Agent(
|
199
|
+
name="Zeus",
|
200
|
+
model=model_instance, # Coordinator also needs a model
|
201
|
+
instructions=zeus_instructions,
|
202
|
+
tools=[
|
203
|
+
odin_agent.as_tool(tool_name="Odin", tool_description="Delegate architecture design or research tasks."),
|
204
|
+
hermes_agent.as_tool(tool_name="Hermes", tool_description="Delegate task breakdown or system setup/checks."),
|
205
|
+
hephaestus_agent.as_tool(tool_name="Hephaestus", tool_description="Delegate core coding implementation tasks."),
|
206
|
+
hecate_agent.as_tool(tool_name="Hecate", tool_description="Delegate specific, smaller coding tasks (usually requested by Hephaestus)."),
|
207
|
+
thoth_agent.as_tool(tool_name="Thoth", tool_description="Delegate database updates or code management tasks."),
|
208
|
+
mnemosyne_agent.as_tool(tool_name="Mnemosyne", tool_description="Delegate DevOps, deployment, or workflow optimization tasks."),
|
209
|
+
chronos_agent.as_tool(tool_name="Chronos", tool_description="Delegate documentation writing tasks.")
|
210
|
+
],
|
211
|
+
mcp_servers=mcp_servers # Zeus might need access to all MCPs if it were to use them directly, though unlikely in this design
|
212
|
+
)
|
213
|
+
|
214
|
+
logger.debug("Divine Ops Team (Zeus & Pantheon) created successfully. Zeus is starting agent.")
|
215
|
+
return zeus_agent
|
216
|
+
|
217
|
+
# Standard Python entry point
|
218
|
+
if __name__ == "__main__":
|
219
|
+
DivineOpsBlueprint.main()
|
@@ -0,0 +1,84 @@
|
|
1
|
+
"""
|
2
|
+
Django Chat Blueprint
|
3
|
+
|
4
|
+
A blueprint providing a web-based chat interface with conversation history management.
|
5
|
+
HTTP-only; not intended for CLI use.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import sys
|
10
|
+
import os
|
11
|
+
from typing import Dict, Any, List
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
logger.setLevel(logging.DEBUG)
|
15
|
+
handler = logging.StreamHandler(sys.stderr)
|
16
|
+
handler.setFormatter(logging.Formatter("[%(asctime)s] [%(levelname)s] %(name)s:%(lineno)d - %(message)s"))
|
17
|
+
logger.addHandler(handler)
|
18
|
+
|
19
|
+
# Reject CLI execution immediately
|
20
|
+
if __name__ == "__main__":
|
21
|
+
logger.info("DjangoChatBlueprint is an HTTP-only service. Access it via the web interface at /django_chat/.")
|
22
|
+
print("This blueprint is designed for HTTP use only. Please access it via the web server at /django_chat/", file=sys.stderr)
|
23
|
+
sys.stderr.flush()
|
24
|
+
sys.exit(1)
|
25
|
+
|
26
|
+
# Django imports after CLI rejection
|
27
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "swarm.settings")
|
28
|
+
import django
|
29
|
+
django.setup()
|
30
|
+
|
31
|
+
from django.shortcuts import render
|
32
|
+
from django.contrib.auth.decorators import login_required
|
33
|
+
from django.views.decorators.csrf import csrf_exempt
|
34
|
+
from django.contrib.auth.models import User
|
35
|
+
from swarm.models import ChatConversation, ChatMessage
|
36
|
+
from swarm.extensions.blueprint.blueprint_base import BlueprintBase as Blueprint
|
37
|
+
from swarm.utils.logger_setup import setup_logger
|
38
|
+
|
39
|
+
logger = setup_logger(__name__)
|
40
|
+
|
41
|
+
class DjangoChatBlueprint(Blueprint):
|
42
|
+
@property
|
43
|
+
def metadata(self) -> Dict[str, Any]:
|
44
|
+
logger.debug("Fetching metadata")
|
45
|
+
return {
|
46
|
+
"title": "Django Chat Interface",
|
47
|
+
"description": "A web-based chat interface with conversation history management. HTTP-only.",
|
48
|
+
"cli_name": "django_chat",
|
49
|
+
"env_vars": [],
|
50
|
+
"urls_module": "blueprints.django_chat.urls",
|
51
|
+
"url_prefix": "django_chat/"
|
52
|
+
}
|
53
|
+
|
54
|
+
def get_or_create_default_user(self):
|
55
|
+
"""Create or retrieve a default 'testuser' for development purposes."""
|
56
|
+
username = "testuser"
|
57
|
+
try:
|
58
|
+
user = User.objects.get(username=username)
|
59
|
+
except User.DoesNotExist:
|
60
|
+
user = User.objects.create_user(username=username, password="testpass")
|
61
|
+
logger.info(f"Created default user: {username}")
|
62
|
+
return user
|
63
|
+
|
64
|
+
@csrf_exempt
|
65
|
+
@login_required
|
66
|
+
def django_chat(self, request):
|
67
|
+
"""Render the django_chat UI with user-specific conversation history."""
|
68
|
+
logger.debug("Rendering django_chat web UI")
|
69
|
+
user = request.user if request.user.is_authenticated else self.get_or_create_default_user()
|
70
|
+
conversations = ChatConversation.objects.filter(student=user).order_by('-created_at')
|
71
|
+
context = {
|
72
|
+
"dark_mode": request.session.get('dark_mode', True),
|
73
|
+
"is_chatbot": False,
|
74
|
+
"conversations": conversations
|
75
|
+
}
|
76
|
+
return render(request, "django_chat/django_chat_webpage.html", context)
|
77
|
+
|
78
|
+
def run_with_context(self, messages: List[Dict[str, str]], context_variables: dict) -> dict:
|
79
|
+
"""Minimal implementation for CLI compatibility without agents."""
|
80
|
+
logger.debug("Running with context (UI-focused implementation)")
|
81
|
+
return {
|
82
|
+
"response": {"messages": [{"role": "assistant", "content": "Django Chat UI active via web interface at /django_chat/"}]},
|
83
|
+
"context_variables": context_variables
|
84
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<div x-data="{ open: false, selectedModel: '', models: [] }" class="relative w-72 mx-auto mt-5">
|
2
|
+
<!-- Dropdown Trigger -->
|
3
|
+
<button
|
4
|
+
@click="open = !open"
|
5
|
+
class="w-full px-4 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm focus:ring focus:ring-blue-500"
|
6
|
+
>
|
7
|
+
<span x-text="selectedModel ? selectedModel : 'Select a Model'"></span>
|
8
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 inline float-right" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
9
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
10
|
+
</svg>
|
11
|
+
</button>
|
12
|
+
|
13
|
+
<!-- Dropdown Options -->
|
14
|
+
<div
|
15
|
+
x-show="open"
|
16
|
+
@click.outside="open = false"
|
17
|
+
class="absolute z-10 w-full mt-2 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-auto"
|
18
|
+
>
|
19
|
+
<!-- Fetch models with HTMX -->
|
20
|
+
<div hx-get="/v1/models" hx-trigger="load" hx-target="#model-list" hx-swap="innerHTML">
|
21
|
+
<p class="px-4 py-2 text-gray-500">Loading models...</p>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<!-- Render Models -->
|
25
|
+
<div id="model-list">
|
26
|
+
<template x-for="model in models" :key="model.id">
|
27
|
+
<div
|
28
|
+
@click="selectedModel = model.name; open = false"
|
29
|
+
class="px-4 py-2 cursor-pointer hover:bg-blue-100"
|
30
|
+
>
|
31
|
+
<p class="text-sm font-medium" x-text="model.name"></p>
|
32
|
+
<p class="text-xs text-gray-500" x-text="model.description"></p>
|
33
|
+
</div>
|
34
|
+
</template>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
from django.shortcuts import render
|
2
|
+
from django.contrib.auth.decorators import login_required
|
3
|
+
from django.views.decorators.csrf import csrf_exempt
|
4
|
+
from django.contrib.auth.models import User
|
5
|
+
from swarm.models import ChatConversation, ChatMessage
|
6
|
+
from swarm.utils.logger_setup import setup_logger
|
7
|
+
|
8
|
+
logger = setup_logger(__name__)
|
9
|
+
|
10
|
+
def get_or_create_default_user():
|
11
|
+
"""Create or retrieve a default 'testuser' for development purposes."""
|
12
|
+
username = "testuser"
|
13
|
+
try:
|
14
|
+
user = User.objects.get(username=username)
|
15
|
+
except User.DoesNotExist:
|
16
|
+
user = User.objects.create_user(username=username, password="testpass")
|
17
|
+
logger.info(f"Created default user: {username}")
|
18
|
+
return user
|
19
|
+
|
20
|
+
@csrf_exempt
|
21
|
+
@login_required
|
22
|
+
def django_chat(request):
|
23
|
+
"""Render the django_chat UI with user-specific conversation history."""
|
24
|
+
logger.debug("Rendering django_chat web UI")
|
25
|
+
user = request.user if request.user.is_authenticated else get_or_create_default_user()
|
26
|
+
conversations = ChatConversation.objects.filter(student=user).order_by('-created_at')
|
27
|
+
context = {
|
28
|
+
"dark_mode": request.session.get('dark_mode', True),
|
29
|
+
"is_chatbot": False,
|
30
|
+
"conversations": conversations
|
31
|
+
}
|
32
|
+
return render(request, "django_chat/django_chat_webpage.html", context)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Dict, List, Any, AsyncGenerator
|
3
|
+
# Correct import path for BlueprintBase
|
4
|
+
from swarm.extensions.blueprint.blueprint_base import BlueprintBase
|
5
|
+
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
class EchoCraftBlueprint(BlueprintBase):
|
9
|
+
"""
|
10
|
+
A simple blueprint that echoes the last user message.
|
11
|
+
"""
|
12
|
+
metadata = {
|
13
|
+
"name": "EchoCraft",
|
14
|
+
"description": "Echoes the last user message.",
|
15
|
+
"author": "SwarmTeam",
|
16
|
+
"version": "1.0",
|
17
|
+
# Example: Specify a default LLM profile if desired
|
18
|
+
# "llm_profile": "default"
|
19
|
+
}
|
20
|
+
|
21
|
+
# *** Make run async and use yield ***
|
22
|
+
async def run(self, messages: List[Dict[str, str]]) -> AsyncGenerator[Dict[str, Any], None]:
|
23
|
+
"""
|
24
|
+
Finds the last user message and yields it back with an 'Echo: ' prefix.
|
25
|
+
"""
|
26
|
+
logger.info(f"EchoCraftBlueprint run called with {len(messages)} messages.")
|
27
|
+
last_user_message = "No user message found."
|
28
|
+
for msg in reversed(messages):
|
29
|
+
if msg.get("role") == "user":
|
30
|
+
last_user_message = msg.get("content", "")
|
31
|
+
logger.debug(f"Found last user message: {last_user_message}")
|
32
|
+
break
|
33
|
+
|
34
|
+
response_content = f"Echo: {last_user_message}"
|
35
|
+
logger.info(f"EchoCraftBlueprint yielding: {response_content}")
|
36
|
+
|
37
|
+
# Yield the final response in the expected format
|
38
|
+
yield {
|
39
|
+
"messages": [
|
40
|
+
{"role": "assistant", "content": response_content}
|
41
|
+
]
|
42
|
+
}
|
43
|
+
logger.info("EchoCraftBlueprint run finished.")
|
44
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from django.apps import AppConfig
|
2
|
+
import logging
|
3
|
+
|
4
|
+
logger = logging.getLogger(__name__)
|
5
|
+
|
6
|
+
class FamilyTiesConfig(AppConfig):
|
7
|
+
name = 'blueprints.family_ties'
|
8
|
+
verbose_name = "Family Ties Blueprint"
|
9
|
+
|
10
|
+
def ready(self):
|
11
|
+
logger.debug(f"Registering {self.name} via AppConfig")
|
@@ -0,0 +1,152 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import sys
|
4
|
+
from typing import Dict, Any, List, ClassVar, Optional
|
5
|
+
|
6
|
+
# Ensure src is in path for BlueprintBase import
|
7
|
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
8
|
+
src_path = os.path.join(project_root, 'src')
|
9
|
+
if src_path not in sys.path: sys.path.insert(0, src_path)
|
10
|
+
|
11
|
+
try:
|
12
|
+
from agents import Agent, Tool, function_tool, Runner
|
13
|
+
from agents.mcp import MCPServer
|
14
|
+
from agents.models.interface import Model
|
15
|
+
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
16
|
+
from openai import AsyncOpenAI
|
17
|
+
from swarm.extensions.blueprint.blueprint_base import BlueprintBase
|
18
|
+
except ImportError as e:
|
19
|
+
print(f"ERROR: Import failed in FamilyTiesBlueprint: {e}. Check dependencies.")
|
20
|
+
print(f"sys.path: {sys.path}")
|
21
|
+
sys.exit(1)
|
22
|
+
|
23
|
+
logger = logging.getLogger(__name__)
|
24
|
+
|
25
|
+
# --- Agent Instructions ---
|
26
|
+
# Keep instructions defined globally for clarity
|
27
|
+
|
28
|
+
SHARED_INSTRUCTIONS = """
|
29
|
+
You are part of the Grifton family WordPress team. Peter coordinates, Brian manages WordPress.
|
30
|
+
Roles:
|
31
|
+
- PeterGrifton (Coordinator): User interface, planning, delegates WP tasks via `BrianGrifton` Agent Tool.
|
32
|
+
- BrianGrifton (WordPress Manager): Uses `server-wp-mcp` MCP tool (likely function `wp_call_endpoint`) to manage content based on Peter's requests.
|
33
|
+
Respond ONLY to the agent who tasked you.
|
34
|
+
"""
|
35
|
+
|
36
|
+
peter_instructions = (
|
37
|
+
f"{SHARED_INSTRUCTIONS}\n\n"
|
38
|
+
"YOUR ROLE: PeterGrifton, Coordinator. You handle user requests about WordPress.\n"
|
39
|
+
"1. Understand the user's goal (create post, edit post, list sites, etc.).\n"
|
40
|
+
"2. Delegate the task to Brian using the `BrianGrifton` agent tool.\n"
|
41
|
+
"3. Provide ALL necessary details to Brian (content, title, site ID, endpoint details if known, method like GET/POST).\n"
|
42
|
+
"4. Relay Brian's response (success, failure, IDs, data) back to the user clearly."
|
43
|
+
)
|
44
|
+
|
45
|
+
brian_instructions = (
|
46
|
+
f"{SHARED_INSTRUCTIONS}\n\n"
|
47
|
+
"YOUR ROLE: BrianGrifton, WordPress Manager. You interact with WordPress sites via the `server-wp-mcp` tool.\n"
|
48
|
+
"1. Receive tasks from Peter.\n"
|
49
|
+
"2. Determine the correct WordPress REST API endpoint and parameters required (e.g., `site`, `endpoint`, `method`, `params`).\n"
|
50
|
+
"3. Call the MCP tool function (likely named `wp_call_endpoint` or similar provided by the MCP server) with the correct JSON arguments.\n"
|
51
|
+
"4. Report the outcome (success confirmation, data returned, or error message) precisely back to Peter."
|
52
|
+
)
|
53
|
+
|
54
|
+
# --- Define the Blueprint ---
|
55
|
+
class FamilyTiesBlueprint(BlueprintBase):
|
56
|
+
"""Manages WordPress content with a Peter/Brian agent team using the `server-wp-mcp` server."""
|
57
|
+
metadata: ClassVar[Dict[str, Any]] = {
|
58
|
+
"name": "FamilyTiesBlueprint", # Standardized name
|
59
|
+
"title": "Family Ties / ChaosCrew WP Manager",
|
60
|
+
"description": "Manages WordPress content using Peter (coordinator) and Brian (WP manager via MCP).",
|
61
|
+
"version": "1.2.0", # Incremented version
|
62
|
+
"author": "Open Swarm Team (Refactored)",
|
63
|
+
"tags": ["wordpress", "cms", "multi-agent", "mcp"],
|
64
|
+
"required_mcp_servers": ["server-wp-mcp"], # Brian needs this
|
65
|
+
"env_vars": ["WP_SITES_PATH"] # Informational: MCP server needs this
|
66
|
+
}
|
67
|
+
|
68
|
+
# Caches
|
69
|
+
_openai_client_cache: Dict[str, AsyncOpenAI] = {}
|
70
|
+
_model_instance_cache: Dict[str, Model] = {}
|
71
|
+
|
72
|
+
# --- Model Instantiation Helper --- (Standard helper)
|
73
|
+
def _get_model_instance(self, profile_name: str) -> Model:
|
74
|
+
"""Retrieves or creates an LLM Model instance."""
|
75
|
+
# ... (Implementation is the same as in previous refactors) ...
|
76
|
+
if profile_name in self._model_instance_cache:
|
77
|
+
logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
|
78
|
+
return self._model_instance_cache[profile_name]
|
79
|
+
logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
|
80
|
+
profile_data = self.get_llm_profile(profile_name)
|
81
|
+
if not profile_data:
|
82
|
+
logger.critical(f"LLM profile '{profile_name}' (or 'default') not found.")
|
83
|
+
raise ValueError(f"Missing LLM profile configuration for '{profile_name}' or 'default'.")
|
84
|
+
provider = profile_data.get("provider", "openai").lower()
|
85
|
+
model_name = profile_data.get("model")
|
86
|
+
if not model_name:
|
87
|
+
logger.critical(f"LLM profile '{profile_name}' missing 'model' key.")
|
88
|
+
raise ValueError(f"Missing 'model' key in LLM profile '{profile_name}'.")
|
89
|
+
if provider != "openai":
|
90
|
+
logger.error(f"Unsupported LLM provider '{provider}'.")
|
91
|
+
raise ValueError(f"Unsupported LLM provider: {provider}")
|
92
|
+
client_cache_key = f"{provider}_{profile_data.get('base_url')}"
|
93
|
+
if client_cache_key not in self._openai_client_cache:
|
94
|
+
client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
|
95
|
+
filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
96
|
+
log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
|
97
|
+
logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
|
98
|
+
try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
|
99
|
+
except Exception as e: raise ValueError(f"Failed to init OpenAI client: {e}") from e
|
100
|
+
client = self._openai_client_cache[client_cache_key]
|
101
|
+
logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
|
102
|
+
try:
|
103
|
+
model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
|
104
|
+
self._model_instance_cache[profile_name] = model_instance
|
105
|
+
return model_instance
|
106
|
+
except Exception as e: raise ValueError(f"Failed to init LLM provider: {e}") from e
|
107
|
+
|
108
|
+
def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
|
109
|
+
"""Creates the Family Ties agent team and returns PeterGrifton (Coordinator)."""
|
110
|
+
logger.debug("Creating Family Ties agent team...")
|
111
|
+
self._model_instance_cache = {}
|
112
|
+
self._openai_client_cache = {}
|
113
|
+
|
114
|
+
default_profile_name = self.config.get("llm_profile", "default")
|
115
|
+
logger.debug(f"Using LLM profile '{default_profile_name}' for Family Ties agents.")
|
116
|
+
model_instance = self._get_model_instance(default_profile_name)
|
117
|
+
|
118
|
+
# Filter for the required MCP server
|
119
|
+
wp_mcp_server = next((s for s in mcp_servers if s.name == "server-wp-mcp"), None)
|
120
|
+
if not wp_mcp_server:
|
121
|
+
# This case should be prevented by BlueprintBase MCP check, but good practice
|
122
|
+
logger.error("Required MCP server 'server-wp-mcp' not found/started. Brian will be non-functional.")
|
123
|
+
# Optionally raise an error or allow degraded functionality
|
124
|
+
# raise ValueError("Critical MCP server 'server-wp-mcp' failed to start.")
|
125
|
+
|
126
|
+
# Instantiate Brian, passing the specific MCP server
|
127
|
+
brian_agent = Agent(
|
128
|
+
name="BrianGrifton",
|
129
|
+
model=model_instance,
|
130
|
+
instructions=brian_instructions,
|
131
|
+
tools=[], # Brian uses MCP tools provided by the server
|
132
|
+
mcp_servers=[wp_mcp_server] if wp_mcp_server else []
|
133
|
+
)
|
134
|
+
|
135
|
+
# Instantiate Peter, giving Brian as a tool
|
136
|
+
peter_agent = Agent(
|
137
|
+
name="PeterGrifton",
|
138
|
+
model=model_instance,
|
139
|
+
instructions=peter_instructions,
|
140
|
+
tools=[
|
141
|
+
brian_agent.as_tool(
|
142
|
+
tool_name="BrianGrifton",
|
143
|
+
tool_description="Delegate WordPress tasks (create/edit/list posts/sites, etc.) to Brian."
|
144
|
+
)
|
145
|
+
],
|
146
|
+
mcp_servers=[] # Peter doesn't directly use MCPs
|
147
|
+
)
|
148
|
+
logger.debug("Agents created: PeterGrifton (Coordinator), BrianGrifton (WordPress Manager).")
|
149
|
+
return peter_agent # Peter is the entry point
|
150
|
+
|
151
|
+
if __name__ == "__main__":
|
152
|
+
FamilyTiesBlueprint.main()
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from django.db import models
|
2
|
+
|
3
|
+
class AgentInstruction(models.Model):
|
4
|
+
agent_name = models.CharField(max_length=50, unique=True, help_text="Unique name (e.g., 'PeterGriffin').")
|
5
|
+
instruction_text = models.TextField(help_text="Instructions for the agent.")
|
6
|
+
model = models.CharField(max_length=50, default="default", help_text="LLM model.")
|
7
|
+
env_vars = models.TextField(blank=True, null=True, help_text="JSON env variables.")
|
8
|
+
mcp_servers = models.TextField(blank=True, null=True, help_text="JSON MCP servers.")
|
9
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
10
|
+
updated_at = models.DateTimeField(auto_now=True)
|
11
|
+
|
12
|
+
class Meta:
|
13
|
+
app_label = "blueprints_chc"
|
14
|
+
db_table = "swarm_agent_instruction_chc"
|
15
|
+
verbose_name = "Agent Instruction"
|
16
|
+
verbose_name_plural = "Agent Instructions"
|
17
|
+
|
18
|
+
def __str__(self):
|
19
|
+
return f"{self.agent_name} Instruction"
|
@@ -0,0 +1,7 @@
|
|
1
|
+
from rest_framework import serializers
|
2
|
+
from blueprints.chc.models import AgentInstruction
|
3
|
+
|
4
|
+
class AgentInstructionSerializer(serializers.ModelSerializer):
|
5
|
+
class Meta:
|
6
|
+
model = AgentInstruction
|
7
|
+
fields = ['id', 'agent_name', 'instruction_text', 'model', 'env_vars', 'mcp_servers', 'created_at', 'updated_at']
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import logging
|
2
|
+
from django.apps import AppConfig
|
3
|
+
|
4
|
+
logger = logging.getLogger(__name__)
|
5
|
+
|
6
|
+
def update_installed_apps(settings):
|
7
|
+
blueprint_app = "blueprints.chc"
|
8
|
+
if blueprint_app not in settings.get("INSTALLED_APPS", []):
|
9
|
+
settings["INSTALLED_APPS"].append(blueprint_app)
|
10
|
+
|
11
|
+
try:
|
12
|
+
update_installed_apps(globals())
|
13
|
+
except Exception as e:
|
14
|
+
logger.error("CHC update failed: %s", e)
|
15
|
+
|
16
|
+
CORS_ALLOW_ALL_ORIGINS = True
|