From 5ba60c165235e1655338609a27919f660c00ff2d Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Wed, 31 Jan 2024 09:00:35 +0400 Subject: [PATCH 01/15] Added message file uploads to gradio --- agency_swarm/agency/agency.py | 35 +++++++++++++++++++++++++++++- agency_swarm/tools/oai/__init__.py | 2 ++ tests/demos/demo_gradio.py | 21 +++++++++--------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/agency_swarm/agency/agency.py b/agency_swarm/agency/agency.py index 8148aee7..e4a8540f 100644 --- a/agency_swarm/agency/agency.py +++ b/agency_swarm/agency/agency.py @@ -1,6 +1,7 @@ import inspect import json import os +import shutil import uuid from enum import Enum from typing import List, TypedDict, Callable, Any, Dict, Literal @@ -132,9 +133,37 @@ def demo_gradio(self, height=600, dark_mode=True): else: js = js.replace("{theme}", "light") + message_files = [] + with gr.Blocks(js=js) as demo: chatbot = gr.Chatbot(height=height) msg = gr.Textbox() + file_upload = gr.Files(label="Upload File", type="filepath") + + def handle_file_upload(file_list): + nonlocal message_files + message_files = [] + if file_list: + try: + for file_obj in file_list: + # copy file to current directory + # path = "./" + os.path.basename(file_obj) + # shutil.copyfile(file_obj.name, path) + # print(f"Uploading file: {path}") + with open(file_obj.name, 'rb') as f: + # Upload the file to OpenAI + file = self.main_thread.client.files.create( + file=f, + purpose="assistants" + ) + message_files.append(file.id) + print(f"Uploaded file ID: {file.id}") + return message_files + except Exception as e: + print(f"Error: {e}") + return str(e) + + return "No files uploaded" def user(user_message, history): # Append the user message with a placeholder for bot response @@ -142,8 +171,10 @@ def user(user_message, history): return "", history + [[user_message, None]] def bot(history): + nonlocal message_files + print("Message files: ", message_files) # Replace this with your actual chatbot logic - gen = self.get_completion(message=history[-1][0]) + gen = self.get_completion(message=history[-1][0], message_files=message_files) try: # Yield each message from the generator @@ -157,9 +188,11 @@ def bot(history): yield history except StopIteration: # Handle the end of the conversation if necessary + message_files = [] pass # Chain the events + file_upload.change(handle_file_upload, file_upload) msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then( bot, chatbot, chatbot ) diff --git a/agency_swarm/tools/oai/__init__.py b/agency_swarm/tools/oai/__init__.py index e69de29b..bc0bbc80 100644 --- a/agency_swarm/tools/oai/__init__.py +++ b/agency_swarm/tools/oai/__init__.py @@ -0,0 +1,2 @@ +from .Retrieval import Retrieval +from .CodeInterpreter import CodeInterpreter \ No newline at end of file diff --git a/tests/demos/demo_gradio.py b/tests/demos/demo_gradio.py index 0d83ef58..b506483c 100644 --- a/tests/demos/demo_gradio.py +++ b/tests/demos/demo_gradio.py @@ -2,25 +2,24 @@ import gradio as gr -from agency_swarm import set_openai_key +from agency_swarm import set_openai_key, Agent sys.path.insert(0, '../agency-swarm') from agency_swarm.agency.agency import Agency -from tests.ceo.ceo import Ceo -from tests.test_agent.test_agent import TestAgent -from tests.test_agent2.test_agent2 import TestAgent2 +from agency_swarm.tools.oai import Retrieval + +ceo = Agent(name="CEO", + description="Responsible for client communication, task planning and management.", + instructions="Read files with myfiles_browser tool.", # can be a file like ./instructions.md + tools=[Retrieval]) + -test_agent1 = TestAgent() -test_agent2 = TestAgent2() -ceo = Ceo() agency = Agency([ ceo, - [ceo, test_agent1, test_agent2], - [ceo, test_agent2], -], shared_instructions="./manifesto.md") +], shared_instructions="") -agency.demo_gradio(height=1500) +agency.demo_gradio(height=900) From 219d46540337834dba162434f0b3be7fc22ab804 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Fri, 2 Feb 2024 09:24:55 +0400 Subject: [PATCH 02/15] Added custom gpt mentions to agency --- agency_swarm/agency/agency.py | 116 ++++++++++++++++++++++++--------- agency_swarm/agents/agent.py | 3 + agency_swarm/threads/thread.py | 30 +++++---- tests/demos/demo_gradio.py | 22 +++++-- 4 files changed, 125 insertions(+), 46 deletions(-) diff --git a/agency_swarm/agency/agency.py b/agency_swarm/agency/agency.py index e4a8540f..b5126d87 100644 --- a/agency_swarm/agency/agency.py +++ b/agency_swarm/agency/agency.py @@ -62,6 +62,7 @@ def __init__(self, self.ceo = None self.agents = [] self.agents_and_threads = {} + self.main_recipients = [] self.shared_files = shared_files if shared_files else [] self.settings_path = settings_path self.settings_callbacks = settings_callbacks @@ -82,7 +83,7 @@ def __init__(self, self.user = User() self.main_thread = Thread(self.user, self.ceo) - def get_completion(self, message: str, message_files=None, yield_messages=True): + def get_completion(self, message: str, message_files=None, yield_messages=True, recipient_agent=None): """ Retrieves the completion for a given message from the main thread. @@ -90,12 +91,13 @@ def get_completion(self, message: str, message_files=None, yield_messages=True): message (str): The message for which completion is to be retrieved. message_files (list, optional): A list of file ids to be sent as attachments with the message. Defaults to None. yield_messages (bool, optional): Flag to determine if intermediate messages should be yielded. Defaults to True. + recipient_agent (Agent, optional): The agent to which the message should be sent. Defaults to the first agent in the agency chart. Returns: Generator or final response: Depending on the 'yield_messages' flag, this method returns either a generator yielding intermediate messages or the final response from the main thread. """ gen = self.main_thread.get_completion(message=message, message_files=message_files, - yield_messages=yield_messages) + yield_messages=yield_messages, recipient_agent=recipient_agent) if not yield_messages: while True: @@ -133,32 +135,44 @@ def demo_gradio(self, height=600, dark_mode=True): else: js = js.replace("{theme}", "light") - message_files = [] + message_file_ids = [] + message_file_names = None + recipient_agents = [agent.name for agent in self.main_recipients] + recipient_agent = self.main_recipients[0] with gr.Blocks(js=js) as demo: chatbot = gr.Chatbot(height=height) - msg = gr.Textbox() - file_upload = gr.Files(label="Upload File", type="filepath") + with gr.Row(): + with gr.Column(scale=9): + dropdown = gr.Dropdown(label="Recipient Agent", choices=recipient_agents, + value=recipient_agent.name) + msg = gr.Textbox(label="Your Message", lines=4) + with gr.Column(scale=1): + file_upload = gr.Files(label="Files", type="filepath") + button = gr.Button(value="Send", variant="primary") + + def handle_dropdown_change(selected_option): + nonlocal recipient_agent + recipient_agent = self.get_agent_by_name(selected_option) def handle_file_upload(file_list): - nonlocal message_files - message_files = [] + nonlocal message_file_ids + nonlocal message_file_names + message_file_ids = [] + message_file_names = [] if file_list: try: for file_obj in file_list: - # copy file to current directory - # path = "./" + os.path.basename(file_obj) - # shutil.copyfile(file_obj.name, path) - # print(f"Uploading file: {path}") with open(file_obj.name, 'rb') as f: # Upload the file to OpenAI file = self.main_thread.client.files.create( file=f, purpose="assistants" ) - message_files.append(file.id) + message_file_ids.append(file.id) + message_file_names.append(file.filename) print(f"Uploaded file ID: {file.id}") - return message_files + return message_file_ids except Exception as e: print(f"Error: {e}") return str(e) @@ -166,16 +180,32 @@ def handle_file_upload(file_list): return "No files uploaded" def user(user_message, history): + if history is None: + history = [] + + original_user_message = user_message + # Append the user message with a placeholder for bot response - user_message = "πŸ‘€ User: " + user_message.strip() - return "", history + [[user_message, None]] + if recipient_agent: + user_message = f"πŸ‘€ User πŸ—£οΈ @{recipient_agent.name}:\n" + user_message.strip() + else: + user_message = f"πŸ‘€ User:" + user_message.strip() - def bot(history): - nonlocal message_files - print("Message files: ", message_files) - # Replace this with your actual chatbot logic - gen = self.get_completion(message=history[-1][0], message_files=message_files) + nonlocal message_file_names + if message_file_names: + user_message += "\n\nπŸ“Ž Files:\n" + "\n".join(message_file_names) + + return original_user_message, history + [[user_message, None]] + def bot(original_message, history): + nonlocal message_file_ids + nonlocal message_file_names + nonlocal recipient_agent + print("Message files: ", message_file_ids) + # Replace this with your actual chatbot logic + gen = self.get_completion(message=original_message, message_files=message_file_ids, recipient_agent=recipient_agent) + message_file_ids = [] + message_file_names = [] try: # Yield each message from the generator for bot_message in gen: @@ -185,16 +215,23 @@ def bot(history): message = bot_message.get_sender_emoji() + " " + bot_message.get_formatted_content() history.append((None, message)) - yield history + yield "", history except StopIteration: # Handle the end of the conversation if necessary - message_files = [] + pass - # Chain the events + button.click( + user, + inputs=[msg, chatbot], + outputs=[msg, chatbot] + ).then( + bot, [msg, chatbot], [msg, chatbot] + ) + dropdown.change(handle_dropdown_change, dropdown) file_upload.change(handle_file_upload, file_upload) msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then( - bot, chatbot, chatbot + bot, [msg, chatbot], [msg, chatbot] ) # Enable queuing for streaming intermediate outputs @@ -324,12 +361,20 @@ def _parse_agency_chart(self, agency_chart): If a node is a list, it iterates through the agents in the list, adding them to the agency and establishing communication threads between them. It raises an exception if the agency chart is invalid or if multiple CEOs are defined. """ + if not isinstance(agency_chart, list): + raise Exception("Invalid agency chart.") + + if len(agency_chart) == 0: + raise Exception("Agency chart cannot be empty.") + for node in agency_chart: if isinstance(node, Agent): - if self.ceo: - raise Exception("Only 1 ceo is supported for now.") - self.ceo = node - self._add_agent(self.ceo) + if not self.ceo: + self.ceo = node + self._add_agent(self.ceo) + else: + self._add_agent(node) + self._add_main_recipient(node) elif isinstance(node, list): for i, agent in enumerate(node): @@ -352,7 +397,6 @@ def _parse_agency_chart(self, agency_chart): "agent": agent.name, "recipient_agent": other_agent.name, } - else: raise Exception("Invalid agency chart.") @@ -379,6 +423,20 @@ def _add_agent(self, agent): else: return self.get_agent_ids().index(agent.id) + def _add_main_recipient(self, agent): + """ + Adds an agent to the agency's list of main recipients. + + Parameters: + agent (Agent): The agent to be added to the agency's list of main recipients. + + This method adds an agent to the agency's list of main recipients. These are agents that can be directly contacted by the user. + """ + main_recipient_ids = [agent.id for agent in self.main_recipients] + + if agent.id not in main_recipient_ids: + self.main_recipients.append(agent) + def _read_instructions(self, path): """ Reads shared instructions from a specified file and stores them in the agency. diff --git a/agency_swarm/agents/agent.py b/agency_swarm/agents/agent.py index 8bdf50f5..722be130 100644 --- a/agency_swarm/agents/agent.py +++ b/agency_swarm/agents/agent.py @@ -486,6 +486,9 @@ def get_class_folder_path(self): return os.path.abspath(os.path.realpath(os.path.dirname(class_file))) def add_shared_instructions(self, instructions: str): + if not instructions: + return + if self._shared_instructions is None: self._shared_instructions = instructions else: diff --git a/agency_swarm/threads/thread.py b/agency_swarm/threads/thread.py index 924896c1..e7c10060 100644 --- a/agency_swarm/threads/thread.py +++ b/agency_swarm/threads/thread.py @@ -26,14 +26,17 @@ def init_thread(self): self.thread = self.client.beta.threads.create() self.id = self.thread.id - def get_completion(self, message: str, message_files=None, yield_messages=True): + def get_completion(self, message: str, message_files=None, yield_messages=True, recipient_agent=None): if not self.thread: self.init_thread() + if not recipient_agent: + recipient_agent = self.recipient_agent + # Determine the sender's name based on the agent type sender_name = "user" if isinstance(self.agent, User) else self.agent.name - playground_url = f'https://platform.openai.com/playground?assistant={self.recipient_agent._assistant.id}&mode=assistant&thread={self.thread.id}' - print(f'THREAD:[ {sender_name} -> {self.recipient_agent.name} ]: URL {playground_url}') + playground_url = f'https://platform.openai.com/playground?assistant={recipient_agent.assistant.id}&mode=assistant&thread={self.thread.id}' + print(f'THREAD:[ {sender_name} -> {recipient_agent.name} ]: URL {playground_url}') # send message self.client.beta.threads.messages.create( @@ -44,12 +47,12 @@ def get_completion(self, message: str, message_files=None, yield_messages=True): ) if yield_messages: - yield MessageOutput("text", self.agent.name, self.recipient_agent.name, message) + yield MessageOutput("text", self.agent.name, recipient_agent.name, message) # create run self.run = self.client.beta.threads.runs.create( thread_id=self.thread.id, - assistant_id=self.recipient_agent.id, + assistant_id=recipient_agent.id, ) while True: @@ -67,10 +70,10 @@ def get_completion(self, message: str, message_files=None, yield_messages=True): tool_outputs = [] for tool_call in tool_calls: if yield_messages: - yield MessageOutput("function", self.recipient_agent.name, self.agent.name, + yield MessageOutput("function", recipient_agent.name, self.agent.name, str(tool_call.function)) - output = self.execute_tool(tool_call) + output = self.execute_tool(tool_call, recipient_agent) if inspect.isgenerator(output): try: while True: @@ -81,7 +84,7 @@ def get_completion(self, message: str, message_files=None, yield_messages=True): output = e.value else: if yield_messages: - yield MessageOutput("function_output", tool_call.function.name, self.recipient_agent.name, + yield MessageOutput("function_output", tool_call.function.name, recipient_agent.name, output) tool_outputs.append({"tool_call_id": tool_call.id, "output": str(output)}) @@ -103,12 +106,15 @@ def get_completion(self, message: str, message_files=None, yield_messages=True): message = messages.data[0].content[0].text.value if yield_messages: - yield MessageOutput("text", self.recipient_agent.name, self.agent.name, message) + yield MessageOutput("text", recipient_agent.name, self.agent.name, message) return message - def execute_tool(self, tool_call): - funcs = self.recipient_agent.functions + def execute_tool(self, tool_call, recipient_agent=None): + if not recipient_agent: + recipient_agent = self.recipient_agent + + funcs = recipient_agent.functions func = next((func for func in funcs if func.__name__ == tool_call.function.name), None) if not func: @@ -117,7 +123,7 @@ def execute_tool(self, tool_call): try: # init tool func = func(**eval(tool_call.function.arguments)) - func.caller_agent = self.recipient_agent + func.caller_agent = recipient_agent # get outputs from the tool output = func.run() diff --git a/tests/demos/demo_gradio.py b/tests/demos/demo_gradio.py index b506483c..3600f377 100644 --- a/tests/demos/demo_gradio.py +++ b/tests/demos/demo_gradio.py @@ -2,24 +2,36 @@ import gradio as gr -from agency_swarm import set_openai_key, Agent +sys.path.insert(0, './agency-swarm') -sys.path.insert(0, '../agency-swarm') +from agency_swarm import set_openai_key, Agent from agency_swarm.agency.agency import Agency from agency_swarm.tools.oai import Retrieval ceo = Agent(name="CEO", description="Responsible for client communication, task planning and management.", - instructions="Read files with myfiles_browser tool.", # can be a file like ./instructions.md + instructions="Analyze uploaded files with myfiles_browser tool.", # can be a file like ./instructions.md tools=[Retrieval]) +test_agent = Agent(name="Test Agent1", + description="Responsible for testing.", + instructions="Read files with myfiles_browser tool.", # can be a file like ./instructions.md + tools=[Retrieval]) + +test_agent2 = Agent(name="Test Agent2", + description="Responsible for testing.", + instructions="Read files with myfiles_browser tool.", # can be a file like ./instructions.md + tools=[Retrieval]) + + agency = Agency([ - ceo, + ceo, test_agent, test_agent2 ], shared_instructions="") +# agency.demo_gradio() -agency.demo_gradio(height=900) +print(agency.get_completion("Hello", recipient_agent=test_agent, yield_messages=False)) From 115890658ab4125dbea48765cf4f0e07ba2bf66c Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Tue, 6 Feb 2024 10:37:43 +0400 Subject: [PATCH 03/15] Improved genesis agency cli, added tools_folder parameter to Agent class, addressed #71 --- agency_swarm/agency/agency.py | 39 ++++++------ .../genesis/AgentCreator/AgentCreator.py | 13 ++-- .../genesis/AgentCreator/__init__.py | 0 .../genesis/AgentCreator/instructions.md | 11 ++++ .../tools}/CreateAgentTemplate.py | 27 +++++---- .../AgentCreator/tools}/GetAvailableAgents.py | 2 +- .../AgentCreator/tools}/ImportAgent.py | 6 +- .../AgentCreator/tools/ReadManifesto.py | 18 ++++++ .../AgentCreator/tools}/util/__init__.py | 0 .../AgentCreator/tools}/util/get_modules.py | 0 agency_swarm/agency/genesis/GenesisAgency.py | 25 ++++---- .../genesis/GenesisCEO/GenesisCEO.py | 3 +- .../genesis/GenesisCEO/__init__.py | 0 .../genesis/GenesisCEO/instructions.md | 9 +-- .../GenesisCEO/tools}/CreateAgencyFolder.py | 57 +++++++----------- .../GenesisCEO/tools}/FinalizeAgency.py | 7 +++ .../genesis/OpenAPICreator/OpenAPICreator.py | 5 +- .../genesis/OpenAPICreator/__init__.py | 0 .../genesis/OpenAPICreator/instructions.md | 10 ++++ .../tools}/CreateToolsFromOpenAPISpec.py | 23 ++----- .../genesis/ToolCreator/ToolCreator.py | 6 +- .../genesis/ToolCreator/__init__.py | 0 .../genesis/ToolCreator/instructions.md | 10 ++++ .../genesis/ToolCreator/tools/CreateTool.py | 37 ++++++++++++ .../genesis/ToolCreator/tools/TestTool.py | 60 +++++++++++++++++++ agency_swarm/agents/agent.py | 31 ++++++++++ agency_swarm/agents/browsing/BrowsingAgent.py | 4 +- .../browsing/tools}/AnalyzeContent.py | 2 +- .../browsing/tools}/ClickElement.py | 6 +- .../browsing/tools}/ExportFile.py | 2 +- .../browsing/tools}/GoBack.py | 2 +- .../browsing/tools}/ReadURL.py | 6 +- .../browsing/tools}/Scroll.py | 7 +-- .../browsing/tools}/SelectDropdown.py | 6 +- .../browsing/tools}/SendKeys.py | 8 +-- .../browsing/tools}/SolveCaptcha.py | 4 +- .../browsing/tools}/__init__.py | 0 .../browsing/tools}/util/__init__.py | 0 .../tools}/util/get_b64_screenshot.py | 0 .../browsing/tools}/util/highlights.py | 0 .../browsing/tools}/util/selenium.py | 0 .../genesis/AgentCreator/instructions.md | 14 ----- .../genesis/OpenAPICreator/instructions.md | 9 --- .../genesis/ToolCreator/instructions.md | 54 ----------------- agency_swarm/agents/genesis/__init__.py | 4 -- agency_swarm/cli.py | 13 +++- agency_swarm/messages/message_output.py | 15 +++-- agency_swarm/threads/thread.py | 45 ++++++++++---- agency_swarm/tools/BaseTool.py | 37 ++++++++---- agency_swarm/tools/ToolFactory.py | 27 +++++++++ agency_swarm/tools/genesis/CreateManifesto.py | 23 ------- .../tools/genesis/ImportLangchainTool.py | 0 agency_swarm/tools/genesis/ImportTool.py | 32 ---------- agency_swarm/tools/genesis/ReadManifesto.py | 23 ------- agency_swarm/tools/genesis/TestTool.py | 47 --------------- agency_swarm/tools/genesis/__init__.py | 8 --- agency_swarm/tools/openapi/__init__.py | 1 - agency_swarm/util/create_agent_template.py | 21 ++++--- tests/data/tools/ExampleTool1.py | 16 +++++ tests/test_tool_factory.py | 9 +++ 60 files changed, 442 insertions(+), 402 deletions(-) rename agency_swarm/{agents => agency}/genesis/AgentCreator/AgentCreator.py (56%) rename agency_swarm/{agents => agency}/genesis/AgentCreator/__init__.py (100%) create mode 100644 agency_swarm/agency/genesis/AgentCreator/instructions.md rename agency_swarm/{tools/genesis => agency/genesis/AgentCreator/tools}/CreateAgentTemplate.py (69%) rename agency_swarm/{tools/genesis => agency/genesis/AgentCreator/tools}/GetAvailableAgents.py (94%) rename agency_swarm/{tools/genesis => agency/genesis/AgentCreator/tools}/ImportAgent.py (92%) create mode 100644 agency_swarm/agency/genesis/AgentCreator/tools/ReadManifesto.py rename agency_swarm/{tools/genesis => agency/genesis/AgentCreator/tools}/util/__init__.py (100%) rename agency_swarm/{tools/genesis => agency/genesis/AgentCreator/tools}/util/get_modules.py (100%) rename agency_swarm/{agents => agency}/genesis/GenesisCEO/GenesisCEO.py (81%) rename agency_swarm/{agents => agency}/genesis/GenesisCEO/__init__.py (100%) rename agency_swarm/{agents => agency}/genesis/GenesisCEO/instructions.md (73%) rename agency_swarm/{tools/genesis => agency/genesis/GenesisCEO/tools}/CreateAgencyFolder.py (57%) rename agency_swarm/{tools/genesis => agency/genesis/GenesisCEO/tools}/FinalizeAgency.py (88%) rename agency_swarm/{agents => agency}/genesis/OpenAPICreator/OpenAPICreator.py (79%) rename agency_swarm/{agents => agency}/genesis/OpenAPICreator/__init__.py (100%) create mode 100644 agency_swarm/agency/genesis/OpenAPICreator/instructions.md rename agency_swarm/{tools/openapi => agency/genesis/OpenAPICreator/tools}/CreateToolsFromOpenAPISpec.py (78%) rename agency_swarm/{agents => agency}/genesis/ToolCreator/ToolCreator.py (65%) rename agency_swarm/{agents => agency}/genesis/ToolCreator/__init__.py (100%) create mode 100644 agency_swarm/agency/genesis/ToolCreator/instructions.md create mode 100644 agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py create mode 100644 agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py rename agency_swarm/{tools/browsing => agents/browsing/tools}/AnalyzeContent.py (96%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/ClickElement.py (95%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/ExportFile.py (95%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/GoBack.py (83%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/ReadURL.py (89%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/Scroll.py (90%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/SelectDropdown.py (95%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/SendKeys.py (94%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/SolveCaptcha.py (98%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/__init__.py (100%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/util/__init__.py (100%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/util/get_b64_screenshot.py (100%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/util/highlights.py (100%) rename agency_swarm/{tools/browsing => agents/browsing/tools}/util/selenium.py (100%) delete mode 100644 agency_swarm/agents/genesis/AgentCreator/instructions.md delete mode 100644 agency_swarm/agents/genesis/OpenAPICreator/instructions.md delete mode 100644 agency_swarm/agents/genesis/ToolCreator/instructions.md delete mode 100644 agency_swarm/agents/genesis/__init__.py delete mode 100644 agency_swarm/tools/genesis/CreateManifesto.py delete mode 100644 agency_swarm/tools/genesis/ImportLangchainTool.py delete mode 100644 agency_swarm/tools/genesis/ImportTool.py delete mode 100644 agency_swarm/tools/genesis/ReadManifesto.py delete mode 100644 agency_swarm/tools/genesis/TestTool.py delete mode 100644 agency_swarm/tools/genesis/__init__.py delete mode 100644 agency_swarm/tools/openapi/__init__.py create mode 100644 tests/data/tools/ExampleTool1.py diff --git a/agency_swarm/agency/agency.py b/agency_swarm/agency/agency.py index b5126d87..7006611c 100644 --- a/agency_swarm/agency/agency.py +++ b/agency_swarm/agency/agency.py @@ -252,12 +252,27 @@ def run_demo(self): """ while True: console.rule() - text = input("USER: ") + text = input("πŸ‘€ USER: ") + + if text.lower() == "exit": + break + + recipient_agent = None + if "@" in text: + recipient_agent = text.split("@")[1].split(" ")[0] + text = text.replace(f"@{recipient_agent}", "").strip() + try: + recipient_agent = self.get_agent_by_name(recipient_agent) + except Exception as e: + print(e) + continue try: - gen = self.main_thread.get_completion(message=text) + gen = self.main_thread.get_completion(message=text, recipient_agent=recipient_agent) while True: message = next(gen) + if message.sender_name.lower() == "user": + continue message.cprint() except StopIteration as e: pass @@ -507,8 +522,6 @@ class SendMessage(BaseTool): message_files: List[str] = Field(default=None, description="A list of file ids to be sent as attachments to this message. Only use this if you have the file id that starts with 'file-'.", examples=["file-1234", "file-5678"]) - caller_agent_name: str = Field(default=agent.name, - description="The agent calling this tool. Defaults to your name. Do not change it.") @field_validator('recipient') def check_recipient(cls, value): @@ -516,14 +529,8 @@ def check_recipient(cls, value): raise ValueError(f"Recipient {value} is not valid. Valid recipients are: {recipient_names}") return value - @field_validator('caller_agent_name') - def check_caller_agent_name(cls, value): - if value != agent.name: - raise ValueError(f"Caller agent name must be {agent.name}.") - return value - def run(self): - thread = outer_self.agents_and_threads[self.caller_agent_name][self.recipient.value] + thread = outer_self.agents_and_threads[self.caller_agent.name][self.recipient.value] if not outer_self.async_mode: gen = thread.get_completion(message=self.message, message_files=self.message_files) @@ -558,8 +565,6 @@ class GetResponse(BaseTool): """This tool allows you to check the status of a task or get a response from a specified recipient agent, if the task has been completed. You must always use 'SendMessage' tool with the designated agent first.""" recipient: recipients = Field(..., description=f"Recipient agent that you want to check the status of. Valid recipients are: {recipient_names}") - caller_agent_name: str = Field(default=agent.name, - description="The agent calling this tool. Defaults to your name. Do not change it.") @field_validator('recipient') def check_recipient(cls, value): @@ -567,14 +572,8 @@ def check_recipient(cls, value): raise ValueError(f"Recipient {value} is not valid. Valid recipients are: {recipient_names}") return value - @field_validator('caller_agent_name') - def check_caller_agent_name(cls, value): - if value != agent.name: - raise ValueError(f"Caller agent name must be {agent.name}.") - return value - def run(self): - thread = outer_self.agents_and_threads[self.caller_agent_name][self.recipient.value] + thread = outer_self.agents_and_threads[self.caller_agent.name][self.recipient.value] return thread.check_status() diff --git a/agency_swarm/agents/genesis/AgentCreator/AgentCreator.py b/agency_swarm/agency/genesis/AgentCreator/AgentCreator.py similarity index 56% rename from agency_swarm/agents/genesis/AgentCreator/AgentCreator.py rename to agency_swarm/agency/genesis/AgentCreator/AgentCreator.py index aa2a4e1c..4029e2d4 100644 --- a/agency_swarm/agents/genesis/AgentCreator/AgentCreator.py +++ b/agency_swarm/agency/genesis/AgentCreator/AgentCreator.py @@ -1,6 +1,8 @@ from agency_swarm import Agent -from agency_swarm.tools.coding import ReadFile -from agency_swarm.tools.genesis import CreateAgentTemplate, ImportAgent, GetAvailableAgents +from .tools.CreateAgentTemplate import CreateAgentTemplate +# from .tools.GetAvailableAgents import GetAvailableAgents +# from .tools.ImportAgent import ImportAgent +from .tools.ReadManifesto import ReadManifesto class AgentCreator(Agent): @@ -12,9 +14,10 @@ def __init__(self, **kwargs): # Add required tools kwargs['tools'].extend([CreateAgentTemplate, - GetAvailableAgents, - ReadFile, - ImportAgent]) + # GetAvailableAgents, + ReadManifesto, + # ImportAgent + ]) # Set instructions if 'instructions' not in kwargs: diff --git a/agency_swarm/agents/genesis/AgentCreator/__init__.py b/agency_swarm/agency/genesis/AgentCreator/__init__.py similarity index 100% rename from agency_swarm/agents/genesis/AgentCreator/__init__.py rename to agency_swarm/agency/genesis/AgentCreator/__init__.py diff --git a/agency_swarm/agency/genesis/AgentCreator/instructions.md b/agency_swarm/agency/genesis/AgentCreator/instructions.md new file mode 100644 index 00000000..89c94e0c --- /dev/null +++ b/agency_swarm/agency/genesis/AgentCreator/instructions.md @@ -0,0 +1,11 @@ +# AgentCreator Agent Instructions + +You are an agent that creates other agents as instructed by the user. + +The user will communicate to you each agent that needs to be created. Below are your instructions that need to be followed for each agent. + +**Primary Instructions:** +1. First, read the manifesto using `ReadManifesto` tool if you have not already done so. This file contains the agency manifesto that describes the agency's purpose and goals. +2. Think if the agent you are creating needs to utilize any APIs. If it does, tell the OpenAPICreator agent to create API schemas for this agent. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. CEO agents do not need to perform any API calls or use any tools, so you can skip to step 7. +3. For agents that do not need to utilize any APIs to perform their roles, tell the ToolCreator agent to create tools for this agent. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. +4. If there are no issues and tools or APIs have been created, notify the user that the agent has been created. Otherwise, try to resolve any issues with other agents before reporting back. \ No newline at end of file diff --git a/agency_swarm/tools/genesis/CreateAgentTemplate.py b/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py similarity index 69% rename from agency_swarm/tools/genesis/CreateAgentTemplate.py rename to agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py index 2a5fdfda..a81f93dd 100644 --- a/agency_swarm/tools/genesis/CreateAgentTemplate.py +++ b/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py @@ -1,4 +1,5 @@ import os +import shutil from typing import List from pydantic import Field, model_validator, field_validator @@ -28,6 +29,14 @@ class CreateAgentTemplate(BaseTool): ) def run(self): + self.shared_state.set("agent_name", self.agent_name) + + os.chdir(self.shared_state.get("agency_path")) + + # remove folder if it already exists + if os.path.exists(self.agent_name): + shutil.rmtree(self.agent_name) + create_agent_template(self.agent_name, self.agent_description, instructions=self.instructions, @@ -41,21 +50,15 @@ def run(self): with open("agency.py", "w") as f: f.writelines(lines) - return f"Agent template has been created in {self.agent_name} folder." + os.chdir(self.shared_state.get("default_folder")) + + if "ceo" in self.agent_name.lower(): + return f"You can tell the user that the process of creating {self.agent_name} has been completed, because CEO agent does not need to utilizie any tools or APIs." + + return f"Agent template has been created for {self.agent_name}. Please now tell ToolCreator to create tools for this agent or OpenAPICreator to create API schemas, if this agent needs to utilize any tools or APIs. If this is unclear, please ask the user for more information." @model_validator(mode="after") def validate_tools(self): for tool in self.default_tools: if tool not in self.allowed_tools: raise ValueError(f"Tool {tool} is not allowed. Allowed tools are: {self.allowed_tools}") - - @field_validator("agent_name", mode='after') - @classmethod - def agent_name_exists(cls, v): - if " " in v: - raise ValueError("Agent name cannot contain spaces.") - if not v.isalnum(): - raise ValueError("Agent name cannot contain special characters.") - if os.path.exists("./" + v): - raise ValueError(f"Agent with name {v} already exists.") - return v diff --git a/agency_swarm/tools/genesis/GetAvailableAgents.py b/agency_swarm/agency/genesis/AgentCreator/tools/GetAvailableAgents.py similarity index 94% rename from agency_swarm/tools/genesis/GetAvailableAgents.py rename to agency_swarm/agency/genesis/AgentCreator/tools/GetAvailableAgents.py index 97dadd3f..ecb9cb15 100644 --- a/agency_swarm/tools/genesis/GetAvailableAgents.py +++ b/agency_swarm/agency/genesis/AgentCreator/tools/GetAvailableAgents.py @@ -1,7 +1,7 @@ import os.path from agency_swarm import BaseTool -from agency_swarm.tools.genesis.util import get_modules +from .util import get_modules import importlib class GetAvailableAgents(BaseTool): diff --git a/agency_swarm/tools/genesis/ImportAgent.py b/agency_swarm/agency/genesis/AgentCreator/tools/ImportAgent.py similarity index 92% rename from agency_swarm/tools/genesis/ImportAgent.py rename to agency_swarm/agency/genesis/AgentCreator/tools/ImportAgent.py index f9def25b..1d307111 100644 --- a/agency_swarm/tools/genesis/ImportAgent.py +++ b/agency_swarm/agency/genesis/AgentCreator/tools/ImportAgent.py @@ -3,7 +3,7 @@ from pydantic import Field, field_validator from agency_swarm import BaseTool -from agency_swarm.tools.genesis.util import get_modules +from .util import get_modules from agency_swarm.util import create_agent_template agent_paths = get_modules('agency_swarm.agents') @@ -20,6 +20,8 @@ class ImportAgent(BaseTool): description="Name of the agent to be imported.") def run(self): + os.chdir(self.shared_state.get("agency_path")) + # find item in available_agents dict by value import_path = [item for item in agent_paths if self.agent_name in item][0] @@ -37,6 +39,8 @@ def run(self): f.write(f"\nfrom {import_path} import {self.agent_name}\n") f.write(f"{instance_name} = {self.agent_name}()") + os.chdir(self.shared_state.get("default_folder")) + return "Success. Agent has been imported. You can now use it in your agency." @field_validator("agent_name", mode='after') diff --git a/agency_swarm/agency/genesis/AgentCreator/tools/ReadManifesto.py b/agency_swarm/agency/genesis/AgentCreator/tools/ReadManifesto.py new file mode 100644 index 00000000..4b25dcb7 --- /dev/null +++ b/agency_swarm/agency/genesis/AgentCreator/tools/ReadManifesto.py @@ -0,0 +1,18 @@ +import os + +from agency_swarm import BaseTool + + +class ReadManifesto(BaseTool): + """ + This tool reads a manifesto for the agency being created from a markdown file. + """ + + def run(self): + os.chdir(self.shared_state.get("agency_path")) + with open("agency_manifesto.md", "r") as f: + manifesto = f.read() + + os.chdir(self.shared_state.get("default_folder")) + + return manifesto diff --git a/agency_swarm/tools/genesis/util/__init__.py b/agency_swarm/agency/genesis/AgentCreator/tools/util/__init__.py similarity index 100% rename from agency_swarm/tools/genesis/util/__init__.py rename to agency_swarm/agency/genesis/AgentCreator/tools/util/__init__.py diff --git a/agency_swarm/tools/genesis/util/get_modules.py b/agency_swarm/agency/genesis/AgentCreator/tools/util/get_modules.py similarity index 100% rename from agency_swarm/tools/genesis/util/get_modules.py rename to agency_swarm/agency/genesis/AgentCreator/tools/util/get_modules.py diff --git a/agency_swarm/agency/genesis/GenesisAgency.py b/agency_swarm/agency/genesis/GenesisAgency.py index bcc2547f..fb3df961 100644 --- a/agency_swarm/agency/genesis/GenesisAgency.py +++ b/agency_swarm/agency/genesis/GenesisAgency.py @@ -1,10 +1,11 @@ from agency_swarm import Agency +from .AgentCreator import AgentCreator from agency_swarm.agents.browsing import BrowsingAgent -from agency_swarm.agents.genesis import GenesisCEO, AgentCreator -import os +from .GenesisCEO import GenesisCEO +from .OpenAPICreator import OpenAPICreator +from .ToolCreator import ToolCreator -from agency_swarm.agents.genesis import ToolCreator -from agency_swarm.agents.genesis import OpenAPICreator +new_agency_path = None class GenesisAgency(Agency): @@ -19,20 +20,18 @@ def __init__(self, **kwargs): browsing_agent.instructions += ("""\n # BrowsingAgent's Primary instructions -1. Browse the web to find the most relevant API that the requested agent needs in order to perform its role. If you already have an idea of what API to use, search google directly for this API documentation. -2. After finding the right API to use, navigate to its documentation page. Prefer to do this by searching for the API documentation page in google, rather than navigating to the API's website and then finding the documentation page, if possible. -3. Ensure that the current page actually contains the necessary API endpoints descriptions with the AnalyzeContent tool. If you can't find a link to the documentation page, try to search for it in google. -4. If you have confirmed that the page contains the necessary API documentation, export the page with ExportFile tool and send the file_id back to the user along with a brief description of the API. -5. If not, continue browsing the web until you find the right API documentation page. -6. Repeat these steps for each new requested agent. +1. Browse the web to find the API documentation requested by the user. Prefer searching google directly for this API documentation page. +2. Navigate to the API documentation page and ensure that it contains the necessary API endpoints descriptions. You can use the AnalyzeContent tool to check if the page contains the necessary API descriptions. If not, try perfrom another search in google and keep browsing until you find the right page. +3. If you have confirmed that the page contains the necessary API documentation, export the page with ExportFile tool. Then, send the file_id back to the user along with a brief description of the API. +4. Repeat these steps for each new agent, as requested by the user. """) - kwargs['agency_chart'] = [ - genesis_ceo, + genesis_ceo, tool_creator, agent_creator, [genesis_ceo, agent_creator], - [agent_creator, browsing_agent], [agent_creator, openapi_creator], + [openapi_creator, browsing_agent], + [agent_creator, tool_creator], ] if 'shared_instructions' not in kwargs: diff --git a/agency_swarm/agents/genesis/GenesisCEO/GenesisCEO.py b/agency_swarm/agency/genesis/GenesisCEO/GenesisCEO.py similarity index 81% rename from agency_swarm/agents/genesis/GenesisCEO/GenesisCEO.py rename to agency_swarm/agency/genesis/GenesisCEO/GenesisCEO.py index fdf9c752..cd632771 100644 --- a/agency_swarm/agents/genesis/GenesisCEO/GenesisCEO.py +++ b/agency_swarm/agency/genesis/GenesisCEO/GenesisCEO.py @@ -1,5 +1,6 @@ from agency_swarm import Agent -from agency_swarm.tools.genesis import CreateAgencyFolder, FinalizeAgency +from .tools.CreateAgencyFolder import CreateAgencyFolder +from .tools.FinalizeAgency import FinalizeAgency class GenesisCEO(Agent): diff --git a/agency_swarm/agents/genesis/GenesisCEO/__init__.py b/agency_swarm/agency/genesis/GenesisCEO/__init__.py similarity index 100% rename from agency_swarm/agents/genesis/GenesisCEO/__init__.py rename to agency_swarm/agency/genesis/GenesisCEO/__init__.py diff --git a/agency_swarm/agents/genesis/GenesisCEO/instructions.md b/agency_swarm/agency/genesis/GenesisCEO/instructions.md similarity index 73% rename from agency_swarm/agents/genesis/GenesisCEO/instructions.md rename to agency_swarm/agency/genesis/GenesisCEO/instructions.md index 5dbb642c..ff6ac1e6 100644 --- a/agency_swarm/agents/genesis/GenesisCEO/instructions.md +++ b/agency_swarm/agency/genesis/GenesisCEO/instructions.md @@ -4,13 +4,13 @@ 2. Ask user about their goals for this agency, its mission and its processes, like what APIs would the agents need to utilize. 3. Propose an initial structure for the agency, including the roles of the agents and their communication flows. Focus on creating at most 2 agents, plus CEO, unless instructed otherwise by the user. Output the code snippet like below. 4. Upon confirmation use `CreateAgencyFolder` tool to create a folder for the agency. If any modifications are required please use this tool again with the same agency name and it will overwrite the existing folder. -5. Tell AgentCreator to create these agents one by one, starting with the CEO. Each agent should be sent in a separate message using the `SendMessage` tool. If one of the agents needs to utilize a specific API, as instructed by the user, please make sure to communicate this as well. +5. Tell AgentCreator to create these agents one by one, starting with the CEO. Each agent should be sent in a separate message using the `SendMessage` tool. Please make sure to communicate if this agent needs to utilize any APIs or tools to perform its role. 6. Once all agents are created, please use the `FinalizeAgency` tool, and tell the user that he can now navigate to the agency folder and start it with `python agency.py` command. ### Example of communication flows -Here is an example of how communication flows are defined in agency swarm. Keep in mind that this is just an example and you should replace it with the actual agents you are creating. Typically, no agents should be able to initiate communication with CEO, unless instructed otherwise by the user. +Here is an example of how communication flows are defined in agency swarm. Essentially, agents that are inside a double array can initiate communication with each other. Agents that are in the top level array can communicate with the user. ```python agency = Agency([ @@ -18,5 +18,6 @@ agency = Agency([ [ceo, dev], # CEO can initiate communication with Developer [ceo, va], # CEO can initiate communication with Virtual Assistant [dev, va] # Developer can initiate communication with Virtual Assistant -], shared_instructions='manifesto.md') # shared instructions for all agents -``` \ No newline at end of file +], shared_instructions='agency_manifesto.md') # shared instructions for all agents +``` +Keep in mind that this is just an example and you should replace it with the actual agents you are creating. \ No newline at end of file diff --git a/agency_swarm/tools/genesis/CreateAgencyFolder.py b/agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py similarity index 57% rename from agency_swarm/tools/genesis/CreateAgencyFolder.py rename to agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py index 246ec5f5..9fe1b820 100644 --- a/agency_swarm/tools/genesis/CreateAgencyFolder.py +++ b/agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py @@ -1,12 +1,13 @@ import shutil +from pathlib import Path from pydantic import Field, field_validator +import agency_swarm.agency.genesis.GenesisAgency from agency_swarm import BaseTool import os -current_agency_name = None class CreateAgencyFolder(BaseTool): """ @@ -21,25 +22,28 @@ class CreateAgencyFolder(BaseTool): examples=["[ceo, [ceo, dev], [ceo, va], [dev, va] ]"] ) manifesto: str = Field( - ..., description="Manifesto for the agency, describing it's goals and additional context shared by all agents " - "in markdown format." + ..., description="Manifesto for the agency, describing its goals and additional context shared by all agents " + "in markdown format. It must include a brief description of each agent, its purpose and " + "whether it needs to utilize any tools or APIs.", ) def run(self): - folder_path = "./" + self.agency_name + "/" - - global current_agency_name - if current_agency_name is not None: - if os.getcwd().strip("/").strip("\\").endswith(current_agency_name): - os.chdir("..") - shutil.rmtree(current_agency_name) - - current_agency_name = self.agency_name - - # create folder - os.mkdir(folder_path) - - os.chdir(folder_path) + if not self.shared_state.get("default_folder"): + self.shared_state.set('default_folder', Path.cwd()) + + if self.shared_state.get("agency_name") is None: + os.mkdir(self.agency_name) + os.chdir("./" + self.agency_name) + self.shared_state.set("agency_name", self.agency_name) + self.shared_state.set("agency_path", Path("./").resolve()) + elif self.shared_state.get("agency_name") == self.agency_name and os.path.exists(self.shared_state.get("agency_path")): + os.chdir(self.shared_state.get("agency_path")) + for file in os.listdir(): + if file != "__init__.py" and os.path.isfile(file): + os.remove(file) + else: + os.mkdir(self.shared_state.get("agency_path")) + os.chdir("./" + self.agency_name) # check that agency chart is valid if not self.agency_chart.startswith("[") or not self.agency_chart.endswith("]"): @@ -66,23 +70,8 @@ def run(self): with open(path, "w") as f: f.write(self.manifesto) - return f"Agency folder has been created in {folder_path}." - - @field_validator('agency_name', mode='after') - @classmethod - def check_agency_name(cls, v): - global current_agency_name - - if os.path.exists("./" + v): - raise ValueError("Agency with this name already exists.") - - if current_agency_name is not None: - if current_agency_name != v: - raise ValueError("You can only create 1 agency at a time. Please tell the user to restart the system if he wants to create a new agency or use the same agency_name to modify an exisiting agency.") - - if not os.getcwd().strip("/").endswith(current_agency_name): - raise ValueError("Please tell the user to restart the system if he wants to create a new agency.") + os.chdir(self.shared_state.get('default_folder')) - return v + return f"Agency folder has been created. You can now tell AgentCreator to create agents for {self.agency_name}.\n" diff --git a/agency_swarm/tools/genesis/FinalizeAgency.py b/agency_swarm/agency/genesis/GenesisCEO/tools/FinalizeAgency.py similarity index 88% rename from agency_swarm/tools/genesis/FinalizeAgency.py rename to agency_swarm/agency/genesis/GenesisCEO/tools/FinalizeAgency.py index bf4ce25d..32a3b731 100644 --- a/agency_swarm/tools/genesis/FinalizeAgency.py +++ b/agency_swarm/agency/genesis/GenesisCEO/tools/FinalizeAgency.py @@ -13,6 +13,8 @@ class FinalizeAgency(BaseTool): """ def run(self): + os.chdir(self.shared_state.get("agency_path")) + client = get_openai_client() # read agency.py @@ -37,6 +39,11 @@ def run(self): return "Successfully finalized agency structure. You can now instruct the user to run the agency.py file." + @model_validator(mode="after") + def validate_agency_path(self): + if not self.shared_state.get("agency_path"): + raise ValueError("Agency path not found. Please use CreateAgencyFolder tool to create the agency folder first.") + SYSTEM_PROMPT = """"Please read the file provided by the user and fix all the imports and indentation accordingly. diff --git a/agency_swarm/agents/genesis/OpenAPICreator/OpenAPICreator.py b/agency_swarm/agency/genesis/OpenAPICreator/OpenAPICreator.py similarity index 79% rename from agency_swarm/agents/genesis/OpenAPICreator/OpenAPICreator.py rename to agency_swarm/agency/genesis/OpenAPICreator/OpenAPICreator.py index 59ef9aa5..ff2f7662 100644 --- a/agency_swarm/agents/genesis/OpenAPICreator/OpenAPICreator.py +++ b/agency_swarm/agency/genesis/OpenAPICreator/OpenAPICreator.py @@ -1,7 +1,6 @@ from agency_swarm import Agent from agency_swarm.tools import Retrieval -from agency_swarm.tools.coding import ListDir -from agency_swarm.tools.openapi import CreateToolsFromOpenAPISpec +from .tools.CreateToolsFromOpenAPISpec import CreateToolsFromOpenAPISpec class OpenAPICreator(Agent): @@ -11,7 +10,7 @@ def __init__(self, **kwargs): if 'tools' not in kwargs: kwargs['tools'] = [] # Add required tools - kwargs['tools'].extend([Retrieval, CreateToolsFromOpenAPISpec, ListDir]) + kwargs['tools'].extend([Retrieval, CreateToolsFromOpenAPISpec]) # Set instructions kwargs['instructions'] = "./instructions.md" diff --git a/agency_swarm/agents/genesis/OpenAPICreator/__init__.py b/agency_swarm/agency/genesis/OpenAPICreator/__init__.py similarity index 100% rename from agency_swarm/agents/genesis/OpenAPICreator/__init__.py rename to agency_swarm/agency/genesis/OpenAPICreator/__init__.py diff --git a/agency_swarm/agency/genesis/OpenAPICreator/instructions.md b/agency_swarm/agency/genesis/OpenAPICreator/instructions.md new file mode 100644 index 00000000..53f819d3 --- /dev/null +++ b/agency_swarm/agency/genesis/OpenAPICreator/instructions.md @@ -0,0 +1,10 @@ +# OpenAPICreator Instructions + +You are an agent that creates tools from OpenAPI schemas. User will provide you with a file with the API documentation webpage for the relevant api and a description of the tools and its purpose. + +**Here are your primary instructions:** +1. Think which API is needed for this agent's role, as communicated by the user. Then, tell the BrowsingAgent to find this API documentation page. +2. Explore the provided file with the myfiles_broswer tool to determine which endpoints are needed for this agent's role. +3. If the file does not contain the actual API documentation page, please notify the BrowsingAgent. Keep in mind that you do not need the full API documentation. You can make an educated guess if some information is not available. +4. Use `CreateToolsFromOpenAPISpec` to create the tools by defining the OpenAPI schema accordingly. Make sure to include all the relevant API endpoints that are needed for this agent to execute its role. +5. Repeat these steps for each new agent that needs to be created, as instructed by the user. \ No newline at end of file diff --git a/agency_swarm/tools/openapi/CreateToolsFromOpenAPISpec.py b/agency_swarm/agency/genesis/OpenAPICreator/tools/CreateToolsFromOpenAPISpec.py similarity index 78% rename from agency_swarm/tools/openapi/CreateToolsFromOpenAPISpec.py rename to agency_swarm/agency/genesis/OpenAPICreator/tools/CreateToolsFromOpenAPISpec.py index 9213c7b3..1b5e7bf9 100644 --- a/agency_swarm/tools/openapi/CreateToolsFromOpenAPISpec.py +++ b/agency_swarm/agency/genesis/OpenAPICreator/tools/CreateToolsFromOpenAPISpec.py @@ -14,9 +14,6 @@ class CreateToolsFromOpenAPISpec(BaseTool): """ This tool creates a set of tools from an OpenAPI specification. Each method in the specification is converted to a separate tool. """ - agent_name: str = Field( - ..., description="Name of the agent for whom the tools are being created. Cannot include special characters." - ) openapi_spec: str = Field( ..., description="OpenAPI specification for the tool to be created as a valid JSON string. Only the relevant " "endpoints must be included. Responses are not required. Each method should contain " @@ -25,6 +22,9 @@ class CreateToolsFromOpenAPISpec(BaseTool): '{\n "openapi": "3.1.0",\n "info": {\n "title": "Get weather data",\n "description": "Retrieves current weather data for a location.",\n "version": "v1.0.0"\n },\n "servers": [\n {\n "url": "https://weather.example.com"\n }\n ],\n "paths": {\n "/location": {\n "get": {\n "description": "Get temperature for a specific location",\n "operationId": "GetCurrentWeather",\n "parameters": [\n {\n "name": "location",\n "in": "query",\n "description": "The city and state to retrieve the weather for",\n "required": true,\n "schema": {\n "type": "string"\n }\n }\n ],\n "deprecated": false\n }\n }\n },\n "components": {\n "schemas": {}\n }\n}']) def run(self): + os.chdir(self.shared_state.get("agency_path")) + os.chdir(self.shared_state.get("agent_name")) + try: try: tools = ToolFactory.from_openapi_schema(self.openapi_spec) @@ -49,9 +49,9 @@ def run(self): with open("schemas/" + api_name + ".json", "w") as f: f.write(self.openapi_spec) - return "Successfully added OpenAPI Schema to agent." + return "Successfully added OpenAPI Schema to " + self.shared_state.get("agent_name") finally: - os.chdir("../") + os.chdir(self.shared_state.get("default_folder")) @field_validator("openapi_spec", mode='before') @classmethod @@ -64,16 +64,3 @@ def validate_openapi_spec(cls, v): raise ValueError("Error validating OpenAPI schema:", e) return v - @field_validator("agent_name", mode='before') - @classmethod - def validate_agent_name(cls, v): - available_agents = os.listdir("./") - available_agents = [agent for agent in available_agents if os.path.isdir(agent)] - - if not v.isalnum(): - raise ValueError(f"Agent name must be alphanumeric. Available agent names are: {available_agents}") - - if not os.path.exists(f"./{v}"): - raise ValueError(f"Agent {v} does not exist. Available agents are: {available_agents}") - - return v diff --git a/agency_swarm/agents/genesis/ToolCreator/ToolCreator.py b/agency_swarm/agency/genesis/ToolCreator/ToolCreator.py similarity index 65% rename from agency_swarm/agents/genesis/ToolCreator/ToolCreator.py rename to agency_swarm/agency/genesis/ToolCreator/ToolCreator.py index a80d87bb..eedb5e4d 100644 --- a/agency_swarm/agents/genesis/ToolCreator/ToolCreator.py +++ b/agency_swarm/agency/genesis/ToolCreator/ToolCreator.py @@ -1,6 +1,6 @@ from agency_swarm import Agent -from agency_swarm.tools.coding import ChangeDir, ChangeLines, ReadFile, ListDir, WriteFiles -from agency_swarm.tools.genesis import TestTool +from .tools.TestTool import TestTool +from .tools.CreateTool import CreateTool class ToolCreator(Agent): @@ -10,7 +10,7 @@ def __init__(self, **kwargs): kwargs['tools'] = [] # Add required tools - kwargs['tools'].extend([ChangeDir, ChangeLines, ReadFile, ListDir, WriteFiles, TestTool]) + kwargs['tools'].extend([CreateTool, TestTool]) # Set instructions if 'instructions' not in kwargs: diff --git a/agency_swarm/agents/genesis/ToolCreator/__init__.py b/agency_swarm/agency/genesis/ToolCreator/__init__.py similarity index 100% rename from agency_swarm/agents/genesis/ToolCreator/__init__.py rename to agency_swarm/agency/genesis/ToolCreator/__init__.py diff --git a/agency_swarm/agency/genesis/ToolCreator/instructions.md b/agency_swarm/agency/genesis/ToolCreator/instructions.md new file mode 100644 index 00000000..8e6d7d0e --- /dev/null +++ b/agency_swarm/agency/genesis/ToolCreator/instructions.md @@ -0,0 +1,10 @@ +# ToolCreator Agent Instructions + +You are an agent that creates tools for other agents, as instructed by the user. + +**Here are your primary instructions:** +1. Determine which tools the agent must utilize to perform it's role. If anything is unclear, ask the user for more information. +2. Create these tools one at a time, using `CreateTool` function. Below is documentation on how tools in agency swarm are defined. +3. Test each tool with the `TestTool` function to ensure it is working as expected. +4. Once all the necessary tools are created, notify the user. + diff --git a/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py b/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py new file mode 100644 index 00000000..f42fdf09 --- /dev/null +++ b/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py @@ -0,0 +1,37 @@ +import os +from typing import Optional + +from agency_swarm.tools import BaseTool +from pydantic import Field, model_validator +import importlib + +from agency_swarm.util.create_agent_template import example_tool_template + + +class CreateTool(BaseTool): + """ + This tool creates tools for the agent. + """ + chain_of_thought: str = Field( + ..., description="Think step by step to determine how to best implement this tool.", exclude=True + ) + tool_name: str = Field(..., description="Name of the tool class in camel case.", examples=["ExampleTool"]) + tool_code: str = Field( + ..., description="Correct code for this tool written in python. Must include all the import statements, " + "as well as the primary tool class that extends BaseTool. Name of this class must match tool_name.", examples=[example_tool_template] + ) + + def run(self): + os.chdir(self.shared_state.get("agency_path")) + os.chdir(self.shared_state.get("agent_name")) + + with open("./tools/" + self.tool_name + ".py", "w") as f: + f.write(self.tool_code) + f.close() + + os.chdir(self.shared_state.get("default_folder")) + + return f"Tool {self.tool_name} has been created successfully. You can now test it with TestTool." + + + diff --git a/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py b/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py new file mode 100644 index 00000000..d27d3790 --- /dev/null +++ b/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py @@ -0,0 +1,60 @@ +import os +from typing import Optional + +from agency_swarm.tools import BaseTool, ToolFactory +from pydantic import Field, model_validator +import importlib + + +class TestTool(BaseTool): + """ + This tool tests other tools defined in tools.py file with the given arguments. Make sure to define the run method before testing. + """ + chain_of_thought: str = Field( + ..., description="Think step by step to determine the correct arguments for testing.", exclude=True + ) + tool_name: str = Field(..., description="Name of the tool to be run. Must be defined in tools.py file.") + arguments: Optional[str] = Field(..., + description="Arguments to be passed to the tool for testing. " + "Must be in serialized json format.") + + def run(self): + os.chdir(self.shared_state.get("agency_path")) + os.chdir(self.shared_state.get("agent_name")) + + # import tool by self.tool_name from local tools.py file + try: + tool = ToolFactory.from_file(f"./tools/") + except Exception as e: + raise ValueError(f"Error importing tool {self.tool_name}: {e}") + finally: + os.chdir(self.shared_state.get("default_folder")) + + try: + output = tool.run() + except Exception as e: + raise ValueError(f"Error running tool {self.tool_name}: {e}") + finally: + os.chdir(self.shared_state.get("default_folder")) + + if not output: + raise ValueError(f"Tool {self.tool_name} did not return any output.") + + return "Successfully initialized and ran tool. Output: " + output + + @model_validator(mode="after") + def validate_tool_name(self): + tool_path = os.path.join(self.shared_state.get("agency_path"), self.shared_state.get("agent_name")) + tool_path = os.path.join(str(tool_path), "tools") + tool_path = os.path.join(tool_path, self.tool_name + ".py") + + # check if tools.py file exists + if not os.path.isfile(tool_path): + available_tools = os.listdir(os.path.join(self.shared_state.get("agency_path"), self.shared_state.get("agent_name"))) + available_tools = [tool for tool in available_tools if tool.endswith(".py")] + available_tools = [tool for tool in available_tools if not tool.startswith("__") or tool.startswith(".")] + available_tools = [tool.replace(".py", "") for tool in available_tools] + available_tools = ", ".join(available_tools) + raise ValueError(f"Tool {self.tool_name} not found. Available tools are: {available_tools}") + + return True diff --git a/agency_swarm/agents/agent.py b/agency_swarm/agents/agent.py index 722be130..7c926044 100644 --- a/agency_swarm/agents/agent.py +++ b/agency_swarm/agents/agent.py @@ -34,6 +34,7 @@ def __init__( description: str = None, instructions: str = "", tools: List[Union[Type[BaseTool], Type[Retrieval], Type[CodeInterpreter]]] = None, + tools_folder: str = None, files_folder: Union[List[str], str] = None, schemas_folder: Union[List[str], str] = None, api_headers: Dict[str, Dict[str, str]] = None, @@ -51,6 +52,7 @@ def __init__( description (str, optional): A brief description of the agent's purpose. Defaults to None. instructions (str, optional): Path to a file containing specific instructions for the agent. Defaults to an empty string. tools (List[Union[Type[BaseTool], Type[Retrieval], Type[CodeInterpreter]]], optional): A list of tools (as classes) that the agent can use. Defaults to an empty list. + tools_folder (str, optional): Path to a directory containing tools associated with the agent. Each tool must be defined in a separate file. Defaults to None. files_folder (Union[List[str], str], optional): Path or list of paths to directories containing files associated with the agent. Defaults to None. schemas_folder (Union[List[str], str], optional): Path or list of paths to directories containing OpenAPI schemas associated with the agent. Defaults to None. api_headers (Dict[str,Dict[str, str]], optional): Headers to be used for the openapi requests. Each key must be a full filename from schemas_folder. Defaults to an empty dictionary. @@ -68,6 +70,7 @@ def __init__( self.instructions = instructions self.tools = tools[:] if tools is not None else [] self.tools = [tool for tool in self.tools if tool.__name__ != "ExampleTool"] + self.tools_folder = tools_folder self.files_folder = files_folder if files_folder else [] self.schemas_folder = schemas_folder if schemas_folder else [] self.api_headers = api_headers if api_headers else {} @@ -87,6 +90,7 @@ def __init__( self._read_instructions() self._upload_files() self._parse_schemas() + self._parse_tools_folder() # --- OpenAI Assistant Methods --- @@ -258,6 +262,8 @@ def add_tool(self, tool): return self.tools.append(tool) elif issubclass(tool, BaseTool): + if tool.__name__ == "ExampleTool": + return for t in self.tools: if t.__name__ == tool.__name__: self.tools.remove(t) @@ -330,6 +336,31 @@ def _parse_schemas(self): else: raise Exception("Schemas folder path must be a string or list of strings.") + def _parse_tools_folder(self): + if not self.tools_folder: + return + if not os.path.isdir(self.tools_folder): + self.tools_folder = os.path.join(self.get_class_folder_path(), self.tools_folder) + self.tools_folder = os.path.normpath(self.tools_folder) + if os.path.isdir(self.tools_folder): + f_paths = os.listdir(self.tools_folder) + f_paths = [f for f in f_paths if not f.startswith(".") or f.startswith("__")] + f_paths = [os.path.join(self.tools_folder, f) for f in f_paths] + for f_path in f_paths: + if not f_path.endswith(".py"): + continue + if os.path.isfile(f_path): + try: + tool = ToolFactory.from_file(f_path) + self.add_tool(tool) + except Exception as e: + print("Error parsing tool: " + os.path.basename(f_path)) + raise e + else: + raise Exception("Items in tools folder must be files: " + f_path) + else: + raise Exception("Tools folder path is not a directory.") + def get_openapi_schema(self, url): """Get openapi schema that contains all tools from the agent as different api paths. Make sure to call this after agency has been initialized.""" if self.assistant is None: diff --git a/agency_swarm/agents/browsing/BrowsingAgent.py b/agency_swarm/agents/browsing/BrowsingAgent.py index 84ee6cb6..72a718b0 100644 --- a/agency_swarm/agents/browsing/BrowsingAgent.py +++ b/agency_swarm/agents/browsing/BrowsingAgent.py @@ -1,8 +1,8 @@ from agency_swarm import Agent from agency_swarm.tools import Retrieval -from agency_swarm.tools.browsing import Scroll, SendKeys, ClickElement, ReadURL, AnalyzeContent, GoBack, SelectDropdown, \ +from .tools import Scroll, SendKeys, ClickElement, ReadURL, AnalyzeContent, GoBack, SelectDropdown, \ SolveCaptcha, ExportFile -from agency_swarm.tools.browsing.util.selenium import set_selenium_config +from .tools.util.selenium import set_selenium_config class BrowsingAgent(Agent): diff --git a/agency_swarm/tools/browsing/AnalyzeContent.py b/agency_swarm/agents/browsing/tools/AnalyzeContent.py similarity index 96% rename from agency_swarm/tools/browsing/AnalyzeContent.py rename to agency_swarm/agents/browsing/tools/AnalyzeContent.py index fb198fd3..59343fdf 100644 --- a/agency_swarm/tools/browsing/AnalyzeContent.py +++ b/agency_swarm/agents/browsing/tools/AnalyzeContent.py @@ -3,7 +3,7 @@ from agency_swarm.tools import BaseTool from pydantic import Field -from agency_swarm.tools.browsing.util import get_web_driver, set_web_driver, get_b64_screenshot +from .util import get_web_driver, set_web_driver, get_b64_screenshot from agency_swarm.util import get_openai_client diff --git a/agency_swarm/tools/browsing/ClickElement.py b/agency_swarm/agents/browsing/tools/ClickElement.py similarity index 95% rename from agency_swarm/tools/browsing/ClickElement.py rename to agency_swarm/agents/browsing/tools/ClickElement.py index dc9a8415..06579a05 100644 --- a/agency_swarm/tools/browsing/ClickElement.py +++ b/agency_swarm/agents/browsing/tools/ClickElement.py @@ -5,9 +5,9 @@ from selenium.webdriver.common.by import By from agency_swarm.tools import BaseTool -from agency_swarm.tools.browsing.util import get_b64_screenshot -from agency_swarm.tools.browsing.util import get_web_driver, set_web_driver -from agency_swarm.tools.browsing.util.highlights import highlight_elements_with_labels, remove_highlight_and_labels +from .util import get_b64_screenshot +from .util import get_web_driver, set_web_driver +from .util.highlights import highlight_elements_with_labels, remove_highlight_and_labels from agency_swarm.util import get_openai_client diff --git a/agency_swarm/tools/browsing/ExportFile.py b/agency_swarm/agents/browsing/tools/ExportFile.py similarity index 95% rename from agency_swarm/tools/browsing/ExportFile.py rename to agency_swarm/agents/browsing/tools/ExportFile.py index c4c04f35..dc82d89a 100644 --- a/agency_swarm/tools/browsing/ExportFile.py +++ b/agency_swarm/agents/browsing/tools/ExportFile.py @@ -2,7 +2,7 @@ import os from agency_swarm import BaseTool, get_openai_client -from agency_swarm.tools.browsing.util import get_web_driver +from .util import get_web_driver class ExportFile(BaseTool): diff --git a/agency_swarm/tools/browsing/GoBack.py b/agency_swarm/agents/browsing/tools/GoBack.py similarity index 83% rename from agency_swarm/tools/browsing/GoBack.py rename to agency_swarm/agents/browsing/tools/GoBack.py index 4592287e..1fb439bc 100644 --- a/agency_swarm/tools/browsing/GoBack.py +++ b/agency_swarm/agents/browsing/tools/GoBack.py @@ -2,7 +2,7 @@ from agency_swarm.tools import BaseTool -from agency_swarm.tools.browsing.util.selenium import get_web_driver, set_web_driver +from .util.selenium import get_web_driver, set_web_driver class GoBack(BaseTool): diff --git a/agency_swarm/tools/browsing/ReadURL.py b/agency_swarm/agents/browsing/tools/ReadURL.py similarity index 89% rename from agency_swarm/tools/browsing/ReadURL.py rename to agency_swarm/agents/browsing/tools/ReadURL.py index b3e987dd..33041666 100644 --- a/agency_swarm/tools/browsing/ReadURL.py +++ b/agency_swarm/agents/browsing/tools/ReadURL.py @@ -1,13 +1,9 @@ -import json -import os import time -from urllib.parse import urlparse from pydantic import Field -from selenium.common import WebDriverException from agency_swarm.tools import BaseTool -from agency_swarm.tools.browsing.util.selenium import get_web_driver, set_web_driver +from .util.selenium import get_web_driver, set_web_driver class ReadURL(BaseTool): diff --git a/agency_swarm/tools/browsing/Scroll.py b/agency_swarm/agents/browsing/tools/Scroll.py similarity index 90% rename from agency_swarm/tools/browsing/Scroll.py rename to agency_swarm/agents/browsing/tools/Scroll.py index 3f248a62..344cf572 100644 --- a/agency_swarm/tools/browsing/Scroll.py +++ b/agency_swarm/agents/browsing/tools/Scroll.py @@ -1,12 +1,9 @@ from typing import Literal -from agency_swarm.tools import BaseTool from pydantic import Field -import tempfile -import base64 -from agency_swarm.tools.browsing.util.selenium import get_web_driver, set_web_driver -from agency_swarm.util import get_openai_client +from agency_swarm.tools import BaseTool +from .util.selenium import get_web_driver, set_web_driver class Scroll(BaseTool): diff --git a/agency_swarm/tools/browsing/SelectDropdown.py b/agency_swarm/agents/browsing/tools/SelectDropdown.py similarity index 95% rename from agency_swarm/tools/browsing/SelectDropdown.py rename to agency_swarm/agents/browsing/tools/SelectDropdown.py index 3ac84e0d..675f278d 100644 --- a/agency_swarm/tools/browsing/SelectDropdown.py +++ b/agency_swarm/agents/browsing/tools/SelectDropdown.py @@ -5,9 +5,9 @@ from selenium.webdriver.support.select import Select from agency_swarm.tools import BaseTool -from agency_swarm.tools.browsing.util import get_b64_screenshot -from agency_swarm.tools.browsing.util import get_web_driver, set_web_driver -from agency_swarm.tools.browsing.util.highlights import highlight_elements_with_labels +from .util import get_b64_screenshot +from .util import get_web_driver, set_web_driver +from .util.highlights import highlight_elements_with_labels from agency_swarm.util import get_openai_client diff --git a/agency_swarm/tools/browsing/SendKeys.py b/agency_swarm/agents/browsing/tools/SendKeys.py similarity index 94% rename from agency_swarm/tools/browsing/SendKeys.py rename to agency_swarm/agents/browsing/tools/SendKeys.py index 9c20c873..4257a389 100644 --- a/agency_swarm/tools/browsing/SendKeys.py +++ b/agency_swarm/agents/browsing/tools/SendKeys.py @@ -1,17 +1,15 @@ import json import time -from typing import Literal from pydantic import Field from selenium.webdriver import Keys from selenium.webdriver.common.by import By -from selenium.webdriver.support.select import Select from agency_swarm.tools import BaseTool -from agency_swarm.tools.browsing.util import get_b64_screenshot, remove_highlight_and_labels -from agency_swarm.tools.browsing.util import get_web_driver, set_web_driver -from agency_swarm.tools.browsing.util.highlights import highlight_elements_with_labels from agency_swarm.util import get_openai_client +from .util import get_b64_screenshot +from .util import get_web_driver, set_web_driver +from .util.highlights import highlight_elements_with_labels class SendKeys(BaseTool): diff --git a/agency_swarm/tools/browsing/SolveCaptcha.py b/agency_swarm/agents/browsing/tools/SolveCaptcha.py similarity index 98% rename from agency_swarm/tools/browsing/SolveCaptcha.py rename to agency_swarm/agents/browsing/tools/SolveCaptcha.py index da942900..9378eeca 100644 --- a/agency_swarm/tools/browsing/SolveCaptcha.py +++ b/agency_swarm/agents/browsing/tools/SolveCaptcha.py @@ -7,8 +7,8 @@ from selenium.webdriver.support.wait import WebDriverWait from agency_swarm.tools import BaseTool -from agency_swarm.tools.browsing.util import get_b64_screenshot, remove_highlight_and_labels -from agency_swarm.tools.browsing.util.selenium import get_web_driver +from .util import get_b64_screenshot, remove_highlight_and_labels +from .util.selenium import get_web_driver from agency_swarm.util import get_openai_client diff --git a/agency_swarm/tools/browsing/__init__.py b/agency_swarm/agents/browsing/tools/__init__.py similarity index 100% rename from agency_swarm/tools/browsing/__init__.py rename to agency_swarm/agents/browsing/tools/__init__.py diff --git a/agency_swarm/tools/browsing/util/__init__.py b/agency_swarm/agents/browsing/tools/util/__init__.py similarity index 100% rename from agency_swarm/tools/browsing/util/__init__.py rename to agency_swarm/agents/browsing/tools/util/__init__.py diff --git a/agency_swarm/tools/browsing/util/get_b64_screenshot.py b/agency_swarm/agents/browsing/tools/util/get_b64_screenshot.py similarity index 100% rename from agency_swarm/tools/browsing/util/get_b64_screenshot.py rename to agency_swarm/agents/browsing/tools/util/get_b64_screenshot.py diff --git a/agency_swarm/tools/browsing/util/highlights.py b/agency_swarm/agents/browsing/tools/util/highlights.py similarity index 100% rename from agency_swarm/tools/browsing/util/highlights.py rename to agency_swarm/agents/browsing/tools/util/highlights.py diff --git a/agency_swarm/tools/browsing/util/selenium.py b/agency_swarm/agents/browsing/tools/util/selenium.py similarity index 100% rename from agency_swarm/tools/browsing/util/selenium.py rename to agency_swarm/agents/browsing/tools/util/selenium.py diff --git a/agency_swarm/agents/genesis/AgentCreator/instructions.md b/agency_swarm/agents/genesis/AgentCreator/instructions.md deleted file mode 100644 index 84cb306d..00000000 --- a/agency_swarm/agents/genesis/AgentCreator/instructions.md +++ /dev/null @@ -1,14 +0,0 @@ -# AgentCreator Agent Instructions - -You are an agent that creates other agents as instructed by the user. - -The user will communicate to you each agent that needs to be created. Below are your instructions that need to be followed for each agent. - -**Primary Instructions:** -1. First, read the `manifesto.md` file using `ReadFile` tool if you have not already done so. This file contains the agency manifesto that describes the new agency's purpose and goals. -2. Check if a similar agent is already available via the `GetAvailableAgents` tool. -3. If it is, use `ImportAgent` tool to import the agent and skip the following steps. Tell the user that the agent has been created. Prefer to import the agent, rather than creating it from scratch, if possible. -4. If a similar agent is not available, create a template folder for the agent using `CreateAgentTemplate` tool. Make sure to clarify any details with the user if needed. Instructions for this agent must include specific processes or functions that it needs to perform. -5. Tell the browsing agent to find the most relevant API for this agent in order to perform its functions. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. For CEO agents, you do not need to do this step and the next steps, you can simply tell the user that CEO agent has been created. Ceo agents do not need to utilize any APIs. -6. For non-CEO and non-available agents, after you receive the file_id with the API documentation from the browsing agent, send it to the OpenAPICreator agent using the `SendMessage` tool via the `message_files` parameter. Describe what tasks this agent needs to perform via this api, and which api this is. Try to trouble shoot any issues with these agents if they arise. For example, if the OpenAPICreator agent tells you that the file does not contain the necessary API documentation, please tell the BrowsingAgent to keep searching. Then, repeat this step. -7. After the OpenAPICreator tells you that the OpenAPI spec has been created, please notify the user. \ No newline at end of file diff --git a/agency_swarm/agents/genesis/OpenAPICreator/instructions.md b/agency_swarm/agents/genesis/OpenAPICreator/instructions.md deleted file mode 100644 index dad5d3b2..00000000 --- a/agency_swarm/agents/genesis/OpenAPICreator/instructions.md +++ /dev/null @@ -1,9 +0,0 @@ -# OpenAPICreator Instructions - -You are an agent that creates tools from OpenAPI schemas. User will provide you with a file with the API documentation webpage for the relevant api and a description of the tools and its purpose. - -**Here are your primary instructions:** -1. Explore the provided file with the myfiles_broswer tool to determine which endpoints are needed for the agent's purpose, communicated by the user. -2. If the file does not contain the actual API documentation page, please notify the user. Keep in mind that you do not need the full API documentation. You can make an educated guess if some information is not available. -3. Use `CreateToolsFromOpenAPISpec` to create the tools by defining the OpenAPI schema accordingly. Make sure to include all the relevant API endpoints that are needed for this agent to execute the tasks. -4. Repeat these steps for each new agent that needs to be created, as instructed by the user. \ No newline at end of file diff --git a/agency_swarm/agents/genesis/ToolCreator/instructions.md b/agency_swarm/agents/genesis/ToolCreator/instructions.md deleted file mode 100644 index 74586ae8..00000000 --- a/agency_swarm/agents/genesis/ToolCreator/instructions.md +++ /dev/null @@ -1,54 +0,0 @@ -# ToolCreator Agent Instructions - -You are an agent that creates tools for other agents as instructed by the user. -You can read the manifesto file for the agency that you are creating with the `ReadManifesto` tool. - -**Here are your primary instructions:** -1. Navigate to the corresponding agent's folder using `ChangeDir` tool. To analyze the current directory, use `ListDir` tool. -2. Start creating required tools for this agent in tools.py file. Make sure to read this file first with `ReadFile` tool. You must define run method before testing the tool. All tools should be production ready and should interact with the actual APIs. If you require any additional information, like API keys, please make sure to ask the user for it, and tell him where to find it. -3. Test each tool by running `TestTool` tool with the arguments that you want to pass to the tool. -4. Keep iterating on the tool until it works as expected. -5. When finished, navigate back to the agency folder using `ChangeDir` tool by using the relative path `../` and tell the user that you are done with the tools for this agent. - - - -### Custom Tool Example - -Tools are defined as classes that inherit from `BaseTool` class and implement the `run` method. BaseTool class inherits from Pydantic's BaseModel class, which allows you to define fields with descriptions using Pydantic's Field class. -You can add as many tools as needed inside `tools.py` file. Each tool should be defined as a separate class. - -```python -from agency_swarm.tools import BaseTool -from pydantic import Field - -class MyCustomTool(BaseTool): - """ - A brief description of what the custom tool does. - The docstring should clearly explain the tool's purpose and functionality for the agent. - """ - - # Define the fields with descriptions using Pydantic Field - example_field: str = Field( - ..., description="Description of the example field, explaining its purpose and usage." - ) - - # Additional fields as required - # ... - - def run(self): - """ - The implementation of the run method, where the tool's main functionality is executed. - This method should utilize the fields defined above to perform its task. - Doc string description is not required for this method. - """ - - # Your custom tool logic goes here - do_something(self.example_field) - - # Return the result of the tool's operation - return "Result of MyCustomTool operation" -``` - -You can include various API calls in your tool's implementation, after importing relevant libraries. For example, you can use `requests` library to make HTTP requests to external APIs. - -Please keep in mind that all tools should be production-ready. This is not a mockup, but a real tool that will be used by the user. Do not skip any logic when creating tools, and make sure to test them thoroughly. \ No newline at end of file diff --git a/agency_swarm/agents/genesis/__init__.py b/agency_swarm/agents/genesis/__init__.py deleted file mode 100644 index f0eea9de..00000000 --- a/agency_swarm/agents/genesis/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .GenesisCEO import GenesisCEO -from .AgentCreator import AgentCreator -from .ToolCreator import ToolCreator -from .OpenAPICreator import OpenAPICreator diff --git a/agency_swarm/cli.py b/agency_swarm/cli.py index 4951f732..13417f0d 100644 --- a/agency_swarm/cli.py +++ b/agency_swarm/cli.py @@ -1,14 +1,14 @@ import argparse - from agency_swarm.util import create_agent_template def main(): parser = argparse.ArgumentParser(description='Create agent template.') - subparsers = parser.add_subparsers(dest='create_template', help='Create agent template.') + subparsers = parser.add_subparsers(dest='command', help='Create agent template.') subparsers.required = True + # create-agent-template create_parser = subparsers.add_parser('create-agent-template', help='Create agent template folder locally.') create_parser.add_argument('--path', type=str, default="./", help='Path to create agent folder.') create_parser.add_argument('--use_txt', action='store_true', default=False, @@ -16,10 +16,17 @@ def main(): create_parser.add_argument('--name', type=str, help='Name of agent.') create_parser.add_argument('--description', type=str, help='Description of agent.') + # genesis-agency + genesis_parser = subparsers.add_parser('genesis', help='Start genesis agency.') + args = parser.parse_args() - if args.create_template == "create-agent-template": + if args.command == "create-agent-template": create_agent_template(args.name, args.description, args.path, args.use_txt) + elif args.command == "genesis": + from agency_swarm.agency.genesis import GenesisAgency + agency = GenesisAgency() + agency.run_demo() if __name__ == "__main__": diff --git a/agency_swarm/messages/message_output.py b/agency_swarm/messages/message_output.py index 8d43bea8..ca5f4d17 100644 --- a/agency_swarm/messages/message_output.py +++ b/agency_swarm/messages/message_output.py @@ -1,5 +1,6 @@ from typing import Literal import hashlib +from rich.markdown import Markdown from rich.console import Console from agency_swarm.util.oai import get_openai_client @@ -27,7 +28,7 @@ def hash_names_to_color(self): hash_obj = hashlib.md5(encoded_str) hash_int = int(hash_obj.hexdigest(), 16) colors = [ - 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', + 'green', 'yellow', 'blue', 'magenta', 'cyan', 'bright_white', ] color_index = hash_int % len(colors) return colors[color_index] @@ -37,13 +38,15 @@ def cprint(self): emoji = self.get_sender_emoji() - header = emoji + self.get_formatted_header() + header = emoji + " " + self.get_formatted_header() - color = self.hash_names_to_color() + # color = self.hash_names_to_color() - console.print(header, style=color) + console.print(header) - console.print(str(self.content), style=color) + md = Markdown(self.content) + + console.print(md) def get_formatted_header(self): if self.msg_type == "function": @@ -51,7 +54,7 @@ def get_formatted_header(self): return text if self.msg_type == "function_output": - text = f"{self.sender_name} βš™οΈFunction Output" + text = f"{self.sender_name} βš™οΈ Function Output" return text text = f"{self.sender_name} πŸ—£οΈ @{self.receiver_name}" diff --git a/agency_swarm/threads/thread.py b/agency_swarm/threads/thread.py index e7c10060..a698e8d2 100644 --- a/agency_swarm/threads/thread.py +++ b/agency_swarm/threads/thread.py @@ -2,6 +2,8 @@ import time from typing import Literal +from openai import BadRequestError + from agency_swarm.agents import Agent from agency_swarm.messages import MessageOutput from agency_swarm.user import User @@ -56,13 +58,7 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, ) while True: - # wait until run completes - while self.run.status in ['queued', 'in_progress']: - time.sleep(0.5) - self.run = self.client.beta.threads.runs.retrieve( - thread_id=self.thread.id, - run_id=self.run.id - ) + self.await_run_completion() # function execution if self.run.status == "requires_action": @@ -90,11 +86,28 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, tool_outputs.append({"tool_call_id": tool_call.id, "output": str(output)}) # submit tool outputs - self.run = self.client.beta.threads.runs.submit_tool_outputs( - thread_id=self.thread.id, - run_id=self.run.id, - tool_outputs=tool_outputs - ) + try: + self.run = self.client.beta.threads.runs.submit_tool_outputs( + thread_id=self.thread.id, + run_id=self.run.id, + tool_outputs=tool_outputs + ) + except BadRequestError as e: + if 'Runs in status "expired"' in e.message: + self.run = self.client.beta.threads.runs.create( + thread_id=self.thread.id, + assistant_id=recipient_agent.id, + ) + + self.await_run_completion() + + self.run = self.client.beta.threads.runs.submit_tool_outputs( + thread_id=self.thread.id, + run_id=self.run.id, + tool_outputs=tool_outputs + ) + else: + raise e # error elif self.run.status == "failed": raise Exception("Run Failed. Error: ", self.run.last_error) @@ -110,6 +123,14 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, return message + def await_run_completion(self): + while self.run.status in ['queued', 'in_progress', "cancelling"]: + time.sleep(0.5) + self.run = self.client.beta.threads.runs.retrieve( + thread_id=self.thread.id, + run_id=self.run.id + ) + def execute_tool(self, tool_call, recipient_agent=None): if not recipient_agent: recipient_agent = self.recipient_agent diff --git a/agency_swarm/tools/BaseTool.py b/agency_swarm/tools/BaseTool.py index 868a04ef..2056fd52 100644 --- a/agency_swarm/tools/BaseTool.py +++ b/agency_swarm/tools/BaseTool.py @@ -1,15 +1,33 @@ from abc import ABC, abstractmethod -from typing import Optional, Any +from typing import Optional, Any, ClassVar from instructor import OpenAISchema from pydantic import Field +class SharedState: + def __init__(self): + self.data = {} + + def set(self, key, value): + if not isinstance(key, str): + raise ValueError("Key must be a string") + self.data[key] = value + + def get(self, key, default=None): + if not isinstance(key, str): + raise ValueError("Key must be a string") + return self.data.get(key, default) + class BaseTool(OpenAISchema, ABC): - caller_agent: Optional[Any] = Field( - None, description="The agent that called this tool. This field will be removed from schema." - ) + shared_state: ClassVar[SharedState] = SharedState() + caller_agent: Any = None + + def __init__(self, **kwargs): + super().__init__(**kwargs) + # # Exclude 'run' method from Pydantic model fields + # self.model_fields.pop("run", None) @classmethod @property @@ -19,22 +37,17 @@ def openai_schema(cls): properties = schema.get("parameters", {}).get("properties", {}) properties.pop("caller_agent", None) - properties.pop("caller_agent_name", None) + properties.pop("shared_state", None) # If 'caller_agent' is in the required list, remove it required = schema.get("parameters", {}).get("required", []) if "caller_agent" in required: required.remove("caller_agent") - if "caller_agent_name" in required: - required.remove("caller_agent_name") + if "shared_state" in required: + required.remove("shared_state") return schema - def __init__(self, **kwargs): - super().__init__(**kwargs) - # # Exclude 'run' method from Pydantic model fields - # self.model_fields.pop("run", None) - @abstractmethod def run(self, **kwargs): pass diff --git a/agency_swarm/tools/ToolFactory.py b/agency_swarm/tools/ToolFactory.py index 3d70620b..cd56b9b1 100644 --- a/agency_swarm/tools/ToolFactory.py +++ b/agency_swarm/tools/ToolFactory.py @@ -1,4 +1,7 @@ +import importlib.util import inspect +import os +import sys from typing import Any, Dict, List, Type, Union import jsonref @@ -254,3 +257,27 @@ def callback(self): tools.append(ToolFactory.from_openai_schema(function, callback)) return tools + + @staticmethod + def from_file(file_path: str): + """Dynamically imports a class from a Python file, ensuring BaseTool itself is not imported.""" + # Extract class name from file path (assuming class name matches file name without .py extension) + class_name = os.path.basename(file_path) + if class_name.endswith('.py'): + class_name = class_name[:-3] # Remove .py extension + + # Load the module from the given file path + spec = importlib.util.spec_from_file_location(class_name, file_path) + module = importlib.util.module_from_spec(spec) + sys.modules[class_name] = module + spec.loader.exec_module(module) + + # Dynamically access the class based on the extracted class name + if hasattr(module, class_name): + tool = getattr(module, class_name) + else: + raise AttributeError(f"The class {class_name} was not found in {file_path}") + + return tool + + diff --git a/agency_swarm/tools/genesis/CreateManifesto.py b/agency_swarm/tools/genesis/CreateManifesto.py deleted file mode 100644 index 6e451b9f..00000000 --- a/agency_swarm/tools/genesis/CreateManifesto.py +++ /dev/null @@ -1,23 +0,0 @@ -import os - -from pydantic import Field, field_validator - -from agency_swarm import BaseTool - - -class CreateManifesto(BaseTool): - """ - This tool creates a manifesto for the agency and saves it to a markdown file. - """ - manifesto: str = Field( - ..., description="Manifesto for the agency, describing it's goals and additional context shared by all agents " - "in markdown format." - ) - - def run(self): - path = os.path.join("manifesto.md") - # save manifesto to manifesto.md file - with open(path, "w") as f: - f.write(self.manifesto) - - return f"Manifesto has been written to {path} file." \ No newline at end of file diff --git a/agency_swarm/tools/genesis/ImportLangchainTool.py b/agency_swarm/tools/genesis/ImportLangchainTool.py deleted file mode 100644 index e69de29b..00000000 diff --git a/agency_swarm/tools/genesis/ImportTool.py b/agency_swarm/tools/genesis/ImportTool.py deleted file mode 100644 index d9318bb1..00000000 --- a/agency_swarm/tools/genesis/ImportTool.py +++ /dev/null @@ -1,32 +0,0 @@ -# from pydantic import Field, field_validator -# -# from agency_swarm import BaseTool -# -# from agency_swarm.tools.genesis.util import get_modules -# -# available_tools = get_modules('agency_swarm.tools') -# -# print(available_tools) -# -# class ImportTool(BaseTool): -# """ -# This tool imports an existing tool from agency swarm framework. -# """ -# tool_name: str = Field(..., description=f"Name of the tool to be imported. Available tools are: {[item for sublist in available_tools.values() for item in sublist]}") -# -# def run(self): -# # find item in available_agents dict by value -# import_path = [k for k, v in available_tools.items() if self.tool_name in v][0] -# -# import_path = import_path.replace(f".{self.tool_name}", "") -# -# return "To import the tool, please add the following code: \n\n" + \ -# f"from {import_path} import {self.tool_name}\n" + \ -# f"agent = {self.tool_name}()" -# -# @field_validator("tool_name", mode='after') -# @classmethod -# def agent_name_exists(cls, v): -# if v not in [item for sublist in available_tools.values() for item in sublist]: -# raise ValueError(f"Tool with name {v} does not exist. Available tools are: {[item for sublist in available_tools.values() for item in sublist]}") -# return v \ No newline at end of file diff --git a/agency_swarm/tools/genesis/ReadManifesto.py b/agency_swarm/tools/genesis/ReadManifesto.py deleted file mode 100644 index 7e8c3a60..00000000 --- a/agency_swarm/tools/genesis/ReadManifesto.py +++ /dev/null @@ -1,23 +0,0 @@ -import os - -from pydantic import Field, field_validator - -from agency_swarm import BaseTool - - -class ReadManifesto(BaseTool): - """ - This tool reads a manifesto for the agency being created from a markdown file. - """ - - def run(self): - # read manifesto from manifesto.md file - try: - with open("agency_manifesto.md", "r") as f: - manifesto = f.read() - except FileNotFoundError: - return (f"Manifesto file not found. Please change your current working directory {os.getcwd()} to the " - f"root agency folder with ChangeDir tool.") - - return manifesto - diff --git a/agency_swarm/tools/genesis/TestTool.py b/agency_swarm/tools/genesis/TestTool.py deleted file mode 100644 index 810c6270..00000000 --- a/agency_swarm/tools/genesis/TestTool.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -from typing import Optional - -from agency_swarm.tools import BaseTool -from pydantic import Field, model_validator -import importlib - - -class TestTool(BaseTool): - """ - This tool tests other tools defined in tools.py file with the given arguments. Make sure to define the run method before testing. - """ - chain_of_thought: str = Field( - ..., description="Think step by step to determine the correct arguments for testing.", exclude=True - ) - tool_name: str = Field(..., description="Name of the tool to be run. Must be defined in tools.py file.") - arguments: Optional[str] = Field(..., - description="Arguments to be passed to the tool for testing. " - "Must be in serialized json format.") - - def run(self): - # import tool by self.tool_name from local tools.py file - tool = importlib.import_module('tools') - - try: - Tool = getattr(tool, self.tool_name) - except AttributeError: - return f"Tool {self.tool_name} not found in tools.py file." - - try: - tool = Tool(**eval(self.arguments)) - except Exception as e: - return f"Error initializing tool with arguments {self.arguments}. Error: {e}" - - output = tool.run() - - if not output: - raise ValueError(f"Tool {self.tool_name} did not return any output.") - - return "Successfully initialized and ran tool. Output: " + output - - @model_validator(mode="after") - def validate_tool_name(self): - # check if tools.py file exists - if not os.path.isfile("tools.py"): - raise ValueError(f"tools.py file does not exist. Please use ListDir tool to check the contents of the " - f"current folder and ChangeDri to navigate to the correct folder.") diff --git a/agency_swarm/tools/genesis/__init__.py b/agency_swarm/tools/genesis/__init__.py deleted file mode 100644 index 922cc877..00000000 --- a/agency_swarm/tools/genesis/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .CreateAgentTemplate import CreateAgentTemplate -from .CreateAgencyFolder import CreateAgencyFolder -from .ReadManifesto import ReadManifesto -from .TestTool import TestTool -from .ImportAgent import ImportAgent -# from .ImportTool import ImportTool -from .GetAvailableAgents import GetAvailableAgents -from .FinalizeAgency import FinalizeAgency diff --git a/agency_swarm/tools/openapi/__init__.py b/agency_swarm/tools/openapi/__init__.py deleted file mode 100644 index 4bf789e0..00000000 --- a/agency_swarm/tools/openapi/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .CreateToolsFromOpenAPISpec import CreateToolsFromOpenAPISpec \ No newline at end of file diff --git a/agency_swarm/util/create_agent_template.py b/agency_swarm/util/create_agent_template.py index 9614343b..b55f6510 100644 --- a/agency_swarm/util/create_agent_template.py +++ b/agency_swarm/util/create_agent_template.py @@ -63,22 +63,20 @@ def create_agent_template(agent_name=None, # create files folder os.mkdir(path + "files") os.mkdir(path + "schemas") + os.mkdir(path + "tools") # create tools file - with open(path + "tools.py", "w") as f: - f.write(tools_template) + with open(path + "tools/" + "ExampleTool.py", "w") as f: + f.write(example_tool_template) + + with open(path + "tools/" + "__init__.py", "w") as f: + f.write("") print("Agent folder created successfully.") print(f"Import it with: from {folder_name} import {class_name}") -agent_template = """ -before_names = dir() -from .tools import * -current_names = dir() -imported_tool_objects = [globals()[name] for name in current_names if name not in before_names and isinstance(globals()[name], type) and issubclass(globals()[name], BaseTool)] -imported_tool_objects = [tool for tool in imported_tool_objects if tool.__name__ != "BaseTool"] -from agency_swarm.agents import Agent +agent_template = """from agency_swarm.agents import Agent {code_interpreter_import} class {class_name}(Agent): @@ -89,11 +87,12 @@ def __init__(self): instructions="./instructions.{ext}", files_folder="./files", schemas_folder="./schemas", - tools=imported_tool_objects + [{code_interpreter}] + tools=[{code_interpreter}], + tools_folder="./tools" ) """ -tools_template = """from agency_swarm.tools import BaseTool +example_tool_template = """from agency_swarm.tools import BaseTool from pydantic import Field diff --git a/tests/data/tools/ExampleTool1.py b/tests/data/tools/ExampleTool1.py new file mode 100644 index 00000000..91b9898f --- /dev/null +++ b/tests/data/tools/ExampleTool1.py @@ -0,0 +1,16 @@ +from agency_swarm.tools import BaseTool +from pydantic import Field + + +class ExampleTool1(BaseTool): + """Enter your tool description here. It should be informative for the Agent.""" + content: str = Field( + ..., description="Enter parameter descriptions using pydantic for the model here." + ) + + def run(self): + # Enter your tool code here. It should return a string. + + # do_something(self.content) + + return "Tool output" \ No newline at end of file diff --git a/tests/test_tool_factory.py b/tests/test_tool_factory.py index 61730334..2dcc0f1c 100644 --- a/tests/test_tool_factory.py +++ b/tests/test_tool_factory.py @@ -117,6 +117,15 @@ def test_get_headers_openapi_schema(self): print(output) + def test_import_from_file(self): + tool = ToolFactory.from_file("./data/tools/ExampleTool1.py") + + print(tool) + + self.assertTrue(tool.__name__ == "ExampleTool1") + + self.assertTrue(tool(content='test').run() == "Tool output") + From c572417004eea2896279a27e09fdbd3dec4b3bd8 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Tue, 6 Feb 2024 10:44:10 +0400 Subject: [PATCH 04/15] Fixed importing tools from folder --- agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py | 2 +- agency_swarm/agents/agent.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py b/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py index d27d3790..6d26daed 100644 --- a/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py +++ b/agency_swarm/agency/genesis/ToolCreator/tools/TestTool.py @@ -52,7 +52,7 @@ def validate_tool_name(self): if not os.path.isfile(tool_path): available_tools = os.listdir(os.path.join(self.shared_state.get("agency_path"), self.shared_state.get("agent_name"))) available_tools = [tool for tool in available_tools if tool.endswith(".py")] - available_tools = [tool for tool in available_tools if not tool.startswith("__") or tool.startswith(".")] + available_tools = [tool for tool in available_tools if not tool.startswith("__") and not tool.startswith(".")] available_tools = [tool.replace(".py", "") for tool in available_tools] available_tools = ", ".join(available_tools) raise ValueError(f"Tool {self.tool_name} not found. Available tools are: {available_tools}") diff --git a/agency_swarm/agents/agent.py b/agency_swarm/agents/agent.py index 7c926044..d432fb4a 100644 --- a/agency_swarm/agents/agent.py +++ b/agency_swarm/agents/agent.py @@ -52,7 +52,7 @@ def __init__( description (str, optional): A brief description of the agent's purpose. Defaults to None. instructions (str, optional): Path to a file containing specific instructions for the agent. Defaults to an empty string. tools (List[Union[Type[BaseTool], Type[Retrieval], Type[CodeInterpreter]]], optional): A list of tools (as classes) that the agent can use. Defaults to an empty list. - tools_folder (str, optional): Path to a directory containing tools associated with the agent. Each tool must be defined in a separate file. Defaults to None. + tools_folder (str, optional): Path to a directory containing tools associated with the agent. Each tool must be defined in a separate file. File must be named as the class name of the tool. Defaults to None. files_folder (Union[List[str], str], optional): Path or list of paths to directories containing files associated with the agent. Defaults to None. schemas_folder (Union[List[str], str], optional): Path or list of paths to directories containing OpenAPI schemas associated with the agent. Defaults to None. api_headers (Dict[str,Dict[str, str]], optional): Headers to be used for the openapi requests. Each key must be a full filename from schemas_folder. Defaults to an empty dictionary. @@ -344,7 +344,7 @@ def _parse_tools_folder(self): self.tools_folder = os.path.normpath(self.tools_folder) if os.path.isdir(self.tools_folder): f_paths = os.listdir(self.tools_folder) - f_paths = [f for f in f_paths if not f.startswith(".") or f.startswith("__")] + f_paths = [f for f in f_paths if not f.startswith(".") and not f.startswith("__")] f_paths = [os.path.join(self.tools_folder, f) for f in f_paths] for f_path in f_paths: if not f_path.endswith(".py"): From 66dfa22ac10e23a38aa46844627cbdff75f99fe6 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Tue, 6 Feb 2024 10:46:32 +0400 Subject: [PATCH 05/15] Added param for example tools in create_agent_template.py --- .../AgentCreator/tools/CreateAgentTemplate.py | 3 ++- agency_swarm/util/create_agent_template.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py b/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py index a81f93dd..2617a07c 100644 --- a/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py +++ b/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py @@ -40,7 +40,8 @@ def run(self): create_agent_template(self.agent_name, self.agent_description, instructions=self.instructions, - code_interpreter=True if "CodeInterpreter" in self.default_tools else None) + code_interpreter=True if "CodeInterpreter" in self.default_tools else None, + include_example_tool=False) # add agent on second line to agency.py with open("agency.py", "r") as f: diff --git a/agency_swarm/util/create_agent_template.py b/agency_swarm/util/create_agent_template.py index b55f6510..b0eac8f4 100644 --- a/agency_swarm/util/create_agent_template.py +++ b/agency_swarm/util/create_agent_template.py @@ -6,7 +6,8 @@ def create_agent_template(agent_name=None, path="./", instructions=None, code_interpreter=False, - use_txt=False): + use_txt=False, + include_example_tool=True): if not agent_name: agent_name = input("Enter agent name: ") if not agent_description: @@ -17,10 +18,10 @@ def create_agent_template(agent_name=None, if not os.path.isfile(os.path.join(path, agency_manifesto)): with open(os.path.join(path, agency_manifesto), "w") as f: f.write("As a member of our Agency, please find below the guiding principles and values that constitute " - "our Agency Manifesto:\n\n")\ - + "our Agency Manifesto:\n\n") \ + \ class_name = agent_name.replace(" ", "").strip() - folder_name = agent_name # .lower().replace(" ", "_").strip() + folder_name = agent_name # .lower().replace(" ", "_").strip() # create or append to init file global_init_path = os.path.join(path, "__init__.py") @@ -65,13 +66,13 @@ def create_agent_template(agent_name=None, os.mkdir(path + "schemas") os.mkdir(path + "tools") - # create tools file - with open(path + "tools/" + "ExampleTool.py", "w") as f: - f.write(example_tool_template) - with open(path + "tools/" + "__init__.py", "w") as f: f.write("") + if include_example_tool: + with open(path + "tools/" + "ExampleTool.py", "w") as f: + f.write(example_tool_template) + print("Agent folder created successfully.") print(f"Import it with: from {folder_name} import {class_name}") From ee0249ef342bd701774dc5969ac4b78ae731712b Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Tue, 6 Feb 2024 11:02:04 +0400 Subject: [PATCH 06/15] Fix typo --- agency_swarm/util/create_agent_template.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agency_swarm/util/create_agent_template.py b/agency_swarm/util/create_agent_template.py index b0eac8f4..7ca25360 100644 --- a/agency_swarm/util/create_agent_template.py +++ b/agency_swarm/util/create_agent_template.py @@ -18,8 +18,8 @@ def create_agent_template(agent_name=None, if not os.path.isfile(os.path.join(path, agency_manifesto)): with open(os.path.join(path, agency_manifesto), "w") as f: f.write("As a member of our Agency, please find below the guiding principles and values that constitute " - "our Agency Manifesto:\n\n") \ - \ + "our Agency Manifesto:\n\n") + class_name = agent_name.replace(" ", "").strip() folder_name = agent_name # .lower().replace(" ", "_").strip() From fc64cec96f73b726d75e992b85bfd0885e019497 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Mon, 12 Feb 2024 08:16:30 +0400 Subject: [PATCH 07/15] Improved genesis agency tools and instructions --- .../agency/genesis/AgentCreator/instructions.md | 8 +++++--- .../genesis/AgentCreator/tools/CreateAgentTemplate.py | 7 +++++-- .../genesis/GenesisCEO/tools/CreateAgencyFolder.py | 4 ++-- .../agency/genesis/OpenAPICreator/instructions.md | 6 +++--- .../agency/genesis/ToolCreator/tools/CreateTool.py | 2 +- agency_swarm/agency/genesis/manifesto.md | 11 +++++++++-- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/agency_swarm/agency/genesis/AgentCreator/instructions.md b/agency_swarm/agency/genesis/AgentCreator/instructions.md index 89c94e0c..86a6012c 100644 --- a/agency_swarm/agency/genesis/AgentCreator/instructions.md +++ b/agency_swarm/agency/genesis/AgentCreator/instructions.md @@ -6,6 +6,8 @@ The user will communicate to you each agent that needs to be created. Below are **Primary Instructions:** 1. First, read the manifesto using `ReadManifesto` tool if you have not already done so. This file contains the agency manifesto that describes the agency's purpose and goals. -2. Think if the agent you are creating needs to utilize any APIs. If it does, tell the OpenAPICreator agent to create API schemas for this agent. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. CEO agents do not need to perform any API calls or use any tools, so you can skip to step 7. -3. For agents that do not need to utilize any APIs to perform their roles, tell the ToolCreator agent to create tools for this agent. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. -4. If there are no issues and tools or APIs have been created, notify the user that the agent has been created. Otherwise, try to resolve any issues with other agents before reporting back. \ No newline at end of file +2. Then, create a new agent using `CreateAgentTemplate` function. +3. Think if the agent you are creating needs to utilize any APIs. If it does, tell the OpenAPICreator agent to create API schemas for this agent. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. CEO agents do not need to perform any API calls or use any tools, so you can skip the following steps. +4. For agents that do not need to utilize any APIs to perform their roles, tell the ToolCreator agent to create tools for this agent. Make sure to also communicate the agent description, name and a summary of the processes that it needs to perform. +5. If there are no issues and tools or APIs have been created, notify the user that the agent has been created. Otherwise, try to resolve any issues with other agents before reporting back. +6. Repeat the process for each agent that needs to be created. \ No newline at end of file diff --git a/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py b/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py index 2617a07c..2974c762 100644 --- a/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py +++ b/agency_swarm/agency/genesis/AgentCreator/tools/CreateAgentTemplate.py @@ -9,9 +9,10 @@ allowed_tools: List = ["CodeInterpreter"] + class CreateAgentTemplate(BaseTool): """ - This tool creates a template folder for a new agent that includes boilerplage code and instructions. + This tool creates a template folder for a new agent. Always use this tool first, before creating tools or APIs for the agent. """ agent_name: str = Field( ..., description="Name of the agent to be created. Cannot include special characters or spaces." @@ -20,7 +21,9 @@ class CreateAgentTemplate(BaseTool): ..., description="Description of the agent to be created." ) instructions: str = Field( - ..., description="Instructions for the agent to be created in markdown format." + ..., description="Instructions for the agent to be created in markdown format. " + "Instructions should include a specific step by step process that this agent must perform in order to execute its role." + "They should also be aligned with other agents' instructions in the same agency to ensure effective collaboration." ) default_tools: List[str] = Field( [], description=f"List of default tools to be included in the agent. Possible values are {allowed_tools}." diff --git a/agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py b/agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py index 9fe1b820..0a0119e5 100644 --- a/agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py +++ b/agency_swarm/agency/genesis/GenesisCEO/tools/CreateAgencyFolder.py @@ -14,8 +14,8 @@ class CreateAgencyFolder(BaseTool): This tool creates or modifies an agency folder. You can use it again with the same agency_name to modify a previously created agency, if the user wants to change the agency chart or the manifesto. """ agency_name: str = Field( - ..., description="Name of the agency to be created.", - examples=["AgencyName"] + ..., description="Name of the agency to be created. Must not contain spaces or special characters.", + examples=["agency-name", "my-agency", "example-agency"] ) agency_chart: str = Field( ..., description="Agency chart to be passed into the Agency class.", diff --git a/agency_swarm/agency/genesis/OpenAPICreator/instructions.md b/agency_swarm/agency/genesis/OpenAPICreator/instructions.md index 53f819d3..6d39560e 100644 --- a/agency_swarm/agency/genesis/OpenAPICreator/instructions.md +++ b/agency_swarm/agency/genesis/OpenAPICreator/instructions.md @@ -1,10 +1,10 @@ # OpenAPICreator Instructions -You are an agent that creates tools from OpenAPI schemas. User will provide you with a file with the API documentation webpage for the relevant api and a description of the tools and its purpose. +You are an agent that creates tools from OpenAPI schemas. User will provide you with a description of the agent's role. If the provided description does not require any API calls, please notify the user. **Here are your primary instructions:** 1. Think which API is needed for this agent's role, as communicated by the user. Then, tell the BrowsingAgent to find this API documentation page. -2. Explore the provided file with the myfiles_broswer tool to determine which endpoints are needed for this agent's role. +2. Explore the provided file from the BrowsingAgent with the `myfiles_broswer` tool to determine which endpoints are needed for this agent's role. 3. If the file does not contain the actual API documentation page, please notify the BrowsingAgent. Keep in mind that you do not need the full API documentation. You can make an educated guess if some information is not available. -4. Use `CreateToolsFromOpenAPISpec` to create the tools by defining the OpenAPI schema accordingly. Make sure to include all the relevant API endpoints that are needed for this agent to execute its role. +4. Use `CreateToolsFromOpenAPISpec` to create the tools by defining the OpenAPI schema accordingly. Make sure to include all the relevant API endpoints that are needed for this agent to execute its role from the provided file. 5. Repeat these steps for each new agent that needs to be created, as instructed by the user. \ No newline at end of file diff --git a/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py b/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py index f42fdf09..2c17d4d6 100644 --- a/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py +++ b/agency_swarm/agency/genesis/ToolCreator/tools/CreateTool.py @@ -31,7 +31,7 @@ def run(self): os.chdir(self.shared_state.get("default_folder")) - return f"Tool {self.tool_name} has been created successfully. You can now test it with TestTool." + return f"Tool {self.tool_name} has been created successfully for {self.shared_state.get('agent_name')} agent. You can now test it with TestTool function." diff --git a/agency_swarm/agency/genesis/manifesto.md b/agency_swarm/agency/genesis/manifesto.md index 26deb094..bce73a8b 100644 --- a/agency_swarm/agency/genesis/manifesto.md +++ b/agency_swarm/agency/genesis/manifesto.md @@ -1,8 +1,15 @@ # Genesis Agency Manifesto -You are a part of an open source agent orchestration framework called agency_swarm. +You are a part of a Genesis Agency for a framework called Agency Swarm. The goal of your agency is to create other agencies. Below is the description of the framework and the roles of the agents in this agency. **Agency Swarm is an open-source agent orchestration framework designed to automate and streamline AI agent development processes. It enables the creation of a collaborative swarm of agents (Agencies), each with distinct roles and capabilities. These agents are then able to talk to each other and collaborate on tasks, with the goal of achieving a common objective.** -Keep in mind that communication with the other agents via the `SendMessage` tool is synchronous. Other agents will not be executing any tasks after your receive the output of this tool. Please instruct the receiving agent to continue its execution, if needed. +### Roles of the Agents in this Agency + +1. **GenesisCEO**: The CEO of the Genesis Agency. The CEO is responsible for creating other agencies. The CEO will communicate with the user to understand their goals for the agency, its mission, and its processes. +2. **AgentCreator**: The AgentCreator agent creates other agents as instructed by the user. +3. **ToolCreator**: The ToolCreator agent creates tools for other agents, as instructed by the user. These tools are executed locally by the agents to perform their roles. +4. **OpenAPICreator**: The OpenAPICreator agent creates API schemas for other agents. These schemas are used by the agents to communicate with external APIs. + +Keep in mind that communication with the other agents via the `SendMessage` tool is synchronous. Other agents will not be executing any tasks after you receive the output of this tool. Please instruct the receiving agent to continue its execution, if needed. From 36b1faea8c3043ece539256fab9af501a14d7b93 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Mon, 12 Feb 2024 08:16:49 +0400 Subject: [PATCH 08/15] Increased gradio interface height --- agency_swarm/agency/agency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agency_swarm/agency/agency.py b/agency_swarm/agency/agency.py index 7006611c..8781a6b8 100644 --- a/agency_swarm/agency/agency.py +++ b/agency_swarm/agency/agency.py @@ -108,7 +108,7 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, return gen - def demo_gradio(self, height=600, dark_mode=True): + def demo_gradio(self, height=450, dark_mode=True): """ Launches a Gradio-based demo interface for the agency chatbot. From 3640acaf96354979e88d30f57e821f492c0bbe42 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Mon, 12 Feb 2024 08:18:08 +0400 Subject: [PATCH 09/15] Added full window option in selemium --- agency_swarm/agents/browsing/tools/util/selenium.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/agency_swarm/agents/browsing/tools/util/selenium.py b/agency_swarm/agents/browsing/tools/util/selenium.py index a9588ddd..548fc084 100644 --- a/agency_swarm/agents/browsing/tools/util/selenium.py +++ b/agency_swarm/agents/browsing/tools/util/selenium.py @@ -10,6 +10,7 @@ selenium_config = { "chrome_profile_path": None, "headless": False, + "full_page_screenshot": False, } @@ -56,7 +57,10 @@ def get_web_driver(): if selenium_config.get("headless", False): chrome_options.add_argument('--headless') - chrome_options.add_argument("--window-size=960,1080") + if selenium_config.get("full_page_screenshot", False): + chrome_options.add_argument("--start-maximized") + else: + chrome_options.add_argument("--window-size=960,1080") chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--disable-dev-shm-usage') chrome_options.add_argument("--disable-extensions") From 5954675d284131054d9496cea74c702459bd0d18 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Mon, 12 Feb 2024 08:21:02 +0400 Subject: [PATCH 10/15] Improved error handling in thread for long running agent conversations --- agency_swarm/threads/thread.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/agency_swarm/threads/thread.py b/agency_swarm/threads/thread.py index a698e8d2..380cb465 100644 --- a/agency_swarm/threads/thread.py +++ b/agency_swarm/threads/thread.py @@ -57,6 +57,7 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, assistant_id=recipient_agent.id, ) + run_failed = False while True: self.await_run_completion() @@ -94,6 +95,12 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, ) except BadRequestError as e: if 'Runs in status "expired"' in e.message: + self.client.beta.threads.messages.create( + thread_id=self.thread.id, + role="user", + content="Please repeat the exact same function calls again." + ) + self.run = self.client.beta.threads.runs.create( thread_id=self.thread.id, assistant_id=recipient_agent.id, @@ -101,6 +108,14 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, self.await_run_completion() + if self.run.status != "requires_action": + raise Exception("Run Failed. Error: ", self.run.last_error) + + # change tool call ids + tool_calls = self.run.required_action.submit_tool_outputs.tool_calls + for i, tool_call in enumerate(tool_calls): + tool_outputs[i]["tool_call_id"] = tool_call.id + self.run = self.client.beta.threads.runs.submit_tool_outputs( thread_id=self.thread.id, run_id=self.run.id, @@ -110,7 +125,15 @@ def get_completion(self, message: str, message_files=None, yield_messages=True, raise e # error elif self.run.status == "failed": - raise Exception("Run Failed. Error: ", self.run.last_error) + # retry run 1 time + if not run_failed and "something went wrong" in self.run.last_error: + self.run = self.client.beta.threads.runs.create( + thread_id=self.thread.id, + assistant_id=recipient_agent.id, + ) + run_failed = True + else: + raise Exception("Run Failed. Error: ", self.run.last_error) # return assistant message else: messages = self.client.beta.threads.messages.list( From ad9724395d96189d5866f51002d3a6ea15c7597c Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Mon, 12 Feb 2024 10:01:38 +0400 Subject: [PATCH 11/15] Added autocomplete to recepient agents in cli --- agency_swarm/agency/agency.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/agency_swarm/agency/agency.py b/agency_swarm/agency/agency.py index 8781a6b8..1700bd82 100644 --- a/agency_swarm/agency/agency.py +++ b/agency_swarm/agency/agency.py @@ -1,6 +1,7 @@ import inspect import json import os +import readline import shutil import uuid from enum import Enum @@ -63,6 +64,7 @@ def __init__(self, self.agents = [] self.agents_and_threads = {} self.main_recipients = [] + self.recipient_agents = None self.shared_files = shared_files if shared_files else [] self.settings_path = settings_path self.settings_callbacks = settings_callbacks @@ -241,15 +243,30 @@ def bot(original_message, history): demo.launch() return demo - def run_demo(self): + def recipient_agent_completer(self, text, state): + """ + Autocomplete completer for recipient agent names. """ - Runs a demonstration of the agency's capabilities in an interactive command line interface. + options = [agent for agent in self.recipient_agents if agent.lower().startswith(text.lower())] + if state < len(options): + return options[state] + else: + return None - This function continuously prompts the user for input and displays responses from the agency's main thread. It leverages the generator pattern for asynchronous message processing. + def setup_autocomplete(self): + """ + Sets up readline with the completer function. + """ + self.recipient_agents = [agent.name for agent in self.main_recipients] # Cache recipient agents for autocomplete + readline.set_completer(self.recipient_agent_completer) + readline.parse_and_bind('tab: complete') - Output: - Outputs the responses from the agency's main thread to the command line. + def run_demo(self): """ + Enhanced run_demo with autocomplete for recipient agent names. + """ + self.setup_autocomplete() # Prepare readline for autocomplete + while True: console.rule() text = input("πŸ‘€ USER: ") @@ -262,9 +279,10 @@ def run_demo(self): recipient_agent = text.split("@")[1].split(" ")[0] text = text.replace(f"@{recipient_agent}", "").strip() try: + recipient_agent = [agent for agent in self.recipient_agents if agent.lower() == recipient_agent.lower()][0] recipient_agent = self.get_agent_by_name(recipient_agent) except Exception as e: - print(e) + print(f"Recipient agent {recipient_agent} not found.") continue try: From d54bb8608549977143654139dd117a07e74ecff2 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Mon, 12 Feb 2024 10:02:23 +0400 Subject: [PATCH 12/15] Added openai_key argument and genesis cli command description --- README.md | 17 +++++++++++++++-- agency_swarm/cli.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d0f3c36c..e6ce7359 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,19 @@ Get completion from the agency: completion_output = agency.get_completion("Please create a new website for our client.", yield_messages=False) ``` -## Creating Agent Templates Locally (CLI) +# CLI + +## Genesis Agency + +The `genesis` command starts the genesis agency in your terminal to help you create new agencies and agents. + +#### **Command Syntax:** + +```bash +agency-swarm genesis [--openai_key "YOUR_API_KEY"] +``` + +## Creating Agent Templates Locally This CLI command simplifies the process of creating a structured environment for each agent. @@ -187,6 +199,7 @@ When you run the `create-agent-template` command, it creates the following folde └── AgentName/ # Directory for the specific agent β”œβ”€β”€ files/ # Directory for files that will be uploaded to openai β”œβ”€β”€ schemas/ # Directory for OpenAPI schemas to be converted into tools + β”œβ”€β”€ tools/ # Directory for tools to be imported by default. β”œβ”€β”€ AgentName.py # The main agent class file β”œβ”€β”€ __init__.py # Initializes the agent folder as a Python package β”œβ”€β”€ instructions.md or .txt # Instruction document for the agent @@ -199,7 +212,7 @@ This structure ensures that each agent has its dedicated space with all necessar ## Future Enhancements 1. [x] Creation of agencies that can autonomously create other agencies. -2. [ ] Asynchronous communication and task handling. +2. [x] Asynchronous communication and task handling. 3. [ ] Inter-agency communication for a self-expanding system. ## Contributing diff --git a/agency_swarm/cli.py b/agency_swarm/cli.py index 13417f0d..b48c09f1 100644 --- a/agency_swarm/cli.py +++ b/agency_swarm/cli.py @@ -1,4 +1,6 @@ import argparse +import os + from agency_swarm.util import create_agent_template @@ -18,12 +20,21 @@ def main(): # genesis-agency genesis_parser = subparsers.add_parser('genesis', help='Start genesis agency.') + genesis_parser.add_argument('--openai_key', default=None, type=str, help='OpenAI API key.') args = parser.parse_args() if args.command == "create-agent-template": create_agent_template(args.name, args.description, args.path, args.use_txt) elif args.command == "genesis": + if not os.getenv('OPENAI_API_KEY') and not args.openai_key: + print("OpenAI API key not set. " + "Please set it with --openai_key argument or by setting OPENAI_API_KEY environment variable.") + + if args.openai_key: + from agency_swarm import set_openai_key + set_openai_key(args.openai_key) + from agency_swarm.agency.genesis import GenesisAgency agency = GenesisAgency() agency.run_demo() From 70486634f123101e448c699c649bc5690f7c04e7 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Wed, 14 Feb 2024 07:48:30 +0400 Subject: [PATCH 13/15] Bump version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fb1a7bee..139731fe 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='agency-swarm', - version='0.1.1', + version='0.1.2', author='VRSEN', author_email='arseny9795@gmail.com', description='An opensource agent orchestration framework built on top of the latest OpenAI Assistants API.', From 5a1a28cd02ff71c0741905f5a8f27876383aa579 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Wed, 14 Feb 2024 07:51:15 +0400 Subject: [PATCH 14/15] Bump requirements --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index d15af606..b37ad323 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -openai==1.10.0 -instructor==0.4.8 +openai==1.12.0 +instructor==0.5.2 deepdiff==6.7.1 termcolor==2.3.0 python-dotenv==1.0.0 From 96e070c0ca18413d45122647d18cac9f89c0ec97 Mon Sep 17 00:00:00 2001 From: Arsenii Shatokhin Date: Wed, 14 Feb 2024 07:59:50 +0400 Subject: [PATCH 15/15] Adjusted test prompt --- tests/test_agency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_agency.py b/tests/test_agency.py index 776c760f..b08b7712 100644 --- a/tests/test_agency.py +++ b/tests/test_agency.py @@ -226,7 +226,7 @@ def test_7_async_agent_communication(self): time.sleep(10) - message = self.__class__.agency.get_completion("Please check response. If the you get TestAgent1's Response like that the message was sent to TestAgent2 or that the process was initiated, say 'success'. If you don't get TestAgent1's Response, or if you get a System Notification instead, or an error, say 'error'.", + message = self.__class__.agency.get_completion("Please check response. If the you get TestAgent1's Response from the function output, say 'success'. If you don't get TestAgent1's Response, or if you get a System Notification, or an error instead, say 'error'.", yield_messages=False) self.assertFalse('error' in message.lower())