diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index bd98222f..e055b770 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -25,7 +25,7 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@e53eb8b103ffcb59469888563dc324e3c8ba6f06 + uses: pypa/gh-action-pypi-publish@81e9d935c883d0b210363ab89cf05f3894778450 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} 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/README.md b/README.md index 1ea16710..acecefee 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,52 @@ print(out) ------ + +# `Agent` with Long Term Memory +`Agent` equipped with quasi-infinite long term memory. Great for long document understanding, analysis, and retrieval. + +```python +from swarms import Agent, ChromaDB, OpenAIChat + +# Making an instance of the ChromaDB class +memory = ChromaDB( + metric="cosine", + n_results=3, + output_dir="results", + docs_folder="docs", +) + +# Initializing the agent with the Gemini instance and other parameters +agent = Agent( + agent_name="Covid-19-Chat", + agent_description=( + "This agent provides information about COVID-19 symptoms." + ), + llm=OpenAIChat(), + max_loops="auto", + autosave=True, + verbose=True, + long_term_memory=memory, + stopping_condition="finish", +) + +# Defining the task and image path +task = ("What are the symptoms of COVID-19?",) + +# Running the agent with the specified task and image +out = agent.run(task) +print(out) + +``` + + + + + + + +---- + ### `SequentialWorkflow` Sequential Workflow enables you to sequentially execute tasks with `Agent` and then pass the output into the next agent and onwards until you have specified your max loops. `SequentialWorkflow` is wonderful for real-world business tasks like sending emails, summarizing documents, and analyzing data. diff --git a/docs/swarms/models/custom_model.md b/docs/swarms/models/custom_model.md new file mode 100644 index 00000000..105dd3d7 --- /dev/null +++ b/docs/swarms/models/custom_model.md @@ -0,0 +1,107 @@ +# How to Create A Custom Language Model + +When working with advanced language models, there might come a time when you need a custom solution tailored to your specific needs. Inheriting from an `AbstractLLM` in a Python framework allows developers to create custom language model classes with ease. This developer guide will take you through the process step by step. + +### Prerequisites + +Before you begin, ensure that you have: + +- A working knowledge of Python programming. +- Basic understanding of object-oriented programming (OOP) in Python. +- Familiarity with language models and natural language processing (NLP). +- The appropriate Python framework installed, with access to `AbstractLLM`. + +### Step-by-Step Guide + +#### Step 1: Understand `AbstractLLM` + +The `AbstractLLM` is an abstract base class that defines a set of methods and properties which your custom language model (LLM) should implement. Abstract classes in Python are not designed to be instantiated directly but are meant to be subclasses. + +#### Step 2: Create a New Class + +Start by defining a new class that inherits from `AbstractLLM`. This class will implement the required methods defined in the abstract base class. + +```python +from swarms import AbstractLLM + +class vLLMLM(AbstractLLM): + pass +``` + +#### Step 3: Initialize Your Class + +Implement the `__init__` method to initialize your custom LLM. You'll want to initialize the base class as well and define any additional parameters for your model. + +```python +class vLLMLM(AbstractLLM): + def __init__(self, model_name='default_model', tensor_parallel_size=1, *args, **kwargs): + super().__init__(*args, **kwargs) + self.model_name = model_name + self.tensor_parallel_size = tensor_parallel_size + # Add any additional initialization here +``` + +#### Step 4: Implement Required Methods + +Implement the `run` method or any other abstract methods required by `AbstractLLM`. This is where you define how your model processes input and returns output. + +```python +class vLLMLM(AbstractLLM): + # ... existing code ... + + def run(self, task, *args, **kwargs): + # Logic for running your model goes here + return "Processed output" +``` + +#### Step 5: Test Your Model + +Instantiate your custom LLM and test it to ensure that it works as expected. + +```python +model = vLLMLM(model_name='my_custom_model', tensor_parallel_size=2) +output = model.run("What are the symptoms of COVID-19?") +print(output) # Outputs: "Processed output" +``` + +#### Step 6: Integrate Additional Components + +Depending on the requirements, you might need to integrate additional components such as database connections, parallel computing resources, or custom processing pipelines. + +#### Step 7: Documentation + +Write comprehensive docstrings for your class and its methods. Good documentation is crucial for maintaining the code and for other developers who might use your model. + +```python +class vLLMLM(AbstractLLM): + """ + A custom language model class that extends AbstractLLM. + + ... more detailed docstring ... + """ + # ... existing code ... +``` + +#### Step 8: Best Practices + +Follow best practices such as error handling, input validation, and resource management to ensure your model is robust and reliable. + +#### Step 9: Packaging Your Model + +Package your custom LLM class into a module or package that can be easily distributed and imported into other projects. + +#### Step 10: Version Control and Collaboration + +Use a version control system like Git to track changes to your model. This makes collaboration easier and helps you keep a history of your work. + +### Conclusion + +By following this guide, you should now have a custom model that extends the `AbstractLLM`. Remember that the key to a successful custom LLM is understanding the base functionalities, implementing necessary changes, and testing thoroughly. Keep iterating and improving based on feedback and performance metrics. + +### Further Reading + +- Official Python documentation on abstract base classes. +- In-depth tutorials on object-oriented programming in Python. +- Advanced NLP techniques and optimization strategies for language models. + +This guide provides the fundamental steps to create custom models using `AbstractLLM`. For detailed implementation and advanced customization, it's essential to dive deeper into the specific functionalities and capabilities of the language model framework you are using. \ No newline at end of file diff --git a/example.py b/example.py index 3d78a20c..4d60edf8 100644 --- a/example.py +++ b/example.py @@ -9,6 +9,7 @@ agent = Agent( streaming_on=True, verbose=True, stopping_token="", + interactive=True, ) # Run the workflow on a task diff --git a/mkdocs.yml b/mkdocs.yml index 6cb6ac3d..255814b2 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: @@ -71,6 +71,7 @@ nav: - AbstractAgent: "swarms/agents/abstractagent.md" - ToolAgent: "swarms/agents/toolagent.md" - swarms.models: + - How to Create A Custom Language Model: "swarms/models/custom_model.md" - Language: - BaseLLM: "swarms/models/base_llm.md" - Overview: "swarms/models/index.md" diff --git a/playground/agents/devin.py b/playground/agents/devin.py new file mode 100644 index 00000000..accb4f65 --- /dev/null +++ b/playground/agents/devin.py @@ -0,0 +1,9 @@ +""" +Plan -> act in a loop until observation is met + + +# Tools +- Terminal +- Text Editor +- Browser +""" diff --git a/playground/demos/ai_acceleerated_learning/Podgraph .py b/playground/demos/ai_acceleerated_learning/Podgraph .py new file mode 100644 index 00000000..70944b31 --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/Podgraph .py @@ -0,0 +1,59 @@ +def test_create_graph(): + """ + Tests that a graph can be created. + """ + graph = create_graph() + assert isinstance(graph, dict) + + +def test_weight_edges(): + """ + Tests that the edges of a graph can be weighted. + """ + graph = create_graph() + weight_edges(graph) + for edge in graph.edges: + assert isinstance(edge.weight, int) + + +def test_create_user_list(): + """ + Tests that a list of all the podcasts that the user has listened to can be created. + """ + user_list = create_user_list() + assert isinstance(user_list, list) + + +def test_find_most_similar_podcasts(): + """ + Tests that the most similar podcasts to a given podcast can be found. + """ + graph = create_graph() + weight_edges(graph) + user_list = create_user_list() + most_similar_podcasts = find_most_similar_podcasts( + graph, user_list + ) + assert isinstance(most_similar_podcasts, list) + + +def test_add_most_similar_podcasts(): + """ + Tests that the most similar podcasts to a given podcast can be added to the user's list. + """ + graph = create_graph() + weight_edges(graph) + user_list = create_user_list() + add_most_similar_podcasts(graph, user_list) + assert len(user_list) > 0 + + +def test_repeat_steps(): + """ + Tests that steps 5-6 can be repeated until the user's list contains the desired number of podcasts. + """ + graph = create_graph() + weight_edges(graph) + user_list = create_user_list() + repeat_steps(graph, user_list) + assert len(user_list) == 10 diff --git a/playground/demos/ai_acceleerated_learning/Vocal.py b/playground/demos/ai_acceleerated_learning/Vocal.py new file mode 100644 index 00000000..85470156 --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/Vocal.py @@ -0,0 +1,46 @@ +import pytest + +def test_create_youtube_account(): + # Arrange + # Act + # Assert + +def test_install_video_editing_software(): + # Arrange + # Act + # Assert + +def test_write_script(): + # Arrange + # Act + # Assert + +def test_gather_footage(): + # Arrange + # Act + # Assert + +def test_edit_video(): + # Arrange + # Act + # Assert + +def test_export_video(): + # Arrange + # Act + # Assert + +def test_upload_video_to_youtube(): + # Arrange + # Act + # Assert + +def test_optimize_video_for_search(): + # Arrange + # Act + # Assert + +def test_share_video(): + # Arrange + # Act + # Assert \ No newline at end of file diff --git a/playground/demos/ai_acceleerated_learning/main.py b/playground/demos/ai_acceleerated_learning/main.py new file mode 100644 index 00000000..44eba542 --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/main.py @@ -0,0 +1,253 @@ +import concurrent +import csv +from swarms import Agent, OpenAIChat +from swarms.memory import ChromaDB +from dotenv import load_dotenv +from swarms.utils.parse_code import extract_code_from_markdown +from swarms.utils.file_processing import create_file +from swarms.utils.loguru_logger import logger + + +# Load ENV +load_dotenv() + +# Gemini +gemini = OpenAIChat() + +# memory +memory = ChromaDB(output_dir="swarm_hackathon") + + +def execute_concurrently(callable_functions: callable, max_workers=5): + """ + Executes callable functions concurrently using multithreading. + + Parameters: + - callable_functions: A list of tuples, each containing the callable function and its arguments. + For example: [(function1, (arg1, arg2), {'kwarg1': val1}), (function2, (), {})] + - max_workers: The maximum number of threads to use. + + Returns: + - results: A list of results returned by the callable functions. If an error occurs in any function, + the exception object will be placed at the corresponding index in the list. + """ + results = [None] * len(callable_functions) + + def worker(fn, args, kwargs, index): + try: + result = fn(*args, **kwargs) + results[index] = result + except Exception as e: + results[index] = e + + with concurrent.futures.ThreadPoolExecutor( + max_workers=max_workers + ) as executor: + futures = [] + for i, (fn, args, kwargs) in enumerate(callable_functions): + futures.append( + executor.submit(worker, fn, args, kwargs, i) + ) + + # Wait for all threads to complete + concurrent.futures.wait(futures) + + return results + + +# Adjusting the function to extract specific column values +def extract_and_create_agents( + csv_file_path: str, target_columns: list +): + """ + Reads a CSV file, extracts "Project Name" and "Lightning Proposal" for each row, + creates an Agent for each, and adds it to the swarm network. + + Parameters: + - csv_file_path: The path to the CSV file. + - target_columns: A list of column names to extract values from. + """ + try: + agents = [] + with open(csv_file_path, mode="r", encoding="utf-8") as file: + reader = csv.DictReader(file) + for row in reader: + project_name = row[target_columns[0]] + lightning_proposal = row[target_columns[1]] + + # Example of creating and adding an agent based on the project name and lightning proposal + agent_name = f"{project_name} agent" + print(agent_name) # For demonstration + + # Create the agent + logger.info("Creating agent...") + + # Design agent + logger.info("Creating design agent...") + design_agent = Agent( + llm=gemini, + agent_name="Design Agent", + max_loops=1, + stopping_token="", + sop=None, + system_prompt=( + "Transform an app idea into step by step very" + " simple algorithmic psuedocode so it can be" + " implemented simply." + ), + long_term_memory=memory, + ) + + # Log the agent + logger.info( + f"Code Agent created: {agent_name} with long term" + " memory" + ) + agent = Agent( + llm=gemini, + agent_name=agent_name, + max_loops=1, + code_interpreter=True, + stopping_token="", + sop=None, + system_prompt=( + "Transform an app idea into a very simple" + " python app in markdown. Return all the" + " python code in a single markdown file." + " Return only code and nothing else." + ), + long_term_memory=memory, + ) + + # Testing agent + logger.info(f"Testing_agent agent: {agent_name}") + agent = Agent( + llm=gemini, + agent_name=agent_name + " testing", + max_loops=1, + stopping_token="", + sop=None, + system_prompt=( + "Create unit tests using pytest based on the" + " code you see, only return unit test code in" + " python using markdown, only return the code" + " and nothing else." + ), + long_term_memory=memory, + ) + + # Log the agent + logger.info( + f"Agent created: {agent_name} with long term" + " memory" + ) + agents.append(agent) + + # Design agent + design_agent_output = design_agent.run( + ( + "Create the algorithmic psuedocode for the" + f" {lightning_proposal} in markdown and" + " return it" + ), + None, + ) + + logger.info( + "Algorithmic psuedocode created:" + f" {design_agent_output}" + ) + + # Create the code for each project + output = agent.run( + ( + "Create the code for the" + f" {lightning_proposal} in python using the" + " algorithmic psuedocode" + f" {design_agent_output} and wrap it in" + " markdown and return it" + ), + None, + ) + print(output) + # Parse the output + output = extract_code_from_markdown(output) + # Create the file + output = create_file(output, f"{project_name}.py") + + # Testing agent + testing_agent_output = agent.run( + ( + "Create the unit tests for the" + f" {lightning_proposal} in python using the" + f" code {output} and wrap it in markdown and" + " return it" + ), + None, + ) + print(testing_agent_output) + + # Parse the output + testing_agent_output = extract_code_from_markdown( + testing_agent_output + ) + # Create the file + testing_agent_output = create_file( + testing_agent_output, f"test_{project_name}.py" + ) + + # Log the project created + logger.info( + f"Project {project_name} created: {output} at" + f" file path {project_name}.py" + ) + print(output) + + # Log the unit tests created + logger.info( + f"Unit tests for {project_name} created:" + f" {testing_agent_output} at file path" + f" test_{project_name}.py" + ) + + print( + f"Agent {agent_name} created and added to the" + " swarm network" + ) + + return agents + + except Exception as e: + logger.error( + "An error occurred while extracting and creating" + f" agents: {e}" + ) + return None + + +# CSV +csv_file = "presentation.csv" + +# Specific columns to extract +target_columns = ["Project Name", "Project Description"] + +# Use the adjusted function +specific_column_values = extract_and_create_agents( + csv_file, target_columns +) + +# Display the extracted column values +print(specific_column_values) + + +# Concurrently execute the function +logger.info( + "Concurrently executing the swarm for each hackathon project..." +) +output = execute_concurrently( + [ + (extract_and_create_agents, (csv_file, target_columns), {}), + ], + max_workers=5, +) +print(output) diff --git a/playground/demos/ai_acceleerated_learning/presentation assistant.py b/playground/demos/ai_acceleerated_learning/presentation assistant.py new file mode 100644 index 00000000..fb03c814 --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/presentation assistant.py @@ -0,0 +1,86 @@ +class MockApp: + def __init__(self): + self.running = True + self.session = None + self.slides = [] + + def main_menu(self): + return input("Choose option: 1. Start, 2. Load, 3. Exit ") + + def start_new_talk(self, title): + self.session = title + self.slides = [] + + def add_slide(self, content): + self.slides.append(content) + + def edit_slide(self, index, content): + self.slides[index] = content + + def delete_slide(self, index): + del self.slides[index] + + def reorder_slides(self, new_order): + self.slides = [self.slides[i] for i in new_order] + + def get_number_of_slides(self): + return len(self.slides) + + # Function to simulate user actions + def simulate_user_action(self, action): + # Placeholder function to simulate user interaction, not part of the actual app code + pass + + +# Testing starting a new talk +def test_start_new_talk(): + app = MockApp() + app.start_new_talk("My New Talk") + assert app.session == "My New Talk" + assert app.slides == [] + + +# Testing adding a slide +def test_add_slide(): + app = MockApp() + app.start_new_talk("Talk 1") + app.add_slide("Slide Content 1") + assert app.slides == ["Slide Content 1"] + + +# Testing editing a slide +def test_edit_slide(): + app = MockApp() + app.start_new_talk("Talk 1") + app.add_slide("Slide Content 1") + app.edit_slide(0, "Updated Slide Content 1") + assert app.slides == ["Updated Slide Content 1"] + + +# Testing deleting a slide +def test_delete_slide(): + app = MockApp() + app.start_new_talk("Talk 1") + app.add_slide("Slide Content 1") + app.add_slide("Slide Content 2") + app.delete_slide(0) + assert app.slides == ["Slide Content 2"] + + +# Testing reordering slides +def test_reorder_slides(): + app = MockApp() + app.start_new_talk("Talk 1") + app.add_slide("Slide Content 1") + app.add_slide("Slide Content 2") + app.reorder_slides([1, 0]) + assert app.slides == ["Slide Content 2", "Slide Content 1"] + + +# Testing the number of slides +def test_slide_count(): + app = MockApp() + app.start_new_talk("Talk 1") + app.add_slide("Slide Content 1") + app.add_slide("Slide Content 2") + assert app.get_number_of_slides() == 2 diff --git a/playground/demos/ai_acceleerated_learning/presentation.csv b/playground/demos/ai_acceleerated_learning/presentation.csv new file mode 100644 index 00000000..66894008 --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/presentation.csv @@ -0,0 +1,15 @@ +Project Name,Team Members,Project Description,Project Link / Code,Team Twitter Handles +presentation assistant,robert nowell,live visual aid for talks,loom,@robertnowell1 +Vocal,"Jeremy Nixon, Amir Gamil, Eliott Hoffenberg, Trina Chatterjee, Ruby Yeh","Educational Video Generation, Prompt -> Youtube Video",,"@jvnixon, @amirbolous, @Eliotthoff, @trina_chatt" +Podgraph ,"DC, Leo, Anupam",Graph based podcast learning,https://github.com/dcsan/kbxt ; https://www.figma.com/file/sui06ZgDGXrHOVlrJDiOD7/Untitled?type=design&node-id=0%3A1&mode=design&t=LnQCl13XroVHVbxD-1,@anupambatra_ | @dcsan +"Listen, chat and learn!!!",James,Chat with a podcast to learn things,https://react.gitwit.dev/run/zfGVjrjsa6ZKaEU1PldW,@jamesmurdza +Recall,Liam & Caden,conversation information retrieval,https://recall-97b8b27a6a92.herokuapp.com/, +VoiceStudyBot,Konrad,Personal tutor to test your knowledge of a book,,@konrad_gnat +Short Form Upskill,"Margarita, Aditya, Johnny",TikTok Scrape and Transcribe ,margro2000/Learn (github.com),https://twitter.com/Marg_Groisman +Rohan,Rohan,Rohan,, +Envision: diagram dataset,Steve,An API to translate any technical concept into diagrams,https://github.com/stephenkfrey/diagrammatic,twitter.com/stevekfrey +Arxiv2Video,Lily Su,Converts an Arxiv web url to a short video,https://github.com/LilySu/Arxiv2Video,@excelsiorpred +Dir Chat,Andy Li,Combine to power of SQL and RAG to serach courses,,@xdotli +Empathy Coach,Ji Young Lim,A chatbot that coches people to make more empathetic conversations,,@jyl1030 +Aimor,Brach Burdick,Platform for assessing and monitoring the psychological wellbeing of a body of students based on conversations with an AI therapist,https://aimor-git-staging-aimor.vercel.app/admin,https://twitter.com/__brach__ +Structured TA bot Generation,Wenxi,Generate structured tutorial chatbot based on video transcript and potentially videos,https://github.com/wenxichen/video2ta , \ No newline at end of file diff --git a/playground/demos/ai_acceleerated_learning/test_Vocal.py b/playground/demos/ai_acceleerated_learning/test_Vocal.py new file mode 100644 index 00000000..b8e1e14f --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/test_Vocal.py @@ -0,0 +1,38 @@ +from ai_acceleerated_learning.Vocal import Vocal + +vocal = Vocal() + + +def test_pass(): + assert ( + vocal.generate_video( + "I love to play basketball, and I am a very good player.", + "basketball", + ) + == "Successfully generated a YouTube video for your prompt: I" + " love to play basketball, and I am a very good player." + ) + + +def test_invalid_sports(): + assert ( + vocal.generate_video( + "I just ate some delicious tacos", "tacos" + ) + == "Invalid sports entered!! Please enter a valid sport." + ) + + +def test_invalid_prompt(): + assert ( + vocal.generate_video(987, "basketball") + == "Invalid prompt entered!! Please enter a valid prompt." + ) + + +def test_not_string(): + assert ( + vocal.generate_video(789, 234) + == "Invalid prompt and sports entered!! Please enter valid" + " prompt and sport." + ) diff --git a/playground/demos/ai_acceleerated_learning/test_presentation assistant.py b/playground/demos/ai_acceleerated_learning/test_presentation assistant.py new file mode 100644 index 00000000..5a27eebd --- /dev/null +++ b/playground/demos/ai_acceleerated_learning/test_presentation assistant.py @@ -0,0 +1,86 @@ +# test_presentation_assistant.py + +import pytest +from presentation_assistant import ( + PresentationAssistant, + SlideNotFoundError, +) + + +@pytest.fixture +def assistant(): + slides = [ + "Welcome to our presentation!", + "Here is the agenda for today.", + "Let's dive into the first topic.", + "Thank you for attending.", + ] + return PresentationAssistant(slides) + + +def test_init(): + slides = ["Slide 1", "Slide 2"] + pa = PresentationAssistant(slides) + assert pa.slides == slides + assert pa.current_slide == 0 + + +def test_next_slide(assistant): + assistant.next_slide() + assert assistant.current_slide == 1 + assistant.next_slide() + assert assistant.current_slide == 2 + + +def test_previous_slide(assistant): + assistant.current_slide = 2 + assistant.previous_slide() + assert assistant.current_slide == 1 + assistant.previous_slide() + assert assistant.current_slide == 0 + + +def test_next_slide_at_end(assistant): + assistant.current_slide = len(assistant.slides) - 1 + with pytest.raises(SlideNotFoundError): + assistant.next_slide() + + +def test_previous_slide_at_start(assistant): + with pytest.raises(SlideNotFoundError): + assistant.previous_slide() + + +def test_go_to_slide(assistant): + assistant.go_to_slide(2) + assert assistant.current_slide == 2 + + +def test_go_to_slide_out_of_range(assistant): + with pytest.raises(SlideNotFoundError): + assistant.go_to_slide(len(assistant.slides)) + + +def test_go_to_slide_negative(assistant): + with pytest.raises(SlideNotFoundError): + assistant.go_to_slide(-1) + + +def test_current_slide_content(assistant): + content = assistant.current_slide_content() + assert content == assistant.slides[0] + assistant.next_slide() + content = assistant.current_slide_content() + assert content == assistant.slides[1] + + +def test_show_slide( + assistant, capsys +): # capsys is a pytest fixture to capture stdout and stderr + assistant.show_slide() + captured = capsys.readouterr() + assert captured.out.strip() == assistant.slides[0] + assistant.next_slide() + assistant.show_slide() + captured = capsys.readouterr() + assert captured.out.strip() == assistant.slides[1] 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/models/custom_model_vllm.py b/playground/models/custom_model_vllm.py new file mode 100644 index 00000000..4f7f2a9b --- /dev/null +++ b/playground/models/custom_model_vllm.py @@ -0,0 +1,89 @@ +from vllm import LLM +from swarms import AbstractLLM, Agent, ChromaDB + + +# Making an instance of the VLLM class +class vLLMLM(AbstractLLM): + """ + This class represents a variant of the Language Model (LLM) called vLLMLM. + It extends the AbstractLLM class and provides additional functionality. + + Args: + model_name (str): The name of the LLM model to use. Defaults to "acebook/opt-13b". + tensor_parallel_size (int): The size of the tensor parallelism. Defaults to 4. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Attributes: + model_name (str): The name of the LLM model. + tensor_parallel_size (int): The size of the tensor parallelism. + llm (LLM): An instance of the LLM class. + + Methods: + run(task: str, *args, **kwargs): Runs the LLM model to generate output for the given task. + + """ + + def __init__( + self, + model_name: str = "acebook/opt-13b", + tensor_parallel_size: int = 4, + *args, + **kwargs + ): + super().__init__(*args, **kwargs) + self.model_name = model_name + self.tensor_parallel_size = tensor_parallel_size + + self.llm = LLM( + model_name=self.model_name, + tensor_parallel_size=self.tensor_parallel_size, + ) + + def run(self, task: str, *args, **kwargs): + """ + Runs the LLM model to generate output for the given task. + + Args: + task (str): The task for which to generate output. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Returns: + str: The generated output for the given task. + + """ + return self.llm.generate(task) + + +# Initializing the agent with the vLLMLM instance and other parameters +model = vLLMLM( + "facebook/opt-13b", + tensor_parallel_size=4, +) + +# Defining the task +task = "What are the symptoms of COVID-19?" + +# Running the agent with the specified task +out = model.run(task) + + +# Integrate Agent +agent = Agent( + agent_name="Doctor agent", + agent_description=( + "This agent provides information about COVID-19 symptoms." + ), + llm=model, + max_loops="auto", + autosave=True, + verbose=True, + long_term_memory=ChromaDB( + metric="cosine", + n_results=3, + output_dir="results", + docs_folder="docs", + ), + stopping_condition="finish", +) \ No newline at end of file diff --git a/playground/structs/custom_model_with_agent.py b/playground/structs/custom_model_with_agent.py new file mode 100644 index 00000000..8849fc41 --- /dev/null +++ b/playground/structs/custom_model_with_agent.py @@ -0,0 +1,29 @@ +from swarms import Agent +from swarms.models.base_llm import AbstractLLM + + +class ExampleLLM(AbstractLLM): + def __init__(): + pass + + def run(self, task: str, *args, **kwargs): + pass + + +## Initialize the workflow +agent = Agent( + llm=ExampleLLM(), + max_loops="auto", + autosave=True, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + interactive=True, +) + +# Run the workflow on a task +agent( + "Generate a transcript for a youtube video on what swarms are!" + " Output a token when done." +) 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 b56df808..625f0e31 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 = "*" @@ -59,6 +59,7 @@ rich = "13.5.2" sqlalchemy = "*" bitsandbytes = "*" pgvector = "*" +cohere = "*" sentence-transformers = "*" peft = "*" psutil = "*" diff --git a/requirements.txt b/requirements.txt index c8432c05..14923f39 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ torch==2.1.1 transformers -pandas==1.5.3 +pandas==2.2.1 langchain==0.0.333 langchain-experimental==0.0.10 httpx==0.24.1 @@ -33,7 +33,7 @@ numpy openai==0.28.0 opencv-python==4.9.0.80 timm -cohere==4.24 +cohere==4.53 torchvision==0.16.1 rich==13.5.2 mkdocs 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/agent.py b/swarms/structs/agent.py index de6c2856..44f38f5d 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -28,6 +28,7 @@ from swarms.utils.data_to_text import data_to_text from swarms.utils.parse_code import extract_code_from_markdown from swarms.utils.pdf_to_text import pdf_to_text from swarms.utils.token_count_tiktoken import limit_tokens_from_string +from swarms.utils.execution_sandbox import execute_code_in_sandbox # Utils @@ -625,6 +626,20 @@ class Agent: response ) + # Code interpreter + if self.code_interpreter: + response = extract_code_from_markdown( + response + ) + # Execute the code in the sandbox + response = execute_code_in_sandbox( + response + ) + response = task + response + response = self.llm( + response, *args, **kwargs + ) + # Add the response to the history history.append(response) @@ -641,7 +656,6 @@ class Agent: evaluated_response = self.evaluator( response ) - out = ( f"Response: {response}\nEvaluated" f" Response: {evaluated_response}" @@ -674,10 +688,6 @@ class Agent: if self.parser: response = self.parser(response) - # If code interpreter is enabled then run the code - if self.code_interpreter: - self.run_code(response) - # If tools are enabled then execute the tools if self.tools: execute_tool_by_name( 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/execution_sandbox.py b/swarms/utils/execution_sandbox.py index 6c65e25a..af6c3840 100644 --- a/swarms/utils/execution_sandbox.py +++ b/swarms/utils/execution_sandbox.py @@ -1,5 +1,7 @@ -import asyncio import logging +import os +import subprocess +import tempfile import traceback from typing import Tuple @@ -51,21 +53,62 @@ async def execute_code_async(code: str) -> Tuple[str, str]: return out, error_message -def execute_code_sandbox( - code: str, async_on: bool = False -) -> Tuple[str, str]: +def execute_code_in_sandbox(code: str, language: str = "python"): """ - Executes the given code in a sandbox environment. + Execute code in a specified language using subprocess and return the results or errors. Args: code (str): The code to be executed. - async_on (bool, optional): Indicates whether to execute the code asynchronously. - Defaults to False. + language (str): The programming language of the code. Currently supports 'python' only. Returns: - Tuple[str, str]: A tuple containing the stdout and stderr outputs of the code execution. + dict: A dictionary containing either the result or any errors. """ - if async_on: - return asyncio.run(execute_code_async(code)) - else: - return execute_code_async(code) + result = {"output": None, "errors": None} + + try: + if language == "python": + # Write the code to a temporary file + with tempfile.NamedTemporaryFile( + delete=False, suffix=".py", mode="w" + ) as tmp: + tmp.write(code) + tmp_path = tmp.name + + # Execute the code in a separate process + process = subprocess.run( + ["python", tmp_path], + capture_output=True, + text=True, + timeout=10, + ) + + # Capture the output and errors + result["output"] = process.stdout + result["errors"] = process.stderr + + else: + # Placeholder for other languages; each would need its own implementation + raise NotImplementedError( + f"Execution for {language} not implemented." + ) + + except subprocess.TimeoutExpired: + result["errors"] = "Execution timed out." + except Exception as e: + result["errors"] = str(e) + finally: + # Ensure the temporary file is removed after execution + if "tmp_path" in locals(): + os.remove(tmp_path) + + return result + + +# # Example usage +# code_to_execute = """ +# print("Hello, world!") +# """ + +# execution_result = execute_code(code_to_execute) +# print(json.dumps(execution_result, indent=4)) 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