diff --git a/.gitignore b/.gitignore index d142b5e4..a583d768 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ runs chroma Unit Testing Agent_state.json swarms/__pycache__ +artifacts venv .DS_Store Cargo.lock diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 87d3cd0a..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "swarms-runtime" # The name of your project -version = "0.1.0" # The current version, adhering to semantic versioning -edition = "2021" # Specifies which edition of Rust you're using, e.g., 2018 or 2021 -authors = ["Your Name "] # Optional: specify the package authors -license = "MIT" # Optional: the license for your project -description = "A brief description of my project" # Optional: a short description of your project - -[dependencies] -cpython = "0.5" -rayon = "1.5" - -[dependencies.pyo3] -version = "0.20.3" -features = ["extension-module", "auto-initialize"] diff --git a/mkdocs.yml b/mkdocs.yml index 6cb6ac3d..db4baf64 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,7 +61,7 @@ nav: - Limitations of Individual Agents: "limits_of_individual_agents.md" - Why Swarms: "why_swarms.md" - The Swarms Bounty System: "swarms_bounty_system.md" -- Swarms Framework: +- Swarms Framework [PY]: - Overview: "swarms/index.md" - DIY Build Your Own Agent: "diy_your_own_agent.md" - swarms.agents: diff --git a/playground/demos/swarm_hackathon/3_2 Multimodal AIGC x Social Hackathon Code Submission & Demos - Lightning Proposals.csv b/playground/demos/swarm_hackathon/3_2 Multimodal AIGC x Social Hackathon Code Submission & Demos - Lightning Proposals.csv new file mode 100644 index 00000000..bfa68104 --- /dev/null +++ b/playground/demos/swarm_hackathon/3_2 Multimodal AIGC x Social Hackathon Code Submission & Demos - Lightning Proposals.csv @@ -0,0 +1,26 @@ +Project Name,Lightning Proposal ,Names of Teammates,Teammates' Contact Info,Discord Handle,Diagram,github +Beggar AI,Self-improving LLMs that we will use to get in-game gold from people in World of Warcraft,Alex Bauer / Hamudi Naana,alex@legionfarm.com,hollyflame_lf,Searching for an engineer who can create self-improving LLMs that we will use to get in-game gold from people in World of Warcraft, +ChatQ,Chat visualization screen aimed to facilitate a dialog and provide visual cues.,"Vlad, Daniel",dkh@cs.stanford.edu,"volkfox, redpowered",, +Ego,"Create 3D characters and prompt them to chat with one another, fully voiced, on any conversation topic","Vish, Peggy","v@ego.live, peggy@ego.live","sixtynine, pegasaurusrex",we need an unreal engine dev to help us :), +Bants,"Public group chats broadcasted to the world, with content created by the interaction between humans and generative agents",Eric Zhang,zhang.bozheng@u.nus.edu,zbz_lvlv,"React Native, Supabase", +Human voice,Leverage AI generated video to represent human voice and encourage authentic social activities. E.g. under represented opinions. Use AI agent to as an influencer and represent a vibe and attract like-minded people to create social tribe.,,Sunnychiuka@gmail.com,carocha_,Dev and anyone:) got a web app for text already and happy anyone who is interested in the topic to join https://dontbeevil.web.app/, +SEMA,Semantic search agent to research arxiv,Matthew Diakonov,matthew.ddy@gmail.com,matthew.ddy,, +OpenMind.bot,"OpenMind.bot streamlines social interactions between personalized bots, representing users, media, and influencers, ensuring meaningful exchanges. It eliminates misunderstandings by using context-aware conversations, followed by summaries or audio recaps of these interactions for efficient communication.",Xuejun(Sheldon) Xie,xuejun.tse@gmail.com,dreammagician,, +From galpha.ai to Video of financial chat,turn a text based QA financial bot from the startup's API at http://galpha.ai into video based QA multimodal bot that can look at real time market,Bill Sun,bill@galpha.ai,bill_sun,"AI Plot generation, AI long video cut tool, product design, front end coding, backend coding", +Feelboard,"Looking to create a chat interface which improves the input based on actual feelings of the user. Interface uses front cam to detect facial expressions and emotions, analyses text being written and formats the text based on the emotions (bold, red, font).",ishan ,marketishan@gmail.com,ishanp0314,, +AiPets,"Create your AiPet, with memory, thinking, reflection. Use gemini 1.5, HeyGen, Gemelo.ai, NextJs, Apple Vision Pro",Konrad,konradmgnat@gmail.com,,, +Dots,External memory for social interactions,Peter k,K11kirky@gmail.com,K11kirky,, +Context Cam,Real-time visual inference & agent actions based on real world context.,Conway Anderson,hello@conwayanderson.com,conway#0000,Related demo: https://twitter.com/ConwayAnderson/status/1756058632960237871, +Subtext,Chat that infers and visualizes message tone for accessibility ,Andrew Acomb,acombandrew@gmail.com,asdlsd,"High EQ product people who text a lot, or devs interested in the project. Text 4088285953 if interested ",https://github.com/AndrewAcomb/subtext +Highlight,Using Gemini to find highlight short clips from long videos,"Jing Xiong, Alex Fu",xiongjing100@gmail.com,milkteax777,, +Merse,"A new way of Storytelling — Democratising Comic Strip creation based on your personal life story, Text to Comic book!","Mark Rachapoom, Kumar Abhirup, Kinjal Nandy, Alex",hey@kumareth.com,,, +DateSmart,create dating simulator that reflects real world dating conversations.,Kai Hung Chang,kaihungc1993@gmail.com,kai kaihungc ,, +YourContact,create a software that help you manage your relationship and remember detail about other people. for example remember birthday to send them birthday congrats,Sam He,samhenan@gmail.com,supersam331,"UX to brainstore and find painpo this idea. FE eng. I""m a BE ", +Beyond,Unlocking cultural capital for the global workforce,Yun Huang,huangyun@sas.upenn.edu,huangyoon,Searching for an inquisitive developer with strong execution skills, +AutoCAD ,generate threejs code for an object given a video of it from multiple angles,Haden Wasserbaech ,hello@haden.io,,, +LogicMind,Addressing hallucinations using NS approaches,Sankeerth Rao,sankeerth1729@gmail.com,sankeerthrao,, +AI Reality TV,,Edgar Haond,,,, +Swarms,Orchestrate Agents to create swarms,"Kye, Nate",kye@apac.ai,eternalreclaimer,Searching for agent engineers,https://github.com/kyegomez/swarms +Simplychat,Chat interface for e-commerce website,Hao,Shenghaozhe@gmail.com,,, +Kindergarten,A place for Kids growing up in the age AI to learn & play with fellow AI agents that feel like good friends and encourage you to learn & get better,Ben,ben@holfeld.ai,benholfeld,Looking for multi-agent interadction engineers,https://twitter.com/benholfeld +Followup,personal networking assistant,Eleanor,eleanorqin@gmail.com,eleanor.q,, \ No newline at end of file diff --git a/playground/demos/swarm_hackathon/main.py b/playground/demos/swarm_hackathon/main.py index 1fefdd5a..2e8eed8c 100644 --- a/playground/demos/swarm_hackathon/main.py +++ b/playground/demos/swarm_hackathon/main.py @@ -1,7 +1,7 @@ import concurrent import csv import os -from swarms import Gemini, Agent, SwarmNetwork, ConcurrentWorkflow +from swarms import Gemini, Agent from swarms.memory import ChromaDB from dotenv import load_dotenv from swarms.utils.parse_code import extract_code_from_markdown @@ -17,24 +17,11 @@ gemini = Gemini( gemini_api_key=os.getenv("GEMINI_API_KEY"), ) -# SwarmNetwork -swarm_network = SwarmNetwork( - logging_enabled=True, -) - - -# ConcurrentWorkflow -workflow = ConcurrentWorkflow( - task_pool=None, - max_workers=10, -) - - # memory memory = ChromaDB(output_dir="swarm_hackathon") -def execute_concurrently(callable_functions, max_workers=5): +def execute_concurrently(callable_functions: callable, max_workers=5): """ Executes callable functions concurrently using multithreading. @@ -71,23 +58,6 @@ def execute_concurrently(callable_functions, max_workers=5): return results -# # For each row in the dataframe, create an agent and add it to the swarm network -# for index, row in df.iterrows(): -# agent_name = row["Project Name"] + "agent" -# system_prompt = row["Lightning Proposal"] -# agent = Agent( -# llm=gemini, -# max_loops="auto", -# stopping_token="", -# system_prompt=system_prompt, -# agent_name=agent_name, -# long_term_memory=ChromaDB(output_dir="swarm_hackathon"), -# ) -# swarm_network.add_agent(agent) - -# out = swarm_network.list_agents() - - # Adjusting the function to extract specific column values def extract_and_create_agents( csv_file_path: str, target_columns: list diff --git a/playground/structs/hierarchical_swarm.py b/playground/structs/hierarchical_swarm.py new file mode 100644 index 00000000..04bea216 --- /dev/null +++ b/playground/structs/hierarchical_swarm.py @@ -0,0 +1,18 @@ +import os +from swarms import OpenAIChat, Agent +from dotenv import load_dotenv + + +# Load environment variables +load_dotenv() + +# Create a chat instance +llm = OpenAIChat( + api_key=os.getenv("OPENAI_API_KEY"), +) + +# Create an agent +agent = Agent( + agent_name="GPT-3", + llm=llm, +) diff --git a/majority_voting.py b/playground/structs/majority_voting.py similarity index 81% rename from majority_voting.py rename to playground/structs/majority_voting.py index 1ab56476..c39cfb54 100644 --- a/majority_voting.py +++ b/playground/structs/majority_voting.py @@ -5,8 +5,11 @@ llm = Anthropic() # Agents agent1 = Agent( - llm = llm, - system_prompt="You are the leader of the Progressive Party. What is your stance on healthcare?", + llm=llm, + system_prompt=( + "You are the leader of the Progressive Party. What is your" + " stance on healthcare?" + ), agent_name="Progressive Leader", agent_description="Leader of the Progressive Party", long_term_memory=ChromaDB(), @@ -39,4 +42,4 @@ mv = MajorityVoting( # Start the majority voting -mv.run("What is your stance on healthcare?") \ No newline at end of file +mv.run("What is your stance on healthcare?") diff --git a/playground/structs/message_pool.py b/playground/structs/message_pool.py index b5230a8e..c19e844d 100644 --- a/playground/structs/message_pool.py +++ b/playground/structs/message_pool.py @@ -5,7 +5,12 @@ from swarms.memory.chroma_db import ChromaDB # Agents agent1 = Agent( - llm=OpenAIChat(system_prompt="You are a Minecraft player. What's your favorite building style?"), + llm=OpenAIChat( + system_prompt=( + "You are a Minecraft player. What's your favorite" + " building style?" + ) + ), agent_name="Steve", agent_description="A Minecraft player agent", long_term_memory=ChromaDB(), @@ -13,7 +18,12 @@ agent1 = Agent( ) agent2 = Agent( - llm=OpenAIChat(system_prompt="You are a Minecraft builder. What's your most impressive creation?"), + llm=OpenAIChat( + system_prompt=( + "You are a Minecraft builder. What's your most impressive" + " creation?" + ) + ), agent_name="Bob", agent_description="A Minecraft builder agent", long_term_memory=ChromaDB(), @@ -21,7 +31,12 @@ agent2 = Agent( ) agent3 = Agent( - llm=OpenAIChat(system_prompt="You are a Minecraft explorer. What's the most interesting place you've discovered?"), + llm=OpenAIChat( + system_prompt=( + "You are a Minecraft explorer. What's the most" + " interesting place you've discovered?" + ) + ), agent_name="Alex", agent_description="A Minecraft explorer agent", long_term_memory=ChromaDB(), @@ -29,7 +44,12 @@ agent3 = Agent( ) agent4 = Agent( - llm=OpenAIChat(system_prompt="You are a Minecraft adventurer. What's the most dangerous situation you've been in?"), + llm=OpenAIChat( + system_prompt=( + "You are a Minecraft adventurer. What's the most" + " dangerous situation you've been in?" + ) + ), agent_name="Ender", agent_description="A Minecraft adventurer agent", long_term_memory=ChromaDB(), @@ -37,7 +57,12 @@ agent4 = Agent( ) moderator = Agent( - llm=OpenAIChat(system_prompt="You are a Minecraft moderator. How do you handle conflicts between players?"), + llm=OpenAIChat( + system_prompt=( + "You are a Minecraft moderator. How do you handle" + " conflicts between players?" + ) + ), agent_name="Admin", agent_description="A Minecraft moderator agent", long_term_memory=ChromaDB(), diff --git a/playground/structs/multi_process_workflow.py b/playground/structs/multi_process_workflow.py new file mode 100644 index 00000000..3c7f39c0 --- /dev/null +++ b/playground/structs/multi_process_workflow.py @@ -0,0 +1,61 @@ +import os +from swarms import Gemini, Agent +from swarms.structs.multi_process_workflow import MultiProcessWorkflow +from dotenv import load_dotenv + +# Load the environment variables +load_dotenv() + +# Gemini API key +api_key = os.getenv("GEMINI_API_KEY") + +# Initialize LLM +llm = Gemini( + model_name="gemini-pro", + api_key=api_key, +) + +# Initialize the agents +finance_agent = Agent( + agent_name="Finance Agent", + llm=llm, + max_loops=1, + system_prompt="Finance", +) + +marketing_agent = Agent( + agent_name="Marketing Agent", + llm=llm, + max_loops=1, + system_prompt="Marketing", +) + +product_agent = Agent( + agent_name="Product Agent", + llm=llm, + max_loops=1, + system_prompt="Product", +) + +other_agent = Agent( + agent_name="Other Agent", + llm=llm, + max_loops=1, + system_prompt="Other", +) + +# Swarm +workflow = MultiProcessWorkflow( + agents=[ + finance_agent, + marketing_agent, + product_agent, + other_agent, + ], + max_workers=5, + autosave=True, +) + + +# Run the workflow +results = workflow.run("What") diff --git a/playground/agents/tool_agent.py b/playground/structs/tool_agent.py similarity index 97% rename from playground/agents/tool_agent.py rename to playground/structs/tool_agent.py index a6445b39..ae10a168 100644 --- a/playground/agents/tool_agent.py +++ b/playground/structs/tool_agent.py @@ -1,6 +1,4 @@ -# Import necessary libraries from transformers import AutoModelForCausalLM, AutoTokenizer - from swarms import ToolAgent # Load the pre-trained model and tokenizer diff --git a/playground/swarms/automate_docs.py b/playground/swarms/automate_docs.py new file mode 100644 index 00000000..f3268fdb --- /dev/null +++ b/playground/swarms/automate_docs.py @@ -0,0 +1,183 @@ +import inspect +import os +import threading +from typing import Callable, List + +from swarms.prompts.documentation import DOCUMENTATION_WRITER_SOP +from swarms import Agent, OpenAIChat +from swarms.utils.loguru_logger import logger +import concurrent + +######### +from swarms.utils.file_processing import ( + load_json, + sanitize_file_path, + zip_workspace, + create_file_in_folder, + zip_folders, +) + + +class PythonDocumentationSwarm: + """ + A class for automating the documentation process for Python classes. + + Args: + agents (List[Agent]): A list of agents used for processing the documentation. + max_loops (int, optional): The maximum number of loops to run. Defaults to 4. + docs_module_name (str, optional): The name of the module where the documentation will be saved. Defaults to "swarms.structs". + docs_directory (str, optional): The directory where the documentation will be saved. Defaults to "docs/swarms/tokenizers". + + Attributes: + agents (List[Agent]): A list of agents used for processing the documentation. + max_loops (int): The maximum number of loops to run. + docs_module_name (str): The name of the module where the documentation will be saved. + docs_directory (str): The directory where the documentation will be saved. + """ + + def __init__( + self, + agents: List[Agent], + max_loops: int = 4, + docs_module_name: str = "swarms.utils", + docs_directory: str = "docs/swarms/utils", + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.agents = agents + self.max_loops = max_loops + self.docs_module_name = docs_module_name + self.docs_directory = docs_directory + + # Initialize agent name logging + logger.info( + "Agents used for documentation:" + f" {', '.join([agent.name for agent in agents])}" + ) + + # Create the directory if it doesn't exist + dir_path = self.docs_directory + os.makedirs(dir_path, exist_ok=True) + logger.info(f"Documentation directory created at {dir_path}.") + + def process_documentation(self, item): + """ + Process the documentation for a given class using OpenAI model and save it in a Markdown file. + + Args: + item: The class or function for which the documentation needs to be processed. + """ + try: + doc = inspect.getdoc(item) + source = inspect.getsource(item) + is_class = inspect.isclass(item) + item_type = "Class Name" if is_class else "Name" + input_content = ( + f"{item_type}:" + f" {item.__name__}\n\nDocumentation:\n{doc}\n\nSource" + f" Code:\n{source}" + ) + + # Process with OpenAI model (assuming the model's __call__ method takes this input and returns processed content) + for agent in self.agents: + processed_content = agent( + DOCUMENTATION_WRITER_SOP( + input_content, self.docs_module_name + ) + ) + + doc_content = f"{processed_content}\n" + + # Create the directory if it doesn't exist + dir_path = self.docs_directory + os.makedirs(dir_path, exist_ok=True) + + # Write the processed documentation to a Markdown file + file_path = os.path.join( + dir_path, f"{item.__name__.lower()}.md" + ) + with open(file_path, "w") as file: + file.write(doc_content) + + logger.info( + f"Documentation generated for {item.__name__}." + ) + except Exception as e: + logger.error( + f"Error processing documentation for {item.__name__}." + ) + logger.error(e) + + def run(self, python_items: List[Callable]): + """ + Run the documentation process for a list of Python items. + + Args: + python_items (List[Callable]): A list of Python classes or functions for which the documentation needs to be generated. + """ + try: + threads = [] + for item in python_items: + thread = threading.Thread( + target=self.process_documentation, args=(item,) + ) + threads.append(thread) + thread.start() + + # Wait for all threads to complete + for thread in threads: + thread.join() + + logger.info( + "Documentation generated in 'swarms.structs'" + " directory." + ) + except Exception as e: + logger.error("Error running documentation process.") + logger.error(e) + + def run_concurrently(self, python_items: List[Callable]): + try: + with concurrent.futures.ThreadPoolExecutor() as executor: + executor.map(self.process_documentation, python_items) + + logger.info( + "Documentation generated in 'swarms.structs'" + " directory." + ) + except Exception as e: + logger.error("Error running documentation process.") + logger.error(e) + + +# Example usage +# Initialize the agents +agent = Agent( + llm=OpenAIChat(max_tokens=3000), + agent_name="Documentation Agent", + system_prompt=( + "You write documentation for Python items functions and" + " classes, return in markdown" + ), + max_loops=1, +) + +# Initialize the documentation swarm +doc_swarm = PythonDocumentationSwarm( + agents=[agent], + max_loops=1, + docs_module_name="swarms.structs", + docs_directory="docs/swarms/tokenizers", +) + +# Run the documentation process +doc_swarm.run( + [ + load_json, + sanitize_file_path, + zip_workspace, + create_file_in_folder, + zip_folders, + ] +) diff --git a/pyproject.toml b/pyproject.toml index 3a932719..a9611796 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "4.2.8" +version = "4.2.9" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] @@ -49,7 +49,7 @@ tiktoken = "0.4.0" ratelimit = "2.2.1" loguru = "0.7.2" huggingface-hub = "*" -pydantic = "1.10.12" +pydantic = "*" tenacity = "8.2.2" Pillow = "9.4.0" chromadb = "*" diff --git a/Dockerfile b/scripts/Dockerfile similarity index 100% rename from Dockerfile rename to scripts/Dockerfile diff --git a/scripts/log_cleanup.py b/scripts/log_cleanup.py new file mode 100644 index 00000000..ad3da11b --- /dev/null +++ b/scripts/log_cleanup.py @@ -0,0 +1,21 @@ +import os +import shutil + +# Create a new directory for the log files if it doesn't exist +if not os.path.exists("artifacts"): + os.makedirs("artifacts") + +# Walk through the current directory +for dirpath, dirnames, filenames in os.walk("."): + for filename in filenames: + # If the file is a log file + if filename.endswith(".log"): + # Construct the full file path + file_path = os.path.join(dirpath, filename) + # Move the log file to the 'artifacts' directory + shutil.move(file_path, "artifacts") + +print( + "Moved all log files into the 'artifacts' directory and deleted" + " their original location." +) diff --git a/swarms/__init__.py b/swarms/__init__.py index 66bbbaa4..220f729f 100644 --- a/swarms/__init__.py +++ b/swarms/__init__.py @@ -1,6 +1,7 @@ # from swarms.telemetry.main import Telemetry # noqa: E402, F403 from swarms.telemetry.bootup import bootup # noqa: E402, F403 import os + os.environ["WANDB_SILENT"] = "true" bootup() @@ -17,4 +18,4 @@ from swarms.telemetry import * # noqa: E402, F403 from swarms.tokenizers import * # noqa: E402, F403 from swarms.tools import * # noqa: E402, F403 from swarms.utils import * # noqa: E402, F403 -from swarms.memory import * # noqa: E402, F403 \ No newline at end of file +from swarms.memory import * # noqa: E402, F403 diff --git a/swarms/models/dalle3.py b/swarms/models/dalle3.py index 6b225b49..0e02c3d6 100644 --- a/swarms/models/dalle3.py +++ b/swarms/models/dalle3.py @@ -13,7 +13,7 @@ from cachetools import TTLCache from dotenv import load_dotenv from openai import OpenAI from PIL import Image -from pydantic import validator +from pydantic import field_validator from termcolor import colored load_dotenv() @@ -90,7 +90,8 @@ class Dalle3: arbitrary_types_allowed = True - @validator("max_retries", "time_seconds") + @field_validator("max_retries", "time_seconds") + @classmethod def must_be_positive(cls, value): if value <= 0: raise ValueError("Must be positive") diff --git a/swarms/models/eleven_labs.py b/swarms/models/eleven_labs.py index 2d55e864..759c65bb 100644 --- a/swarms/models/eleven_labs.py +++ b/swarms/models/eleven_labs.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Any, Dict, Union from langchain.utils import get_from_dict_or_env -from pydantic import root_validator +from pydantic import model_validator from swarms.tools.tool import BaseTool @@ -59,7 +59,8 @@ class ElevenLabsText2SpeechTool(BaseTool): " Italian, French, Portuguese, and Hindi. " ) - @root_validator(pre=True) + @model_validator(mode="before") + @classmethod def validate_environment(cls, values: Dict) -> Dict: """Validate that api key exists in environment.""" _ = get_from_dict_or_env( diff --git a/swarms/models/gemini.py b/swarms/models/gemini.py index cf739418..276cd05d 100644 --- a/swarms/models/gemini.py +++ b/swarms/models/gemini.py @@ -36,7 +36,6 @@ def get_gemini_api_key_env(): return str(key) - # Main class class Gemini(BaseMultiModalModel): """Gemini model diff --git a/swarms/models/types.py b/swarms/models/types.py index 10957329..49b1ed9d 100644 --- a/swarms/models/types.py +++ b/swarms/models/types.py @@ -9,21 +9,21 @@ class TextModality(BaseModel): class ImageModality(BaseModel): url: str - alt_text: Optional[str] + alt_text: Optional[str] = None class AudioModality(BaseModel): url: str - transcript: Optional[str] + transcript: Optional[str] = None class VideoModality(BaseModel): url: str - transcript: Optional[str] + transcript: Optional[str] = None class MultimodalData(BaseModel): - text: Optional[List[TextModality]] - images: Optional[List[ImageModality]] - audio: Optional[List[AudioModality]] - video: Optional[List[VideoModality]] + text: Optional[List[TextModality]] = None + images: Optional[List[ImageModality]] = None + audio: Optional[List[AudioModality]] = None + video: Optional[List[VideoModality]] = None diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 12186129..db63275a 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -19,7 +19,7 @@ from swarms.structs.message import Message from swarms.structs.model_parallizer import ModelParallelizer from swarms.structs.multi_agent_collab import MultiAgentCollaboration from swarms.structs.multi_process_workflow import ( - MultiProcessingWorkflow, + MultiProcessWorkflow, ) from swarms.structs.multi_threaded_workflow import ( MultiThreadedWorkflow, @@ -136,7 +136,7 @@ __all__ = [ "MajorityVoting", "synchronized_queue", "TaskQueueBase", - "MultiProcessingWorkflow", + "MultiProcessWorkflow", "MultiThreadedWorkflow", "AgentJob", ] diff --git a/swarms/structs/majority_voting.py b/swarms/structs/majority_voting.py index 513f20be..536b0787 100644 --- a/swarms/structs/majority_voting.py +++ b/swarms/structs/majority_voting.py @@ -114,11 +114,10 @@ def majority_voting(answers: List[str]): """ counter = Counter(answers) if counter: - answer = counter.most_common(1)[0][0] - else: + else: answer = "I don't know" - + return answer @@ -166,7 +165,9 @@ class MajorityVoting: # If autosave is enabled, save the conversation to a file if self.autosave: - create_file(str(self.conversation), "majority_voting.json") + create_file( + str(self.conversation), "majority_voting.json" + ) # Log the agents logger.info("Initializing majority voting system") @@ -205,9 +206,14 @@ class MajorityVoting: # Add responses to conversation and log them for agent, response in zip(self.agents, results): - response = response if isinstance(response, list) else [response] + response = ( + response if isinstance(response, list) else [response] + ) self.conversation.add(agent.agent_name, response) - logger.info(f"[Agent][Name: {agent.agent_name}][Response: {response}]") + logger.info( + f"[Agent][Name: {agent.agent_name}][Response:" + f" {response}]" + ) # Perform majority voting on the conversation responses = [ @@ -218,10 +224,11 @@ class MajorityVoting: # If an output parser is provided, parse the responses if self.output_parser is not None: - majority_vote = self.output_parser(responses, *args, **kwargs) + majority_vote = self.output_parser( + responses, *args, **kwargs + ) else: majority_vote = majority_voting(responses) - # Return the majority vote return majority_vote diff --git a/swarms/structs/message_pool.py b/swarms/structs/message_pool.py index 96467fd9..88766d06 100644 --- a/swarms/structs/message_pool.py +++ b/swarms/structs/message_pool.py @@ -210,4 +210,4 @@ class MessagePool: # (mod, content) # for mod, content, _ in self.messages # Add an underscore to ignore the rest of the elements # if query in content - # ] \ No newline at end of file + # ] diff --git a/swarms/structs/multi_process_workflow.py b/swarms/structs/multi_process_workflow.py index 39c69eaa..e8f52db9 100644 --- a/swarms/structs/multi_process_workflow.py +++ b/swarms/structs/multi_process_workflow.py @@ -1,17 +1,11 @@ -import logging from functools import wraps from multiprocessing import Manager, Pool, cpu_count from time import sleep -from typing import List +from typing import Sequence +from swarms.structs.agent import Agent from swarms.structs.base_workflow import BaseWorkflow -from swarms.structs.task import Task - -# Configure logging -logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(levelname)s - %(message)s", -) +from swarms.utils.loguru_logger import logger # Retry on failure @@ -35,7 +29,7 @@ def retry_on_failure(max_retries: int = 3, delay: int = 5): try: return func(*args, **kwargs) except Exception as error: - logging.error( + logger.error( f"Error: {str(error)}, retrying in" f" {delay} seconds..." ) @@ -47,7 +41,7 @@ def retry_on_failure(max_retries: int = 3, delay: int = 5): return decorator -class MultiProcessingWorkflow(BaseWorkflow): +class MultiProcessWorkflow(BaseWorkflow): """ Initialize a MultiProcessWorkflow object. @@ -90,25 +84,32 @@ class MultiProcessingWorkflow(BaseWorkflow): self, max_workers: int = 5, autosave: bool = True, - tasks: List[Task] = None, + agents: Sequence[Agent] = None, *args, **kwargs, ): super().__init__(*args, **kwargs) self.max_workers = max_workers self.autosave = autosave - self.tasks = sorted( - tasks or [], key=lambda task: task.priority, reverse=True - ) + self.agents = agents self.max_workers or cpu_count() - if tasks is None: - tasks = [] + # Log + logger.info( + ( + "Initialized MultiProcessWorkflow with" + f" {self.max_workers} max workers and autosave set to" + f" {self.autosave}" + ), + ) - self.tasks = tasks + # Log the agents + if self.agents is not None: + for agent in self.agents: + logger.info(f"Agent: {agent.agent_name}") - def execute_task(self, task: Task, *args, **kwargs): + def execute_task(self, task: str, *args, **kwargs): """Execute a task and handle exceptions. Args: @@ -121,27 +122,23 @@ class MultiProcessingWorkflow(BaseWorkflow): """ try: - result = task.execute(*args, **kwargs) - - logging.info( - f"Task {task} completed successfully with result" - f" {result}" - ) + if self.agents is not None: + # Execute the task + for agent in self.agents: + result = agent.run(task, *args, **kwargs) - if self.autosave: - self._autosave_task_result(task, result) + return result except Exception as e: - logging.error( + logger.error( ( "An error occurred during execution of task" f" {task}: {str(e)}" ), - exc_info=True, ) return None - def run(self, task: Task, *args, **kwargs): + def run(self, task: str, *args, **kwargs): """Run the workflow. Args: @@ -163,14 +160,14 @@ class MultiProcessingWorkflow(BaseWorkflow): results_list = manager.list() jobs = [ pool.apply_async( - self.execute_task, - (task,), + self.execute_task, # Pass the function, not the function call + args=(task,) + + args, # Pass the arguments as a tuple + kwds=kwargs, # Pass the keyword arguments as a dictionary callback=results_list.append, timeout=task.timeout, - *args, - **kwargs, ) - for task in self.tasks + for agent in self.agent ] # Wait for all jobs to complete @@ -181,17 +178,5 @@ class MultiProcessingWorkflow(BaseWorkflow): return results except Exception as error: - logging.error(f"Error in run: {error}") + logger.error(f"Error in run: {error}") return None - - def _autosave_task_result(self, task: Task, result): - """Autosave task result. This should be adapted based on how autosaving is implemented. - - Args: - task (Task): The task for which to autosave the result. - result (Any): The result of the task execution. - - """ - # Note: This method might need to be adapted to ensure it's process-safe, depending on how autosaving is implemented. - logging.info(f"Autosaving result for task {task}: {result}") - # Actual autosave logic here diff --git a/swarms/structs/schemas.py b/swarms/structs/schemas.py index f4cf6a3b..e6a801cc 100644 --- a/swarms/structs/schemas.py +++ b/swarms/structs/schemas.py @@ -12,7 +12,7 @@ class TaskInput(BaseModel): description=( "The input parameters for the task. Any value is allowed." ), - example='{\n"debug": false,\n"mode": "benchmarks"\n}', + examples=['{\n"debug": false,\n"mode": "benchmarks"\n}'], ) @@ -29,17 +29,19 @@ class Artifact(BaseModel): artifact_id: str = Field( ..., description="Id of the artifact", - example="b225e278-8b4c-4f99-a696-8facf19f0e56", + examples=["b225e278-8b4c-4f99-a696-8facf19f0e56"], ) file_name: str = Field( - ..., description="Filename of the artifact", example="main.py" + ..., + description="Filename of the artifact", + examples=["main.py"], ) relative_path: str | None = Field( None, description=( "Relative path of the artifact in the agent's workspace" ), - example="python/code/", + examples=["python/code/"], ) @@ -50,7 +52,7 @@ class ArtifactUpload(BaseModel): description=( "Relative path of the artifact in the agent's workspace" ), - example="python/code/", + examples=["python/code/"], ) @@ -61,7 +63,7 @@ class StepInput(BaseModel): "Input parameters for the task step. Any value is" " allowed." ), - example='{\n"file_to_refactor": "models.py"\n}', + examples=['{\n"file_to_refactor": "models.py"\n}'], ) @@ -72,7 +74,7 @@ class StepOutput(BaseModel): "Output that the task step has produced. Any value is" " allowed." ), - example='{\n"tokens": 7894,\n"estimated_cost": "0,24$"\n}', + examples=['{\n"tokens": 7894,\n"estimated_cost": "0,24$"\n}'], ) @@ -80,9 +82,9 @@ class TaskRequestBody(BaseModel): input: str | None = Field( None, description="Input prompt for the task.", - example=( + examples=[ "Write the words you receive to the file 'output.txt'." - ), + ], ) additional_input: TaskInput | None = None @@ -91,14 +93,16 @@ class Task(TaskRequestBody): task_id: str = Field( ..., description="The ID of the task.", - example="50da533e-3904-4401-8a07-c49adf88b5eb", + examples=["50da533e-3904-4401-8a07-c49adf88b5eb"], ) artifacts: list[Artifact] = Field( [], description="A list of artifacts that the task has produced.", - example=[ - "7a49f31c-f9c6-4346-a22c-e32bc5af4d8e", - "ab7b4091-2560-4692-a4fe-d831ea3ca7d6", + examples=[ + [ + "7a49f31c-f9c6-4346-a22c-e32bc5af4d8e", + "ab7b4091-2560-4692-a4fe-d831ea3ca7d6", + ] ], ) @@ -107,7 +111,7 @@ class StepRequestBody(BaseModel): input: str | None = Field( None, description="Input prompt for the step.", - example="Washington", + examples=["Washington"], ) additional_input: StepInput | None = None @@ -122,17 +126,17 @@ class Step(StepRequestBody): task_id: str = Field( ..., description="The ID of the task this step belongs to.", - example="50da533e-3904-4401-8a07-c49adf88b5eb", + examples=["50da533e-3904-4401-8a07-c49adf88b5eb"], ) step_id: str = Field( ..., description="The ID of the task step.", - example="6bb1801a-fd80-45e8-899a-4dd723cc602e", + examples=["6bb1801a-fd80-45e8-899a-4dd723cc602e"], ) name: str | None = Field( None, description="The name of the task step.", - example="Write to file", + examples=["Write to file"], ) status: Status = Field( ..., description="The status of the task step." @@ -140,11 +144,11 @@ class Step(StepRequestBody): output: str | None = Field( None, description="Output of the task step.", - example=( + examples=[ "I am going to use the write_to_file command and write" " Washington to a file called output.txt" " str: - """ - Scrape the docstrings and parameters of a function decorated with `tool` and return a formatted string. - - Args: - fn (Callable): The function to scrape. - - Returns: - str: A string containing the function's name, documentation string, and a list of its parameters. Each parameter is represented as a line containing the parameter's name, default value, and annotation. - """ - try: - # If the function is a tool, get the original function - if hasattr(fn, "func"): - fn = fn.func - - signature = inspect.signature(fn) - parameters = [] - for name, param in signature.parameters.items(): - parameters.append( - f"Name: {name}, Type:" - f" {param.default if param.default is not param.empty else 'None'}," - " Annotation:" - f" {param.annotation if param.annotation is not param.empty else 'None'}" - ) - parameters_str = "\n".join(parameters) - return ( - f"Function: {fn.__name__}\nDocstring:" - f" {inspect.getdoc(fn)}\nParameters:\n{parameters_str}" - ) - except Exception as error: - print( - colored( - ( - f"Error scraping tool function docs {error} try" - " optimizing your inputs with different" - " variables and attempt once more." - ), - "red", - ) - ) diff --git a/swarms/tools/tool_utils.py b/swarms/tools/tool_utils.py index ee6b6391..4d8c7c52 100644 --- a/swarms/tools/tool_utils.py +++ b/swarms/tools/tool_utils.py @@ -4,7 +4,52 @@ from typing import Any, List from swarms.prompts.tools import SCENARIOS from swarms.tools.tool import BaseTool -from swarms.tools.tool_func_doc_scraper import scrape_tool_func_docs +import inspect +from typing import Callable + +from termcolor import colored + + +def scrape_tool_func_docs(fn: Callable) -> str: + """ + Scrape the docstrings and parameters of a function decorated with `tool` and return a formatted string. + + Args: + fn (Callable): The function to scrape. + + Returns: + str: A string containing the function's name, documentation string, and a list of its parameters. Each parameter is represented as a line containing the parameter's name, default value, and annotation. + """ + try: + # If the function is a tool, get the original function + if hasattr(fn, "func"): + fn = fn.func + + signature = inspect.signature(fn) + parameters = [] + for name, param in signature.parameters.items(): + parameters.append( + f"Name: {name}, Type:" + f" {param.default if param.default is not param.empty else 'None'}," + " Annotation:" + f" {param.annotation if param.annotation is not param.empty else 'None'}" + ) + parameters_str = "\n".join(parameters) + return ( + f"Function: {fn.__name__}\nDocstring:" + f" {inspect.getdoc(fn)}\nParameters:\n{parameters_str}" + ) + except Exception as error: + print( + colored( + ( + f"Error scraping tool function docs {error} try" + " optimizing your inputs with different" + " variables and attempt once more." + ), + "red", + ) + ) def tool_find_by_name(tool_name: str, tools: List[Any]): diff --git a/swarms/utils/json_utils.py b/swarms/utils/json_utils.py index b4e452e4..62dc2323 100644 --- a/swarms/utils/json_utils.py +++ b/swarms/utils/json_utils.py @@ -1,6 +1,5 @@ import json - from pydantic import BaseModel @@ -33,3 +32,19 @@ def extract_json_from_str(response: str): json_start = response.index("{") json_end = response.rfind("}") return json.loads(response[json_start : json_end + 1]) + + +def base_model_to_json(base_model_instance: BaseModel) -> str: + """ + Convert a Pydantic base model instance to a JSON string. + + Args: + base_model_instance (BaseModel): Instance of the Pydantic base model. + + Returns: + str: JSON string representation of the base model instance. + """ + model_dict = base_model_instance.dict() + json_string = json.dumps(model_dict) + + return json_string