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.
Files changed (52) hide show
  1. {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/METADATA +12 -8
  2. {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/RECORD +52 -25
  3. swarm/blueprints/README.md +19 -18
  4. swarm/blueprints/blueprint_audit_status.json +1 -1
  5. swarm/blueprints/chatbot/blueprint_chatbot.py +160 -72
  6. swarm/blueprints/codey/README.md +88 -8
  7. swarm/blueprints/codey/blueprint_codey.py +1116 -210
  8. swarm/blueprints/codey/codey_cli.py +10 -0
  9. swarm/blueprints/codey/session_logs/session_2025-04-19T01-15-31.md +17 -0
  10. swarm/blueprints/codey/session_logs/session_2025-04-19T01-16-03.md +17 -0
  11. swarm/blueprints/common/operation_box_utils.py +83 -0
  12. swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +21 -298
  13. swarm/blueprints/divine_code/blueprint_divine_code.py +182 -9
  14. swarm/blueprints/django_chat/blueprint_django_chat.py +150 -24
  15. swarm/blueprints/echocraft/blueprint_echocraft.py +142 -13
  16. swarm/blueprints/geese/README.md +97 -0
  17. swarm/blueprints/geese/blueprint_geese.py +677 -93
  18. swarm/blueprints/geese/geese_cli.py +102 -0
  19. swarm/blueprints/jeeves/blueprint_jeeves.py +712 -0
  20. swarm/blueprints/jeeves/jeeves_cli.py +55 -0
  21. swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +109 -22
  22. swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +172 -40
  23. swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +79 -41
  24. swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +82 -35
  25. swarm/blueprints/omniplex/blueprint_omniplex.py +56 -24
  26. swarm/blueprints/poets/blueprint_poets.py +141 -100
  27. swarm/blueprints/poets/poets_cli.py +23 -0
  28. swarm/blueprints/rue_code/README.md +8 -0
  29. swarm/blueprints/rue_code/blueprint_rue_code.py +188 -20
  30. swarm/blueprints/rue_code/rue_code_cli.py +43 -0
  31. swarm/blueprints/stewie/apps.py +12 -0
  32. swarm/blueprints/stewie/blueprint_family_ties.py +349 -0
  33. swarm/blueprints/stewie/models.py +19 -0
  34. swarm/blueprints/stewie/serializers.py +10 -0
  35. swarm/blueprints/stewie/settings.py +17 -0
  36. swarm/blueprints/stewie/urls.py +11 -0
  37. swarm/blueprints/stewie/views.py +26 -0
  38. swarm/blueprints/suggestion/blueprint_suggestion.py +54 -39
  39. swarm/blueprints/whinge_surf/README.md +22 -0
  40. swarm/blueprints/whinge_surf/__init__.py +1 -0
  41. swarm/blueprints/whinge_surf/blueprint_whinge_surf.py +565 -0
  42. swarm/blueprints/whinge_surf/whinge_surf_cli.py +99 -0
  43. swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +66 -37
  44. swarm/blueprints/zeus/__init__.py +2 -0
  45. swarm/blueprints/zeus/apps.py +4 -0
  46. swarm/blueprints/zeus/blueprint_zeus.py +270 -0
  47. swarm/blueprints/zeus/zeus_cli.py +13 -0
  48. swarm/cli/async_input.py +65 -0
  49. swarm/cli/async_input_demo.py +32 -0
  50. {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/WHEEL +0 -0
  51. {open_swarm-0.1.1745125933.dist-info → open_swarm-0.1.1745126277.dist-info}/entry_points.txt +0 -0
  52. {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
- import subprocess
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
- result = subprocess.run(command, shell=True, capture_output=True, text=True)
52
- return result.stdout + result.stderr
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
- return f"ERROR: {e}"
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
- # --- RueCodeBlueprint Definition ---
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, *args, **kwargs):
221
- super().__init__(*args, **kwargs)
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
- # Spinner demo: cycle through a few states, then fallback
252
- import asyncio
253
- for i in range(4):
254
- yield {"messages": [{"role": "assistant", "content": self.ux.box("RueCode", self.ux.spinner(i), summary="Preparing to process", params=prompt_context["user_request"])}]}
255
- await asyncio.sleep(0.2)
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.ux.code_vs_semantic("code", code_results) + "\n" + self.ux.code_vs_semantic("semantic", semantic_results),
263
- summary=self.ux.summary("Analyzed codebase", 4, prompt_context["user_request"]),
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
- async for response in blueprint.run(messages):
279
- print(json.dumps(response, indent=2))
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")