open-swarm 0.1.1743371228__py3-none-any.whl → 0.1.1743372974__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-swarm
3
- Version: 0.1.1743371228
3
+ Version: 0.1.1743372974
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
@@ -104,7 +104,7 @@ Open Swarm can be used in two primary ways:
104
104
 
105
105
  * **Agents:** Individual AI units performing specific tasks, powered by LLMs (like GPT-4, Claude, etc.). Built using the `openai-agents` SDK.
106
106
  * **Blueprints:** Python classes (`BlueprintBase` subclasses) defining a swarm's structure, agents, coordination logic, and external dependencies (like required environment variables or MCP servers). They act as reusable templates for specific tasks (e.g., code generation, research, data analysis).
107
- * **MCP (Mission Control Platform) Servers:** Optional external processes providing specialized capabilities (tools) to agents, such as filesystem access, web browsing, database interaction, or interacting with specific APIs (Slack, Monday.com, etc.). Agents interact with MCP servers via a standardized communication protocol.
107
+ * **MCP (Model Context Protocol) Servers:** Optional external processes providing specialized capabilities (tools) to agents, such as filesystem access, web browsing, database interaction, or interacting with specific APIs (Slack, Monday.com, etc.). Agents interact with MCP servers via a standardized communication protocol.
108
108
  * **Configuration (`swarm_config.json`):** A central JSON file defining available LLM profiles (API keys, models) and configurations for MCP servers. Typically managed via `swarm-cli` in `~/.config/swarm/`.
109
109
  * **`swarm-cli`:** A command-line tool for managing blueprints (adding, listing, running, installing) and the `swarm_config.json` file. Uses XDG directories for storing blueprints (`~/.local/share/swarm/blueprints/`) and configuration (`~/.config/swarm/`).
110
110
  * **`swarm-api`:** A launcher for the Django/DRF backend that exposes installed blueprints via an OpenAI-compatible REST API (`/v1/models`, `/v1/chat/completions`).
@@ -26,7 +26,7 @@ swarm/blueprints/django_chat/blueprint_django_chat.py,sha256=jROcPgqNJZv2E2efCoG
26
26
  swarm/blueprints/django_chat/urls.py,sha256=TTTF3pgymvCYbuxpwi4WRBPv8ftQNH4pEoURT8sEVAg,147
27
27
  swarm/blueprints/django_chat/views.py,sha256=MUKjXXjXsq8jMZtAb4RR9g2mEYrwFemN6Bqxpeyi7p4,1299
28
28
  swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html,sha256=wAEOI4Wg0JJ8drXaOcr2Pel6lW3JSHmyIpbocLS5tI8,1649
29
- swarm/blueprints/echocraft/blueprint_echocraft.py,sha256=kd5EFDhveaF2oaaYXhycakA9TYyH7KuXluyb99V4Puo,1589
29
+ swarm/blueprints/echocraft/blueprint_echocraft.py,sha256=6xQT9qb2mIf28TAj1N4lWN1PzTq4CWVmPeTG_3Flrpc,2740
30
30
  swarm/blueprints/family_ties/apps.py,sha256=EjV7AxDNsLM4gsLr_qMEiLAVbERuo1ZsdU9vPtOEYAY,287
31
31
  swarm/blueprints/family_ties/blueprint_family_ties.py,sha256=XVcSZNHmqQNqyjDZM36wtUh2V2Qx2_lComjD7E7er1A,7986
32
32
  swarm/blueprints/family_ties/models.py,sha256=C3_okdVVYuu9xOpoKRsaLoGrM2775cS_cU4UKYAkJ9s,903
@@ -53,7 +53,7 @@ swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py,sha256=i
53
53
  swarm/extensions/__init__.py,sha256=SadbzfxckByaaqzuKPfXMvqmj45-dcMlavlfQYhGnzE,56
54
54
  swarm/extensions/blueprint/__init__.py,sha256=VHSlq8q3AeclMsp63f8RXc3vhcZyzHH0uEaYV6AW-ZI,1841
55
55
  swarm/extensions/blueprint/agent_utils.py,sha256=YWpANYTaFKAK4inWSirRlvzbHwQAH1wNkgW0esHKIeg,542
56
- swarm/extensions/blueprint/blueprint_base.py,sha256=vebDxZnQiaz2lhivPYElhZM886BwigJc6EG_zEEG09U,7513
56
+ swarm/extensions/blueprint/blueprint_base.py,sha256=xuwGq2tAGGevpXVkh_bfIuw1RYRJupeTHKPp8EfvEwI,5145
57
57
  swarm/extensions/blueprint/blueprint_discovery.py,sha256=oU7q1JeVMP5k52hfTGObDy1Jn2v5lJRufz9BNvP3W58,5531
58
58
  swarm/extensions/blueprint/blueprint_utils.py,sha256=Ef_pu-RYomqzFjMg6LOSPSdbYFCbYXjEoSvK1OT49Eo,702
59
59
  swarm/extensions/blueprint/cli_handler.py,sha256=ri812w0w2JzXtLSmwfSbpJCCveokgABnTfZEv4Iup2Y,9250
@@ -253,8 +253,8 @@ swarm/views/message_views.py,sha256=sDUnXyqKXC8WwIIMAlWf00s2_a2T9c75Na5FvYMJwBM,
253
253
  swarm/views/model_views.py,sha256=aAbU4AZmrOTaPeKMWtoKK7FPYHdaN3Zbx55JfKzYTRY,2937
254
254
  swarm/views/utils.py,sha256=geX3Z5ZDKFYyXYBMilc-4qgOSjhujK3AfRtvbXgFpXk,3643
255
255
  swarm/views/web_views.py,sha256=ExQQeJpZ8CkLZQC_pXKOOmdnEy2qR3wEBP4LLp27DPU,7404
256
- open_swarm-0.1.1743371228.dist-info/METADATA,sha256=Ckpb7CTJuopfeFIpw751O2Dcp8QbTbgDZNQttPp31-Y,13680
257
- open_swarm-0.1.1743371228.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
258
- open_swarm-0.1.1743371228.dist-info/entry_points.txt,sha256=LudR3dBqGnglrE1n2zAeWo38Ck0m57sKPW36KfK-pzo,71
259
- open_swarm-0.1.1743371228.dist-info/licenses/LICENSE,sha256=BU9bwRlnOt_JDIb6OT55Q4leLZx9RArDLTFnlDIrBEI,1062
260
- open_swarm-0.1.1743371228.dist-info/RECORD,,
256
+ open_swarm-0.1.1743372974.dist-info/METADATA,sha256=blPlP9G_zGz6Vi-7nxiIpWiegGOvObE1FCYtDJJf3aw,13678
257
+ open_swarm-0.1.1743372974.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
258
+ open_swarm-0.1.1743372974.dist-info/entry_points.txt,sha256=LudR3dBqGnglrE1n2zAeWo38Ck0m57sKPW36KfK-pzo,71
259
+ open_swarm-0.1.1743372974.dist-info/licenses/LICENSE,sha256=BU9bwRlnOt_JDIb6OT55Q4leLZx9RArDLTFnlDIrBEI,1062
260
+ open_swarm-0.1.1743372974.dist-info/RECORD,,
@@ -1,6 +1,10 @@
1
+
2
+ # --- Content for src/swarm/blueprints/echocraft/blueprint_echocraft.py ---
1
3
  import logging
2
- from typing import Dict, List, Any, AsyncGenerator
3
- # Correct import path for BlueprintBase
4
+ from typing import List, Dict, Any, AsyncGenerator
5
+ import uuid # Import uuid to generate IDs
6
+ import time # Import time for timestamp
7
+
4
8
  from swarm.extensions.blueprint.blueprint_base import BlueprintBase
5
9
 
6
10
  logger = logging.getLogger(__name__)
@@ -8,37 +12,60 @@ logger = logging.getLogger(__name__)
8
12
  class EchoCraftBlueprint(BlueprintBase):
9
13
  """
10
14
  A simple blueprint that echoes the last user message.
15
+ Used for testing and demonstrating basic blueprint structure.
11
16
  """
12
- metadata = {
13
- "name": "EchoCraft",
14
- "description": "Echoes the last user message.",
15
- "author": "SwarmTeam",
16
- "version": "1.0",
17
- # Example: Specify a default LLM profile if desired
18
- # "llm_profile": "default"
19
- }
20
-
21
- # *** Make run async and use yield ***
22
- async def run(self, messages: List[Dict[str, str]]) -> AsyncGenerator[Dict[str, Any], None]:
17
+
18
+ # No specific __init__ needed beyond the base class unless adding more params
19
+ # def __init__(self, blueprint_id: str, **kwargs):
20
+ # super().__init__(blueprint_id=blueprint_id, **kwargs)
21
+ # logger.info(f"EchoCraftBlueprint '{self.blueprint_id}' initialized.")
22
+
23
+ async def run(self, messages: List[Dict[str, Any]], **kwargs: Any) -> AsyncGenerator[Dict[str, Any], None]:
23
24
  """
24
- Finds the last user message and yields it back with an 'Echo: ' prefix.
25
+ Echoes the content of the last message with role 'user'.
26
+ Yields a final message in OpenAI ChatCompletion format.
25
27
  """
26
28
  logger.info(f"EchoCraftBlueprint run called with {len(messages)} messages.")
27
- last_user_message = "No user message found."
29
+
30
+ last_user_message_content = "No user message found."
28
31
  for msg in reversed(messages):
29
32
  if msg.get("role") == "user":
30
- last_user_message = msg.get("content", "")
31
- logger.debug(f"Found last user message: {last_user_message}")
33
+ last_user_message_content = msg.get("content", "(empty content)")
34
+ logger.debug(f"Found last user message: {last_user_message_content}")
32
35
  break
33
36
 
34
- response_content = f"Echo: {last_user_message}"
35
- logger.info(f"EchoCraftBlueprint yielding: {response_content}")
37
+ echo_content = f"Echo: {last_user_message_content}"
38
+ logger.info(f"EchoCraftBlueprint yielding: {echo_content}")
39
+
40
+ # --- Format the final output as an OpenAI ChatCompletion object ---
41
+ completion_id = f"chatcmpl-echo-{uuid.uuid4()}"
42
+ created_timestamp = int(time.time())
36
43
 
37
- # Yield the final response in the expected format
38
- yield {
39
- "messages": [
40
- {"role": "assistant", "content": response_content}
41
- ]
44
+ final_message_chunk = {
45
+ "id": completion_id,
46
+ "object": "chat.completion",
47
+ "created": created_timestamp,
48
+ "model": self.llm_profile_name, # Use profile name as model identifier
49
+ "choices": [
50
+ {
51
+ "index": 0,
52
+ "message": {
53
+ "role": "assistant",
54
+ "content": echo_content,
55
+ },
56
+ "finish_reason": "stop",
57
+ "logprobs": None, # Add null logprobs if needed
58
+ }
59
+ ],
60
+ # Add usage stats if desired/possible
61
+ # "usage": {
62
+ # "prompt_tokens": 0,
63
+ # "completion_tokens": 0,
64
+ # "total_tokens": 0
65
+ # }
42
66
  }
67
+ yield final_message_chunk
68
+ # --- End formatting change ---
69
+
43
70
  logger.info("EchoCraftBlueprint run finished.")
44
71
 
@@ -1,160 +1,111 @@
1
+
2
+ # --- Content for src/swarm/extensions/blueprint/blueprint_base.py ---
1
3
  import logging
2
- import os
3
- import inspect
4
- from typing import Dict, List, Any, AsyncGenerator, Optional, Type
4
+ import json
5
5
  from abc import ABC, abstractmethod
6
- from jinja2 import Environment, FileSystemLoader, select_autoescape
6
+ from typing import Dict, Any, Optional, List, AsyncGenerator
7
7
  from pathlib import Path
8
- import copy
9
- # *** Import Django settings ***
10
- from django.conf import settings
11
-
12
- # *** REMOVE SwarmConfig import from apps.py ***
13
- # from swarm.apps import SwarmConfig
8
+ from django.apps import apps # Import Django apps registry
14
9
 
15
- # Import helpers from config_loader if needed directly
16
- # from swarm.extensions.config.config_loader import find_config_file, DEFAULT_CONFIG_FILENAME
17
-
18
- # *** LLMRegistry doesn't exist yet - Comment out import ***
19
- # from swarm.llm.llm_registry import LLMRegistry # Example path - CHANGE ME!
10
+ # Keep the function import
11
+ from swarm.extensions.config.config_loader import get_profile_from_config
20
12
 
21
13
  logger = logging.getLogger(__name__)
22
14
 
23
15
  class BlueprintBase(ABC):
24
16
  """
25
- Abstract base class for all blueprints.
26
- Ensures common interface and configuration handling.
27
- """
28
- metadata: Dict[str, Any] = {}
17
+ Abstract base class for all Swarm blueprints.
29
18
 
30
- @abstractmethod
31
- async def run(self, messages: List[Dict[str, str]]) -> AsyncGenerator[Dict[str, Any], None]:
19
+ Defines the core interface for blueprint initialization and execution.
20
+ """
21
+ def __init__(self, blueprint_id: str, config_path: Optional[Path] = None):
32
22
  """
33
- Asynchronously runs the blueprint logic with the given messages.
34
- Must be implemented by subclasses.
23
+ Initializes the blueprint.
24
+
25
+ Args:
26
+ blueprint_id: A unique identifier for this blueprint instance.
27
+ config_path: Optional path to a specific swarm_config.json file.
28
+ If None, the standard search logic will be used.
35
29
  """
36
- if False: yield {}
37
- pass
38
-
39
- def __init__(
40
- self,
41
- # config_override: Optional[Dict] = None, # Override logic needs rethink
42
- params: Optional[Dict] = None,
43
- ):
44
- logger.debug(f"BlueprintBase.__init__ starting for {self.__class__.__name__}")
45
- self.params = params if params else {}
46
- # self._config_override = config_override # Store override for use in _configure
47
- # self._resolved_config_path = None # Initialize path tracker
48
-
49
- # Configuration loading needs rethink - access via settings for now
50
- self._configure() # This sets self.config attributes from settings
51
-
52
- # Now setup others that might depend on config
53
- self._setup_llm() # Uses self.config attributes
54
- self._setup_jinja() # Uses class path
55
-
56
- # Logging Adjustment
57
- # Check config *after* it has been loaded by _configure
58
- # Use self.is_debug which should be set in _configure
59
- if hasattr(self, 'is_debug') and self.is_debug:
60
- log_level_int = logging.DEBUG
61
- if logging.root.level > log_level_int:
62
- logger.info(f"Root logger level is {logging.getLevelName(logging.root.level)}. Lowering to DEBUG due to config.")
63
- logging.root.setLevel(log_level_int)
64
-
65
- # Ensure llm_profile_name is set by _setup_llm before logging it
66
- # Add check if llm_profile_name exists before logging
67
- profile_name_to_log = getattr(self, 'llm_profile_name', 'N/A')
68
- logger.info(f"Initialized blueprint '{self.metadata.get('name', self.__class__.__name__)}' with profile '{profile_name_to_log}'")
69
-
70
- def _configure(self):
71
- """Placeholder: Sets config attributes based on django.conf.settings."""
72
- logger.debug("BlueprintBase._configure accessing django.conf.settings")
73
- # *** Access settings directly (temporary) ***
74
- # This assumes swarm_config.json content is NOT yet loaded into settings
75
- # We'll rely on defaults or potentially fail in _setup_llm
76
- self.is_debug = getattr(settings, 'DEBUG', False)
77
- # Store blueprint-specific parts for convenience - these won't exist in settings yet!
78
- self.blueprint_config = {} # Placeholder
79
- self.blueprint_defaults = {} # Placeholder
80
- logger.warning("_configure is using placeholders. Swarm config file content is not loaded here.")
81
-
82
-
83
- def _setup_llm(self):
84
- """Sets up the LLM provider based on configuration stored in self.config."""
85
- # *** Access settings directly (temporary) ***
86
- # Get default profile name from settings if defined, else 'default'
87
- default_profile = getattr(settings, 'DEFAULT_LLM_PROFILE', 'default')
88
- self.llm_profile_name = self.blueprint_config.get('llm_profile', default_profile) # Use placeholder blueprint_config
89
- logger.debug(f"Getting LLM profile details for '{self.llm_profile_name}'.")
90
-
91
- # *** This part will likely fail or use defaults as swarm_config isn't in settings ***
92
- # Attempt to get profiles from settings if they were somehow loaded there
93
- all_llm_profiles = getattr(settings, 'LLM_PROFILES', {})
94
- profile_data = all_llm_profiles.get(self.llm_profile_name)
95
-
96
- if profile_data is None:
97
- logger.warning(f"LLM profile '{self.llm_profile_name}' not found in django settings. LLM will not be available.")
98
- self.llm_profile = {} # Set empty profile
99
- self.llm = None # Set LLM to None
100
- return # Exit setup early
101
-
102
- logger.debug(f"Using LLM profile '{self.llm_profile_name}' from settings.")
103
- self.llm_profile = profile_data
104
- # *** LLMRegistry doesn't exist yet - Comment out usage and set placeholder ***
105
- # self.llm = LLMRegistry.get_llm(self.llm_profile)
106
- self.llm = None # Placeholder until registry is implemented
107
- logger.warning("LLMRegistry not implemented. self.llm set to None.")
108
-
109
-
110
- @staticmethod
111
- def _substitute_env_vars(data: Any) -> Any:
112
- """Recursively substitutes environment variables in strings."""
113
- if isinstance(data, dict):
114
- return {k: BlueprintBase._substitute_env_vars(v) for k, v in data.items()}
115
- if isinstance(data, list):
116
- return [BlueprintBase._substitute_env_vars(item) for item in data]
117
- if isinstance(data, str):
118
- return os.path.expandvars(data)
119
- return data
120
-
121
- def _setup_jinja(self):
122
- """Sets up Jinja2 environment."""
30
+ if not blueprint_id:
31
+ raise ValueError("blueprint_id cannot be empty or None") # Add validation
32
+ self.blueprint_id = blueprint_id
33
+ self.config_path = config_path # Note: config_path is currently unused if we rely on AppConfig
34
+ self._config: Optional[Dict[str, Any]] = None
35
+ self._llm_profile_name: Optional[str] = None
36
+ self._llm_profile_data: Optional[Dict[str, Any]] = None
37
+ self._markdown_output: bool = True # Default
38
+
39
+ logger.info(f"Initializing blueprint '{self.blueprint_id}' (Type: {self.__class__.__name__})")
40
+ self._load_and_process_config()
41
+
42
+ def _load_and_process_config(self):
43
+ """Loads the main Swarm config and extracts relevant settings."""
123
44
  try:
124
- blueprint_file_path = inspect.getfile(self.__class__)
125
- blueprint_dir = os.path.dirname(blueprint_file_path)
126
- template_dir = os.path.join(blueprint_dir, 'templates')
127
- if os.path.isdir(template_dir):
128
- logger.debug(f"Setting up Jinja env with loader at: {template_dir}")
129
- self.jinja_env = Environment(
130
- loader=FileSystemLoader(template_dir),
131
- autoescape=select_autoescape(['html', 'xml'])
132
- )
133
- else:
134
- logger.debug(f"No 'templates' directory found for blueprint at {template_dir}. Jinja env not created.")
135
- self.jinja_env = None
45
+ # --- Get config from the AppConfig instance ---
46
+ app_config_instance = apps.get_app_config('swarm')
47
+ # Assuming the loaded config is stored in an attribute named 'config'
48
+ # Adjust 'config' if your AppConfig uses a different attribute name
49
+ if not hasattr(app_config_instance, 'config') or not app_config_instance.config:
50
+ logger.error("Swarm configuration not found on AppConfig instance. Was ready() called?")
51
+ raise ValueError("Swarm configuration unavailable via AppConfig.")
52
+ self._config = app_config_instance.config
53
+ # --- End change ---
54
+
55
+ logger.debug(f"Blueprint '{self.blueprint_id}' using loaded Swarm config.")
56
+
57
+ # Determine LLM profile
58
+ self._llm_profile_name = self._config.get("settings", {}).get("default_llm_profile", "default")
59
+ logger.debug(f"Attempting to use LLM profile: '{self._llm_profile_name}'")
60
+
61
+ # Get substituted profile data
62
+ self._llm_profile_data = get_profile_from_config(self._config, self._llm_profile_name)
63
+ logger.info(f"Successfully loaded LLM profile '{self._llm_profile_name}'. Provider: {self._llm_profile_data.get('provider')}")
64
+
65
+ # Get markdown setting
66
+ blueprint_specific_settings = self._config.get("blueprints", {}).get(self.blueprint_id, {})
67
+ global_markdown_setting = self._config.get("settings", {}).get("default_markdown_output", True)
68
+ self._markdown_output = blueprint_specific_settings.get("markdown_output", global_markdown_setting)
69
+ logger.debug(f"Markdown output for '{self.blueprint_id}': {self._markdown_output}")
70
+
71
+ except ValueError as e:
72
+ logger.error(f"Configuration error for blueprint '{self.blueprint_id}': {e}", exc_info=True)
73
+ raise
136
74
  except Exception as e:
137
- logger.warning(f"Could not determine template path for {self.__class__.__name__}: {e}. Jinja env not created.")
138
- self.jinja_env = None
139
-
140
- def render_prompt(self, template_name: str, context: Dict = None) -> str:
141
- """Renders a Jinja2 template."""
142
- if not self.jinja_env:
143
- raise RuntimeError(f"Jinja environment not set up for blueprint {self.__class__.__name__}.")
144
- if context is None:
145
- context = {}
146
- try:
147
- template = self.jinja_env.get_template(template_name)
148
- full_context = {**self.params, **context}
149
- return template.render(full_context)
150
- except Exception as e:
151
- logger.error(f"Error rendering template '{template_name}': {e}", exc_info=True)
152
- raise
153
-
154
- def get_llm_profile(self) -> Dict:
155
- """Returns the configuration for the LLM profile being used."""
156
- if not hasattr(self, 'llm_profile'):
157
- logger.warning("LLM profile was not set during initialization, returning empty dict.")
158
- return {}
159
- return self.llm_profile
75
+ logger.error(f"Unexpected error loading config for blueprint '{self.blueprint_id}': {e}", exc_info=True)
76
+ raise
77
+
78
+ @property
79
+ def config(self) -> Dict[str, Any]:
80
+ """Returns the loaded and processed Swarm configuration."""
81
+ if self._config is None:
82
+ raise RuntimeError("Configuration accessed before initialization or after failure.")
83
+ return self._config
84
+
85
+ @property
86
+ def llm_profile(self) -> Dict[str, Any]:
87
+ """Returns the loaded and processed LLM profile data for this blueprint."""
88
+ if self._llm_profile_data is None:
89
+ raise RuntimeError("LLM profile accessed before initialization or after failure.")
90
+ return self._llm_profile_data
91
+
92
+ @property
93
+ def llm_profile_name(self) -> str:
94
+ """Returns the name of the LLM profile being used."""
95
+ if self._llm_profile_name is None:
96
+ raise RuntimeError("LLM profile name accessed before initialization or after failure.")
97
+ return self._llm_profile_name
98
+
99
+ @property
100
+ def should_output_markdown(self) -> bool:
101
+ """Returns whether the blueprint should format output as Markdown."""
102
+ return self._markdown_output
103
+
104
+ @abstractmethod
105
+ async def run(self, messages: List[Dict[str, Any]], **kwargs: Any) -> AsyncGenerator[Dict[str, Any], None]:
106
+ """
107
+ The main execution method for the blueprint.
108
+ """
109
+ raise NotImplementedError("Subclasses must implement the 'run' method.")
110
+ yield {}
160
111