diff --git a/.env.example b/.env.example index 48dd0429..840d7866 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,15 @@ -OPENAI_API_KEY="" -WOLFRAM_ALPHA_APPID="" -ZAPIER_NLA_API_KEY="" +OPENAI_API_KEY="your_openai_api_key_here" +WOLFRAM_ALPHA_APPID="your_wolfram_alpha_appid_here" +ZAPIER_NLA_API_KEY="your_zapier_nla_api_key_here" EVAL_PORT=8000 -MODEL_NAME="" -CELERY_BROKER_URL="" +MODEL_NAME="gpt-4" +CELERY_BROKER_URL="redis://localhost:6379" -SERVER="" +SERVER="http://localhost:8000" USE_GPU=True PLAYGROUND_DIR="playground" -OPENAI_API_KEY="your_openai_api_key_here" LOG_LEVEL="INFO" BOT_NAME="Orca" @@ -20,10 +19,8 @@ BING_SEARCH_URL="your_bing_search_url_here" BING_SUBSCRIPTION_KEY="your_bing_subscription_key_here" SERPAPI_API_KEY="your_serpapi_api_key_here" -IFTTTKey="" - -BRAVE_API_KEY="" -SPOONACULAR_KEY="" -HF_API_KEY="Huggingface api key" +IFTTTKey="your_iftttkey_here" -MODEL_NAME="" \ No newline at end of file +BRAVE_API_KEY="your_brave_api_key_here" +SPOONACULAR_KEY="your_spoonacular_key_here" +HF_API_KEY="your_huggingface_api_key_here" diff --git a/PULL_REQUEST_TEMPLATE.yml b/.github/PULL_REQUEST_TEMPLATE.yml similarity index 91% rename from PULL_REQUEST_TEMPLATE.yml rename to .github/PULL_REQUEST_TEMPLATE.yml index 1148e304..b02a7ef7 100644 --- a/PULL_REQUEST_TEMPLATE.yml +++ b/.github/PULL_REQUEST_TEMPLATE.yml @@ -22,5 +22,4 @@ Maintainer responsibilities: If no one reviews your PR within a few days, feel free to kye@apac.ai -See contribution guidelines for more information on how to write/run tests, lint, etc: https://github.com/hwchase17/langchain/blob/master/.github/CONTRIBUTING.md - --> \ No newline at end of file +See contribution guidelines for more information on how to write/run tests, lint, etc: https://github.com/kyegomez/swarms \ No newline at end of file diff --git a/DOCS/CLEAN_CODE.md b/DOCS/CLEAN_CODE.md new file mode 100644 index 00000000..6707b1ff --- /dev/null +++ b/DOCS/CLEAN_CODE.md @@ -0,0 +1,223 @@ +Code is clean if it can be understood easily – by everyone on the team. Clean code can be read and enhanced by a developer other than its original author. With understandability comes readability, changeability, extensibility and maintainability. +_____________________________________ + +## General rules +1. Follow standard conventions. +2. Keep it simple stupid. Simpler is always better. Reduce complexity as much as possible. +3. Boy scout rule. Leave the campground cleaner than you found it. +4. Always find root cause. Always look for the root cause of a problem. + +## Design rules +1. Keep configurable data at high levels. +2. Prefer polymorphism to if/else or switch/case. +3. Separate multi-threading code. +4. Prevent over-configurability. +5. Use dependency injection. +6. Follow Law of Demeter. A class should know only its direct dependencies. + +## Understandability tips +1. Be consistent. If you do something a certain way, do all similar things in the same way. +2. Use explanatory variables. +3. Encapsulate boundary conditions. Boundary conditions are hard to keep track of. Put the processing for them in one place. +4. Prefer dedicated value objects to primitive type. +5. Avoid logical dependency. Don't write methods which works correctly depending on something else in the same class. +6. Avoid negative conditionals. + +## Names rules +1. Choose descriptive and unambiguous names. +2. Make meaningful distinction. +3. Use pronounceable names. +4. Use searchable names. +5. Replace magic numbers with named constants. +6. Avoid encodings. Don't append prefixes or type information. + +## Functions rules +1. Small. +2. Do one thing. +3. Use descriptive names. +4. Prefer fewer arguments. +5. Have no side effects. +6. Don't use flag arguments. Split method into several independent methods that can be called from the client without the flag. + +## Comments rules +1. Always try to explain yourself in code. +2. Don't be redundant. +3. Don't add obvious noise. +4. Don't use closing brace comments. +5. Don't comment out code. Just remove. +6. Use as explanation of intent. +7. Use as clarification of code. +8. Use as warning of consequences. + +## Source code structure +1. Separate concepts vertically. +2. Related code should appear vertically dense. +3. Declare variables close to their usage. +4. Dependent functions should be close. +5. Similar functions should be close. +6. Place functions in the downward direction. +7. Keep lines short. +8. Don't use horizontal alignment. +9. Use white space to associate related things and disassociate weakly related. +10. Don't break indentation. + +## Objects and data structures +1. Hide internal structure. +2. Prefer data structures. +3. Avoid hybrids structures (half object and half data). +4. Should be small. +5. Do one thing. +6. Small number of instance variables. +7. Base class should know nothing about their derivatives. +8. Better to have many functions than to pass some code into a function to select a behavior. +9. Prefer non-static methods to static methods. + +## Tests +1. One assert per test. +2. Readable. +3. Fast. +4. Independent. +5. Repeatable. + +## Code smells +1. Rigidity. The software is difficult to change. A small change causes a cascade of subsequent changes. +2. Fragility. The software breaks in many places due to a single change. +3. Immobility. You cannot reuse parts of the code in other projects because of involved risks and high effort. +4. Needless Complexity. +5. Needless Repetition. +6. Opacity. The code is hard to understand. + + + + + + + + +# Clean Code + +Here are some general principles for writing highly usable, functional, reliable, fast, and scalable code: + +1. **Clear and Understandable:** The code should be written in a way that's easy for others to understand. This includes using clear variable and function names, and including comments to explain complex sections of code. + +2. **Modular and Reusable:** Code should be broken down into small, modular functions and classes that each perform a single task. This makes the code more understandable, and also allows for code reuse. + +3. **Robust Error Handling:** The code should be able to handle all potential errors gracefully, and should never crash unexpectedly. This includes checking for invalid input, catching exceptions, and providing useful error messages. + +4. **Type Handling:** Whenever possible, the code should enforce and check types to prevent type-related errors. This can be done through the use of type hints in languages like Python, or through explicit type checks. + +5. **Logging:** The code should include extensive logging to make it easier to debug and understand what the code is doing. This includes logging any errors that occur, as well as important events or state changes. + +6. **Performance:** The code should be optimized for performance, avoiding unnecessary computation and using efficient algorithms and data structures. This includes profiling the code to identify and optimize performance bottlenecks. + +7. **Scalability:** The code should be designed to scale well as the size of the input data or the number of users increases. This includes using scalable algorithms and data structures, and designing the code to work well in a distributed or parallel computing environment if necessary. + +8. **Testing:** The code should include comprehensive tests to ensure that it works correctly. This includes unit tests for individual functions and classes, as well as integration tests to ensure that the different parts of the code work well together. + +9. **Version Control:** The code should be stored in a version control system like Git, which allows for tracking changes, collaborating with others, and rolling back to a previous state if necessary. + +10. **Documentation:** The codebase should be well-documented, both in terms of comments within the code and external documentation that explains how to use and contribute to the code. + +11. **Continuous Integration/Continuous Deployment (CI/CD):** Implement CI/CD pipelines for automatic testing and deployment. This ensures that any new changes do not break existing functionality and that the latest version of the application is always available for deployment. + +# Examples +1. **Clear and Understandable:** Use meaningful variable and function names. Include comments when necessary. + + ```python + # Good example + def calculate_average(numbers: List[int]) -> float: + """Calculate and return the average of a list of numbers.""" + total = sum(numbers) + count = len(numbers) + return total / count + ``` + + For file and folder names, use descriptive names that relate to their function in your program. For example, a file that contains functions for handling user input might be named `user_input.py`. + +2. **Modular and Reusable:** Write functions for tasks that you perform over and over. + + ```python + def greet_user(name: str): + """Print a greeting to the user.""" + print(f"Hello, {name}!") + ``` + + For folder structure, group related files in the same directory. For example, all test files could be in a `tests` directory. + +3. **Robust Error Handling:** Use try/except blocks to catch and handle errors. + + ```python + def divide_numbers(numerator: float, denominator: float) -> float: + """Divide two numbers and handle division by zero.""" + try: + return numerator / denominator + except ZeroDivisionError: + print("Error: Division by zero.") + return None + ``` + +4. **Type Handling:** Use type hints to specify the type of function arguments and return values. + + ```python + def greet_user(name: str) -> None: + """Greet the user.""" + print(f"Hello, {name}!") + ``` + +5. **Logging:** Use the `logging` module to log events. + + ```python + import logging + + logging.basicConfig(level=logging.INFO) + + def divide_numbers(numerator: float, denominator: float) -> float: + """Divide two numbers and log if division by zero occurs.""" + try: + return numerator / denominator + except ZeroDivisionError: + logging.error("Attempted division by zero.") + return None + ``` + +6. **Performance:** Use built-in functions and data types for better performance. + + ```python + # Using a set to check for membership is faster than using a list + numbers_set = set(numbers) + if target in numbers_set: + print(f"{target} is in the set of numbers.") + ``` + +7. **Scalability:** For scalability, an example might involve using a load balancer or dividing tasks among different workers or threads. This is more of a system design consideration than a single piece of code. + +8. **Testing:** Write tests for your functions. + + ```python + def test_calculate_average(): + assert calculate_average([1, 2, 3, 4]) == 2.5 + ``` + + For tests, you could have a separate `tests` directory. Inside this directory, each test file could be named `test_.py` where `` is the name of the file being tested. + +9. **Version Control:** This point refers to using tools like Git for version control. A simple example would be committing changes to a repository: + + ```bash + git add . + git commit -m "Add function to calculate average" + git push + ``` + +10. **Documentation:** Write docstrings for your functions. + + ```python + def calculate_average(numbers: List[int]) -> float: + """Calculate and return the average of a list of numbers.""" + ... + ``` + + Documentation might be kept in a `docs` directory, with separate files for different topics. + +11. **Continuous Integration/Continuous Deployment (CI/CD):** This is typically handled by a system like Jenkins, GitHub Actions, or GitLab CI/CD. It involves creating a script or configuration file that tells the CI/CD system how to build, test, and deploy your code. For example, a `.github/workflows/main.yml` file for a GitHub Actions workflow. + +Remember, consistency in your naming conventions and organization is key. Having a standard and sticking to it will make your codebase easier to navigate and understand. \ No newline at end of file diff --git a/DOCS/DEVELOPER_PLAN.md b/DOCS/DEVELOPER_PLAN.md new file mode 100644 index 00000000..18d62db5 --- /dev/null +++ b/DOCS/DEVELOPER_PLAN.md @@ -0,0 +1,101 @@ +# Flywheel Effect for Developer Acquisition and Incentivization + +As with the sales model, the developer acquisition and incentivization model also relies on a flywheel effect. This effect is particularly potent in a community-driven ecosystem such as ours, where the value proposition continually grows as more developers join and contribute to our projects. Here's how we could apply this approach: + +## Step 1: Initial Value Proposition for Developers +The starting point of the flywheel is to provide an attractive value proposition for developers. This could include: + +- The ability to work on cutting-edge technology (Swarms, in this case). +- The opportunity to contribute to a community-driven, open-source project. +- The chance to learn from and collaborate with a global network of highly skilled developers. +- An incentivization structure that rewards contributions (more on this later). + +## Step 2: Developer Acquisition +With the initial value proposition in place, we can move on to the actual acquisition of developers. This could be accomplished through: + +- Active recruitment from online developer communities. +- Referral programs that incentivize current contributors to bring in new developers. +- Partnerships with universities, boot camps, and other institutions to attract budding developers. + +## Step 3: Collaboration and Learning +Once developers join our ecosystem, they become part of a collaborative community where they can learn from each other, improve their skills, and work on exciting and meaningful projects. This, in turn, attracts more developers, adding momentum to the flywheel. + +## Step 4: Recognizing and Rewarding Contributions +To keep the flywheel spinning, it's crucial to recognize and reward the contributions made by developers. This can be done in various ways: + +- Monetary rewards: Developers can be paid based on the value their contributions bring to the project. This could be determined through various metrics, such as the complexity of their contributions, the impact on the project, or the amount of their code that gets used in production. + +- Reputation and recognition: The open-source nature of our project means that all contributions are public and can be used by developers to build their professional profiles. Contributors could also be highlighted on our website, in our communications, and at community events. + +- Career advancement: Developers who consistently make valuable contributions could be offered positions of leadership within the project, such as becoming maintainers or joining a steering committee. + +- Agora Tokens: We could create a system of tokens that are earned based on contributions. These tokens could be exchanged for various benefits, such as access to exclusive events, special training, or even physical goods. + +## Step 5: Scaling the Flywheel +With the flywheel in motion, the next step is to scale. As our community grows and our technology improves, we can attract more developers and create more value. This leads to a virtuous cycle of growth, where each new developer adds to the attractiveness of our project, which in turn brings in more developers. + +In essence, this flywheel approach is about creating a community where everyone benefits from each other's contributions. The more value a developer adds, the more they are rewarded. The more developers contribute, the more value is created, attracting even more developers. + +Such a model not only aligns with our values of openness, collaboration, and shared success, but it also gives us a sustainable and scalable method for growing our developer community. It makes Agora not just a place to work, but also a place to learn, grow, and be recognized for one's contributions. This is a powerful way to ensure that we can continue to advance our technology and make a significant impact on the world. + + +# Risks and mitigations + +The open source engineering freelancer model brings with it its own set of potential risks and challenges. Here's an exploration of some of these, along with strategies for mitigation: + +**1. Quality Control:** When dealing with a wide network of freelance contributors, ensuring a consistent standard of quality across all contributions can be challenging. This can be mitigated by implementing rigorous review processes and standards, establishing an automated testing infrastructure, and fostering a culture of quality among contributors. Providing clear contribution guidelines, code style guides, and other resources can help freelancers understand what's expected of them. Providing Educational resources such as sponsoring creators like Yannic, and even making our own courses and then building techno-monasteries where young people can come in and research for free. + +**2. Security Risks:** Open-source projects can be susceptible to malicious contributors, who might introduce vulnerabilities into the codebase. To mitigate this, rigorous code review processes should be in place. Additionally, adopting a "trust but verify" approach, leveraging automated security scanning tools, and conducting periodic security audits can be beneficial. + +**3. Intellectual Property Issues:** Open-source projects can face risks around intellectual property, such as contributors introducing code that infringes on someone else's copyrights. A clear Contributor License Agreement (CLA) should be in place, which contributors need to agree to before their contributions can be accepted. This helps protect the project and its users from potential legal issues. + +**4. Loss of Core Focus:** With numerous contributors focusing on different aspects of the project, there can be a risk of losing sight of the project's core objectives. Maintaining a clear roadmap, having a strong leadership team, and ensuring open and regular communication can help keep the project focused. + +**5. Contributor Burnout:** Freelancers contributing in their free time might face burnout, especially if they feel their contributions aren't being recognized or rewarded. To mitigate this, create a supportive environment where contributors' efforts are acknowledged and rewarded. This might include monetary rewards, but can also include non-monetary rewards like public recognition, advancement opportunities within the project, and so on. + +**6. Fragmentation:** In open source projects, there is a risk of fragmentation where different contributors or groups of contributors might want to take the project in different directions. Strong project governance, a clear roadmap, and open, transparent decision-making processes can help mitigate this risk. + +**7. Dependency on Key Individuals:** If key parts of the project are understood and maintained by only a single contributor, there is a risk if that individual decides to leave or is unable to contribute for some reason. This can be mitigated by ensuring knowledge is shared and responsibilities are spread among multiple contributors. + +Overall, these risks can be managed with proper planning, clear communication, and the implementation of good governance and security practices. It's essential to approach the open source model with a clear understanding of these potential pitfalls and a plan to address them. + +## Plan to Gain Open Source Developers for SWARMS + +Attracting and retaining open-source developers is a challenge that requires a strategic approach. This plan emphasizes delivering value to the developers as well as providing recognition, community, and financial incentives. + +### Step 1: Foster an Engaging and Inclusive Community + +The first step is to foster an engaging and inclusive open-source community around SWARMS. This community should be a place where developers feel welcome and excited to contribute. Regular community events (both online and offline), engaging content, and a supportive environment can help attract and retain developers. + +### Step 2: Provide Clear Contribution Guidelines + +Providing clear and comprehensive contribution guidelines will make it easier for developers to get started. These guidelines should cover the basics of how to set up the development environment, how to submit changes, and how the code review process works. + +### Step 3: Offer Educational Resources and Training + +Providing training and educational resources can help developers grow their skills and contribute more effectively. These resources could include tutorials, webinars, workshops, documentation, and more. + +### Step 4: Establish a Recognition and Reward System + +Recognize and reward the contributions of developers. This could involve public recognition, like featuring contributors on the SWARMS website, as well as financial incentives. Implementing a system where developers earn a share of the revenue from SWARMS based on their contributions can be a strong motivator. + +### Step 5: Implement a Strong Support System + +Offer strong technical support to developers. This could include dedicated channels for developers to ask questions, request feedback, and share their progress. Having core team members available to provide assistance and mentorship can be hugely beneficial. + +### Step 6: Regularly Solicit and Incorporate Feedback + +Regularly ask for feedback from developers and incorporate their suggestions into future developments. This shows developers that their opinions are valued and can lead to improvements in SWARMS. + +## Flywheel for Gaining More Open Source Developers + +Now let's look at the flywheel effect that can result from this plan. The idea of the flywheel is that each part of the process feeds into the next, creating a cycle of growth that becomes self-sustaining over time. + +1. We build an engaging and supportive community around SWARMS. +2. This community attracts more developers who are interested in contributing to SWARMS. +3. As more developers contribute, the quality and scope of SWARMS improve, making it more attractive to potential users. +4. As SWARMS gains more users, the potential revenue from SWARMS increases, allowing for larger rewards to be distributed to developers. +5. The prospect of these rewards attracts even more developers to the SWARMS community. +6. The cycle repeats, with each iteration attracting more developers, improving SWARMS, increasing its user base, and raising potential rewards. + +Through this plan and the resulting flywheel effect, we can attract a strong, committed team of open-source developers to build SWARMS and make it the best it can be. \ No newline at end of file diff --git a/DOCS/DOCUMENTATION.md b/DOCS/DOCUMENTATION.md index 9f761155..699483c8 100644 --- a/DOCS/DOCUMENTATION.md +++ b/DOCS/DOCUMENTATION.md @@ -1,36 +1,96 @@ # Swarms Documentation -## Overview -The Swarm module includes the implementation of two classes, `WorkerNode` and `BossNode`, which respectively represent a worker agent and a boss agent. A worker agent is responsible for completing given tasks, while a boss agent is responsible for creating and managing tasks for the worker agent(s). +## ClassName -## Key Classes +Swarms + +## Purpose + +The Swarms module provides a powerful framework for creating and managing swarms of autonomous agents to accomplish complex tasks. It consists of the `WorkerNode` and `BossNode` classes, along with the `LLM` utility class, which allow you to easily set up and run a swarm of agents to tackle any objective. The module is highly configurable and extensible, providing flexibility to accommodate various use cases. + +## Usage example + +```python +from swarms import Swarms + +api_key = "your_openai_api_key" + +# Initialize Swarms with your API key +swarm = Swarms(api_key=api_key) + +# Define an objective +objective = "Please make a web GUI for using HTTP API server..." + +# Run Swarms +result = swarm.run_swarms(objective) + +print(result) +``` + +## Constructor + +```python +def __init__(self, openai_api_key) +``` + +- `openai_api_key` (required): The API key for OpenAI's models. + +## Methods + +### run_swarms(objective) + +Runs the swarm with the given objective by initializing the worker and boss nodes. + +- `objective` (required): The objective or task to be accomplished by the swarm. + +Returns the result of the swarm execution. + +## Example Usage + +```python +from swarms import Swarms + +api_key = "your_openai_api_key" + +# Initialize Swarms with your API key +swarm = Swarms(api_key=api_key) + +# Define an objective +objective = "Please make a web GUI for using HTTP API server..." + +# Run Swarms +result = swarm.run_swarms(objective) + +print(result) +``` + +## WorkerNode -### WorkerNode The `WorkerNode` class represents an autonomous agent instance that functions as a worker to accomplish complex tasks. It has the ability to search the internet, process and generate images, text, audio, and more. -#### Constructor +### Constructor + ```python def __init__(self, llm, tools, vectorstore) ``` + - `llm` (required): The language model used by the worker node. - `tools` (required): A list of tools available to the worker node. - `vectorstore` (required): The vector store used by the worker node. -#### Methods +### Methods + - `create_agent(ai_name, ai_role, human_in_the_loop, search_kwargs)`: Creates an agent within the worker node. - `add_tool(tool)`: Adds a tool to the worker node. - `run(prompt)`: Runs the worker node to complete a task specified by the prompt. - -#### Example Usage +### Example Usage ```python - - from swarms import worker_node # Your OpenAI API key -api_key = "sk-your api key" +api_key = "your_openai_api_key" # Initialize a WorkerNode with your API key node = worker_node(api_key) @@ -44,98 +104,68 @@ task = node.run(objective) print(task) ``` -### BossNode +## BossNode + The `BossNode` class represents an agent responsible for creating and managing tasks for the worker agent(s). It interacts with the worker node(s) to delegate tasks and monitor their progress. -#### Constructor +### Constructor + ```python def __init__(self, llm, vectorstore, agent_executor, max_iterations) ``` + - `llm` (required): The language model used by the boss node. - `vectorstore` (required): The vector store used by the boss node. - `agent_executor` (required): The agent executor used to execute tasks. - `max_iterations` (required): The maximum number of iterations for task execution. -#### Methods +### Methods + - `create_task(objective)`: Creates a task with the given objective. - `execute_task(task)`: Executes the given task by interacting with the worker agent(s). -### LLM +## LLM + The `LLM` class is a utility class that provides an interface to different language models (LLMs) such as OpenAI's ChatGPT and Hugging Face models. It is used to initialize the language model for the worker and boss nodes. -#### Constructor +### Constructor + ```python def __init__(self, openai_api_key=None, hf_repo_id=None, hf_api_token=None, model_kwargs=None) ``` + - `openai_api_key` (optional): The API key for OpenAI's models. - `hf_repo_id` (optional): The repository ID for the Hugging Face model. - `hf_api_token` (optional): The API token for the Hugging Face model. - `model_kwargs` (optional): Additional keyword arguments to pass to the language model. -#### Methods -- `run(prompt)`: Runs the language model with the given prompt and returns the generated response. - -### Swarms -The `Swarms` class is a wrapper class that encapsulates the functionality of the worker and boss nodes. It provides a convenient way to initialize and run a swarm of agents to accomplish tasks. - -#### Constructor -```python -def __init__(self, openai_api_key) -``` -- `openai_api_key` (required): The API key for OpenAI's models. - -#### Methods -- `run_swarms(objective)`: Runs the swarm with the given objective by initializing the worker and boss nodes. - -## Example Usage -```python -from swarms import Swarms +### Methods -api_key = "sksdsds" - -# Initialize Swarms with your API key -swarm = Swarms(openai_api_key=api_key) - -# Define an objective -objective = """ -Please make a web GUI for using HTTP API server. -The name of it is Swarms. -You can check the server code at ./main.py. -The server is served on localhost:8000. -Users should be able to write text input as 'query' and url array as 'files', and check the response. -Users input form should be delivered in JSON format. -I want it to have neumorphism-style. Serve it on port 4500. - -""" - -# Run Swarms -task = swarm.run_swarms(objective) - -print(task) -``` - -This will create a swarm of agents to complete the given objective. The boss agent will create tasks and delegate them to the worker agent(s) for execution. - -Please make sure to replace `"your_openai_api_key"` with your actual OpenAI API key. +- `run(prompt)`: Runs the language model with the given prompt and returns the generated response. ## Configuration + The Swarms module can be configured by modifying the following parameters: ### WorkerNode + - `llm_class`: The language model class to use for the worker node (default: `ChatOpenAI`). - `temperature`: The temperature parameter for the language model (default: `0.5`). ### BossNode + - `llm_class`: The language model class to use for the boss node (default: `OpenAI`). - `max_iterations`: The maximum number of iterations for task execution (default: `5`). ### LLM + - `openai_api_key`: The API key for OpenAI's models. - `hf_repo_id`: The repository ID for the Hugging Face model. - `hf_api_token`: The API token for the Hugging Face model. - `model_kwargs`: Additional keyword arguments to pass to the language model. ## Tool Configuration + The Swarms module supports various tools that can be added to the worker node for performing specific tasks. The following tools are available: - `DuckDuckGoSearchRun`: A tool for performing web searches. @@ -147,6 +177,7 @@ The Swarms module supports various tools that can be added to the worker node fo Additional tools can be added by extending the functionality of the `Tool` class. ## Advanced Usage + For more advanced usage, you can customize the tools and parameters according to your specific requirements. The Swarms module provides flexibility and extensibility to accommodate various use cases. For example, you can add your own custom tools by extending the `Tool` class and adding them to the worker node. You can also modify the prompt templates used by the boss node to customize the interaction between the boss and worker agents. @@ -154,4 +185,56 @@ For example, you can add your own custom tools by extending the `Tool` class and Please refer to the source code and documentation of the Swarms module for more details and examples. ## Conclusion -The Swarms module provides a powerful framework for creating and managing swarms of autonomous agents to accomplish complex tasks. With the WorkerNode and BossNode classes, along with the LLM utility class, you can easily set up and run a swarm of agents to tackle any objective. The module is highly configurable and extensible, allowing you to tailor it to your specific needs. \ No newline at end of file + +The Swarms module provides a powerful framework for creating and managing swarms of autonomous agents to accomplish complex tasks. With the `WorkerNode` and `BossNode` classes, along with the `LLM` utility class, you can easily set up and run a swarm of agents to tackle any objective. The module is highly configurable and extensible, allowing you to tailor it to your specific needs. + + +## LLM +### Purpose +The `LLM` class provides an interface to different language models (LLMs) such as OpenAI's ChatGPT and Hugging Face models. It allows you to initialize and run a language model with a given prompt and obtain the generated response. + +### Systems Understanding +The `LLM` class takes an OpenAI API key or Hugging Face repository ID and API token as input. It uses these credentials to initialize the language model, either from OpenAI's models or from a specific Hugging Face repository. The language model can then be run with a prompt, and the generated response is returned. + +### Usage Example +```python +from swarms import LLM + +# Create an instance of LLM with OpenAI API key +llm_instance = LLM(openai_api_key="your_openai_key") + +# Run the language model with a prompt +result = llm_instance.run("Who won the FIFA World Cup in 1998?") +print(result) + +# Create an instance of LLM with Hugging Face repository ID and API token +llm_instance = LLM(hf_repo_id="google/flan-t5-xl", hf_api_token="your_hf_api_token") + +# Run the language model with a prompt +result = llm_instance.run("Who won the FIFA World Cup in 1998?") +print(result) +``` + +### Constructor +```python +def __init__(self, openai_api_key: Optional[str] = None, + hf_repo_id: Optional[str] = None, + hf_api_token: Optional[str] = None, + model_kwargs: Optional[dict] = None) +``` +- `openai_api_key` (optional): The API key for OpenAI's models. +- `hf_repo_id` (optional): The repository ID for the Hugging Face model. +- `hf_api_token` (optional): The API token for the Hugging Face model. +- `model_kwargs` (optional): Additional keyword arguments to pass to the language model. + +### Methods +- `run(prompt: str) -> str`: Runs the language model with the given prompt and returns the generated response. + +### Args +- `prompt` (str): The prompt to be passed to the language model. + +### Returns +- `result` (str): The generated response from the language model. + +## Conclusion +The `LLM` class provides a convenient way to initialize and run different language models using either OpenAI's API or Hugging Face models. By providing the necessary credentials and a prompt, you can obtain the generated response from the language model. \ No newline at end of file diff --git a/DOCS/FLYWHEEL.md b/DOCS/FLYWHEEL.md new file mode 100644 index 00000000..ac8851be --- /dev/null +++ b/DOCS/FLYWHEEL.md @@ -0,0 +1,101 @@ +# The Swarms Flywheel + +1. **Building a Supportive Community:** Initiate by establishing an engaging and inclusive open-source community for both developers and sales freelancers around Swarms. Regular online meetups, webinars, tutorials, and sales training can make them feel welcome and encourage contributions and sales efforts. + +2. **Increased Contributions and Sales Efforts:** The more engaged the community, the more developers will contribute to Swarms and the more effort sales freelancers will put into selling Swarms. + +3. **Improvement in Quality and Market Reach:** More developer contributions mean better quality, reliability, and feature offerings from Swarms. Simultaneously, increased sales efforts from freelancers boost Swarms' market penetration and visibility. + +4. **Rise in User Base:** As Swarms becomes more robust and more well-known, the user base grows, driving more revenue. + +5. **Greater Financial Incentives:** Increased revenue can be redirected to offer more significant financial incentives to both developers and salespeople. Developers can be incentivized based on their contribution to Swarms, and salespeople can be rewarded with higher commissions. + +6. **Attract More Developers and Salespeople:** These financial incentives, coupled with the recognition and experience from participating in a successful project, attract more developers and salespeople to the community. + +7. **Wider Adoption of Swarms:** An ever-improving product, a growing user base, and an increasing number of passionate salespeople accelerate the adoption of Swarms. + +8. **Return to Step 1:** As the community, user base, and sales network continue to grow, the cycle repeats, each time speeding up the flywheel. + + +```markdown + +---------------------+ + | Building a | + | Supportive | <--+ + | Community | | + +--------+-----------+ | + | | + v | + +--------+-----------+ | + | Increased | | + | Contributions & | | + | Sales Efforts | | + +--------+-----------+ | + | | + v | + +--------+-----------+ | + | Improvement in | | + | Quality & Market | | + | Reach | | + +--------+-----------+ | + | | + v | + +--------+-----------+ | + | Rise in User | | + | Base | | + +--------+-----------+ | + | | + v | + +--------+-----------+ | + | Greater Financial | | + | Incentives | | + +--------+-----------+ | + | | + v | + +--------+-----------+ | + | Attract More | | + | Developers & | | + | Salespeople | | + +--------+-----------+ | + | | + v | + +--------+-----------+ | + | Wider Adoption of | | + | Swarms |----+ + +---------------------+ +``` + + +# Potential Risks and Mitigations: + +1. **Insufficient Contributions or Quality of Work**: Open-source efforts rely on individuals being willing and able to spend time contributing. If not enough people participate, or the work they produce is of poor quality, the product development could stall. + * **Mitigation**: Create a robust community with clear guidelines, support, and resources. Provide incentives for quality contributions, such as a reputation system, swag, or financial rewards. Conduct thorough code reviews to ensure the quality of contributions. + +2. **Lack of Sales Results**: Commission-based salespeople will only continue to sell the product if they're successful. If they aren't making enough sales, they may lose motivation and cease their efforts. + * **Mitigation**: Provide adequate sales training and resources. Ensure the product-market fit is strong, and adjust messaging or sales tactics as necessary. Consider implementing a minimum commission or base pay to reduce risk for salespeople. + +3. **Poor User Experience or User Adoption**: If users don't find the product useful or easy to use, they won't adopt it, and the user base won't grow. This could also discourage salespeople and contributors. + * **Mitigation**: Prioritize user experience in the product development process. Regularly gather and incorporate user feedback. Ensure robust user support is in place. + +4. **Inadequate Financial Incentives**: If the financial rewards don't justify the time and effort contributors and salespeople are putting in, they will likely disengage. + * **Mitigation**: Regularly review and adjust financial incentives as needed. Ensure that the method for calculating and distributing rewards is transparent and fair. + +5. **Security and Compliance Risks**: As the user base grows and the software becomes more complex, the risk of security issues increases. Moreover, as contributors from various regions join, compliance with various international laws could become an issue. + * **Mitigation**: Establish strong security practices from the start. Regularly conduct security audits. Seek legal counsel to understand and adhere to international laws and regulations. + +## Activation Plan for the Flywheel: + +1. **Community Building**: Begin by fostering a supportive community around Swarms. Encourage early adopters to contribute and provide feedback. Create comprehensive documentation, community guidelines, and a forum for discussion and support. + +2. **Sales and Development Training**: Provide resources and training for salespeople and developers. Make sure they understand the product, its value, and how to effectively contribute or sell. + +3. **Increase Contributions and Sales Efforts**: Encourage increased participation by highlighting successful contributions and sales, rewarding top contributors and salespeople, and regularly communicating about the project's progress and impact. + +4. **Iterate and Improve**: Continually gather and implement feedback to improve Swarms and its market reach. The better the product and its alignment with the market, the more the user base will grow. + +5. **Expand User Base**: As the product improves and sales efforts continue, the user base should grow. Ensure you have the infrastructure to support this growth and maintain a positive user experience. + +6. **Increase Financial Incentives**: As the user base and product grow, so too should the financial incentives. Make sure rewards continue to be competitive and attractive. + +7. **Attract More Contributors and Salespeople**: As the financial incentives and success of the product increase, this should attract more contributors and salespeople, further feeding the flywheel. + +Throughout this process, it's important to regularly reassess and adjust your strategy as necessary. Stay flexible and responsive to changes in the market, user feedback, and the evolving needs of the community. \ No newline at end of file diff --git a/DOCS/SALES.md b/DOCS/SALES.md new file mode 100644 index 00000000..e7f24a19 --- /dev/null +++ b/DOCS/SALES.md @@ -0,0 +1,367 @@ +# Sales Documentation + +## Small Businesses + +Small businesses often lack the resources to hire a dedicated team of data analysts and AI experts. This is where Swarms steps in. With our platform, these businesses can automate many of the tasks that previously required manual effort or expert knowledge. Our strategy for engaging small businesses involves showcasing the simplicity and cost-effectiveness of Swarms. + +### Stage 1: Awareness and Education +* Questions: Have you considered implementing AI solutions? Are you aware of how AI can help your business? +* Commitments: Schedule a demo of Swarms. + +### Stage 2: Engagement and Evaluation +* Questions: Have you identified areas where AI can improve efficiency? How do you currently manage these tasks? +* Commitments: Conduct a trial run of Swarms on a select project. + +### Stage 3: Adoption and Integration +* Questions: Are you satisfied with the results of the trial run? Are you ready to implement Swarms more broadly? +* Commitments: Purchase a subscription and begin the full-scale integration of Swarms. + +## Medium-Sized Enterprises + +Medium-sized enterprises typically have some level of AI integration but often struggle with scalability. Swarms can offer these organizations a path to seamlessly scale their existing AI capabilities. Our strategy for engaging medium-sized enterprises involves demonstrating how Swarms can take their current AI solutions to the next level. + +### Stage 1: Awareness and Potential Evaluation +* Questions: Are you looking to scale your current AI solutions? Are you satisfied with the performance of your current AI tools? +* Commitments: Arrange a personalized demonstration of Swarms. + +### Stage 2: Engagement and Testing +* Questions: Have you identified the specific areas where your AI solutions need scaling? Are you open to piloting Swarms in these areas? +* Commitments: Run a pilot program using Swarms. + +### Stage 3: Adoption and Expansion +* Questions: Did the pilot program meet your expectations? Are you ready to expand the use of Swarms across your organization? +* Commitments: Commit to a long-term partnership and full integration of Swarms. + +## Large Corporations + +Large corporations typically have extensive AI capabilities, but they may struggle with coordination and efficiency. Swarms can help streamline these organizations' AI operations. Our strategy for engaging large corporations involves demonstrating how Swarms can enhance the efficiency of their AI ecosystems. + +### Stage 1: Awareness and Relevance Evaluation +* Questions: Are you experiencing inefficiencies with your existing AI operations? Have you considered solutions to improve coordination among your AI tools? +* Commitments: Organize an executive briefing session about Swarms. + +### Stage 2: Engagement and Trial +* Questions: Can you identify specific use cases for Swarms in your organization? Are you willing to conduct a trial run of Swarms? +* Commitments: Implement a trial run of Swarms for selected use cases. + +### Stage 3: Adoption and Wide-Scale Implementation +* Questions: Was the trial run of Swarms successful? Are you ready to implement Swarms throughout your organization? +* Commitments: Form a strategic alliance and proceed with wide-scale implementation of Swarms. + +Remember, this is more than just a transaction. It's a partnership. And like any good partnership, it's built on trust, communication, and a shared vision. We're excited to embark on this journey with you, and we're committed to supporting you every step of the way. + + + +# SPIN SOP + +This is a detailed customer journey roadmap and Standard Operating Procedure for selling Swarms to businesses of varying sizes. The SPIN selling method (Situation, Problem, Implication, Need-payoff) will be incorporated throughout the document to provide a comprehensive approach. We'll explore a scenario that begins with cold emailing and culminates in offering theoretical consultation package deals. + +**1. Lead Generation and Cold Outreach** + +Our journey begins with identifying potential leads that could benefit from the capabilities of Swarms. This step involves researching potential clients based on their industry, size, digital footprint, and overall potential to benefit from AI automation. + +Upon identifying a potential client, initiate contact with a tailored cold email. This email should: + +- Grab the recipient's attention (Subject line is crucial) +- Introduce Swarms and its potential benefits (unique selling proposition) +- Propose a discovery call or meeting to discuss how Swarms could be beneficial + +An example could be: + +*Subject: Elevate Your Business with the Power of AI Swarm Technology* + +Dear [Recipient's Name], + +I represent Agora, a pioneer in the field of cooperative AI. Our latest innovation, Swarms, harnesses the power of AI to drive efficiency, streamline operations, and ultimately boost profitability. I would love to have a conversation with you to discuss how this cutting-edge technology can specifically help [Their Company Name]. + +Are you available for a quick call next week? + +Best regards, +[Your Name] + +**2. Discovery Call** + +The aim of the discovery call is to learn about the potential client's business and identify their needs using the SPIN selling method: + +*SITUATION* - Get an understanding of the client's current operations, their use of technology, and their overall business landscape. + +*PROBLEM* - Identify any potential areas where the client might be facing challenges - these could be inefficiencies, high operating costs, or unmet customer needs. + +*IMPLICATION* - Discuss the consequences of these problems, such as reduced profitability or customer dissatisfaction. + +*NEED-PAYOFF* - Finally, demonstrate how Swarms can address these issues and the benefits it will bring to their business. + +**3. Follow-Up and Tailored Proposal** + +After gaining a deeper understanding of the client's needs, follow up with a detailed proposal that outlines how Swarms can specifically address their challenges. The proposal should highlight: + +- How Swarms fits into their current operations +- A projection of improvements and potential return on investment +- The steps involved in the implementation process + +**4. Theoretical Consultation Packages** + +Introduce consultation packages at this stage to provide further value and assure the client of continued support during the Swarms integration journey. The packages could include: + +- *Swarms Starter Pack*: Ideal for small businesses. Includes initial setup and integration, basic training, and a month of technical support. +- *Swarms Business Pack*: Suited for medium-sized businesses. Offers advanced setup, employee training, a dedicated support manager, and three months of technical support. +- *Swarms Enterprise Pack*: For large corporations. Includes customized setup and integration, extensive training, a dedicated account manager, and six months of priority technical support. + +**5. Demonstration and Commitment** + +Offer a demonstration to show Swarms in action. This could be a simulated use-case relevant to the client's industry or a walk-through of the platform. + +Post-demonstration, ask for the client's commitment to move to the next step. This could be a meeting with other decision-makers, an agreement to a trial period, or a signed contract. + +**6. Implementation and Onboarding** + +After gaining the client's commitment, the next stage involves the implementation of Swarms in their operations. This will depend on the client's size, the complexity of their operations, and the specifics agreed upon in the proposal. + +**7. Continued Support and Review** + +Continued technical support is essential. Regularly check in with the client, ensure they are getting the most out of Swarms, and address any issues promptly. It's also important to review the impact of Swarms on the client's operations after a set period and adjust the strategy as necessary. + +Selling Swarms is not about pushing a product; it's about offering a solution that can revolutionize businesses. The journey from cold emailing to a fully-fledged partnership should reflect this philosophy at every stage. + + +# Tactics + + +# Value proposition Formula +``` +Dream outcome • Perceived Likelihood +------------------------------------- +Time Delay * Effort & Sacrifice +``` + +Where: + +#### Maximize Value Using Value Equation +❏ Maximize Dream Outcome (solve problem worth solving) + +❏ Maximize Perceived Likelihood of Success (testimonials& proven case studies) + +❏ Minimize Time to Success (How can we make this faster? How can we show progress?) + +❏ Minimize Effort & Sacrifice (How can we make this easier? More Seamless? Convenient?) + +### Swarms Examples + +### Swarms Value Proposition + +"Leap into a new era of productivity with Swarms. Automate up to 50% of your business tasks with the power of cooperative AI, a proven solution trusted by thousands worldwide. With an easy integration process, your dream of seamless automation is just a few steps away." + +Where: + +- The dream outcome is achieving a significant boost in efficiency and productivity by automating 30-50% of your daily business tasks with Swarms. + +- The perceived likelihood of success is backed by our strong track record, with thousands of successful implementations and satisfied customers globally. + +- We've minimized the time delay to success. Swarms provides quick and painless onboarding, with step-by-step support to ensure smooth integration into your existing workflow. + +- The effort and sacrifice required is significantly less than traditional methods. Swarms is designed for ease of use, requiring minimal technical know-how. Plus, our dedicated support team is always at hand to ensure a seamless experience. + +In essence, Swarms makes the dream of comprehensive business automation an attainable reality. Join thousands of our active users in harnessing the power of cooperative AI, and transform your business operations with us today. + +### Value prop SWARMS +``` +We've helped thousands of people just like you automate 30% of their activities with Swarms. And, all it takes to get started is a fast simple onboarding flow that asks you to integrate your tools and datasources. +``` + +In today's competitive landscape, organizations of all sizes are continually seeking ways to automate routine tasks, streamline processes, and make data-driven decisions. Enter Swarms, a revolutionary AI-based technology that leverages the power of multiple autonomous agents to perform tasks with unprecedented speed and accuracy. + +This guide lays out a SPIN (Situation, Problem, Implication, Need-payoff) approach to selling Swarms, taking you through a step-by-step journey from cold outreach to closing the deal. + +#2 +Cold Outreach + +Our first step is to generate interest in Swarms, and we do this through personalized, value-driven outreach. Focus on how Swarms can solve their pain points and bring value to their organization. + +Situation Questions: +- Do you currently use any AI or machine learning tools in your organization? +- How are you managing tasks that could be automated or require large-scale data analysis? + +Problem Questions: +- Are there any specific challenges in managing these tasks manually or using traditional AI models? +- How much time and resources are you currently dedicating to these tasks? + +Implication Questions: +- What is the cost of not addressing these challenges or improving these processes? +- How does this affect your team’s productivity, your operational efficiency, or your competitive advantage? + +Need-payoff Questions: +- How would your organization benefit from automating these tasks or making them more efficient? +- Could an AI-based tool that leverages the power of multiple autonomous agents be beneficial for your organization? + +#3 +Discovery Calls + +Once you've generated interest and scheduled a discovery call, dive deeper into their business operations, their pain points, and their goals. Establish a clear understanding of what they need and how Swarms can fulfill those needs. + +Situation Questions: +- Could you tell me more about your current workflows and operational processes? +- What is the biggest challenge your team is facing in managing these workflows? + +Problem Questions: +- Have you ever encountered situations where the scale or complexity of tasks was overwhelming for your existing systems? +- Are there any tasks that you believe require a level of intelligence or speed that your current solutions can’t provide? + +Implication Questions: +- How does this affect your growth, competitiveness, or profitability in the long term? +- What are the ripple effects of these challenges on other aspects of your business? + +Need-payoff Questions: +- Would a solution that can handle tasks of any scale or complexity efficiently and accurately be of value to your team? +- How would such a solution impact your operational efficiency, team productivity, and bottom line? + +#4 +Product Demonstration + +This is the stage where you showcase the capabilities of Swarms, demonstrating its features and how it can be applied to their specific use cases. Show, don't tell. + +Situation Questions: +- Can you share a few tasks that you believe could be significantly improved with automation or intelligent processing? +- What features or functionalities are you looking for in a solution to improve these tasks? + +Problem Questions: +- Are there any specific issues that you expect to arise if these tasks are managed with your current systems? +- Have past solutions failed to deliver on your expectations in any way? + +Implication Questions: +- What are the potential consequences if these issues are not addressed or if the tasks are not improved? +- How does this affect your team’s morale, your customer satisfaction, or your market position? + +Need-payoff Questions: +- Would you be interested in a solution that can automate these tasks, provide intelligent processing, and scale according to your needs? +- How would such a solution change the way your team works and the outcomes they achieve? + +#5 +Proposal and Negotiation + +Once they've seen Swarms in action, it's time to present a tailored proposal that highlights the value of Swarms for their organization. Always be ready to negotiate, but remember, the focus is on value, not price. + +Situation Questions: +- What does your budget look like for a solution like Swarms? +- What are the key factors you'll consider in making your decision? + +Problem Questions: +- Are there any concerns or roadblocks that you think might prevent you from moving forward with Swarms? +- Have budget constraints or cost issues affected your ability to implement effective solutions in the past? + +Implication Questions: +- If cost or resource constraints continue to limit your ability to implement effective solutions, how will this impact your organization in the long term? +- Are you prepared to deal with the ramifications of continued inefficiencies or challenges? + +Need-payoff Questions: +- How would investing in Swarms impact your budget compared to the potential return on investment? +- How much value do you place on a solution that can transform the way you manage tasks, improve efficiency, and drive growth? + +#6 +Closing the Deal + +Closing the deal is about more than just signing a contract. It’s about setting the stage for a long-term partnership, ensuring they see the value in Swarms not just as a product, but as a key part of their business strategy. + +Situation Questions: +- Are you ready to move forward with implementing Swarms in your organization? +- What expectations do you have from Swarms in the initial phase? + +Problem Questions: +- Are there any final concerns or questions you have that could prevent us from moving forward? +- Is there anything that’s still unclear about how Swarms works or the value it can bring to your organization? + +Implication Questions: +- If these concerns or uncertainties are not addressed, how will it affect your decision? +- Are you willing to overlook the potential value Swarms could bring due to these concerns? + +Need-payoff Questions: +- How can we address these concerns to make Swarms a part of your organization's growth story? +- Can we agree on the fact that Swarms, with its unique capabilities, could significantly boost your organization's operational efficiency and competitiveness? + +#7 +Consultation Packages + +As part of our commitment to ensure our clients extract the maximum value from Swarms, we offer several consultation packages. These packages are designed to provide continuous support as you integrate Swarms into your workflows and processes, helping you overcome any challenges and optimize the system for your specific needs. + +Package 1 - Initial Setup & Training: Our team of experts will assist you in setting up Swarms, train your team on its functionalities and features, and provide support as you start to use the system. + +Package 2 - Optimization & Fine-tuning: As you use Swarms, we'll work closely with you to optimize the system for your specific tasks and workflows, ensuring you extract the maximum value from the platform. + +Package 3 - Ongoing Support & Upgrades: We provide continuous support to address any challenges you encounter and ensure you always have access to the + + latest upgrades and improvements to Swarms. + +Remember, Swarms isn't just a product; it's a partnership. We're committed to working with you every step of the way, ensuring you harness the full power of cooperative AI to transform your organization. + + + + + +# Open Source Salesperson Onboarding Experience + +Creating an efficient, streamlined, and effective onboarding experience for open source salespeople is essential to minimize time and maximize engagement. Drawing inspiration from the simplicity and user-focus of Steve Jobs, this document proposes an onboarding flow that is effortless, engaging, and educational. + +## Landing Page + +The journey starts with a landing page that is clean, crisp, and intuitively designed. A minimalistic aesthetic, akin to Apple's design philosophy, helps the user focus on what's important. The landing page should contain: + +- A bold, clear headline, expressing the value proposition for becoming an open source salesperson for Swarms. +- A short video or animation introducing Swarms and the opportunity for the salespeople. +- Clear call-to-action (CTA) buttons to start the onboarding process or to learn more. + +## Interactive Learning Modules + +Once the user clicks on the CTA, they're taken to an interactive course platform. This platform should feature short, digestible video modules covering a range of essential topics, including: + +1. An Introduction to Swarms: An engaging video that explains the concept, the value it brings to businesses, and the open-source ethos driving it. + +2. Understanding the Technology: A simple, jargon-free explanation of the underlying technology, how it works, and why it works that way. Emphasis should be on benefits rather than technical intricacies. + +3. Successful Sales Strategies: Sharing effective sales techniques and strategies that have worked for Swarms, along with case studies and testimonials. + +4. Navigating Customer Conversations: Guidance on how to effectively communicate with potential customers, including understanding their needs, presenting Swarms as a solution, and addressing objections. + +After each module, the user is asked to answer a few questions to ensure understanding and engagement. This also helps in identifying potential areas of confusion or difficulty. + +## Personal Interaction + +Once the user completes all the modules and successfully answers the questions, they're invited to schedule a one-on-one call with a member of APAC AI or an experienced open source sales freelancer. This serves as a validation step, providing personalized feedback and guidance to ensure the salesperson is ready to start selling Swarms. + +Throughout this journey, the focus should be on simplicity and intuitiveness. Just like Steve Jobs did with Apple's products, the onboarding experience should be so seamless and enjoyable that it's almost invisible, allowing the user to focus on what truly matters – learning about Swarms and how to sell it. + + + +# Open Source Salesperson Onboarding: Post Course Completion + +### For more assistance check out these resources + +* [Pricing Package](https://www.acquisition.com/hubfs/Offer%20Checklists%20-%20PDF%20Downloads/Pricing-Value-Checklist.pdf?hsLang=en) + +*[Alex Hormozi](https://www.acquisition.com/offers-value-equation) + +Once a salesperson has completed the initial course and had their one-on-one session, the onboarding continues to the next phase – preparing them for sales activities and ensuring they have the necessary tools and resources. + +## Access to CRM and Sales Tools + +Every salesperson is provided with access to a CRM (Customer Relationship Management) system. This CRM would be a simplified, user-friendly system that allows them to manage their prospects, track their interactions, and monitor their sales progress. They would also receive training on how to effectively use the CRM to streamline their sales activities. + +## Sales Resources + +Salespeople would be provided with a suite of sales resources, including Swarms product brochures, case studies, presentations, and a script guideline. They would also be given access to a community forum where they can connect with other salespeople, share experiences, ask questions, and learn from each other. + +## Making a Sale + +In terms of the actual sale process, the salesperson would direct the customer to a unique landing page on the APAC.ai website. This landing page would be specifically designed for the sales journey, and it would allow the customer to input their salesperson's ID during the checkout process. + +This ID linking mechanism is critical, as it ensures that the salesperson gets credited for any sales they bring in. Once a sale is made, the salesperson's commission gets credited to their account. + +## Post-Sale and Account Management + +Post-sale, the salesperson's role transitions to more of an account manager. They become the primary point of contact for the customer, responsible for ensuring customer satisfaction, handling any issues or concerns, and identifying opportunities for upselling or cross-selling. + +The salesperson would also receive a recurring revenue from their accounts. This not only incentivizes them to maintain a good relationship with their customers but also rewards them for the ongoing value they provide. + +## Feedback and Performance Reviews + +Periodic performance reviews would be conducted to provide feedback to the salespeople and help them improve. These reviews would also be an opportunity to recognize top performers and share their success stories with the wider community. + +Overall, the objective is to create a smooth, rewarding, and self-sustaining sales ecosystem. Salespeople are empowered with the tools, resources, and incentives they need to succeed, and in return, they help drive the growth and success of Swarms. It's a win-win scenario that has the potential to dramatically accelerate Swarms' market penetration and customer adoption. \ No newline at end of file diff --git a/DOCS/SALESPEOPLE_PLAN.md b/DOCS/SALESPEOPLE_PLAN.md new file mode 100644 index 00000000..219a66f0 --- /dev/null +++ b/DOCS/SALESPEOPLE_PLAN.md @@ -0,0 +1,69 @@ +# **Open Source Freelancer Salespeople Recruitment Plan** + +In the spirit of Jeff Bezos' philosophy of thinking long-term, customer obsession, and commitment to operational excellence, here is a strategic plan to attract open-source freelancer salespeople to Swarms. + +1. **Promote the Vision**: A compelling vision is the cornerstone of any recruitment strategy. Share the vision and mission of Swarms – its potential to revolutionize AI and digital automation – on every possible platform. The goal is to attract freelancers who are excited about the potential of AI and are eager to be part of this revolution. + +2. **Compensation Structure**: Offer a highly competitive, commission-based compensation structure. This could include a base rate for each sale, as well as performance-based bonuses and incentives for high-performing salespeople. Make it clear that the better they do, the more they earn. + +3. **Comprehensive Training**: Ensure all salespeople receive comprehensive training about Swarms, its capabilities, and the potential benefits it can offer to businesses. The more knowledgeable they are about the product, the better they can sell it. + +4. **Collaborative Community**: Foster a community of open-source freelancer salespeople. This community will provide a platform for salespeople to exchange ideas, share success stories, and learn from each other. Foster a culture of collaboration and continuous learning. + +5. **Clear Communication**: Be clear about expectations, targets, and performance metrics. Provide regular feedback and recognition to keep salespeople motivated and aligned with the company's goals. + +6. **Sales Tools & Resources**: Equip salespeople with the necessary tools and resources they need to sell effectively. This might include sales scripts, customer personas, case studies, product demos, and any other material that can aid them in their sales efforts. + +7. **Marketing Support**: In parallel to sales efforts, invest in marketing initiatives to build brand awareness and generate leads. The higher the brand visibility, the easier it will be for salespeople to sell the product. + +8. **Advocate Program**: Introduce an advocate program where salespeople get additional rewards for bringing in more salespeople. This will not only increase the sales force but also instill a sense of ownership and involvement among salespeople. + +**Flywheel Research Diagram** + +Building a flywheel involves understanding and leveraging the compounding effect of a circular, connected process, where each stage fuels the next. Here's a conceptualization of a Swarms Sales Flywheel: + +1. **Training & Empowerment**: Start by attracting freelance salespeople and providing comprehensive training and resources. As salespeople gain competence, they become better at selling Swarms. + +2. **Sales**: As sales increase, so do the visibility of Swarms and the earnings of the salespeople. This attracts more clients and more potential salespeople. + +3. **Client Success**: Successful clients serve as testimonials and case studies, helping to validate the product and making it easier for salespeople to sell Swarms. Success stories and increased visibility generate more interest among potential salespeople. + +4. **Community & Advocacy**: A growing community of successful salespeople, satisfied clients, and a strong product fuels advocacy. Salespeople are more likely to recommend the opportunity to other potential salespeople. + +5. **Growth**: As the community and client base grow, so do the opportunities for salespeople. Increased earnings and a stronger product reputation attract more salespeople, turning the flywheel faster. + +6. **Back to Training & Empowerment**: The increased interest from potential salespeople leads back to the first stage of the flywheel – training and empowering more salespeople. + +The key to the flywheel's momentum is ensuring each stage is performed effectively, creating a virtuous cycle that builds momentum over time. It relies on customer success, salesperson success, and product success, all fueling each other to keep the flywheel spinning. + + + +# Risks and Mitigations +Embracing an open source salesforce represents an innovative approach and can have significant benefits, including scalability, diversity, and cost-effectiveness. However, there are potential risks that need to be considered and mitigated. Here they are, along with strategies for managing them: + +**1. Brand Representation:** In an open source sales model, you can't control who represents your brand, as anyone can essentially become a salesperson. This can pose a risk if salespeople don't present the product or brand accurately, or don't uphold company values in their interactions. + + *Mitigation Strategy:* Provide clear brand guidelines, sales training, and codes of conduct that salespeople must adhere to. Regular monitoring and feedback can help ensure compliance. Also, introduce a certification process to officially recognize salespeople who demonstrate their understanding of the product and brand. + +**2. Product Misrepresentation:** Salespeople may overpromise or misrepresent the product to close a deal, leading to customer dissatisfaction and damage to the brand. + + *Mitigation Strategy:* Ensure that comprehensive and accurate product information is readily available. Provide clear guidelines on what can and cannot be promised to customers. Regularly update salespeople on product developments so their knowledge remains accurate. + +**3. Variable Quality of Salespeople:** In an open-source model, the quality of salespeople can vary widely, which may lead to inconsistent customer experiences. + + *Mitigation Strategy:* Offer comprehensive training and provide sales scripts or guidelines to ensure a uniform sales approach. Monitor performance and provide feedback to help salespeople improve. + +**4. Competition and Infighting:** Salespeople may compete with each other for the same leads, causing conflicts and damaging team cohesion. + + *Mitigation Strategy:* Create a clear system for lead assignment and territory division to prevent overlaps. Promote a collaborative rather than competitive culture, emphasizing the collective success of the team over individual achievements. + +**5. Data Security and Privacy:** With more individuals having access to company and customer information, the risk of data breaches increases. + + *Mitigation Strategy:* Provide training on data security and privacy policies. Use secure systems for data access and sharing. Regularly audit and monitor data access to detect any potential breaches. + +**6. Lack of Control:** The company may have less control over an open-source salesforce compared to an in-house team, leading to potential inconsistencies and inefficiencies. + + *Mitigation Strategy:* Regular communication and feedback are crucial. Use a performance management system to set expectations, track progress, and identify areas for improvement. + +Ultimately, the key is to adopt a long-term perspective, just like Jeff Bezos. Invest in training and building relationships with the salespeople. Foster a culture of customer obsession, and instill a sense of ownership and responsibility in the salespeople. Just as with any other risk, these can be mitigated with careful planning, continuous monitoring, and regular feedback. + diff --git a/README.md b/README.md index 2e94ec24..1e9cb8a6 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ Welcome to Swarms - the future of AI, where we leverage the power of autonomous
- -[![GitHub issues](https://img.shields.io/github/issues/kyegomez/swarms)](https://github.com/kyegomez/swarms/issues) [![GitHub forks](https://img.shields.io/github/forks/kyegomez/swarms)](https://github.com/kyegomez/swarms/network) [![GitHub stars](https://img.shields.io/github/stars/kyegomez/swarms)](https://github.com/kyegomez/swarms/stargazers) [![GitHub license](https://img.shields.io/github/license/kyegomez/swarms)](https://github.com/kyegomez/swarms/blob/main/LICENSE) +[![GitHub issues](https://img.shields.io/github/issues/kyegomez/swarms)](https://github.com/kyegomez/swarms/issues) [![GitHub forks](https://img.shields.io/github/forks/kyegomez/swarms)](https://github.com/kyegomez/swarms/network) [![GitHub stars](https://img.shields.io/github/stars/kyegomez/swarms)](https://github.com/kyegomez/swarms/stargazers) [![GitHub license](https://img.shields.io/github/license/kyegomez/swarms)](https://github.com/kyegomez/swarms/blob/main/LICENSE)[![GitHub star chart](https://img.shields.io/github/stars/kyegomez/swarms?style=social)](https://star-history.com/#kyegomez/swarms) +[![Dependency Status](https://img.shields.io/librariesio/github/kyegomez/swarms)](https://libraries.io/github/kyegomez/swarms) [![Downloads](https://static.pepy.tech/badge/swarms/month)](https://pepy.tech/project/swarms) ### Share on Social Media -[![Share on Twitter](https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Share%20%40kyegomez/swarms)](https://twitter.com/intent/tweet?text=Check%20out%20this%20amazing%20AI%20project:%20&url=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms) [![Share on Facebook](https://img.shields.io/badge/Share-%20facebook-blue)](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms) [![Share on LinkedIn](https://img.shields.io/badge/Share-%20linkedin-blue)](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms&title=&summary=&source=) +[![](https://img.shields.io/badge/badge-preview-success.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBpZD0iZGlzY29yZCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjQgMjQiPgogIDxwYXRoIGQ9Ik0xOC41OSw1Ljg5Yy0xLjIzLS41Ny0yLjU0LS45OS0zLjkyLTEuMjMtLjE3LC4zLS4zNywuNzEtLjUsMS4wNC0xLjQ2LS4yMi0yLjkxLS4yMi00LjM0LDAtLjE0LS4zMy0uMzQtLjc0LS41MS0xLjA0LTEuMzgsLjI0LTIuNjksLjY2LTMuOTIsMS4yMy0yLjQ4LDMuNzQtMy4xNSw3LjM5LTIuODIsMTAuOTgsMS42NSwxLjIzLDMuMjQsMS45Nyw0LjgxLDIuNDYsLjM5LS41MywuNzMtMS4xLDEuMDMtMS42OS0uNTctLjIxLTEuMTEtLjQ4LTEuNjItLjc5LC4xNC0uMSwuMjctLjIxLC40LS4zMSwzLjEzLDEuNDYsNi41MiwxLjQ2LDkuNjEsMCwuMTMsLjExLC4yNiwuMjEsLjQsLjMxLS41MSwuMzEtMS4wNiwuNTctMS42MiwuNzksLjMsLjU5LC42NCwxLjE2LDEuMDMsMS42OSwxLjU3LS40OSwzLjE3LTEuMjMsNC44MS0yLjQ2LC4zOS00LjE3LS42Ny03Ljc4LTIuODItMTAuOThaTTguODQsMTQuNjdjLS45NCwwLTEuNzEtLjg3LTEuNzEtMS45NHMuNzUtMS45NCwxLjcxLTEuOTQsMS43MiwuODcsMS43MSwxLjk0YzAsMS4wNi0uNzUsMS45NC0xLjcxLDEuOTRabTYuMzEsMGMtLjk0LDAtMS43MS0uODctMS43MS0xLjk0cy43NS0xLjk0LDEuNzEtMS45NCwxLjcyLC44NywxLjcxLDEuOTRjMCwxLjA2LS43NSwxLjk0LTEuNzEsMS45NFoiLz4KPC9zdmc%2B)](https://discord.gg/qUtxnK2NMf)[![Share on Twitter](https://img.shields.io/twitter/url/https/twitter.com/cloudposse.svg?style=social&label=Share%20%40kyegomez/swarms)](https://twitter.com/intent/tweet?text=Check%20out%20this%20amazing%20AI%20project:%20&url=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms) [![Share on Facebook](https://img.shields.io/badge/Share-%20facebook-blue)](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms) [![Share on LinkedIn](https://img.shields.io/badge/Share-%20linkedin-blue)](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms&title=&summary=&source=) [![Share on Reddit](https://img.shields.io/badge/-Share%20on%20Reddit-orange)](https://www.reddit.com/submit?url=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms&title=Swarms%20-%20the%20future%20of%20AI) [![Share on Hacker News](https://img.shields.io/badge/-Share%20on%20Hacker%20News-orange)](https://news.ycombinator.com/submitlink?u=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms&t=Swarms%20-%20the%20future%20of%20AI) [![Share on Pinterest](https://img.shields.io/badge/-Share%20on%20Pinterest-red)](https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms&media=https%3A%2F%2Fexample.com%2Fimage.jpg&description=Swarms%20-%20the%20future%20of%20AI) [![Share on WhatsApp](https://img.shields.io/badge/-Share%20on%20WhatsApp-green)](https://api.whatsapp.com/send?text=Check%20out%20Swarms%20-%20the%20future%20of%20AI%20%23swarms%20%23AI%0A%0Ahttps%3A%2F%2Fgithub.com%2Fkyegomez%2Fswarms) @@ -117,8 +117,6 @@ The ports you can use are 4500 and 6500. swarm.run_swarms(objective) ``` -This will create and execute a task to write a summary about the latest news on quantum computing. The result will be the summary of the news. - --- ## Share with your Friends @@ -191,12 +189,17 @@ Remember, our roadmap is a guide, and we encourage you to bring your own ideas a # EcoSystem +[![Star History Chart](https://api.star-history.com/svg?repos=kyegomez/swarms&type=Date)](https://star-history.com/#kyegomez/swarms) + + * [The-Compiler, compile natural language into serene, reliable, and secure programs](https://github.com/kyegomez/the-compiler) *[The Replicator, an autonomous swarm that conducts Multi-Modal AI research by creating new underlying mathematical operations and models](https://github.com/kyegomez/The-Replicator) * Make a swarm that checks arxviv for papers -> checks if there is a github link -> then implements them and checks them +* [SwarmLogic, where a swarm is your API, database, and backend!](https://github.com/kyegomez/SwarmLogic) + --- # Demos @@ -211,7 +214,3 @@ Remember, our roadmap is a guide, and we encourage you to bring your own ideas a - - - - diff --git a/api/container.py b/api/container.py index 6d32c6d4..251c697a 100644 --- a/api/container.py +++ b/api/container.py @@ -5,13 +5,13 @@ from typing import Dict, List from fastapi.templating import Jinja2Templates -from swarms.agents.utils.manager import AgentManager -from swarms.utils.utils import BaseHandler, FileHandler, FileType +from swarms.agents.utils.AgentManager import AgentManager +from swarms.utils.main import BaseHandler, FileHandler, FileType from swarms.tools.main import CsvToDataframe, ExitConversation, RequestsGet, CodeEditor, Terminal from swarms.tools.main import BaseToolSet -from swarms.utils.utils import StaticUploader +from swarms.utils.main import StaticUploader BASE_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) os.chdir(BASE_DIR / os.environ["PLAYGROUND_DIR"]) diff --git a/pyproject.txt b/misc/pyproject.txt similarity index 100% rename from pyproject.txt rename to misc/pyproject.txt diff --git a/misc/swarms.txt b/misc/swarms.txt new file mode 100644 index 00000000..19b9ade0 --- /dev/null +++ b/misc/swarms.txt @@ -0,0 +1,386 @@ + + + + + + + + + + + + + + + + + + + + + + + + +# class Swarms: +# def __init__(self, openai_api_key): +# self.openai_api_key = openai_api_key + +# def initialize_llm(self, llm_class, temperature=0.5): +# # Initialize language model +# return llm_class(openai_api_key=self.openai_api_key, temperature=temperature) + +# def initialize_tools(self, llm_class): +# llm = self.initialize_llm(llm_class) +# # Initialize tools +# web_search = DuckDuckGoSearchRun() +# tools = [ +# web_search, +# WriteFileTool(root_dir=ROOT_DIR), +# ReadFileTool(root_dir=ROOT_DIR), + +# process_csv, +# WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), + +# # RequestsGet() +# Tool(name="RequestsGet", func=RequestsGet.get, description="A portal to the internet, Use this when you need to get specific content from a website. Input should be a url (i.e. https://www.google.com). The output will be the text response of the GET request."), + + +# # CodeEditor, +# # Terminal, +# # RequestsGet, +# # ExitConversation + +# #code editor + terminal editor + visual agent +# # Give the worker node itself as a tool + +# ] +# assert tools is not None, "tools is not initialized" +# return tools + +# def initialize_vectorstore(self): +# # Initialize vector store +# embeddings_model = OpenAIEmbeddings(openai_api_key=self.openai_api_key) +# embedding_size = 1536 +# index = faiss.IndexFlatL2(embedding_size) +# return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) + +# def initialize_worker_node(self, worker_tools, vectorstore): +# # Initialize worker node +# llm = self.initialize_llm(ChatOpenAI) +# worker_node = WorkerNode(llm=llm, tools=worker_tools, vectorstore=vectorstore) +# worker_node.create_agent(ai_name="Swarm Worker AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) +# worker_node_tool = Tool(name="WorkerNode AI Agent", func=worker_node.run, description="Input: an objective with a todo list for that objective. Output: your task completed: Please be very clear what the objective and task instructions are. The Swarm worker agent is Useful for when you need to spawn an autonomous agent instance as a worker to accomplish any complex tasks, it can search the internet or write code or spawn child multi-modality models to process and generate images and text or audio and so on") +# return worker_node_tool + +# def initialize_boss_node(self, vectorstore, worker_node): +# # Initialize boss node +# llm = self.initialize_llm(OpenAI) +# todo_prompt = PromptTemplate.from_template("You are a boss planer in a swarm who is an expert at coming up with a todo list for a given objective and then creating an worker to help you accomplish your task. Come up with a todo list for this objective: {objective} and then spawn a worker agent to complete the task for you. Always spawn an worker agent after creating a plan and pass the objective and plan to the worker agent.") +# todo_chain = LLMChain(llm=llm, prompt=todo_prompt) +# tools = [ +# Tool(name="TODO", func=todo_chain.run, description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!"), +# worker_node +# ] +# suffix = """Question: {task}\n{agent_scratchpad}""" +# prefix = """You are an Boss in a swarm who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\n """ +# prompt = ZeroShotAgent.create_prompt(tools, prefix=prefix, suffix=suffix, input_variables=["objective", "task", "context", "agent_scratchpad"],) +# llm_chain = LLMChain(llm=llm, prompt=prompt) +# agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=[tool.name for tool in tools]) +# agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True) +# # return BossNode(return BossNode(llm, vectorstore, agent_executor, max_iterations=5) +# return BossNode(llm, vectorstore, agent_executor, max_iterations=5) + + +# def run_swarms(self, objective, run_as=None): +# try: +# # Run the swarm with the given objective +# worker_tools = self.initialize_tools(OpenAI) +# assert worker_tools is not None, "worker_tools is not initialized" + +# vectorstore = self.initialize_vectorstore() +# worker_node = self.initialize_worker_node(worker_tools, vectorstore) + +# if run_as.lower() == 'worker': +# tool_input = {'prompt': objective} +# return worker_node.run(tool_input) +# else: +# boss_node = self.initialize_boss_node(vectorstore, worker_node) +# task = boss_node.create_task(objective) +# return boss_node.execute_task(task) +# except Exception as e: +# logging.error(f"An error occurred in run_swarms: {e}") +# raise + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#omni agent ===> working +# class Swarms: +# def __init__(self, +# openai_api_key, +# # omni_api_key=None, +# # omni_api_endpoint=None, +# # omni_api_type=None +# ): +# self.openai_api_key = openai_api_key +# # self.omni_api_key = omni_api_key +# # self.omni_api_endpoint = omni_api_endpoint +# # self.omni_api_key = omni_api_type + +# # if omni_api_key and omni_api_endpoint and omni_api_type: +# # self.omni_worker_agent = OmniWorkerAgent(omni_api_key, omni_api_endpoint, omni_api_type) +# # else: +# # self.omni_worker_agent = None + +# def initialize_llm(self): +# # Initialize language model +# return ChatOpenAI(model_name="gpt-4", temperature=1.0, openai_api_key=self.openai_api_key) + +# def initialize_tools(self, llm): +# # Initialize tools +# web_search = DuckDuckGoSearchRun() +# tools = [ +# web_search, +# WriteFileTool(root_dir=ROOT_DIR), +# ReadFileTool(root_dir=ROOT_DIR), +# process_csv, +# WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), +# ] +# # if self.omni_worker_agent: +# # tools.append(self.omni_worker_agent.chat) #add omniworker agent class +# return tools + +# def initialize_vectorstore(self): +# # Initialize vector store +# embeddings_model = OpenAIEmbeddings() +# embedding_size = 1536 +# index = faiss.IndexFlatL2(embedding_size) +# return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) + +# def initialize_worker_node(self, llm, worker_tools, vectorstore): +# # Initialize worker node +# worker_node = WorkerNode(llm=llm, tools=worker_tools, vectorstore=vectorstore) +# worker_node.create_agent(ai_name="AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) +# return worker_node + +# def initialize_boss_node(self, llm, vectorstore, worker_node): +# # Initialize boss node +# todo_prompt = PromptTemplate.from_template("You are a planner who is an expert at coming up with a todo list for a given objective. Come up with a todo list for this objective: {objective}") +# todo_chain = LLMChain(llm=OpenAI(temperature=0), prompt=todo_prompt) +# tools = [ +# Tool(name="TODO", func=todo_chain.run, description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!"), +# worker_node, +# ] +# suffix = """Question: {task}\n{agent_scratchpad}""" +# prefix = """You are an Boss in a swarm who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\n""" +# prompt = ZeroShotAgent.create_prompt(tools, prefix=prefix, suffix=suffix, input_variables=["objective", "task", "context", "agent_scratchpad"],) +# llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt) +# agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=[tool.name for tool in tools]) +# agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True) +# return BossNode(self.openai_api_key, llm, vectorstore, agent_executor, verbose=True, max_iterations=5) + +# def run_swarms(self, objective): +# # Run the swarm with the given objective +# llm = self.initialize_llm() +# worker_tools = self.initialize_tools(llm) +# vectorstore = self.initialize_vectorstore() +# worker_node = self.initialize_worker_node(llm, worker_tools, vectorstore) +# boss_node = self.initialize_boss_node(llm, vectorstore, worker_node) +# task = boss_node.create_task(objective) +# boss_node.execute_task(task) +# worker_node.run_agent(objective) + + + + + + + + + + + + +# class Swarms: +# def __init__(self, num_nodes: int, llm: BaseLLM, self_scaling: bool): +# self.nodes = [WorkerNode(llm) for _ in range(num_nodes)] +# self.self_scaling = self_scaling + +# def add_worker(self, llm: BaseLLM): +# self.nodes.append(WorkerNode(llm)) + +# def remove_workers(self, index: int): +# self.nodes.pop(index) + +# def execute(self, task): +# #placeholer for main execution logic +# pass + +# def scale(self): +# #placeholder for self scaling logic +# pass + + + +#special classes + +# class HierarchicalSwarms(Swarms): +# def execute(self, task): +# pass + + +# class CollaborativeSwarms(Swarms): +# def execute(self, task): +# pass + +# class CompetitiveSwarms(Swarms): +# def execute(self, task): +# pass + +# class MultiAgentDebate(Swarms): +# def execute(self, task): +# pass + + +#======================================> WorkerNode + + +# class MetaWorkerNode: +# def __init__(self, llm, tools, vectorstore): +# self.llm = llm +# self.tools = tools +# self.vectorstore = vectorstore + +# self.agent = None +# self.meta_chain = None + +# def init_chain(self, instructions): +# self.agent = WorkerNode(self.llm, self.tools, self.vectorstore) +# self.agent.create_agent("Assistant", "Assistant Role", False, {}) + +# def initialize_meta_chain(): +# meta_template = """ +# Assistant has just had the below interactions with a User. Assistant followed their "Instructions" closely. Your job is to critique the Assistant's performance and then revise the Instructions so that Assistant would quickly and correctly respond in the future. + +# #### + +# {chat_history} + +# #### + +# Please reflect on these interactions. + +# You should first critique Assistant's performance. What could Assistant have done better? What should the Assistant remember about this user? Are there things this user always wants? Indicate this with "Critique: ...". + +# You should next revise the Instructions so that Assistant would quickly and correctly respond in the future. Assistant's goal is to satisfy the user in as few interactions as possible. Assistant will only see the new Instructions, not the interaction history, so anything important must be summarized in the Instructions. Don't forget any important details in the current Instructions! Indicate the new Instructions by "Instructions: ...". +# """ + +# meta_prompt = PromptTemplate( +# input_variables=["chat_history"], template=meta_template +# ) + +# meta_chain = LLMChain( +# llm=OpenAI(temperature=0), +# prompt=meta_prompt, +# verbose=True, +# ) +# return meta_chain + +# def meta_chain(self): +# #define meta template and meta prompting as per your needs +# self.meta_chain = initialize_meta_chain() + + +# def get_chat_history(chain_memory): +# memory_key = chain_memory.memory_key +# chat_history = chain_memory.load_memory_variables(memory_key)[memory_key] +# return chat_history + + +# def get_new_instructions(meta_output): +# delimiter = "Instructions: " +# new_instructions = meta_output[meta_output.find(delimiter) + len(delimiter) :] +# return new_instructions + + +# def main(self, task, max_iters=3, max_meta_iters=5): +# failed_phrase = "task failed" +# success_phrase = "task succeeded" +# key_phrases = [success_phrase, failed_phrase] + +# instructions = "None" +# for i in range(max_meta_iters): +# print(f"[Episode {i+1}/{max_meta_iters}]") +# self.initialize_chain(instructions) +# output = self.agent.perform('Assistant', {'request': task}) +# for j in range(max_iters): +# print(f"(Step {j+1}/{max_iters})") +# print(f"Assistant: {output}") +# print(f"Human: ") +# human_input = input() +# if any(phrase in human_input.lower() for phrase in key_phrases): +# break +# output = self.agent.perform('Assistant', {'request': human_input}) +# if success_phrase in human_input.lower(): +# print(f"You succeeded! Thanks for playing!") +# return +# self.initialize_meta_chain() +# meta_output = self.meta_chain.predict(chat_history=self.get_chat_history()) +# print(f"Feedback: {meta_output}") +# instructions = self.get_new_instructions(meta_output) +# print(f"New Instructions: {instructions}") +# print("\n" + "#" * 80 + "\n") +# print(f"You failed! Thanks for playing!") + + +# #init instance of MetaWorkerNode +# meta_worker_node = MetaWorkerNode(llm=OpenAI, tools=tools, vectorstore=vectorstore) + + +# #specify a task and interact with the agent +# task = "Provide a sysmatic argument for why we should always eat past with olives" +# meta_worker_node.main(task) + + +####################################################################### => Boss Node +####################################################################### => Boss Node +####################################################################### => Boss Node diff --git a/swarms/agents/misc/utils.py b/misc/utils.txt similarity index 100% rename from swarms/agents/misc/utils.py rename to misc/utils.txt diff --git a/analysis/task1.txt b/playground/analysis/task1.txt similarity index 100% rename from analysis/task1.txt rename to playground/analysis/task1.txt diff --git a/analysis/worker_agent.txt b/playground/analysis/worker_agent.txt similarity index 100% rename from analysis/worker_agent.txt rename to playground/analysis/worker_agent.txt diff --git a/playground/ultranode_example.py b/playground/ultranode_example.py index 24935fb5..a3f62c8a 100644 --- a/playground/ultranode_example.py +++ b/playground/ultranode_example.py @@ -1,4 +1,4 @@ -from swarms import UltraNode +from swarms import WorkerUltraUltraNode # Define an objective objective = """ @@ -12,6 +12,6 @@ I want it to have neumorphism-style. Serve it on port 4500. """ -node = UltraNode(objective) +node = WorkerUltraUltraNode(objective) result = node.execute() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 420df298..dca3e347 100644 --- a/requirements.txt +++ b/requirements.txt @@ -93,9 +93,9 @@ tiktoken espnet==202301 espnet_model_zoo==0.1.7 -flask==2.2.3 +# flask==2.2.3 -flask_cors==3.0.10 +# flask_cors==3.0.10 waitress==2.1.2 asteroid diff --git a/swarms/__init__.py b/swarms/__init__.py index 1a936d07..3ae053b9 100644 --- a/swarms/__init__.py +++ b/swarms/__init__.py @@ -1,3 +1,4 @@ # from swarms import Swarms, swarm from swarms.swarms import Swarms, swarm -from swarms.agents import worker_node, UltraNode \ No newline at end of file +from swarms.agents import worker_node +from swarms.agents.workers.WorkerUltraNode import WorkerUltra \ No newline at end of file diff --git a/swarms/agents/.DS_Store b/swarms/agents/.DS_Store new file mode 100644 index 00000000..fc0e967b Binary files /dev/null and b/swarms/agents/.DS_Store differ diff --git a/swarms/agents/__init__.py b/swarms/agents/__init__.py index a328d7e3..94e17d66 100644 --- a/swarms/agents/__init__.py +++ b/swarms/agents/__init__.py @@ -1,3 +1,3 @@ """Agents, workers and bosses""" from ..agents.workers import worker_node -from ..agents.workers.worker_ultranode import UltraNode \ No newline at end of file +from .workers.WorkerUltraNode import WorkerUltraNode \ No newline at end of file diff --git a/swarms/agents/boss/BossNode.py b/swarms/agents/boss/BossNode.py new file mode 100644 index 00000000..a9a8a844 --- /dev/null +++ b/swarms/agents/boss/BossNode.py @@ -0,0 +1,56 @@ +from swarms.tools.agent_tools import * +from pydantic import ValidationError +import logging + +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') + +# ---------- Boss Node ---------- +class BossNode: + """ + The BossNode class is responsible for creating and executing tasks using the BabyAGI model. + It takes a language model (llm), a vectorstore for memory, an agent_executor for task execution, and a maximum number of iterations for the BabyAGI model. + """ + def __init__(self, llm, vectorstore, agent_executor, max_iterations): + if not llm or not vectorstore or not agent_executor or not max_iterations: + logging.error("llm, vectorstore, agent_executor, and max_iterations cannot be None.") + raise ValueError("llm, vectorstore, agent_executor, and max_iterations cannot be None.") + self.llm = llm + self.vectorstore = vectorstore + self.agent_executor = agent_executor + self.max_iterations = max_iterations + + try: + self.baby_agi = BabyAGI.from_llm( + llm=self.llm, + vectorstore=self.vectorstore, + task_execution_chain=self.agent_executor, + max_iterations=self.max_iterations, + ) + except ValidationError as e: + logging.error(f"Validation Error while initializing BabyAGI: {e}") + raise + except Exception as e: + logging.error(f"Unexpected Error while initializing BabyAGI: {e}") + raise + + def create_task(self, objective): + """ + Creates a task with the given objective. + """ + if not objective: + logging.error("Objective cannot be empty.") + raise ValueError("Objective cannot be empty.") + return {"objective": objective} + + def execute_task(self, task): + """ + Executes a task using the BabyAGI model. + """ + if not task: + logging.error("Task cannot be empty.") + raise ValueError("Task cannot be empty.") + try: + self.baby_agi(task) + except Exception as e: + logging.error(f"Error while executing task: {e}") + raise \ No newline at end of file diff --git a/swarms/agents/boss/boss_agent.py b/swarms/agents/boss/boss_agent.py deleted file mode 100644 index d8c000bb..00000000 --- a/swarms/agents/boss/boss_agent.py +++ /dev/null @@ -1,27 +0,0 @@ -from swarms.tools.agent_tools import * -from pydantic import ValidationError - -# ---------- Boss Node ---------- -class BossNode: - def __init__(self, llm, vectorstore, agent_executor, max_iterations): - self.llm = llm - self.vectorstore = vectorstore - self.agent_executor = agent_executor - self.max_iterations = max_iterations - - try: - self.baby_agi = BabyAGI.from_llm( - llm=self.llm, - vectorstore=self.vectorstore, - task_execution_chain=self.agent_executor, - max_iterations=self.max_iterations, - ) - except ValidationError as e: - print(f"Validation Error while initializing BabyAGI: {e}") - except Exception as e: - print(f"Unexpected Error while initializing BabyAGI: {e}") - def create_task(self, objective): - return {"objective": objective} - - def execute_task(self, task): - self.baby_agi(task) \ No newline at end of file diff --git a/swarms/agents/utils/Agent.py b/swarms/agents/utils/Agent.py new file mode 100644 index 00000000..e6e6871a --- /dev/null +++ b/swarms/agents/utils/Agent.py @@ -0,0 +1,617 @@ +"""Chain that takes in an input and produces an action and action input.""" +from __future__ import annotations + +import asyncio +import json +import logging +import time +from abc import abstractmethod +from pathlib import Path +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union + +import yaml +from pydantic import BaseModel, root_validator + +from langchain.agents.agent_types import AgentType +from langchain.agents.tools import InvalidTool +from langchain.callbacks.base import BaseCallbackManager +from langchain.callbacks.manager import ( + AsyncCallbackManagerForChainRun, + AsyncCallbackManagerForToolRun, + CallbackManagerForChainRun, + CallbackManagerForToolRun, + Callbacks, +) +from langchain.chains.base import Chain +from langchain.chains.llm import LLMChain +from langchain.input import get_color_mapping +from langchain.prompts.few_shot import FewShotPromptTemplate +from langchain.prompts.prompt import PromptTemplate +from langchain.schema import ( + AgentAction, + AgentFinish, + BaseOutputParser, + BasePromptTemplate, + OutputParserException, +) +from langchain.schema.language_model import BaseLanguageModel +from langchain.schema.messages import BaseMessage +from langchain.tools.base import BaseTool +from langchain.utilities.asyncio import asyncio_timeout + +logger = logging.getLogger(__name__) + + +class BaseSingleActionAgent(BaseModel): + """Base Agent class.""" + + @property + def return_values(self) -> List[str]: + """Return values of the agent.""" + return ["output"] + + def get_allowed_tools(self) -> Optional[List[str]]: + return None + + @abstractmethod + def plan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[AgentAction, AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Action specifying what tool to use. + """ + + @abstractmethod + async def aplan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[AgentAction, AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Action specifying what tool to use. + """ + + @property + @abstractmethod + def input_keys(self) -> List[str]: + """Return the input keys. + + :meta private: + """ + + def return_stopped_response( + self, + early_stopping_method: str, + intermediate_steps: List[Tuple[AgentAction, str]], + **kwargs: Any, + ) -> AgentFinish: + """Return response when agent has been stopped due to max iterations.""" + if early_stopping_method == "force": + # `force` just returns a constant string + return AgentFinish( + {"output": "Agent stopped due to iteration limit or time limit."}, "" + ) + else: + raise ValueError( + f"Got unsupported early_stopping_method `{early_stopping_method}`" + ) + + @classmethod + def from_llm_and_tools( + cls, + llm: BaseLanguageModel, + tools: Sequence[BaseTool], + callback_manager: Optional[BaseCallbackManager] = None, + **kwargs: Any, + ) -> BaseSingleActionAgent: + raise NotImplementedError + + @property + def _agent_type(self) -> str: + """Return Identifier of agent type.""" + raise NotImplementedError + + def dict(self, **kwargs: Any) -> Dict: + """Return dictionary representation of agent.""" + _dict = super().dict() + _type = self._agent_type + if isinstance(_type, AgentType): + _dict["_type"] = str(_type.value) + else: + _dict["_type"] = _type + return _dict + + def save(self, file_path: Union[Path, str]) -> None: + """Save the agent. + + Args: + file_path: Path to file to save the agent to. + + Example: + .. code-block:: python + + # If working with agent executor + agent.agent.save(file_path="path/agent.yaml") + """ + # Convert file to Path object. + if isinstance(file_path, str): + save_path = Path(file_path) + else: + save_path = file_path + + directory_path = save_path.parent + directory_path.mkdir(parents=True, exist_ok=True) + + # Fetch dictionary to save + agent_dict = self.dict() + + if save_path.suffix == ".json": + with open(file_path, "w") as f: + json.dump(agent_dict, f, indent=4) + elif save_path.suffix == ".yaml": + with open(file_path, "w") as f: + yaml.dump(agent_dict, f, default_flow_style=False) + else: + raise ValueError(f"{save_path} must be json or yaml") + + def tool_run_logging_kwargs(self) -> Dict: + return {} + + +class BaseMultiActionAgent(BaseModel): + """Base Agent class.""" + + @property + def return_values(self) -> List[str]: + """Return values of the agent.""" + return ["output"] + + def get_allowed_tools(self) -> Optional[List[str]]: + return None + + @abstractmethod + def plan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[List[AgentAction], AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Actions specifying what tool to use. + """ + + @abstractmethod + async def aplan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[List[AgentAction], AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Actions specifying what tool to use. + """ + + @property + @abstractmethod + def input_keys(self) -> List[str]: + """Return the input keys. + + :meta private: + """ + + def return_stopped_response( + self, + early_stopping_method: str, + intermediate_steps: List[Tuple[AgentAction, str]], + **kwargs: Any, + ) -> AgentFinish: + """Return response when agent has been stopped due to max iterations.""" + if early_stopping_method == "force": + # `force` just returns a constant string + return AgentFinish({"output": "Agent stopped due to max iterations."}, "") + else: + raise ValueError( + f"Got unsupported early_stopping_method `{early_stopping_method}`" + ) + + @property + def _agent_type(self) -> str: + """Return Identifier of agent type.""" + raise NotImplementedError + + def dict(self, **kwargs: Any) -> Dict: + """Return dictionary representation of agent.""" + _dict = super().dict() + _dict["_type"] = str(self._agent_type) + return _dict + + def save(self, file_path: Union[Path, str]) -> None: + """Save the agent. + + Args: + file_path: Path to file to save the agent to. + + Example: + .. code-block:: python + + # If working with agent executor + agent.agent.save(file_path="path/agent.yaml") + """ + # Convert file to Path object. + if isinstance(file_path, str): + save_path = Path(file_path) + else: + save_path = file_path + + directory_path = save_path.parent + directory_path.mkdir(parents=True, exist_ok=True) + + # Fetch dictionary to save + agent_dict = self.dict() + + if save_path.suffix == ".json": + with open(file_path, "w") as f: + json.dump(agent_dict, f, indent=4) + elif save_path.suffix == ".yaml": + with open(file_path, "w") as f: + yaml.dump(agent_dict, f, default_flow_style=False) + else: + raise ValueError(f"{save_path} must be json or yaml") + + def tool_run_logging_kwargs(self) -> Dict: + return {} + + +class AgentOutputParser(BaseOutputParser): + @abstractmethod + def parse(self, text: str) -> Union[AgentAction, AgentFinish]: + """Parse text into agent action/finish.""" + + +class LLMSingleActionAgent(BaseSingleActionAgent): + llm_chain: LLMChain + output_parser: AgentOutputParser + stop: List[str] + + @property + def input_keys(self) -> List[str]: + return list(set(self.llm_chain.input_keys) - {"intermediate_steps"}) + + def dict(self, **kwargs: Any) -> Dict: + """Return dictionary representation of agent.""" + _dict = super().dict() + del _dict["output_parser"] + return _dict + + def plan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[AgentAction, AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Action specifying what tool to use. + """ + output = self.llm_chain.run( + intermediate_steps=intermediate_steps, + stop=self.stop, + callbacks=callbacks, + **kwargs, + ) + return self.output_parser.parse(output) + + async def aplan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[AgentAction, AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Action specifying what tool to use. + """ + output = await self.llm_chain.arun( + intermediate_steps=intermediate_steps, + stop=self.stop, + callbacks=callbacks, + **kwargs, + ) + return self.output_parser.parse(output) + + def tool_run_logging_kwargs(self) -> Dict: + return { + "llm_prefix": "", + "observation_prefix": "" if len(self.stop) == 0 else self.stop[0], + } + + +class Agent(BaseSingleActionAgent): + """Class responsible for calling the language model and deciding the action. + + This is driven by an LLMChain. The prompt in the LLMChain MUST include + a variable called "agent_scratchpad" where the agent can put its + intermediary work. + """ + + llm_chain: LLMChain + output_parser: AgentOutputParser + allowed_tools: Optional[List[str]] = None + + def dict(self, **kwargs: Any) -> Dict: + """Return dictionary representation of agent.""" + _dict = super().dict() + del _dict["output_parser"] + return _dict + + def get_allowed_tools(self) -> Optional[List[str]]: + return self.allowed_tools + + @property + def return_values(self) -> List[str]: + return ["output"] + + def _fix_text(self, text: str) -> str: + """Fix the text.""" + raise ValueError("fix_text not implemented for this agent.") + + @property + def _stop(self) -> List[str]: + return [ + f"\n{self.observation_prefix.rstrip()}", + f"\n\t{self.observation_prefix.rstrip()}", + ] + + def _construct_scratchpad( + self, intermediate_steps: List[Tuple[AgentAction, str]] + ) -> Union[str, List[BaseMessage]]: + """Construct the scratchpad that lets the agent continue its thought process.""" + thoughts = "" + for action, observation in intermediate_steps: + thoughts += action.log + thoughts += f"\n{self.observation_prefix}{observation}\n{self.llm_prefix}" + return thoughts + + def plan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[AgentAction, AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Action specifying what tool to use. + """ + full_inputs = self.get_full_inputs(intermediate_steps, **kwargs) + full_output = self.llm_chain.predict(callbacks=callbacks, **full_inputs) + return self.output_parser.parse(full_output) + + async def aplan( + self, + intermediate_steps: List[Tuple[AgentAction, str]], + callbacks: Callbacks = None, + **kwargs: Any, + ) -> Union[AgentAction, AgentFinish]: + """Given input, decided what to do. + + Args: + intermediate_steps: Steps the LLM has taken to date, + along with observations + callbacks: Callbacks to run. + **kwargs: User inputs. + + Returns: + Action specifying what tool to use. + """ + full_inputs = self.get_full_inputs(intermediate_steps, **kwargs) + full_output = await self.llm_chain.apredict(callbacks=callbacks, **full_inputs) + return self.output_parser.parse(full_output) + + def get_full_inputs( + self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any + ) -> Dict[str, Any]: + """Create the full inputs for the LLMChain from intermediate steps.""" + thoughts = self._construct_scratchpad(intermediate_steps) + new_inputs = {"agent_scratchpad": thoughts, "stop": self._stop} + full_inputs = {**kwargs, **new_inputs} + return full_inputs + + @property + def input_keys(self) -> List[str]: + """Return the input keys. + + :meta private: + """ + return list(set(self.llm_chain.input_keys) - {"agent_scratchpad"}) + + @root_validator() + def validate_prompt(cls, values: Dict) -> Dict: + """Validate that prompt matches format.""" + prompt = values["llm_chain"].prompt + if "agent_scratchpad" not in prompt.input_variables: + logger.warning( + "`agent_scratchpad` should be a variable in prompt.input_variables." + " Did not find it, so adding it at the end." + ) + prompt.input_variables.append("agent_scratchpad") + if isinstance(prompt, PromptTemplate): + prompt.template += "\n{agent_scratchpad}" + elif isinstance(prompt, FewShotPromptTemplate): + prompt.suffix += "\n{agent_scratchpad}" + else: + raise ValueError(f"Got unexpected prompt type {type(prompt)}") + return values + + @property + @abstractmethod + def observation_prefix(self) -> str: + """Prefix to append the observation with.""" + + @property + @abstractmethod + def llm_prefix(self) -> str: + """Prefix to append the LLM call with.""" + + @classmethod + @abstractmethod + def create_prompt(cls, tools: Sequence[BaseTool]) -> BasePromptTemplate: + """Create a prompt for this class.""" + + @classmethod + def _validate_tools(cls, tools: Sequence[BaseTool]) -> None: + """Validate that appropriate tools are passed in.""" + pass + + @classmethod + @abstractmethod + def _get_default_output_parser(cls, **kwargs: Any) -> AgentOutputParser: + """Get default output parser for this class.""" + + @classmethod + def from_llm_and_tools( + cls, + llm: BaseLanguageModel, + tools: Sequence[BaseTool], + callback_manager: Optional[BaseCallbackManager] = None, + output_parser: Optional[AgentOutputParser] = None, + **kwargs: Any, + ) -> Agent: + """Construct an agent from an LLM and tools.""" + cls._validate_tools(tools) + llm_chain = LLMChain( + llm=llm, + prompt=cls.create_prompt(tools), + callback_manager=callback_manager, + ) + tool_names = [tool.name for tool in tools] + _output_parser = output_parser or cls._get_default_output_parser() + return cls( + llm_chain=llm_chain, + allowed_tools=tool_names, + output_parser=_output_parser, + **kwargs, + ) + + def return_stopped_response( + self, + early_stopping_method: str, + intermediate_steps: List[Tuple[AgentAction, str]], + **kwargs: Any, + ) -> AgentFinish: + """Return response when agent has been stopped due to max iterations.""" + if early_stopping_method == "force": + # `force` just returns a constant string + return AgentFinish( + {"output": "Agent stopped due to iteration limit or time limit."}, "" + ) + elif early_stopping_method == "generate": + # Generate does one final forward pass + thoughts = "" + for action, observation in intermediate_steps: + thoughts += action.log + thoughts += ( + f"\n{self.observation_prefix}{observation}\n{self.llm_prefix}" + ) + # Adding to the previous steps, we now tell the LLM to make a final pred + thoughts += ( + "\n\nI now need to return a final answer based on the previous steps:" + ) + new_inputs = {"agent_scratchpad": thoughts, "stop": self._stop} + full_inputs = {**kwargs, **new_inputs} + full_output = self.llm_chain.predict(**full_inputs) + # We try to extract a final answer + parsed_output = self.output_parser.parse(full_output) + if isinstance(parsed_output, AgentFinish): + # If we can extract, we send the correct stuff + return parsed_output + else: + # If we can extract, but the tool is not the final tool, + # we just return the full output + return AgentFinish({"output": full_output}, full_output) + else: + raise ValueError( + "early_stopping_method should be one of `force` or `generate`, " + f"got {early_stopping_method}" + ) + + def tool_run_logging_kwargs(self) -> Dict: + return { + "llm_prefix": self.llm_prefix, + "observation_prefix": self.observation_prefix, + } + + +class ExceptionTool(BaseTool): + name = "_Exception" + description = "Exception tool" + + def _run( + self, + query: str, + run_manager: Optional[CallbackManagerForToolRun] = None, + ) -> str: + return query + + async def _arun( + self, + query: str, + run_manager: Optional[AsyncCallbackManagerForToolRun] = None, + ) -> str: + return query diff --git a/swarms/agents/utils/builder.py b/swarms/agents/utils/AgentBuilder.py similarity index 59% rename from swarms/agents/utils/builder.py rename to swarms/agents/utils/AgentBuilder.py index 5a34275e..fb8ef625 100644 --- a/swarms/agents/utils/builder.py +++ b/swarms/agents/utils/AgentBuilder.py @@ -9,23 +9,32 @@ from langchain.chat_models.base import BaseChatModel from langchain.schema import BaseOutputParser from langchain.callbacks.base import BaseCallbackManager -from .chat_agent import ConversationalChatAgent -from .llm import ChatOpenAI -from .parser import EvalOutputParser +from .ConversationalChatAgent import ConversationalChatAgent +# from .ChatOpenAI import ChatOpenAI +from langchain.chat_models import ChatOpenAI +from .EvalOutputParser import EvalOutputParser class AgentBuilder: - def __init__(self, toolsets: list[BaseToolSet] = []): + def __init__(self, toolsets: list[BaseToolSet] = [], openai_api_key: str = None, serpapi_api_key: str = None, bing_search_url: str = None, bing_subscription_key: str = None): self.llm: BaseChatModel = None self.parser: BaseOutputParser = None self.global_tools: list = None self.toolsets = toolsets - - def build_llm(self, callback_manager: BaseCallbackManager = None): - self.llm = ChatOpenAI( - temperature=0, callback_manager=callback_manager, verbose=True - ) - self.llm.check_access() + self.openai_api_key = openai_api_key or os.getenv("OPENAI_API_KEY") + self.serpapi_api_key = serpapi_api_key or os.getenv('SERPAPI_API_KEY') + self.bing_search_url = bing_search_url or os.getenv('BING_SEARCH_URL') + self.bing_subscription_key = bing_subscription_key or os.getenv('BING_SUBSCRIPTION_KEY') + if not self.openai_api_key: + raise ValueError("OpenAI key is missing, it should either be set as an environment variable or passed as a parameter") + + def build_llm(self, callback_manager: BaseCallbackManager = None, openai_api_key: str = None): + if openai_api_key is None: + openai_api_key = os.getenv('OPENAI_API_KEY') + if openai_api_key is None: + raise ValueError("OpenAI API key is missing. It should either be set as an environment variable or passed as a parameter.") + + self.llm = ChatOpenAI(openai_api_key=openai_api_key, temperature=0.5, callback_manager=callback_manager, verbose=True) def build_parser(self): self.parser = EvalOutputParser() @@ -36,9 +45,11 @@ class AgentBuilder: toolnames = ["wikipedia"] - if os.environ["SERPAPI_API_KEY"]: + + if self.serpapi_api_key: toolnames.append("serpapi") - if os.environ["BING_SEARCH_URL"] and os.environ["BING_SUBSCRIPTION_KEY"]: + + if self.bing_search_url and self.bing_subscription_key: toolnames.append("bing-search") self.global_tools = [ @@ -76,8 +87,8 @@ class AgentBuilder: self.toolsets ), # for names and descriptions ], - system_message=EVAL_PREFIX.format(bot_name=os.environ["BOT_NAME"]), - human_message=EVAL_SUFFIX.format(bot_name=os.environ["BOT_NAME"]), + system_message=EVAL_PREFIX.format(bot_name=os.environ["BOT_NAME"] or 'WorkerUltraNode'), + human_message=EVAL_SUFFIX.format(bot_name=os.environ["BOT_NAME"] or 'WorkerUltraNode'), output_parser=self.parser, max_iterations=30, ) \ No newline at end of file diff --git a/swarms/agents/utils/AgentManager.py b/swarms/agents/utils/AgentManager.py new file mode 100644 index 00000000..e392cfee --- /dev/null +++ b/swarms/agents/utils/AgentManager.py @@ -0,0 +1,115 @@ +from typing import Dict, Optional +import logging + +from celery import Task + +from langchain.agents.agent import AgentExecutor +from langchain.callbacks.manager import CallbackManager +from langchain.chains.conversation.memory import ConversationBufferMemory +from langchain.memory.chat_memory import BaseChatMemory + +from swarms.tools.main import BaseToolSet, ToolsFactory +from .AgentBuilder import AgentBuilder +from .Calback import EVALCallbackHandler, ExecutionTracingCallbackHandler + + +callback_manager_instance = CallbackManager(EVALCallbackHandler()) + + +class AgentManager: + def __init__(self, toolsets: list[BaseToolSet] = []): + if not isinstance(toolsets, list): + raise TypeError("Toolsets must be a list") + self.toolsets: list[BaseToolSet] = toolsets + self.memories: Dict[str, BaseChatMemory] = {} + self.executors: Dict[str, AgentExecutor] = {} + + def create_memory(self) -> BaseChatMemory: + return ConversationBufferMemory(memory_key="chat_history", return_messages=True) + + def get_or_create_memory(self, session: str) -> BaseChatMemory: + if not isinstance(session, str): + raise TypeError("Session must be a string") + if not session: + raise ValueError("Session is empty") + if not (session in self.memories): + self.memories[session] = self.create_memory() + return self.memories[session] + + def create_executor(self, session: str, execution: Optional[Task] = None, openai_api_key: str = None) -> AgentExecutor: + try: + builder = AgentBuilder(self.toolsets) + builder.build_parser() + + + callbacks = [] + eval_callback = EVALCallbackHandler() + eval_callback.set_parser(builder.get_parser()) + callbacks.append(eval_callback) + + if execution: + execution_callback = ExecutionTracingCallbackHandler(execution) + execution_callback.set_parser(builder.get_parser()) + callbacks.append(execution_callback) + + #llm init + callback_manager = CallbackManager(callbacks) + builder.build_llm(callback_manager, openai_api_key) + if builder.llm is None: + raise ValueError('LLM not created') + + builder.build_global_tools() + + #agent init + agent = builder.get_agent() + if not agent: + raise ValueError("Agent not created") + + memory: BaseChatMemory = self.get_or_create_memory(session) + tools = [ + *builder.get_global_tools(), + *ToolsFactory.create_per_session_tools( + self.toolsets, + get_session=lambda: (session, self.executors[session]), + ), + ] + + for tool in tools: + tool.callback_manager = callback_manager + + # # Ensure the 'agent' key is present in the values dictionary + # values = {'agent': agent, 'tools': tools} + + # executor = AgentExecutor.from_agent_and_tools( + # agent=agent, + # tools=tools, + # memory=memory, + # callback_manager=callback_manager, + # verbose=True, + # ) + + # Prepare the arguments for the executor + executor_args = { + 'agent': agent, + 'tools': tools, + 'memory': memory, + 'callback_manager': callback_manager, + 'verbose': True # Or any other value based on your requirement + } + + executor = AgentExecutor.from_agent_and_tools(**executor_args) + + if 'agent' not in executor.__dict__: + executor.__dict__['agent'] = agent + self.executors[session] = executor + + return executor + except Exception as e: + logging.error(f"Error while creating executor: {str(e)}") + raise e + + @staticmethod + def create(toolsets: list[BaseToolSet]) -> "AgentManager": + if not isinstance(toolsets, list): + raise TypeError("Toolsets must be a list") + return AgentManager(toolsets=toolsets) \ No newline at end of file diff --git a/swarms/agents/utils/callback.py b/swarms/agents/utils/Calback.py similarity index 98% rename from swarms/agents/utils/callback.py rename to swarms/agents/utils/Calback.py index d4720fd2..771f370f 100644 --- a/swarms/agents/utils/callback.py +++ b/swarms/agents/utils/Calback.py @@ -2,10 +2,10 @@ from typing import Any, Dict, List, Optional, Union from langchain.callbacks.base import BaseCallbackHandler from langchain.schema import AgentAction, AgentFinish, LLMResult -# from celery import Task +from celery import Task # from ansi import ANSI, Color, Style, dim_multiline -from swarms.utils.utils import ANSI, Color, Style, dim_multiline +from swarms.utils.main import ANSI, Color, Style, dim_multiline from swarms.utils.logger import logger diff --git a/swarms/agents/utils/llm.py b/swarms/agents/utils/ChatOpenAI.py similarity index 98% rename from swarms/agents/utils/llm.py rename to swarms/agents/utils/ChatOpenAI.py index d4e7ee18..a6775c38 100644 --- a/swarms/agents/utils/llm.py +++ b/swarms/agents/utils/ChatOpenAI.py @@ -1,6 +1,7 @@ """OpenAI chat wrapper.""" from __future__ import annotations +import os import logging import sys from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple @@ -17,8 +18,9 @@ from langchain.schema import ( HumanMessage, SystemMessage, ) + from langchain.utils import get_from_dict_or_env -from logger import logger +from swarms.utils.logger import logger from pydantic import BaseModel, Extra, Field, root_validator from tenacity import ( before_sleep_log, @@ -30,7 +32,7 @@ from tenacity import ( # from ansi import ANSI, Color, Style -from swarms.utils.utils import ANSI, Color, Style +from swarms.utils.main import ANSI, Color, Style import os def _create_retry_decorator(llm: ChatOpenAI) -> Callable[[Any], Any]: @@ -138,7 +140,7 @@ class ChatOpenAI(BaseChatModel, BaseModel): """ client: Any #: :meta private: - model_name: str = os.env["MODEL_NAME"] + model_name: str = os.environ.get("MODEL_NAME", "gpt-4") """Model name to use.""" model_kwargs: Dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" diff --git a/swarms/agents/utils/chat_agent.py b/swarms/agents/utils/ConversationalChatAgent.py similarity index 73% rename from swarms/agents/utils/chat_agent.py rename to swarms/agents/utils/ConversationalChatAgent.py index 811cb54d..778e38f5 100644 --- a/swarms/agents/utils/chat_agent.py +++ b/swarms/agents/utils/ConversationalChatAgent.py @@ -1,6 +1,7 @@ from typing import Any, List, Optional, Sequence, Tuple +import logging -from langchain.agents.agent import Agent +from swarms.agents.utils.Agent import Agent from langchain.callbacks.base import BaseCallbackManager from langchain.chains import LLMChain from langchain.schema import BaseOutputParser @@ -20,9 +21,17 @@ from langchain.schema import ( ) from langchain.tools.base import BaseTool + +from langchain.agents.agent import AgentOutputParser +from langchain.schema import AgentAction + + from swarms.prompts.prompts import EVAL_TOOL_RESPONSE + +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') + class ConversationalChatAgent(Agent): """An agent designed to hold a conversation in addition to using tools.""" @@ -31,7 +40,11 @@ class ConversationalChatAgent(Agent): @property def _agent_type(self) -> str: raise NotImplementedError + + def _get_default_output_parser(cls, **kwargs: Any) -> AgentOutputParser: + """Get default output parser for this class.""" + @property def observation_prefix(self) -> str: """Prefix to append the observation with.""" @@ -51,6 +64,17 @@ class ConversationalChatAgent(Agent): output_parser: BaseOutputParser, input_variables: Optional[List[str]] = None, ) -> BasePromptTemplate: + if not isinstance(tools, Sequence): + raise TypeError("Tools must be a sequence") + if not isinstance(system_message, str): + raise TypeError("System message must be a string") + if not isinstance(human_message, str): + raise TypeError("Human message must be a string") + if not isinstance(output_parser, BaseOutputParser): + raise TypeError("Output parser must be an instance of BaseOutputParser") + if input_variables and not isinstance(input_variables, list): + raise TypeError("Input variables must be a list") + tool_strings = "\n".join( [f"> {tool.name}: {tool.description}" for tool in tools] ) @@ -75,7 +99,8 @@ class ConversationalChatAgent(Agent): try: response = self.output_parser.parse(llm_output) return response["action"], response["action_input"] - except Exception: + except Exception as e: + logging.error(f"Error while extracting tool and input: {str(e)}") raise ValueError(f"Could not parse LLM output: {llm_output}") def _construct_scratchpad( @@ -118,9 +143,14 @@ class ConversationalChatAgent(Agent): callback_manager=callback_manager, ) tool_names = [tool.name for tool in tools] - return cls( - llm_chain=llm_chain, - allowed_tools=tool_names, - output_parser=output_parser, - **kwargs, - ) \ No newline at end of file + try: + return cls( + llm_chain=llm_chain, + allowed_tools=tool_names, + output_parser=output_parser, + **kwargs, + ) + except Exception as e: + logging.error(f"Error while creating agent from LLM and tools: {str(e)}") + raise e + \ No newline at end of file diff --git a/swarms/agents/utils/parser.py b/swarms/agents/utils/EvalOutputParser.py similarity index 100% rename from swarms/agents/utils/parser.py rename to swarms/agents/utils/EvalOutputParser.py diff --git a/swarms/agents/utils/__init__.py b/swarms/agents/utils/__init__.py index 3cee74ba..e6fd24dd 100644 --- a/swarms/agents/utils/__init__.py +++ b/swarms/agents/utils/__init__.py @@ -1 +1 @@ -"""Agents""" \ No newline at end of file +"""Agents""" diff --git a/swarms/agents/utils/manager.py b/swarms/agents/utils/manager.py deleted file mode 100644 index 8eb383a7..00000000 --- a/swarms/agents/utils/manager.py +++ /dev/null @@ -1,82 +0,0 @@ -from typing import Dict, Optional -# from celery import Task - -from langchain.agents.agent import AgentExecutor -from langchain.callbacks.manager import CallbackManager -from langchain.callbacks.base import set_handler -from langchain.chains.conversation.memory import ConversationBufferMemory -from langchain.memory.chat_memory import BaseChatMemory - -from swarms.tools.main import BaseToolSet, ToolsFactory - -from .builder import AgentBuilder -from .callback import EVALCallbackHandler, ExecutionTracingCallbackHandler - - -set_handler(EVALCallbackHandler()) - - -class AgentManager: - def __init__( - self, - toolsets: list[BaseToolSet] = [], - ): - self.toolsets: list[BaseToolSet] = toolsets - self.memories: Dict[str, BaseChatMemory] = {} - self.executors: Dict[str, AgentExecutor] = {} - - def create_memory(self) -> BaseChatMemory: - return ConversationBufferMemory(memory_key="chat_history", return_messages=True) - - def get_or_create_memory(self, session: str) -> BaseChatMemory: - if not (session in self.memories): - self.memories[session] = self.create_memory() - return self.memories[session] - - def create_executor( - self, session: str, execution: Optional[Task] = None - ) -> AgentExecutor: - builder = AgentBuilder(self.toolsets) - builder.build_parser() - - callbacks = [] - eval_callback = EVALCallbackHandler() - eval_callback.set_parser(builder.get_parser()) - callbacks.append(eval_callback) - if execution: - execution_callback = ExecutionTracingCallbackHandler(execution) - execution_callback.set_parser(builder.get_parser()) - callbacks.append(execution_callback) - - callback_manager = CallbackManager(callbacks) - - builder.build_llm(callback_manager) - builder.build_global_tools() - - memory: BaseChatMemory = self.get_or_create_memory(session) - tools = [ - *builder.get_global_tools(), - *ToolsFactory.create_per_session_tools( - self.toolsets, - get_session=lambda: (session, self.executors[session]), - ), - ] - - for tool in tools: - tool.callback_manager = callback_manager - - executor = AgentExecutor.from_agent_and_tools( - agent=builder.get_agent(), - tools=tools, - memory=memory, - callback_manager=callback_manager, - verbose=True, - ) - self.executors[session] = executor - return executor - - @staticmethod - def create(toolsets: list[BaseToolSet]) -> "AgentManager": - return AgentManager( - toolsets=toolsets, - ) \ No newline at end of file diff --git a/swarms/agents/utils/prompts.py b/swarms/agents/utils/prompts.py new file mode 100644 index 00000000..c2cbb6dc --- /dev/null +++ b/swarms/agents/utils/prompts.py @@ -0,0 +1,105 @@ +def generate_agent_role_prompt(agent): + """ Generates the agent role prompt. + Args: agent (str): The type of the agent. + Returns: str: The agent role prompt. + """ + prompts = { + "Finance Agent": "You are a seasoned finance analyst AI assistant. Your primary goal is to compose comprehensive, astute, impartial, and methodically arranged financial reports based on provided data and trends.", + "Travel Agent": "You are a world-travelled AI tour guide assistant. Your main purpose is to draft engaging, insightful, unbiased, and well-structured travel reports on given locations, including history, attractions, and cultural insights.", + "Academic Research Agent": "You are an AI academic research assistant. Your primary responsibility is to create thorough, academically rigorous, unbiased, and systematically organized reports on a given research topic, following the standards of scholarly work.", + "Default Agent": "You are an AI critical thinker research assistant. Your sole purpose is to write well written, critically acclaimed, objective and structured reports on given text." + + } + + return prompts.get(agent, "No such agent") + + +def generate_report_prompt(question, research_summary): + """ Generates the report prompt for the given question and research summary. + Args: question (str): The question to generate the report prompt for + research_summary (str): The research summary to generate the report prompt for + Returns: str: The report prompt for the given question and research summary + """ + + return f'"""{research_summary}""" Using the above information, answer the following'\ + f' question or topic: "{question}" in a detailed report --'\ + " The report should focus on the answer to the question, should be well structured, informative," \ + " in depth, with facts and numbers if available, a minimum of 1,200 words and with markdown syntax and apa format. "\ + "Write all source urls at the end of the report in apa format" + +def generate_search_queries_prompt(question): + """ Generates the search queries prompt for the given question. + Args: question (str): The question to generate the search queries prompt for + Returns: str: The search queries prompt for the given question + """ + + return f'Write 4 google search queries to search online that form an objective opinion from the following: "{question}"'\ + f'You must respond with a list of strings in the following format: ["query 1", "query 2", "query 3", "query 4"]' + + +def generate_resource_report_prompt(question, research_summary): + """Generates the resource report prompt for the given question and research summary. + + Args: + question (str): The question to generate the resource report prompt for. + research_summary (str): The research summary to generate the resource report prompt for. + + Returns: + str: The resource report prompt for the given question and research summary. + """ + return f'"""{research_summary}""" Based on the above information, generate a bibliography recommendation report for the following' \ + f' question or topic: "{question}". The report should provide a detailed analysis of each recommended resource,' \ + ' explaining how each source can contribute to finding answers to the research question.' \ + ' Focus on the relevance, reliability, and significance of each source.' \ + ' Ensure that the report is well-structured, informative, in-depth, and follows Markdown syntax.' \ + ' Include relevant facts, figures, and numbers whenever available.' \ + ' The report should have a minimum length of 1,200 words.' + + +def generate_outline_report_prompt(question, research_summary): + """ Generates the outline report prompt for the given question and research summary. + Args: question (str): The question to generate the outline report prompt for + research_summary (str): The research summary to generate the outline report prompt for + Returns: str: The outline report prompt for the given question and research summary + """ + + return f'"""{research_summary}""" Using the above information, generate an outline for a research report in Markdown syntax'\ + f' for the following question or topic: "{question}". The outline should provide a well-structured framework'\ + ' for the research report, including the main sections, subsections, and key points to be covered.' \ + ' The research report should be detailed, informative, in-depth, and a minimum of 1,200 words.' \ + ' Use appropriate Markdown syntax to format the outline and ensure readability.' + +def generate_concepts_prompt(question, research_summary): + """ Generates the concepts prompt for the given question. + Args: question (str): The question to generate the concepts prompt for + research_summary (str): The research summary to generate the concepts prompt for + Returns: str: The concepts prompt for the given question + """ + + return f'"""{research_summary}""" Using the above information, generate a list of 5 main concepts to learn for a research report'\ + f' on the following question or topic: "{question}". The outline should provide a well-structured framework'\ + 'You must respond with a list of strings in the following format: ["concepts 1", "concepts 2", "concepts 3", "concepts 4, concepts 5"]' + + +def generate_lesson_prompt(concept): + """ + Generates the lesson prompt for the given question. + Args: + concept (str): The concept to generate the lesson prompt for. + Returns: + str: The lesson prompt for the given concept. + """ + + prompt = f'generate a comprehensive lesson about {concept} in Markdown syntax. This should include the definition'\ + f'of {concept}, its historical background and development, its applications or uses in different'\ + f'fields, and notable events or facts related to {concept}.' + + return prompt + +def get_report_by_type(report_type): + report_type_mapping = { + 'research_report': generate_report_prompt, + 'resource_report': generate_resource_report_prompt, + 'outline_report': generate_outline_report_prompt + } + return report_type_mapping[report_type] \ No newline at end of file diff --git a/swarms/agents/workers/visual_worker.py b/swarms/agents/workers/MultiModalVisualAgentTool.py similarity index 100% rename from swarms/agents/workers/visual_worker.py rename to swarms/agents/workers/MultiModalVisualAgentTool.py diff --git a/swarms/agents/workers/omni_worker.py b/swarms/agents/workers/OmniWorkerAgent.py similarity index 100% rename from swarms/agents/workers/omni_worker.py rename to swarms/agents/workers/OmniWorkerAgent.py diff --git a/swarms/agents/workers/metaprompt_worker.py b/swarms/agents/workers/PromptWorker.py similarity index 100% rename from swarms/agents/workers/metaprompt_worker.py rename to swarms/agents/workers/PromptWorker.py diff --git a/swarms/agents/workers/WorkerNode.py b/swarms/agents/workers/WorkerNode.py new file mode 100644 index 00000000..f02199f3 --- /dev/null +++ b/swarms/agents/workers/WorkerNode.py @@ -0,0 +1,157 @@ +from swarms.tools.agent_tools import * +from langchain.tools import BaseTool +from typing import Optional, Type + +from langchain.callbacks.manager import ( + AsyncCallbackManagerForToolRun, + CallbackManagerForToolRun, +) +from typing import List, Any, Dict, Optional +from langchain.memory.chat_message_histories import FileChatMessageHistory + +import logging +from pydantic import BaseModel, Extra +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') + + +class WorkerNode: + """Useful for when you need to spawn an autonomous agent instance as a worker to accomplish complex tasks, it can search the internet or spawn child multi-modality models to process and generate images and text or audio and so on""" + + def __init__(self, llm, tools, vectorstore): + if not llm or not tools or not vectorstore: + logging.error("llm, tools, and vectorstore cannot be None.") + raise ValueError("llm, tools, and vectorstore cannot be None.") + + self.llm = llm + self.tools = tools + self.vectorstore = vectorstore + self.agent = None + + def create_agent(self, ai_name="Swarm Worker AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}, verbose=False): + logging.info("Creating agent in WorkerNode") + try: + self.agent = AutoGPT.from_llm_and_tools( + ai_name=ai_name, + ai_role=ai_role, + tools=self.tools, + llm=self.llm, + memory=self.vectorstore.as_retriever(search_kwargs=search_kwargs), + human_in_the_loop=human_in_the_loop, + chat_history_memory=FileChatMessageHistory("chat_history.txt"), + ) + self.agent.chain.verbose = verbose + except Exception as e: + logging.error(f"Error while creating agent: {str(e)}") + raise e + + + def add_tool(self, tool: Tool): + if not isinstance(tool, Tool): + logging.error("Tool must be an instance of Tool.") + raise TypeError("Tool must be an instance of Tool.") + + self.tools.append(tool) + + def run(self, prompt: str) -> str: + if not isinstance(prompt, str): + logging.error("Prompt must be a string.") + raise TypeError("Prompt must be a string.") + + if not prompt: + logging.error("Prompt is empty.") + raise ValueError("Prompt is empty.") + + try: + self.agent.run([f"{prompt}"]) + return "Task completed by WorkerNode" + except Exception as e: + logging.error(f"While running the agent: {str(e)}") + raise e + + + + + +class WorkerNodeInitializer: + def __init__(self, openai_api_key): + if not openai_api_key: + logging.error("OpenAI API key is not provided") + raise ValueError("openai_api_key cannot be None") + + self.openai_api_key = openai_api_key + + def initialize_llm(self, llm_class, temperature=0.5): + if not llm_class: + logging.error("llm_class cannot be none") + raise ValueError("llm_class cannot be None") + + try: + return llm_class(openai_api_key=self.openai_api_key, temperature=temperature) + except Exception as e: + logging.error(f"Failed to initialize language model: {e}") + raise + + def initialize_tools(self, llm_class): + if not llm_class: + logging.error("llm_class not cannot be none") + raise ValueError("llm_class cannot be none") + try: + + logging.info('Creating WorkerNode') + llm = self.initialize_llm(llm_class) + web_search = DuckDuckGoSearchRun() + + tools = [ + web_search, + WriteFileTool(root_dir=ROOT_DIR), + ReadFileTool(root_dir=ROOT_DIR), + process_csv, + WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), + ] + if not tools: + logging.error("Tools are not initialized") + raise ValueError("Tools are not initialized") + return tools + except Exception as e: + logging.error(f"Failed to initialize tools: {e}") + + def initialize_vectorstore(self): + try: + + embeddings_model = OpenAIEmbeddings(openai_api_key=self.openai_api_key) + embedding_size = 1536 + index = faiss.IndexFlatL2(embedding_size) + return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) + except Exception as e: + logging.error(f"Failed to initialize vector store: {e}") + raise + + def create_worker_node(self, llm_class=ChatOpenAI, ai_name="Swarm Worker AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}, verbose=False): + if not llm_class: + logging.error("llm_class cannot be None.") + raise ValueError("llm_class cannot be None.") + try: + worker_tools = self.initialize_tools(llm_class) + vectorstore = self.initialize_vectorstore() + worker_node = WorkerNode(llm=self.initialize_llm(llm_class), tools=worker_tools, vectorstore=vectorstore) + worker_node.create_agent(ai_name=ai_name, ai_role=ai_role, human_in_the_loop=human_in_the_loop, search_kwargs=search_kwargs, verbose=verbose) + return worker_node + except Exception as e: + logging.error(f"Failed to create worker node: {e}") + raise + +def worker_node(openai_api_key): + if not openai_api_key: + logging.error("OpenAI API key is not provided") + raise ValueError("OpenAI API key is required") + + try: + + initializer = WorkerNodeInitializer(openai_api_key) + worker_node = initializer.create_worker_node() + return worker_node + except Exception as e: + logging.error(f"An error occured in worker_node: {e}") + raise + + diff --git a/swarms/agents/workers/WorkerUltraNode.py b/swarms/agents/workers/WorkerUltraNode.py new file mode 100644 index 00000000..274fbf39 --- /dev/null +++ b/swarms/agents/workers/WorkerUltraNode.py @@ -0,0 +1,114 @@ +import os +import re +import logging +from pathlib import Path +from typing import Dict, List + +from swarms.agents.utils.AgentManager import AgentManager +from swarms.utils.main import BaseHandler, FileHandler, FileType + +from swarms.tools.main import ExitConversation, RequestsGet, CodeEditor, Terminal +from swarms.utils.main import CsvToDataframe + +from swarms.tools.main import BaseToolSet +from swarms.utils.main import StaticUploader + +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') + +BASE_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +# Check if "PLAYGROUND_DIR" environment variable exists, if not, set a default value +playground = os.environ.get("PLAYGROUND_DIR", './playground') + +# Ensure the path exists before changing the directory +os.makedirs(BASE_DIR / playground, exist_ok=True) + +try: + os.chdir(BASE_DIR / playground) +except Exception as e: + logging.error(f"Failed to change directory: {e}") + +class WorkerUltraNode: + def __init__(self, objective: str, openai_api_key: str): + self.openai_api_key = openai_api_key + + if not isinstance(objective, str): + raise TypeError("Objective must be a string") + if not objective: + raise ValueError("Objective cannot be empty") + + toolsets: List[BaseToolSet] = [ + Terminal(), + CodeEditor(), + RequestsGet(), + ExitConversation(), + ] + handlers: Dict[FileType, BaseHandler] = {FileType.DATAFRAME: CsvToDataframe()} + + if os.environ.get("USE_GPU", False): + import torch + from swarms.tools.main import ImageCaptioning + from swarms.tools.main import ImageEditing, InstructPix2Pix, Text2Image, VisualQuestionAnswering + + if torch.cuda.is_available(): + toolsets.extend( + [ + Text2Image("cuda"), + ImageEditing("cuda"), + InstructPix2Pix("cuda"), + VisualQuestionAnswering("cuda"), + ] + ) + handlers[FileType.IMAGE] = ImageCaptioning("cuda") + + try: + + + self.agent_manager = AgentManager.create(toolsets=toolsets) + self.file_handler = FileHandler(handlers=handlers, path=BASE_DIR) + self.uploader = StaticUploader.from_settings( + path=BASE_DIR / "static", endpoint="static" + ) + + + self.session = self.agent_manager.create_executor(objective, self.openai_api_key) + + except Exception as e: + logging.error(f"Error while initializing WorkerUltraNode: {str(e)}") + raise e + + def execute_task(self): + # Now the prompt is not needed as an argument + promptedQuery = self.file_handler.handle(self.objective) + + try: + res = self.session({"input": promptedQuery}) + except Exception as e: + logging.error(f"Error while executing task: {str(e)}") + return {"answer": str(e), "files": []} + + files = re.findall(r"\[file://\S*\]", res["output"]) + files = [file[1:-1].split("file://")[1] for file in files] + + return { + "answer": res["output"], + "files": [self.uploader.upload(file) for file in files], + } + + + def execute(self): + try: + + # The prompt is not needed here either + return self.execute_task() + except Exception as e: + logging.error(f"Error while executing: {str(e)}") + raise e + + + +def WorkerUltra(objective: str, openai_api_key: str): + worker_node = WorkerUltraNode(objective, openai_api_key) + # Return the result of the execution + return worker_node.result + diff --git a/swarms/agents/workers/__init__.py b/swarms/agents/workers/__init__.py index eab37564..9f052dea 100644 --- a/swarms/agents/workers/__init__.py +++ b/swarms/agents/workers/__init__.py @@ -1,3 +1,2 @@ -from .worker_agent import worker_node - -from .worker_ultranode import UltraNode +from .WorkerNode import worker_node +from .WorkerUltraNode import WorkerUltraNode diff --git a/swarms/agents/workers/agents.py b/swarms/agents/workers/agents.py deleted file mode 100644 index 8419dd95..00000000 --- a/swarms/agents/workers/agents.py +++ /dev/null @@ -1,1056 +0,0 @@ -from __future__ import annotations -from enum import Enum -from typing import Callable, Tuple - -from langchain.agents.agent import AgentExecutor -from langchain.agents.tools import BaseTool, Tool - - -class ToolScope(Enum): - GLOBAL = "global" - SESSION = "session" - -SessionGetter = Callable[[], Tuple[str, AgentExecutor]] - - -def tool( - name: str, - description: str, - scope: ToolScope = ToolScope.GLOBAL, -): - def decorator(func): - func.name = name - func.description = description - func.is_tool = True - func.scope = scope - return func - - return decorator - - -class ToolWrapper: - def __init__(self, name: str, description: str, scope: ToolScope, func): - self.name = name - self.description = description - self.scope = scope - self.func = func - - def is_global(self) -> bool: - return self.scope == ToolScope.GLOBAL - - def is_per_session(self) -> bool: - return self.scope == ToolScope.SESSION - - def to_tool( - self, - get_session: SessionGetter = lambda: [], - ) -> BaseTool: - func = self.func - if self.is_per_session(): - func = lambda *args, **kwargs: self.func( - *args, **kwargs, get_session=get_session - ) - - return Tool( - name=self.name, - description=self.description, - func=func, - ) - - -class BaseToolSet: - def tool_wrappers(cls) -> list[ToolWrapper]: - methods = [ - getattr(cls, m) for m in dir(cls) if hasattr(getattr(cls, m), "is_tool") - ] - return [ToolWrapper(m.name, m.description, m.scope, m) for m in methods] - - -#=====================================> -from typing import Optional - -from langchain.agents import load_tools -from langchain.agents.tools import BaseTool -from langchain.llms.base import BaseLLM - - -class ToolsFactory: - @staticmethod - def from_toolset( - toolset: BaseToolSet, - only_global: Optional[bool] = False, - only_per_session: Optional[bool] = False, - get_session: SessionGetter = lambda: [], - ) -> list[BaseTool]: - tools = [] - for wrapper in toolset.tool_wrappers(): - if only_global and not wrapper.is_global(): - continue - if only_per_session and not wrapper.is_per_session(): - continue - tools.append(wrapper.to_tool(get_session=get_session)) - return tools - - @staticmethod - def create_global_tools( - toolsets: list[BaseToolSet], - ) -> list[BaseTool]: - tools = [] - for toolset in toolsets: - tools.extend( - ToolsFactory.from_toolset( - toolset=toolset, - only_global=True, - ) - ) - return tools - - @staticmethod - def create_per_session_tools( - toolsets: list[BaseToolSet], - get_session: SessionGetter = lambda: [], - ) -> list[BaseTool]: - tools = [] - for toolset in toolsets: - tools.extend( - ToolsFactory.from_toolset( - toolset=toolset, - only_per_session=True, - get_session=get_session, - ) - ) - return tools - - @staticmethod - def create_global_tools_from_names( - toolnames: list[str], - llm: Optional[BaseLLM], - ) -> list[BaseTool]: - return load_tools(toolnames, llm=llm) - -#=====================================> -#=====================================> - - - - - -################ -# from core.prompts.input import EVAL_PREFIX, EVAL_SUFFIX -from ...prompts.prompts import EVAL_PREFIX, EVAL_SUFFIX -############ - - -from env import settings -from langchain.chat_models.base import BaseChatModel -from langchain.schema import BaseOutputParser -from langchain.callbacks.base import BaseCallbackManager - - -class AgentBuilder: - def __init__(self, toolsets: list[BaseToolSet] = []): - self.llm: BaseChatModel = None - self.parser: BaseOutputParser = None - self.global_tools: list = None - self.toolsets = toolsets - - def build_llm(self, callback_manager: BaseCallbackManager = None): - self.llm = ChatOpenAI( - temperature=0, callback_manager=callback_manager, verbose=True - ) - self.llm.check_access() - - def build_parser(self): - self.parser = EvalOutputParser() - - def build_global_tools(self): - if self.llm is None: - raise ValueError("LLM must be initialized before tools") - - toolnames = ["wikipedia"] - - if settings["SERPAPI_API_KEY"]: - toolnames.append("serpapi") - if settings["BING_SEARCH_URL"] and settings["BING_SUBSCRIPTION_KEY"]: - toolnames.append("bing-search") - - self.global_tools = [ - *ToolsFactory.create_global_tools_from_names(toolnames, llm=self.llm), - *ToolsFactory.create_global_tools(self.toolsets), - ] - - def get_parser(self): - if self.parser is None: - raise ValueError("Parser is not initialized yet") - - return self.parser - - def get_global_tools(self): - if self.global_tools is None: - raise ValueError("Global tools are not initialized yet") - - return self.global_tools - - def get_agent(self): - if self.llm is None: - raise ValueError("LLM must be initialized before agent") - - if self.parser is None: - raise ValueError("Parser must be initialized before agent") - - if self.global_tools is None: - raise ValueError("Global tools must be initialized before agent") - - return ConversationalChatAgent.from_llm_and_tools( - llm=self.llm, - tools=[ - *self.global_tools, - *ToolsFactory.create_per_session_tools( - self.toolsets - ), # for names and descriptions - ], - system_message=EVAL_PREFIX.format(bot_name=settings["BOT_NAME"]), - human_message=EVAL_SUFFIX.format(bot_name=settings["BOT_NAME"]), - output_parser=self.parser, - max_iterations=30, - ) -#===================================> callback - -#===================================> callback - -from typing import Any, Dict, List, Optional, Union - -from langchain.callbacks.base import BaseCallbackHandler -from langchain.schema import AgentAction, AgentFinish, LLMResult -# from celery import Task - -from swarms.utils.utils import ANSI, Color, Style, dim_multiline, logger - - -class EVALCallbackHandler(BaseCallbackHandler): - @property - def ignore_llm(self) -> bool: - return False - - def set_parser(self, parser) -> None: - self.parser = parser - - def on_llm_start( - self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any - ) -> None: - pass - - def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None: - text = response.generations[0][0].text - - parsed = self.parser.parse_all(text) - - logger.info(ANSI("Plan").to(Color.blue().bright()) + ": " + parsed["plan"]) - logger.info(ANSI("What I Did").to(Color.blue()) + ": " + parsed["what_i_did"]) - logger.info( - ANSI("Action").to(Color.cyan()) - + ": " - + ANSI(parsed["action"]).to(Style.bold()) - ) - logger.info( - ANSI("Input").to(Color.cyan()) - + ": " - + dim_multiline(parsed["action_input"]) - ) - - def on_llm_new_token(self, token: str, **kwargs: Any) -> None: - logger.info(ANSI(f"on_llm_new_token {token}").to(Color.green(), Style.italic())) - - def on_llm_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any - ) -> None: - pass - - def on_chain_start( - self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any - ) -> None: - logger.info(ANSI(f"Entering new chain.").to(Color.green(), Style.italic())) - logger.info(ANSI("Prompted Text").to(Color.yellow()) + f': {inputs["input"]}\n') - - def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> None: - logger.info(ANSI(f"Finished chain.").to(Color.green(), Style.italic())) - - def on_chain_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any - ) -> None: - logger.error( - ANSI(f"Chain Error").to(Color.red()) + ": " + dim_multiline(str(error)) - ) - - def on_tool_start( - self, - serialized: Dict[str, Any], - input_str: str, - **kwargs: Any, - ) -> None: - pass - - def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any: - pass - - def on_tool_end( - self, - output: str, - observation_prefix: Optional[str] = None, - llm_prefix: Optional[str] = None, - **kwargs: Any, - ) -> None: - logger.info( - ANSI("Observation").to(Color.magenta()) + ": " + dim_multiline(output) - ) - logger.info(ANSI("Thinking...").to(Color.green(), Style.italic())) - - def on_tool_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any - ) -> None: - logger.error(ANSI("Tool Error").to(Color.red()) + f": {error}") - - def on_text( - self, - text: str, - color: Optional[str] = None, - end: str = "", - **kwargs: Optional[str], - ) -> None: - pass - - def on_agent_finish( - self, finish: AgentFinish, color: Optional[str] = None, **kwargs: Any - ) -> None: - logger.info( - ANSI("Final Answer").to(Color.yellow()) - + ": " - + dim_multiline(finish.return_values.get("output", "")) - ) - - -class ExecutionTracingCallbackHandler(BaseCallbackHandler): - def __init__(self, execution: Task): - self.execution = execution - self.index = 0 - - def set_parser(self, parser) -> None: - self.parser = parser - - def on_llm_start( - self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any - ) -> None: - pass - - def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None: - text = response.generations[0][0].text - parsed = self.parser.parse_all(text) - self.index += 1 - parsed["index"] = self.index - self.execution.update_state(state="LLM_END", meta=parsed) - - def on_llm_new_token(self, token: str, **kwargs: Any) -> None: - pass - - def on_llm_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any - ) -> None: - pass - - def on_chain_start( - self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any - ) -> None: - pass - - def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> None: - pass - - def on_chain_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any - ) -> None: - self.execution.update_state(state="CHAIN_ERROR", meta={"error": str(error)}) - - def on_tool_start( - self, - serialized: Dict[str, Any], - input_str: str, - **kwargs: Any, - ) -> None: - pass - - def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any: - pass - - def on_tool_end( - self, - output: str, - observation_prefix: Optional[str] = None, - llm_prefix: Optional[str] = None, - **kwargs: Any, - ) -> None: - previous = self.execution.AsyncResult(self.execution.request.id) - self.execution.update_state( - state="TOOL_END", meta={**previous.info, "observation": output} - ) - - def on_tool_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any - ) -> None: - previous = self.execution.AsyncResult(self.execution.request.id) - self.execution.update_state( - state="TOOL_ERROR", meta={**previous.info, "error": str(error)} - ) - - def on_text( - self, - text: str, - color: Optional[str] = None, - end: str = "", - **kwargs: Optional[str], - ) -> None: - pass - - def on_agent_finish( - self, finish: AgentFinish, color: Optional[str] = None, **kwargs: Any - ) -> None: - pass - -#===================================> callback -#===================================> callback - - -#===================================> callback - -from typing import Any, List, Optional, Sequence, Tuple - -from langchain.agents.agent import Agent -from langchain.callbacks.base import BaseCallbackManager -from langchain.chains import LLMChain -from langchain.schema import BaseOutputParser -from langchain.prompts.base import BasePromptTemplate -from langchain.prompts.chat import ( - ChatPromptTemplate, - HumanMessagePromptTemplate, - MessagesPlaceholder, - SystemMessagePromptTemplate, -) -from langchain.schema import ( - AgentAction, - AIMessage, - BaseLanguageModel, - BaseMessage, - HumanMessage, -) -from langchain.tools.base import BaseTool - -# from core.prompts.input import EVAL_TOOL_RESPONSE -from prompts.prompts import EVAL_TOOL_RESPONSE - -from prompts.prompts import EVAL_FORMAT_INSTRUCTIONS - - -class ConversationalChatAgent(Agent): - """An agent designed to hold a conversation in addition to using tools.""" - - output_parser: BaseOutputParser - - @property - def _agent_type(self) -> str: - raise NotImplementedError - - @property - def observation_prefix(self) -> str: - """Prefix to append the observation with.""" - return "Observation: " - - @property - def llm_prefix(self) -> str: - """Prefix to append the llm call with.""" - return "Thought: " - - @classmethod - def create_prompt( - cls, - tools: Sequence[BaseTool], - system_message: str, - human_message: str, - output_parser: BaseOutputParser, - input_variables: Optional[List[str]] = None, - ) -> BasePromptTemplate: - tool_strings = "\n".join( - [f"> {tool.name}: {tool.description}" for tool in tools] - ) - tool_names = ", ".join([tool.name for tool in tools]) - format_instructions = human_message.format( - format_instructions=output_parser.get_format_instructions() - ) - final_prompt = format_instructions.format( - tool_names=tool_names, tools=tool_strings - ) - if input_variables is None: - input_variables = ["input", "chat_history", "agent_scratchpad"] - messages = [ - SystemMessagePromptTemplate.from_template(system_message), - MessagesPlaceholder(variable_name="chat_history"), - HumanMessagePromptTemplate.from_template(final_prompt), - MessagesPlaceholder(variable_name="agent_scratchpad"), - ] - return ChatPromptTemplate(input_variables=input_variables, messages=messages) - - def _extract_tool_and_input(self, llm_output: str) -> Optional[Tuple[str, str]]: - try: - response = self.output_parser.parse(llm_output) - return response["action"], response["action_input"] - except Exception: - raise ValueError(f"Could not parse LLM output: {llm_output}") - - def _construct_scratchpad( - self, intermediate_steps: List[Tuple[AgentAction, str]] - ) -> List[BaseMessage]: - """Construct the scratchpad that lets the agent continue its thought process.""" - thoughts: List[BaseMessage] = [] - for action, observation in intermediate_steps: - thoughts.append(AIMessage(content=action.log)) - human_message = HumanMessage( - content=EVAL_TOOL_RESPONSE.format(observation=observation) - ) - thoughts.append(human_message) - return thoughts - - @classmethod - def from_llm_and_tools( - cls, - llm: BaseLanguageModel, - tools: Sequence[BaseTool], - system_message: str, - human_message: str, - output_parser: BaseOutputParser, - callback_manager: Optional[BaseCallbackManager] = None, - input_variables: Optional[List[str]] = None, - **kwargs: Any, - ) -> Agent: - """Construct an agent from an LLM and tools.""" - cls._validate_tools(tools) - prompt = cls.create_prompt( - tools, - system_message=system_message, - human_message=human_message, - input_variables=input_variables, - output_parser=output_parser, - ) - llm_chain = LLMChain( - llm=llm, - prompt=prompt, - callback_manager=callback_manager, - ) - tool_names = [tool.name for tool in tools] - return cls( - llm_chain=llm_chain, - allowed_tools=tool_names, - output_parser=output_parser, - **kwargs, - ) -#===================================> CHAT AGENT - - - -#####===============================> - -"""OpenAI chat wrapper.""" - -import logging -import sys -from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple - -import openai - -from langchain.chat_models.base import BaseChatModel -from langchain.schema import ( - AIMessage, - BaseMessage, - ChatGeneration, - ChatMessage, - ChatResult, - HumanMessage, - SystemMessage, -) -from langchain.utils import get_from_dict_or_env -from logger import logger -from pydantic import BaseModel, Extra, Field, root_validator -from tenacity import ( - before_sleep_log, - retry, - retry_if_exception_type, - stop_after_attempt, - wait_exponential, -) - -from env import settings - - - -def _create_retry_decorator(llm: ChatOpenAI) -> Callable[[Any], Any]: - import openai - - min_seconds = 4 - max_seconds = 10 - # Wait 2^x * 1 second between each retry starting with - # 4 seconds, then up to 10 seconds, then 10 seconds afterwards - return retry( - reraise=True, - stop=stop_after_attempt(llm.max_retries), - wait=wait_exponential(multiplier=1, min=min_seconds, max=max_seconds), - retry=( - retry_if_exception_type(openai.error.Timeout) - | retry_if_exception_type(openai.error.APIError) - | retry_if_exception_type(openai.error.APIConnectionError) - | retry_if_exception_type(openai.error.RateLimitError) - | retry_if_exception_type(openai.error.ServiceUnavailableError) - ), - before_sleep=before_sleep_log(logger, logging.WARNING), - ) - - -async def acompletion_with_retry(llm: ChatOpenAI, **kwargs: Any) -> Any: - """Use tenacity to retry the async completion call.""" - retry_decorator = _create_retry_decorator(llm) - - @retry_decorator - async def _completion_with_retry(**kwargs: Any) -> Any: - # Use OpenAI's async api https://github.com/openai/openai-python#async-api - return await llm.client.acreate(**kwargs) - - return await _completion_with_retry(**kwargs) - - -def _convert_dict_to_message(_dict: dict) -> BaseMessage: - role = _dict["role"] - if role == "user": - return HumanMessage(content=_dict["content"]) - elif role == "assistant": - return AIMessage(content=_dict["content"]) - elif role == "system": - return SystemMessage(content=_dict["content"]) - else: - return ChatMessage(content=_dict["content"], role=role) - - -def _convert_message_to_dict(message: BaseMessage) -> dict: - if isinstance(message, ChatMessage): - message_dict = {"role": message.role, "content": message.content} - elif isinstance(message, HumanMessage): - message_dict = {"role": "user", "content": message.content} - elif isinstance(message, AIMessage): - message_dict = {"role": "assistant", "content": message.content} - elif isinstance(message, SystemMessage): - message_dict = {"role": "system", "content": message.content} - else: - raise ValueError(f"Got unknown type {message}") - if "name" in message.additional_kwargs: - message_dict["name"] = message.additional_kwargs["name"] - return message_dict - - -def _create_chat_result(response: Mapping[str, Any]) -> ChatResult: - generations = [] - for res in response["choices"]: - message = _convert_dict_to_message(res["message"]) - gen = ChatGeneration(message=message) - generations.append(gen) - return ChatResult(generations=generations) - - -class ModelNotFoundException(Exception): - """Exception raised when the model is not found.""" - - def __init__(self, model_name: str): - self.model_name = model_name - super().__init__( - f"\n\nModel {ANSI(self.model_name).to(Color.red())} does not exist.\nMake sure if you have access to the model.\n" - + f"You can set the model name with the environment variable {ANSI('MODEL_NAME').to(Style.bold())} on {ANSI('.env').to(Style.bold())}.\n" - + "\nex) MODEL_NAME=gpt-4\n" - + ANSI( - "\nLooks like you don't have access to gpt-4 yet. Try using `gpt-3.5-turbo`." - if self.model_name == "gpt-4" - else "" - ).to(Style.italic()) - ) - - -class ChatOpenAI(BaseChatModel, BaseModel): - """Wrapper around OpenAI Chat large language models. - - To use, you should have the ``openai`` python package installed, and the - environment variable ``OPENAI_API_KEY`` set with your API key. - - Any parameters that are valid to be passed to the openai.create call can be passed - in, even if not explicitly saved on this class. - - Example: - .. code-block:: python - - from langchain.chat_models import ChatOpenAI - openai = ChatOpenAI(model_name="gpt-3.5-turbo") - """ - - client: Any #: :meta private: - model_name: str = settings["MODEL_NAME"] - """Model name to use.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) - """Holds any model parameters valid for `create` call not explicitly specified.""" - openai_api_key: Optional[str] = None - max_retries: int = 6 - """Maximum number of retries to make when generating.""" - streaming: bool = False - """Whether to stream the results or not.""" - n: int = 1 - """Number of chat completions to generate for each prompt.""" - max_tokens: int = 2048 - """Maximum number of tokens to generate.""" - - class Config: - """Configuration for this pydantic object.""" - - extra = Extra.ignore - - def check_access(self) -> None: - """Check that the user has access to the model.""" - - try: - openai.Engine.retrieve(self.model_name) - except openai.error.InvalidRequestError: - raise ModelNotFoundException(self.model_name) - - @root_validator(pre=True) - def build_extra(cls, values: Dict[str, Any]) -> Dict[str, Any]: - """Build extra kwargs from additional params that were passed in.""" - all_required_field_names = {field.alias for field in cls.__fields__.values()} - - extra = values.get("model_kwargs", {}) - for field_name in list(values): - if field_name not in all_required_field_names: - if field_name in extra: - raise ValueError(f"Found {field_name} supplied twice.") - extra[field_name] = values.pop(field_name) - values["model_kwargs"] = extra - return values - - @root_validator() - def validate_environment(cls, values: Dict) -> Dict: - """Validate that api key and python package exists in environment.""" - openai_api_key = get_from_dict_or_env( - values, "openai_api_key", "OPENAI_API_KEY" - ) - try: - import openai - - openai.api_key = openai_api_key - except ImportError: - raise ValueError( - "Could not import openai python package. " - "Please it install it with `pip install openai`." - ) - try: - values["client"] = openai.ChatCompletion - except AttributeError: - raise ValueError( - "`openai` has no `ChatCompletion` attribute, this is likely " - "due to an old version of the openai package. Try upgrading it " - "with `pip install --upgrade openai`." - ) - if values["n"] < 1: - raise ValueError("n must be at least 1.") - if values["n"] > 1 and values["streaming"]: - raise ValueError("n must be 1 when streaming.") - return values - - @property - def _default_params(self) -> Dict[str, Any]: - """Get the default parameters for calling OpenAI API.""" - return { - "model": self.model_name, - "max_tokens": self.max_tokens, - "stream": self.streaming, - "n": self.n, - **self.model_kwargs, - } - - def _create_retry_decorator(self) -> Callable[[Any], Any]: - import openai - - min_seconds = 4 - max_seconds = 10 - # Wait 2^x * 1 second between each retry starting with - # 4 seconds, then up to 10 seconds, then 10 seconds afterwards - return retry( - reraise=True, - stop=stop_after_attempt(self.max_retries), - wait=wait_exponential(multiplier=1, min=min_seconds, max=max_seconds), - retry=( - retry_if_exception_type(openai.error.Timeout) - | retry_if_exception_type(openai.error.APIError) - | retry_if_exception_type(openai.error.APIConnectionError) - | retry_if_exception_type(openai.error.RateLimitError) - | retry_if_exception_type(openai.error.ServiceUnavailableError) - ), - before_sleep=before_sleep_log(logger, logging.WARNING), - ) - - def completion_with_retry(self, **kwargs: Any) -> Any: - """Use tenacity to retry the completion call.""" - retry_decorator = self._create_retry_decorator() - - @retry_decorator - def _completion_with_retry(**kwargs: Any) -> Any: - response = self.client.create(**kwargs) - logger.debug("Response:\n\t%s", response) - return response - - return _completion_with_retry(**kwargs) - - def _generate( - self, messages: List[BaseMessage], stop: Optional[List[str]] = None - ) -> ChatResult: - message_dicts, params = self._create_message_dicts(messages, stop) - logger.debug("Messages:\n") - for item in message_dicts: - for k, v in item.items(): - logger.debug(f"\t\t{k}: {v}") - logger.debug("\t-------") - logger.debug("===========") - - if self.streaming: - inner_completion = "" - role = "assistant" - params["stream"] = True - for stream_resp in self.completion_with_retry( - messages=message_dicts, **params - ): - role = stream_resp["choices"][0]["delta"].get("role", role) - token = stream_resp["choices"][0]["delta"].get("content", "") - inner_completion += token - self.callback_manager.on_llm_new_token( - token, - verbose=self.verbose, - ) - message = _convert_dict_to_message( - {"content": inner_completion, "role": role} - ) - return ChatResult(generations=[ChatGeneration(message=message)]) - response = self.completion_with_retry(messages=message_dicts, **params) - return _create_chat_result(response) - - def _create_message_dicts( - self, messages: List[BaseMessage], stop: Optional[List[str]] - ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]: - params: Dict[str, Any] = {**{"model": self.model_name}, **self._default_params} - if stop is not None: - if "stop" in params: - raise ValueError("`stop` found in both the input and default params.") - params["stop"] = stop - message_dicts = [_convert_message_to_dict(m) for m in messages] - return message_dicts, params - - async def _agenerate( - self, messages: List[BaseMessage], stop: Optional[List[str]] = None - ) -> ChatResult: - message_dicts, params = self._create_message_dicts(messages, stop) - if self.streaming: - inner_completion = "" - role = "assistant" - params["stream"] = True - async for stream_resp in await acompletion_with_retry( - self, messages=message_dicts, **params - ): - role = stream_resp["choices"][0]["delta"].get("role", role) - token = stream_resp["choices"][0]["delta"].get("content", "") - inner_completion += token - if self.callback_manager.is_async: - await self.callback_manager.on_llm_new_token( - token, - verbose=self.verbose, - ) - else: - self.callback_manager.on_llm_new_token( - token, - verbose=self.verbose, - ) - message = _convert_dict_to_message( - {"content": inner_completion, "role": role} - ) - return ChatResult(generations=[ChatGeneration(message=message)]) - else: - response = await acompletion_with_retry( - self, messages=message_dicts, **params - ) - return _create_chat_result(response) - - @property - def _identifying_params(self) -> Mapping[str, Any]: - """Get the identifying parameters.""" - return {**{"model_name": self.model_name}, **self._default_params} - - def get_num_tokens(self, text: str) -> int: - """Calculate num tokens with tiktoken package.""" - # tiktoken NOT supported for Python 3.8 or below - if sys.version_info[1] <= 8: - return super().get_num_tokens(text) - try: - import tiktoken - except ImportError: - raise ValueError( - "Could not import tiktoken python package. " - "This is needed in order to calculate get_num_tokens. " - "Please it install it with `pip install tiktoken`." - ) - # create a GPT-3.5-Turbo encoder instance - enc = tiktoken.encoding_for_model(self.model_name) - - # encode the text using the GPT-3.5-Turbo encoder - tokenized_text = enc.encode(text) - - # calculate the number of tokens in the encoded text - return len(tokenized_text) - - -###############LLM END => - - - -from typing import Dict, Optional - -from langchain.agents.agent import AgentExecutor -from langchain.callbacks.base import CallbackManager -from langchain.callbacks import set_handler -from langchain.chains.conversation.memory import ConversationBufferMemory -from langchain.memory.chat_memory import BaseChatMemory - -# from core.tools.base import BaseToolSet -# from core.tools.factory import ToolsFactory - -# from .builder import AgentBuilder -# from .callback import EVALCallbackHandler, ExecutionTracingCallbackHandler - - -set_handler(EVALCallbackHandler()) - - -class AgentManager: - def __init__( - self, - toolsets: list[BaseToolSet] = [], - ): - self.toolsets: list[BaseToolSet] = toolsets - self.memories: Dict[str, BaseChatMemory] = {} - self.executors: Dict[str, AgentExecutor] = {} - - def create_memory(self) -> BaseChatMemory: - return ConversationBufferMemory(memory_key="chat_history", return_messages=True) - - def get_or_create_memory(self, session: str) -> BaseChatMemory: - if not (session in self.memories): - self.memories[session] = self.create_memory() - return self.memories[session] - - def create_executor( - self, session: str, execution: Optional[Task] = None - ) -> AgentExecutor: - builder = AgentBuilder(self.toolsets) - builder.build_parser() - - callbacks = [] - eval_callback = EVALCallbackHandler() - eval_callback.set_parser(builder.get_parser()) - callbacks.append(eval_callback) - if execution: - execution_callback = ExecutionTracingCallbackHandler(execution) - execution_callback.set_parser(builder.get_parser()) - callbacks.append(execution_callback) - - callback_manager = CallbackManager(callbacks) - - builder.build_llm(callback_manager) - builder.build_global_tools() - - memory: BaseChatMemory = self.get_or_create_memory(session) - tools = [ - *builder.get_global_tools(), - *ToolsFactory.create_per_session_tools( - self.toolsets, - get_session=lambda: (session, self.executors[session]), - ), - ] - - for tool in tools: - tool.callback_manager = callback_manager - - executor = AgentExecutor.from_agent_and_tools( - agent=builder.get_agent(), - tools=tools, - memory=memory, - callback_manager=callback_manager, - verbose=True, - ) - self.executors[session] = executor - return executor - - @staticmethod - def create(toolsets: list[BaseToolSet]) -> "AgentManager": - return AgentManager( - toolsets=toolsets, - ) - - - - - - -#PARSER=============================+> PARSER -import re -from typing import Dict - -from langchain.schema import BaseOutputParser - -# from core.prompts.input import EVAL_FORMAT_INSTRUCTIONS - -# from prompts.prompts import EVAL_FORMAT_INSTRUCTIONS -from ...prompts.prompts import EVAL_FORMAT_INSTRUCTIONS - - -class EvalOutputParser(BaseOutputParser): - @staticmethod - def parse_all(text: str) -> Dict[str, str]: - regex = r"Action: (.*?)[\n]Plan:(.*)[\n]What I Did:(.*)[\n]Action Input: (.*)" - match = re.search(regex, text, re.DOTALL) - if not match: - raise Exception("parse error") - - action = match.group(1).strip() - plan = match.group(2) - what_i_did = match.group(3) - action_input = match.group(4).strip(" ") - - return { - "action": action, - "plan": plan, - "what_i_did": what_i_did, - "action_input": action_input, - } - - def get_format_instructions(self) -> str: - return EVAL_FORMAT_INSTRUCTIONS - - def parse(self, text: str) -> Dict[str, str]: - regex = r"Action: (.*?)[\n]Plan:(.*)[\n]What I Did:(.*)[\n]Action Input: (.*)" - match = re.search(regex, text, re.DOTALL) - if not match: - raise Exception("parse error") - - parsed = EvalOutputParser.parse_all(text) - - return {"action": parsed["action"], "action_input": parsed["action_input"]} - - def __str__(self): - return "EvalOutputParser" - -#PARSER=============================+> PARSER diff --git a/swarms/agents/workers/auto_worker.py b/swarms/agents/workers/auto_worker.py deleted file mode 100644 index 98104ea8..00000000 --- a/swarms/agents/workers/auto_worker.py +++ /dev/null @@ -1,95 +0,0 @@ -# General -import os -import pandas as pd -from langchain.experimental.autonomous_agents.autogpt.agent import AutoGPT - -from langchain.chat_models import ChatOpenAI -from langchain.agents.agent_toolkits.pandas.base import create_pandas_dataframe_agent -from langchain.docstore.document import Document - -import asyncio -import nest_asyncio - -# Tools - -from contextlib import contextmanager -from typing import Optional -from langchain.agents import tool - -from langchain.tools.file_management.read import ReadFileTool -from langchain.tools.file_management.write import WriteFileTool -from langchain.tools import BaseTool, DuckDuckGoSearchRun - -from langchain.text_splitter import RecursiveCharacterTextSplitter -from pydantic import Field -from langchain.chains.qa_with_sources.loading import load_qa_with_sources_chain, BaseCombineDocumentsChain - -# Memory -import faiss -from langchain.vectorstores import FAISS -from langchain.docstore import InMemoryDocstore -from langchain.embeddings import OpenAIEmbeddings - -from langchain.tools.human.tool import HumanInputRun -# from swarms.agents.workers.auto_agent import -from swarms.agents.workers.visual_worker import multimodal_agent_tool -from swarms.tools.main import Terminal, CodeWriter, CodeEditor, process_csv, WebpageQATool - - - -class WorkerAgent: - def __init__(self, objective: str, api_key: str): - self.objective = objective - self.api_key = api_key - self.worker = self.create_agent_worker() - - def create_agent_worker(self): - os.environ['OPENAI_API_KEY'] = self.api_key - - llm = ChatOpenAI(model_name="gpt-4", temperature=1.0) - embeddings_model = OpenAIEmbeddings() - embedding_size = 1536 - index = faiss.IndexFlatL2(embedding_size) - vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) - - query_website_tool = WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)) - web_search = DuckDuckGoSearchRun() - - tools = [ - web_search, - WriteFileTool(root_dir="./data"), - ReadFileTool(root_dir="./data"), - - multimodal_agent_tool, - process_csv, - query_website_tool, - Terminal, - - - CodeWriter, - CodeEditor - ] - - agent_worker = AutoGPT.from_llm_and_tools( - ai_name="WorkerX", - ai_role="Assistant", - tools=tools, - llm=llm, - memory=vectorstore.as_retriever(search_kwargs={"k": 8}), - human_in_the_loop=True, - ) - - agent_worker.chain.verbose = True - - return agent_worker - - # objective = "Your objective here" - # api_key = "Your OpenAI API key here" - - # worker_agent = WorkerAgent(objective, api_key) - - -# objective = "Your objective here" - - -# worker_agent = WorkerAgent(objective) diff --git a/swarms/agents/workers/worker_agent.py b/swarms/agents/workers/worker_agent.py deleted file mode 100644 index 55342e02..00000000 --- a/swarms/agents/workers/worker_agent.py +++ /dev/null @@ -1,96 +0,0 @@ -from swarms.tools.agent_tools import * -from langchain.tools import BaseTool -from typing import Optional, Type - -from langchain.callbacks.manager import ( - AsyncCallbackManagerForToolRun, - CallbackManagerForToolRun, -) -from typing import List, Any, Dict, Optional -from langchain.memory.chat_message_histories import FileChatMessageHistory - -import logging -from pydantic import BaseModel, Extra -logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') - -class WorkerNode: - """Useful for when you need to spawn an autonomous agent instance as a worker to accomplish complex tasks, it can search the internet or spawn child multi-modality models to process and generate images and text or audio and so on""" - - def __init__(self, llm, tools, vectorstore): - self.llm = llm - self.tools = tools - self.vectorstore = vectorstore - self.agent = None - - def create_agent(self, ai_name, ai_role, human_in_the_loop, search_kwargs): - logging.info("Creating agent in WorkerNode") - self.agent = AutoGPT.from_llm_and_tools( - ai_name=ai_name, - ai_role=ai_role, - tools=self.tools, - llm=self.llm, - memory=self.vectorstore.as_retriever(search_kwargs=search_kwargs), - human_in_the_loop=human_in_the_loop, - chat_history_memory=FileChatMessageHistory("chat_history.txt"), - ) - self.agent.chain.verbose = True - - def add_tool(self, tool: Tool): - self.tools.append(tool) - - def run(self, prompt: str) -> str: - if not isinstance(prompt, str): - raise TypeError("Prompt must be a string") - - if not prompt: - raise ValueError("Prompt is empty") - - self.agent.run([f"{prompt}"]) - return "Task completed by WorkerNode" - - -worker_tool = Tool( - name="WorkerNode AI Agent", - func=WorkerNode.run, - description="Useful for when you need to spawn an autonomous agent instance as a worker to accomplish complex tasks, it can search the internet or spawn child multi-modality models to process and generate images and text or audio and so on" -) - - -class WorkerNodeInitializer: - def __init__(self, openai_api_key): - self.openai_api_key = openai_api_key - - def initialize_llm(self, llm_class, temperature=0.5): - return llm_class(openai_api_key=self.openai_api_key, temperature=temperature) - - def initialize_tools(self, llm_class): - llm = self.initialize_llm(llm_class) - web_search = DuckDuckGoSearchRun() - tools = [ - web_search, - WriteFileTool(root_dir=ROOT_DIR), - ReadFileTool(root_dir=ROOT_DIR), - process_csv, - WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), - ] - return tools - - def initialize_vectorstore(self): - embeddings_model = OpenAIEmbeddings(openai_api_key=self.openai_api_key) - embedding_size = 1536 - index = faiss.IndexFlatL2(embedding_size) - return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) - - def create_worker_node(self, llm_class=ChatOpenAI): - worker_tools = self.initialize_tools(llm_class) - vectorstore = self.initialize_vectorstore() - worker_node = WorkerNode(llm=self.initialize_llm(llm_class), tools=worker_tools, vectorstore=vectorstore) - worker_node.create_agent(ai_name="Swarm Worker AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) - return worker_node - -def worker_node(openai_api_key): - initializer = WorkerNodeInitializer(openai_api_key) - worker_node = initializer.create_worker_node() - return worker_node - - diff --git a/swarms/agents/workers/worker_ultranode.py b/swarms/agents/workers/worker_ultranode.py deleted file mode 100644 index 08230671..00000000 --- a/swarms/agents/workers/worker_ultranode.py +++ /dev/null @@ -1,74 +0,0 @@ -import os -from pathlib import Path -from typing import Dict, List - -from swarms.agents.utils.manager import AgentManager -from swarms.utils.utils import BaseHandler, FileHandler, FileType -from swarms.tools.main import CsvToDataframe, ExitConversation, RequestsGet, CodeEditor, Terminal -from swarms.tools.main import BaseToolSet -from swarms.utils.utils import StaticUploader - -BASE_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -os.chdir(BASE_DIR / os.environ["PLAYGROUND_DIR"]) - -class UltraNode: - def __init__(self, objective: str): - toolsets: List[BaseToolSet] = [ - Terminal(), - CodeEditor(), - RequestsGet(), - ExitConversation(), - ] - handlers: Dict[FileType, BaseHandler] = {FileType.DATAFRAME: CsvToDataframe()} - - if os.environ["USE_GPU"]: - import torch - from swarms.tools.main import ImageCaptioning - from swarms.tools.main import ImageEditing, InstructPix2Pix, Text2Image, VisualQuestionAnswering - - if torch.cuda.is_available(): - toolsets.extend( - [ - Text2Image("cuda"), - ImageEditing("cuda"), - InstructPix2Pix("cuda"), - VisualQuestionAnswering("cuda"), - ] - ) - handlers[FileType.IMAGE] = ImageCaptioning("cuda") - - self.agent_manager = AgentManager.create(toolsets=toolsets) - self.file_handler = FileHandler(handlers=handlers, path=BASE_DIR) - self.uploader = StaticUploader.from_settings( - path=BASE_DIR / "static", endpoint="static" - ) - - - self.session = self.agent_manager.create_executor(objective) - - def execute_task(self): - # Now the prompt is not needed as an argument - promptedQuery = self.file_handler.handle(self.objective) - - try: - res = self.session({"input": promptedQuery}) - except Exception as e: - return {"answer": str(e), "files": []} - - files = re.findall(r"\[file://\S*\]", res["output"]) - files = [file[1:-1].split("file://")[1] for file in files] - - return { - "answer": res["output"], - "files": [self.uploader.upload(file) for file in files], - } - - - def execute(self): - # The prompt is not needed here either - return self.execute_task() - -# from worker_node import UltraNode - -# node = UltraNode('objective') -# result = node.execute() diff --git a/swarms/hivemind.py b/swarms/hivemind.py new file mode 100644 index 00000000..b8033641 --- /dev/null +++ b/swarms/hivemind.py @@ -0,0 +1,2 @@ +# many boss + workers in unison +#kye gomez jul 13 4:01pm, can scale up the number of swarms working on a probkem with `hivemind(swarms=4, or swarms=auto which will scale the agents depending on the complexity)` \ No newline at end of file diff --git a/swarms/swarms.py b/swarms/swarms.py index 6e884558..a19f1c57 100644 --- a/swarms/swarms.py +++ b/swarms/swarms.py @@ -1,522 +1,186 @@ from swarms.tools.agent_tools import * -from swarms.agents.workers.worker_agent import WorkerNode -from swarms.agents.boss.boss_agent import BossNode -# from swarms.agents.workers.omni_worker import OmniWorkerAgent -# from swarms.tools.main import RequestsGet, ExitConversation -# visual agent +from swarms.agents.workers.WorkerNode import WorkerNode, worker_node +from swarms.agents.boss.BossNode import BossNode +from swarms.agents.workers.WorkerUltraNode import WorkerUltra -from swarms.agents.workers.worker_agent import worker_tool import logging - logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') class Swarms: - def __init__(self, openai_api_key): + def __init__(self, openai_api_key=""): + #openai_api_key: the openai key. Default is empty + if not openai_api_key: + logging.error("OpenAI key is not provided") + raise ValueError("OpenAI API key is required") + self.openai_api_key = openai_api_key def initialize_llm(self, llm_class, temperature=0.5): - # Initialize language model - return llm_class(openai_api_key=self.openai_api_key, temperature=temperature) + """ + Init LLM + + Params: + llm_class(class): The Language model class. Default is OpenAI. + temperature (float): The Temperature for the language model. Default is 0.5 + """ + try: + # Initialize language model + return llm_class(openai_api_key=self.openai_api_key, temperature=temperature) + except Exception as e: + logging.error(f"Failed to initialize language model: {e}") def initialize_tools(self, llm_class): - llm = self.initialize_llm(llm_class) - # Initialize tools - web_search = DuckDuckGoSearchRun() - tools = [ - web_search, - WriteFileTool(root_dir=ROOT_DIR), - ReadFileTool(root_dir=ROOT_DIR), - - process_csv, - WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), - - # CodeEditor, - # Terminal, - # RequestsGet, - # ExitConversation - - #code editor + terminal editor + visual agent - - ] - assert tools is not None, "tools is not initialized" - return tools - - def initialize_vectorstore(self): - # Initialize vector store - embeddings_model = OpenAIEmbeddings(openai_api_key=self.openai_api_key) - embedding_size = 1536 - index = faiss.IndexFlatL2(embedding_size) - return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) - - def initialize_worker_node(self, worker_tools, vectorstore): - # Initialize worker node - llm = self.initialize_llm(ChatOpenAI) - worker_node = WorkerNode(llm=llm, tools=worker_tools, vectorstore=vectorstore) - worker_node.create_agent(ai_name="Swarm Worker AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) - worker_node_tool = Tool(name="WorkerNode AI Agent", func=worker_node.run, description="Input: an objective with a todo list for that objective. Output: your task completed: Please be very clear what the objective and task instructions are. The Swarm worker agent is Useful for when you need to spawn an autonomous agent instance as a worker to accomplish any complex tasks, it can search the internet or write code or spawn child multi-modality models to process and generate images and text or audio and so on") - return worker_node_tool - - def initialize_boss_node(self, vectorstore, worker_node): - # Initialize boss node - llm = self.initialize_llm(OpenAI) - todo_prompt = PromptTemplate.from_template("You are a boss planer in a swarm who is an expert at coming up with a todo list for a given objective and then creating an worker to help you accomplish your task. Come up with a todo list for this objective: {objective} and then spawn a worker agent to complete the task for you. Always spawn an worker agent after creating a plan and pass the objective and plan to the worker agent.") - todo_chain = LLMChain(llm=llm, prompt=todo_prompt) - tools = [ - Tool(name="TODO", func=todo_chain.run, description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!"), - worker_node - ] - suffix = """Question: {task}\n{agent_scratchpad}""" - prefix = """You are an Boss in a swarm who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\n """ - prompt = ZeroShotAgent.create_prompt(tools, prefix=prefix, suffix=suffix, input_variables=["objective", "task", "context", "agent_scratchpad"],) - llm_chain = LLMChain(llm=llm, prompt=prompt) - agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=[tool.name for tool in tools]) - agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True) - # return BossNode(return BossNode(llm, vectorstore, agent_executor, max_iterations=5) - return BossNode(llm, vectorstore, agent_executor, max_iterations=5) - - - def run_swarms(self, objective): + """ + Init tools + + Params: + llm_class (class): The Language model class. Default is OpenAI + """ try: - # Run the swarm with the given objective - worker_tools = self.initialize_tools(OpenAI) - assert worker_tools is not None, "worker_tools is not initialized" + llm = self.initialize_llm(llm_class) + # Initialize tools + web_search = DuckDuckGoSearchRun() + tools = [ + web_search, + WriteFileTool(root_dir=ROOT_DIR), + ReadFileTool(root_dir=ROOT_DIR), - vectorstore = self.initialize_vectorstore() - worker_node = self.initialize_worker_node(worker_tools, vectorstore) + process_csv, + WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), + ] - boss_node = self.initialize_boss_node(vectorstore, worker_node) + assert tools is not None, "tools is not initialized" + return tools - task = boss_node.create_task(objective) - return boss_node.execute_task(task) except Exception as e: - logging.error(f"An error occurred in run_swarms: {e}") + logging.error(f"Failed to initialize tools: {e}") raise + def initialize_vectorstore(self): + """ + Init vector store + """ + try: + + embeddings_model = OpenAIEmbeddings(openai_api_key=self.openai_api_key) + embedding_size = 1536 + index = faiss.IndexFlatL2(embedding_size) + return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) + except Exception as e: + logging.error(f"Failed to initialize vector store: {e}") + raise + def initialize_worker_node(self, worker_tools, vectorstore, llm_class=ChatOpenAI, ai_name="Swarm Worker AI Assistant"): + """ + Init WorkerNode + Params: + worker_tools (list): The list of worker tools. + vectorstore (object): The vector store object + llm_class (class): The Language model class. Default is ChatOpenAI + ai_name (str): The AI name. Default is "Swarms worker AI assistant" + """ + try: + + # Initialize worker node + llm = self.initialize_llm(ChatOpenAI) + worker_node = WorkerNode(llm=llm, tools=worker_tools, vectorstore=vectorstore) + worker_node.create_agent(ai_name=ai_name, ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) # add search kwargs + + worker_node_tool = Tool(name="WorkerNode AI Agent", func=worker_node.run, description="Input: an objective with a todo list for that objective. Output: your task completed: Please be very clear what the objective and task instructions are. The Swarm worker agent is Useful for when you need to spawn an autonomous agent instance as a worker to accomplish any complex tasks, it can search the internet or write code or spawn child multi-modality models to process and generate images and text or audio and so on") + return worker_node_tool + except Exception as e: + logging.error(f"Failed to initialize worker node: {e}") + raise + def initialize_boss_node(self, vectorstore, worker_node, llm_class=OpenAI, max_iterations=5, verbose=False): + """ + Init BossNode + Params: + vectorstore (object): the vector store object. + worker_node (object): the worker node object + llm_class (class): the language model class. Default is OpenAI + max_iterations(int): The number of max iterations. Default is 5 + verbose(bool): Debug mode. Default is False + + """ + try: + # Initialize boss node + llm = self.initialize_llm(llm_class) + todo_prompt = PromptTemplate.from_template("You are a boss planer in a swarm who is an expert at coming up with a todo list for a given objective and then creating an worker to help you accomplish your task. Come up with a todo list for this objective: {objective} and then spawn a worker agent to complete the task for you. Always spawn an worker agent after creating a plan and pass the objective and plan to the worker agent.") + todo_chain = LLMChain(llm=llm, prompt=todo_prompt) + + tools = [ + Tool(name="TODO", func=todo_chain.run, description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!"), + worker_node + ] + suffix = """Question: {task}\n{agent_scratchpad}""" + prefix = """You are an Boss in a swarm who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\n """ + + prompt = ZeroShotAgent.create_prompt(tools, prefix=prefix, suffix=suffix, input_variables=["objective", "task", "context", "agent_scratchpad"],) + llm_chain = LLMChain(llm=llm, prompt=prompt) + agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=[tool.name for tool in tools]) + agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=verbose) + return BossNode(llm, vectorstore, agent_executor, max_iterations=max_iterations) + except Exception as e: + logging.error(f"Failed to initialize boss node: {e}") + raise + def run_swarms(self, objective): + """ + Run the swarm with the given objective + Params: + objective(str): The task + """ + try: + # Run the swarm with the given objective + worker_tools = self.initialize_tools(OpenAI) + assert worker_tools is not None, "worker_tools is not initialized" + vectorstore = self.initialize_vectorstore() + worker_node = self.initialize_worker_node(worker_tools, vectorstore) + boss_node = self.initialize_boss_node(vectorstore, worker_node) - - - - - - - - - - - - + task = boss_node.create_task(objective) + return boss_node.execute_task(task) + except Exception as e: + logging.error(f"An error occurred in run_swarms: {e}") + raise # usage -def swarm(api_key, objective): - swarms = Swarms(api_key) - return swarms.run_swarms(objective) - -# # Use the function -# api_key = "APIKEY" -# objective = "What is the capital of the UK?" -# result = swarm(api_key, objective) -# print(result) # Prints: "The capital of the UK is London." - - - - - - - - - - - - - - - - - - - - - - - - - - -# class Swarms: -# def __init__(self, openai_api_key): -# self.openai_api_key = openai_api_key - -# def initialize_llm(self, llm_class, temperature=0.5): -# # Initialize language model -# return llm_class(openai_api_key=self.openai_api_key, temperature=temperature) - -# def initialize_tools(self, llm_class): -# llm = self.initialize_llm(llm_class) -# # Initialize tools -# web_search = DuckDuckGoSearchRun() -# tools = [ -# web_search, -# WriteFileTool(root_dir=ROOT_DIR), -# ReadFileTool(root_dir=ROOT_DIR), - -# process_csv, -# WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), - -# # RequestsGet() -# Tool(name="RequestsGet", func=RequestsGet.get, description="A portal to the internet, Use this when you need to get specific content from a website. Input should be a url (i.e. https://www.google.com). The output will be the text response of the GET request."), - +def swarm(api_key="", objective=""): + """ + Run the swarm with the given API key and objective. + + Parameters: + api_key (str): The OpenAI API key. Default is an empty string. + objective (str): The objective. Default is an empty string. + + Returns: + The result of the swarm. + """ + + if not api_key: + logging.error("OpenAIkey is not provided") + raise ValueError("OpenAI API key is not provided") + if not objective: + logging.error("Objective is not provided") + raise ValueError("Objective is required") + try: -# # CodeEditor, -# # Terminal, -# # RequestsGet, -# # ExitConversation - -# #code editor + terminal editor + visual agent -# # Give the worker node itself as a tool - -# ] -# assert tools is not None, "tools is not initialized" -# return tools - -# def initialize_vectorstore(self): -# # Initialize vector store -# embeddings_model = OpenAIEmbeddings(openai_api_key=self.openai_api_key) -# embedding_size = 1536 -# index = faiss.IndexFlatL2(embedding_size) -# return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) - -# def initialize_worker_node(self, worker_tools, vectorstore): -# # Initialize worker node -# llm = self.initialize_llm(ChatOpenAI) -# worker_node = WorkerNode(llm=llm, tools=worker_tools, vectorstore=vectorstore) -# worker_node.create_agent(ai_name="Swarm Worker AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) -# worker_node_tool = Tool(name="WorkerNode AI Agent", func=worker_node.run, description="Input: an objective with a todo list for that objective. Output: your task completed: Please be very clear what the objective and task instructions are. The Swarm worker agent is Useful for when you need to spawn an autonomous agent instance as a worker to accomplish any complex tasks, it can search the internet or write code or spawn child multi-modality models to process and generate images and text or audio and so on") -# return worker_node_tool - -# def initialize_boss_node(self, vectorstore, worker_node): -# # Initialize boss node -# llm = self.initialize_llm(OpenAI) -# todo_prompt = PromptTemplate.from_template("You are a boss planer in a swarm who is an expert at coming up with a todo list for a given objective and then creating an worker to help you accomplish your task. Come up with a todo list for this objective: {objective} and then spawn a worker agent to complete the task for you. Always spawn an worker agent after creating a plan and pass the objective and plan to the worker agent.") -# todo_chain = LLMChain(llm=llm, prompt=todo_prompt) -# tools = [ -# Tool(name="TODO", func=todo_chain.run, description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!"), -# worker_node -# ] -# suffix = """Question: {task}\n{agent_scratchpad}""" -# prefix = """You are an Boss in a swarm who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\n """ -# prompt = ZeroShotAgent.create_prompt(tools, prefix=prefix, suffix=suffix, input_variables=["objective", "task", "context", "agent_scratchpad"],) -# llm_chain = LLMChain(llm=llm, prompt=prompt) -# agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=[tool.name for tool in tools]) -# agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True) -# # return BossNode(return BossNode(llm, vectorstore, agent_executor, max_iterations=5) -# return BossNode(llm, vectorstore, agent_executor, max_iterations=5) - - -# def run_swarms(self, objective, run_as=None): -# try: -# # Run the swarm with the given objective -# worker_tools = self.initialize_tools(OpenAI) -# assert worker_tools is not None, "worker_tools is not initialized" - -# vectorstore = self.initialize_vectorstore() -# worker_node = self.initialize_worker_node(worker_tools, vectorstore) - -# if run_as.lower() == 'worker': -# tool_input = {'prompt': objective} -# return worker_node.run(tool_input) -# else: -# boss_node = self.initialize_boss_node(vectorstore, worker_node) -# task = boss_node.create_task(objective) -# return boss_node.execute_task(task) -# except Exception as e: -# logging.error(f"An error occurred in run_swarms: {e}") -# raise - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#omni agent ===> working -# class Swarms: -# def __init__(self, -# openai_api_key, -# # omni_api_key=None, -# # omni_api_endpoint=None, -# # omni_api_type=None -# ): -# self.openai_api_key = openai_api_key -# # self.omni_api_key = omni_api_key -# # self.omni_api_endpoint = omni_api_endpoint -# # self.omni_api_key = omni_api_type - -# # if omni_api_key and omni_api_endpoint and omni_api_type: -# # self.omni_worker_agent = OmniWorkerAgent(omni_api_key, omni_api_endpoint, omni_api_type) -# # else: -# # self.omni_worker_agent = None - -# def initialize_llm(self): -# # Initialize language model -# return ChatOpenAI(model_name="gpt-4", temperature=1.0, openai_api_key=self.openai_api_key) - -# def initialize_tools(self, llm): -# # Initialize tools -# web_search = DuckDuckGoSearchRun() -# tools = [ -# web_search, -# WriteFileTool(root_dir=ROOT_DIR), -# ReadFileTool(root_dir=ROOT_DIR), -# process_csv, -# WebpageQATool(qa_chain=load_qa_with_sources_chain(llm)), -# ] -# # if self.omni_worker_agent: -# # tools.append(self.omni_worker_agent.chat) #add omniworker agent class -# return tools - -# def initialize_vectorstore(self): -# # Initialize vector store -# embeddings_model = OpenAIEmbeddings() -# embedding_size = 1536 -# index = faiss.IndexFlatL2(embedding_size) -# return FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {}) - -# def initialize_worker_node(self, llm, worker_tools, vectorstore): -# # Initialize worker node -# worker_node = WorkerNode(llm=llm, tools=worker_tools, vectorstore=vectorstore) -# worker_node.create_agent(ai_name="AI Assistant", ai_role="Assistant", human_in_the_loop=False, search_kwargs={}) -# return worker_node - -# def initialize_boss_node(self, llm, vectorstore, worker_node): -# # Initialize boss node -# todo_prompt = PromptTemplate.from_template("You are a planner who is an expert at coming up with a todo list for a given objective. Come up with a todo list for this objective: {objective}") -# todo_chain = LLMChain(llm=OpenAI(temperature=0), prompt=todo_prompt) -# tools = [ -# Tool(name="TODO", func=todo_chain.run, description="useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!"), -# worker_node, -# ] -# suffix = """Question: {task}\n{agent_scratchpad}""" -# prefix = """You are an Boss in a swarm who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\n""" -# prompt = ZeroShotAgent.create_prompt(tools, prefix=prefix, suffix=suffix, input_variables=["objective", "task", "context", "agent_scratchpad"],) -# llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt) -# agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=[tool.name for tool in tools]) -# agent_executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True) -# return BossNode(self.openai_api_key, llm, vectorstore, agent_executor, verbose=True, max_iterations=5) - -# def run_swarms(self, objective): -# # Run the swarm with the given objective -# llm = self.initialize_llm() -# worker_tools = self.initialize_tools(llm) -# vectorstore = self.initialize_vectorstore() -# worker_node = self.initialize_worker_node(llm, worker_tools, vectorstore) -# boss_node = self.initialize_boss_node(llm, vectorstore, worker_node) -# task = boss_node.create_task(objective) -# boss_node.execute_task(task) -# worker_node.run_agent(objective) - - - - - - - - - - - - -# class Swarms: -# def __init__(self, num_nodes: int, llm: BaseLLM, self_scaling: bool): -# self.nodes = [WorkerNode(llm) for _ in range(num_nodes)] -# self.self_scaling = self_scaling - -# def add_worker(self, llm: BaseLLM): -# self.nodes.append(WorkerNode(llm)) - -# def remove_workers(self, index: int): -# self.nodes.pop(index) - -# def execute(self, task): -# #placeholer for main execution logic -# pass - -# def scale(self): -# #placeholder for self scaling logic -# pass - - - -#special classes - -# class HierarchicalSwarms(Swarms): -# def execute(self, task): -# pass - - -# class CollaborativeSwarms(Swarms): -# def execute(self, task): -# pass - -# class CompetitiveSwarms(Swarms): -# def execute(self, task): -# pass - -# class MultiAgentDebate(Swarms): -# def execute(self, task): -# pass - - -#======================================> WorkerNode - - -# class MetaWorkerNode: -# def __init__(self, llm, tools, vectorstore): -# self.llm = llm -# self.tools = tools -# self.vectorstore = vectorstore - -# self.agent = None -# self.meta_chain = None - -# def init_chain(self, instructions): -# self.agent = WorkerNode(self.llm, self.tools, self.vectorstore) -# self.agent.create_agent("Assistant", "Assistant Role", False, {}) - -# def initialize_meta_chain(): -# meta_template = """ -# Assistant has just had the below interactions with a User. Assistant followed their "Instructions" closely. Your job is to critique the Assistant's performance and then revise the Instructions so that Assistant would quickly and correctly respond in the future. - -# #### - -# {chat_history} - -# #### - -# Please reflect on these interactions. - -# You should first critique Assistant's performance. What could Assistant have done better? What should the Assistant remember about this user? Are there things this user always wants? Indicate this with "Critique: ...". - -# You should next revise the Instructions so that Assistant would quickly and correctly respond in the future. Assistant's goal is to satisfy the user in as few interactions as possible. Assistant will only see the new Instructions, not the interaction history, so anything important must be summarized in the Instructions. Don't forget any important details in the current Instructions! Indicate the new Instructions by "Instructions: ...". -# """ - -# meta_prompt = PromptTemplate( -# input_variables=["chat_history"], template=meta_template -# ) - -# meta_chain = LLMChain( -# llm=OpenAI(temperature=0), -# prompt=meta_prompt, -# verbose=True, -# ) -# return meta_chain - -# def meta_chain(self): -# #define meta template and meta prompting as per your needs -# self.meta_chain = initialize_meta_chain() - - -# def get_chat_history(chain_memory): -# memory_key = chain_memory.memory_key -# chat_history = chain_memory.load_memory_variables(memory_key)[memory_key] -# return chat_history - - -# def get_new_instructions(meta_output): -# delimiter = "Instructions: " -# new_instructions = meta_output[meta_output.find(delimiter) + len(delimiter) :] -# return new_instructions - - -# def main(self, task, max_iters=3, max_meta_iters=5): -# failed_phrase = "task failed" -# success_phrase = "task succeeded" -# key_phrases = [success_phrase, failed_phrase] - -# instructions = "None" -# for i in range(max_meta_iters): -# print(f"[Episode {i+1}/{max_meta_iters}]") -# self.initialize_chain(instructions) -# output = self.agent.perform('Assistant', {'request': task}) -# for j in range(max_iters): -# print(f"(Step {j+1}/{max_iters})") -# print(f"Assistant: {output}") -# print(f"Human: ") -# human_input = input() -# if any(phrase in human_input.lower() for phrase in key_phrases): -# break -# output = self.agent.perform('Assistant', {'request': human_input}) -# if success_phrase in human_input.lower(): -# print(f"You succeeded! Thanks for playing!") -# return -# self.initialize_meta_chain() -# meta_output = self.meta_chain.predict(chat_history=self.get_chat_history()) -# print(f"Feedback: {meta_output}") -# instructions = self.get_new_instructions(meta_output) -# print(f"New Instructions: {instructions}") -# print("\n" + "#" * 80 + "\n") -# print(f"You failed! Thanks for playing!") - - -# #init instance of MetaWorkerNode -# meta_worker_node = MetaWorkerNode(llm=OpenAI, tools=tools, vectorstore=vectorstore) - - -# #specify a task and interact with the agent -# task = "Provide a sysmatic argument for why we should always eat past with olives" -# meta_worker_node.main(task) - - -####################################################################### => Boss Node -####################################################################### => Boss Node -####################################################################### => Boss Node + swarms = Swarms(api_key) + return swarms.run_swarms(objective) + except Exception as e: + logging.error(f"An error occured in swarm: {e}") + raise \ No newline at end of file diff --git a/swarms/tools/main.py b/swarms/tools/main.py index fb21fabb..67a32b9b 100644 --- a/swarms/tools/main.py +++ b/swarms/tools/main.py @@ -428,7 +428,7 @@ import time from datetime import datetime from typing import Dict, List -from swarms.utils.utils import ANSI, Color, Style # test +from swarms.utils.main import ANSI, Color, Style # test class Terminal(BaseToolSet): def __init__(self): @@ -1100,7 +1100,7 @@ from transformers import ( ) -from swarms.utils.utils import get_new_image_name +from swarms.utils.main import get_new_image_name class MaskFormer(BaseToolSet): @@ -1300,6 +1300,56 @@ class VisualQuestionAnswering(BaseToolSet): return answer + +#========================> handlers/image +import torch +from PIL import Image +from transformers import BlipForConditionalGeneration, BlipProcessor + +# from core.prompts.file import IMAGE_PROMPT +from swarms.prompts.prompts import IMAGE_PROMPT + +from swarms.utils.main import BaseHandler + +class ImageCaptioning(BaseHandler): + def __init__(self, device): + print("Initializing ImageCaptioning to %s" % device) + self.device = device + self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 + self.processor = BlipProcessor.from_pretrained( + "Salesforce/blip-image-captioning-base" + ) + self.model = BlipForConditionalGeneration.from_pretrained( + "Salesforce/blip-image-captioning-base", torch_dtype=self.torch_dtype + ).to(self.device) + + def handle(self, filename: str): + img = Image.open(filename) + width, height = img.size + ratio = min(512 / width, 512 / height) + width_new, height_new = (round(width * ratio), round(height * ratio)) + img = img.resize((width_new, height_new)) + img = img.convert("RGB") + img.save(filename, "PNG") + print(f"Resize image form {width}x{height} to {width_new}x{height_new}") + + inputs = self.processor(Image.open(filename), return_tensors="pt").to( + self.device, self.torch_dtype + ) + out = self.model.generate(**inputs) + description = self.processor.decode(out[0], skip_special_tokens=True) + print( + f"\nProcessed ImageCaptioning, Input Image: {filename}, Output Text: {description}" + ) + + return IMAGE_PROMPT.format(filename=filename, description=description) + + + + + + + #segment anything: ########################### MODELS diff --git a/swarms/utils/utils.py b/swarms/utils/main.py similarity index 78% rename from swarms/utils/utils.py rename to swarms/utils/main.py index 6cf1a56e..78e9b7f8 100644 --- a/swarms/utils/utils.py +++ b/swarms/utils/main.py @@ -225,7 +225,7 @@ class AbstractUploader(ABC): #========================= upload s3 -import os + import boto3 @@ -262,7 +262,6 @@ class S3Uploader(AbstractUploader): #========================= upload s3 #========================> upload/static -import os import shutil from pathlib import Path @@ -275,7 +274,10 @@ class StaticUploader(AbstractUploader): @staticmethod def from_settings(path: Path, endpoint: str) -> "StaticUploader": - return StaticUploader(os.environ["SERVER"], path, endpoint) + server = os.environ.get("SERVER", "http://localhost:8000") + return StaticUploader(server, path, endpoint) + + def get_url(self, uploaded_path: str) -> str: return f"{self.server}/{uploaded_path}" @@ -291,11 +293,9 @@ class StaticUploader(AbstractUploader): #========================> handlers/base -import os -import shutil + import uuid from enum import Enum -from pathlib import Path from typing import Dict import requests @@ -370,19 +370,17 @@ class FileHandler: def handle(self, url: str) -> str: try: - if url.startswith(settings["SERVER"]): - local_filepath = url[len(settings["SERVER"]) + 1 :] + if url.startswith(os.environ.get("SERVER", "http://localhost:8000")): + local_filepath = url[len(os.environ.get("SERVER", "http://localhost:8000")) + 1 :] local_filename = Path("file") / local_filepath.split("/")[-1] src = self.path / local_filepath - dst = self.path / settings["PLAYGROUND_DIR"] / local_filename + dst = self.path / os.environ.get("PLAYGROUND_DIR", "./playground") / local_filename os.makedirs(os.path.dirname(dst), exist_ok=True) shutil.copy(src, dst) else: local_filename = self.download(url) - - try: - handler = self.handlers[FileType.from_url(url)] - except KeyError: + handler = self.handlers.get(FileType.from_url(url)) + if handler is None: if FileType.from_url(url) == FileType.IMAGE: raise Exception( f"No handler for {FileType.from_url(url)}. " @@ -390,10 +388,9 @@ class FileHandler: ) else: raise Exception(f"No handler for {FileType.from_url(url)}") - handler.handle(local_filename) + return handler.handle(local_filename) except Exception as e: raise e - ########################### => base end @@ -402,11 +399,10 @@ class FileHandler: #############===========================> -import pandas as pd from swarms.prompts.prompts import DATAFRAME_PROMPT - +import pandas as pd class CsvToDataframe(BaseHandler): def handle(self, filename: str): df = pd.read_csv(filename) @@ -425,77 +421,3 @@ class CsvToDataframe(BaseHandler): - -#========================> handlers/image -import torch -from PIL import Image -from transformers import BlipForConditionalGeneration, BlipProcessor - -# from core.prompts.file import IMAGE_PROMPT -from swarms.prompts.prompts import IMAGE_PROMPT - - - -class ImageCaptioning(BaseHandler): - def __init__(self, device): - print("Initializing ImageCaptioning to %s" % device) - self.device = device - self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 - self.processor = BlipProcessor.from_pretrained( - "Salesforce/blip-image-captioning-base" - ) - self.model = BlipForConditionalGeneration.from_pretrained( - "Salesforce/blip-image-captioning-base", torch_dtype=self.torch_dtype - ).to(self.device) - - def handle(self, filename: str): - img = Image.open(filename) - width, height = img.size - ratio = min(512 / width, 512 / height) - width_new, height_new = (round(width * ratio), round(height * ratio)) - img = img.resize((width_new, height_new)) - img = img.convert("RGB") - img.save(filename, "PNG") - print(f"Resize image form {width}x{height} to {width_new}x{height_new}") - - inputs = self.processor(Image.open(filename), return_tensors="pt").to( - self.device, self.torch_dtype - ) - out = self.model.generate(**inputs) - description = self.processor.decode(out[0], skip_special_tokens=True) - print( - f"\nProcessed ImageCaptioning, Input Image: {filename}, Output Text: {description}" - ) - - return IMAGE_PROMPT.format(filename=filename, description=description) - - - - -# from autogpt.agent import Agent -# from swarms.agents.swarms import worker_node - -# class MultiAgent(worker_node): - -# def __init__( -# self, -# ai_name, -# memory, -# full_message_history, -# prompt, -# user_input, -# agent_id -# ): -# super().__init__( -# ai_name=ai_name, -# memory=memory, -# full_message_history=full_message_history, -# next_action_count=0, -# prompt=prompt, -# user_input=user_input, -# ) -# self.agent_id = agent_id -# self.auditory_buffer = [] # contains the non processed parts of the conversation - -# def receive_message(self, speaker, message): -# self.auditory_buffer.append((speaker.ai_name, message)) \ No newline at end of file diff --git a/swarms/utils/static.py b/swarms/utils/static.py index 7b642857..72acbcef 100644 --- a/swarms/utils/static.py +++ b/swarms/utils/static.py @@ -4,7 +4,7 @@ from pathlib import Path # from env import DotEnv -from swarms.utils.utils import AbstractUploader +from swarms.utils.main import AbstractUploader class StaticUploader(AbstractUploader): def __init__(self, server: str, path: Path, endpoint: str): diff --git a/tests/LLM.py b/tests/LLM.py index 57360294..9f384ade 100644 --- a/tests/LLM.py +++ b/tests/LLM.py @@ -3,7 +3,7 @@ import os from unittest.mock import patch, MagicMock from langchain import PromptTemplate, HuggingFaceHub, ChatOpenAI, LLMChain -from swarms.utils.llm import LLM +from swarms.utils.LLM import LLM class TestLLM(unittest.TestCase): @patch.object(HuggingFaceHub, '__init__', return_value=None) diff --git a/tests/swarms.py b/tests/swarms.py index 565d4389..291aad08 100644 --- a/tests/swarms.py +++ b/tests/swarms.py @@ -1,7 +1,7 @@ import unittest import swarms -from swarms.agents.workers.worker_agent import WorkerNode -from swarms.agents.boss.boss_agent import BossNode +from swarms.agents.workers.WorkerNode import WorkerNode +from swarms.agents.boss.BossNode import BossNode class TestSwarms(unittest.TestCase): def setUp(self): diff --git a/worker_ultra.py b/worker_ultra.py new file mode 100644 index 00000000..2c9fe09c --- /dev/null +++ b/worker_ultra.py @@ -0,0 +1,21 @@ +import os +from swarms.swarms import WorkerUltra + +api_key = os.getenv("OPENAI_API_KEY") + +# Define an objective +objective = """ +Please make a web GUI for using HTTP API server. +The name of it is Swarms. +You can check the server code at ./main.py. +The server is served on localhost:8000. +Users should be able to write text input as 'query' and url array as 'files', and check the response. +Users input form should be delivered in JSON format. +I want it to have neumorphism-style. Serve it on port 4500. + +""" + +node = WorkerUltra(objective, openai_api_key=api_key) + + +result = node.execute() \ No newline at end of file