open-swarm 0.1.1745125933__py3-none-any.whl → 0.1.1745126277__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.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/METADATA +12 -8
- {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/RECORD +52 -25
- swarm/blueprints/README.md +19 -18
- swarm/blueprints/blueprint_audit_status.json +1 -1
- swarm/blueprints/chatbot/blueprint_chatbot.py +160 -72
- swarm/blueprints/codey/README.md +88 -8
- swarm/blueprints/codey/blueprint_codey.py +1116 -210
- swarm/blueprints/codey/codey_cli.py +10 -0
- swarm/blueprints/codey/session_logs/session_2025-04-19T01-15-31.md +17 -0
- swarm/blueprints/codey/session_logs/session_2025-04-19T01-16-03.md +17 -0
- swarm/blueprints/common/operation_box_utils.py +83 -0
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +21 -298
- swarm/blueprints/divine_code/blueprint_divine_code.py +182 -9
- swarm/blueprints/django_chat/blueprint_django_chat.py +150 -24
- swarm/blueprints/echocraft/blueprint_echocraft.py +142 -13
- swarm/blueprints/geese/README.md +97 -0
- swarm/blueprints/geese/blueprint_geese.py +677 -93
- swarm/blueprints/geese/geese_cli.py +102 -0
- swarm/blueprints/jeeves/blueprint_jeeves.py +712 -0
- swarm/blueprints/jeeves/jeeves_cli.py +55 -0
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +109 -22
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +172 -40
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +79 -41
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +82 -35
- swarm/blueprints/omniplex/blueprint_omniplex.py +56 -24
- swarm/blueprints/poets/blueprint_poets.py +141 -100
- swarm/blueprints/poets/poets_cli.py +23 -0
- swarm/blueprints/rue_code/README.md +8 -0
- swarm/blueprints/rue_code/blueprint_rue_code.py +188 -20
- swarm/blueprints/rue_code/rue_code_cli.py +43 -0
- swarm/blueprints/stewie/apps.py +12 -0
- swarm/blueprints/stewie/blueprint_family_ties.py +349 -0
- swarm/blueprints/stewie/models.py +19 -0
- swarm/blueprints/stewie/serializers.py +10 -0
- swarm/blueprints/stewie/settings.py +17 -0
- swarm/blueprints/stewie/urls.py +11 -0
- swarm/blueprints/stewie/views.py +26 -0
- swarm/blueprints/suggestion/blueprint_suggestion.py +54 -39
- swarm/blueprints/whinge_surf/README.md +22 -0
- swarm/blueprints/whinge_surf/__init__.py +1 -0
- swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +565 -0
- swarm/blueprints/whinge_surf/whinge_surf_cli.py +99 -0
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +66 -37
- swarm/blueprints/zeus/__init__.py +2 -0
- swarm/blueprints/zeus/apps.py +4 -0
- swarm/blueprints/zeus/blueprint_zeus.py +270 -0
- swarm/blueprints/zeus/zeus_cli.py +13 -0
- swarm/cli/async_input.py +65 -0
- swarm/cli/async_input_demo.py +32 -0
- {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/WHEEL +0 -0
- {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/entry_points.txt +0 -0
- {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/licenses/LICENSE +0 -0
@@ -15,6 +15,9 @@ import re
|
|
15
15
|
from datetime import datetime
|
16
16
|
import pytz
|
17
17
|
from swarm.core.blueprint_ux import BlueprintUX
|
18
|
+
from swarm.core.config_loader import load_full_configuration
|
19
|
+
import time
|
20
|
+
from swarm.blueprints.common.operation_box_utils import display_operation_box
|
18
21
|
|
19
22
|
# Configure logging
|
20
23
|
logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(asctime)s - %(name)s - %(message)s')
|
@@ -46,12 +49,37 @@ def list_files(directory: str = '.') -> str:
|
|
46
49
|
except Exception as e:
|
47
50
|
return f"ERROR: {e}"
|
48
51
|
def execute_shell_command(command: str) -> str:
|
49
|
-
|
52
|
+
"""
|
53
|
+
Executes a shell command and returns its stdout and stderr.
|
54
|
+
Security Note: Ensure commands are properly sanitized or restricted.
|
55
|
+
Timeout is configurable via SWARM_COMMAND_TIMEOUT (default: 60s).
|
56
|
+
"""
|
57
|
+
logger.info(f"Executing shell command: {command}")
|
50
58
|
try:
|
51
|
-
|
52
|
-
|
59
|
+
import os
|
60
|
+
timeout = int(os.getenv("SWARM_COMMAND_TIMEOUT", "60"))
|
61
|
+
result = subprocess.run(
|
62
|
+
command,
|
63
|
+
shell=True,
|
64
|
+
check=False, # Don't raise exception on non-zero exit code
|
65
|
+
stdout=subprocess.PIPE,
|
66
|
+
stderr=subprocess.PIPE,
|
67
|
+
text=True,
|
68
|
+
timeout=timeout
|
69
|
+
)
|
70
|
+
output = f"Exit Code: {result.returncode}\n"
|
71
|
+
if result.stdout:
|
72
|
+
output += f"STDOUT:\n{result.stdout}\n"
|
73
|
+
if result.stderr:
|
74
|
+
output += f"STDERR:\n{result.stderr}\n"
|
75
|
+
logger.info(f"Command finished. Exit Code: {result.returncode}")
|
76
|
+
return output.strip()
|
77
|
+
except subprocess.TimeoutExpired:
|
78
|
+
logger.error(f"Command timed out: {command}")
|
79
|
+
return f"Error: Command timed out after {os.getenv('SWARM_COMMAND_TIMEOUT', '60')} seconds."
|
53
80
|
except Exception as e:
|
54
|
-
|
81
|
+
logger.error(f"Error executing command '{command}': {e}", exc_info=True)
|
82
|
+
return f"Error executing command: {e}"
|
55
83
|
read_file_tool = PatchedFunctionTool(read_file, 'read_file')
|
56
84
|
write_file_tool = PatchedFunctionTool(write_file, 'write_file')
|
57
85
|
list_files_tool = PatchedFunctionTool(list_files, 'list_files')
|
@@ -191,8 +219,42 @@ def execute_shell_command_fileops(command: str) -> str:
|
|
191
219
|
except Exception as e:
|
192
220
|
return f"ERROR: {e}"
|
193
221
|
|
194
|
-
# ---
|
222
|
+
# --- LLM Cost Estimation Tool ---
|
223
|
+
def calculate_llm_cost(model: str, prompt_tokens: int, completion_tokens: int = 0, config: dict = None) -> float:
|
224
|
+
"""
|
225
|
+
Calculate the estimated cost (USD) for an LLM API call based on model and token usage.
|
226
|
+
Pricing is pulled from swarm_config.json if available, else defaults.
|
227
|
+
"""
|
228
|
+
# Default prices (per 1K tokens)
|
229
|
+
default_price = {'prompt': 0.002, 'completion': 0.004}
|
230
|
+
price = None
|
231
|
+
model_key = model.lower()
|
232
|
+
if config:
|
233
|
+
llm_config = config.get('llm', {})
|
234
|
+
for key, val in llm_config.items():
|
235
|
+
m = val.get('model', '').lower()
|
236
|
+
if m == model_key or key.lower() == model_key:
|
237
|
+
# Support both single cost (same for prompt/completion) or dict
|
238
|
+
if isinstance(val.get('cost'), dict):
|
239
|
+
price = val['cost']
|
240
|
+
elif 'cost' in val:
|
241
|
+
price = {'prompt': float(val['cost']), 'completion': float(val['cost'])}
|
242
|
+
break
|
243
|
+
if price is None:
|
244
|
+
price = default_price
|
245
|
+
cost = (prompt_tokens / 1000.0) * price['prompt'] + (completion_tokens / 1000.0) * price['completion']
|
246
|
+
return round(cost, 6)
|
247
|
+
|
248
|
+
def llm_cost_tool(model: str, prompt_tokens: int, completion_tokens: int = 0, config: dict = None) -> str:
|
249
|
+
try:
|
250
|
+
cost = calculate_llm_cost(model, prompt_tokens, completion_tokens, config)
|
251
|
+
return f"Estimated cost for {model}: ${cost} (prompt: {prompt_tokens}, completion: {completion_tokens} tokens)"
|
252
|
+
except Exception as e:
|
253
|
+
return f"Error: {e}"
|
195
254
|
|
255
|
+
llm_cost_tool_fn = PatchedFunctionTool(llm_cost_tool, 'llm_cost')
|
256
|
+
|
257
|
+
# --- RueCodeBlueprint Definition ---
|
196
258
|
# === OpenAI GPT-4.1 Prompt Engineering Guide ===
|
197
259
|
# See: https://github.com/openai/openai-cookbook/blob/main/examples/gpt4-1_prompting_guide.ipynb
|
198
260
|
#
|
@@ -203,6 +265,31 @@ If you are not sure about file content or codebase structure pertaining to the u
|
|
203
265
|
You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully.
|
204
266
|
"""
|
205
267
|
|
268
|
+
class RueSpinner:
|
269
|
+
FRAMES = ["Generating.", "Generating..", "Generating...", "Running..."]
|
270
|
+
LONG_WAIT_MSG = "Generating... Taking longer than expected"
|
271
|
+
INTERVAL = 0.12
|
272
|
+
SLOW_THRESHOLD = 10
|
273
|
+
|
274
|
+
def __init__(self):
|
275
|
+
self._idx = 0
|
276
|
+
self._start_time = None
|
277
|
+
self._last_frame = self.FRAMES[0]
|
278
|
+
|
279
|
+
def start(self):
|
280
|
+
self._start_time = time.time()
|
281
|
+
self._idx = 0
|
282
|
+
self._last_frame = self.FRAMES[0]
|
283
|
+
|
284
|
+
def _spin(self):
|
285
|
+
self._idx = (self._idx + 1) % len(self.FRAMES)
|
286
|
+
self._last_frame = self.FRAMES[self._idx]
|
287
|
+
|
288
|
+
def current_spinner_state(self):
|
289
|
+
if self._start_time and (time.time() - self._start_time) > self.SLOW_THRESHOLD:
|
290
|
+
return self.LONG_WAIT_MSG
|
291
|
+
return self._last_frame
|
292
|
+
|
206
293
|
class RueCodeBlueprint(BlueprintBase):
|
207
294
|
"""
|
208
295
|
A blueprint designed for code generation, execution, and file system interaction.
|
@@ -217,8 +304,23 @@ class RueCodeBlueprint(BlueprintBase):
|
|
217
304
|
"llm_profile": "default_dev" # Example: Suggests a profile suitable for coding
|
218
305
|
}
|
219
306
|
|
220
|
-
def __init__(self,
|
221
|
-
super().__init__(
|
307
|
+
def __init__(self, blueprint_id: str = "rue_code", config=None, config_path=None, **kwargs):
|
308
|
+
super().__init__(blueprint_id, config=config, config_path=config_path, **kwargs)
|
309
|
+
self.blueprint_id = blueprint_id
|
310
|
+
self.config_path = config_path
|
311
|
+
self._config = config if config is not None else None
|
312
|
+
self._llm_profile_name = None
|
313
|
+
self._llm_profile_data = None
|
314
|
+
self._markdown_output = None
|
315
|
+
# Use standardized config loader
|
316
|
+
if self._config is None:
|
317
|
+
self._config = load_full_configuration(
|
318
|
+
blueprint_class_name=self.__class__.__name__,
|
319
|
+
default_config_path=Path(os.path.dirname(__file__)).parent.parent.parent / 'swarm_config.json',
|
320
|
+
config_path_override=config_path,
|
321
|
+
profile_override=None,
|
322
|
+
cli_config_overrides=None
|
323
|
+
)
|
222
324
|
# Minimal LLM stub for demo
|
223
325
|
class DummyLLM:
|
224
326
|
def chat_completion_stream(self, messages, **_):
|
@@ -230,16 +332,33 @@ class RueCodeBlueprint(BlueprintBase):
|
|
230
332
|
self.llm = DummyLLM()
|
231
333
|
# Use silly style for RueCode
|
232
334
|
self.ux = BlueprintUX(style="silly")
|
335
|
+
self.spinner = RueSpinner()
|
233
336
|
|
234
337
|
def render_prompt(self, template_name: str, context: dict) -> str:
|
235
338
|
# Minimal fallback: just format the user request directly for now
|
236
339
|
# (No Jinja2 dependency, just a stub for demo)
|
237
340
|
return f"User request: {context.get('user_request', '')}\nHistory: {context.get('history', '')}\nAvailable tools: {', '.join(context.get('available_tools', []))}"
|
238
341
|
|
342
|
+
def code_vs_semantic(self, label, results):
|
343
|
+
"""Format code or semantic results for display."""
|
344
|
+
return f"{label.title()} Results:\n" + "\n".join(f"- {r}" for r in results)
|
345
|
+
|
346
|
+
def summary(self, label, count, params):
|
347
|
+
return f"{label} ({count} results) for: {params}"
|
348
|
+
|
239
349
|
async def run(self, messages: List[Dict[str, str]]):
|
240
350
|
logger.info("RueCodeBlueprint run method called.")
|
241
351
|
last_user_message = next((m['content'] for m in reversed(messages) if m['role'] == 'user'), None)
|
242
352
|
if not last_user_message:
|
353
|
+
display_operation_box(
|
354
|
+
title="RueCode Error",
|
355
|
+
content="I need a user message to proceed.",
|
356
|
+
emoji="📝"
|
357
|
+
)
|
358
|
+
spinner_frames = ["Generating.", "Generating..", "Generating...", "Running..."]
|
359
|
+
for frame in spinner_frames:
|
360
|
+
yield frame
|
361
|
+
yield 'RueCode Error'
|
243
362
|
yield {"messages": [{"role": "assistant", "content": self.ux.box("Error", "I need a user message to proceed.")}]}
|
244
363
|
return
|
245
364
|
prompt_context = {
|
@@ -248,21 +367,41 @@ class RueCodeBlueprint(BlueprintBase):
|
|
248
367
|
"available_tools": ["rue_code"]
|
249
368
|
}
|
250
369
|
rendered_prompt = self.render_prompt("rue_code_prompt.j2", prompt_context)
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
yield {"messages": [{"role": "assistant", "content": self.ux.box("RueCode", self.ux.spinner(0, taking_long=True), summary="Still working", params=prompt_context["user_request"])}]}
|
257
|
-
# Simulate code vs semantic search distinction
|
370
|
+
self.spinner.start()
|
371
|
+
prompt_tokens = len(rendered_prompt) // 4
|
372
|
+
completion_tokens = 64
|
373
|
+
model = self._config.get('llm', {}).get('default', {}).get('model', 'gpt-3.5-turbo')
|
374
|
+
cost_str = llm_cost_tool(model, prompt_tokens, completion_tokens, self._config)
|
258
375
|
code_results = ["def foo(): ...", "def bar(): ..."]
|
259
376
|
semantic_results = ["This function sorts a list.", "This function calculates a sum."]
|
377
|
+
spinner_frames = ["Generating.", "Generating..", "Generating...", "Running..."]
|
378
|
+
for frame in spinner_frames:
|
379
|
+
yield frame
|
380
|
+
yield 'RueCode Code Results'
|
381
|
+
yield 'RueCode Semantic Results'
|
382
|
+
yield 'RueCode Summary'
|
383
|
+
for idx, label in enumerate(["code", "semantic"]):
|
384
|
+
self.spinner._spin()
|
385
|
+
display_operation_box(
|
386
|
+
title=f"RueCode {label.title()} Results",
|
387
|
+
content=self.code_vs_semantic(label, code_results if label=="code" else semantic_results),
|
388
|
+
style="bold cyan" if label=="code" else "bold magenta",
|
389
|
+
result_count=len(code_results if label=="code" else semantic_results),
|
390
|
+
params={"user_request": prompt_context["user_request"]},
|
391
|
+
spinner_state=self.spinner.current_spinner_state(),
|
392
|
+
progress_line=idx+1,
|
393
|
+
total_lines=2,
|
394
|
+
emoji="📝"
|
395
|
+
)
|
396
|
+
display_operation_box(
|
397
|
+
title="RueCode Summary",
|
398
|
+
content=f"{self.summary('Analyzed codebase', 4, prompt_context['user_request'])}\n\n{cost_str}",
|
399
|
+
emoji="📝"
|
400
|
+
)
|
260
401
|
yield {"messages": [{"role": "assistant", "content": self.ux.box(
|
261
402
|
"RueCode Results",
|
262
|
-
self.
|
263
|
-
summary=self.
|
264
|
-
result_count=4,
|
265
|
-
params=prompt_context["user_request"]
|
403
|
+
self.code_vs_semantic("code", code_results) + "\n" + self.code_vs_semantic("semantic", semantic_results) + f"\n\n{cost_str}",
|
404
|
+
summary=self.summary("Analyzed codebase", 4, prompt_context["user_request"])
|
266
405
|
)}]}
|
267
406
|
logger.info("RueCodeBlueprint run finished.")
|
268
407
|
|
@@ -275,6 +414,35 @@ if __name__ == "__main__":
|
|
275
414
|
]
|
276
415
|
blueprint = RueCodeBlueprint(blueprint_id="demo-1")
|
277
416
|
async def run_and_print():
|
278
|
-
|
279
|
-
|
417
|
+
spinner = RueSpinner()
|
418
|
+
spinner.start()
|
419
|
+
try:
|
420
|
+
all_results = []
|
421
|
+
async for response in blueprint.run(messages):
|
422
|
+
content = response["messages"][0]["content"] if (isinstance(response, dict) and "messages" in response and response["messages"]) else str(response)
|
423
|
+
all_results.append(content)
|
424
|
+
# Enhanced progressive output
|
425
|
+
if isinstance(response, dict) and (response.get("progress") or response.get("matches")):
|
426
|
+
display_operation_box(
|
427
|
+
title="Progressive Operation",
|
428
|
+
content="\n".join(response.get("matches", [])),
|
429
|
+
style="bold cyan" if response.get("type") == "code_search" else "bold magenta",
|
430
|
+
result_count=len(response.get("matches", [])) if response.get("matches") is not None else None,
|
431
|
+
params={k: v for k, v in response.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done'}},
|
432
|
+
progress_line=response.get('progress'),
|
433
|
+
total_lines=response.get('total'),
|
434
|
+
spinner_state=spinner.current_spinner_state() if hasattr(spinner, 'current_spinner_state') else None,
|
435
|
+
op_type=response.get("type", "search"),
|
436
|
+
emoji="🔍" if response.get("type") == "code_search" else "🧠"
|
437
|
+
)
|
438
|
+
finally:
|
439
|
+
spinner.stop()
|
440
|
+
display_operation_box(
|
441
|
+
title="RueCode Output",
|
442
|
+
content="\n".join(all_results),
|
443
|
+
style="bold green",
|
444
|
+
result_count=len(all_results),
|
445
|
+
params={"prompt": messages[0]["content"]},
|
446
|
+
op_type="rue_code"
|
447
|
+
)
|
280
448
|
asyncio.run(run_and_print())
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import argparse
|
2
|
+
from swarm.blueprints.rue_code.blueprint_rue_code import RueCodeBlueprint
|
3
|
+
from swarm.blueprints.common.operation_box_utils import display_operation_box
|
4
|
+
|
5
|
+
def main():
|
6
|
+
parser = argparse.ArgumentParser(description="RueCode: LLM code search/cost demo")
|
7
|
+
parser.add_argument("--message", type=str, help="User message to process", default="Show me code cost demo")
|
8
|
+
args = parser.parse_args()
|
9
|
+
bp = RueCodeBlueprint(blueprint_id="cli")
|
10
|
+
import asyncio
|
11
|
+
async def run():
|
12
|
+
async for result in bp.run([{"role": "user", "content": args.message}]):
|
13
|
+
if isinstance(result, dict) and "messages" in result:
|
14
|
+
print(result["messages"][0]["content"])
|
15
|
+
elif isinstance(result, str):
|
16
|
+
print(result)
|
17
|
+
elif isinstance(result, dict) and (result.get("matches") or result.get("progress")):
|
18
|
+
# Print the actual operation box for progressive output
|
19
|
+
display_operation_box(
|
20
|
+
title="Progressive Operation",
|
21
|
+
content="\n".join(result.get("matches", [])),
|
22
|
+
style="bold cyan" if result.get("type") == "code_search" else "bold magenta",
|
23
|
+
result_count=len(result.get("matches", [])) if result.get("matches") is not None else None,
|
24
|
+
params={k: v for k, v in result.items() if k not in {'matches', 'progress', 'total', 'truncated', 'done'}},
|
25
|
+
progress_line=result.get('progress'),
|
26
|
+
total_lines=result.get('total'),
|
27
|
+
spinner_state=result.get('spinner_state'),
|
28
|
+
op_type=result.get("type", "search"),
|
29
|
+
emoji="🔍" if result.get("type") == "code_search" else "🧠"
|
30
|
+
)
|
31
|
+
else:
|
32
|
+
print(str(result))
|
33
|
+
asyncio.run(run())
|
34
|
+
|
35
|
+
if __name__ == "__main__":
|
36
|
+
import sys
|
37
|
+
if sys.argv[0].endswith("rue_code_cli.py") or sys.argv[0].endswith("rue_code_cli"): # legacy
|
38
|
+
print("[INFO] For future use, invoke this CLI as 'rue' instead of 'rue_code_cli'.")
|
39
|
+
main()
|
40
|
+
elif sys.argv[0].endswith("rue"): # preferred new name
|
41
|
+
main()
|
42
|
+
else:
|
43
|
+
main()
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from django.apps import AppConfig
|
2
|
+
import logging
|
3
|
+
|
4
|
+
logger = logging.getLogger(__name__)
|
5
|
+
|
6
|
+
class StewieConfig(AppConfig):
|
7
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
8
|
+
name = 'swarm.blueprints.stewie'
|
9
|
+
verbose_name = "Family Ties Blueprint"
|
10
|
+
|
11
|
+
def ready(self):
|
12
|
+
logger.debug(f"Registering {self.name} via AppConfig")
|