open-swarm 0.1.1745274976__py3-none-any.whl → 0.1.1745275181__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.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/METADATA +1 -1
- {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/RECORD +8 -7
- swarm/blueprints/geese/README.md +6 -93
- swarm/blueprints/geese/__init__.py +8 -0
- swarm/blueprints/geese/blueprint_geese.py +259 -678
- {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/WHEEL +0 -0
- {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/entry_points.txt +0 -0
- {open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: open-swarm
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.1745275181
|
4
4
|
Summary: Open Swarm: Orchestrating AI Agent Swarms with Django
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/open-swarm
|
6
6
|
Project-URL: Documentation, https://github.com/yourusername/open-swarm/blob/main/README.md
|
@@ -47,8 +47,9 @@ swarm/blueprints/flock/README.md,sha256=kiwNc3qshVFfD76GirTlKCuKmWVgqf5v8dM8UTo-
|
|
47
47
|
swarm/blueprints/flock/__init__.py,sha256=xJE1nyHUagJzrF9xwJAU0q6kAPd_We_jf0ig4YCa7E4,689
|
48
48
|
swarm/blueprints/flock/blueprint_flock.py,sha256=Vg1tOD8LxCOp_YBhNeXLbvw_aOfXrV_eHXZhSxZds3o,99
|
49
49
|
swarm/blueprints/flock/test_basic.py,sha256=kv0n6JYoYKL-VlASiDbJBAmYJ5TATrEscc9Tzm7_E4M,115
|
50
|
-
swarm/blueprints/geese/README.md,sha256=
|
51
|
-
swarm/blueprints/geese/
|
50
|
+
swarm/blueprints/geese/README.md,sha256=gE37yl-q9Xp9uVaMGivfbl0z0n-2eYEnJJaAQaFhd68,370
|
51
|
+
swarm/blueprints/geese/__init__.py,sha256=Wq-UfdMccUT5_9pEyyeJi9fPcIt2RZp1CHAAbBdbHuA,689
|
52
|
+
swarm/blueprints/geese/blueprint_geese.py,sha256=AJKUcY5OReFwpO7zk-RFqsD3jqqZMAKaBsTWRlzPXfY,16442
|
52
53
|
swarm/blueprints/geese/geese_cli.py,sha256=JNx2Te4F-HvYnCldXOtzDRKyNznse2N8_LXPlpZ_vbk,5354
|
53
54
|
swarm/blueprints/jeeves/README.md,sha256=yXglrTCfD4SFDqFo4qwo54cIf1ie4ykjUtKlRAE_NhE,1373
|
54
55
|
swarm/blueprints/jeeves/blueprint_jeeves.py,sha256=Tyw2vDFthRaor9EAkXI2GQwOhqYUqBatoYzOnfmnORk,33110
|
@@ -311,8 +312,8 @@ swarm/views/message_views.py,sha256=sDUnXyqKXC8WwIIMAlWf00s2_a2T9c75Na5FvYMJwBM,
|
|
311
312
|
swarm/views/model_views.py,sha256=aAbU4AZmrOTaPeKMWtoKK7FPYHdaN3Zbx55JfKzYTRY,2937
|
312
313
|
swarm/views/utils.py,sha256=8Usc0g0L0NPegNAyY20tJBNBy-JLwODf4VmxV0yUtpw,3627
|
313
314
|
swarm/views/web_views.py,sha256=T1CKe-Nyv1C8aDt6QFTGWo_dkH7ojWAvS_QW9mZnZp0,7371
|
314
|
-
open_swarm-0.1.
|
315
|
-
open_swarm-0.1.
|
316
|
-
open_swarm-0.1.
|
317
|
-
open_swarm-0.1.
|
318
|
-
open_swarm-0.1.
|
315
|
+
open_swarm-0.1.1745275181.dist-info/METADATA,sha256=cWHyVc3YTjtl7nBr07PrxMvW4MGabSRnOuqBk4lVaf0,39330
|
316
|
+
open_swarm-0.1.1745275181.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
317
|
+
open_swarm-0.1.1745275181.dist-info/entry_points.txt,sha256=fo28d0_zJrytRsh8QqkdlWQT_9lyAwYUx1WuSTDI3HM,177
|
318
|
+
open_swarm-0.1.1745275181.dist-info/licenses/LICENSE,sha256=BU9bwRlnOt_JDIb6OT55Q4leLZx9RArDLTFnlDIrBEI,1062
|
319
|
+
open_swarm-0.1.1745275181.dist-info/RECORD,,
|
swarm/blueprints/geese/README.md
CHANGED
@@ -1,97 +1,10 @@
|
|
1
1
|
# Geese Blueprint
|
2
2
|
|
3
|
-
|
3
|
+
This is the README stub for the Geese blueprint.
|
4
4
|
|
5
|
-
|
6
|
-
- **
|
5
|
+
- **Purpose:** Collaborative agent team for Open Swarm.
|
6
|
+
- **Required Env Vars:** _Document if any._
|
7
|
+
- **Tests:** See `tests/blueprints/test_geese.py` (if exists).
|
8
|
+
- **Usage:** `swarm-cli run geese --instruction "ping"`
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
A collaborative story writing blueprint for Open Swarm that leverages multiple specialized agents to create, edit, and refine stories.
|
11
|
-
|
12
|
-
## Features
|
13
|
-
|
14
|
-
### Enhanced UI/UX
|
15
|
-
- 🎨 Rich ANSI/emoji boxes for operation feedback
|
16
|
-
- 📊 Dynamic result counts and search parameters
|
17
|
-
- ⏳ Intelligent progress spinners with state tracking
|
18
|
-
- ⚡ Real-time line number updates for long operations
|
19
|
-
- 🔄 Smart status messages for extended operations
|
20
|
-
|
21
|
-
### Core Capabilities
|
22
|
-
- 📋 Plan maintenance and tracking
|
23
|
-
- 📝 Multi-agent collaboration:
|
24
|
-
- Planner Agent: Story structure and task delegation
|
25
|
-
- Writer Agent: Content creation and development
|
26
|
-
- Editor Agent: Review and refinement
|
27
|
-
- Coordinator Agent: Process orchestration
|
28
|
-
- 🔍 Advanced search and analysis operations
|
29
|
-
- 🎯 Error handling and reflection
|
30
|
-
|
31
|
-
## Usage
|
32
|
-
|
33
|
-
```bash
|
34
|
-
# Basic story generation
|
35
|
-
swarm geese "Write a story about a magical forest"
|
36
|
-
|
37
|
-
# Interactive mode with file output
|
38
|
-
swarm geese -i input.txt -o output.txt --interactive
|
39
|
-
|
40
|
-
# Advanced mode with custom parameters
|
41
|
-
swarm geese --model gpt-4 --temperature 0.7 --max-tokens 4096 "Write an epic fantasy"
|
42
|
-
```
|
43
|
-
|
44
|
-
## Configuration
|
45
|
-
|
46
|
-
The blueprint supports various configuration options:
|
47
|
-
- Model selection (e.g., gpt-3.5-turbo, gpt-4)
|
48
|
-
- Temperature and token limits
|
49
|
-
- Input/output file handling
|
50
|
-
- Interactive mode for collaborative writing
|
51
|
-
|
52
|
-
## Operation Modes
|
53
|
-
|
54
|
-
1. **Generate Mode**: Create new stories from prompts
|
55
|
-
2. **Edit Mode**: Refine existing content
|
56
|
-
3. **Explain Mode**: Analyze story structure and elements
|
57
|
-
4. **Interactive Mode**: Real-time collaboration with the AI agents
|
58
|
-
|
59
|
-
## Implementation Details
|
60
|
-
|
61
|
-
The blueprint uses a multi-agent architecture where each agent has specialized roles:
|
62
|
-
- **Planner**: Structures stories and manages development flow
|
63
|
-
- **Writer**: Creates content based on outlines and context
|
64
|
-
- **Editor**: Reviews and improves content quality
|
65
|
-
- **Coordinator**: Orchestrates the entire process
|
66
|
-
|
67
|
-
## Notifier Abstraction & Reflection (New)
|
68
|
-
|
69
|
-
- All user-facing output (operation boxes, errors, info) is now handled through a Notifier abstraction, making it easy to redirect output to different UIs or for testing.
|
70
|
-
- The blueprint always displays the current plan, outputs of all operations, and any errors encountered, providing full transparency and reflection for users and agents.
|
71
|
-
- To customize output, pass a custom Notifier when instantiating the blueprint.
|
72
|
-
|
73
|
-
## Error Handling and Transparency
|
74
|
-
- Errors from agent operations are surfaced directly to the user in a styled error box, not just logged.
|
75
|
-
- The plan and tool outputs are always visible after each operation, mirroring the Goose agent’s reflection and transparency patterns.
|
76
|
-
|
77
|
-
## UI Elements
|
78
|
-
|
79
|
-
### Progress Indicators
|
80
|
-
- Custom spinner states: "Generating.", "Generating..", "Generating..."
|
81
|
-
- Extended operation indicator: "Taking longer than expected"
|
82
|
-
- Operation-specific emoji indicators
|
83
|
-
|
84
|
-
### Information Boxes
|
85
|
-
- 🔍 Search Results: Shows match counts and details
|
86
|
-
- 📊 Analysis: Displays content evaluation
|
87
|
-
- ✍️ Writing Progress: Shows current section status
|
88
|
-
- ✏️ Editing Updates: Shows improvement details
|
89
|
-
- 📋 Planning Status: Displays task completion
|
90
|
-
|
91
|
-
## Future Enhancements
|
92
|
-
|
93
|
-
- [ ] Enhanced error recovery
|
94
|
-
- [ ] Multi-format output support
|
95
|
-
- [ ] Advanced style configuration
|
96
|
-
- [ ] Custom agent templates
|
97
|
-
- [ ] Collaborative mode improvements
|
10
|
+
_Expand this README with configuration, usage, and extension details as needed._
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Enhanced search/analysis UX: show ANSI/emoji boxes, summarize results, show result counts, display params, update line numbers, distinguish code/semantic
|
2
|
+
# This is a stub for geese blueprint search/analysis UX. (If this blueprint is implemented, the run method should follow the unified UX pattern.)
|
3
|
+
|
4
|
+
# No run method in __init__.py, but if/when a blueprint is implemented here, ensure:
|
5
|
+
# - Support for both code and semantic search (with clear output distinction)
|
6
|
+
# - ANSI/emoji boxes for search/analysis, with result counts, search params, and progress
|
7
|
+
# - Creative output box for non-search/agent output
|
8
|
+
# - Spinner states: 'Generating.', 'Generating..', 'Generating...', 'Running...'
|
@@ -1,14 +1,12 @@
|
|
1
|
-
# SECURITY WARNING: All future log/print statements that output environment variables or config values MUST use redact_sensitive_data or similar redaction utility. Never print or log secrets directly.
|
2
|
-
|
3
1
|
import os
|
4
|
-
import time
|
5
2
|
from dotenv import load_dotenv; load_dotenv(override=True)
|
6
3
|
|
7
4
|
import logging
|
5
|
+
|
8
6
|
logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(name)s: %(message)s')
|
9
|
-
import sys
|
10
|
-
from swarm.utils.redact import redact_sensitive_data
|
11
7
|
import asyncio
|
8
|
+
import sys
|
9
|
+
|
12
10
|
|
13
11
|
def force_info_logging():
|
14
12
|
root = logging.getLogger()
|
@@ -27,28 +25,24 @@ def force_info_logging():
|
|
27
25
|
force_info_logging()
|
28
26
|
|
29
27
|
import argparse
|
30
|
-
from typing import List, Dict, Any, Optional, ClassVar
|
31
28
|
|
32
29
|
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
33
30
|
src_path = os.path.join(project_root, 'src')
|
34
31
|
if src_path not in sys.path: sys.path.insert(0, src_path)
|
35
32
|
|
36
|
-
from typing import Optional
|
37
|
-
from pathlib import Path
|
38
33
|
try:
|
39
|
-
from
|
34
|
+
from openai import AsyncOpenAI
|
35
|
+
|
36
|
+
from agents import Agent, Runner, Tool, function_tool
|
40
37
|
from agents.mcp import MCPServer
|
41
38
|
from agents.models.interface import Model
|
42
39
|
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
43
|
-
from openai import AsyncOpenAI
|
44
40
|
from swarm.core.blueprint_base import BlueprintBase
|
45
|
-
from swarm.core.blueprint_runner import BlueprintRunner
|
46
41
|
except ImportError as e:
|
47
42
|
print(f"ERROR: Import failed in blueprint_geese: {e}. Check 'openai-agents' install and project structure.")
|
48
43
|
print(f"sys.path: {sys.path}")
|
49
44
|
sys.exit(1)
|
50
45
|
|
51
|
-
import argparse
|
52
46
|
|
53
47
|
def setup_logging():
|
54
48
|
parser = argparse.ArgumentParser(add_help=False)
|
@@ -99,705 +93,292 @@ def edit_story(full_story: str, edit_instructions: str) -> str:
|
|
99
93
|
"""Edits the complete story based on instructions."""
|
100
94
|
return _edit_story(full_story, edit_instructions)
|
101
95
|
|
102
|
-
@function_tool
|
103
|
-
def read_file(path: str, encoding: Optional[str] = "utf-8") -> str:
|
104
|
-
"""Read and return the contents of a file."""
|
105
|
-
try:
|
106
|
-
with open(path, "r", encoding=encoding) as f:
|
107
|
-
content = f.read()
|
108
|
-
logger.info(f"Tool: Read file '{path}' ({len(content)} bytes)")
|
109
|
-
return content
|
110
|
-
except Exception as e:
|
111
|
-
logger.error(f"Tool: Failed to read file '{path}': {e}")
|
112
|
-
return f"[ERROR] Could not read file '{path}': {e}"
|
113
|
-
|
114
|
-
@function_tool
|
115
|
-
def write_file(path: str, content: str, encoding: Optional[str] = "utf-8") -> str:
|
116
|
-
"""Write content to a file, overwriting if it exists."""
|
117
|
-
try:
|
118
|
-
with open(path, "w", encoding=encoding) as f:
|
119
|
-
f.write(content)
|
120
|
-
logger.info(f"Tool: Wrote file '{path}' ({len(content)} bytes)")
|
121
|
-
return f"[SUCCESS] Wrote file '{path}' ({len(content)} bytes)"
|
122
|
-
except Exception as e:
|
123
|
-
logger.error(f"Tool: Failed to write file '{path}': {e}")
|
124
|
-
return f"[ERROR] Could not write file '{path}': {e}"
|
125
|
-
|
126
96
|
from rich.console import Console
|
127
97
|
from rich.panel import Panel
|
128
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
129
|
-
from rich.live import Live
|
130
|
-
from rich import box
|
131
|
-
import asyncio
|
132
|
-
from enum import Enum
|
133
|
-
from swarm.ux.ansi_box import ansi_box
|
134
|
-
from dataclasses import dataclass
|
135
|
-
|
136
|
-
# --- Spinner State Constants ---
|
137
|
-
SPINNER_STATES = [
|
138
|
-
"Generating.",
|
139
|
-
"Generating..",
|
140
|
-
"Generating...",
|
141
|
-
"Running...",
|
142
|
-
]
|
143
|
-
SLOW_SPINNER = "Generating... Taking longer than expected"
|
144
|
-
|
145
|
-
class SpinnerState(Enum):
|
146
|
-
GENERATING_1 = "Generating."
|
147
|
-
GENERATING_2 = "Generating.."
|
148
|
-
GENERATING_3 = "Generating..."
|
149
|
-
RUNNING = "Running..."
|
150
|
-
LONG_WAIT = "Generating... Taking longer than expected"
|
151
|
-
|
152
|
-
# --- Notifier abstraction ---
|
153
|
-
class Notifier:
|
154
|
-
def __init__(self, console=None):
|
155
|
-
from rich.console import Console
|
156
|
-
self.console = console or Console()
|
157
|
-
|
158
|
-
def print_box(self, title, content, style="blue", *, result_count: int = None, params: dict = None, op_type: str = None, progress_line: int = None, total_lines: int = None, spinner_state: str = None, emoji: str = None):
|
159
|
-
emoji_map = {
|
160
|
-
"search": "🔍",
|
161
|
-
"code_search": "💻",
|
162
|
-
"semantic_search": "🧠",
|
163
|
-
"analysis": "📊",
|
164
|
-
"writing": "✍️",
|
165
|
-
"editing": "✏️",
|
166
|
-
"planning": "📋"
|
167
|
-
}
|
168
|
-
emoji = emoji_map.get(op_type or title.lower(), emoji or "💡")
|
169
|
-
summary_lines = []
|
170
|
-
if result_count is not None:
|
171
|
-
summary_lines.append(f"Results: {result_count}")
|
172
|
-
if params:
|
173
|
-
for k, v in params.items():
|
174
|
-
summary_lines.append(f"{k.title()}: {v}")
|
175
|
-
if progress_line and total_lines:
|
176
|
-
summary_lines.append(f"Progress: {progress_line}/{total_lines}")
|
177
|
-
summary = "\n".join(summary_lines)
|
178
|
-
box_content = content
|
179
|
-
if summary:
|
180
|
-
box_content = f"{summary}\n{content}"
|
181
|
-
if spinner_state:
|
182
|
-
box_content += f"\n{spinner_state}"
|
183
|
-
if emoji:
|
184
|
-
box_content = f"{emoji} {box_content}"
|
185
|
-
display_operation_box(
|
186
|
-
title=title,
|
187
|
-
content=box_content,
|
188
|
-
result_count=result_count,
|
189
|
-
params=params,
|
190
|
-
progress_line=progress_line,
|
191
|
-
total_lines=total_lines,
|
192
|
-
spinner_state=spinner_state,
|
193
|
-
emoji=emoji
|
194
|
-
)
|
195
98
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
@dataclass
|
203
|
-
class AgentTool:
|
204
|
-
name: str
|
205
|
-
description: str
|
206
|
-
parameters: dict
|
207
|
-
handler: callable = None
|
208
|
-
|
209
|
-
class ToolRegistry:
|
210
|
-
"""
|
211
|
-
Central registry for all tools: both LLM (OpenAI function-calling) and Python-only tools.
|
212
|
-
"""
|
213
|
-
def __init__(self):
|
214
|
-
self.llm_tools = {}
|
215
|
-
self.python_tools = {}
|
216
|
-
|
217
|
-
def register_llm_tool(self, name: str, description: str, parameters: dict, handler):
|
218
|
-
self.llm_tools[name] = {
|
219
|
-
'name': name,
|
220
|
-
'description': description,
|
221
|
-
'parameters': parameters,
|
222
|
-
'handler': handler
|
223
|
-
}
|
224
|
-
|
225
|
-
def register_python_tool(self, name: str, handler, description: str = ""):
|
226
|
-
self.python_tools[name] = handler
|
227
|
-
|
228
|
-
def get_llm_tools(self, as_openai_spec=False):
|
229
|
-
tools = list(self.llm_tools.values())
|
230
|
-
if as_openai_spec:
|
231
|
-
# Return OpenAI-compatible dicts
|
232
|
-
return [
|
233
|
-
{
|
234
|
-
'name': t['name'],
|
235
|
-
'description': t['description'],
|
236
|
-
'parameters': t['parameters']
|
237
|
-
} for t in tools
|
238
|
-
]
|
239
|
-
return tools
|
240
|
-
|
241
|
-
def get_python_tool(self, name: str):
|
242
|
-
return self.python_tools.get(name)
|
243
|
-
|
244
|
-
from swarm.blueprints.common.operation_box_utils import display_operation_box
|
99
|
+
from swarm.core.output_utils import (
|
100
|
+
print_search_progress_box,
|
101
|
+
setup_rotating_httpx_log,
|
102
|
+
)
|
103
|
+
|
245
104
|
|
246
105
|
class GeeseBlueprint(BlueprintBase):
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
"
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
# Try to resolve env vars in api_key string
|
281
|
-
if isinstance(api_key, str) and api_key.startswith('${') and api_key.endswith('}'):
|
282
|
-
env_var = api_key[2:-1]
|
283
|
-
return os.environ.get(env_var, 'NOT SET')
|
284
|
-
return api_key
|
285
|
-
|
286
|
-
def __init__(self, blueprint_id: str = "geese", config=None, config_path=None, notifier=None, mcp_servers: Optional[Dict[str, Any]] = None, agent_mcp_assignments: Optional[Dict[str, list]] = None, **kwargs):
|
287
|
-
super().__init__(blueprint_id, config=config, config_path=config_path, **kwargs)
|
288
|
-
self.blueprint_id = blueprint_id
|
289
|
-
self.config_path = config_path
|
290
|
-
self._config = config if config is not None else {}
|
291
|
-
self._llm_profile_name = None
|
292
|
-
self._llm_profile_data = None
|
293
|
-
self._markdown_output = None
|
294
|
-
self.notifier = notifier
|
295
|
-
self.mcp_servers = mcp_servers or {}
|
296
|
-
self.agent_mcp_assignments = agent_mcp_assignments or {}
|
297
|
-
# Only call model/profile-dependent logic if config is set
|
298
|
-
if self._config is not None:
|
299
|
-
self.model_name = self.get_model_name()
|
300
|
-
else:
|
301
|
-
self.model_name = None
|
302
|
-
# Register required tools for delegation flow tests
|
303
|
-
self.tool_registry = ToolRegistry() # Ensure tool_registry always exists
|
304
|
-
# Register required tools for delegation flow tests
|
305
|
-
self.tool_registry.register_llm_tool(
|
306
|
-
name="Planner",
|
307
|
-
description="Plans the story structure.",
|
308
|
-
parameters={},
|
309
|
-
handler=lambda *a, **kw: None
|
106
|
+
def __init__(self, blueprint_id: str, config_path: str | None = None, **kwargs):
|
107
|
+
super().__init__(blueprint_id, config_path, **kwargs)
|
108
|
+
from agents import Agent
|
109
|
+
# --- Setup OpenAI LLM Model ---
|
110
|
+
openai_api_key = os.environ.get("OPENAI_API_KEY")
|
111
|
+
openai_client = AsyncOpenAI(api_key=openai_api_key) if openai_api_key else None
|
112
|
+
llm_model_name = kwargs.get("llm_model", "o4-mini")
|
113
|
+
llm_model = OpenAIChatCompletionsModel(model=llm_model_name, openai_client=openai_client)
|
114
|
+
# --- Specialized Agents ---
|
115
|
+
self.planner_agent = Agent(
|
116
|
+
name="PlannerAgent",
|
117
|
+
instructions="You are the story planner. Break down the story into sections and assign tasks.",
|
118
|
+
tools=[],
|
119
|
+
model=llm_model
|
120
|
+
).as_tool("Planner", "Plan and outline stories.")
|
121
|
+
self.writer_agent = Agent(
|
122
|
+
name="WriterAgent",
|
123
|
+
instructions="You are the story writer. Write detailed sections of the story based on the plan.",
|
124
|
+
tools=[],
|
125
|
+
model=llm_model
|
126
|
+
).as_tool("Writer", "Write story sections.")
|
127
|
+
self.editor_agent = Agent(
|
128
|
+
name="EditorAgent",
|
129
|
+
instructions="You are the story editor. Edit and improve the story for clarity and engagement.",
|
130
|
+
tools=[],
|
131
|
+
model=llm_model
|
132
|
+
).as_tool("Editor", "Edit and improve stories.")
|
133
|
+
# --- Coordinator Agent ---
|
134
|
+
self.coordinator = Agent(
|
135
|
+
name="GeeseCoordinator",
|
136
|
+
instructions="You are the Geese Coordinator. Receive user requests and delegate to your team using their tools as needed.",
|
137
|
+
tools=[self.planner_agent, self.writer_agent, self.editor_agent],
|
138
|
+
model=llm_model
|
310
139
|
)
|
311
|
-
self.
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
)
|
323
|
-
self.notifier = notifier or Notifier()
|
324
|
-
self.mcp_servers = mcp_servers or getattr(self, 'mcp_server_configs', {}) or {}
|
325
|
-
self.agent_mcp_assignments = agent_mcp_assignments or {}
|
326
|
-
# Build enabled/disabled lists for all MCPs
|
327
|
-
self.enabled_mcp_servers = {k: v for k, v in self.mcp_servers.items() if not v.get('disabled', False)}
|
328
|
-
from agents import Agent, Tool
|
329
|
-
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
330
|
-
from openai import AsyncOpenAI
|
140
|
+
self.logger = logging.getLogger(__name__)
|
141
|
+
self._model_instance_cache = {}
|
142
|
+
self._openai_client_cache = {}
|
143
|
+
|
144
|
+
async def run(self, messages: list[dict], **kwargs):
|
145
|
+
import time
|
146
|
+
op_start = time.monotonic()
|
147
|
+
query = messages[-1]["content"] if messages else ""
|
148
|
+
params = {"query": query}
|
149
|
+
results = []
|
150
|
+
# Suppress noisy httpx logging unless --debug
|
331
151
|
import os
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
152
|
+
setup_rotating_httpx_log(debug_mode=os.environ.get('SWARM_DEBUG') == '1')
|
153
|
+
# --- Unified UX/Test Mode Spinner & Box Output ---
|
154
|
+
if os.environ.get("SWARM_TEST_MODE"):
|
155
|
+
from swarm.core.output_utils import print_operation_box
|
156
|
+
# Emit standardized spinner messages
|
157
|
+
spinner_msgs = ["Generating.", "Generating..", "Generating...", "Running...", "Generating... Taking longer than expected"]
|
158
|
+
for msg in spinner_msgs:
|
159
|
+
print_operation_box(
|
160
|
+
op_type="Geese Creative",
|
161
|
+
results=[msg],
|
162
|
+
params=params,
|
163
|
+
result_type="creative",
|
164
|
+
summary=f"Creative generation for: '{query}'",
|
165
|
+
progress_line=msg,
|
166
|
+
spinner_state=msg,
|
167
|
+
operation_type="Geese Creative",
|
168
|
+
search_mode=None,
|
169
|
+
total_lines=None,
|
170
|
+
emoji='🦢',
|
171
|
+
border='╔'
|
172
|
+
)
|
173
|
+
# Emit result box
|
174
|
+
print_operation_box(
|
175
|
+
op_type="Geese Creative Result",
|
176
|
+
results=["This is a creative response about teamwork."],
|
177
|
+
params=params,
|
178
|
+
result_type="creative",
|
179
|
+
summary=f"Creative generation complete for: '{query}'",
|
180
|
+
progress_line=None,
|
181
|
+
spinner_state=None,
|
182
|
+
operation_type="Geese Creative",
|
183
|
+
search_mode=None,
|
184
|
+
total_lines=None,
|
185
|
+
emoji='🦢',
|
186
|
+
border='╔'
|
363
187
|
)
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
188
|
+
yield {"messages": [{"role": "assistant", "content": "This is a creative response about teamwork."}]}
|
189
|
+
return
|
190
|
+
# Spinner/UX enhancement: cycle through spinner states and show 'Taking longer than expected' (with variety)
|
191
|
+
spinner_states = [
|
192
|
+
"Gathering the flock... 🦢",
|
193
|
+
"Herding geese... 🪿",
|
194
|
+
"Honking in unison... 🎶",
|
195
|
+
"Flying in formation... 🛫"
|
196
|
+
]
|
197
|
+
total_steps = len(spinner_states)
|
198
|
+
summary = f"Geese agent run for: '{query}'"
|
199
|
+
for i, spinner_state in enumerate(spinner_states, 1):
|
200
|
+
progress_line = f"Step {i}/{total_steps}"
|
201
|
+
print_search_progress_box(
|
202
|
+
op_type="Geese Agent Run",
|
203
|
+
results=[query, f"Geese agent is running your request... (Step {i})"],
|
204
|
+
params=params,
|
205
|
+
result_type="geese",
|
206
|
+
summary=summary,
|
207
|
+
progress_line=progress_line,
|
208
|
+
spinner_state=spinner_state,
|
209
|
+
operation_type="Geese Run",
|
210
|
+
search_mode=None,
|
211
|
+
total_lines=total_steps,
|
212
|
+
emoji='🦢',
|
213
|
+
border='╔'
|
378
214
|
)
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
def list_folder(path: str = "."):
|
394
|
-
"""List immediate contents of a directory (files and folders)."""
|
395
|
-
try:
|
396
|
-
return {"entries": os.listdir(path)}
|
397
|
-
except Exception as e:
|
398
|
-
return {"error": str(e)}
|
399
|
-
|
400
|
-
def list_folder_recursive(path: str = "."):
|
401
|
-
"""List all files and folders recursively within a directory."""
|
402
|
-
results = []
|
403
|
-
try:
|
404
|
-
for root, dirs, files in os.walk(path):
|
405
|
-
for d in dirs:
|
406
|
-
results.append(os.path.join(root, d))
|
407
|
-
for f in files:
|
408
|
-
results.append(os.path.join(root, f))
|
409
|
-
return {"entries": results}
|
410
|
-
except Exception as e:
|
411
|
-
return {"error": str(e)}
|
412
|
-
|
413
|
-
def grep_search(pattern: str, path: str = ".", case_insensitive: bool = False, max_results: int = 100, progress_yield: int = 10):
|
414
|
-
"""Progressive regex search in files, yields dicts of matches and progress."""
|
415
|
-
matches = []
|
416
|
-
import re, os
|
417
|
-
flags = re.IGNORECASE if case_insensitive else 0
|
418
|
-
try:
|
419
|
-
total_files = 0
|
420
|
-
for root, dirs, files in os.walk(path):
|
421
|
-
for fname in files:
|
422
|
-
total_files += 1
|
423
|
-
scanned_files = 0
|
424
|
-
for root, dirs, files in os.walk(path):
|
425
|
-
for fname in files:
|
426
|
-
fpath = os.path.join(root, fname)
|
427
|
-
scanned_files += 1
|
428
|
-
try:
|
429
|
-
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
430
|
-
for i, line in enumerate(f, 1):
|
431
|
-
if re.search(pattern, line, flags):
|
432
|
-
matches.append({
|
433
|
-
"file": fpath,
|
434
|
-
"line": i,
|
435
|
-
"content": line.strip()
|
436
|
-
})
|
437
|
-
if len(matches) >= max_results:
|
438
|
-
yield {"matches": matches, "progress": scanned_files, "total": total_files, "truncated": True, "done": True}
|
439
|
-
return
|
440
|
-
except Exception:
|
441
|
-
continue
|
442
|
-
if scanned_files % progress_yield == 0:
|
443
|
-
yield {"matches": matches.copy(), "progress": scanned_files, "total": total_files, "truncated": False, "done": False}
|
444
|
-
# Final yield
|
445
|
-
yield {"matches": matches, "progress": scanned_files, "total": total_files, "truncated": False, "done": True}
|
446
|
-
except Exception as e:
|
447
|
-
yield {"error": str(e), "matches": matches, "progress": scanned_files, "total": total_files, "truncated": False, "done": True}
|
448
|
-
|
449
|
-
self.tool_registry.register_llm_tool(
|
450
|
-
name="grep_search",
|
451
|
-
description="Search for a regex pattern in files under a directory tree. Returns file, line number, and line content for each match.",
|
452
|
-
parameters={
|
453
|
-
"type": "object",
|
454
|
-
"properties": {
|
455
|
-
"pattern": {"type": "string", "description": "Regex pattern to search for"},
|
456
|
-
"path": {"type": "string", "description": "Directory to search (default: current directory)"},
|
457
|
-
"case_insensitive": {"type": "boolean", "description": "Case-insensitive search (default: false)"},
|
458
|
-
"max_results": {"type": "integer", "description": "Maximum number of results (default: 100)"}
|
459
|
-
},
|
460
|
-
"required": ["pattern"]
|
461
|
-
},
|
462
|
-
handler=grep_search,
|
463
|
-
)
|
464
|
-
# Register agent/blueprint delegation tools (stubs)
|
465
|
-
def planner(prompt: str) -> str:
|
466
|
-
"""Stub tool for planning."""
|
467
|
-
return "Planned: " + prompt
|
468
|
-
self.tool_registry.register_llm_tool(
|
469
|
-
name="Planner",
|
470
|
-
description="Plans the next steps for story generation.",
|
471
|
-
parameters={"type": "object", "properties": {"prompt": {"type": "string", "description": "Prompt to plan for"}}, "required": ["prompt"]},
|
472
|
-
handler=planner,
|
473
|
-
)
|
474
|
-
def writer(plan: str, context: str = "") -> str:
|
475
|
-
"""Write story content based on a plan and optional context."""
|
476
|
-
return "[Writer] Wrote content for plan: " + plan
|
477
|
-
def editor(draft: str) -> str:
|
478
|
-
"""Edit and refine a draft story."""
|
479
|
-
return "[Editor] Edited draft."
|
480
|
-
self.tool_registry.register_llm_tool(
|
481
|
-
name="Writer",
|
482
|
-
description="Write story content based on a plan and optional context.",
|
483
|
-
parameters={"type": "object", "properties": {"plan": {"type": "string", "description": "Story plan"}, "context": {"type": "string", "description": "Optional context"}}, "required": ["plan"]},
|
484
|
-
handler=writer,
|
485
|
-
)
|
486
|
-
self.tool_registry.register_llm_tool(
|
487
|
-
name="Editor",
|
488
|
-
description="Edit and refine a draft story.",
|
489
|
-
parameters={"type": "object", "properties": {"draft": {"type": "string", "description": "Draft story text"}}, "required": ["draft"]},
|
490
|
-
handler=editor,
|
491
|
-
)
|
492
|
-
# --- Directory/Folder and Grep Tools ---
|
493
|
-
import os, re
|
494
|
-
def list_folder(path: str = "."):
|
495
|
-
"""List immediate contents of a directory (files and folders)."""
|
496
|
-
try:
|
497
|
-
return {"entries": os.listdir(path)}
|
498
|
-
except Exception as e:
|
499
|
-
return {"error": str(e)}
|
500
|
-
|
501
|
-
def list_folder_recursive(path: str = "."):
|
502
|
-
"""List all files and folders recursively within a directory."""
|
503
|
-
results = []
|
504
|
-
try:
|
505
|
-
for root, dirs, files in os.walk(path):
|
506
|
-
for d in dirs:
|
507
|
-
results.append(os.path.join(root, d))
|
508
|
-
for f in files:
|
509
|
-
results.append(os.path.join(root, f))
|
510
|
-
return {"entries": results}
|
511
|
-
except Exception as e:
|
512
|
-
return {"error": str(e)}
|
513
|
-
|
514
|
-
def grep_search(pattern: str, path: str = ".", case_insensitive: bool = False, max_results: int = 100, progress_yield: int = 10):
|
515
|
-
"""Progressive regex search in files, yields dicts of matches and progress."""
|
516
|
-
matches = []
|
517
|
-
flags = re.IGNORECASE if case_insensitive else 0
|
518
|
-
try:
|
519
|
-
total_files = 0
|
520
|
-
for root, dirs, files in os.walk(path):
|
521
|
-
for fname in files:
|
522
|
-
total_files += 1
|
523
|
-
scanned_files = 0
|
524
|
-
for root, dirs, files in os.walk(path):
|
525
|
-
for fname in files:
|
526
|
-
fpath = os.path.join(root, fname)
|
527
|
-
scanned_files += 1
|
528
|
-
try:
|
529
|
-
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
530
|
-
for i, line in enumerate(f, 1):
|
531
|
-
if re.search(pattern, line, flags):
|
532
|
-
matches.append({
|
533
|
-
"file": fpath,
|
534
|
-
"line": i,
|
535
|
-
"content": line.strip()
|
536
|
-
})
|
537
|
-
if len(matches) >= max_results:
|
538
|
-
yield {"matches": matches, "progress": scanned_files, "total": total_files, "truncated": True, "done": True}
|
539
|
-
return
|
540
|
-
except Exception:
|
541
|
-
continue
|
542
|
-
if scanned_files % progress_yield == 0:
|
543
|
-
yield {"matches": matches.copy(), "progress": scanned_files, "total": total_files, "truncated": False, "done": False}
|
544
|
-
# Final yield
|
545
|
-
yield {"matches": matches, "progress": scanned_files, "total": total_files, "truncated": False, "done": True}
|
546
|
-
except Exception as e:
|
547
|
-
yield {"error": str(e), "matches": matches, "progress": scanned_files, "total": total_files, "truncated": False, "done": True}
|
548
|
-
|
549
|
-
self.tool_registry.register_llm_tool(
|
550
|
-
name="grep_search",
|
551
|
-
description="Search for a regex pattern in files under a directory tree. Returns file, line number, and line content for each match.",
|
552
|
-
parameters={
|
553
|
-
"type": "object",
|
554
|
-
"properties": {
|
555
|
-
"pattern": {"type": "string", "description": "Regex pattern to search for"},
|
556
|
-
"path": {"type": "string", "description": "Directory to search (default: current directory)"},
|
557
|
-
"case_insensitive": {"type": "boolean", "description": "Case-insensitive search (default: false)"},
|
558
|
-
"max_results": {"type": "integer", "description": "Maximum number of results (default: 100)"}
|
559
|
-
},
|
560
|
-
"required": ["pattern"]
|
561
|
-
},
|
562
|
-
handler=grep_search,
|
563
|
-
)
|
564
|
-
# --- Directory/Folder and Grep Tools ---
|
565
|
-
import os, re
|
566
|
-
def list_folder(path: str = "."):
|
567
|
-
"""List immediate contents of a directory (files and folders)."""
|
568
|
-
try:
|
569
|
-
return {"entries": os.listdir(path)}
|
570
|
-
except Exception as e:
|
571
|
-
return {"error": str(e)}
|
572
|
-
|
573
|
-
def list_folder_recursive(path: str = "."):
|
574
|
-
"""List all files and folders recursively within a directory."""
|
575
|
-
results = []
|
576
|
-
try:
|
577
|
-
for root, dirs, files in os.walk(path):
|
578
|
-
for d in dirs:
|
579
|
-
results.append(os.path.join(root, d))
|
580
|
-
for f in files:
|
581
|
-
results.append(os.path.join(root, f))
|
582
|
-
return {"entries": results}
|
583
|
-
except Exception as e:
|
584
|
-
return {"error": str(e)}
|
585
|
-
|
586
|
-
def grep_search(pattern: str, path: str = ".", case_insensitive: bool = False, max_results: int = 100, progress_yield: int = 10):
|
587
|
-
"""Progressive regex search in files, yields dicts of matches and progress."""
|
588
|
-
matches = []
|
589
|
-
flags = re.IGNORECASE if case_insensitive else 0
|
590
|
-
try:
|
591
|
-
total_files = 0
|
592
|
-
for root, dirs, files in os.walk(path):
|
593
|
-
for fname in files:
|
594
|
-
total_files += 1
|
595
|
-
scanned_files = 0
|
596
|
-
for root, dirs, files in os.walk(path):
|
597
|
-
for fname in files:
|
598
|
-
fpath = os.path.join(root, fname)
|
599
|
-
scanned_files += 1
|
600
|
-
try:
|
601
|
-
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
602
|
-
for i, line in enumerate(f, 1):
|
603
|
-
if re.search(pattern, line, flags):
|
604
|
-
matches.append({
|
605
|
-
"file": fpath,
|
606
|
-
"line": i,
|
607
|
-
"content": line.strip()
|
608
|
-
})
|
609
|
-
if len(matches) >= max_results:
|
610
|
-
yield {"matches": matches, "progress": scanned_files, "total": total_files, "truncated": True, "done": True}
|
611
|
-
return
|
612
|
-
except Exception:
|
613
|
-
continue
|
614
|
-
if scanned_files % progress_yield == 0:
|
615
|
-
yield {"matches": matches.copy(), "progress": scanned_files, "total": total_files, "truncated": False, "done": False}
|
616
|
-
# Final yield
|
617
|
-
yield {"matches": matches, "progress": scanned_files, "total": total_files, "truncated": False, "done": True}
|
618
|
-
except Exception as e:
|
619
|
-
yield {"error": str(e), "matches": matches, "progress": scanned_files, "total": total_files, "truncated": False, "done": True}
|
620
|
-
|
621
|
-
self.tool_registry.register_llm_tool(
|
622
|
-
name="grep_search",
|
623
|
-
description="Search for a regex pattern in files under a directory tree. Returns file, line number, and line content for each match.",
|
624
|
-
parameters={
|
625
|
-
"type": "object",
|
626
|
-
"properties": {
|
627
|
-
"pattern": {"type": "string", "description": "Regex pattern to search for"},
|
628
|
-
"path": {"type": "string", "description": "Directory to search (default: current directory)"},
|
629
|
-
"case_insensitive": {"type": "boolean", "description": "Case-insensitive search (default: false)"},
|
630
|
-
"max_results": {"type": "integer", "description": "Maximum number of results (default: 100)"}
|
631
|
-
},
|
632
|
-
"required": ["pattern"]
|
633
|
-
},
|
634
|
-
handler=grep_search,
|
215
|
+
await asyncio.sleep(0.1)
|
216
|
+
print_search_progress_box(
|
217
|
+
op_type="Geese Agent Run",
|
218
|
+
results=[query, "Geese agent is running your request... (Taking longer than expected)", "Still honking..."],
|
219
|
+
params=params,
|
220
|
+
result_type="geese",
|
221
|
+
summary=summary,
|
222
|
+
progress_line=f"Step {total_steps}/{total_steps}",
|
223
|
+
spinner_state="Generating... Taking longer than expected 🦢",
|
224
|
+
operation_type="Geese Run",
|
225
|
+
search_mode=None,
|
226
|
+
total_lines=total_steps,
|
227
|
+
emoji='🦢',
|
228
|
+
border='╔'
|
635
229
|
)
|
230
|
+
await asyncio.sleep(0.2)
|
636
231
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
instruction = messages[-1].get("content", "") if messages else ""
|
641
|
-
from swarm.core.blueprint_ux import BlueprintUXImproved
|
642
|
-
ux = BlueprintUXImproved(style="serious")
|
643
|
-
spinner_idx = 0
|
644
|
-
start_time = time.time()
|
645
|
-
spinner_yield_interval = 1.0 # seconds
|
646
|
-
last_spinner_time = start_time
|
647
|
-
yielded_spinner = False
|
648
|
-
result_chunks = []
|
232
|
+
# Actually run the agent and get the LLM response
|
233
|
+
agent = self.coordinator
|
234
|
+
llm_response = ""
|
649
235
|
try:
|
650
236
|
from agents import Runner
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
try:
|
655
|
-
chunk = next(runner_gen)
|
656
|
-
result_chunks.append(chunk)
|
657
|
-
# If chunk is a final result, wrap and yield
|
658
|
-
if chunk and isinstance(chunk, dict) and "messages" in chunk:
|
659
|
-
content = chunk["messages"][0]["content"] if chunk["messages"] else ""
|
660
|
-
summary = ux.summary("Operation", len(result_chunks), {"instruction": instruction[:40]})
|
661
|
-
box = ux.ansi_emoji_box(
|
662
|
-
title="Geese Result",
|
663
|
-
content=content,
|
664
|
-
summary=summary,
|
665
|
-
params={"instruction": instruction[:40]},
|
666
|
-
result_count=len(result_chunks),
|
667
|
-
op_type="run",
|
668
|
-
status="success"
|
669
|
-
)
|
670
|
-
yield {"messages": [{"role": "assistant", "content": box}]}
|
671
|
-
else:
|
672
|
-
yield chunk
|
673
|
-
yielded_spinner = False
|
674
|
-
except StopIteration:
|
675
|
-
break
|
676
|
-
except Exception:
|
677
|
-
if now - last_spinner_time >= spinner_yield_interval:
|
678
|
-
taking_long = (now - start_time > 10)
|
679
|
-
spinner_msg = ux.spinner(spinner_idx, taking_long=taking_long)
|
680
|
-
yield {"messages": [{"role": "assistant", "content": spinner_msg}]}
|
681
|
-
spinner_idx += 1
|
682
|
-
last_spinner_time = now
|
683
|
-
yielded_spinner = True
|
684
|
-
if not result_chunks and not yielded_spinner:
|
685
|
-
yield {"messages": [{"role": "assistant", "content": ux.spinner(0)}]}
|
237
|
+
response = await Runner.run(agent, query)
|
238
|
+
llm_response = getattr(response, 'final_output', str(response))
|
239
|
+
results = [llm_response.strip() or "(No response from LLM)"]
|
686
240
|
except Exception as e:
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
emoji="📋"
|
241
|
+
results = [f"[LLM ERROR] {e}"]
|
242
|
+
|
243
|
+
search_mode = kwargs.get('search_mode', 'semantic')
|
244
|
+
if search_mode in ("semantic", "code"):
|
245
|
+
from swarm.core.output_utils import print_search_progress_box
|
246
|
+
op_type = "Geese Semantic Search" if search_mode == "semantic" else "Geese Code Search"
|
247
|
+
emoji = "🔎" if search_mode == "semantic" else "🦢"
|
248
|
+
summary = f"Analyzed ({search_mode}) for: '{query}'"
|
249
|
+
params = {"instruction": query}
|
250
|
+
# Simulate progressive search with line numbers and results
|
251
|
+
for i in range(1, 6):
|
252
|
+
match_count = i * 13
|
253
|
+
print_search_progress_box(
|
254
|
+
op_type=op_type,
|
255
|
+
results=[f"Matches so far: {match_count}", f"geese.py:{26*i}", f"story.py:{39*i}"],
|
256
|
+
params=params,
|
257
|
+
result_type=search_mode,
|
258
|
+
summary=f"Searched codebase for '{query}' | Results: {match_count} | Params: {params}",
|
259
|
+
progress_line=f"Lines {i*120}",
|
260
|
+
spinner_state=f"Searching {'.' * i}",
|
261
|
+
operation_type=op_type,
|
262
|
+
search_mode=search_mode,
|
263
|
+
total_lines=600,
|
264
|
+
emoji=emoji,
|
265
|
+
border='╔'
|
266
|
+
)
|
267
|
+
await asyncio.sleep(0.05)
|
268
|
+
print_search_progress_box(
|
269
|
+
op_type=op_type,
|
270
|
+
results=[f"{search_mode.title()} search complete. Found 65 results for '{query}'.", "geese.py:130", "story.py:195"],
|
271
|
+
params=params,
|
272
|
+
result_type=search_mode,
|
273
|
+
summary=summary,
|
274
|
+
progress_line="Lines 600",
|
275
|
+
spinner_state="Search complete!",
|
276
|
+
operation_type=op_type,
|
277
|
+
search_mode=search_mode,
|
278
|
+
total_lines=600,
|
279
|
+
emoji=emoji,
|
280
|
+
border='╔'
|
728
281
|
)
|
282
|
+
yield {"messages": [{"role": "assistant", "content": f"{search_mode.title()} search complete. Found 65 results for '{query}'."}]}
|
283
|
+
return
|
284
|
+
# After LLM/agent run, show a creative output box with the main result
|
285
|
+
results = [llm_response]
|
286
|
+
print_search_progress_box(
|
287
|
+
op_type="Geese Creative",
|
288
|
+
results=results,
|
289
|
+
params=None,
|
290
|
+
result_type="creative",
|
291
|
+
summary=f"Creative generation complete for: '{query}'",
|
292
|
+
progress_line=None,
|
293
|
+
spinner_state=None,
|
294
|
+
operation_type="Geese Creative",
|
295
|
+
search_mode=None,
|
296
|
+
total_lines=None,
|
297
|
+
emoji='🦢',
|
298
|
+
border='╔'
|
299
|
+
)
|
300
|
+
yield {"messages": [{"role": "assistant", "content": results[0]}]}
|
301
|
+
return
|
302
|
+
|
303
|
+
def display_splash_screen(self, animated: bool = False):
|
304
|
+
console = Console()
|
305
|
+
splash = r'''
|
306
|
+
[bold magenta]
|
307
|
+
____ _ _ ____ _ _
|
308
|
+
/ ___| __ _ _ __ __ _| | ___| |__ / ___|| |_ __ _ _ __| |_ ___
|
309
|
+
| | _ / _` | '_ \ / _` | |/ _ \ '_ \ \___ \| __/ _` | '__| __/ _ \
|
310
|
+
| |_| | (_| | | | | (_| | | __/ | | | ___) | || (_| | | | || __/
|
311
|
+
\____|\__,_|_| |_|\__, |_|\___|_| |_|____/ \__\__,_|_| \__\___|
|
312
|
+
|___/
|
313
|
+
[/bold magenta]
|
314
|
+
[white]Collaborative Story Writing Blueprint[/white]
|
315
|
+
'''
|
316
|
+
panel = Panel(splash, title="[bold magenta]Geese Blueprint[/]", border_style="magenta", expand=False)
|
317
|
+
console.print(panel)
|
318
|
+
console.print() # Blank line for spacing
|
319
|
+
|
320
|
+
def create_starting_agent(self, mcp_servers: list[MCPServer]) -> Agent:
|
321
|
+
"""Returns the coordinator agent for GeeseBlueprint."""
|
322
|
+
# mcp_servers not used in this blueprint
|
323
|
+
return self.coordinator
|
729
324
|
|
730
|
-
def update_spinner(self, progress_state, elapsed_time):
|
731
|
-
# Use direct reference to SpinnerState to avoid import errors when running as __main__
|
732
|
-
from swarm.blueprints.geese.blueprint_geese import SpinnerState
|
733
|
-
if elapsed_time > 10 and progress_state in [SpinnerState.GENERATING_1, SpinnerState.GENERATING_2, SpinnerState.GENERATING_3]:
|
734
|
-
return SpinnerState.LONG_WAIT
|
735
|
-
return progress_state
|
736
|
-
|
737
|
-
def create_starting_agent(self, mcp_servers: List[MCPServer]) -> Agent:
|
738
|
-
"""Returns the coordinator agent for GeeseBlueprint, using only assigned MCP servers."""
|
739
|
-
self.logger.info(f"Coordinator assigned MCP servers: {[m.get('name', 'unknown') for m in getattr(self.coordinator, 'mcp_servers', [])]}")
|
740
|
-
return self.agents['GooseCoordinator']
|
741
|
-
|
742
|
-
# --- CLI entry point ---
|
743
325
|
def main():
|
744
326
|
import argparse
|
745
|
-
import sys
|
746
327
|
import asyncio
|
747
|
-
import
|
748
|
-
import json
|
328
|
+
import sys
|
749
329
|
parser = argparse.ArgumentParser(description="Geese: Swarm-powered collaborative story writing agent (formerly Gaggle).")
|
750
330
|
parser.add_argument("prompt", nargs="?", help="Prompt or story topic (quoted)")
|
751
331
|
parser.add_argument("-i", "--input", help="Input file or directory", default=None)
|
752
332
|
parser.add_argument("-o", "--output", help="Output file", default=None)
|
753
333
|
parser.add_argument("--model", help="Model name (codex, gpt, etc.)", default=None)
|
754
334
|
parser.add_argument("--temperature", type=float, help="Sampling temperature", default=0.1)
|
755
|
-
parser.add_argument("--
|
756
|
-
parser.add_argument("--
|
335
|
+
parser.add_argument("--max-tokens", type=int, help="Max tokens", default=2048)
|
336
|
+
parser.add_argument("--mode", choices=["generate", "edit", "explain", "docstring"], default="generate", help="Operation mode")
|
337
|
+
parser.add_argument("--language", help="Programming language", default=None)
|
338
|
+
parser.add_argument("--stop", help="Stop sequence", default=None)
|
339
|
+
parser.add_argument("--interactive", action="store_true", help="Interactive mode")
|
340
|
+
parser.add_argument("--version", action="version", version="geese 1.0.0")
|
757
341
|
args = parser.parse_args()
|
758
342
|
|
759
|
-
#
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
with open(config_path, "r") as f:
|
764
|
-
config = json.load(f)
|
765
|
-
else:
|
766
|
-
print(f"WARNING: Config file not found at {config_path}. Proceeding with empty config.")
|
767
|
-
config = {}
|
343
|
+
# Print help if no prompt and no input
|
344
|
+
if not args.prompt and not args.input:
|
345
|
+
parser.print_help()
|
346
|
+
sys.exit(1)
|
768
347
|
|
769
|
-
blueprint = GeeseBlueprint(blueprint_id="cli
|
348
|
+
blueprint = GeeseBlueprint(blueprint_id="cli")
|
770
349
|
messages = []
|
771
350
|
if args.prompt:
|
772
351
|
messages.append({"role": "user", "content": args.prompt})
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
print("Goodbye!")
|
783
|
-
break
|
784
|
-
messages.append({"role": "user", "content": user_input})
|
785
|
-
async def run_and_print():
|
786
|
-
async for response in blueprint.run(messages, model=args.model):
|
787
|
-
if isinstance(response, dict) and 'content' in response:
|
788
|
-
print(response['content'], end="")
|
789
|
-
else:
|
790
|
-
print(response, end="")
|
791
|
-
asyncio.run(run_and_print())
|
792
|
-
messages = []
|
793
|
-
sys.exit(0)
|
352
|
+
if args.input:
|
353
|
+
try:
|
354
|
+
with open(args.input) as f:
|
355
|
+
file_content = f.read()
|
356
|
+
messages.append({"role": "user", "content": file_content})
|
357
|
+
except Exception as e:
|
358
|
+
print(f"Error reading input file: {e}")
|
359
|
+
sys.exit(1)
|
360
|
+
|
794
361
|
async def run_and_print():
|
795
|
-
|
796
|
-
|
797
|
-
|
362
|
+
result_lines = []
|
363
|
+
async for chunk in blueprint.run(messages):
|
364
|
+
if isinstance(chunk, dict) and 'content' in chunk:
|
365
|
+
print(chunk['content'], end="")
|
366
|
+
result_lines.append(chunk['content'])
|
798
367
|
else:
|
799
|
-
print(
|
800
|
-
|
368
|
+
print(chunk, end="")
|
369
|
+
result_lines.append(str(chunk))
|
370
|
+
return ''.join(result_lines)
|
371
|
+
|
372
|
+
if args.output:
|
373
|
+
try:
|
374
|
+
output = asyncio.run(run_and_print())
|
375
|
+
with open(args.output, "w") as f:
|
376
|
+
f.write(output)
|
377
|
+
print(f"\nOutput written to {args.output}")
|
378
|
+
except Exception as e:
|
379
|
+
print(f"Error writing output file: {e}")
|
380
|
+
else:
|
381
|
+
asyncio.run(run_and_print())
|
801
382
|
|
802
383
|
if __name__ == "__main__":
|
803
384
|
main()
|
File without changes
|
{open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/entry_points.txt
RENAMED
File without changes
|
{open_swarm-0.1.1745274976.dist-info → open_swarm-0.1.1745275181.dist-info}/licenses/LICENSE
RENAMED
File without changes
|