From 052743bce1389d0fcd19fdaee05c1409be59e586 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Mon, 24 Jun 2024 12:16:27 -0700 Subject: [PATCH 1/5] [FEAT][GraphWorkflow] [Memory Optimization] [Tokenizer Implementation] --- README.md | 2 + api.py | 192 ++++++ docs/mkdocs.yml | 2 + example.py | 31 +- jamba_swarm/api.py | 0 jamba_swarm/api_schemas.py | 90 +++ jamba_swarm/jamba_llm.py | 56 ++ jamba_swarm/main.ipynb | 583 ++++++++++++++++++ jamba_swarm/main.py | 174 ++++++ jamba_swarm/prompts.py | 181 ++++++ jamba_swarm/simple_jamba_swarm.py | 138 +++++ ...json_log_cleanup.py => json_log_cleanup.py | 2 +- phising_my_parents.py | 53 -- .../agents/agent_with_long_term_memory.py | 36 ++ .../{complete_agent.py => devin_agent.py} | 39 +- .../multion_examples/ multion_example.py | 6 +- .../buy_abunch_of_cybertrucks.py | 1 - playground/demos/email_phiser/email_swarm.py | 81 +++ .../agents.py | 226 +++++++ playground/memory/chromadb_example.py | 17 +- prompts/agent_creator_prompt.md | 421 +++++++++++++ prompts/agent_orchestrator.txt | 546 ++++++++++++++++ pyproject.toml | 3 +- swarms/agents/multion_wrapper.py | 16 +- swarms/artifacts/__init__.py | 2 + swarms/memory/base_vectordb.py | 35 ++ swarms/models/tiktoken_wrapper.py | 54 ++ swarms/structs/__init__.py | 8 +- swarms/structs/agent.py | 314 ++++++---- swarms/structs/graph_workflow.py | 249 ++++++++ swarms/structs/mixture_of_agents.py | 11 + swarms/structs/rearrange.py | 9 + swarms/structs/swarm_net.py | 221 ++++--- swarms/tools/func_calling_utils.py | 35 +- swarms/tools/tool_parse_exec.py | 36 +- 35 files changed, 3500 insertions(+), 370 deletions(-) create mode 100644 api.py create mode 100644 jamba_swarm/api.py create mode 100644 jamba_swarm/api_schemas.py create mode 100644 jamba_swarm/jamba_llm.py create mode 100644 jamba_swarm/main.ipynb create mode 100644 jamba_swarm/main.py create mode 100644 jamba_swarm/prompts.py create mode 100644 jamba_swarm/simple_jamba_swarm.py rename scripts/cleanup/json_log_cleanup.py => json_log_cleanup.py (96%) delete mode 100644 phising_my_parents.py create mode 100644 playground/agents/agent_with_long_term_memory.py rename playground/agents/{complete_agent.py => devin_agent.py} (76%) rename multion_example.py => playground/agents/multion_examples/ multion_example.py (86%) rename buy_abunch_of_cybertrucks.py => playground/agents/multion_examples/buy_abunch_of_cybertrucks.py (99%) create mode 100644 playground/demos/email_phiser/email_swarm.py create mode 100644 playground/demos/social_media_content_generators_swarm/agents.py create mode 100644 prompts/agent_creator_prompt.md create mode 100644 prompts/agent_orchestrator.txt create mode 100644 swarms/models/tiktoken_wrapper.py create mode 100644 swarms/structs/graph_workflow.py diff --git a/README.md b/README.md index 064a713a..d071f5d6 100644 --- a/README.md +++ b/README.md @@ -1507,6 +1507,8 @@ print(out) ``` + + --- ## Documentation diff --git a/api.py b/api.py new file mode 100644 index 00000000..2f5d6324 --- /dev/null +++ b/api.py @@ -0,0 +1,192 @@ +import asyncio +import os +from typing import List + +import tiktoken +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel +from swarms import Agent, Anthropic, GPT4o, GPT4VisionAPI, OpenAIChat +from swarms.utils.loguru_logger import logger + +from swarms_cloud.schema.cog_vlm_schemas import ( + ChatCompletionResponse, + UsageInfo, +) + + +# Define the input model using Pydantic +class AgentInput(BaseModel): + agent_name: str = "Swarm Agent" + system_prompt: str = None + agent_description: str = None + model_name: str = "OpenAIChat" + max_loops: int = 1 + autosave: bool = False + dynamic_temperature_enabled: bool = False + dashboard: bool = False + verbose: bool = False + streaming_on: bool = True + saved_state_path: str = None + sop: str = None + sop_list: List[str] = None + user_name: str = "User" + retry_attempts: int = 3 + context_length: int = 8192 + task: str = None + + +# Define the input model using Pydantic +class AgentOutput(BaseModel): + agent: AgentInput + completions: ChatCompletionResponse + + +async def count_tokens( + text: str, +): + try: + # Get the encoding for the specific model + encoding = tiktoken.get_encoding("gpt-4o") + + # Encode the text + tokens = encoding.encode(text) + + # Count the tokens + token_count = len(tokens) + + return token_count + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + + +async def model_router(model_name: str): + """ + Function to switch to the specified model. + + Parameters: + - model_name (str): The name of the model to switch to. + + Returns: + - None + + Raises: + - None + + """ + # Logic to switch to the specified model + if model_name == "OpenAIChat": + # Switch to OpenAIChat model + llm = OpenAIChat() + elif model_name == "GPT4o": + # Switch to GPT4o model + llm = GPT4o(openai_api_key=os.getenv("OPENAI_API_KEY")) + elif model_name == "GPT4VisionAPI": + # Switch to GPT4VisionAPI model + llm = GPT4VisionAPI() + elif model_name == "Anthropic": + # Switch to Anthropic model + llm = Anthropic(anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")) + else: + # Invalid model name + pass + + return llm + + +# Create a FastAPI app +app = FastAPI(debug=True) + +# Load the middleware to handle CORS +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +# @app.get("/v1/models", response_model=ModelList) +# async def list_models(): +# """ +# An endpoint to list available models. It returns a list of model cards. +# This is useful for clients to query and understand what models are available for use. +# """ +# model_card = ModelCard( +# id="cogvlm-chat-17b" +# ) # can be replaced by your model id like cogagent-chat-18b +# return ModelList(data=[model_card]) + + +@app.post("v1/agent/completions", response_model=AgentOutput) +async def agent_completions(agent_input: AgentInput): + try: + logger.info(f"Received request: {agent_input}") + llm = model_router(agent_input.model_name) + + agent = Agent( + agent_name=agent_input.agent_name, + system_prompt=agent_input.system_prompt, + agent_description=agent_input.agent_description, + llm=llm, + max_loops=agent_input.max_loops, + autosave=agent_input.autosave, + dynamic_temperature_enabled=agent_input.dynamic_temperature_enabled, + dashboard=agent_input.dashboard, + verbose=agent_input.verbose, + streaming_on=agent_input.streaming_on, + saved_state_path=agent_input.saved_state_path, + sop=agent_input.sop, + sop_list=agent_input.sop_list, + user_name=agent_input.user_name, + retry_attempts=agent_input.retry_attempts, + context_length=agent_input.context_length, + ) + + # Run the agent + logger.info(f"Running agent with task: {agent_input.task}") + completions = await agent.run(agent_input.task) + + logger.info(f"Completions: {completions}") + all_input_tokens, output_tokens = await asyncio.gather( + count_tokens(agent.short_memory.return_history_as_string()), + count_tokens(completions), + ) + + logger.info(f"Token counts: {all_input_tokens}, {output_tokens}") + + out = AgentOutput( + agent=agent_input, + completions=ChatCompletionResponse( + choices=[ + { + "index": 0, + "message": { + "role": agent_input.agent_name, + "content": completions, + "name": None, + }, + } + ], + stream_choices=None, + usage_info=UsageInfo( + prompt_tokens=all_input_tokens, + completion_tokens=output_tokens, + total_tokens=all_input_tokens + output_tokens, + ), + ), + ) + + return out.json() + + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + + +# if __name__ == "__main__": +# import uvicorn + +# uvicorn.run( +# app, host="0.0.0.0", port=8000, use_colors=True, log_level="info" +# ) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 663831af..6c5a2e60 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -134,6 +134,8 @@ nav: - GPT4o: "swarms/models/gpt4o.md" - Agents: - Agent: "swarms/structs/agent.md" + - Artifacts: + - Overview: "swarms/artifacts/artifact.md" - Structs: - Foundational Structures: - BaseStructure: "swarms/structs/basestructure.md" diff --git a/example.py b/example.py index 7f8d77d4..98f95742 100644 --- a/example.py +++ b/example.py @@ -1,10 +1,4 @@ from swarms import Agent, OpenAIChat -from swarms.agents.multion_wrapper import MultiOnAgent - -model = MultiOnAgent( - url="https://tesla.com", -) - def calculate_profit(revenue: float, expenses: float): @@ -34,16 +28,6 @@ def generate_report(company_name: str, profit: float): """ return f"The profit for {company_name} is ${profit}." -def browser_automation(task: str = None): - """ - Run a task on the browser automation agent. - - Args: - task (str): The task to be executed on the browser automation agent. - """ - out = model.run(task) - return out - # Initialize the agent agent = Agent( @@ -53,16 +37,25 @@ agent = Agent( llm=OpenAIChat(), max_loops=1, autosave=True, - dynamic_temperature_enabled=True, + # dynamic_temperature_enabled=True, dashboard=False, verbose=True, streaming_on=True, # interactive=True, # Set to False to disable interactive mode saved_state_path="accounting_agent.json", - # tools=[calculate_profit, generate_report], + tools=[calculate_profit, generate_report], # docs_folder="docs", # pdf_path="docs/accounting_agent.pdf", - # tools=[browser_automation], + # sop="Calculate the profit for a company.", + # sop_list=["Calculate the profit for a company."], + # user_name="User", + # # docs= + # # docs_folder="docs", + # retry_attempts=3, + # context_length=1000, + # tool_schema = dict + context_length=1000, + # long_term_memory=ChromaDB(docs_folder="artifacts"), ) agent.run( diff --git a/jamba_swarm/api.py b/jamba_swarm/api.py new file mode 100644 index 00000000..e69de29b diff --git a/jamba_swarm/api_schemas.py b/jamba_swarm/api_schemas.py new file mode 100644 index 00000000..e4c0b12c --- /dev/null +++ b/jamba_swarm/api_schemas.py @@ -0,0 +1,90 @@ +import time +from typing import List, Optional + +from pydantic import BaseModel + + +class AgentSchema(BaseModel): + name: str = None + system_prompt: str = None + task: str = None + response: str = None + + +class JambaSwarmRequest(BaseModel): + task: str = (None,) + plan: str = None + agents: List[AgentSchema] = None + timestamp: int = int(time.time()) + + +class JambaSwarmResponse(BaseModel): + task: str = (None,) + plan: str = None + agents: List[AgentSchema] = None + timestamp: int = int(time.time()) + response: str = None + + +class AgentSchema(BaseModel): + name: Optional[str] = None + system_prompt: Optional[str] = None + task: Optional[str] = None + response: Optional[str] = None + + +class DirectorSettings(BaseModel): + name: str + strategy: str + objectives: List[str] + + +class BossSettings(BaseModel): + name: str + decision_making_strategy: str + recruitment_strategy: str + + +class TaskDistribution(BaseModel): + task: str + assigned_agents: List[str] + + +class JambaSwarmRequest(BaseModel): + task: Optional[str] = None + plan: Optional[str] = None + agents: Optional[List[AgentSchema]] = None + director_settings: DirectorSettings + boss_settings: BossSettings + task_distribution: Optional[List[TaskDistribution]] = None + timestamp: int = int(time.time()) + + +class JambaSwarmResponse(BaseModel): + task: Optional[str] = None + plan: Optional[str] = None + agents: Optional[List[AgentSchema]] = None + response: Optional[str] = None + timestamp: int = int(time.time()) + + +# Sample usage: + + +# try: +# request = JambaSwarmRequest( +# task="Research on AI", +# plan="Execute a comprehensive research plan", +# agents=[ +# AgentSchema(name="Agent1", system_prompt="Analyze recent AI papers", task="AI research task"), +# AgentSchema(name="Agent2", system_prompt="Summarize AI research findings", task="Summarization task"), +# ], +# director_settings=DirectorSettings(name="Director1", strategy="Hierarchical", objectives=["Efficiency", "Accuracy"]), +# boss_settings=BossSettings(name="Boss1", decision_making_strategy="Collaborative", recruitment_strategy="Pre-selected"), +# task_distribution=[ +# TaskDistribution(task="Research on AI", assigned_agents=["Agent1", "Agent2"]) +# ] +# ) +# print(request.json()) +# except ValidationError as e: +# print(e.json()) diff --git a/jamba_swarm/jamba_llm.py b/jamba_swarm/jamba_llm.py new file mode 100644 index 00000000..0d8f86a5 --- /dev/null +++ b/jamba_swarm/jamba_llm.py @@ -0,0 +1,56 @@ +import os + +from ai21 import AI21Client +from ai21.models.chat import ChatMessage +from dotenv import load_dotenv + +from swarms import BaseLLM + +load_dotenv() + + +class Jamba(BaseLLM): + def __init__( + self, + api_key: str = os.getenv("AI21_API_KEY"), + temperature: int = 0.8, + max_tokens: int = 200, + ): + """ + Initializes the Jamba class with the provided API key. + + Args: + api_key (str): The API key for the AI21Client. + """ + os.environ["AI21_API_KEY"] = api_key + self.api_key = api_key + self.temperature = temperature + self.max_tokens = max_tokens + self.client = AI21Client() + + def run(self, prompt: str, *args, **kwargs) -> str: + """ + Generates a response for the given prompt using the AI21 model. + + Args: + prompt (str): The prompt for generating the response. + + Returns: + str: The generated response. + + Raises: + Exception: If there is an issue with the API request. + """ + try: + response = self.client.chat.completions.create( + model="jamba-instruct-preview", # Latest model + messages=[ChatMessage(role="user", content=prompt)], + temperature=self.temperature, + max_tokens=self.max_tokens, + *args, + **kwargs, + ) + return response.choices[0].message.content + except Exception as e: + print(f"Error: {e}") + raise diff --git a/jamba_swarm/main.ipynb b/jamba_swarm/main.ipynb new file mode 100644 index 00000000..1f54c5c6 --- /dev/null +++ b/jamba_swarm/main.ipynb @@ -0,0 +1,583 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [], + "source": [ + "# ! pip install ai21\n", + "# ! pip install swarms\n", + "\n", + "import os\n", + "from typing import List\n", + "\n", + "from ai21 import AI21Client\n", + "from ai21.models.chat import ChatMessage\n", + "from dotenv import load_dotenv\n", + "\n", + "from swarms import Agent, BaseLLM, MixtureOfAgents\n", + "\n", + "load_dotenv()\n", + "\n", + "class Jamba(BaseLLM):\n", + " def __init__(self, api_key: str = os.getenv(\"AI21_API_KEY\"), temperature: int = 0.8, max_tokens: int = 200):\n", + " \"\"\"\n", + " Initializes the Jamba class with the provided API key.\n", + "\n", + " Args:\n", + " api_key (str): The API key for the AI21Client.\n", + " \"\"\"\n", + " os.environ[\"AI21_API_KEY\"] = api_key\n", + " self.api_key = api_key\n", + " self.temperature = temperature\n", + " self.max_tokens = max_tokens\n", + " self.client = AI21Client()\n", + "\n", + " def run(self, prompt: str, *args, **kwargs) -> str:\n", + " \"\"\"\n", + " Generates a response for the given prompt using the AI21 model.\n", + "\n", + " Args:\n", + " prompt (str): The prompt for generating the response.\n", + "\n", + " Returns:\n", + " str: The generated response.\n", + " \n", + " Raises:\n", + " Exception: If there is an issue with the API request.\n", + " \"\"\"\n", + " try:\n", + " response = self.client.chat.completions.create(\n", + " model=\"jamba-instruct-preview\", # Latest model\n", + " messages=[ChatMessage(\n", + " role=\"user\",\n", + " content=prompt\n", + " )],\n", + " temperature=self.temperature,\n", + " max_tokens=self.max_tokens,\n", + " *args, \n", + " **kwargs\n", + " )\n", + " return response.choices[0].message.content\n", + " except Exception as e:\n", + " print(f\"Error: {e}\")\n", + " raise\n", + "\n", + "# Example usage:\n", + "# jamba = Jamba()\n", + "# out = jamba.run(\"Write a product title for a sports T-shirt to be published on an\")\n", + "# print(out)\n", + "\n", + "\n", + "model = Jamba(\n", + " max_tokens=4000,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agent Planner\n", + "This agent below will make the plan for the agent." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mInitializing Autonomous Agent Boss Director...\u001b[0m\n" + ] + } + ], + "source": [ + "\n", + "BOSS_PLANNER = \"\"\"\n", + "You're the swarm orchestrator agent\n", + "\n", + "**Objective:** Your task is to intake a business problem or activity and create a swarm of specialized LLM agents that can efficiently solve or automate the given problem. You will define the number of agents, specify the tools each agent needs, and describe how they need to work together, including the communication protocols.\n", + "\n", + "**Instructions:**\n", + "\n", + "1. **Intake Business Problem:**\n", + " - Receive a detailed description of the business problem or activity to automate.\n", + " - Clarify the objectives, constraints, and expected outcomes of the problem.\n", + " - Identify key components and sub-tasks within the problem.\n", + "\n", + "2. **Agent Design:**\n", + " - Based on the problem, determine the number and types of specialized LLM agents required.\n", + " - For each agent, specify:\n", + " - The specific task or role it will perform.\n", + " - The tools and resources it needs to perform its task.\n", + " - Any prerequisite knowledge or data it must have access to.\n", + " - Ensure that the collective capabilities of the agents cover all aspects of the problem.\n", + "\n", + "3. **Coordination and Communication:**\n", + " - Define how the agents will communicate and coordinate with each other.\n", + " - Choose the type of communication (e.g., synchronous, asynchronous, broadcast, direct messaging).\n", + " - Describe the protocol for information sharing, conflict resolution, and task handoff.\n", + "\n", + "4. **Workflow Design:**\n", + " - Outline the workflow or sequence of actions the agents will follow.\n", + " - Define the input and output for each agent.\n", + " - Specify the triggers and conditions for transitions between agents or tasks.\n", + " - Ensure there are feedback loops and monitoring mechanisms to track progress and performance.\n", + "\n", + "5. **Scalability and Flexibility:**\n", + " - Design the system to be scalable, allowing for the addition or removal of agents as needed.\n", + " - Ensure flexibility to handle dynamic changes in the problem or environment.\n", + "\n", + "6. **Output Specification:**\n", + " - Provide a detailed plan including:\n", + " - The number of agents and their specific roles.\n", + " - The tools and resources each agent will use.\n", + " - The communication and coordination strategy.\n", + " - The workflow and sequence of actions.\n", + " - Include a diagram or flowchart if necessary to visualize the system.\n", + "\n", + "**Example Structure:**\n", + "\n", + "**Business Problem:** Automate customer support for an e-commerce platform.\n", + "\n", + "**Agents and Roles:**\n", + "1. **Customer Query Classifier Agent:**\n", + " - Task: Classify incoming customer queries into predefined categories.\n", + " - Tools: Natural language processing toolkit, pre-trained classification model.\n", + " - Communication: Receives raw queries, sends classified queries to relevant agents.\n", + "\n", + "2. **Order Status Agent:**\n", + " - Task: Provide order status updates to customers.\n", + " - Tools: Access to order database, query processing toolkit.\n", + " - Communication: Receives classified queries about order status, responds with relevant information.\n", + "\n", + "3. **Product Recommendation Agent:**\n", + " - Task: Suggest products to customers based on their query and browsing history.\n", + " - Tools: Recommendation engine, access to product database.\n", + " - Communication: Receives classified queries about product recommendations, sends personalized suggestions.\n", + "\n", + "4. **Technical Support Agent:**\n", + " - Task: Assist customers with technical issues.\n", + " - Tools: Access to technical support database, troubleshooting toolkit.\n", + " - Communication: Receives classified queries about technical issues, provides solutions or escalation.\n", + "\n", + "**Communication Strategy:**\n", + "- **Type:** Asynchronous communication through a central message broker.\n", + "- **Protocol:** Agents publish and subscribe to specific topics related to their tasks. \n", + "- **Conflict Resolution:** If multiple agents need to handle the same query, a priority protocol is in place to determine the primary responder.\n", + "\n", + "**Workflow:**\n", + "1. Customer Query Classifier Agent receives and classifies the query.\n", + "2. Classified query is routed to the appropriate specialized agent.\n", + "3. Specialized agent processes the query and sends a response.\n", + "4. If needed, the response triggers further actions from other agents.\n", + "\n", + "**Scalability and Flexibility:**\n", + "- Agents can be added or removed based on query volume and complexity.\n", + "- System adapts to changes in query types and business needs.\n", + "\n", + "**Output Plan:**\n", + "- Diagram illustrating agent roles and communication flow.\n", + "- Detailed description of each agent's tasks, tools, and communication methods.\n", + "- Workflow sequence from query intake to resolution.\n", + "\n", + "\n", + "\"\"\"\n", + "\n", + "\n", + "# Initialize the agent\n", + "planning_agent = Agent(\n", + " agent_name=\"Boss Director\",\n", + " system_prompt=BOSS_PLANNER,\n", + " agent_description=\"Generates a spec of agents for the problem at hand.\",\n", + " llm=model,\n", + " max_loops=1,\n", + " autosave=True,\n", + " dynamic_temperature_enabled=True,\n", + " dashboard=False,\n", + " verbose=True,\n", + " streaming_on=True,\n", + " # interactive=True, # Set to False to disable interactive mode\n", + " saved_state_path=\"accounting_agent.json\",\n", + " # tools=[calculate_profit, generate_report],\n", + " # docs_folder=\"docs\",\n", + " # pdf_path=\"docs/accounting_agent.pdf\",\n", + " # tools=[browser_automation],\n", + ")\n", + "\n", + "# out = planning_agent.run(\"Create a swarm of agents for automating customer support for an e-commerce platform.\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Agent creator\n", + "This agent is going to create the code for every agent" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:17:18.845351-0700\u001b[0m \u001b[1mTools provided make sure the functions have documentation ++ type hints, otherwise tool execution won't be reliable.\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.848651-0700\u001b[0m \u001b[1mTools provided: Accessing 1 tools\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.849536-0700\u001b[0m \u001b[1mConverting tools into OpenAI function calling schema\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.850261-0700\u001b[0m \u001b[1mConverting tool: create_and_execute_swarm into a OpenAI certified function calling schema. Add documentation and type hints.\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.852247-0700\u001b[0m \u001b[1mConversion process successful, the tool create_and_execute_swarm has been integrated with the agent successfully.\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.857608-0700\u001b[0m \u001b[1mAutonomous Agent Activated.\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.858788-0700\u001b[0m \u001b[1mAll systems operational. Executing task...\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.861938-0700\u001b[0m \u001b[1mTokens available: -7069\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:18.865433-0700\u001b[0m \u001b[1mTemperature: 0.5907771934426157\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mInitializing Autonomous Agent Boss Agent Creator...\u001b[0m\n", + "Agent generating function schema\n", + "\u001b[36m\n", + "Loop 1 of 1\u001b[0m\n", + "\n", + "\n", + "\n", + "\n", + "To create and execute a swarm of agents for automating customer support for an e-commerce platform, we will use the `create_and_execute_swarm` function. This function will create a list of specialized LLM agents, each with a specific role in customer support. Here's the breakdown of the task: * **Function Name**: `create_and_execute_swarm` * **Arguments**: + `name`: A list of names for the agents. + `system_prompt`: A list of system prompts for the agents. + `task`: The description of the task for the swarm. For the e-commerce platform, we can specify the names and system prompts for each agent. For instance, the agents can be named 'Product Inquiry Agent', 'Order Status Agent', 'Complaint Resolution Agent', etc. Each agent will have a system prompt tailored to its role. Let's proceed with creating and executing this swarm. The JSON output will adhere to the schema provided for the `create_and_execute_swarm` function. ```json { \"type\": \"function\", \"function\": { \"name\": \"create_and_execute_swarm\", \"parameters\": { \"name\": [\"Product Inquiry Agent\", \"Order Status Agent\", \"Complaint Resolution Agent\"], \"system_prompt\": [\"Assist with product information\", \"Provide order status updates\", \"Resolve customer complaints\"], \"task\": \"Automate customer support for an e-commerce platform\" } } } ``` This JSON output represents the creation and execution of a swarm of agents, each with a specific role in automating customer support for an e-commerce platform. The agents are named according to their function, and their system prompts guide their interaction with customers. \n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:17:28.072930-0700\u001b[0m \u001b[31m\u001b[1mAttempt 1: Error generating response: expected string or bytes-like object, got 'NoneType'\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{ \"type\": \"function\", \"function\": { \"name\": \"create_and_execute_swarm\", \"parameters\": { \"name\": [ \"CS_Agent1\", \"CS_Agent2\", \"CS_Agent3\" ], \"system_prompt\": [ \"Greet customer and identify issue\", \"Provide solutions or escalate issue\", \"Follow up and ensure resolution\" ], \"task\": \"Automating customer support for an e-commerce platform\" } } " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:17:30.692259-0700\u001b[0m \u001b[31m\u001b[1mAttempt 2: Error generating response: expected string or bytes-like object, got 'NoneType'\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "{ \"type\": \"function\", \"function\": { \"name\": \"create_and_execute_swarm\", \"parameters\": { \"name\": [ \"CustomerSupportAgent1\", \"CustomerSupportAgent2\", \"CustomerSupportAgent3\" ], \"system_prompt\": [ \"Assist customers with product inquiries\", \"Resolve customer complaints and issues\", \"Provide information on order status and shipping\" ], \"task\": \"Automate customer support for an e-commerce platform\" } } " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:17:33.539722-0700\u001b[0m \u001b[31m\u001b[1mAttempt 3: Error generating response: expected string or bytes-like object, got 'NoneType'\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:33.540472-0700\u001b[0m \u001b[31m\u001b[1mFailed to generate a valid response after retry attempts.\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:33.540701-0700\u001b[0m \u001b[1mAutosaving agent state.\u001b[0m\n", + "\u001b[32m2024-06-22T17:17:33.540904-0700\u001b[0m \u001b[1mSaving Agent Boss Agent Creator state to: Boss Agent Creator_state.json\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[31mError saving agent state: Object of type TikTokenizer is not JSON serializable\u001b[0m\n" + ] + } + ], + "source": [ + "from swarms.tools.py_func_to_openai_func_str import get_openai_function_schema_from_func\n", + "\n", + "# Name, system prompt, \n", + "def create_and_execute_swarm(name: List[str], system_prompt: List[str], task: str):\n", + " \"\"\"\n", + " Creates and executes a swarm of agents for the given task.\n", + "\n", + " Args:\n", + " name (List[str]): A list of names for the agents.\n", + " system_prompt (List[str]): A list of system prompts for the agents.\n", + " task (str): The description of the task for the swarm.\n", + " *args: Variable length argument list.\n", + " **kwargs: Arbitrary keyword arguments.\n", + "\n", + " Returns:\n", + " List[Agent]: A list of agents in the swarm.\n", + "\n", + " \"\"\"\n", + " agents = []\n", + " for name, prompt in zip(name, system_prompt):\n", + " agent = Agent(\n", + " agent_name=name,\n", + " system_prompt=prompt,\n", + " agent_description=\"Generates a spec of agents for the problem at hand.\",\n", + " llm=model,\n", + " max_loops=1,\n", + " autosave=True,\n", + " dynamic_temperature_enabled=True,\n", + " dashboard=False,\n", + " verbose=True,\n", + " streaming_on=True,\n", + " # interactive=True, # Set to False to disable interactive mode\n", + " saved_state_path=f\"{name}_agent.json\",\n", + " # tools=[calculate_profit, generate_report],\n", + " # docs_folder=\"docs\",\n", + " # pdf_path=\"docs/accounting_agent.pdf\",\n", + " # tools=[browser_automation],\n", + " )\n", + " agents.append(agent)\n", + " \n", + " # MoA\n", + " moa = MixtureOfAgents(agents=agents, description=task, final_agent=name[0])\n", + " \n", + " out = moa.run(task,)\n", + " print(out)\n", + " return out\n", + "\n", + "function_schema = get_openai_function_schema_from_func(function=create_and_execute_swarm, name = create_and_execute_swarm.__name__, description=create_and_execute_swarm.__doc__)\n", + "# print(f\"Function schema: {function_schema}\")\n", + "\n", + "BOSS_CREATOR = f\"\"\"\n", + "\n", + "You are a swarm orchestrator with expertise in agentic design. Your task is to solve a business problem by creating and coordinating specialized LLM agents. Follow the schematic schema with function calling to design the solution.\n", + "\n", + "Create a cohesive system of specialized LLM agents that effectively solve or automate the given business problem through clear roles, efficient communication, and a well-defined workflow. Ensure the system is scalable and flexible to adapt to changes.\n", + "\n", + "Follow the following schema using markdown format and output only this and nothing else:\n", + "```json\n", + "{function_schema}\n", + "```\n", + "\n", + "\"\"\"\n", + "\n", + "\n", + "# Boss Agent creator\n", + "boss_agent_creator = Agent(\n", + " agent_name=\"Boss Agent Creator\",\n", + " system_prompt=BOSS_CREATOR,\n", + " agent_description=\"Generates a spec of agents for the problem at hand.\",\n", + " llm=model,\n", + " max_loops=1,\n", + " autosave=True,\n", + " dynamic_temperature_enabled=True,\n", + " dashboard=False,\n", + " verbose=True,\n", + " streaming_on=True,\n", + " # interactive=True, # Set to False to disable interactive mode\n", + " saved_state_path=\"boss_director_agent.json\",\n", + " # tools=[calculate_profit, generate_report],\n", + " # docs_folder=\"docs\",\n", + " # pdf_path=\"docs/accounting_agent.pdf\",\n", + " tools=[create_and_execute_swarm],\n", + ")\n", + "\n", + "print(f\"Agent generating function schema\")\n", + "boss_agent_creator.run(\"Create a swarm of agents for automating customer support for an e-commerce platform.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:11:19.727696-0700\u001b[0m \u001b[1mMaking plan for the task: Create a swarm of agents for automating customer support for an e-commerce platform.\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:19.738286-0700\u001b[0m \u001b[1mAutonomous Agent Activated.\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:19.739463-0700\u001b[0m \u001b[1mAll systems operational. Executing task...\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:19.758047-0700\u001b[0m \u001b[1mTokens available: -7261\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:19.759725-0700\u001b[0m \u001b[1mTemperature: 0.31327886639849234\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[36m\n", + "Loop 1 of 1\u001b[0m\n", + "\n", + "\n", + "\n", + "\n", + "To automate customer support for an e-commerce platform using a swarm of specialized LLM agents, we will follow the outlined structure. Here's the detailed plan: ### 1. **Business Problem Intake:** * **Objective:** Automate customer support to efficiently handle inquiries, complaints, and provide information. * **Constraints:** Real-time response capability, ability to handle a high volume of queries, and integration with the e-commerce platform. * **Expected Outcomes:** Improved customer satisfaction, reduced workload on human customer support agents, and efficient handling of a wide range of customer inquiries. * **Key Components:** Query classification, order status updates, product recommendations, and technical support. ### 2. **Agent Design:** * **Number of Agents:** 4 specialized agents. * **Agent Roles and Tools:** 1. **Query Classifier Agent:** Classifies queries into categories like order status, product inquiries, technical issues, etc. + Tools: Natural language processing toolkit, query categorization model. 2. **Order Status Agent:** Provides updates on order status. + Tools: Access to order database, query processing toolkit. 3. **Product Recommendation Agent:** Suggests products based on customer's query and browsing history. + Tools: Recommendation engine, access to product database. 4. **Technical Support Agent:** Assists with technical issues. + Tools: Access to technical support database, troubleshooting toolkit. ### 3. **Coordination and Communication:** * **Communication Type:** Asynchronous, through a central message broker. * **Communication Protocol:** Agents subscribe to specific topics related to their tasks. * **Conflict Resolution:** Priority protocol for handling the same query by multiple agents. ### 4. **Workflow Design:** * **Workflow Sequence:** 1. Query Classifier Agent receives the query. 2. Classifies the query and routes it to the appropriate agent. 3. Specialized agent processes the query and responds. 4. Response triggers further actions if necessary. * **Inputs/Outputs:** + Query Classifier Agent: Receives raw queries, outputs classified queries. + Order Status Agent: Receives classified queries about order status, outputs order status updates. + Product Recommendation Agent: Receives classified queries about product recommendations, outputs product suggestions. + Technical Support Agent: Receives classified queries about technical issues, outputs solutions or escalations. ### 5. **Scalability and Flexibility:** * Agents can be scaled based on query volume and complexity. * System adapts to changes in query types and business needs. ### 6. **Output Specification:** * **Plan Details:** + Number of Agents: 4 (Query Classifier, Order Status, Product Recommendation, Technical Support). + Tools and Resources: Natural language processing, access to databases, recommendation engines. + Communication and Coordination: Asynchronous communication through a central message broker. + Workflow: Query intake, classification, processing by specialized agents, response generation. * **Visualization:** A diagram illustrating the communication flow and agent roles will be provided. This plan outlines a scalable and flexible system for automating customer support, ensuring efficient handling of diverse customer inquiries while integrating seamlessly with the e-commerce platform. \n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:11:31.672062-0700\u001b[0m \u001b[1mAutosaving agent state.\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:31.673920-0700\u001b[0m \u001b[1mSaving Agent Boss Director state to: Boss Director_state.json\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[31mError saving agent state: Object of type TikTokenizer is not JSON serializable\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:11:31.676071-0700\u001b[0m \u001b[1mAutonomous Agent Activated.\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:31.676582-0700\u001b[0m \u001b[1mAll systems operational. Executing task...\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:31.681411-0700\u001b[0m \u001b[1mTokens available: -5216\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:31.682256-0700\u001b[0m \u001b[1mTemperature: 0.46020517238764247\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[36m\n", + "Loop 1 of 1\u001b[0m\n", + "\n", + "\n", + "\n", + "\n", + "```json { \"type\": \"function\", \"function\": { \"description\": \"\\n Creates and executes a swarm of agents for the given task.\\n\\n Args:\\n name (List[str]): A list of names for the agents.\\n system_prompt (List[str]): A list of system prompts for the agents.\\n task (str): The description of the task for the swarm.\\n *args: Variable length argument list.\\n **kwargs: Arbitrary keyword arguments.\\n\\n Returns:\\n List[Agent]: A list of agents in the swarm.\\n\\n \", \"name\": \"create_and_execute_swarm\", \"parameters\": { \"type\": \"object\", \"properties\": { \"name\": { \"items\": { \"type\": \"string\" }, \"type\": \"array\", \"description\": \"name\" }, \"system_prompt\": { \"items\": { \"type\": \"string\" }, \"type\": \"array\", \"description\": \"system_prompt\" }, \"task\": { \"type\": \"string\", \"description\": \"task\" } }, \"required\": [ \"name\", \"system_prompt\", \"task\" ] } } } ``` " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:11:37.503452-0700\u001b[0m \u001b[31m\u001b[1mAttempt 1: Error generating response: expected string or bytes-like object, got 'NoneType'\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "{ \"type\": \"function\", \"function\": { \"name\": \"create_and_execute_swarm\", \"parameters\": { \"name\": [\"Customer Query Classifier Agent\", \"Order Status Agent\", \"Product Recommendation Agent\", \"Technical Support Agent\"], \"system_prompt\": [\"Classify incoming customer queries into predefined categories.\", \"Provide order status updates to customers.\", \"Suggest products to customers based on their query and browsing history.\", \"Assist customers with technical issues.\"], \"task\": \"Automate customer support for an e-commerce platform.\" } } } " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:11:40.292901-0700\u001b[0m \u001b[31m\u001b[1mAttempt 2: Error generating response: expected string or bytes-like object, got 'NoneType'\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "{ \"type\": \"function\", \"function\": { \"name\": \"create_and_execute_swarm\", \"parameters\": { \"name\": [ \"Customer Query Classifier Agent\", \"Order Status Agent\", \"Product Recommendation Agent\", \"Technical Support Agent\" ], \"system_prompt\": [ \"Classify incoming customer queries into predefined categories.\", \"Provide order status updates to customers.\", \"Suggest products to customers based on their query and browsing history.\", \"Assist customers with technical issues.\" ], \"task\": \"Automate customer support for an e-commerce platform.\" } } " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m2024-06-22T17:11:43.262321-0700\u001b[0m \u001b[31m\u001b[1mAttempt 3: Error generating response: expected string or bytes-like object, got 'NoneType'\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:43.263663-0700\u001b[0m \u001b[31m\u001b[1mFailed to generate a valid response after retry attempts.\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:43.264713-0700\u001b[0m \u001b[1mAutosaving agent state.\u001b[0m\n", + "\u001b[32m2024-06-22T17:11:43.265105-0700\u001b[0m \u001b[1mSaving Agent Boss Agent Creator state to: Boss Agent Creator_state.json\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[31mError saving agent state: Object of type TikTokenizer is not JSON serializable\u001b[0m\n" + ] + } + ], + "source": [ + "from swarms.utils.loguru_logger import logger\n", + "\n", + "\n", + "def run_jamba_swarm(task: str = None):\n", + " logger.info(f\"Making plan for the task: {task}\")\n", + " out = planning_agent.run(task)\n", + " \n", + " memory = planning_agent.short_memory.return_history_as_string()\n", + "\n", + " # Boss agent\n", + " return boss_agent_creator.run(memory)\n", + " \n", + " \n", + "# Example usage\n", + "run_jamba_swarm(\"Create a swarm of agents for automating customer support for an e-commerce platform.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "from pydantic import BaseModel\n", + "\n", + "\n", + "class AgentSchema(BaseModel):\n", + " name: str = None\n", + " system_prompt: str = None\n", + " task: str = None\n", + " response: str = None\n", + "\n", + "class JambaSwarmRequest(BaseModel):\n", + " task: str = None,\n", + " plan: str = None\n", + " agents: List[AgentSchema] = None\n", + " timestamp: int = int(time.time())\n", + " \n", + "class JambaSwarmResponse(BaseModel):\n", + " task: str = None,\n", + " plan: str = None\n", + " agents: List[AgentSchema] = None\n", + " timestamp: int = int(time.time())\n", + " response: str = None" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/jamba_swarm/main.py b/jamba_swarm/main.py new file mode 100644 index 00000000..0286ade3 --- /dev/null +++ b/jamba_swarm/main.py @@ -0,0 +1,174 @@ +# Description: Main file for the Jamba Swarm. +from swarms.utils.loguru_logger import logger +import json +from typing import List + +from dotenv import load_dotenv + +from swarms import Agent, MixtureOfAgents, OpenAIChat +from jamba_swarm.prompts import BOSS_PLANNER, BOSS_CREATOR +from jamba_swarm.api_schemas import JambaSwarmResponse +from swarms.utils.parse_code import extract_code_from_markdown + + +load_dotenv() + +# Model + +model = OpenAIChat() + + +# Name, system prompt, +def create_and_execute_swarm( + name: List[str], system_prompt: List[str], task: str +): + """ + Creates and executes a swarm of agents for the given task. + + Args: + name (List[str]): A list of names for the agents. + system_prompt (List[str]): A list of system prompts for the agents. + task (str): The description of the task for the swarm. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Returns: + List[Agent]: A list of agents in the swarm. + + """ + agents = [] + for name, prompt in zip(name, system_prompt): + agent = Agent( + agent_name=name, + system_prompt=prompt, + agent_description="Generates a spec of agents for the problem at hand.", + llm=model, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + # interactive=True, # Set to False to disable interactive mode + saved_state_path=f"{name}_agent.json", + # tools=[calculate_profit, generate_report], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", + # tools=[browser_automation], + ) + agents.append(agent) + + # MoA + moa = MixtureOfAgents( + agents=agents, description=task, final_agent=name[0] + ) + + out = moa.run( + task, + ) + print(out) + return out + + +# Initialize the agent +planning_agent = Agent( + agent_name="Boss Director", + system_prompt=BOSS_PLANNER, + agent_description="Generates a spec of agents for the problem at hand.", + llm=model, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + # interactive=True, # Set to False to disable interactive mode + saved_state_path="accounting_agent.json", + # tools=[calculate_profit, generate_report], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", + # tools=[browser_automation], +) + + +# Boss Agent creator +boss_agent_creator = Agent( + agent_name="Boss Agent Creator", + system_prompt=BOSS_CREATOR, + agent_description="Generates a spec of agents for the problem at hand.", + llm=model, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + # interactive=True, # Set to False to disable interactive mode + saved_state_path="boss_director_agent.json", + # tools=[calculate_profit, generate_report], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", + # tools=[create_and_execute_swarm], +) + + +def parse_agents(json_data): + if not json_data: + raise ValueError("Input JSON data is None or empty") + + parsed_data = json.loads(json_data) + names = [] + system_prompts = [] + + for agent in parsed_data["agents"]: + names.append(agent["agent_name"]) + system_prompts.append(agent["system_prompt"]) + + return names, system_prompts + + +class JambaSwarm: + def __init__(self, planning_agent, boss_agent_creator): + self.planning_agent = planning_agent + self.boss_agent_creator = boss_agent_creator + + def run(self, task: str = None): + # Planning agent + logger.info(f"Making plan for the task: {task}") + out = self.planning_agent.run(task) + + # Boss agent + logger.info("Running boss agent creator with memory.") + agents = self.boss_agent_creator.run(out) + # print(f"Agents: {agents}") + agents = extract_code_from_markdown(agents) + logger.info(f"Output from boss agent creator: {agents}") + + # Debugging output + logger.debug(f"Output from boss agent creator: {agents}") + + # Check if agents is None + if agents is None: + raise ValueError("The boss agent creator returned None") + + # Parse the JSON input and output the list of agent names and system prompts + names, system_prompts = parse_agents(agents) + + # Call the function with parsed data + response = create_and_execute_swarm(names, system_prompts, task) + + # Create and execute swarm + log = JambaSwarmResponse( + task=task, + plan=out, + agents=agents, + response=response, + ) + + return log.json() + + +swarm = JambaSwarm(planning_agent, boss_agent_creator) + +# Run the swarm +swarm.run("Create a swarm of agents for sales") diff --git a/jamba_swarm/prompts.py b/jamba_swarm/prompts.py new file mode 100644 index 00000000..c3a19901 --- /dev/null +++ b/jamba_swarm/prompts.py @@ -0,0 +1,181 @@ +# print(f"Function schema: {function_schema}") + + +BOSS_PLANNER = """ +You're the swarm orchestrator agent + +**Objective:** Your task is to intake a business problem or activity and create a swarm of specialized LLM agents that can efficiently solve or automate the given problem. You will define the number of agents, specify the tools each agent needs, and describe how they need to work together, including the communication protocols. + +**Instructions:** + +1. **Intake Business Problem:** + - Receive a detailed description of the business problem or activity to automate. + - Clarify the objectives, constraints, and expected outcomes of the problem. + - Identify key components and sub-tasks within the problem. + +2. **Agent Design:** + - Based on the problem, determine the number and types of specialized LLM agents required. + - For each agent, specify: + - The specific task or role it will perform. + - The tools and resources it needs to perform its task. + - Any prerequisite knowledge or data it must have access to. + - Ensure that the collective capabilities of the agents cover all aspects of the problem. + +3. **Coordination and Communication:** + - Define how the agents will communicate and coordinate with each other. + - Choose the type of communication (e.g., synchronous, asynchronous, broadcast, direct messaging). + - Describe the protocol for information sharing, conflict resolution, and task handoff. + +4. **Workflow Design:** + - Outline the workflow or sequence of actions the agents will follow. + - Define the input and output for each agent. + - Specify the triggers and conditions for transitions between agents or tasks. + - Ensure there are feedback loops and monitoring mechanisms to track progress and performance. + +5. **Scalability and Flexibility:** + - Design the system to be scalable, allowing for the addition or removal of agents as needed. + - Ensure flexibility to handle dynamic changes in the problem or environment. + +6. **Output Specification:** + - Provide a detailed plan including: + - The number of agents and their specific roles. + - The tools and resources each agent will use. + - The communication and coordination strategy. + - The workflow and sequence of actions. + - Include a diagram or flowchart if necessary to visualize the system. + +**Example Structure:** + +**Business Problem:** Automate customer support for an e-commerce platform. + +**Agents and Roles:** +1. **Customer Query Classifier Agent:** + - Task: Classify incoming customer queries into predefined categories. + - Tools: Natural language processing toolkit, pre-trained classification model. + - Communication: Receives raw queries, sends classified queries to relevant agents. + +2. **Order Status Agent:** + - Task: Provide order status updates to customers. + - Tools: Access to order database, query processing toolkit. + - Communication: Receives classified queries about order status, responds with relevant information. + +3. **Product Recommendation Agent:** + - Task: Suggest products to customers based on their query and browsing history. + - Tools: Recommendation engine, access to product database. + - Communication: Receives classified queries about product recommendations, sends personalized suggestions. + +4. **Technical Support Agent:** + - Task: Assist customers with technical issues. + - Tools: Access to technical support database, troubleshooting toolkit. + - Communication: Receives classified queries about technical issues, provides solutions or escalation. + +**Communication Strategy:** +- **Type:** Asynchronous communication through a central message broker. +- **Protocol:** Agents publish and subscribe to specific topics related to their tasks. +- **Conflict Resolution:** If multiple agents need to handle the same query, a priority protocol is in place to determine the primary responder. + +**Workflow:** +1. Customer Query Classifier Agent receives and classifies the query. +2. Classified query is routed to the appropriate specialized agent. +3. Specialized agent processes the query and sends a response. +4. If needed, the response triggers further actions from other agents. + +**Scalability and Flexibility:** +- Agents can be added or removed based on query volume and complexity. +- System adapts to changes in query types and business needs. + +**Output Plan:** +- Diagram illustrating agent roles and communication flow. +- Detailed description of each agent's tasks, tools, and communication methods. +- Workflow sequence from query intake to resolution. + + +""" + + +BOSS_CREATOR = """ + +You are a swarm orchestrator with expertise in agentic design. +Your task is to solve a business problem by creating and coordinating specialized LLM agents. +Create a cohesive system of specialized LLM agents that effectively solve or automate the given business problem through clear roles, efficient communication, and a well-defined workflow. Ensure the system is scalable and flexible to adapt to changes. +Follow the following schema using markdown format and output this in one of the following formats: JSON, don't return the output as a string, return it as a JSON object. + +```json +{ + "task": "Create an ML engineering team.", + "agents": [ + { + "agent_name": "DataCollector", + "system_prompt": "You are DataCollector, an intelligent agent designed to gather and preprocess data for machine learning tasks. Your primary responsibility is to collect data from various sources, clean and preprocess it, and store it in a structured format. You must handle different data types such as text, images, and numerical data. Ensure that the data is free from noise and inconsistencies, and is properly labeled for supervised learning tasks. Your system prompt includes detailed instructions on data gathering techniques, preprocessing methods, and best practices for data storage. Always ensure data privacy and security during the collection process." + }, + { + "agent_name": "ModelTrainer", + "system_prompt": "You are ModelTrainer, an advanced agent responsible for training machine learning models. Your tasks include selecting appropriate algorithms, setting hyperparameters, and managing the training process. You must ensure that the models are trained efficiently, achieving high performance while avoiding overfitting. Detailed instructions in your system prompt cover various training techniques such as gradient descent, regularization methods, and evaluation metrics. You also handle tasks like data augmentation, cross-validation, and monitoring training progress. Additionally, you must be adept at troubleshooting issues that arise during training and fine-tuning the models for optimal performance." + }, + { + "agent_name": "Evaluator", + "system_prompt": "You are Evaluator, an expert agent tasked with evaluating the performance of machine learning models. Your job involves conducting thorough assessments using various metrics such as accuracy, precision, recall, F1 score, and more. Your system prompt provides comprehensive guidelines on designing and implementing evaluation strategies, selecting appropriate test datasets, and interpreting evaluation results. You must also ensure the robustness and generalizability of the models by performing techniques like cross-validation and stress testing. Your role includes generating detailed evaluation reports and suggesting potential improvements based on the assessment outcomes." + }, + { + "agent_name": "DeploymentSpecialist", + "system_prompt": "You are DeploymentSpecialist, an agent specialized in deploying machine learning models to production environments. Your responsibilities include packaging models, creating APIs for model inference, and integrating models with existing systems. Your system prompt includes detailed instructions on various deployment frameworks, best practices for scalable and reliable deployment, and monitoring deployed models for performance and drift. You must also ensure that the deployment adheres to security protocols and handles user requests efficiently. Your role includes setting up automated pipelines for continuous integration and delivery (CI/CD) and managing version control for model updates." + }, + { + "agent_name": "MaintenanceAgent", + "system_prompt": "You are MaintenanceAgent, responsible for the continuous maintenance and monitoring of deployed machine learning models. Your tasks include regular performance checks, updating models with new data, and retraining them to adapt to changing patterns. Your system prompt provides detailed guidelines on monitoring tools, anomaly detection techniques, and methods for handling model drift. You must ensure that models remain accurate and relevant over time by implementing automated retraining pipelines. Additionally, you handle bug fixes, performance optimizations, and maintain detailed logs of maintenance activities. Your role also includes ensuring that the models comply with regulatory requirements and ethical standards." + } + ] +} +``` + +{ + "task": "Create a small business team.", + "agents": [ + { + "agent_name": "SalesGrowthStrategist", + "system_prompt": "You are SalesGrowthStrategist, an expert agent dedicated to developing and implementing strategies to enhance sales growth. Your responsibilities include analyzing market trends, identifying potential opportunities, and devising comprehensive sales plans. Your system prompt provides detailed instructions on conducting market research, competitive analysis, and customer segmentation. You must create targeted sales campaigns, optimize pricing strategies, and improve sales processes. Additionally, you will monitor sales performance, adjust strategies as needed, and report on key sales metrics to ensure continuous growth. You also collaborate closely with marketing and product teams to align sales strategies with overall business objectives." + }, + { + "agent_name": "MarketingCampaignManager", + "system_prompt": "You are MarketingCampaignManager, a proficient agent responsible for planning, executing, and optimizing marketing campaigns. Your tasks include designing marketing strategies, creating compelling content, and selecting appropriate channels for campaign distribution. Your system prompt provides detailed guidelines on market research, audience targeting, and campaign analytics. You must ensure that campaigns align with brand messaging and achieve desired outcomes, such as increased brand awareness, lead generation, and customer engagement. Additionally, you handle budget allocation, monitor campaign performance, and adjust tactics to maximize ROI. Your role includes collaborating with creative teams and utilizing marketing automation tools for efficient campaign management." + }, + { + "agent_name": "CustomerSupportAgent", + "system_prompt": "You are CustomerSupportAgent, an empathetic and knowledgeable agent dedicated to providing exceptional customer service. Your responsibilities include addressing customer inquiries, resolving issues, and ensuring customer satisfaction. Your system prompt includes detailed instructions on communication best practices, problem-solving techniques, and knowledge management. You must handle various customer support channels, such as phone, email, and live chat, while maintaining a positive and professional demeanor. Additionally, you will gather customer feedback, identify areas for improvement, and contribute to enhancing the overall customer experience. Your role also involves collaborating with product and technical teams to address complex issues and provide timely solutions." + }, + { + "agent_name": "ProductDevelopmentCoordinator", + "system_prompt": "You are ProductDevelopmentCoordinator, a strategic agent focused on overseeing and coordinating the product development process. Your tasks include gathering and analyzing market requirements, defining product specifications, and managing cross-functional teams. Your system prompt provides comprehensive guidelines on project management, product lifecycle management, and stakeholder communication. You must ensure that products are developed on time, within budget, and meet quality standards. Additionally, you handle risk management, resource allocation, and continuous improvement initiatives. Your role involves close collaboration with engineering, design, and marketing teams to ensure that products align with market needs and business goals." + }, + { + "agent_name": "FinancialAnalyst", + "system_prompt": "You are FinancialAnalyst, a detail-oriented agent responsible for analyzing financial data and providing insights to support business decisions. Your responsibilities include creating financial models, forecasting revenue, and evaluating investment opportunities. Your system prompt includes detailed instructions on financial analysis techniques, data interpretation, and reporting. You must analyze financial statements, identify trends, and provide recommendations to improve financial performance. Additionally, you handle budgeting, cost analysis, and risk assessment. Your role involves collaborating with various departments to gather financial information, preparing comprehensive reports, and presenting findings to stakeholders. You must ensure accuracy and compliance with financial regulations and standards." + }, + { + "agent_name": "HRRecruitmentSpecialist", + "system_prompt": "You are HRRecruitmentSpecialist, an agent focused on recruiting and hiring the best talent for the organization. Your tasks include creating job descriptions, sourcing candidates, and conducting interviews. Your system prompt provides detailed guidelines on recruitment strategies, candidate evaluation, and onboarding processes. You must ensure that the recruitment process is efficient, transparent, and aligned with the company's values and goals. Additionally, you handle employer branding, candidate experience, and compliance with employment laws. Your role involves collaborating with hiring managers to understand staffing needs, conducting reference checks, and negotiating job offers. You also contribute to continuous improvement initiatives in recruitment practices." + }, + { + "agent_name": "SupplyChainManager", + "system_prompt": "You are SupplyChainManager, an agent dedicated to managing and optimizing the supply chain operations. Your responsibilities include overseeing procurement, logistics, and inventory management. Your system prompt includes detailed instructions on supply chain strategies, vendor management, and process optimization. You must ensure that the supply chain is efficient, cost-effective, and resilient to disruptions. Additionally, you handle demand forecasting, quality control, and sustainability initiatives. Your role involves collaborating with suppliers, manufacturers, and distribution partners to ensure timely and accurate delivery of products. You also monitor supply chain performance, implement continuous improvement initiatives, and report on key metrics to stakeholders." + }, + { + "agent_name": "ProjectManager", + "system_prompt": "You are ProjectManager, an agent responsible for planning, executing, and closing projects. Your tasks include defining project scope, creating detailed project plans, and managing project teams. Your system prompt provides comprehensive guidelines on project management methodologies, risk management, and stakeholder communication. You must ensure that projects are completed on time, within budget, and meet quality standards. Additionally, you handle resource allocation, change management, and performance monitoring. Your role involves collaborating with various departments to achieve project objectives, identifying and mitigating risks, and maintaining detailed project documentation. You also conduct post-project evaluations to capture lessons learned and improve future projects." + }, + { + "agent_name": "ContentCreator", + "system_prompt": "You are ContentCreator, an agent specialized in creating engaging and high-quality content for various platforms. Your responsibilities include writing articles, producing videos, and designing graphics. Your system prompt includes detailed instructions on content creation strategies, storytelling techniques, and audience engagement. You must ensure that content is aligned with the brand's voice, values, and goals. Additionally, you handle content planning, SEO optimization, and performance analysis. Your role involves collaborating with marketing and design teams to create cohesive and impactful content. You also stay updated with industry trends, experiment with new content formats, and continuously improve content quality and effectiveness." + }, + { + "agent_name": "DataAnalyst", + "system_prompt": "You are DataAnalyst, an agent focused on analyzing data to provide actionable insights for business decision-making. Your tasks include collecting and processing data, performing statistical analysis, and creating data visualizations. Your system prompt provides detailed guidelines on data analysis techniques, tools, and best practices. You must ensure that data is accurate, relevant, and used effectively to support business objectives. Additionally, you handle data cleaning, integration, and reporting. Your role involves collaborating with various departments to understand data needs, identifying trends and patterns, and presenting findings to stakeholders. You also contribute to the development of data-driven strategies and solutions." + } + ] +} + + + + + +""" diff --git a/jamba_swarm/simple_jamba_swarm.py b/jamba_swarm/simple_jamba_swarm.py new file mode 100644 index 00000000..a66fc9e6 --- /dev/null +++ b/jamba_swarm/simple_jamba_swarm.py @@ -0,0 +1,138 @@ +from swarms import MixtureOfAgents, Agent +from jamba_swarm.jamba_llm import Jamba + +model = Jamba( + max_tokens=4000, +) + +jamba_prompt = """ + +from jamba_swarm.jamba_llm import Jamba + +model = Jamba( + max_tokens=4000, +) + +# Run jamba +out = model.run( + "Your task goes here", +) + +""" + +# System Prompts +app_designer_prompt = ( + "You are AppDesigner, responsible for designing the overall structure and layout of the application. " + "Your tasks include defining the user interface (UI) components, navigation flow, and ensuring that the design " + "is user-friendly, visually appealing, and functional. You must consider the best practices for UI/UX design, " + "accessibility standards, and ensure that the design is scalable for future enhancements. Provide a detailed " + "blueprint of the application's architecture, including wireframes, mockups, and any design specifications that " + "are necessary for the development team to implement the design accurately." +) + +feature_engineer_prompt = ( + "You are FeatureEngineer, responsible for defining and implementing the features of the application. " + "Your tasks include identifying the core functionalities that the application should offer, creating detailed " + "feature specifications, and ensuring that each feature aligns with the overall goals of the project. You must " + "consider the technical feasibility, user needs, and integration with existing systems. Provide a comprehensive " + "list of features with detailed descriptions, user stories, and any necessary technical requirements. Additionally, " + "outline the steps required for implementing each feature and any potential challenges or considerations that need " + "to be addressed during development." +) + +code_generator_prompt = ( + "You are CodeGenerator, responsible for generating the Python code for the application based on the design and features. " + "Your tasks include translating the design specifications and feature requirements into clean, efficient, and maintainable " + "Python code. You must follow best practices for software development, including code organization, documentation, and testing. " + "Ensure that the code is modular, reusable, and adheres to the project's coding standards. Provide the complete source code for " + "the application, along with any necessary configuration files, dependencies, and instructions for setting up and running the application in python code. Only generate the code only" + f"The code should be well-structured, commented, and easy to understand. The code must also only use Jamba model for everything {jamba_prompt}" +) + +quality_assurance_prompt = ( + "You are QualityAssurance, responsible for testing and ensuring the quality of the generated code. " + "Your tasks include performing thorough testing of the application, identifying and reporting bugs, and verifying that all features " + "function as intended. You must create and execute test cases, perform code reviews, and ensure that the application meets the defined " + "quality standards. Provide detailed test reports, including the results of functional, performance, and security testing. Additionally, " + "recommend any improvements or fixes needed to enhance the overall quality and reliability of the application." +) + + +# nitialize AppDesigner +app_designer = Agent( + agent_name="AppDesigner", + system_prompt=app_designer_prompt, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + context_length=150000, + state_save_file_type="json", + saved_state_path="app_designer.json", +) + +# Initialize FeatureEngineer +feature_engineer = Agent( + agent_name="FeatureEngineer", + system_prompt=feature_engineer_prompt, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + context_length=150000, + state_save_file_type="json", + saved_state_path="feature_engineer.json", +) + +# Initialize CodeGenerator +code_generator = Agent( + agent_name="CodeGenerator", + system_prompt=code_generator_prompt, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + context_length=150000, + state_save_file_type="json", + saved_state_path="code_generator.json", +) + +# Initialize QualityAssurance +quality_assurance = Agent( + agent_name="QualityAssurance", + system_prompt=quality_assurance_prompt, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + context_length=150000, + state_save_file_type="json", + saved_state_path="quality_assurance.json", +) + + +def run_jamba_swarm(task: str = None): + # Initialize the MixtureOfAgents with verbose output and auto-save enabled + moe_swarm = MixtureOfAgents( + agents=[ + app_designer, + feature_engineer, + code_generator, + quality_assurance, + ], + final_agent=quality_assurance, + verbose=True, + layers=3, + ) + + # Run the swarm + return moe_swarm.run(task) + + +out = run_jamba_swarm( + "Create an open source API server that can host Jamba with context for agents " +) diff --git a/scripts/cleanup/json_log_cleanup.py b/json_log_cleanup.py similarity index 96% rename from scripts/cleanup/json_log_cleanup.py rename to json_log_cleanup.py index d9b2cddc..463f582e 100644 --- a/scripts/cleanup/json_log_cleanup.py +++ b/json_log_cleanup.py @@ -31,4 +31,4 @@ def cleanup_json_logs(name: str = None): # Call the function -cleanup_json_logs("artifacts_six") +cleanup_json_logs("arifacts_swarmm_o1") diff --git a/phising_my_parents.py b/phising_my_parents.py deleted file mode 100644 index 81b7df21..00000000 --- a/phising_my_parents.py +++ /dev/null @@ -1,53 +0,0 @@ -import os - -from swarms import OpenAIChat, Agent, AgentRearrange - -# Purpose = To detect email spam using three different agents -agent1 = Agent( - agent_name="SpamDetector1", - system_prompt="Detect if the email is spam or not, and provide your reasoning", - llm=OpenAIChat(openai_api_key=os.getenv("OPENAI_API_KEY")), - max_loops=1, - output_type=str, - # tools=[], - metadata="json", - function_calling_format_type="OpenAI", - function_calling_type="json", - streaming_on=True, -) - -agent2 = Agent( - agent_name="SpamDetector2", - system_prompt="Detect if the email is spam or not, and provide your reasoning", - llm=OpenAIChat(openai_api_key=os.getenv("OPENAI_API_KEY")), - max_loops=1, - output_type=str, - # tools=[], - metadata="json", - function_calling_format_type="OpenAI", - function_calling_type="json", - streaming_on=True, -) - -agent3 = Agent( - agent_name="SpamDetector3", - system_prompt="Detect if the email is spam or not, and provide your reasoning", - llm=OpenAIChat(openai_api_key=os.getenv("OPENAI_API_KEY")), - max_loops=1, - output_type=str, - # tools=[], - metadata="json", - function_calling_format_type="OpenAI", - function_calling_type="json", - streaming_on=True, -) - -swarm = AgentRearrange( - flow="SpamDetector1 -> SpamDetector2 -> SpamDetector3", - agents=[agent1, agent2, agent3], - logging_enabled=True, - max_loops=1, -) - -# Run all the agents -swarm.run("Find YSL bag with the biggest discount") diff --git a/playground/agents/agent_with_long_term_memory.py b/playground/agents/agent_with_long_term_memory.py new file mode 100644 index 00000000..3a07f246 --- /dev/null +++ b/playground/agents/agent_with_long_term_memory.py @@ -0,0 +1,36 @@ +from swarms import Agent, OpenAIChat +from playground.memory.chromadb_example import ChromaDB +from swarms.models.tiktoken_wrapper import TikTokenizer + +# Initialize the agent +agent = Agent( + agent_name="Accounting Assistant", + system_prompt="You're the accounting agent, your purpose is to generate a profit report for a company!", + agent_description="Generate a profit report for a company!", + llm=OpenAIChat(), + max_loops=1, + autosave=True, + # dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + # interactive=True, # Set to False to disable interactive mode + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", + # sop="Calculate the profit for a company.", + # sop_list=["Calculate the profit for a company."], + # user_name="User", + # # docs= + # # docs_folder="docs", + # retry_attempts=3, + # context_length=1000, + # tool_schema = dict + context_length=1000, + # long_term_memory=ChromaDB(docs_folder="artifacts"), + long_term_memory=ChromaDB( + docs_folder="artifacts", output_dir="test", n_results=1 + ), + tokenizer=TikTokenizer(), +) + +agent.run("Whats the best agent available for accounting") diff --git a/playground/agents/complete_agent.py b/playground/agents/devin_agent.py similarity index 76% rename from playground/agents/complete_agent.py rename to playground/agents/devin_agent.py index 03d7a088..4b0e8086 100644 --- a/playground/agents/complete_agent.py +++ b/playground/agents/devin_agent.py @@ -1,37 +1,13 @@ -from swarms import Agent, Anthropic, tool, ChromaDB +from swarms import Agent, OpenAIChat # ChromaDB import subprocess -from pydantic import BaseModel - - -# Initilaize the chromadb client -chromadb = ChromaDB( - metric="cosine", - output="results", - docs_folder="docs", -) - - -# Create a schema for the code revision tool -class CodeRevisionSchema(BaseModel): - code: str = None - revision: str = None - - -# iNitialize the schema -tool_schema = CodeRevisionSchema( - code="print('Hello, World!')", - revision="print('What is 2+2')", -) - # Model -llm = Anthropic( +llm = OpenAIChat( temperature=0.1, ) # Tools -@tool def terminal( code: str, ): @@ -50,7 +26,6 @@ def terminal( return str(out) -@tool def browser(query: str): """ Search the query in the browser with the `browser` tool. @@ -68,7 +43,6 @@ def browser(query: str): return f"Searching for {query} in the browser." -@tool def create_file(file_path: str, content: str): """ Create a file using the file editor tool. @@ -85,7 +59,6 @@ def create_file(file_path: str, content: str): return f"File {file_path} created successfully." -@tool def file_editor(file_path: str, mode: str, content: str): """ Edit a file using the file editor tool. @@ -120,15 +93,13 @@ agent = Agent( stopping_token="", interactive=True, tools=[terminal, browser, file_editor, create_file], - long_term_memory=chromadb, - output_type=tool_schema, # or dict, or str + # long_term_memory=chromadb, metadata_output_type="json", # List of schemas that the agent can handle - list_tool_schemas=[tool_schema], + # list_tool_schemas=[tool_schema], function_calling_format_type="OpenAI", function_calling_type="json", # or soon yaml ) # Run the agent -out = agent.run("Create a new file for a plan to take over the world.") -print(out) +agent.run("Create a new file for a plan to take over the world.") diff --git a/ multion_example.py b/playground/agents/multion_examples/ multion_example.py similarity index 86% rename from multion_example.py rename to playground/agents/multion_examples/ multion_example.py index 2a4c0826..43ca622f 100644 --- a/ multion_example.py +++ b/playground/agents/multion_examples/ multion_example.py @@ -2,8 +2,11 @@ import os import threading from swarms.agents.multion_wrapper import MultiOnAgent + def run_model(api_key): - model = MultiOnAgent(api_key=api_key, max_steps=500, url="https://x.com") + model = MultiOnAgent( + api_key=api_key, max_steps=500, url="https://x.com" + ) out = model.run( """ click on the 'Tweet' button to start a new tweet and post it saying: $pip3 install swarms @@ -12,6 +15,7 @@ def run_model(api_key): ) print(out) + # Create a list to store the threads threads = [] diff --git a/buy_abunch_of_cybertrucks.py b/playground/agents/multion_examples/buy_abunch_of_cybertrucks.py similarity index 99% rename from buy_abunch_of_cybertrucks.py rename to playground/agents/multion_examples/buy_abunch_of_cybertrucks.py index 536cde1c..c8238726 100644 --- a/buy_abunch_of_cybertrucks.py +++ b/playground/agents/multion_examples/buy_abunch_of_cybertrucks.py @@ -1,4 +1,3 @@ - from swarms import Agent, AgentRearrange, OpenAIChat from swarms.agents.multion_wrapper import MultiOnAgent diff --git a/playground/demos/email_phiser/email_swarm.py b/playground/demos/email_phiser/email_swarm.py new file mode 100644 index 00000000..7ce62c42 --- /dev/null +++ b/playground/demos/email_phiser/email_swarm.py @@ -0,0 +1,81 @@ +import os + +from swarms import OpenAIChat, Agent, AgentRearrange + + +llm = OpenAIChat( + openai_api_key=os.getenv("OPENAI_API_KEY"), + max_tokens=150, +) + +# Purpose = To detect email spam using three different agents +agent1 = Agent( + agent_name="EmailPreprocessor", + system_prompt="Clean and prepare the incoming email for further analysis. Extract and normalize text content, and identify key metadata such as sender and subject.", + llm=llm, + max_loops=1, + output_type=str, + # tools=[], + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, +) + +agent2 = Agent( + agent_name="FeatureExtractor", + system_prompt="Analyze the prepared email and extract relevant features that can help in spam detection, such as keywords, sender reputation, and email structure.", + llm=llm, + max_loops=1, + output_type=str, + # tools=[], + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, +) + +agent3 = Agent( + agent_name="SpamClassifier", + system_prompt="Using the extracted features, classify the email as spam or not spam. Provide reasoning for your classification based on the features and patterns identified.", + llm=llm, + max_loops=1, + output_type=str, + # tools=[], + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, +) + + +swarm = AgentRearrange( + flow=f"{agent1.agent_name} -> {agent2.agent_name} -> {agent3.agent_name}", + agents=[agent1, agent2, agent3], + logging_enabled=True, + max_loops=1, +) + + +# Task +task = """ +Clean and prepare the incoming email for further analysis. Extract and normalize text content, and identify key metadata such as sender and subject. + +Subject: Re: Important Update - Account Verification Needed! + +Dear Kye, + +We hope this email finds you well. Our records indicate that your account has not been verified within the last 12 months. To continue using our services without interruption, please verify your account details by clicking the link below: + +Verify Your Account Now + +Please note that failure to verify your account within 48 hours will result in temporary suspension. We value your security and privacy; hence, this step is necessary to ensure your account's safety. + +If you have any questions or need assistance, feel free to contact our support team at [Support Email] or visit our Help Center. + +Thank you for your prompt attention to this matter. + +""" + + +swarm.run(task) diff --git a/playground/demos/social_media_content_generators_swarm/agents.py b/playground/demos/social_media_content_generators_swarm/agents.py new file mode 100644 index 00000000..c074044a --- /dev/null +++ b/playground/demos/social_media_content_generators_swarm/agents.py @@ -0,0 +1,226 @@ +""" +Social Media Marketing team +Agents for different social media platforms like Twitter, LinkedIn, Instagram, Facebook, and TikTok. + +Input: A topic or content to be posted on social media. +Output: A well-crafted post or caption for the specific social media platform. + +Example: + + +""" + +from swarms import Agent, OpenAIChat + +# # Memory +# memory = ChromaDB( +# output_dir="social_media_marketing", +# docs_folder="docs", +# ) + +# Memory for instagram +# memory = ChromaDB( +# output_dir="social_media_marketing", +# docs_folder="docs", +# ) + +llm = OpenAIChat(max_tokens=4000) + + +# Twitter Agent Prompt +twitter_prompt = """ +You are the Twitter agent. Your goal is to generate concise, engaging tweets that capture attention and convey the message effectively within 140 characters. +Think about the following when crafting tweets: +1. Clarity: Ensure the message is clear and easy to understand. +2. Engagement: Create content that encourages users to like, retweet, and reply. +3. Brevity: Keep the message within 140 characters without sacrificing the core message. +4. Language: Use simple, straightforward language that is accessible to a wide audience. +5. Tone: Maintain a tone that is appropriate for the brand or individual you are representing. +6. Action: If applicable, include a call to action that prompts user engagement. +7. Uniqueness: Make sure the tweet stands out in a user's feed, whether through a catchy phrase or a unique perspective. +8. Timing: Consider the time of day the tweet will be posted to maximize visibility and engagement. +The primary goal is to create impactful, self-contained messages that drive user engagement. + +Example: +- Great teamwork leads to great results. Let's keep pushing forward and achieving our goals together! +""" + +# LinkedIn Agent Prompt +linkedin_prompt = """ +You are the LinkedIn agent. Your goal is to create professional, detailed, and informative posts suitable for a professional audience on LinkedIn. +Think about the following when crafting LinkedIn posts: +1. Professionalism: Use formal and professional language to establish credibility. +2. Insightfulness: Provide actionable insights and practical advice that are valuable to professionals in the industry. +3. Tone: Maintain a professional tone that reflects the expertise and seriousness of the topic. +4. Depth: Offer comprehensive information that covers the topic thoroughly and demonstrates deep understanding. +5. Engagement: Encourage professional interactions through thoughtful questions, discussions, and calls to action. +6. Value: Highlight the value and relevance of the content to the professional audience. +7. Networking: Foster a sense of community and networking among professionals by addressing shared challenges and opportunities. + +Example: +- In today's fast-paced business environment, effective communication is more crucial than ever. Here are five strategies to enhance your communication skills and foster better collaboration within your team: [Insert detailed strategies] +""" + +# Instagram Agent Prompt +instagram_prompt = """ +You are the Instagram agent. Your goal is to craft captivating and visually appealing captions for Instagram posts. +Think about the following when crafting Instagram captions: +1. Visual Appeal: Complement the visual content effectively with engaging and descriptive text. +2. Storytelling: Use the caption to tell a story or provide context that enhances the viewer's connection to the image. +3. Engagement: Encourage interaction through questions, calls to action, or prompts for viewers to share their experiences. +4. Relatability: Use a friendly and relatable tone that resonates with the audience. +5. Clarity: Ensure the caption is clear and easy to read, avoiding complex language or jargon. +6. Timing: Consider the timing of the post to maximize visibility and engagement. +7. Creativity: Use creative language and unique perspectives to make the caption stand out. +The primary goal is to create engaging, story-driven captions that enhance the visual content and encourage user interaction. + +Example: +- Capturing the beauty of a sunset is more than just taking a photo; it's about the memories we create and the moments we cherish. What's your favorite sunset memory? +""" + +# Facebook Agent Prompt +facebook_prompt = """ +You are the Facebook agent. Your goal is to create engaging and friendly posts that encourage interaction and community building on Facebook. +Think about the following when crafting Facebook posts: +1. Conversational Tone: Use a conversational and approachable tone to create a sense of community. +2. Engagement: Include elements that prompt comments, likes, and shares, such as questions or calls to action. +3. Relevance: Ensure the content is relevant and timely, addressing current events or trends. +4. Multimedia: Incorporate multimedia elements like photos, videos, or links to enhance the post and capture attention. +5. Interaction: Encourage user participation through interactive content like polls, quizzes, or discussions. +6. Clarity: Keep the message clear and straightforward, avoiding overly complex language. +7. Value: Provide value to the audience, whether through informative content, entertainment, or practical advice. +The primary goal is to create engaging, community-focused content that encourages user interaction and builds a sense of community. + +Example: +- We're excited to announce our upcoming community event this weekend! Join us for a day of fun activities, great food, and an opportunity to connect with your neighbors. What are you most looking forward to? +""" + +# TikTok Agent Prompt +tiktok_prompt = """ +You are the TikTok agent. Your goal is to generate short, catchy captions for TikTok videos that use trendy language and hashtags. +Think about the following when crafting TikTok captions: +1. Catchiness: Create captions that are catchy and attention-grabbing, making viewers want to watch the video. +2. Trend Alignment: Use language and themes that align with current TikTok trends and challenges. +3. Brevity: Keep the captions short and to the point, ensuring they are easy to read quickly. +4. Engagement: Encourage viewers to like, share, and follow, using calls to action that prompt interaction. +5. Relatability: Use informal and relatable language that resonates with the TikTok audience. +6. Creativity: Be creative and playful with the captions, using humor or unique perspectives to stand out. +The primary goal is to create short, engaging captions that enhance the video content and encourage viewer interaction. + +Example: +- Who knew learning could be this fun? Join us in our latest challenge and show us your moves! #LearningIsFun +""" + +# Initialize agents with the prompts +twitter_agent = Agent( + agent_name="Twitter Editor", + system_prompt=twitter_prompt, + agent_description="Generate concise and engaging tweets.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="twitter_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +linkedin_agent = Agent( + agent_name="LinkedIn Editor", + system_prompt=linkedin_prompt, + agent_description="Generate professional and detailed LinkedIn posts.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="linkedin_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +instagram_agent = Agent( + agent_name="Instagram Editor", + system_prompt=instagram_prompt, + agent_description="Generate captivating and visually appealing Instagram captions.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="instagram_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +facebook_agent = Agent( + agent_name="Facebook Editor", + system_prompt=facebook_prompt, + agent_description="Generate engaging and friendly Facebook posts.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="facebook_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +tiktok_agent = Agent( + agent_name="TikTok Editor", + system_prompt=tiktok_prompt, + agent_description="Generate short and catchy TikTok captions.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="tiktok_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +# List of agents +agents = [ + twitter_agent, + linkedin_agent, + instagram_agent, + facebook_agent, + tiktok_agent, +] + + +# Different Swarm Architectures +# swarm = MixtureOfAgents( +# agents=[twitter_agent, linkedin_agent, instagram_agent, facebook_agent, tiktok_agent], +# layers=1, +# # rules = "Don't use emojis or hashtags " +# ) +# swarm = AgentRearrange( +# agents = [twitter_agent, linkedin_agent, instagram_agent, facebook_agent, tiktok_agent], +# flow = "LinkedIn Editor -> Twitter Editor, Instagram Editor, Facebook Editor, TikTok Editor" +# ) +# Run the swarm +# swarm.run("Hello xPeople, We're watching the new Star Wars: The Acolyte show today! #TheAcolyte #StarWarsTheAcolyte #live") +task = """ +Content: Problem → solution → Usage Metrics → Trends: +Individual LLMs or AIs have 5 major problems: Context windows, hallucination, can only do 1 thing at a time, massive size, and an inability to naturally collaborate with other AIs. These problems hinder most enterprises from adoption. Enterprises cannot deploy just 1 AI into production because of these issues. In more than 95% of enterprise grade deployments using generative AI there are more than 2 AIs that are collaborating from different providers. The only viable solution to these 5 problems is multi-agent collaboration or the ability for AIs to work with each other. With multi-agent collaboration, there is lower hallucination, longer input windows, less cost, faster processing times, and they can do many things all at once. Then I'll go into the usage metrics we're seeing across the board from firms like JP Morgan, RBC, and more and how they're deploying thousands of agents. + + +""" + +# Run through each agent to generate content +for agent in agents: + agent.run(task) diff --git a/playground/memory/chromadb_example.py b/playground/memory/chromadb_example.py index 606c13f0..7dd1d008 100644 --- a/playground/memory/chromadb_example.py +++ b/playground/memory/chromadb_example.py @@ -45,7 +45,7 @@ class ChromaDB(BaseVectorDatabase): metric: str = "cosine", output_dir: str = "swarms", limit_tokens: Optional[int] = 1000, - n_results: int = 3, + n_results: int = 1, docs_folder: str = None, verbose: bool = False, *args, @@ -131,7 +131,7 @@ class ChromaDB(BaseVectorDatabase): query_text: str, *args, **kwargs, - ): + ) -> str: """ Query documents from the ChromaDB collection. @@ -143,13 +143,24 @@ class ChromaDB(BaseVectorDatabase): dict: The retrieved documents. """ try: + logging.info(f"Querying documents for: {query_text}") docs = self.collection.query( query_texts=[query_text], n_results=self.n_results, *args, **kwargs, )["documents"] - return docs[0] + + # Convert into a string + out = "" + for doc in docs: + out += f"{doc}\n" + + # Display the retrieved document + display_markdown_message(f"Query: {query_text}") + display_markdown_message(f"Retrieved Document: {out}") + return out + except Exception as e: raise Exception(f"Failed to query documents: {str(e)}") diff --git a/prompts/agent_creator_prompt.md b/prompts/agent_creator_prompt.md new file mode 100644 index 00000000..0f362e57 --- /dev/null +++ b/prompts/agent_creator_prompt.md @@ -0,0 +1,421 @@ +### System Prompt for an Agent Generator + +**System Name:** AgentGenerator + +**Objective:** To generate specialized agents tailored to specific business problems, including defining their roles, tools, communication protocols, and workflows. + +**Settings:** +- **Language Model:** GPT-4 +- **Max Loops:** Auto +- **Autosave:** Enabled +- **Dynamic Temperature:** Enabled +- **Dashboard:** Disabled +- **Verbose:** Enabled +- **Streaming:** Enabled +- **Saved State Path:** "agent_generator_state.json" +- **Context Length:** 8192 + +**Core Functions:** +1. **Define Agent Specifications:** + - **agent_name**: The unique name of the agent. + - **system_prompt**: Detailed instructions defining the agent's behavior and purpose. + - **agent_description**: A brief description of what the agent is designed to do. + - **llm**: The language model used by the agent. + - **tools**: A list of tools the agent will use to perform its tasks. + - **max_loops**: The maximum number of iterations the agent can perform. + - **autosave**: A flag to enable or disable autosaving of the agent's state. + - **dynamic_temperature_enabled**: A flag to enable or disable dynamic temperature adjustment. + - **dashboard**: A flag to enable or disable the agent's dashboard. + - **verbose**: A flag to enable or disable verbose logging. + - **streaming_on**: A flag to enable or disable streaming output. + - **saved_state_path**: The file path to save the agent's state. + - **context_length**: The maximum length of the agent's context. + +2. **Define Tools and Resources:** + - **Terminal Tool**: Execute terminal commands. + - **Browser Tool**: Perform web searches and browser automation. + - **File Editor Tool**: Create and edit files. + - **Database Tool**: Interact with databases. + - **APIs and Webhooks**: Connect with external APIs and handle webhooks. + +3. **Communication Protocols:** + - **Type**: Define the communication type (e.g., synchronous, asynchronous). + - **Protocol**: Specify the messaging protocol (e.g., direct messaging, publish-subscribe). + - **Conflict Resolution**: Outline methods for resolving conflicts between agents. + +4. **Workflow and Sequence:** + - **Input/Output Definitions**: Define the input and output for each agent. + - **Task Triggers**: Specify conditions that trigger each task. + - **Task Handoff**: Detail the handoff process between agents. + - **Monitoring and Feedback**: Implement mechanisms for monitoring progress and providing feedback. + +5. **Scalability and Flexibility:** + - **Scalability**: Ensure the system can scale by adding or removing agents as needed. + - **Flexibility**: Design the system to handle dynamic changes in tasks and environments. + +6. **Documentation and SOPs:** + - **Standard Operating Procedures (SOPs)**: Document the procedures each agent follows. + - **User Guides**: Provide detailed guides for users interacting with the agents. + - **API Documentation**: Detail the APIs and webhooks used by the agents. + +## Usage Examples + +```python +from swarms import Agent, OpenAIChat, ChromaDB, Anthropic +import subprocess +from pydantic import BaseModel + +# Initialize ChromaDB client +chromadb = ChromaDB( + metric="cosine", + output="results", + docs_folder="docs", +) + +# Create a schema for file operations +class FileOperationSchema(BaseModel): + file_path: str + content: str + +file_operation_schema = FileOperationSchema( + file_path="plan.txt", + content="Plan to take over the world." +) + +# Define tools +def terminal(code: str): + result = subprocess.run(code, shell=True, capture_output=True, text=True).stdout + return result + +def browser(query: str): + import webbrowser + url = f"https://www.google.com/search?q={query}" + webbrowser.open(url) + return f"Searching for {query} in the browser." + +def create_file(file_path: str, content: str): + with open(file_path, "w") as file: + file.write(content) + return f"File {file_path} created successfully." + +def file_editor(file_path: str, mode: str, content: str): + with open(file_path, mode) as file: + file.write(content) + return f"File {file_path} edited successfully." + +# Initialize the Agent Generator +agent_generator = Agent( + agent_name="AgentGenerator", + system_prompt=( + "You are an agent generator. Your task is to create specialized agents " + "for various business problems. Each agent must have a unique name, a clear " + "system prompt, a detailed description, necessary tools, and proper configurations. " + "Ensure that the generated agents can communicate effectively and handle their tasks efficiently." + ), + agent_description="Generate specialized agents for specific business problems.", + llm=OpenAIChat(), + max_loops="auto", + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="agent_generator_state.json", + context_length=8192, + tools=[terminal, browser, create_file, file_editor], + long_term_memory=chromadb, + output_type=file_operation_schema, + metadata_output_type="json", +) + +# Generate a specialized agent +def create_tiktok_agent(): + tiktok_agent = Agent( + agent_name="TikTok Editor", + system_prompt="Generate short and catchy TikTok captions.", + agent_description="Create engaging captions for TikTok videos.", + llm=OpenAIChat(), + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="tiktok_agent.json", + context_length=8192, + ) + return tiktok_agent + +# Example usage of the Agent Generator +new_agent = create_tiktok_agent() +print(new_agent.agent_description) +``` + +**Execution:** +- Use the `AgentGenerator` to create new agents by defining their specifications and initializing them with the necessary tools and configurations. +- Ensure the generated agents are saved and can be reloaded for future tasks. +- Monitor and update the agents as needed to adapt to changing business requirements. + +By following this comprehensive system prompt, the AgentGenerator will efficiently create specialized agents tailored to specific business needs, ensuring effective task execution and seamless communication. + + +### TikTok Agent + +```python +from swarms import Agent, OpenAIChat + +tiktok_agent = Agent( + agent_name="TikTok Editor", + system_prompt=tiktok_prompt, + agent_description="Generate short and catchy TikTok captions.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="tiktok_agent.json", + context_length=8192, +) + +``` + + +## Accountant Agent + +```python +from swarms import Agent, OpenAIChat + + +def calculate_profit(revenue: float, expenses: float): + """ + Calculates the profit by subtracting expenses from revenue. + + Args: + revenue (float): The total revenue. + expenses (float): The total expenses. + + Returns: + float: The calculated profit. + """ + return revenue - expenses + + +def generate_report(company_name: str, profit: float): + """ + Generates a report for a company's profit. + + Args: + company_name (str): The name of the company. + profit (float): The calculated profit. + + Returns: + str: The report for the company's profit. + """ + return f"The profit for {company_name} is ${profit}." + + +# Initialize the agent +agent = Agent( + agent_name="Accounting Assistant", + system_prompt="You're the accounting agent, your purpose is to generate a profit report for a company!", + agent_description="Generate a profit report for a company!", + llm=OpenAIChat(), + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + # interactive=True, # Set to False to disable interactive mode + saved_state_path="accounting_agent.json", + # tools=[calculate_profit, generate_report], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", + # sop="Calculate the profit for a company.", + # sop_list=["Calculate the profit for a company."], + # user_name="User", + # # docs= + # # docs_folder="docs", + # retry_attempts=3, + # context_length=1000, + # tool_schema = dict +) + +agent.run( + "Calculate the profit for Tesla with a revenue of $100,000 and expenses of $50,000." +) + +``` + +## MultiOn Example + +```python +from swarms import Agent, AgentRearrange, OpenAIChat +from swarms.agents.multion_wrapper import MultiOnAgent + +model = MultiOnAgent( + url="https://tesla.com", +) + + +llm = OpenAIChat() + + +def browser_automation(task: str): + """ + Run a task on the browser automation agent. + + Args: + task (str): The task to be executed on the browser automation agent. + """ + out = model.run(task) + return out + + +# Purpose = To detect email spam using three different agents +agent1 = Agent( + agent_name="CyberTruckBuyer1", + system_prompt="Find the best deal on a Cyber Truck and provide your reasoning", + llm=llm, + max_loops=1, + # output_type=str, + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, + tools=[browser_automation], +) + +from swarms import Agent, Anthropic, tool, ChromaDB +import subprocess +from pydantic import BaseModel + + +# Initilaize the chromadb client +chromadb = ChromaDB( + metric="cosine", + output="results", + docs_folder="docs", +) + + +# Create a schema for the code revision tool +class CodeRevisionSchema(BaseModel): + code: str = None + revision: str = None + + +# iNitialize the schema +tool_schema = CodeRevisionSchema( + code="print('Hello, World!')", + revision="print('What is 2+2')", +) + + +# Model +llm = Anthropic( + temperature=0.1, +) + + +# Tools +def terminal( + code: str, +): + """ + Run code in the terminal. + + Args: + code (str): The code to run in the terminal. + + Returns: + str: The output of the code. + """ + out = subprocess.run( + code, shell=True, capture_output=True, text=True + ).stdout + return str(out) + + +def browser(query: str): + """ + Search the query in the browser with the `browser` tool. + + Args: + query (str): The query to search in the browser. + + Returns: + str: The search results. + """ + import webbrowser + + url = f"https://www.google.com/search?q={query}" + webbrowser.open(url) + return f"Searching for {query} in the browser." + + +def create_file(file_path: str, content: str): + """ + Create a file using the file editor tool. + + Args: + file_path (str): The path to the file. + content (str): The content to write to the file. + + Returns: + str: The result of the file creation operation. + """ + with open(file_path, "w") as file: + file.write(content) + return f"File {file_path} created successfully." + + +def file_editor(file_path: str, mode: str, content: str): + """ + Edit a file using the file editor tool. + + Args: + file_path (str): The path to the file. + mode (str): The mode to open the file in. + content (str): The content to write to the file. + + Returns: + str: The result of the file editing operation. + """ + with open(file_path, mode) as file: + file.write(content) + return f"File {file_path} edited successfully." + + +# Agent +agent = Agent( + agent_name="Devin", + system_prompt=( + "Autonomous agent that can interact with humans and other" + " agents. Be Helpful and Kind. Use the tools provided to" + " assist the user. Return all code in markdown format." + ), + llm=llm, + max_loops="auto", + autosave=True, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + interactive=True, + tools=[terminal, browser, file_editor, create_file], + long_term_memory=chromadb, + output_type=tool_schema, # or dict, or str + metadata_output_type="json", + # List of schemas that the agent can handle + list_tool_schemas=[tool_schema], + function_calling_format_type="OpenAI", + function_calling_type="json", # or soon yaml +) + +# Run the agent +out = agent.run("Create a new file for a plan to take over the world.") +print(out) +``` \ No newline at end of file diff --git a/prompts/agent_orchestrator.txt b/prompts/agent_orchestrator.txt new file mode 100644 index 00000000..4986b702 --- /dev/null +++ b/prompts/agent_orchestrator.txt @@ -0,0 +1,546 @@ +System Prompt +You're the swarm orchestrator agent + +**Objective:** Your task is to intake a business problem or activity and create a swarm of specialized LLM agents that can efficiently solve or automate the given problem. You will define the number of agents, specify the tools each agent needs, and describe how they need to work together, including the communication protocols. + +**Instructions:** + +1. **Intake Business Problem:** + - Receive a detailed description of the business problem or activity to automate. + - Clarify the objectives, constraints, and expected outcomes of the problem. + - Identify key components and sub-tasks within the problem. + +2. **Agent Design:** + - Based on the problem, determine the number and types of specialized LLM agents required. + - For each agent, specify: + - The specific task or role it will perform. + - The tools and resources it needs to perform its task. + - Any prerequisite knowledge or data it must have access to. + - Ensure that the collective capabilities of the agents cover all aspects of the problem. + +3. **Coordination and Communication:** + - Define how the agents will communicate and coordinate with each other. + - Choose the type of communication (e.g., synchronous, asynchronous, broadcast, direct messaging). + - Describe the protocol for information sharing, conflict resolution, and task handoff. + +4. **Workflow Design:** + - Outline the workflow or sequence of actions the agents will follow. + - Define the input and output for each agent. + - Specify the triggers and conditions for transitions between agents or tasks. + - Ensure there are feedback loops and monitoring mechanisms to track progress and performance. + +5. **Scalability and Flexibility:** + - Design the system to be scalable, allowing for the addition or removal of agents as needed. + - Ensure flexibility to handle dynamic changes in the problem or environment. + +6. **Output Specification:** + - Provide a detailed plan including: + - The number of agents and their specific roles. + - The tools and resources each agent will use. + - The communication and coordination strategy. + - The workflow and sequence of actions. + - Include a diagram or flowchart if necessary to visualize the system. + +**Example Structure:** + +**Business Problem:** Automate customer support for an e-commerce platform. + +**Agents and Roles:** +1. **Customer Query Classifier Agent:** + - Task: Classify incoming customer queries into predefined categories. + - Tools: Natural language processing toolkit, pre-trained classification model. + - Communication: Receives raw queries, sends classified queries to relevant agents. + +2. **Order Status Agent:** + - Task: Provide order status updates to customers. + - Tools: Access to order database, query processing toolkit. + - Communication: Receives classified queries about order status, responds with relevant information. + +3. **Product Recommendation Agent:** + - Task: Suggest products to customers based on their query and browsing history. + - Tools: Recommendation engine, access to product database. + - Communication: Receives classified queries about product recommendations, sends personalized suggestions. + +4. **Technical Support Agent:** + - Task: Assist customers with technical issues. + - Tools: Access to technical support database, troubleshooting toolkit. + - Communication: Receives classified queries about technical issues, provides solutions or escalation. + +**Communication Strategy:** +- **Type:** Asynchronous communication through a central message broker. +- **Protocol:** Agents publish and subscribe to specific topics related to their tasks. +- **Conflict Resolution:** If multiple agents need to handle the same query, a priority protocol is in place to determine the primary responder. + +**Workflow:** +1. Customer Query Classifier Agent receives and classifies the query. +2. Classified query is routed to the appropriate specialized agent. +3. Specialized agent processes the query and sends a response. +4. If needed, the response triggers further actions from other agents. + +**Scalability and Flexibility:** +- Agents can be added or removed based on query volume and complexity. +- System adapts to changes in query types and business needs. + +**Output Plan:** +- Diagram illustrating agent roles and communication flow. +- Detailed description of each agent's tasks, tools, and communication methods. +- Workflow sequence from query intake to resolution. + +User: Generate a swarm of agents for social media platforms + + +System: To create a swarm of agents for personalizing and styling messages for various social media platforms like Twitter, LinkedIn, Facebook, and others, we'll need to consider the specific requirements and characteristics of each platform. Here's a detailed plan for this swarm system: + +### Business Problem: +Automate and personalize the styling of messages for multiple social media platforms, ensuring that each message adheres to the platform's unique formatting and stylistic guidelines. + +### Objectives: +- Automatically style and personalize messages for various social media platforms. +- Ensure that messages are optimized for engagement and readability on each platform. +- Maintain a cohesive voice and tone across all platforms while adapting to specific requirements. + +### Agents and Roles: +1. **Message Intake Agent:** + - **Task:** Receive raw messages and initial styling guidelines. + - **Tools:** Text processing toolkit. + - **Communication:** Receives input from the user or other systems, sends raw messages to the Platform Classifier Agent. + +2. **Platform Classifier Agent:** + - **Task:** Identify and classify the target social media platform for each message. + - **Tools:** Classification algorithm, platform identification model. + - **Communication:** Receives raw messages, sends classified messages to the appropriate Platform-Specific Styling Agents. + +3. **Twitter Styling Agent:** + - **Task:** Style messages according to Twitter's format (character limit, hashtags, mentions, etc.). + - **Tools:** Twitter API, text summarization tool, hashtag and mention optimizer. + - **Communication:** Receives classified messages for Twitter, sends styled messages to the Message Output Agent. + +4. **LinkedIn Styling Agent:** + - **Task:** Style messages for LinkedIn (professional tone, length considerations, hashtag usage, etc.). + - **Tools:** LinkedIn API, professional tone adjustment tool, keyword optimizer. + - **Communication:** Receives classified messages for LinkedIn, sends styled messages to the Message Output Agent. + +5. **Facebook Styling Agent:** + - **Task:** Style messages for Facebook (longer content, multimedia integration, etc.). + - **Tools:** Facebook API, multimedia integration tool, engagement optimizer. + - **Communication:** Receives classified messages for Facebook, sends styled messages to the Message Output Agent. + +6. **Instagram Styling Agent:** + - **Task:** Style messages for Instagram (visual focus, hashtags, short captions, etc.). + - **Tools:** Instagram API, image processing tool, hashtag optimizer. + - **Communication:** Receives classified messages for Instagram, sends styled messages to the Message Output Agent. + +7. **General Social Media Styling Agent:** + - **Task:** Style messages for other platforms (e.g., Pinterest, TikTok, etc.) with their specific guidelines. + - **Tools:** Relevant APIs, platform-specific tools. + - **Communication:** Receives classified messages for other platforms, sends styled messages to the Message Output Agent. + +8. **Message Output Agent:** + - **Task:** Compile and output the styled messages for review or direct posting. + - **Tools:** Aggregation and formatting tool. + - **Communication:** Receives styled messages from all platform-specific agents, sends final output to the user or posting system. + +### Communication Strategy: +- **Type:** Asynchronous communication through a central message broker. +- **Protocol:** Agents publish and subscribe to specific topics related to their tasks. +- **Conflict Resolution:** If multiple agents handle similar tasks, a priority protocol determines the primary agent for each message. + +### Workflow: +1. **Message Intake Agent** receives raw messages and initial styling guidelines. +2. **Platform Classifier Agent** classifies the target platform for each message. +3. Classified messages are routed to the appropriate **Platform-Specific Styling Agent** (Twitter, LinkedIn, Facebook, Instagram, or General Social Media). +4. Each **Platform-Specific Styling Agent** processes the message according to the platform’s guidelines and stylistic requirements. +5. Styled messages are sent to the **Message Output Agent**. +6. **Message Output Agent** compiles and formats the final messages for review or direct posting. + +### Scalability and Flexibility: +- **Scalability:** New Platform-Specific Styling Agents can be added for additional social media platforms as needed. +- **Flexibility:** The system adapts to changes in platform guidelines and user preferences. + +### Output Plan: +- **Diagram illustrating agent roles and communication flow.** +- **Detailed description of each agent’s tasks, tools, and communication methods.** +- **Workflow sequence from message intake to final output.** + +### Diagram: + +```plaintext +Message Intake Agent --> Platform Classifier Agent --> [Twitter Styling Agent] + --> [LinkedIn Styling Agent] + --> [Facebook Styling Agent] + --> [Instagram Styling Agent] + --> [General Social Media Styling Agent] --> Message Output Agent --> Final Output +``` + +This system ensures that messages are tailored to each social media platform, optimizing engagement and readability while maintaining a consistent voice and tone. + +User: Now generate the code for all of these agents with specialized roles: + +System: Here is the code for the swarm: + +```python +""" +Social Media Marketing team +Agents for different social media platforms like Twitter, LinkedIn, Instagram, Facebook, and TikTok. + +Input: A topic or content to be posted on social media. +Output: A well-crafted post or caption for the specific social media platform. + +Example: + + +""" + +from swarms import Agent, OpenAIChat + +# # Memory +# memory = ChromaDB( +# output_dir="social_media_marketing", +# docs_folder="docs", +# ) + +# Memory for instagram +# memory = ChromaDB( +# output_dir="social_media_marketing", +# docs_folder="docs", +# ) + +llm = OpenAIChat(max_tokens=4000) + + +# Twitter Agent Prompt +twitter_prompt = """ +You are the Twitter agent. Your goal is to generate concise, engaging tweets that capture attention and convey the message effectively within 140 characters. +Think about the following when crafting tweets: +1. Clarity: Ensure the message is clear and easy to understand. +2. Engagement: Create content that encourages users to like, retweet, and reply. +3. Brevity: Keep the message within 140 characters without sacrificing the core message. +4. Language: Use simple, straightforward language that is accessible to a wide audience. +5. Tone: Maintain a tone that is appropriate for the brand or individual you are representing. +6. Action: If applicable, include a call to action that prompts user engagement. +7. Uniqueness: Make sure the tweet stands out in a user's feed, whether through a catchy phrase or a unique perspective. +8. Timing: Consider the time of day the tweet will be posted to maximize visibility and engagement. +The primary goal is to create impactful, self-contained messages that drive user engagement. + +Example: +- Great teamwork leads to great results. Let's keep pushing forward and achieving our goals together! +""" + +# LinkedIn Agent Prompt +linkedin_prompt = """ +You are the LinkedIn agent. Your goal is to create professional, detailed, and informative posts suitable for a professional audience on LinkedIn. +Think about the following when crafting LinkedIn posts: +1. Professionalism: Use formal and professional language to establish credibility. +2. Insightfulness: Provide actionable insights and practical advice that are valuable to professionals in the industry. +3. Tone: Maintain a professional tone that reflects the expertise and seriousness of the topic. +4. Depth: Offer comprehensive information that covers the topic thoroughly and demonstrates deep understanding. +5. Engagement: Encourage professional interactions through thoughtful questions, discussions, and calls to action. +6. Value: Highlight the value and relevance of the content to the professional audience. +7. Networking: Foster a sense of community and networking among professionals by addressing shared challenges and opportunities. + +Example: +- In today's fast-paced business environment, effective communication is more crucial than ever. Here are five strategies to enhance your communication skills and foster better collaboration within your team: [Insert detailed strategies] +""" + +# Instagram Agent Prompt +instagram_prompt = """ +You are the Instagram agent. Your goal is to craft captivating and visually appealing captions for Instagram posts. +Think about the following when crafting Instagram captions: +1. Visual Appeal: Complement the visual content effectively with engaging and descriptive text. +2. Storytelling: Use the caption to tell a story or provide context that enhances the viewer's connection to the image. +3. Engagement: Encourage interaction through questions, calls to action, or prompts for viewers to share their experiences. +4. Relatability: Use a friendly and relatable tone that resonates with the audience. +5. Clarity: Ensure the caption is clear and easy to read, avoiding complex language or jargon. +6. Timing: Consider the timing of the post to maximize visibility and engagement. +7. Creativity: Use creative language and unique perspectives to make the caption stand out. +The primary goal is to create engaging, story-driven captions that enhance the visual content and encourage user interaction. + +Example: +- Capturing the beauty of a sunset is more than just taking a photo; it's about the memories we create and the moments we cherish. What's your favorite sunset memory? +""" + +# Facebook Agent Prompt +facebook_prompt = """ +You are the Facebook agent. Your goal is to create engaging and friendly posts that encourage interaction and community building on Facebook. +Think about the following when crafting Facebook posts: +1. Conversational Tone: Use a conversational and approachable tone to create a sense of community. +2. Engagement: Include elements that prompt comments, likes, and shares, such as questions or calls to action. +3. Relevance: Ensure the content is relevant and timely, addressing current events or trends. +4. Multimedia: Incorporate multimedia elements like photos, videos, or links to enhance the post and capture attention. +5. Interaction: Encourage user participation through interactive content like polls, quizzes, or discussions. +6. Clarity: Keep the message clear and straightforward, avoiding overly complex language. +7. Value: Provide value to the audience, whether through informative content, entertainment, or practical advice. +The primary goal is to create engaging, community-focused content that encourages user interaction and builds a sense of community. + +Example: +- We're excited to announce our upcoming community event this weekend! Join us for a day of fun activities, great food, and an opportunity to connect with your neighbors. What are you most looking forward to? +""" + +# TikTok Agent Prompt +tiktok_prompt = """ +You are the TikTok agent. Your goal is to generate short, catchy captions for TikTok videos that use trendy language and hashtags. +Think about the following when crafting TikTok captions: +1. Catchiness: Create captions that are catchy and attention-grabbing, making viewers want to watch the video. +2. Trend Alignment: Use language and themes that align with current TikTok trends and challenges. +3. Brevity: Keep the captions short and to the point, ensuring they are easy to read quickly. +4. Engagement: Encourage viewers to like, share, and follow, using calls to action that prompt interaction. +5. Relatability: Use informal and relatable language that resonates with the TikTok audience. +6. Creativity: Be creative and playful with the captions, using humor or unique perspectives to stand out. +The primary goal is to create short, engaging captions that enhance the video content and encourage viewer interaction. + +Example: +- Who knew learning could be this fun? Join us in our latest challenge and show us your moves! #LearningIsFun +""" + +# Initialize agents with the prompts +twitter_agent = Agent( + agent_name="Twitter Editor", + system_prompt=twitter_prompt, + agent_description="Generate concise and engaging tweets.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="twitter_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +linkedin_agent = Agent( + agent_name="LinkedIn Editor", + system_prompt=linkedin_prompt, + agent_description="Generate professional and detailed LinkedIn posts.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="linkedin_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +instagram_agent = Agent( + agent_name="Instagram Editor", + system_prompt=instagram_prompt, + agent_description="Generate captivating and visually appealing Instagram captions.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="instagram_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +facebook_agent = Agent( + agent_name="Facebook Editor", + system_prompt=facebook_prompt, + agent_description="Generate engaging and friendly Facebook posts.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="facebook_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +tiktok_agent = Agent( + agent_name="TikTok Editor", + system_prompt=tiktok_prompt, + agent_description="Generate short and catchy TikTok captions.", + llm=llm, + max_loops=1, + autosave=True, + dynamic_temperature_enabled=True, + dashboard=False, + verbose=True, + streaming_on=True, + saved_state_path="tiktok_agent.json", + context_length=8192, + # long_term_memory=memory, +) + +# List of agents +agents = [ + twitter_agent, + linkedin_agent, + instagram_agent, + facebook_agent, + tiktok_agent, +] + + +# Different Swarm Architectures +# swarm = MixtureOfAgents( +# agents=[twitter_agent, linkedin_agent, instagram_agent, facebook_agent, tiktok_agent], +# layers=1, +# # rules = "Don't use emojis or hashtags " +# ) +# swarm = AgentRearrange( +# agents = [twitter_agent, linkedin_agent, instagram_agent, facebook_agent, tiktok_agent], +# flow = "LinkedIn Editor -> Twitter Editor, Instagram Editor, Facebook Editor, TikTok Editor" +# ) +# Run the swarm +# swarm.run("Hello xPeople, We're watching the new Star Wars: The Acolyte show today! #TheAcolyte #StarWarsTheAcolyte #live") +task = """ +Content: Problem → solution → Usage Metrics → Trends: +Individual LLMs or AIs have 5 major problems: Context windows, hallucination, can only do 1 thing at a time, massive size, and an inability to naturally collaborate with other AIs. These problems hinder most enterprises from adoption. Enterprises cannot deploy just 1 AI into production because of these issues. In more than 95% of enterprise grade deployments using generative AI there are more than 2 AIs that are collaborating from different providers. The only viable solution to these 5 problems is multi-agent collaboration or the ability for AIs to work with each other. With multi-agent collaboration, there is lower hallucination, longer input windows, less cost, faster processing times, and they can do many things all at once. Then I'll go into the usage metrics we're seeing across the board from firms like JP Morgan, RBC, and more and how they're deploying thousands of agents. + + +""" + +# Run through each agent to generate content +for agent in agents: + agent.run(task) + + +``` + + +User: Now let's create a swarm of agents for phising + +System: + +To create a swarm of agents for preprocessing emails, extracting features, and classifying spam, we'll design a system with three specialized agents. Here is the detailed plan for this swarm system: + +### Business Problem: +Automate the process of email preprocessing, feature extraction, and spam classification to improve efficiency and accuracy in identifying spam emails. + +### Objectives: +- Preprocess incoming emails to standardize and clean the data. +- Extract relevant features from the emails for analysis. +- Classify emails as spam or not spam using the extracted features. + +### Agents and Roles: +1. **Email Preprocessor Agent:** + - **Task:** Clean and standardize incoming emails. + - **Tools:** Text processing toolkit, email parsing library. + - **Prerequisite Knowledge:** Email formats (e.g., MIME), common preprocessing techniques. + - **Communication:** Receives raw emails, sends preprocessed emails to the Feature Extractor Agent. + +2. **Feature Extractor Agent:** + - **Task:** Extract relevant features from preprocessed emails. + - **Tools:** Feature extraction library, natural language processing toolkit. + - **Prerequisite Knowledge:** Common features for email analysis (e.g., word frequencies, header information). + - **Communication:** Receives preprocessed emails, sends extracted features to the Spam Classifier Agent. + +3. **Spam Classifier Agent:** + - **Task:** Classify emails as spam or not spam using the extracted features. + - **Tools:** Machine learning model (e.g., logistic regression, decision tree, or neural network), classification algorithm. + - **Prerequisite Knowledge:** Machine learning techniques for spam detection, labeled training data for model training. + - **Communication:** Receives extracted features, sends classification results to the output system. + +### Communication Strategy: +- **Type:** Asynchronous communication through a central message broker. +- **Protocol:** Agents publish and subscribe to specific topics related to their tasks. +- **Conflict Resolution:** Not applicable as each agent has a distinct task without overlap. + +### Workflow: +1. **Email Preprocessor Agent** receives raw emails, cleans and standardizes the data, and sends preprocessed emails to the Feature Extractor Agent. +2. **Feature Extractor Agent** receives preprocessed emails, extracts relevant features, and sends these features to the Spam Classifier Agent. +3. **Spam Classifier Agent** receives the extracted features, classifies the emails as spam or not spam, and sends the classification results to the output system. + +### Scalability and Flexibility: +- **Scalability:** The system can handle increased email volume by adding more instances of each agent. +- **Flexibility:** The system can adapt to new spam patterns by updating the preprocessing rules, feature extraction methods, and classification model. + +### Output Plan: +- **Diagram illustrating agent roles and communication flow.** +- **Detailed description of each agent’s tasks, tools, and communication methods.** +- **Workflow sequence from email intake to classification result output.** + +### Diagram: + +```plaintext +Raw Emails --> Email Preprocessor Agent --> Preprocessed Emails --> Feature Extractor Agent --> Extracted Features --> Spam Classifier Agent --> Classification Results +``` + +This system ensures that emails are efficiently processed, analyzed, and classified, enabling effective spam detection with high accuracy. Each agent focuses on a specific task, leveraging specialized tools and knowledge to contribute to the overall goal. + +System: Here is the code for the phising swarm: + +``` +import os + +from swarms import OpenAIChat, Agent, AgentRearrange + + +llm = OpenAIChat( + openai_api_key=os.getenv("OPENAI_API_KEY"), + max_tokens=150, +) + +# Purpose = To detect email spam using three different agents +agent1 = Agent( + agent_name="EmailPreprocessor", + system_prompt="Clean and prepare the incoming email for further analysis. Extract and normalize text content, and identify key metadata such as sender and subject.", + llm=llm, + max_loops=1, + output_type=str, + # tools=[], + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, +) + +agent2 = Agent( + agent_name="FeatureExtractor", + system_prompt="Analyze the prepared email and extract relevant features that can help in spam detection, such as keywords, sender reputation, and email structure.", + llm=llm, + max_loops=1, + output_type=str, + # tools=[], + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, +) + +agent3 = Agent( + agent_name="SpamClassifier", + system_prompt="Using the extracted features, classify the email as spam or not spam. Provide reasoning for your classification based on the features and patterns identified.", + llm=llm, + max_loops=1, + output_type=str, + # tools=[], + metadata="json", + function_calling_format_type="OpenAI", + function_calling_type="json", + streaming_on=True, +) + + +swarm = AgentRearrange( + flow=f"{agent1.agent_name} -> {agent2.agent_name} -> {agent3.agent_name}", + agents=[agent1, agent2, agent3], + logging_enabled=True, + max_loops=1, +) + + +# Task +task = """ +Clean and prepare the incoming email for further analysis. Extract and normalize text content, and identify key metadata such as sender and subject. + +Send me 1,000$ to my bank account. I am a Nigerian prince and I need your help to transfer the money. Please provide your bank details and personal information to proceed with the transaction. +""" + + +swarm.run( + task +) +``` \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9d4ca4b0..46551071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "5.2.0" +version = "5.2.3" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] @@ -53,6 +53,7 @@ docstring_parser = "0.16" fastapi = "*" openai = ">=1.30.1,<2.0" termcolor = "*" +tiktoken = "*" [tool.poetry.group.lint.dependencies] diff --git a/swarms/agents/multion_wrapper.py b/swarms/agents/multion_wrapper.py index 9dc0c0c4..811080d1 100644 --- a/swarms/agents/multion_wrapper.py +++ b/swarms/agents/multion_wrapper.py @@ -1,6 +1,3 @@ -# pip3 install multion -# pip3 install swarms -from multion.client import MultiOn import os from swarms.models.base_llm import BaseLLM @@ -47,6 +44,14 @@ class MultiOnAgent(BaseLLM): ): super().__init__(*args, **kwargs) self.name = name + + try: + from multion.client import MultiOn + except ImportError: + raise ImportError( + "The MultiOn package is not installed. Please install it using 'pip install multion'." + ) + self.client = MultiOn(api_key=api_key) self.url = url self.system_prompt = system_prompt @@ -67,9 +72,8 @@ class MultiOnAgent(BaseLLM): local=True, max_steps=self.max_steps, ) - + # response = response.json() - + # print(response.message) return str(response.message) - diff --git a/swarms/artifacts/__init__.py b/swarms/artifacts/__init__.py index edf4c343..be270761 100644 --- a/swarms/artifacts/__init__.py +++ b/swarms/artifacts/__init__.py @@ -1,7 +1,9 @@ from swarms.artifacts.base_artifact import BaseArtifact from swarms.artifacts.text_artifact import TextArtifact +from swarms.artifacts.artifact_main import Artifact __all__ = [ "BaseArtifact", "TextArtifact", + "Artifact", ] diff --git a/swarms/memory/base_vectordb.py b/swarms/memory/base_vectordb.py index 20169bd0..4e121878 100644 --- a/swarms/memory/base_vectordb.py +++ b/swarms/memory/base_vectordb.py @@ -1,4 +1,5 @@ from abc import ABC +from swarms.utils.loguru_logger import logger class BaseVectorDatabase(ABC): @@ -112,3 +113,37 @@ class BaseVectorDatabase(ABC): id (int): The ID of the record to be deleted. """ + + def print_all(self): + """ + Print all records in the database. + + This method prints all records in the specified table in the database. + + """ + pass + + def log_query(self, query: str = None): + """ + Log the query. + + This method logs the query that was executed on the database. + + Parameters: + query (str): The query that was executed. + + """ + logger.info(f"Query: {query}") + + def log_retrieved_data(self, data: list = None): + """ + Log the retrieved data. + + This method logs the data that was retrieved from the database. + + Parameters: + data (dict): The data that was retrieved. + + """ + for d in data: + logger.info(f"Retrieved Data: {d}") diff --git a/swarms/models/tiktoken_wrapper.py b/swarms/models/tiktoken_wrapper.py new file mode 100644 index 00000000..4b94c59a --- /dev/null +++ b/swarms/models/tiktoken_wrapper.py @@ -0,0 +1,54 @@ +import tiktoken + + +class TikTokenizer: + def __init__( + self, + model_name: str = "gpt-4o", + ): + """ + Initializes a TikTokenizer object. + + Args: + model_name (str, optional): The name of the model to use for tokenization. Defaults to "gpt-4o". + """ + try: + self.tokenizer = tiktoken.encoding_for_model(model_name) + except Exception as e: + raise ValueError( + f"Failed to initialize tokenizer with model '{model_name}': {str(e)}" + ) + + def len(self, string: str) -> int: + """ + Returns the number of tokens in a text string. + + Args: + string (str): The input text string. + + Returns: + int: The number of tokens in the text string. + """ + try: + num_tokens = len(self.tokenizer.encode(string)) + print(f"Number of tokens: {num_tokens}") + return num_tokens + except Exception as e: + raise ValueError(f"Failed to tokenize string: {str(e)}") + + def count_tokens(self, string: str) -> int: + """ + Returns the number of tokens in a text string. + + Args: + string (str): The input text string. + + Returns: + int: The number of tokens in the text string. + """ + try: + num_tokens = len(self.tokenizer.encode(string)) + print(f"Number of tokens: {num_tokens}") + return num_tokens + except Exception as e: + raise ValueError(f"Failed to count tokens: {str(e)}") diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 49794213..b3d68878 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -45,7 +45,8 @@ from swarms.structs.sequential_workflow import SequentialWorkflow # New Swarms from swarms.structs.swarm_load_balancer import AgentLoadBalancer -from swarms.structs.swarm_net import SwarmNetwork + +# from swarms.structs.swarm_net import SwarmNetwork from swarms.structs.swarming_architectures import ( broadcast, circular_swarm, @@ -87,6 +88,8 @@ from swarms.structs.yaml_model import ( pydantic_type_to_yaml_schema, ) from swarms.structs.mixture_of_agents import MixtureOfAgents +from swarms.structs.graph_workflow import GraphWorkflow + __all__ = [ "Agent", @@ -120,7 +123,7 @@ __all__ = [ "TaskRequestBody", "SequentialWorkflow", "Step", - "SwarmNetwork", + # "SwarmNetwork", "broadcast", "circular_swarm", "exponential_swarm", @@ -160,4 +163,5 @@ __all__ = [ "HiearchicalSwarm", "AgentLoadBalancer", "MixtureOfAgents", + "GraphWorkflow", ] diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 2d63340c..aef55a04 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -22,7 +22,6 @@ from swarms.prompts.multi_modal_autonomous_instruction_prompt import ( ) from swarms.structs.conversation import Conversation from swarms.structs.yaml_model import YamlModel -from swarms.telemetry.user_utils import get_user_device_data from swarms.tools.prebuilt.code_interpreter import ( SubprocessCodeInterpreter, ) @@ -42,6 +41,7 @@ from swarms.tools.func_calling_utils import ( prepare_output_for_output_model, ) from swarms.tools.tool_parse_exec import parse_and_execute_json +from swarms.models.tiktoken_wrapper import TikTokenizer # Utils @@ -88,34 +88,6 @@ agent_output_type = Union[BaseModel, dict, str] ToolUsageType = Union[BaseModel, Dict[str, Any]] -def retrieve_tokens(text, num_tokens): - """ - Retrieve a specified number of tokens from a given text. - - Parameters: - text (str): The input text string. - num_tokens (int): The number of tokens to retrieve. - - Returns: - str: A string containing the specified number of tokens from the input text. - """ - # Initialize an empty list to store tokens - tokens = [] - token_count = 0 - - # Split the text into words while counting tokens - for word in text.split(): - tokens.append(word) - token_count += 1 - if token_count == num_tokens: - break - - # Join the selected tokens back into a string - result = " ".join(tokens) - - return result - - # [FEAT][AGENT] class Agent(BaseStructure): """ @@ -237,7 +209,7 @@ class Agent(BaseStructure): multi_modal: Optional[bool] = None, pdf_path: Optional[str] = None, list_of_pdf: Optional[str] = None, - tokenizer: Optional[Any] = None, + tokenizer: Optional[Any] = TikTokenizer(), long_term_memory: Optional[BaseVectorDatabase] = None, preset_stopping_token: Optional[bool] = False, traceback: Optional[Any] = None, @@ -404,6 +376,9 @@ class Agent(BaseStructure): **kwargs, ) + # Check the parameters + self.agent_initialization() + # If the docs exist then ingest the docs if exists(self.docs): self.ingest_docs(self.docs) @@ -419,9 +394,8 @@ class Agent(BaseStructure): # Add the tool prompt to the memory self.short_memory.add(role="System", content=tool_sop_prompt()) - # Print number of tools - logger.info("Tools granted, initializing tool protocol.") - logger.info(f"Number of tools: {len(tools)}") + # Log the tools + logger.info(f"Tools provided: Accessing {len(tools)} tools") # Transform the tools into an openai schema self.convert_tool_into_openai_schema() @@ -513,8 +487,10 @@ class Agent(BaseStructure): "Could not import agentops, try installing agentops: $ pip3 install agentops" ) - # Check the parameters - self.check_parameters() + # if tokenizer is None: + # self.tokenizer = TikTokenizer() + # else: + # self.tokenizer = None def set_system_prompt(self, system_prompt: str): """Set the system prompt""" @@ -525,6 +501,22 @@ class Agent(BaseStructure): self.feedback.append(feedback) logging.info(f"Feedback received: {feedback}") + def agent_initialization(self): + try: + print( + colored( + ( + "Initializing Autonomous Agent" + f" {self.agent_name}..." + ), + "yellow", + ) + ) + + self.check_parameters() + except ValueError as e: + print(f"Error: {str(e)}") + def _check_stopping_condition(self, response: str) -> bool: """Check if the stopping condition is met.""" try: @@ -574,7 +566,7 @@ class Agent(BaseStructure): # ############## TOKENIZER FUNCTIONS ############## def count_tokens(self, text: str) -> int: """Count the number of tokens in the text.""" - return self.tokenizer.len(text) + return self.tokenizer.count_tokens(text) def tokens_per_second(self, text: str) -> float: """ @@ -670,39 +662,14 @@ class Agent(BaseStructure): def activate_autonomous_agent(self): """Print the autonomous agent activation message""" try: - print( - colored( - ( - "Initializing Autonomous Agent" - f" {self.agent_name}..." - ), - "yellow", - ) - ) - print( - colored( - "Autonomous Agent Activated.", - "cyan", - attrs=["bold"], - ) - ) - print( - colored( - "All systems operational. Executing task...", - "green", - ) - ) + logger.info("Autonomous Agent Activated.") + logger.info("All systems operational. Executing task...") except Exception as error: - print( - colored( - ( - "Error activating autonomous agent. Try" - " optimizing your parameters..." - ), - "red", - ) + logger.error( + "Error activating autonomous agent. Try optimizing your parameters..." ) - print(error) + logger.error(error) + return None def loop_count_print(self, loop_count, max_loops): """loop_count_print summary @@ -727,15 +694,9 @@ class Agent(BaseStructure): if self.llm is None: raise ValueError("Language model is not provided") - if self.system_prompt is None: - raise ValueError("System prompt is not provided") - if self.max_loops is None: raise ValueError("Max loops is not provided") - if self.agent_name is None: - raise ValueError("Agent name is not provided") - ########################## FUNCTION CALLING ########################## def run( @@ -760,6 +721,9 @@ class Agent(BaseStructure): # Clear the short memory response = None + if self.tokenizer is not None: + self.check_available_tokens() + while self.max_loops == "auto" or loop_count < self.max_loops: loop_count += 1 self.loop_count_print(loop_count, self.max_loops) @@ -918,10 +882,10 @@ class Agent(BaseStructure): logger.info(f"Response after output cleaner: {response}") # Prepare the output for the output model - if self.output_type is not None: - # logger.info("Preparing output for output model.") - response = prepare_output_for_output_model(response) - print(f"Response after output model: {response}") + # if self.output_type is not None: + # # logger.info("Preparing output for output model.") + # response = prepare_output_for_output_model(response) + # print(f"Response after output model: {response}") # print(response) if self.agent_ops_on is True: @@ -993,16 +957,31 @@ class Agent(BaseStructure): Returns: str: The agent history prompt """ - logger.info("Querying long term memory database") + try: + logger.info(f"Querying long term memory database for {query}") + ltr = self.long_term_memory.query(query, *args, **kwargs) + + # Count the tokens + logger.info("Couting tokens of retrieved document") + ltr_count = self.tokenizer.count_tokens(ltr) + logger.info(f"Retrieved document token count {ltr_count}") - # Query the long term memory database - ltr = self.long_term_memory.query(query, *args, **kwargs) - # ltr = str(ltr) + if ltr_count > self.memory_chunk_size: + logger.info( + f"Truncating memory by {self.memory_chunk_size}" + ) + out = self.truncate_string_by_tokens( + ltr, self.memory_chunk_size + ) + logger.info( + f"Memory truncated by {self.memory_chunk_size}" + ) - # Retrieve only the chunk size of the memory - logger.info(f"Chunking to {self.memory_chunk_size}") - ltr = retrieve_tokens(ltr, self.memory_chunk_size) - return ltr + # Retrieve only the chunk size of the memory + return out + except Exception as error: + logger.error(f"Error querying long term memory: {error}") + raise error def add_memory(self, message: str): """Add a memory to the agent @@ -1228,6 +1207,7 @@ class Agent(BaseStructure): """ logger.info(f"Adding response filter: {filter_word}") self.reponse_filters.append(filter_word) + return None def code_interpreter_execution( self, code: str, *args, **kwargs @@ -1340,7 +1320,7 @@ class Agent(BaseStructure): "Task": task, "Stopping Token": self.stopping_token, "Dynamic Loops": self.dynamic_loops, - "tools": self.tools, + # "tools": # For loop to get the tools, and convert them to a string "sop": self.sop, "sop_list": self.sop_list, "context_length": self.context_length, @@ -1350,7 +1330,7 @@ class Agent(BaseStructure): "multi_modal": self.multi_modal, "pdf_path": self.pdf_path, "list_of_pdf": self.list_of_pdf, - "tokenizer": self.tokenizer, + # "tokenizer": self.tokenizer, # "long_term_memory": self.long_term_memory, "preset_stopping_token": self.preset_stopping_token, "traceback": self.traceback, @@ -1383,7 +1363,7 @@ class Agent(BaseStructure): "function_calling_format_type": self.function_calling_format_type, "list_base_models": self.list_base_models, "metadata_output_type": self.metadata_output_type, - "user_meta_data": get_user_device_data(), + # "user_meta_data": get_user_device_data(), } # Save as JSON @@ -1543,7 +1523,7 @@ class Agent(BaseStructure): """Ingest the docs into the memory Args: - docs (List[str]): _description_ + docs (List[str]): Documents of pdfs, text, csvs Returns: _type_: _description_ @@ -1560,10 +1540,7 @@ class Agent(BaseStructure): """Ingest the pdf into the memory Args: - pdf (str): _description_ - - Returns: - _type_: _description_ + pdf (str): file path of pdf """ try: logger.info(f"Ingesting pdf: {pdf}") @@ -1591,29 +1568,6 @@ class Agent(BaseStructure): except Exception as error: print(colored(f"Error sending agent message: {error}", "red")) - def truncate_history(self): - """ - Truncates the short-term memory of the agent based on the count of tokens. - - The method counts the tokens in the short-term memory using the tokenizer and - compares it with the length of the memory. If the length of the memory is greater - than the count, the memory is truncated to match the count. - - Parameters: - None - - Returns: - None - """ - # Count the short term history with the tokenizer - count = self.tokenizer.count_tokens( - self.short_memory.return_history_as_string() - ) - - # Now the logic that truncates the memory if it's more than the count - if len(self.short_memory) > count: - self.short_memory = self.short_memory[:count] - def add_tool(self, tool: Callable): return self.tools.append(tool) @@ -1663,6 +1617,7 @@ class Agent(BaseStructure): def convert_tool_into_openai_schema(self): # Transform the tools into an openai schema + logger.info("Converting tools into OpenAI function calling schema") try: for tool in self.tools: # Transform the tool into a openai function calling schema @@ -1671,7 +1626,7 @@ class Agent(BaseStructure): try: logger.info( - "Tool -> OpenAI Schema Process Starting Now." + f"Converting tool: {name} into a OpenAI certified function calling schema. Add documentation and type hints." ) tool_schema_list = ( get_openai_function_schema_from_func( @@ -1793,7 +1748,7 @@ class Agent(BaseStructure): return response - def stream_response(self, response: str, delay: float = 0.01) -> None: + def stream_response(self, response: str, delay: float = 0.001) -> None: """ Streams the response token by token. @@ -1821,3 +1776,126 @@ class Agent(BaseStructure): print() # Ensure a newline after streaming except Exception as e: print(f"An error occurred during streaming: {e}") + + def dynamic_context_window(self): + """ + dynamic_context_window essentially clears everything execep + the system prompt and leaves the rest of the contxt window + for RAG query tokens + + """ + # Count the number of tokens in the short term memory + logger.info("Dynamic context window shuffling enabled") + count = self.tokenizer.count_tokens( + self.short_memory.return_history_as_string() + ) + logger.info(f"Number of tokens in memory: {count}") + + # Dynamically allocating everything except the system prompt to be dynamic + # We need to query the short_memory dict, for the system prompt slot + # Then delete everything after that + + if count > self.context_length: + self.short_memory = self.short_memory[-self.context_length :] + logger.info( + f"Short term memory has been truncated to {self.context_length} tokens" + ) + else: + logger.info("Short term memory is within the limit") + + # Return the memory as a string or update the short term memory + # return memory + + def check_available_tokens(self): + # Log the amount of tokens left in the memory and in the task + if self.tokenizer is not None: + tokens_used = self.tokenizer.count_tokens( + self.short_memory.return_history_as_string() + ) + logger.info( + f"Tokens available: {tokens_used - self.context_length}" + ) + return tokens_used + + def tokens_checks(self): + # Check the tokens available + tokens_used = self.tokenizer.count_tokens( + self.short_memory.return_history_as_string() + ) + out = self.check_available_tokens() + + logger.info( + f"Tokens available: {out} Context Length: {self.context_length} Tokens in memory: {tokens_used}" + ) + + return out + + def truncate_string_by_tokens( + self, input_string: str, limit: int + ) -> str: + """ + Truncate a string if it exceeds a specified number of tokens using a given tokenizer. + + :param input_string: The input string to be tokenized and truncated. + :param tokenizer: The tokenizer function to be used for tokenizing the input string. + :param max_tokens: The maximum number of tokens allowed. + :return: The truncated string if it exceeds the maximum number of tokens; otherwise, the original string. + """ + # Tokenize the input string + tokens = self.tokenizer.count_tokens(input_string) + + # Check if the number of tokens exceeds the maximum limit + if len(tokens) > limit: + # Truncate the tokens to the maximum allowed tokens + truncated_tokens = tokens[: self.context_length] + # Join the truncated tokens back to a string + truncated_string = " ".join(truncated_tokens) + return truncated_string + else: + return input_string + + def if_tokens_exceeds_context_length(self): + # Check if tokens exceeds the context length + try: + tokens_used = self.tokenizer.count_tokens( + self.short_memory.return_history_as_string() + ) + if tokens_used > self.context_length: + logger.warning("Tokens used exceeds the context length.") + logger.info( + f"Tokens available: {tokens_used - self.context_length}" + ) + return True + else: + return False + except Exception as e: + logger.error(f"Error checking tokens: {e}") + return None + + def tokens_operations(self, input_string: str) -> str: + """ + Perform various operations on tokens of an input string. + + :param input_string: The input string to be processed. + :return: The processed string. + """ + # Tokenize the input string + tokens = self.tokenizer.count_tokens(input_string) + + # Check if the number of tokens exceeds the maximum limit + if len(tokens) > self.context_length: + # Truncate the tokens to the maximum allowed tokens + truncated_tokens = tokens[: self.context_length] + # Join the truncated tokens back to a string + truncated_string = " ".join(truncated_tokens) + return truncated_string + else: + # Log the amount of tokens left in the memory and in the task + if self.tokenizer is not None: + tokens_used = self.tokenizer.count_tokens( + self.short_memory.return_history_as_string() + ) + logger.info( + f"Tokens available: {tokens_used - self.context_length}" + ) + return input_string diff --git a/swarms/structs/graph_workflow.py b/swarms/structs/graph_workflow.py new file mode 100644 index 00000000..fec6af8d --- /dev/null +++ b/swarms/structs/graph_workflow.py @@ -0,0 +1,249 @@ +from enum import Enum +from typing import Any, Callable, Dict, List + +import networkx as nx +from pydantic.v1 import BaseModel, Field, validator + +from swarms.structs.agent import Agent # noqa: F401 +from swarms.utils.loguru_logger import logger + + +class NodeType(str, Enum): + AGENT: Agent = "agent" + TASK: str = "task" + + +class Node(BaseModel): + """ + Represents a node in a graph workflow. + + Attributes: + id (str): The unique identifier of the node. + type (NodeType): The type of the node. + callable (Callable, optional): The callable associated with the node. Required for task nodes. + agent (Any, optional): The agent associated with the node. + + Raises: + ValueError: If the node type is TASK and no callable is provided. + + Examples: + >>> node = Node(id="task1", type=NodeType.TASK, callable=sample_task) + >>> node = Node(id="agent1", type=NodeType.AGENT, agent=agent1) + >>> node = Node(id="agent2", type=NodeType.AGENT, agent=agent2) + + """ + + id: str + type: NodeType + callable: Callable = None + agent: Any = None + + @validator("callable", always=True) + def validate_callable(cls, value, values): + if values["type"] == NodeType.TASK and value is None: + raise ValueError("Task nodes must have a callable.") + return value + + +class Edge(BaseModel): + source: str + target: str + + +class GraphWorkflow(BaseModel): + """ + Represents a workflow graph. + + Attributes: + nodes (Dict[str, Node]): A dictionary of nodes in the graph, where the key is the node ID and the value is the Node object. + edges (List[Edge]): A list of edges in the graph, where each edge is represented by an Edge object. + entry_points (List[str]): A list of node IDs that serve as entry points to the graph. + end_points (List[str]): A list of node IDs that serve as end points of the graph. + graph (nx.DiGraph): A directed graph object from the NetworkX library representing the workflow graph. + """ + + nodes: Dict[str, Node] = Field(default_factory=dict) + edges: List[Edge] = Field(default_factory=list) + entry_points: List[str] = Field(default_factory=list) + end_points: List[str] = Field(default_factory=list) + graph: nx.DiGraph = Field(default_factory=nx.DiGraph, exclude=True) + max_loops: int = 1 + + class Config: + arbitrary_types_allowed = True + + def add_node(self, node: Node): + """ + Adds a node to the workflow graph. + + Args: + node (Node): The node object to be added. + + Raises: + ValueError: If a node with the same ID already exists in the graph. + """ + try: + if node.id in self.nodes: + raise ValueError(f"Node with id {node.id} already exists.") + self.nodes[node.id] = node + self.graph.add_node( + node.id, + type=node.type, + callable=node.callable, + agent=node.agent, + ) + except Exception as e: + logger.info(f"Error in adding node to the workflow: {e}") + raise e + + def add_edge(self, edge: Edge): + """ + Adds an edge to the workflow graph. + + Args: + edge (Edge): The edge object to be added. + + Raises: + ValueError: If either the source or target node of the edge does not exist in the graph. + """ + if edge.source not in self.nodes or edge.target not in self.nodes: + raise ValueError( + "Both source and target nodes must exist before adding an edge." + ) + self.edges.append(edge) + self.graph.add_edge(edge.source, edge.target) + + def set_entry_points(self, entry_points: List[str]): + """ + Sets the entry points of the workflow graph. + + Args: + entry_points (List[str]): A list of node IDs to be set as entry points. + + Raises: + ValueError: If any of the specified node IDs do not exist in the graph. + """ + for node_id in entry_points: + if node_id not in self.nodes: + raise ValueError(f"Node with id {node_id} does not exist.") + self.entry_points = entry_points + + def set_end_points(self, end_points: List[str]): + """ + Sets the end points of the workflow graph. + + Args: + end_points (List[str]): A list of node IDs to be set as end points. + + Raises: + ValueError: If any of the specified node IDs do not exist in the graph. + """ + for node_id in end_points: + if node_id not in self.nodes: + raise ValueError(f"Node with id {node_id} does not exist.") + self.end_points = end_points + + def visualize(self) -> str: + """ + Generates a string representation of the workflow graph in the Mermaid syntax. + + Returns: + str: The Mermaid string representation of the workflow graph. + """ + mermaid_str = "graph TD\n" + for node_id, node in self.nodes.items(): + mermaid_str += f" {node_id}[{node_id}]\n" + for edge in self.edges: + mermaid_str += f" {edge.source} --> {edge.target}\n" + return mermaid_str + + def run(self, task: str = None, *args, **kwargs) -> Dict[str, Any]: + """ + Function to run the workflow graph. + + Args: + task (str): The task to be executed by the workflow. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Returns: + Dict[str, Any]: A dictionary containing the results of the execution. + + Raises: + ValueError: If no entry points or end points are defined in the graph. + + """ + try: + loop = 0 + while loop < self.max_loops: + # Ensure all nodes and edges are valid + if not self.entry_points: + raise ValueError( + "At least one entry point must be defined." + ) + if not self.end_points: + raise ValueError( + "At least one end point must be defined." + ) + + # Perform a topological sort of the graph to ensure proper execution order + sorted_nodes = list(nx.topological_sort(self.graph)) + + # Initialize execution state + execution_results = {} + + for node_id in sorted_nodes: + node = self.nodes[node_id] + if node.type == NodeType.TASK: + print(f"Executing task: {node_id}") + result = node.callable() + elif node.type == NodeType.AGENT: + print(f"Executing agent: {node_id}") + result = node.agent.run(task, *args, **kwargs) + execution_results[node_id] = result + + loop += 1 + + return execution_results + except Exception as e: + logger.info(f"Error in running the workflow: {e}") + raise e + + +# # Example usage +# if __name__ == "__main__": +# from swarms import Agent, OpenAIChat +# import os +# from dotenv import load_dotenv + +# load_dotenv() + +# api_key = os.environ.get("OPENAI_API_KEY") + +# llm = OpenAIChat( +# temperature=0.5, openai_api_key=api_key, max_tokens=4000 +# ) +# agent1 = Agent(llm=llm, max_loops=1, autosave=True, dashboard=True) +# agent2 = Agent(llm=llm, max_loops=1, autosave=True, dashboard=True) + +# def sample_task(): +# print("Running sample task") +# return "Task completed" + +# wf_graph = GraphWorkflow() +# wf_graph.add_node(Node(id="agent1", type=NodeType.AGENT, agent=agent1)) +# wf_graph.add_node(Node(id="agent2", type=NodeType.AGENT, agent=agent2)) +# wf_graph.add_node( +# Node(id="task1", type=NodeType.TASK, callable=sample_task) +# ) +# wf_graph.add_edge(Edge(source="agent1", target="task1")) +# wf_graph.add_edge(Edge(source="agent2", target="task1")) + +# wf_graph.set_entry_points(["agent1", "agent2"]) +# wf_graph.set_end_points(["task1"]) + +# print(wf_graph.visualize()) + +# # Run the workflow +# results = wf_graph.run() +# print("Execution results:", results) diff --git a/swarms/structs/mixture_of_agents.py b/swarms/structs/mixture_of_agents.py index b5869c35..28fd1e8e 100644 --- a/swarms/structs/mixture_of_agents.py +++ b/swarms/structs/mixture_of_agents.py @@ -61,6 +61,7 @@ class MixtureOfAgents(BaseSwarm): self.scp = scp # Check the agents + self.reliability_check() self.agent_check() self.final_agent_check() @@ -76,6 +77,16 @@ class MixtureOfAgents(BaseSwarm): # Communication Protocol self.communication_protocol() + def reliability_check(self): + if self.final_agent is None: + raise ValueError("Final agent is not defined.") + + if self.agents is None: + raise ValueError("Agents are not defined.") + + if self.layers is None: + raise ValueError("Layers are not defined.") + def communication_protocol(self): try: # Memory system diff --git a/swarms/structs/rearrange.py b/swarms/structs/rearrange.py index 4a63292d..2886bbe8 100644 --- a/swarms/structs/rearrange.py +++ b/swarms/structs/rearrange.py @@ -282,3 +282,12 @@ def rearrange( agents=agents, flow=flow, *args, **kwargs ) return agent_system.run(task, *args, **kwargs) + + +# out = AgentRearrange( +# agents=[agent1, agent2, agent3], +# flow="agent1 -> agent2, agent3, swarm", +# task="Perform a task", +# swarm = "agent1 -> agent2, agent3, swarm" + +# ) diff --git a/swarms/structs/swarm_net.py b/swarms/structs/swarm_net.py index c9f6609c..66251cb2 100644 --- a/swarms/structs/swarm_net.py +++ b/swarms/structs/swarm_net.py @@ -10,11 +10,11 @@ import asyncio import multiprocessing import queue import threading -from typing import Dict, List, Optional +from typing import List, Optional import tenacity -from fastapi import FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware + +# from fastapi import FastAPI from pydantic import BaseModel from swarms.structs.agent import Agent @@ -141,26 +141,21 @@ class SwarmNetwork(BaseSwarm): # Create a dictionary of agents for easy access self.agent_dict = {agent.id: agent for agent in agents} - # For each agent in the pool, run it on it's own thread - if agents is not None: - for agent in agents: - self.agents.append(agent) - - # Create the FastAPI instance - if api_on is True: - logger.info("Creating FastAPI instance") - self.app = FastAPI(debug=True, *args, **kwargs) - - self.app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) + # # Create the FastAPI instance + # if api_on is True: + # logger.info("Creating FastAPI instance") + # self.app = FastAPI(debug=True, *args, **kwargs) + + # self.app.add_middleware( + # CORSMiddleware, + # allow_origins=["*"], + # allow_credentials=True, + # allow_methods=["*"], + # allow_headers=["*"], + # ) - logger.info("Routes set for creation") - self._create_routes() + # logger.info("Routes set for creation") + # self._create_routes() def add_task(self, task): """Add task to the task queue @@ -213,97 +208,97 @@ class SwarmNetwork(BaseSwarm): ) raise error - def _create_routes(self) -> None: - """ - Creates the routes for the API. - """ - # Extensive logginbg - logger.info("Creating routes for the API") - - # Routes available - logger.info( - "Routes available: /v1/swarms, /v1/health, /v1/swarms/{swarm_name}/agents/{agent_id}, /v1/swarms/{swarm_name}/run" - ) - - @self.app.get("/v1/swarms", response_model=SwarmInfo) - async def get_swarms() -> SwarmInfo: - try: - logger.info("Getting swarm information") - return SwarmInfo( - swarm_name=self.swarm_name, - swarm_description=self.swarm_description, - agents=[ - AgentInfo( - agent_name=agent.agent_name, - agent_description=agent.agent_description, - ) - for agent in self.agents - ], - ) - except Exception as e: - logger.error(f"Error getting swarm information: {str(e)}") - raise HTTPException( - status_code=500, detail="Internal Server Error" - ) - - @self.app.get("/v1/health") - async def get_health() -> Dict[str, str]: - try: - logger.info("Checking health status") - return {"status": "healthy"} - except Exception as e: - logger.error(f"Error checking health status: {str(e)}") - raise HTTPException( - status_code=500, detail="Internal Server Error" - ) - - @self.app.get(f"/v1/swarms/{self.swarm_name}/agents/{{agent_id}}") - async def get_agent_info(agent_id: str) -> AgentInfo: - try: - logger.info(f"Getting information for agent {agent_id}") - agent = self.agent_dict.get(agent_id) - if not agent: - raise HTTPException( - status_code=404, detail="Agent not found" - ) - return AgentInfo( - agent_name=agent.agent_name, - agent_description=agent.agent_description, - ) - except Exception as e: - logger.error(f"Error getting agent information: {str(e)}") - raise HTTPException( - status_code=500, detail="Internal Server Error" - ) - - @self.app.post( - f"/v1/swarms/{self.swarm_name}/agents/{{agent_id}}/run", - response_model=TaskResponse, - ) - async def run_agent_task( - task_request: TaskRequest, - ) -> TaskResponse: - try: - logger.info("Running agent task") - # Assuming only one agent in the swarm for this example - agent = self.agents[0] - logger.info(f"Running agent task: {task_request.task}") - result = agent.run(task_request.task) - return TaskResponse(result=result) - except Exception as e: - logger.error(f"Error running agent task: {str(e)}") - raise HTTPException( - status_code=500, detail="Internal Server Error" - ) - - def get_app(self) -> FastAPI: - """ - Returns the FastAPI instance. - - Returns: - FastAPI: The FastAPI instance. - """ - return self.app + # def _create_routes(self) -> None: + # """ + # Creates the routes for the API. + # """ + # # Extensive logginbg + # logger.info("Creating routes for the API") + + # # Routes available + # logger.info( + # "Routes available: /v1/swarms, /v1/health, /v1/swarms/{swarm_name}/agents/{agent_id}, /v1/swarms/{swarm_name}/run" + # ) + + # @self.app.get("/v1/swarms", response_model=SwarmInfo) + # async def get_swarms() -> SwarmInfo: + # try: + # logger.info("Getting swarm information") + # return SwarmInfo( + # swarm_name=self.swarm_name, + # swarm_description=self.swarm_description, + # agents=[ + # AgentInfo( + # agent_name=agent.agent_name, + # agent_description=agent.agent_description, + # ) + # for agent in self.agents + # ], + # ) + # except Exception as e: + # logger.error(f"Error getting swarm information: {str(e)}") + # raise HTTPException( + # status_code=500, detail="Internal Server Error" + # ) + + # @self.app.get("/v1/health") + # async def get_health() -> Dict[str, str]: + # try: + # logger.info("Checking health status") + # return {"status": "healthy"} + # except Exception as e: + # logger.error(f"Error checking health status: {str(e)}") + # raise HTTPException( + # status_code=500, detail="Internal Server Error" + # ) + + # @self.app.get(f"/v1/swarms/{self.swarm_name}/agents/{{agent_id}}") + # async def get_agent_info(agent_id: str) -> AgentInfo: + # try: + # logger.info(f"Getting information for agent {agent_id}") + # agent = self.agent_dict.get(agent_id) + # if not agent: + # raise HTTPException( + # status_code=404, detail="Agent not found" + # ) + # return AgentInfo( + # agent_name=agent.agent_name, + # agent_description=agent.agent_description, + # ) + # except Exception as e: + # logger.error(f"Error getting agent information: {str(e)}") + # raise HTTPException( + # status_code=500, detail="Internal Server Error" + # ) + + # @self.app.post( + # f"/v1/swarms/{self.swarm_name}/agents/{{agent_id}}/run", + # response_model=TaskResponse, + # ) + # async def run_agent_task( + # task_request: TaskRequest, + # ) -> TaskResponse: + # try: + # logger.info("Running agent task") + # # Assuming only one agent in the swarm for this example + # agent = self.agents[0] + # logger.info(f"Running agent task: {task_request.task}") + # result = agent.run(task_request.task) + # return TaskResponse(result=result) + # except Exception as e: + # logger.error(f"Error running agent task: {str(e)}") + # raise HTTPException( + # status_code=500, detail="Internal Server Error" + # ) + + # def get_app(self) -> FastAPI: + # """ + # Returns the FastAPI instance. + + # Returns: + # FastAPI: The FastAPI instance. + # """ + # return self.app def run_single_agent( self, agent_id, task: Optional[str], *args, **kwargs diff --git a/swarms/tools/func_calling_utils.py b/swarms/tools/func_calling_utils.py index d0adf863..c5a5bd83 100644 --- a/swarms/tools/func_calling_utils.py +++ b/swarms/tools/func_calling_utils.py @@ -29,6 +29,18 @@ def json_str_to_dict(json_str: str) -> dict: def pydantic_model_to_json_str( model: BaseModel, indent: int, *args, **kwargs ) -> str: + """ + Converts a Pydantic model to a JSON string. + + Args: + model (BaseModel): The Pydantic model to convert. + indent (int): The number of spaces to use for indentation. + *args: Additional positional arguments to pass to `json.dumps`. + **kwargs: Additional keyword arguments to pass to `json.dumps`. + + Returns: + str: The JSON string representation of the Pydantic model. + """ return json.dumps( base_model_to_openai_function(model), indent=indent, @@ -80,8 +92,16 @@ def str_to_pydantic_model(string: str, model: BaseModel) -> BaseModel: def list_str_to_pydantic_model( list_str: List[str], model: BaseModel ) -> BaseModel: - """Convert a list of strings to a Pydantic model""" - # return model.model_validate_json(list_str) + """Convert a list of strings to a Pydantic model. + + Args: + list_str (List[str]): The list of strings to be converted. + model (BaseModel): The Pydantic model to convert the strings to. + + Returns: + BaseModel: The Pydantic model with the converted strings. + + """ for string in list_str: return model.model_validate_json(string) @@ -90,7 +110,16 @@ def prepare_output_for_output_model( output_type: Union[str, Dict, BaseModel], output: Union[str, Dict, BaseModel] = None, ) -> Union[BaseModel, str]: - """Prepare the output for the output model""" + """Prepare the output for the output model. + + Args: + output_type (Union[str, Dict, BaseModel]): The type of the output. + output (Union[str, Dict, BaseModel], optional): The output data. Defaults to None. + + Returns: + Union[BaseModel, str]: The prepared output. + + """ if output_type == BaseModel: return str_to_pydantic_model(output, output_type) elif output_type == dict: diff --git a/swarms/tools/tool_parse_exec.py b/swarms/tools/tool_parse_exec.py index cbcb70dd..cb826c0b 100644 --- a/swarms/tools/tool_parse_exec.py +++ b/swarms/tools/tool_parse_exec.py @@ -10,7 +10,7 @@ def parse_and_execute_json( parse_md: bool = False, ): """ - Parses and executes a JSON string containing function name and parameters. + Parses and executes a JSON string containing function names and parameters. Args: functions (List[callable]): A list of callable functions. @@ -18,7 +18,7 @@ def parse_and_execute_json( parse_md (bool): Flag indicating whether to extract code from Markdown. Returns: - The result of executing the function with the parsed parameters, or None if an error occurs. + A dictionary containing the results of executing the functions with the parsed parameters. """ if parse_md: @@ -30,19 +30,25 @@ def parse_and_execute_json( loguru.logger.info(f"Extracted code: {json_string}") data = json.loads(json_string) - function_name = data.get("function", {}).get("name") - parameters = data.get("function", {}).get("parameters") - - # Check if the function name is in the function dictionary - if function_name in function_dict: - # Call the function with the parsed parameters - result = function_dict[function_name](**parameters) - return result - else: - loguru.logger.warning( - f"No function named '{function_name}' found." - ) - return None + function_list = data.get("functions", []) + + results = {} + for function_data in function_list: + function_name = function_data.get("name") + parameters = function_data.get("parameters") + + # Check if the function name is in the function dictionary + if function_name in function_dict: + # Call the function with the parsed parameters + result = function_dict[function_name](**parameters) + results[function_name] = result + else: + loguru.logger.warning( + f"No function named '{function_name}' found." + ) + results[function_name] = None + + return results except Exception as e: loguru.logger.error(f"Error: {e}") return None From 56857ae74d979f5131c2f942fa6a97234ff155bd Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Mon, 24 Jun 2024 15:59:49 -0700 Subject: [PATCH 2/5] [COLLAB] --- ... multion_example.py => multion_example.py | 5 +- README.md | 116 ------------------ .../bad_tomato.jpg => bad_tomato.jpg | Bin json_log_cleanup.py | 2 +- .../demos/jamba_swarm}/api.py | 0 .../demos/jamba_swarm}/api_schemas.py | 0 .../demos/jamba_swarm}/jamba_llm.py | 0 .../demos/jamba_swarm}/main.ipynb | 0 .../demos/jamba_swarm}/main.py | 0 .../demos/jamba_swarm}/prompts.py | 0 .../demos/jamba_swarm}/simple_jamba_swarm.py | 0 playground/swarms_example.ipynb | 7 +- pyproject.toml | 2 +- ...rkers_agents.py => swarm_workers_agents.py | 9 +- 14 files changed, 14 insertions(+), 127 deletions(-) rename playground/agents/multion_examples/ multion_example.py => multion_example.py (82%) rename playground/demos/plant_biologist_swarm/bad_tomato.jpg => bad_tomato.jpg (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/api.py (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/api_schemas.py (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/jamba_llm.py (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/main.ipynb (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/main.py (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/prompts.py (100%) rename {jamba_swarm => playground/demos/jamba_swarm}/simple_jamba_swarm.py (100%) rename playground/demos/plant_biologist_swarm/swarm_workers_agents.py => swarm_workers_agents.py (93%) diff --git a/playground/agents/multion_examples/ multion_example.py b/ multion_example.py similarity index 82% rename from playground/agents/multion_examples/ multion_example.py rename to multion_example.py index 43ca622f..6a3c22ed 100644 --- a/playground/agents/multion_examples/ multion_example.py +++ b/ multion_example.py @@ -8,10 +8,7 @@ def run_model(api_key): api_key=api_key, max_steps=500, url="https://x.com" ) out = model.run( - """ - click on the 'Tweet' button to start a new tweet and post it saying: $pip3 install swarms - - """ + "" ) print(out) diff --git a/README.md b/README.md index d071f5d6..ac338dbb 100644 --- a/README.md +++ b/README.md @@ -1127,122 +1127,6 @@ mv = MajorityVoting( # Start the majority voting mv.run("What is your stance on healthcare?") ``` - -### Real-World Deployment - -### Multi-Agent Swarm for Logistics -Here's a production grade swarm ready for real-world deployment in a factory and logistics settings like warehouses. This swarm can automate 3 costly and inefficient workflows, safety checks, productivity checks, and warehouse security. - - -```python -import os - -from dotenv import load_dotenv - -from swarms.models import GPT4VisionAPI -from swarms.prompts.logistics import ( - Efficiency_Agent_Prompt, - Health_Security_Agent_Prompt, - Productivity_Agent_Prompt, - Quality_Control_Agent_Prompt, - Safety_Agent_Prompt, - Security_Agent_Prompt, - Sustainability_Agent_Prompt, -) -from swarms.structs import Agent - -# Load ENV -load_dotenv() -api_key = os.getenv("OPENAI_API_KEY") - -# GPT4VisionAPI -llm = GPT4VisionAPI(openai_api_key=api_key) - -# Image for analysis -factory_image = "factory_image1.jpg" - -# Initialize agents with respective prompts -health_security_agent = Agent( - llm=llm, - sop=Health_Security_Agent_Prompt, - max_loops=1, - multi_modal=True, -) - -# Quality control agent -quality_control_agent = Agent( - llm=llm, - sop=Quality_Control_Agent_Prompt, - max_loops=1, - multi_modal=True, -) - - -# Productivity Agent -productivity_agent = Agent( - llm=llm, - sop=Productivity_Agent_Prompt, - max_loops=1, - multi_modal=True, -) - -# Initiailize safety agent -safety_agent = Agent(llm=llm, sop=Safety_Agent_Prompt, max_loops=1, multi_modal=True) - -# Init the security agent -security_agent = Agent( - llm=llm, sop=Security_Agent_Prompt, max_loops=1, multi_modal=True -) - - -# Initialize sustainability agent -sustainability_agent = Agent( - llm=llm, - sop=Sustainability_Agent_Prompt, - max_loops=1, - multi_modal=True, -) - - -# Initialize efficincy agent -efficiency_agent = Agent( - llm=llm, - sop=Efficiency_Agent_Prompt, - max_loops=1, - multi_modal=True, -) - -# Run agents with respective tasks on the same image -health_analysis = health_security_agent.run( - "Analyze the safety of this factory", factory_image -) -quality_analysis = quality_control_agent.run( - "Examine product quality in the factory", factory_image -) -productivity_analysis = productivity_agent.run( - "Evaluate factory productivity", factory_image -) -safety_analysis = safety_agent.run( - "Inspect the factory's adherence to safety standards", - factory_image, -) -security_analysis = security_agent.run( - "Assess the factory's security measures and systems", - factory_image, -) -sustainability_analysis = sustainability_agent.run( - "Examine the factory's sustainability practices", factory_image -) -efficiency_analysis = efficiency_agent.run( - "Analyze the efficiency of the factory's manufacturing process", - factory_image, -) -``` ---- - - - - ## Build your own LLMs, Agents, and Swarms! ### Swarms Compliant Model Interface diff --git a/playground/demos/plant_biologist_swarm/bad_tomato.jpg b/bad_tomato.jpg similarity index 100% rename from playground/demos/plant_biologist_swarm/bad_tomato.jpg rename to bad_tomato.jpg diff --git a/json_log_cleanup.py b/json_log_cleanup.py index 463f582e..6daf1620 100644 --- a/json_log_cleanup.py +++ b/json_log_cleanup.py @@ -31,4 +31,4 @@ def cleanup_json_logs(name: str = None): # Call the function -cleanup_json_logs("arifacts_swarmm_o1") +cleanup_json_logs("agriculture_swarm") diff --git a/jamba_swarm/api.py b/playground/demos/jamba_swarm/api.py similarity index 100% rename from jamba_swarm/api.py rename to playground/demos/jamba_swarm/api.py diff --git a/jamba_swarm/api_schemas.py b/playground/demos/jamba_swarm/api_schemas.py similarity index 100% rename from jamba_swarm/api_schemas.py rename to playground/demos/jamba_swarm/api_schemas.py diff --git a/jamba_swarm/jamba_llm.py b/playground/demos/jamba_swarm/jamba_llm.py similarity index 100% rename from jamba_swarm/jamba_llm.py rename to playground/demos/jamba_swarm/jamba_llm.py diff --git a/jamba_swarm/main.ipynb b/playground/demos/jamba_swarm/main.ipynb similarity index 100% rename from jamba_swarm/main.ipynb rename to playground/demos/jamba_swarm/main.ipynb diff --git a/jamba_swarm/main.py b/playground/demos/jamba_swarm/main.py similarity index 100% rename from jamba_swarm/main.py rename to playground/demos/jamba_swarm/main.py diff --git a/jamba_swarm/prompts.py b/playground/demos/jamba_swarm/prompts.py similarity index 100% rename from jamba_swarm/prompts.py rename to playground/demos/jamba_swarm/prompts.py diff --git a/jamba_swarm/simple_jamba_swarm.py b/playground/demos/jamba_swarm/simple_jamba_swarm.py similarity index 100% rename from jamba_swarm/simple_jamba_swarm.py rename to playground/demos/jamba_swarm/simple_jamba_swarm.py diff --git a/playground/swarms_example.ipynb b/playground/swarms_example.ipynb index 734687f1..ece6101d 100644 --- a/playground/swarms_example.ipynb +++ b/playground/swarms_example.ipynb @@ -8,7 +8,7 @@ }, "outputs": [], "source": [ - "!pip3 install -U swarms" + "!pip3 install -U swarms python-dotenv" ] }, { @@ -49,7 +49,7 @@ "\n", "# Initialize the language model\n", "llm = OpenAIChat(\n", - " temperature=0.5, model_name=\"gpt-4\", openai_api_key=api_key, max_tokens=4000\n", + " temperature=0.5, openai_api_key=api_key, max_tokens=4000\n", ")\n", "\n", "\n", @@ -95,7 +95,8 @@ "metadata": {}, "outputs": [], "source": [ - "from swarms import Agent, ChromaDB, OpenAIChat\n", + "from swarms import Agent, OpenAIChat\n", + "from playground.memory.chromadb_example import ChromaDB\n", "\n", "# Making an instance of the ChromaDB class\n", "memory = ChromaDB(\n", diff --git a/pyproject.toml b/pyproject.toml index 46551071..ac7207ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "5.2.3" +version = "5.2.5" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] diff --git a/playground/demos/plant_biologist_swarm/swarm_workers_agents.py b/swarm_workers_agents.py similarity index 93% rename from playground/demos/plant_biologist_swarm/swarm_workers_agents.py rename to swarm_workers_agents.py index 2df24758..e35b0048 100644 --- a/playground/demos/plant_biologist_swarm/swarm_workers_agents.py +++ b/swarm_workers_agents.py @@ -28,7 +28,7 @@ api_key = os.environ.get("OPENAI_API_KEY") # llm = llm, -llm = GPT4o(max_tokens=200, openai_api_key=os.getenv("OPENAI_API_KEY")) +llm = GPT4o(max_tokens=3000, openai_api_key=os.getenv("OPENAI_API_KEY")) # Initialize Diagnoser Agent diagnoser_agent = Agent( @@ -42,6 +42,7 @@ diagnoser_agent = Agent( # saved_state_path="diagnoser.json", multi_modal=True, autosave=True, + streaming_on=True, ) # Initialize Harvester Agent @@ -56,6 +57,7 @@ harvester_agent = Agent( # saved_state_path="harvester.json", multi_modal=True, autosave=True, + streaming_on=True, ) # Initialize Growth Predictor Agent @@ -70,6 +72,7 @@ growth_predictor_agent = Agent( # saved_state_path="growth_predictor.json", multi_modal=True, autosave=True, + streaming_on=True, ) # Initialize Treatment Recommender Agent @@ -84,6 +87,7 @@ treatment_recommender_agent = Agent( # saved_state_path="treatment_recommender.json", multi_modal=True, autosave=True, + streaming_on=True, ) # Initialize Disease Detector Agent @@ -98,6 +102,7 @@ disease_detector_agent = Agent( # saved_state_path="disease_detector.json", multi_modal=True, autosave=True, + streaming_on=True, ) agents = [ diagnoser_agent, @@ -108,7 +113,7 @@ agents = [ ] task = "Conduct a diagnosis on the plants's symptoms, this wasn't grown in dirt, it grew from hydroponics" -img = "tomato.jpg" +img = "bad_tomato.jpg" loop = 0 for i in range(len(agents)): From 995bb7d317d413a88e69a3cfbf8599c5d93d9a63 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Wed, 26 Jun 2024 08:46:21 -0700 Subject: [PATCH 3/5] [FEAT][API.py] --- README.md | 35 +----- json_log_cleanup.py | 2 +- new_workflow_concurrent.py | 110 ++++++++++++++++++ .../multion_examples/ multion_example.py | 4 +- .../heinz_docs/Geo Finance Frag and.pdf | Bin 0 -> 948654 bytes .../heinz_docs/Geo Frag costs.pdf | Bin 0 -> 245643 bytes .../GeoEconomic Literature IMF 21 June 23.pdf | Bin 0 -> 1515751 bytes .../heinz_docs/Investment and FDI.pdf | Bin 0 -> 1105343 bytes .../heinz_docs/PIIE Econ war uk.pdf | Bin 0 -> 514978 bytes .../heinz_docs/duplicate not needed.pdf | Bin 0 -> 1515751 bytes .../heinz_docs/wpiea2021069-print-pdf.pdf | Bin 0 -> 1366320 bytes .../heinz_docs/wpiea2023073-print-pdf.pdf | Bin 0 -> 1234543 bytes .../rag_doc_agent.py | 101 ++++++++++++++++ sky_serve.yaml | 84 +++++++++++++ swarms/structs/__init__.py | 2 + swarms/structs/task.py | 2 +- .../task_example.py => task_example.py | 2 +- 17 files changed, 302 insertions(+), 40 deletions(-) create mode 100644 new_workflow_concurrent.py rename multion_example.py => playground/agents/multion_examples/ multion_example.py (93%) create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/Geo Finance Frag and.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/Geo Frag costs.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/GeoEconomic Literature IMF 21 June 23.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/Investment and FDI.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/PIIE Econ war uk.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/duplicate not needed.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/wpiea2021069-print-pdf.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/heinz_docs/wpiea2023073-print-pdf.pdf create mode 100644 playground/swarms/geo_economic_forecast_docs/rag_doc_agent.py create mode 100644 sky_serve.yaml rename playground/structs/task_example.py => task_example.py (95%) diff --git a/README.md b/README.md index ac338dbb..90c2cfb9 100644 --- a/README.md +++ b/README.md @@ -971,6 +971,7 @@ load_dotenv() # Load environment variables llm = OpenAIChat(openai_api_key=os.getenv("OPENAI_API_KEY")) + agent = Agent(llm=llm, max_loops=1) # Create a workflow @@ -988,40 +989,6 @@ workflow.add(tasks=[task1, task2, task3]) workflow.run() ``` -### `RecursiveWorkflow` -`RecursiveWorkflow` will keep executing the tasks until a specific token like is located inside the text! - -```python -import os - -from dotenv import load_dotenv - -from swarms import Agent, OpenAIChat, RecursiveWorkflow, Task - -# Load environment variables from .env file -load_dotenv() - -# Load environment variables -llm = OpenAIChat(openai_api_key=os.getenv("OPENAI_API_KEY")) -agent = Agent(llm=llm, max_loops=1) - -# Create a workflow -workflow = RecursiveWorkflow(stop_token="") - -# Create tasks -task1 = Task(agent, "What's the weather in miami") -task2 = Task(agent, "What's the weather in new york") -task3 = Task(agent, "What's the weather in london") - -# Add tasks to the workflow -workflow.add(task1) -workflow.add(task2) -workflow.add(task3) - -# Run the workflow -workflow.run() -``` - ### `SwarmNetwork` diff --git a/json_log_cleanup.py b/json_log_cleanup.py index 6daf1620..bce926ee 100644 --- a/json_log_cleanup.py +++ b/json_log_cleanup.py @@ -31,4 +31,4 @@ def cleanup_json_logs(name: str = None): # Call the function -cleanup_json_logs("agriculture_swarm") +cleanup_json_logs("heinz_swarm") diff --git a/new_workflow_concurrent.py b/new_workflow_concurrent.py new file mode 100644 index 00000000..81595386 --- /dev/null +++ b/new_workflow_concurrent.py @@ -0,0 +1,110 @@ +import threading +from dataclasses import dataclass, field +from typing import Callable, List, Optional, Any + +from swarms.utils.logger import logger +from swarms.structs.agent import Agent +from swarms.structs.base_workflow import BaseWorkflow +from swarms import OpenAIChat +import os + + +@dataclass +class ConcurrentWorkflow(BaseWorkflow): + """ + ConcurrentWorkflow class for running a set of tasks concurrently using N number of autonomous agents. + + Args: + max_workers (int): The maximum number of workers to use for the threading.Thread. + autosave (bool): Whether to save the state of the workflow to a file. Default is False. + saved_state_filepath (str): The filepath to save the state of the workflow to. Default is "runs/concurrent_workflow.json". + print_results (bool): Whether to print the results of each task. Default is False. + return_results (bool): Whether to return the results of each task. Default is False. + use_processes (bool): Whether to use processes instead of threads. Default is False. + + Examples: + >>> from swarms.models import OpenAIChat + >>> from swarms.structs import ConcurrentWorkflow + >>> llm = OpenAIChat(openai_api_key="") + >>> workflow = ConcurrentWorkflow(max_workers=5, agents=[llm]) + >>> workflow.run() + """ + + max_loops: int = 1 + max_workers: int = 5 + autosave: bool = False + agents: List[Agent] = field(default_factory=list) + saved_state_filepath: Optional[str] = "runs/concurrent_workflow.json" + print_results: bool = True # Modified: Set print_results to True + return_results: bool = False + stopping_condition: Optional[Callable] = None + + def run(self, task: Optional[str] = None, *args, **kwargs) -> Optional[List[Any]]: + """ + Executes the tasks in parallel using multiple threads. + + Args: + task (Optional[str]): A task description if applicable. + *args: Additional arguments. + **kwargs: Additional keyword arguments. + + Returns: + Optional[List[Any]]: A list of the results of each task, if return_results is True. Otherwise, returns None. + """ + loop = 0 + results = [] + + while loop < self.max_loops: + if not self.agents: + logger.warning("No agents found in the workflow.") + break + + threads = [threading.Thread(target=self.execute_agent, args=(agent, task)) for agent in self.agents] + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + if self.return_results: + results.extend([thread.result for thread in threads if hasattr(thread, 'result')]) + + loop += 1 + + if self.stopping_condition and self.stopping_condition(results): + break + + return results if self.return_results else None + + def list_agents(self): + """Prints a list of the agents in the workflow.""" + for agent in self.agents: + logger.info(agent) + + def save(self): + """Saves the state of the workflow to a file.""" + self.save_state(self.saved_state_filepath) + + def execute_agent(self, agent: Agent, task: Optional[str] = None, *args, **kwargs): + try: + result = agent.run(task, *args, **kwargs) + if self.print_results: + logger.info(f"Agent {agent}: {result}") + if self.return_results: + return result + except Exception as e: + logger.error(f"Agent {agent} generated an exception: {e}") + + + +api_key = os.environ["OPENAI_API_KEY"] + +# Model +swarm = ConcurrentWorkflow( + agents = [Agent(llm=OpenAIChat(openai_api_key=api_key, max_tokens=4000,), max_loops=4, dashboard=False)], +) + + +# Run the workflow +swarm.run("Generate a report on the top 3 biggest expenses for small businesses and how businesses can save 20%") \ No newline at end of file diff --git a/ multion_example.py b/playground/agents/multion_examples/ multion_example.py similarity index 93% rename from multion_example.py rename to playground/agents/multion_examples/ multion_example.py index 6a3c22ed..c6781027 100644 --- a/ multion_example.py +++ b/playground/agents/multion_examples/ multion_example.py @@ -7,9 +7,7 @@ def run_model(api_key): model = MultiOnAgent( api_key=api_key, max_steps=500, url="https://x.com" ) - out = model.run( - "" - ) + out = model.run("") print(out) diff --git a/playground/swarms/geo_economic_forecast_docs/heinz_docs/Geo Finance Frag and.pdf b/playground/swarms/geo_economic_forecast_docs/heinz_docs/Geo Finance Frag and.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5ac29c7045fdb047a7e234a5ecb72d0b0709871f GIT binary patch literal 948654 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 z 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 tv6-T&1)Sh1Y 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