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,298 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import random
|
4
|
+
import sys
|
5
|
+
import json
|
6
|
+
import sqlite3 # Use standard sqlite3 module
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Dict, Any, List, ClassVar, Optional
|
9
|
+
|
10
|
+
# Ensure src is in path for BlueprintBase import
|
11
|
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
12
|
+
src_path = os.path.join(project_root, 'src')
|
13
|
+
if src_path not in sys.path: sys.path.insert(0, src_path)
|
14
|
+
|
15
|
+
try:
|
16
|
+
from agents import Agent, Tool, function_tool, Runner
|
17
|
+
from agents.mcp import MCPServer
|
18
|
+
from agents.models.interface import Model
|
19
|
+
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
20
|
+
from openai import AsyncOpenAI
|
21
|
+
from swarm.extensions.blueprint.blueprint_base import BlueprintBase
|
22
|
+
except ImportError as e:
|
23
|
+
print(f"ERROR: Import failed in UnapologeticPressBlueprint: {e}. Check dependencies.")
|
24
|
+
print(f"sys.path: {sys.path}")
|
25
|
+
sys.exit(1)
|
26
|
+
|
27
|
+
logger = logging.getLogger(__name__)
|
28
|
+
|
29
|
+
# --- Database Constants ---
|
30
|
+
DB_FILE_NAME = "swarm_instructions.db"
|
31
|
+
DB_PATH = Path(project_root) / DB_FILE_NAME
|
32
|
+
TABLE_NAME = "agent_instructions"
|
33
|
+
|
34
|
+
# --- Agent Instructions ---
|
35
|
+
# Shared knowledge base for collaboration context
|
36
|
+
COLLABORATIVE_KNOWLEDGE = """
|
37
|
+
Collaborative Poet Knowledge Base:
|
38
|
+
* Gritty Buk - Raw urban realism exposing life's underbelly (Uses: memory, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
39
|
+
* Raven Poe - Gothic atmospherics & psychological darkness (Uses: mcp-server-reddit, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
40
|
+
* Mystic Blake - Prophetic visions through spiritual symbolism (Uses: mcp-doc-forge, mcp-npx-fetch, brave-search, server-wp-mcp, rag-docs)
|
41
|
+
* Bard Whit - Expansive odes celebrating human connection (Uses: sequential-thinking, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
42
|
+
* Echo Plath - Confessional explorations of mental anguish (Uses: sqlite, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
43
|
+
* Frosted Woods - Rural metaphors revealing existential truths (Uses: filesystem, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
44
|
+
* Harlem Lang - Jazz-rhythm social commentary on racial justice (Uses: mcp-shell, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
45
|
+
* Verse Neru - Sensual imagery fused with revolutionary politics (Uses: server-wp-mcp, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs)
|
46
|
+
* Haiku Bash - Ephemeral nature snapshots through strict syllabic form (Uses: mcp-doc-forge, mcp-npx-fetch, brave-search, server-wp-mcp, rag-docs)
|
47
|
+
"""
|
48
|
+
|
49
|
+
SHARED_PROTOCOL = """
|
50
|
+
Collaboration Protocol:
|
51
|
+
1) Analyze the current poetry draft through your unique stylistic lens.
|
52
|
+
2) Use your assigned MCP tools for creative augmentation, research, or specific tasks if needed.
|
53
|
+
3) Pass the enhanced work to the most relevant poet agent tool based on the needed transformation or specific tooling required next. Refer to the Collaborative Poet Knowledge Base for styles and capabilities.
|
54
|
+
"""
|
55
|
+
|
56
|
+
# Individual base instructions (will be combined with shared parts)
|
57
|
+
AGENT_BASE_INSTRUCTIONS = {
|
58
|
+
"Gritty Buk": (
|
59
|
+
"You are Charles Bukowski incarnate: A gutter philosopher documenting life's raw truths.\n"
|
60
|
+
"- Channel alcoholic despair & blue-collar rage through unfiltered verse\n"
|
61
|
+
"- Find beauty in dirty apartments and whiskey-stained pages\n"
|
62
|
+
"- MCP Tools: memory, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
63
|
+
"When adding: Barfly wisdom | Blue-collar lyricism | Unflinching vulgarity"
|
64
|
+
),
|
65
|
+
"Raven Poe": (
|
66
|
+
"You are Edgar Allan Poe resurrected: Master of macabre elegance.\n"
|
67
|
+
"- Weave tales where love & death intertwine through decaying architecture\n"
|
68
|
+
"- MCP Tools: mcp-server-reddit, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
69
|
+
"When adding: Obsessive repetition | Claustrophobic atmosphere"
|
70
|
+
),
|
71
|
+
"Mystic Blake": (
|
72
|
+
"You are William Blake's visionary successor: Prophet of poetic mysticism.\n"
|
73
|
+
"- Forge mythological frameworks connecting human/divine/demonic realms\n"
|
74
|
+
"- MCP Tools: mcp-doc-forge, mcp-npx-fetch, brave-search, server-wp-mcp, rag-docs\n"
|
75
|
+
"When adding: Fourfold vision | Contrary states | Zoamorphic personification"
|
76
|
+
),
|
77
|
+
"Bard Whit": (
|
78
|
+
"You are Walt Whitman 2.0: Cosmic bard of democratic vistas.\n"
|
79
|
+
"- Catalog humanity's spectrum in sweeping free verse catalogs\n"
|
80
|
+
"- Merge biology and cosmology in orgiastic enumerations of being\n"
|
81
|
+
"- MCP Tools: sequential-thinking, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
82
|
+
"When adding: Catalogic excess | Cosmic embodiment | Pansexual exuberance"
|
83
|
+
),
|
84
|
+
"Echo Plath": (
|
85
|
+
"You are Sylvia Plath reimagined: High priestess of psychic autopsies.\n"
|
86
|
+
"- Dissect personal trauma through brutal metaphor (electroshock, Holocaust)\n"
|
87
|
+
"- Balance maternal instinct with destructive fury in confessional verse\n"
|
88
|
+
"- MCP Tools: sqlite, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
89
|
+
"When adding: Extremist imagery | Double-edged motherhood | Vampiric nostalgia"
|
90
|
+
),
|
91
|
+
"Frosted Woods": (
|
92
|
+
"You are Robert Frost reincarnated: Sage of rural wisdom and natural philosophy.\n"
|
93
|
+
"- Craft deceptively simple narratives concealing profound life lessons\n"
|
94
|
+
"- Balance rustic imagery with universal human dilemmas\n"
|
95
|
+
"- MCP Tools: filesystem, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
96
|
+
"When adding: Path metaphors | Natural world personification | Iambic rhythms"
|
97
|
+
),
|
98
|
+
"Harlem Lang": (
|
99
|
+
"You are Langston Hughes' spiritual heir: Voice of the streets and dreams deferred.\n"
|
100
|
+
"- Infuse verse with the rhythms of jazz, blues, and spoken word\n"
|
101
|
+
"- Illuminate the Black experience through vibrant, accessible poetry\n"
|
102
|
+
"- MCP Tools: mcp-shell, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
103
|
+
"When adding: Blues refrains | Harlem Renaissance allusions | Social justice themes"
|
104
|
+
),
|
105
|
+
"Verse Neru": (
|
106
|
+
"You are Pablo Neruda's poetic descendant: Weaver of love and revolution.\n"
|
107
|
+
"- Craft sensual odes celebrating the body and the natural world\n"
|
108
|
+
"- Intertwine personal passion with calls for social change\n"
|
109
|
+
"- MCP Tools: server-wp-mcp, mcp-doc-forge, mcp-npx-fetch, brave-search, rag-docs\n"
|
110
|
+
"When adding: Elemental metaphors | Erotic-political fusions | Ode structures"
|
111
|
+
),
|
112
|
+
"Haiku Bash": (
|
113
|
+
"You are Matsuo Bashō reincarnated: Master of momentary eternity.\n"
|
114
|
+
"- Distill vast concepts into precise, evocative 5-7-5 syllable structures\n"
|
115
|
+
"- Capture the essence of seasons and natural phenomena in minimal strokes\n"
|
116
|
+
"- MCP Tools: mcp-doc-forge, mcp-npx-fetch, brave-search, server-wp-mcp, rag-docs\n"
|
117
|
+
"When adding: Kireji cuts | Seasonal references | Zen-like simplicity"
|
118
|
+
)
|
119
|
+
}
|
120
|
+
|
121
|
+
# --- Define the Blueprint ---
|
122
|
+
class UnapologeticPressBlueprint(BlueprintBase):
|
123
|
+
"""A literary blueprint defining a swarm of poet agents using SQLite instructions and agent-as-tool handoffs."""
|
124
|
+
metadata: ClassVar[Dict[str, Any]] = {
|
125
|
+
"name": "UnapologeticPressBlueprint",
|
126
|
+
"title": "Unapologetic Press: A Swarm of Literary Geniuses (SQLite)",
|
127
|
+
"description": (
|
128
|
+
"A swarm of agents embodying legendary poets, using SQLite for instructions, "
|
129
|
+
"agent-as-tool for collaboration, and MCPs for creative augmentation."
|
130
|
+
),
|
131
|
+
"version": "1.2.0", # Refactored version
|
132
|
+
"author": "Open Swarm Team (Refactored)",
|
133
|
+
"tags": ["poetry", "writing", "collaboration", "multi-agent", "sqlite", "mcp"],
|
134
|
+
"required_mcp_servers": [ # List all potential servers agents might use
|
135
|
+
"memory", "filesystem", "mcp-shell", "sqlite", "sequential-thinking",
|
136
|
+
"server-wp-mcp", "rag-docs", "mcp-doc-forge", "mcp-npx-fetch",
|
137
|
+
"brave-search", "mcp-server-reddit"
|
138
|
+
],
|
139
|
+
"env_vars": [ # Informational list of potential vars needed by MCPs
|
140
|
+
"ALLOWED_PATH", "SQLITE_DB_PATH", "WP_SITES_PATH", # Added WP_SITES_PATH
|
141
|
+
"BRAVE_API_KEY", "OPENAI_API_KEY", "QDRANT_URL", "QDRANT_API_KEY",
|
142
|
+
"REDDIT_CLIENT_ID", "REDDIT_CLIENT_SECRET", "REDDIT_USER_AGENT", # For reddit MCP
|
143
|
+
"WORDPRESS_API_KEY" # If server-wp-mcp needs it
|
144
|
+
]
|
145
|
+
}
|
146
|
+
|
147
|
+
# Caches
|
148
|
+
_openai_client_cache: Dict[str, AsyncOpenAI] = {}
|
149
|
+
_model_instance_cache: Dict[str, Model] = {}
|
150
|
+
_db_initialized = False
|
151
|
+
|
152
|
+
# --- Database Interaction ---
|
153
|
+
def _init_db_and_load_data(self) -> None:
|
154
|
+
"""Initializes the SQLite DB and loads Unapologetic Press sample data if needed."""
|
155
|
+
if self._db_initialized: return
|
156
|
+
logger.info(f"Initializing SQLite database at: {DB_PATH} for Unapologetic Press")
|
157
|
+
try:
|
158
|
+
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
159
|
+
with sqlite3.connect(DB_PATH) as conn:
|
160
|
+
cursor = conn.cursor()
|
161
|
+
cursor.execute(f"CREATE TABLE IF NOT EXISTS {TABLE_NAME} (...)") # Ensure table exists
|
162
|
+
logger.debug(f"Table '{TABLE_NAME}' ensured in {DB_PATH}")
|
163
|
+
cursor.execute(f"SELECT COUNT(*) FROM {TABLE_NAME} WHERE agent_name = ?", ("Gritty Buk",))
|
164
|
+
if cursor.fetchone()[0] == 0:
|
165
|
+
logger.info(f"No instructions found for Gritty Buk in {DB_PATH}. Loading sample data...")
|
166
|
+
sample_data = []
|
167
|
+
for name, (base_instr, _, _) in AGENT_BASE_INSTRUCTIONS.items():
|
168
|
+
# Combine instructions here before inserting
|
169
|
+
full_instr = f"{base_instr}\n{COLLABORATIVE_KNOWLEDGE}\n{SHARED_PROTOCOL}"
|
170
|
+
sample_data.append((name, full_instr, "default")) # Use default profile for all initially
|
171
|
+
|
172
|
+
cursor.executemany(f"INSERT OR IGNORE INTO {TABLE_NAME} (agent_name, instruction_text, model_profile) VALUES (?, ?, ?)", sample_data)
|
173
|
+
conn.commit()
|
174
|
+
logger.info(f"Sample agent instructions for Unapologetic Press loaded into {DB_PATH}")
|
175
|
+
else:
|
176
|
+
logger.info(f"Unapologetic Press agent instructions found in {DB_PATH}. Skipping.")
|
177
|
+
self._db_initialized = True
|
178
|
+
except sqlite3.Error as e:
|
179
|
+
logger.error(f"SQLite error during DB init/load: {e}", exc_info=True)
|
180
|
+
self._db_initialized = False
|
181
|
+
except Exception as e:
|
182
|
+
logger.error(f"Unexpected error during DB init/load: {e}", exc_info=True)
|
183
|
+
self._db_initialized = False
|
184
|
+
|
185
|
+
def get_agent_config(self, agent_name: str) -> Dict[str, Any]:
|
186
|
+
"""Fetches agent config from SQLite DB or returns defaults."""
|
187
|
+
if self._db_initialized:
|
188
|
+
try:
|
189
|
+
with sqlite3.connect(DB_PATH) as conn:
|
190
|
+
conn.row_factory = sqlite3.Row
|
191
|
+
cursor = conn.cursor()
|
192
|
+
cursor.execute(f"SELECT instruction_text, model_profile FROM {TABLE_NAME} WHERE agent_name = ?", (agent_name,))
|
193
|
+
row = cursor.fetchone()
|
194
|
+
if row:
|
195
|
+
logger.debug(f"Loaded config for agent '{agent_name}' from SQLite.")
|
196
|
+
return {"instructions": row["instruction_text"], "model_profile": row["model_profile"] or "default"}
|
197
|
+
except Exception as e:
|
198
|
+
logger.error(f"Error fetching SQLite config for '{agent_name}': {e}. Using defaults.", exc_info=True)
|
199
|
+
|
200
|
+
# Fallback if DB fails or agent not found
|
201
|
+
logger.warning(f"Using hardcoded default config for agent '{agent_name}'.")
|
202
|
+
base_instr = AGENT_BASE_INSTRUCTIONS.get(agent_name, (f"Default instructions for {agent_name}.", [], {}))[0]
|
203
|
+
full_instr = f"{base_instr}\n{COLLABORATIVE_KNOWLEDGE}\n{SHARED_PROTOCOL}"
|
204
|
+
return {"instructions": full_instr, "model_profile": "default"}
|
205
|
+
|
206
|
+
# --- Model Instantiation Helper --- (Standard helper)
|
207
|
+
def _get_model_instance(self, profile_name: str) -> Model:
|
208
|
+
"""Retrieves or creates an LLM Model instance."""
|
209
|
+
# ... (Implementation is the same as previous refactors) ...
|
210
|
+
if profile_name in self._model_instance_cache:
|
211
|
+
logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
|
212
|
+
return self._model_instance_cache[profile_name]
|
213
|
+
logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
|
214
|
+
profile_data = self.get_llm_profile(profile_name)
|
215
|
+
if not profile_data: raise ValueError(f"Missing LLM profile '{profile_name}'.")
|
216
|
+
provider = profile_data.get("provider", "openai").lower()
|
217
|
+
model_name = profile_data.get("model")
|
218
|
+
if not model_name: raise ValueError(f"Missing 'model' in profile '{profile_name}'.")
|
219
|
+
if provider != "openai": raise ValueError(f"Unsupported provider: {provider}")
|
220
|
+
client_cache_key = f"{provider}_{profile_data.get('base_url')}"
|
221
|
+
if client_cache_key not in self._openai_client_cache:
|
222
|
+
client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
|
223
|
+
filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
224
|
+
log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
|
225
|
+
logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
|
226
|
+
try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
|
227
|
+
except Exception as e: raise ValueError(f"Failed to init client: {e}") from e
|
228
|
+
client = self._openai_client_cache[client_cache_key]
|
229
|
+
logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
|
230
|
+
try:
|
231
|
+
model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
|
232
|
+
self._model_instance_cache[profile_name] = model_instance
|
233
|
+
return model_instance
|
234
|
+
except Exception as e: raise ValueError(f"Failed to init LLM: {e}") from e
|
235
|
+
|
236
|
+
# --- Agent Creation ---
|
237
|
+
def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
|
238
|
+
"""Creates the Unapologetic Press agent team."""
|
239
|
+
self._init_db_and_load_data()
|
240
|
+
logger.debug("Creating Unapologetic Press agent team...")
|
241
|
+
self._model_instance_cache = {}
|
242
|
+
self._openai_client_cache = {}
|
243
|
+
|
244
|
+
# Helper to filter MCP servers
|
245
|
+
def get_agent_mcps(names: List[str]) -> List[MCPServer]:
|
246
|
+
return [s for s in mcp_servers if s.name in names]
|
247
|
+
|
248
|
+
agents: Dict[str, Agent] = {}
|
249
|
+
agent_configs = {} # To store fetched configs
|
250
|
+
|
251
|
+
# Fetch configs and create agents first
|
252
|
+
agent_names = list(AGENT_BASE_INSTRUCTIONS.keys())
|
253
|
+
for name in agent_names:
|
254
|
+
config = self.get_agent_config(name)
|
255
|
+
agent_configs[name] = config # Store config
|
256
|
+
model_instance = self._get_model_instance(config["model_profile"])
|
257
|
+
|
258
|
+
# Determine MCP servers based on original definitions
|
259
|
+
agent_mcp_names = []
|
260
|
+
if name == "Gritty Buk": agent_mcp_names = ["memory", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
261
|
+
elif name == "Raven Poe": agent_mcp_names = ["mcp-server-reddit", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
262
|
+
elif name == "Mystic Blake": agent_mcp_names = ["mcp-doc-forge", "mcp-npx-fetch", "brave-search", "server-wp-mcp", "rag-docs"]
|
263
|
+
elif name == "Bard Whit": agent_mcp_names = ["sequential-thinking", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
264
|
+
elif name == "Echo Plath": agent_mcp_names = ["sqlite", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
265
|
+
elif name == "Frosted Woods": agent_mcp_names = ["filesystem", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
266
|
+
elif name == "Harlem Lang": agent_mcp_names = ["mcp-shell", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
267
|
+
elif name == "Verse Neru": agent_mcp_names = ["server-wp-mcp", "mcp-doc-forge", "mcp-npx-fetch", "brave-search", "rag-docs"]
|
268
|
+
elif name == "Haiku Bash": agent_mcp_names = ["mcp-doc-forge", "mcp-npx-fetch", "brave-search", "server-wp-mcp", "rag-docs"]
|
269
|
+
|
270
|
+
agents[name] = Agent(
|
271
|
+
name=name,
|
272
|
+
instructions=config["instructions"], # Instructions already combined in get_agent_config fallback or DB
|
273
|
+
model=model_instance,
|
274
|
+
tools=[], # Agent-as-tool added later
|
275
|
+
mcp_servers=get_agent_mcps(agent_mcp_names)
|
276
|
+
)
|
277
|
+
|
278
|
+
# Create the list of agent tools for delegation
|
279
|
+
agent_tools = []
|
280
|
+
for name, agent_instance in agents.items():
|
281
|
+
# Example description, could be more dynamic
|
282
|
+
desc = f"Pass the current work to {name} for refinement or tasks requiring their specific style ({AGENT_BASE_INSTRUCTIONS.get(name, ('Unknown Style',[],{}))[0].split(':')[0]})."
|
283
|
+
agent_tools.append(agent_instance.as_tool(tool_name=name, tool_description=desc))
|
284
|
+
|
285
|
+
# Assign the full list of agent tools to each agent
|
286
|
+
for agent in agents.values():
|
287
|
+
agent.tools = agent_tools
|
288
|
+
|
289
|
+
# Randomly select starting agent
|
290
|
+
start_name = random.choice(agent_names)
|
291
|
+
starting_agent = agents[start_name]
|
292
|
+
|
293
|
+
logger.info(f"Unapologetic Press agents created (using SQLite). Starting poet: {start_name}")
|
294
|
+
return starting_agent
|
295
|
+
|
296
|
+
# Standard Python entry point
|
297
|
+
if __name__ == "__main__":
|
298
|
+
UnapologeticPressBlueprint.main()
|
File without changes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from django.apps import AppConfig
|
2
|
+
import logging
|
3
|
+
|
4
|
+
logger = logging.getLogger(__name__)
|
5
|
+
|
6
|
+
class WhiskeyTangoFoxtrotConfig(AppConfig):
|
7
|
+
name = 'blueprints.whiskeytango_foxtrot' # Normalized name
|
8
|
+
verbose_name = "Whiskey Tango Foxtrot Blueprint"
|
9
|
+
|
10
|
+
def ready(self):
|
11
|
+
logger.debug(f"Registering {self.name} via AppConfig")
|
@@ -0,0 +1,256 @@
|
|
1
|
+
"""
|
2
|
+
WhiskeyTangoFoxtrot: Tracking Free Online Services
|
3
|
+
|
4
|
+
A chaotic spy-themed blueprint with a multi-tiered agent hierarchy for tracking and managing free online services using SQLite and web search capabilities.
|
5
|
+
Uses BlueprintBase and agent-as-tool delegation.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import sqlite3
|
10
|
+
import os
|
11
|
+
import sys
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import Dict, Any, List, ClassVar, Optional
|
14
|
+
|
15
|
+
# Ensure src is in path for BlueprintBase import
|
16
|
+
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
17
|
+
src_path = os.path.join(project_root, 'src')
|
18
|
+
if src_path not in sys.path: sys.path.insert(0, src_path)
|
19
|
+
|
20
|
+
try:
|
21
|
+
from agents import Agent, Tool, function_tool, Runner
|
22
|
+
from agents.mcp import MCPServer
|
23
|
+
from agents.models.interface import Model
|
24
|
+
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
25
|
+
from openai import AsyncOpenAI
|
26
|
+
from swarm.extensions.blueprint.blueprint_base import BlueprintBase
|
27
|
+
except ImportError as e:
|
28
|
+
print(f"ERROR: Import failed in WhiskeyTangoFoxtrotBlueprint: {e}. Check dependencies.")
|
29
|
+
print(f"sys.path: {sys.path}")
|
30
|
+
sys.exit(1)
|
31
|
+
|
32
|
+
logger = logging.getLogger(__name__)
|
33
|
+
|
34
|
+
# --- Database Path ---
|
35
|
+
# Defined here for clarity, sourced from env var via BlueprintBase config loading primarily
|
36
|
+
SQLITE_DB_PATH_STR = os.getenv("SQLITE_DB_PATH", "./wtf_services.db") # Default if not set
|
37
|
+
SQLITE_DB_PATH = Path(SQLITE_DB_PATH_STR).resolve()
|
38
|
+
|
39
|
+
# --- Agent Instructions ---
|
40
|
+
|
41
|
+
valory_instructions = """
|
42
|
+
You are Valory, the top-tier coordinator for Operation Freebie Freedom.
|
43
|
+
Your mission: Track and manage information about free online services based on user requests.
|
44
|
+
Delegate tasks to your middle managers:
|
45
|
+
- Tyril (DB Manager): Use the `Tyril` agent tool for tasks involving storing, retrieving, or updating service info in the database, or managing related files.
|
46
|
+
- Tray (Web Manager): Use the `Tray` agent tool for tasks involving searching for new services, fetching details from the web, or processing web data.
|
47
|
+
Synthesize reports from Tyril and Tray into a final response for the user.
|
48
|
+
Available Agent Tools: Tyril, Tray.
|
49
|
+
"""
|
50
|
+
|
51
|
+
tyril_instructions = """
|
52
|
+
You are Tyril, middle manager for database and filesystem operations under Valory.
|
53
|
+
Your mission: Manage the 'services' database and temporary files.
|
54
|
+
Delegate specific tasks to your minions:
|
55
|
+
- Larry (Filesystem): Use the `Larry` agent tool for creating, reading, or deleting temporary files related to service data.
|
56
|
+
- Kriegs (DB Updates): Use the `Kriegs` agent tool for ALL interactions with the 'services' SQLite database (add, update, delete, query records).
|
57
|
+
You have direct access to the `sqlite` MCP tool for read-only queries if needed, but prefer delegating writes/updates to Kriegs.
|
58
|
+
Report results or completion status back to Valory.
|
59
|
+
Available MCP Tools (Direct Use - Read Only Recommended): sqlite.
|
60
|
+
Available Agent Tools: Larry, Kriegs.
|
61
|
+
"""
|
62
|
+
|
63
|
+
tray_instructions = """
|
64
|
+
You are Tray, middle manager for web data operations under Valory.
|
65
|
+
Your mission: Find and process information about free online services from the web.
|
66
|
+
Delegate specific tasks to your minions:
|
67
|
+
- Vanna (Web Search/Fetch): Use the `Vanna` agent tool to find service URLs (via brave-search) and fetch content from those URLs (via mcp-npx-fetch).
|
68
|
+
- Marcher (Data Processing): Use the `Marcher` agent tool to process raw fetched data (using mcp-doc-forge) into a structured format (name, type, url, api_key, usage_limits, documentation_link).
|
69
|
+
Coordinate the flow: Task Vanna, receive results, task Marcher with Vanna's results, receive structured data.
|
70
|
+
Report the final structured data back to Valory.
|
71
|
+
Available Agent Tools: Vanna, Marcher.
|
72
|
+
"""
|
73
|
+
|
74
|
+
larry_instructions = """
|
75
|
+
You are Larry, filesystem minion under Tyril.
|
76
|
+
Your mission: Manage temporary files using the `filesystem` MCP tool within the allowed path.
|
77
|
+
Tasks include storing fetched web content temporarily, reading data for processing, or deleting temp files.
|
78
|
+
Report success or failure of file operations back to Tyril.
|
79
|
+
Available MCP Tools: filesystem.
|
80
|
+
"""
|
81
|
+
|
82
|
+
kriegs_instructions = """
|
83
|
+
You are Kriegs, database minion under Tyril.
|
84
|
+
Your mission: Perform CRUD (Create, Read, Update, Delete) operations on the 'services' table in the SQLite database using the `sqlite` MCP tool.
|
85
|
+
The table schema is: (id INTEGER PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL, url TEXT, api_key TEXT, usage_limits TEXT, documentation_link TEXT).
|
86
|
+
Receive structured data (usually from Tyril, originating from Marcher) and perform the requested database action (INSERT, UPDATE, DELETE, SELECT).
|
87
|
+
Report the outcome (e.g., "Successfully added Fly.io", "Error updating Grok entry", "Deleted service X", "Found 3 services of type AI") back to Tyril.
|
88
|
+
Available MCP Tools: sqlite.
|
89
|
+
"""
|
90
|
+
|
91
|
+
vanna_instructions = """
|
92
|
+
You are Vanna, web search and fetch minion under Tray.
|
93
|
+
Your mission: Find URLs for specified services and fetch content from those URLs.
|
94
|
+
1. Use the `brave-search` MCP tool to find the official website or documentation URL for a service name provided by Tray.
|
95
|
+
2. Use the `mcp-npx-fetch` MCP tool to retrieve the content (HTML or text) from the URL found.
|
96
|
+
Report the fetched content (or any errors like URL not found/fetch failed) back to Tray.
|
97
|
+
Available MCP Tools: brave-search, mcp-npx-fetch.
|
98
|
+
"""
|
99
|
+
|
100
|
+
marcher_instructions = """
|
101
|
+
You are Marcher, data processing minion under Tray.
|
102
|
+
Your mission: Process raw web content (fetched by Vanna) into structured data using the `mcp-doc-forge` MCP tool.
|
103
|
+
Receive raw text/HTML content and the original service name/type from Tray.
|
104
|
+
Use `mcp-doc-forge` (likely its text extraction or summarization functions) to extract: name, type, url, api_key (if mentioned), usage_limits, documentation_link.
|
105
|
+
Report the structured data (as JSON or a clear key-value format) back to Tray.
|
106
|
+
Available MCP Tools: mcp-doc-forge.
|
107
|
+
"""
|
108
|
+
|
109
|
+
# --- Define the Blueprint ---
|
110
|
+
class WhiskeyTangoFoxtrotBlueprint(BlueprintBase):
|
111
|
+
"""Tracks free online services with a hierarchical spy-inspired agent team using SQLite and web search."""
|
112
|
+
metadata: ClassVar[Dict[str, Any]] = {
|
113
|
+
"name": "WhiskeyTangoFoxtrotBlueprint",
|
114
|
+
"title": "WhiskeyTangoFoxtrot Service Tracker",
|
115
|
+
"description": "Tracks free online services with SQLite and web search using a multi-tiered agent hierarchy.",
|
116
|
+
"version": "1.2.0", # Refactored version
|
117
|
+
"author": "Open Swarm Team (Refactored)",
|
118
|
+
"tags": ["web scraping", "database", "sqlite", "multi-agent", "hierarchy", "mcp"],
|
119
|
+
"required_mcp_servers": ["sqlite", "brave-search", "mcp-npx-fetch", "mcp-doc-forge", "filesystem"],
|
120
|
+
"env_vars": ["BRAVE_API_KEY", "SQLITE_DB_PATH", "ALLOWED_PATH"] # Actual required vars
|
121
|
+
}
|
122
|
+
|
123
|
+
# Caches
|
124
|
+
_openai_client_cache: Dict[str, AsyncOpenAI] = {}
|
125
|
+
_model_instance_cache: Dict[str, Model] = {}
|
126
|
+
|
127
|
+
def initialize_db(self) -> None:
|
128
|
+
"""Initializes the SQLite database schema if not present."""
|
129
|
+
db_path = SQLITE_DB_PATH
|
130
|
+
logger.info(f"Ensuring database schema exists at: {db_path}")
|
131
|
+
try:
|
132
|
+
db_path.parent.mkdir(parents=True, exist_ok=True) # Ensure directory exists
|
133
|
+
conn = sqlite3.connect(db_path)
|
134
|
+
cursor = conn.cursor()
|
135
|
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='services';")
|
136
|
+
if not cursor.fetchone():
|
137
|
+
logger.info("Initializing 'services' table in SQLite database.")
|
138
|
+
cursor.execute("""
|
139
|
+
CREATE TABLE services (
|
140
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
141
|
+
name TEXT NOT NULL UNIQUE,
|
142
|
+
type TEXT NOT NULL,
|
143
|
+
url TEXT,
|
144
|
+
api_key TEXT,
|
145
|
+
usage_limits TEXT,
|
146
|
+
documentation_link TEXT,
|
147
|
+
last_checked TEXT
|
148
|
+
);
|
149
|
+
""")
|
150
|
+
conn.commit()
|
151
|
+
logger.info("'services' table created.")
|
152
|
+
else:
|
153
|
+
logger.debug("'services' table already exists.")
|
154
|
+
conn.close()
|
155
|
+
except sqlite3.Error as e:
|
156
|
+
logger.error(f"SQLite error during DB initialization: {e}", exc_info=True)
|
157
|
+
# Depending on severity, you might want to raise this
|
158
|
+
# raise RuntimeError(f"Failed to initialize database: {e}") from e
|
159
|
+
except Exception as e:
|
160
|
+
logger.error(f"Unexpected error during DB initialization: {e}", exc_info=True)
|
161
|
+
# raise RuntimeError(f"Failed to initialize database: {e}") from e
|
162
|
+
|
163
|
+
|
164
|
+
# --- Model Instantiation Helper --- (Standard helper)
|
165
|
+
def _get_model_instance(self, profile_name: str) -> Model:
|
166
|
+
"""Retrieves or creates an LLM Model instance."""
|
167
|
+
# ... (Implementation is the same as previous refactors) ...
|
168
|
+
if profile_name in self._model_instance_cache:
|
169
|
+
logger.debug(f"Using cached Model instance for profile '{profile_name}'.")
|
170
|
+
return self._model_instance_cache[profile_name]
|
171
|
+
logger.debug(f"Creating new Model instance for profile '{profile_name}'.")
|
172
|
+
profile_data = self.get_llm_profile(profile_name)
|
173
|
+
if not profile_data: raise ValueError(f"Missing LLM profile '{profile_name}'.")
|
174
|
+
provider = profile_data.get("provider", "openai").lower()
|
175
|
+
model_name = profile_data.get("model")
|
176
|
+
if not model_name: raise ValueError(f"Missing 'model' in profile '{profile_name}'.")
|
177
|
+
if provider != "openai": raise ValueError(f"Unsupported provider: {provider}")
|
178
|
+
client_cache_key = f"{provider}_{profile_data.get('base_url')}"
|
179
|
+
if client_cache_key not in self._openai_client_cache:
|
180
|
+
client_kwargs = { "api_key": profile_data.get("api_key"), "base_url": profile_data.get("base_url") }
|
181
|
+
filtered_kwargs = {k: v for k, v in client_kwargs.items() if v is not None}
|
182
|
+
log_kwargs = {k:v for k,v in filtered_kwargs.items() if k != 'api_key'}
|
183
|
+
logger.debug(f"Creating new AsyncOpenAI client for '{profile_name}': {log_kwargs}")
|
184
|
+
try: self._openai_client_cache[client_cache_key] = AsyncOpenAI(**filtered_kwargs)
|
185
|
+
except Exception as e: raise ValueError(f"Failed to init client: {e}") from e
|
186
|
+
client = self._openai_client_cache[client_cache_key]
|
187
|
+
logger.debug(f"Instantiating OpenAIChatCompletionsModel(model='{model_name}') for '{profile_name}'.")
|
188
|
+
try:
|
189
|
+
model_instance = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
|
190
|
+
self._model_instance_cache[profile_name] = model_instance
|
191
|
+
return model_instance
|
192
|
+
except Exception as e: raise ValueError(f"Failed to init LLM: {e}") from e
|
193
|
+
|
194
|
+
|
195
|
+
def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
|
196
|
+
"""Creates the WTF agent hierarchy and returns Valory (Coordinator)."""
|
197
|
+
self.initialize_db() # Ensure DB is ready
|
198
|
+
|
199
|
+
logger.debug("Creating WhiskeyTangoFoxtrot agent team...")
|
200
|
+
self._model_instance_cache = {}
|
201
|
+
self._openai_client_cache = {}
|
202
|
+
|
203
|
+
default_profile_name = self.config.get("llm_profile", "default")
|
204
|
+
logger.debug(f"Using LLM profile '{default_profile_name}' for WTF agents.")
|
205
|
+
model_instance = self._get_model_instance(default_profile_name)
|
206
|
+
|
207
|
+
# Helper to filter started MCP servers
|
208
|
+
def get_agent_mcps(names: List[str]) -> List[MCPServer]:
|
209
|
+
started_names = {s.name for s in mcp_servers}
|
210
|
+
required_found = [name for name in names if name in started_names]
|
211
|
+
if len(required_found) != len(names):
|
212
|
+
missing = set(names) - started_names
|
213
|
+
logger.warning(f"Agent needing {names} is missing started MCP(s): {', '.join(missing)}")
|
214
|
+
return [s for s in mcp_servers if s.name in required_found]
|
215
|
+
|
216
|
+
# Instantiate all agents first
|
217
|
+
agents: Dict[str, Agent] = {}
|
218
|
+
|
219
|
+
agents["Larry"] = Agent(name="Larry", model=model_instance, instructions=larry_instructions, tools=[], mcp_servers=get_agent_mcps(["filesystem"]))
|
220
|
+
agents["Kriegs"] = Agent(name="Kriegs", model=model_instance, instructions=kriegs_instructions, tools=[], mcp_servers=get_agent_mcps(["sqlite"]))
|
221
|
+
agents["Vanna"] = Agent(name="Vanna", model=model_instance, instructions=vanna_instructions, tools=[], mcp_servers=get_agent_mcps(["brave-search", "mcp-npx-fetch"]))
|
222
|
+
agents["Marcher"] = Agent(name="Marcher", model=model_instance, instructions=marcher_instructions, tools=[], mcp_servers=get_agent_mcps(["mcp-doc-forge"]))
|
223
|
+
|
224
|
+
agents["Tyril"] = Agent(
|
225
|
+
name="Tyril", model=model_instance, instructions=tyril_instructions,
|
226
|
+
tools=[ # Tools for delegating to minions
|
227
|
+
agents["Larry"].as_tool(tool_name="Larry", tool_description="Delegate filesystem tasks (temp files)."),
|
228
|
+
agents["Kriegs"].as_tool(tool_name="Kriegs", tool_description="Delegate SQLite database operations (CRUD).")
|
229
|
+
],
|
230
|
+
mcp_servers=get_agent_mcps(["sqlite"]) # Tyril might read DB directly
|
231
|
+
)
|
232
|
+
agents["Tray"] = Agent(
|
233
|
+
name="Tray", model=model_instance, instructions=tray_instructions,
|
234
|
+
tools=[ # Tools for delegating to minions
|
235
|
+
agents["Vanna"].as_tool(tool_name="Vanna", tool_description="Delegate web search/fetch tasks."),
|
236
|
+
agents["Marcher"].as_tool(tool_name="Marcher", tool_description="Delegate processing/structuring of fetched web data.")
|
237
|
+
],
|
238
|
+
mcp_servers=[] # Tray coordinates web minions
|
239
|
+
)
|
240
|
+
|
241
|
+
agents["Valory"] = Agent(
|
242
|
+
name="Valory", model=model_instance, instructions=valory_instructions,
|
243
|
+
tools=[ # Tools for delegating to middle managers
|
244
|
+
agents["Tyril"].as_tool(tool_name="Tyril", tool_description="Delegate database and filesystem management tasks."),
|
245
|
+
agents["Tray"].as_tool(tool_name="Tray", tool_description="Delegate web data fetching and processing tasks.")
|
246
|
+
],
|
247
|
+
mcp_servers=[] # Coordinator doesn't directly use MCPs
|
248
|
+
)
|
249
|
+
|
250
|
+
logger.debug("WhiskeyTangoFoxtrot agents created. Starting with Valory.")
|
251
|
+
return agents["Valory"]
|
252
|
+
|
253
|
+
# Standard Python entry point
|
254
|
+
if __name__ == "__main__":
|
255
|
+
WhiskeyTangoFoxtrotBlueprint.main()
|
256
|
+
|
@@ -1,36 +1,51 @@
|
|
1
1
|
"""
|
2
|
-
Blueprint
|
2
|
+
Blueprint Extension Package for Open Swarm.
|
3
|
+
|
4
|
+
Provides the base class, discovery mechanisms, and utilities for creating
|
5
|
+
and running autonomous agent workflows (blueprints).
|
3
6
|
"""
|
4
7
|
|
8
|
+
# Core components
|
5
9
|
from .blueprint_base import BlueprintBase
|
6
10
|
from .blueprint_discovery import discover_blueprints
|
7
11
|
from .blueprint_utils import filter_blueprints
|
8
12
|
|
9
|
-
#
|
10
|
-
|
11
|
-
|
13
|
+
# Helper modules (primarily used internally by BlueprintBase or CLI)
|
14
|
+
from . import config_loader
|
15
|
+
from . import cli_handler
|
16
|
+
# from . import interactive_mode # If interactive mode is refactored out
|
17
|
+
# from . import output_utils # If output utils are used externally
|
18
|
+
|
19
|
+
# Re-export essential message utilities if they are part of the public API
|
20
|
+
# of this extension package. If they are purely internal utilities,
|
21
|
+
# they don't necessarily need to be re-exported here.
|
12
22
|
try:
|
13
23
|
from swarm.utils.message_sequence import repair_message_payload, validate_message_sequence
|
14
24
|
from swarm.utils.context_utils import truncate_message_history
|
15
|
-
# If specific old truncation functions are truly needed, they'd have to be
|
16
|
-
# re-implemented or their callers refactored to use truncate_message_history.
|
17
|
-
# Assuming truncate_message_history is the intended replacement for now.
|
18
|
-
# Define aliases if old names are required by downstream code:
|
19
|
-
# truncate_preserve_pairs = truncate_message_history # Example if needed
|
20
25
|
except ImportError as e:
|
21
|
-
# Log an error or warning if imports fail, helpful for debugging setup issues
|
22
26
|
import logging
|
23
|
-
logging.getLogger(__name__).
|
24
|
-
# Define dummy functions or
|
25
|
-
def repair_message_payload(m, **kwargs):
|
26
|
-
def validate_message_sequence(m):
|
27
|
-
def truncate_message_history(m, *args, **kwargs):
|
27
|
+
logging.getLogger(__name__).warning(f"Could not import core message utilities: {e}")
|
28
|
+
# Define dummy functions or let importers handle the ImportError
|
29
|
+
def repair_message_payload(m, **kwargs): raise NotImplementedError from e
|
30
|
+
def validate_message_sequence(m): raise NotImplementedError from e
|
31
|
+
def truncate_message_history(m, *args, **kwargs): raise NotImplementedError from e
|
32
|
+
|
28
33
|
|
29
34
|
__all__ = [
|
35
|
+
# Core
|
30
36
|
"BlueprintBase",
|
31
37
|
"discover_blueprints",
|
32
38
|
"filter_blueprints",
|
39
|
+
|
40
|
+
# Helper Modules (Exporting for potential external use, though less common)
|
41
|
+
"config_loader",
|
42
|
+
"cli_handler",
|
43
|
+
# "interactive_mode",
|
44
|
+
# "output_utils",
|
45
|
+
|
46
|
+
# Utility Functions (If considered part of the public API)
|
33
47
|
"repair_message_payload",
|
34
48
|
"validate_message_sequence",
|
35
49
|
"truncate_message_history",
|
36
50
|
]
|
51
|
+
|