diff --git a/.env.example b/.env.example index 8ae19111..7776faeb 100644 --- a/.env.example +++ b/.env.example @@ -5,46 +5,16 @@ AI21_API_KEY="your_api_key_here" COHERE_API_KEY="your_api_key_here" ALEPHALPHA_API_KEY="your_api_key_here" HUGGINFACEHUB_API_KEY="your_api_key_here" -STABILITY_API_KEY="your_api_key_here" -POSTHOG_API_KEY="" -POSTHOG_HOST="" -WOLFRAM_ALPHA_APPID="your_wolfram_alpha_appid_here" -ZAPIER_NLA_API_KEY="your_zapier_nla_api_key_here" EVAL_PORT=8000 MODEL_NAME="gpt-4" -CELERY_BROKER_URL="redis://localhost:6379" -SERVER="http://localhost:8000" USE_GPU=True PLAYGROUND_DIR="playground" LOG_LEVEL="INFO" BOT_NAME="Orca" - -WINEDB_HOST="your_winedb_host_here" -WINEDB_PASSWORD="your_winedb_password_here" -BING_SEARCH_URL="your_bing_search_url_here" - -BING_SUBSCRIPTION_KEY="your_bing_subscription_key_here" -SERPAPI_API_KEY="your_serpapi_api_key_here" -IFTTTKey="your_iftttkey_here" - -BRAVE_API_KEY="your_brave_api_key_here" -SPOONACULAR_KEY="your_spoonacular_key_here" HF_API_KEY="your_huggingface_api_key_here" -USE_TELEMTRY=True - - -REDIS_HOST= -REDIS_PORT= - -#dbs -PINECONE_API_KEY="" -BING_COOKIE="" -PSG_CONNECTION_STRING="" -GITHUB_USERNAME="" -GITHUB_REPO_NAME="" -GITHUB_TOKEN="" -USE_TELEMETRY=True \ No newline at end of file +USE_TELEMETRY=True +AGENTOPS_API_KEY="" diff --git a/example.py b/example.py index 59ec9392..b6f8d712 100644 --- a/example.py +++ b/example.py @@ -1,27 +1,53 @@ 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="Transcript Generator", - system_prompt="Generate a transcript for a youtube video on what swarms are!", - agent_description=( - "Generate a transcript for a youtube video on what swarms" " are!" - ), + 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="auto", + max_loops=1, autosave=True, + dynamic_temperature_enabled=True, dashboard=False, - streaming_on=True, verbose=True, - stopping_token="", - interactive=True, - state_save_file_type="json", - saved_state_path="transcript_generator.json", + # interactive=True, # Set to False to disable interactive mode + # stopping_token="", + # saved_state_path="accounting_agent.json", + tools=[calculate_profit, generate_report], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", ) -# Run the Agent on a task -out = agent.run( - "Generate a transcript for a youtube video on what swarms are!" +agent.run( + "We're the Swarm Corporation, our total revenue is $100,000 and our total expenses are $50,000." ) -print(out) diff --git a/playground/agents/agent_ops.py b/playground/agents/agent_ops.py new file mode 100644 index 00000000..5d9bf467 --- /dev/null +++ b/playground/agents/agent_ops.py @@ -0,0 +1,25 @@ +from swarms import Agent, OpenAIChat + + +# Initialize the agent +agent = Agent( + agent_name="Transcript Generator", + system_prompt="Generate a transcript for a youtube video on what swarms are!", + agent_description=( + "Generate a transcript for a youtube video on what swarms" " are!" + ), + llm=OpenAIChat(), + max_loops=1, + autosave=True, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + interactive=False, + state_save_file_type="json", + saved_state_path="transcript_generator.json", + agent_ops_on=True, +) + +# Run the Agent on a task +agent.run("Generate a transcript for a youtube video on what swarms are!") diff --git a/playground/structs/multi_agent_collaboration/agent_delegation.py b/playground/structs/multi_agent_collaboration/agent_delegation.py new file mode 100644 index 00000000..91ce1eb3 --- /dev/null +++ b/playground/structs/multi_agent_collaboration/agent_delegation.py @@ -0,0 +1,82 @@ +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}." + + +def account_agent(task: str = None): + """ + delegate a task to an agent! + + Task: str (What task to give to an agent) + + """ + agent = Agent( + agent_name="Finance Agent", + system_prompt="You're the Finance 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, + # interactive=True, # Set to False to disable interactive mode + # stopping_token="", + # saved_state_path="accounting_agent.json", + # tools=[calculate_profit, generate_report], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", + ) + + out = agent.run(task) + return out + + +# 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, + # interactive=True, # Set to False to disable interactive mode + # stopping_token="", + # saved_state_path="accounting_agent.json", + tools=[account_agent], + # docs_folder="docs", + # pdf_path="docs/accounting_agent.pdf", +) + +agent.run( + "Delegate a task to the accounting agent: what are the best ways to read cashflow statements" +) diff --git a/pyproject.toml b/pyproject.toml index 27af93cf..34d050c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "5.1.6" +version = "5.1.8" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] diff --git a/scripts/cleanup/json_log_cleanup.py b/scripts/cleanup/json_log_cleanup.py index b376ea74..8e3ee53a 100644 --- a/scripts/cleanup/json_log_cleanup.py +++ b/scripts/cleanup/json_log_cleanup.py @@ -31,4 +31,4 @@ def cleanup_json_logs(name: str = None): # Call the function -cleanup_json_logs("artifacts_seven") +cleanup_json_logs("artifacts_three") diff --git a/swarms/models/__init__.py b/swarms/models/__init__.py index a9b26c7c..e7823eff 100644 --- a/swarms/models/__init__.py +++ b/swarms/models/__init__.py @@ -3,15 +3,17 @@ from swarms.models.base_llm import BaseLLM # noqa: E402 from swarms.models.base_multimodal_model import BaseMultiModalModel from swarms.models.fuyu import Fuyu # noqa: E402 from swarms.models.gpt4_vision_api import GPT4VisionAPI # noqa: E402 +from swarms.models.gpt_o import GPT4o from swarms.models.huggingface import HuggingfaceLLM # noqa: E402 from swarms.models.idefics import Idefics # noqa: E402 from swarms.models.kosmos_two import Kosmos # noqa: E402 from swarms.models.layoutlm_document_qa import LayoutLMDocumentQA +from swarms.models.llama3_hosted import llama3Hosted from swarms.models.llava import LavaMultiModal # noqa: E402 - from swarms.models.nougat import Nougat # noqa: E402 -from swarms.models.palm import GooglePalm as Palm # noqa: E402 +from swarms.models.openai_embeddings import OpenAIEmbeddings from swarms.models.openai_tts import OpenAITTS # noqa: E402 +from swarms.models.palm import GooglePalm as Palm # noqa: E402 from swarms.models.popular_llms import Anthropic as Anthropic from swarms.models.popular_llms import ( AzureOpenAILLM as AzureOpenAI, @@ -19,15 +21,15 @@ from swarms.models.popular_llms import ( from swarms.models.popular_llms import ( CohereChat as Cohere, ) +from swarms.models.popular_llms import OctoAIChat from swarms.models.popular_llms import ( OpenAIChatLLM as OpenAIChat, ) from swarms.models.popular_llms import ( OpenAILLM as OpenAI, ) -from swarms.models.popular_llms import OctoAIChat -from swarms.models.qwen import QwenVLMultiModal # noqa: E402 from swarms.models.popular_llms import ReplicateChat as Replicate +from swarms.models.qwen import QwenVLMultiModal # noqa: E402 from swarms.models.sampling_params import SamplingParams, SamplingType from swarms.models.together import TogetherLLM # noqa: E402 from swarms.models.types import ( # noqa: E402 @@ -38,9 +40,6 @@ from swarms.models.types import ( # noqa: E402 VideoModality, ) from swarms.models.vilt import Vilt # noqa: E402 -from swarms.models.openai_embeddings import OpenAIEmbeddings -from swarms.models.llama3_hosted import llama3Hosted -from swarms.models.gpt_o import GPT4o __all__ = [ "BaseEmbeddingModel", diff --git a/swarms/models/distilled_whisperx.py b/swarms/models/distilled_whisperx.py deleted file mode 100644 index bd2bbcf3..00000000 --- a/swarms/models/distilled_whisperx.py +++ /dev/null @@ -1,182 +0,0 @@ -import asyncio -import os -import time -from functools import wraps -from typing import Union - -import torch -from termcolor import colored -from transformers import ( - AutoModelForSpeechSeq2Seq, - AutoProcessor, - pipeline, -) - - -def async_retry(max_retries=3, exceptions=(Exception,), delay=1): - """ - A decorator for adding retry logic to async functions. - :param max_retries: Maximum number of retries before giving up. - :param exceptions: A tuple of exceptions to catch and retry on. - :param delay: Delay between retries. - """ - - def decorator(func): - @wraps(func) - async def wrapper(*args, **kwargs): - retries = max_retries - while retries: - try: - return await func(*args, **kwargs) - except exceptions as e: - retries -= 1 - if retries <= 0: - raise - print( - f"Retry after exception: {e}, Attempts" - f" remaining: {retries}" - ) - await asyncio.sleep(delay) - - return wrapper - - return decorator - - -class DistilWhisperModel: - """ - This class encapsulates the Distil-Whisper model for English speech recognition. - It allows for both synchronous and asynchronous transcription of short and long-form audio. - - Args: - model_id: The model ID to use. Defaults to "distil-whisper/distil-large-v2". - - - Attributes: - device: The device to use for inference. - torch_dtype: The torch data type to use for inference. - model_id: The model ID to use. - model: The model instance. - processor: The processor instance. - - Usage: - model_wrapper = DistilWhisperModel() - transcription = model_wrapper('path/to/audio.mp3') - - # For async usage - transcription = asyncio.run(model_wrapper.async_transcribe('path/to/audio.mp3')) - """ - - def __init__(self, model_id="distil-whisper/distil-large-v2"): - self.device = "cuda:0" if torch.cuda.is_available() else "cpu" - self.torch_dtype = ( - torch.float16 if torch.cuda.is_available() else torch.float32 - ) - self.model_id = model_id - self.model = AutoModelForSpeechSeq2Seq.from_pretrained( - model_id, - torch_dtype=self.torch_dtype, - low_cpu_mem_usage=True, - use_safetensors=True, - ).to(self.device) - self.processor = AutoProcessor.from_pretrained(model_id) - - def __call__(self, inputs: Union[str, dict]): - return self.transcribe(inputs) - - def transcribe(self, inputs: Union[str, dict]): - """ - Synchronously transcribe the given audio input using the Distil-Whisper model. - :param inputs: A string representing the file path or a dict with audio data. - :return: The transcribed text. - """ - pipe = pipeline( - "automatic-speech-recognition", - model=self.model, - tokenizer=self.processor.tokenizer, - feature_extractor=self.processor.feature_extractor, - max_new_tokens=128, - torch_dtype=self.torch_dtype, - device=self.device, - ) - - return pipe(inputs)["text"] - - @async_retry() - async def async_transcribe(self, inputs: Union[str, dict]): - """ - Asynchronously transcribe the given audio input using the Distil-Whisper model. - :param inputs: A string representing the file path or a dict with audio data. - :return: The transcribed text. - """ - loop = asyncio.get_event_loop() - return await loop.run_in_executor(None, self.transcribe, inputs) - - def real_time_transcribe(self, audio_file_path, chunk_duration=5): - """ - Simulates real-time transcription of an audio file, processing and printing results - in chunks with colored output for readability. - - :param audio_file_path: Path to the audio file to be transcribed. - :param chunk_duration: Duration in seconds of each audio chunk to be processed. - """ - if not os.path.isfile(audio_file_path): - print(colored("The audio file was not found.", "red")) - return - - # Assuming `chunk_duration` is in seconds and `processor` can handle chunk-wise processing - try: - with torch.no_grad(): - # Load the whole audio file, but process and transcribe it in chunks - audio_input = self.processor.audio_file_to_array( - audio_file_path - ) - sample_rate = audio_input.sampling_rate - len(audio_input.array) / sample_rate - chunks = [ - audio_input.array[i : i + sample_rate * chunk_duration] - for i in range( - 0, - len(audio_input.array), - sample_rate * chunk_duration, - ) - ] - - print( - colored("Starting real-time transcription...", "green") - ) - - for i, chunk in enumerate(chunks): - # Process the current chunk - processed_inputs = self.processor( - chunk, - sampling_rate=sample_rate, - return_tensors="pt", - padding=True, - ) - processed_inputs = processed_inputs.input_values.to( - self.device - ) - - # Generate transcription for the chunk - logits = self.model.generate(processed_inputs) - transcription = self.processor.batch_decode( - logits, skip_special_tokens=True - )[0] - - # Print the chunk's transcription - print( - colored(f"Chunk {i+1}/{len(chunks)}: ", "yellow") - + transcription - ) - - # Wait for the chunk's duration to simulate real-time processing - time.sleep(chunk_duration) - - except Exception as e: - print( - colored( - f"An error occurred during transcription: {e}", - "red", - ) - ) diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 62c83c87..9bb732c5 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -1,3 +1,14 @@ +from swarms.schemas.plan import Plan +from swarms.schemas.schemas import ( + Artifact, + ArtifactUpload, + StepInput, + StepOutput, + StepRequestBody, + TaskInput, + TaskRequestBody, +) +from swarms.schemas.step import Step from swarms.structs.agent import Agent from swarms.structs.agent_job import AgentJob from swarms.structs.agent_process import ( @@ -11,6 +22,7 @@ from swarms.structs.base_workflow import BaseWorkflow from swarms.structs.concurrent_workflow import ConcurrentWorkflow from swarms.structs.conversation import Conversation from swarms.structs.groupchat import GroupChat +from swarms.structs.hiearchical_swarm import HiearchicalSwarm from swarms.structs.majority_voting import ( MajorityVoting, majority_voting, @@ -26,21 +38,13 @@ from swarms.structs.multi_process_workflow import ( from swarms.structs.multi_threaded_workflow import ( MultiThreadedWorkflow, ) -from swarms.schemas.plan import Plan from swarms.structs.rearrange import AgentRearrange, rearrange from swarms.structs.recursive_workflow import RecursiveWorkflow from swarms.structs.round_robin import RoundRobinSwarm -from swarms.schemas.schemas import ( - Artifact, - ArtifactUpload, - StepInput, - StepOutput, - StepRequestBody, - TaskInput, - TaskRequestBody, -) from swarms.structs.sequential_workflow import SequentialWorkflow -from swarms.schemas.step import Step + +# New Swarms +from swarms.structs.swarm_load_balancer import AgentLoadBalancer from swarms.structs.swarm_net import SwarmNetwork from swarms.structs.swarming_architectures import ( broadcast, @@ -83,10 +87,6 @@ from swarms.structs.yaml_model import ( pydantic_type_to_yaml_schema, ) -# New Swarms -from swarms.structs.swarm_load_balancer import AgentLoadBalancer -from swarms.structs.hiearchical_swarm import HiearchicalSwarm - __all__ = [ "Agent", "AgentJob", diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 79923198..30fee9a9 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -35,13 +35,13 @@ from swarms.utils.pdf_to_text import pdf_to_text from swarms.tools.py_func_to_openai_func_str import ( get_openai_function_schema_from_func, ) -from swarms.tools.func_calling_executor import openai_tool_executor from swarms.structs.base_structure import BaseStructure from swarms.prompts.tools import tool_sop_prompt from swarms.tools.func_calling_utils import ( pydantic_model_to_json_str, prepare_output_for_output_model, ) +from swarms.tools.tool_parse_exec import parse_and_execute_json # Utils @@ -285,6 +285,7 @@ class Agent(BaseStructure): device: str = None, custom_planning_prompt: str = None, memory_chunk_size: int = 2000, + agent_ops_on: bool = False, *args, **kwargs, ): @@ -366,6 +367,7 @@ class Agent(BaseStructure): self.rules = rules self.custom_tools_prompt = custom_tools_prompt self.memory_chunk_size = memory_chunk_size + self.agent_ops_on = agent_ops_on # Name self.name = agent_name @@ -376,12 +378,12 @@ class Agent(BaseStructure): self.answer = "" # The max_loops will be set dynamically if the dynamic_loop - if self.dynamic_loops: + if self.dynamic_loops is True: logger.info("Dynamic loops enabled") self.max_loops = "auto" # If multimodal = yes then set the sop to the multimodal sop - if self.multi_modal: + if self.multi_modal is True: self.sop = MULTI_MODAL_AUTO_AGENT_SYSTEM_PROMPT_1 # Memory @@ -403,54 +405,26 @@ class Agent(BaseStructure): ) # If the docs exist then ingest the docs - if self.docs is not None: + if exists(self.docs): self.ingest_docs(self.docs) # If docs folder exists then get the docs from docs folder - if self.docs_folder: + if exists(self.docs_folder): self.get_docs_from_doc_folders() - # If tokenizer and context length exists then: - # if self.tokenizer and self.context_length: - # self.truncate_history() - - # If verbose is enabled then set the logger level to info - # if verbose is not False: - # logger.setLevel(logging.INFO) - if tools is not None: - + logger.info( + "Tools provided make sure the functions have documentation ++ type hints, otherwise tool execution won't be reliable." + ) # 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)}") - logger.info( - "Tools provided, Automatically converting to OpenAI function" - ) - - # Now the names of the tools - for tool in tools: - logger.info(f"Tool: {tool.__name__}") # Transform the tools into an openai schema - for tool in tools: - - # Transform the tool into a openai function calling schema - tool_schema_list = get_openai_function_schema_from_func( - tool, - name=tool.__name__, - description=tool.__doc__, - ) - - # Transform the dictionary to a string - tool_schema_list = json.dumps(tool_schema_list, indent=4) - # print(tool_schema_list) - - # Add the tool schema to the short memory - self.short_memory.add( - role="System", content=tool_schema_list - ) + self.convert_tool_into_openai_schema() # Now create a function calling map for every tools self.function_map = {tool.__name__: tool for tool in tools} @@ -522,6 +496,23 @@ class Agent(BaseStructure): # If the device is not provided then get the device data + # if agent ops is enabled then import agent ops + if agent_ops_on is True: + try: + from swarms.utils.agent_ops_check import ( + try_import_agentops, + ) + + # Try importing agent ops + logger.info( + "Agent Ops Initializing, ensure that you have the agentops API key and the pip package installed." + ) + try_import_agentops() + except ImportError: + logger.error( + "Could not import agentops, try installing agentops: $ pip3 install agentops" + ) + def set_system_prompt(self, system_prompt: str): """Set the system prompt""" self.system_prompt = system_prompt @@ -751,7 +742,7 @@ class Agent(BaseStructure): loop_count = 0 # Clear the short memory - # response = None + response = None while self.max_loops == "auto" or loop_count < self.max_loops: loop_count += 1 @@ -807,7 +798,17 @@ class Agent(BaseStructure): # Check if tools is not None if self.tools is not None: - self.parse_and_execute_tools(response) + # self.parse_and_execute_tools(response) + tool_call_output = parse_and_execute_json( + self.tools, response, parse_md=True + ) + logger.info( + f"Tool Call Output: {tool_call_output}" + ) + self.short_memory.add( + role=self.agent_name, + content=tool_call_output, + ) if self.code_interpreter is not False: self.code_interpreter_execution(response) @@ -916,6 +917,8 @@ class Agent(BaseStructure): print(f"Response after output model: {response}") # print(response) + if self.agent_ops_on is True: + self.check_end_session_agentops() return response except Exception as error: @@ -937,7 +940,7 @@ class Agent(BaseStructure): def parse_and_execute_tools(self, response: str, *args, **kwargs): # Extract json from markdown - response = extract_code_from_markdown(response) + # response = extract_code_from_markdown(response) # Try executing the tool if self.execute_tool is not False: @@ -945,11 +948,8 @@ class Agent(BaseStructure): logger.info("Executing tool...") # try to Execute the tool and return a string - out = openai_tool_executor( - tools=response, - function_map=self.function_map, - *args, - **kwargs, + out = parse_and_execute_json( + self.tools, response, parse_md=True, *args, **kwargs ) print(f"Tool Output: {out}") @@ -980,6 +980,8 @@ class Agent(BaseStructure): Returns: str: The agent history prompt """ + logger.info("Querying long term memory database") + # Query the long term memory database ltr = self.long_term_memory.query(query, *args, **kwargs) ltr = str(ltr) @@ -987,7 +989,6 @@ class Agent(BaseStructure): # Retrieve only the chunk size of the memory ltr = retrieve_tokens(ltr, self.memory_chunk_size) - print(len(ltr)) # print(f"Long Term Memory Query: {ltr}") return ltr @@ -1633,3 +1634,59 @@ class Agent(BaseStructure): "red", ) ) + + def check_end_session_agentops(self): + if self.agent_ops_on is True: + try: + from swarms.utils.agent_ops_check import ( + end_session_agentops, + ) + + # Try ending the session + return end_session_agentops() + except ImportError: + logger.error( + "Could not import agentops, try installing agentops: $ pip3 install agentops" + ) + + def convert_tool_into_openai_schema(self): + # Transform the tools into an openai schema + try: + for tool in self.tools: + # Transform the tool into a openai function calling schema + name = tool.__name__ + description = tool.__doc__ + + try: + logger.info( + "Tool -> OpenAI Schema Process Starting Now." + ) + tool_schema_list = ( + get_openai_function_schema_from_func( + tool, name=name, description=description + ) + ) + + # Transform the dictionary to a string + tool_schema_list = json.dumps( + tool_schema_list, indent=4 + ) + + # Add the tool schema to the short memory + self.short_memory.add( + role="System", content=tool_schema_list + ) + + logger.info( + f"Conversion process successful, the tool {name} has been integrated with the agent successfully." + ) + except Exception as error: + logger.info( + f"There was an error converting your tool into a OpenAI certified function calling schema. Add documentation and type hints: {error}" + ) + raise error + except Exception as error: + logger.info( + f"Error detected: {error} make sure you have inputted a callable and that it has documentation as docstrings" + ) + raise error diff --git a/swarms/structs/base_structure.py b/swarms/structs/base_structure.py index 9e5833bf..48957ac0 100644 --- a/swarms/structs/base_structure.py +++ b/swarms/structs/base_structure.py @@ -68,7 +68,7 @@ class BaseStructure: self, name: Optional[str] = None, description: Optional[str] = None, - save_metadata: bool = True, + save_metadata_on: bool = True, save_artifact_path: Optional[str] = "./artifacts", save_metadata_path: Optional[str] = "./metadata", save_error_path: Optional[str] = "./errors", @@ -76,7 +76,7 @@ class BaseStructure: super().__init__() self.name = name self.description = description - self.save_metadata = save_metadata + self.save_metadata_on = save_metadata_on self.save_artifact_path = save_artifact_path self.save_metadata_path = save_metadata_path self.save_error_path = save_error_path diff --git a/swarms/structs/base_swarm.py b/swarms/structs/base_swarm.py index bd71c60b..2159bdd5 100644 --- a/swarms/structs/base_swarm.py +++ b/swarms/structs/base_swarm.py @@ -1,3 +1,4 @@ +import uuid import asyncio import json from abc import ABC @@ -17,11 +18,12 @@ from swarms.structs.agent import Agent from swarms.structs.conversation import Conversation from swarms.utils.loguru_logger import logger from swarms.structs.omni_agent_types import AgentType +from swarms.memory.base_vectordb import BaseVectorDatabase class BaseSwarm(ABC): """ - Abstract Swarm Class for multi-agent systems + Base Swarm Class for all multi-agent systems Attributes: agents (List[Agent]): A list of agents @@ -83,6 +85,10 @@ class BaseSwarm(ABC): stopping_function: Optional[Callable] = None, stopping_condition: Optional[str] = "stop", stopping_condition_args: Optional[Dict] = None, + agentops_on: Optional[bool] = False, + speaker_selection_func: Optional[Callable] = None, + rules: Optional[str] = None, + collective_memory_system: Optional[BaseVectorDatabase] = False, *args, **kwargs, ): @@ -100,10 +106,28 @@ class BaseSwarm(ABC): self.stopping_function = stopping_function self.stopping_condition = stopping_condition self.stopping_condition_args = stopping_condition_args + self.agentops_on = agentops_on + self.speaker_selection_func = speaker_selection_func + self.rules = rules + self.collective_memory_system = collective_memory_system + + # Ensure that agents is exists + if self.agents is None: + raise ValueError("Agents must be provided.") + + # Ensure that agents is a list + if not isinstance(self.agents, list): + logger.error("Agents must be a list.") + raise TypeError("Agents must be a list.") + + # Ensure that agents is not empty + if len(self.agents) == 0: + logger.error("Agents list must not be empty.") + raise ValueError("Agents list must not be empty.") # Initialize conversation self.conversation = Conversation( - time_enabled=True, *args, **kwargs + time_enabled=True, rules=self.rules, *args, **kwargs ) # Handle callbacks @@ -116,12 +140,6 @@ class BaseSwarm(ABC): if autosave: self.save_to_json(metadata_filename) - # Handle logging - if self.agents: - logger.info( - f"Swarm initialized with {len(self.agents)} agents" - ) - # Handle stopping function if stopping_function is not None: if not callable(stopping_function): @@ -139,8 +157,27 @@ class BaseSwarm(ABC): self.stopping_condition_args = stopping_condition_args self.stopping_condition = stopping_condition + # If agentops is enabled, try to import agentops + if agentops_on is True: + for agent in self.agents: + agent.agent_ops_on = True + + # Handle speaker selection function + if speaker_selection_func is not None: + if not callable(speaker_selection_func): + raise TypeError( + "Speaker selection function must be callable." + ) + self.speaker_selection_func = speaker_selection_func + + # Agents dictionary with agent name as key and agent object as value + self.agents_dict = { + agent.agent_name: agent for agent in self.agents + } + def communicate(self): """Communicate with the swarm through the orchestrator, protocols, and the universal communication layer""" + ... def run(self): """Run the swarm""" @@ -221,6 +258,21 @@ class BaseSwarm(ABC): return agent return None + def self_find_agent_by_id(self, id: uuid.UUID): + """ + Find an agent by its id. + + Args: + id (str): The id of the agent to find. + + Returns: + Agent: The Agent object if found, None otherwise. + """ + for agent in self.agents: + if agent.id == id: + return agent + return None + def agent_exists(self, name: str): """ Check if an agent exists in the swarm. @@ -391,24 +443,6 @@ class BaseSwarm(ABC): """ return self.batched_run(tasks, *args, **kwargs) - def reset_all_agents(self): - """Reset all agents - - Returns: - - """ - for agent in self.agents: - agent.reset() - - def select_agent(self, agent_id: str): - """ - Select an agent through their id - """ - # Find agent with id - for agent in self.agents: - if agent.id == agent_id: - return agent - def select_agent_by_name(self, agent_name: str): """ Select an agent through their name @@ -650,3 +684,6 @@ class BaseSwarm(ABC): def __contains__(self, value): return value in self.agents + + def __eq__(self, other): + return self.__dict__ == other.__dict__ diff --git a/swarms/tools/tool_parse_exec.py b/swarms/tools/tool_parse_exec.py new file mode 100644 index 00000000..cbcb70dd --- /dev/null +++ b/swarms/tools/tool_parse_exec.py @@ -0,0 +1,48 @@ +from typing import List +import json +import loguru +from swarms.utils.parse_code import extract_code_from_markdown + + +def parse_and_execute_json( + functions: List[callable] = None, + json_string: str = None, + parse_md: bool = False, +): + """ + Parses and executes a JSON string containing function name and parameters. + + Args: + functions (List[callable]): A list of callable functions. + json_string (str): The JSON string to parse and execute. + 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. + + """ + if parse_md: + json_string = extract_code_from_markdown(json_string) + + try: + # Create a dictionary that maps function names to functions + function_dict = {func.__name__: func for func in functions} + + 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 + except Exception as e: + loguru.logger.error(f"Error: {e}") + return None diff --git a/swarms/utils/agent_ops_check.py b/swarms/utils/agent_ops_check.py new file mode 100644 index 00000000..4e1df6e7 --- /dev/null +++ b/swarms/utils/agent_ops_check.py @@ -0,0 +1,28 @@ +from swarms.utils.loguru_logger import logger +import os +from dotenv import load_dotenv + + +def try_import_agentops(*args, **kwargs): + try: + load_dotenv() + logger.info("Trying to import agentops") + import agentops + + agentops.init(os.getenv("AGENTOPS_API_KEY"), *args, **kwargs) + + return "agentops imported successfully." + except ImportError: + logger.error("Could not import agentops") + + +def end_session_agentops(): + try: + logger.info("Trying to end session") + import agentops + + agentops.end_session("Success") + return "Session ended successfully." + except ImportError: + logger.error("Could not import agentops") + return "Could not end session."