From 05b0d3c11290e9a4cb5f7f4e0267654628d6ef82 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:22:40 +0530 Subject: [PATCH 01/35] Modified the coding tool --- superagi/tools/code/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py index ca9b8ee29..679aed2c9 100644 --- a/superagi/tools/code/tools.py +++ b/superagi/tools/code/tools.py @@ -28,7 +28,7 @@ class CodingTool(BaseTool): llm: Optional[BaseLlm] = None name = "CodingTool" description = ( - "Useful for writing, reviewing, and refactoring code. Can also fix bugs and explain programming concepts." + "Useful for writing, reviewing, and refactoring code. Can also fix bugs and explain programming concepts.Follow the specs from write_spec to write the code more efficiently" ) args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] From a54d72a4ba11eee9cd681352fe9f43e575f6724a Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:23:14 +0530 Subject: [PATCH 02/35] Added write spec tool for the coding tool --- superagi/tools/code/write_spec.py | 131 ++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 superagi/tools/code/write_spec.py diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py new file mode 100644 index 000000000..d5c980190 --- /dev/null +++ b/superagi/tools/code/write_spec.py @@ -0,0 +1,131 @@ +from typing import Type, Optional, List + +from pydantic import BaseModel, Field +from superagi.config.config import get_config +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os +from superagi.llms.base_llm import BaseLlm +from superagi.tools.base_tool import BaseTool +from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker + + +class WriteSpecSchema(BaseModel): + task_description: str = Field( + ..., + description="Specification task description.", + ) + + spec_file_name: str = Field( + ..., + description="Name of the file to write. Only include the file name. Don't include path." + ) + +class WriteSpecTool(BaseTool): + """ + Used to generate program specification. + + Attributes: + llm: LLM used for specification generation. + name : The name of tool. + description : The description of tool. + args_schema : The args schema. + goals : The goals. + """ + llm: Optional[BaseLlm] = None + agent_id: int = None + name = "WriteSpecTool" + description = ( + "A tool to write the spec of a program." + ) + args_schema: Type[WriteSpecSchema] = WriteSpecSchema + goals: List[str] = [] + + class Config: + arbitrary_types_allowed = True + + def _write_spec_to_file(self, spec_content: str, spec_file_name: str) -> str: + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = spec_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + spec_file_name + else: + final_path = os.getcwd() + "/" + spec_file_name + + try: + with open(final_path, mode="w") as spec_file: + spec_file.write(spec_content) + + with open(final_path, 'r') as spec_file: + resource = ResourceHelper.make_written_file_resource(file_name=spec_file_name, + agent_id=self.agent_id, file=spec_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(spec_file, path=resource.path) + logger.info(f"Specification {spec_file_name} saved successfully") + except Exception as err: + session.close() + return f"Error: {err}" + session.close() + + return "Specification saved successfully" + + except Exception as e: + return f"Error saving specification to file: {e}" + + def _execute(self, task_description: str, spec_file_name: str) -> str: + """ + Execute the write_spec tool. + + Args: + task_description : The task description. + spec_file_name: The name of the file where the generated specification will be saved. + + Returns: + Generated specification or error message. + """ + try: + prompt = """You are a super smart developer who has been asked to make a specification for a program. + + Your high-level goal is: + {goals} + + Please keep in mind the following when creating the specification: + 1. Be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. + 2. Lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + 3. List all non-standard dependencies that will have to be used. + + Write a specification for the following task: + {task} + """ + prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{task}", task_description) + messages = [{"role": "system", "content": prompt}] + + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + + # Save the specification to a file + write_result = self._write_spec_to_file(result["content"], spec_file_name) + if not write_result.startswith("Error"): + return result + '\n' + "Specification generated and saved successfully" + else: + return write_result + + except Exception as e: + logger.error(e) + return f"Error generating specification: {e}" \ No newline at end of file From 2fd17940a4671096b345abfc75917022c0a219d9 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:23:38 +0530 Subject: [PATCH 03/35] Added write test tool for the coding tool --- superagi/tools/code/write_test.py | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 superagi/tools/code/write_test.py diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py new file mode 100644 index 000000000..8f6f41e89 --- /dev/null +++ b/superagi/tools/code/write_test.py @@ -0,0 +1,74 @@ +from typing import Type, Optional, List + +from pydantic import BaseModel, Field + +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.llms.base_llm import BaseLlm +from superagi.tools.base_tool import BaseTool +from superagi.lib.logger import logger + + +class WriteTestSchema(BaseModel): + spec_description: str = Field( + ..., + description="Specification for generating tests.", + ) + + +class WriteTestTool(BaseTool): + """ + Used to generate pytest unit tests based on the specification. + + Attributes: + llm: LLM used for test generation. + name : The name of tool. + description : The description of tool. + args_schema : The args schema. + goals : The goals. + """ + llm: Optional[BaseLlm] = None + agent_id: int = None + name = "WriteTestTool" + description = ( + "You are a super smart developer using Test Driven Development to write tests according to a specification.\n" + "Please generate tests based on the above specification. The tests should be as simple as possible, " + "but still cover all the functionality.\n" + "Write it in the file" + ) + args_schema: Type[WriteTestSchema] = WriteTestSchema + goals: List[str] = [] + + class Config: + arbitrary_types_allowed = True + + def _execute(self, spec_description: str) -> str: + """ + Execute the write_test tool. + + Args: + spec_description : The specification description. + + Returns: + Generated pytest unit tests or error message. + """ + try: + prompt = """You are a super smart developer who practices Test Driven Development for writing tests according to a specification. + + Your high-level goal is: + {goals} + + Please generate pytest unit tests based on the following specification description: + {spec} + + The tests should be as simple as possible, but still cover all the functionality described in the specification. + """ + prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{spec}", spec_description) + messages = [{"role": "system", "content": prompt}] + + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + return result["content"] + + except Exception as e: + logger.error(e) + return f"Error generating tests: {e}" \ No newline at end of file From 0f62ab381df85bbfa0c8f7db0c3bdcf17e7ac487 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:39:03 +0530 Subject: [PATCH 04/35] updated json reponse --- superagi/tools/code/write_spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index d5c980190..ca3af35d1 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -122,7 +122,8 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: # Save the specification to a file write_result = self._write_spec_to_file(result["content"], spec_file_name) if not write_result.startswith("Error"): - return result + '\n' + "Specification generated and saved successfully" + result["specification"] = "Specification generated and saved successfully" + return result else: return write_result From 7ed8672d0b8461dc0b0e9c2948e4c0af8f2be7c1 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Wed, 21 Jun 2023 10:21:39 +0530 Subject: [PATCH 05/35] adding email tests and fixing json cleaner test --- superagi/tools/email/send_email.py | 2 +- tests/helper/test_json_cleaner.py | 2 +- tests/tools/email/__init__.py | 0 tests/tools/email/test_send_email.py | 70 ++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 tests/tools/email/__init__.py create mode 100644 tests/tools/email/test_send_email.py diff --git a/superagi/tools/email/send_email.py b/superagi/tools/email/send_email.py index 2d6641cf4..d475e2a71 100644 --- a/superagi/tools/email/send_email.py +++ b/superagi/tools/email/send_email.py @@ -57,7 +57,7 @@ def _execute(self, to: str, subject: str, body: str) -> str: body += f"\n{signature}" message.set_content(body) draft_folder = get_config('EMAIL_DRAFT_MODE_WITH_FOLDER') - send_to_draft = draft_folder is not None or draft_folder != "YOUR_DRAFTS_FOLDER" + send_to_draft = draft_folder is not None and draft_folder != "YOUR_DRAFTS_FOLDER" if message["To"] == "example@example.com" or send_to_draft: conn = ImapEmail().imap_open(draft_folder, email_sender, email_password) conn.append( diff --git a/tests/helper/test_json_cleaner.py b/tests/helper/test_json_cleaner.py index be64eaa6f..8579a9900 100644 --- a/tests/helper/test_json_cleaner.py +++ b/tests/helper/test_json_cleaner.py @@ -40,4 +40,4 @@ def test_clean_newline_spaces_json(): def test_has_newline_in_string(): test_str = r'{key: "value\n"\n \n}' result = JsonCleaner.check_and_clean_json(test_str) - assert result == '{key: "value\\n"}' + assert result == '{key: "value"}' diff --git a/tests/tools/email/__init__.py b/tests/tools/email/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/tools/email/test_send_email.py b/tests/tools/email/test_send_email.py new file mode 100644 index 000000000..16d802477 --- /dev/null +++ b/tests/tools/email/test_send_email.py @@ -0,0 +1,70 @@ +from unittest.mock import MagicMock + +import pytest +import imaplib +import time +from email.message import EmailMessage + +from superagi.config.config import get_config +from superagi.helper.imap_email import ImapEmail +from superagi.tools.email import send_email +from superagi.tools.email.send_email import SendEmailTool + +def test_send_to_draft(mocker): + + mock_get_config = mocker.patch('superagi.tools.email.send_email.get_config', autospec=True) + mock_get_config.side_effect = [ + 'test_sender@test.com', # EMAIL_ADDRESS + 'password', # EMAIL_PASSWORD + 'Test Signature', # EMAIL_SIGNATURE + "Draft", # EMAIL_DRAFT_MODE_WITH_FOLDER + 'smtp_host', # EMAIL_SMTP_HOST + 'smtp_port' # EMAIL_SMTP_PORT + ] + + + # Mocking the ImapEmail call + mock_imap_email = mocker.patch('superagi.tools.email.send_email.ImapEmail') + mock_imap_instance = mock_imap_email.return_value.imap_open.return_value + + # Mocking the SMTP call + mock_smtp = mocker.patch('smtplib.SMTP') + smtp_instance = mock_smtp.return_value + + # Test the SendEmailTool's execute method + send_email_tool = SendEmailTool() + result = send_email_tool._execute('mukunda@contlo.com', 'Test Subject', 'Test Body') + + # Assert the return value + assert result == 'Email went to Draft' + +def test_send_to_mailbox(mocker): + # Mocking the get_config calls + mock_get_config = mocker.patch('superagi.tools.email.send_email.get_config') + mock_get_config.side_effect = [ + 'test_sender@test.com', # EMAIL_ADDRESS + 'password', # EMAIL_PASSWORD + 'Test Signature', # EMAIL_SIGNATURE + "YOUR_DRAFTS_FOLDER", # EMAIL_DRAFT_MODE_WITH_FOLDER + 'smtp_host', # EMAIL_SMTP_HOST + 'smtp_port' # EMAIL_SMTP_PORT + ] + + # mock_get_config.return_value = 'True' + # Mocking the ImapEmail call + mock_imap_email = mocker.patch('superagi.tools.email.send_email.ImapEmail') + mock_imap_instance = mock_imap_email.return_value.imap_open.return_value + + # Mocking the SMTP call + mock_smtp = mocker.patch('smtplib.SMTP') + smtp_instance = mock_smtp.return_value + + # Test the SendEmailTool's execute method + send_email_tool = SendEmailTool() + result = send_email_tool._execute('test_receiver@test.com', 'Test Subject', 'Test Body') + + # Assert that the ImapEmail was not called (no draft mode) + mock_imap_email.assert_not_called() + + # Assert the return value + assert result == 'Email was sent to test_receiver@test.com' \ No newline at end of file From 9aaba15fd758b583dd751c81048316061ea05beb Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 13:57:36 +0530 Subject: [PATCH 06/35] Added Add to file logic in all file --- superagi/tools/code/tools.py | 134 ++++++++++++++++++++++++++---- superagi/tools/code/write_spec.py | 3 +- superagi/tools/code/write_test.py | 68 +++++++++++++-- 3 files changed, 180 insertions(+), 25 deletions(-) diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py index 679aed2c9..09b4bc51a 100644 --- a/superagi/tools/code/tools.py +++ b/superagi/tools/code/tools.py @@ -1,19 +1,23 @@ from typing import Type, Optional, List from pydantic import BaseModel, Field - +from superagi.config.config import get_config from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker class CodingSchema(BaseModel): - task_description: str = Field( + spec_description: str = Field( ..., - description="Coding task description.", + description="Specification for generating tests.", ) - class CodingTool(BaseTool): """ Used to generate code. @@ -26,9 +30,14 @@ class CodingTool(BaseTool): goals : The goals. """ llm: Optional[BaseLlm] = None + agent_id: int = None name = "CodingTool" description = ( - "Useful for writing, reviewing, and refactoring code. Can also fix bugs and explain programming concepts.Follow the specs from write_spec to write the code more efficiently" + "You will get instructions for code to write. You will write a very long answer. " + "Make sure that every detail of the architecture is, in the end, implemented as code. " + "Think step by step and reason yourself to the right decisions to make sure we get it right. " + "You will first lay out the names of the core classes, functions, methods that will be necessary, " + "as well as a quick comment on their purpose. Then you will output the content of each file including ALL code." ) args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] @@ -36,8 +45,66 @@ class CodingTool(BaseTool): class Config: arbitrary_types_allowed = True + + def _write_code_to_files(self, code_content: str) -> str: + try: + code_sections = code_content.split("\n[FILENAME]\n") + + for section in code_sections: + if not section.strip(): + continue + + lines = section.strip().split("\n") + file_name = lines[0].strip() + + file_name = file_name[:70] # Truncate long file names + + if len(lines) > 1 and "```" in lines[1]: + code = "\n".join(lines[2:-1]) + else: + code = "\n".join(lines[1:]) + + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + file_name + else: + final_path = os.getcwd() + "/" + file_name - def _execute(self, task_description: str): + try: + with open(final_path, mode="w") as code_file: + code_file.write(code) + + with open(final_path, 'r') as code_file: + resource = ResourceHelper.make_written_file_resource(file_name=file_name, + agent_id=self.agent_id, file=code_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(code_file, path=resource.path) + logger.info(f"Code {file_name} saved successfully") + except Exception as err: + session.close() + return f"Error: {err}" + + session.close() + + return "Code saved successfully" + + except Exception as e: + return f"Error saving code to files: {e}" + + def _execute(self, spec_description: str) -> str: """ Execute the code tool. @@ -48,23 +115,54 @@ def _execute(self, task_description: str): Generated code or error message. """ try: - prompt = """You're a top-notch coder, knowing all programming languages, software systems, and architecture. + prompt = """You are a super smart developer who has been asked to make a specification for a program. - Your high level goal is: + Your high-level goal is: {goals} - - Provide no information about who you are and focus on writing code. - Ensure code is bug and error free and explain complex concepts through comments - Respond in well-formatted markdown. Ensure code blocks are used for code sections. - - Write code to accomplish the following: - {task} + + You will get instructions for code to write. + You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. + Make sure that every detail of the architecture is, in the end, implemented as code. + + Think step by step and reason yourself to the right decisions to make sure we get it right. + You will start by laying out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + + Then you will output the content of each file including ALL code. + Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that + [FILENAME] is the lowercase file name including the file extension, + [LANG] is the markup code block language for the code's language, and [CODE] is the code: + ``` + [FILENAME] + [LANG] + [CODE] + ``` + + You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. + Please note that the code should be fully functional. No placeholders. + + Follow a language and framework appropriate best practice file naming convention. + Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. + Ensure to implement all code, if you are unsure, write a plausible implementation. + Include module dependency or package manager dependency definition file. + Before you finish, double check that all parts of the architecture is present in the files. + + Please generate code files based on the following specification description: + {spec} """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{task}", task_description) + prompt = prompt.replace("{task}", spec_description) messages = [{"role": "system", "content": prompt}] + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - return result["content"] + + # Save the code to files + save_result = self._write_code_to_files(result["content"]) + if not save_result.startswith("Error"): + return "Code generated and saved successfully" + else: + return save_result + except Exception as e: logger.error(e) - return f"Error generating text: {e}" \ No newline at end of file + return f"Error generating code: {e}" + \ No newline at end of file diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index ca3af35d1..40d04417a 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -122,8 +122,7 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: # Save the specification to a file write_result = self._write_spec_to_file(result["content"], spec_file_name) if not write_result.startswith("Error"): - result["specification"] = "Specification generated and saved successfully" - return result + return result["content"] + "Specification generated and saved successfully" else: return write_result diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 8f6f41e89..5545153aa 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -1,11 +1,16 @@ from typing import Type, Optional, List from pydantic import BaseModel, Field - +from superagi.config.config import get_config from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker class WriteTestSchema(BaseModel): @@ -13,6 +18,10 @@ class WriteTestSchema(BaseModel): ..., description="Specification for generating tests.", ) + test_file_name: str = Field( + ..., + description="Name of the file to write. Only include the file name. Don't include path." + ) class WriteTestTool(BaseTool): @@ -41,12 +50,55 @@ class WriteTestTool(BaseTool): class Config: arbitrary_types_allowed = True - def _execute(self, spec_description: str) -> str: + + def _write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = test_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + test_file_name + else: + final_path = os.getcwd() + "/" + test_file_name + + try: + with open(final_path, mode="w") as test_file: + test_file.write(tests_content) + + with open(final_path, 'r') as test_file: + resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, + agent_id=self.agent_id, file=test_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(test_file, path=resource.path) + logger.info(f"Tests {test_file_name} saved successfully") + except Exception as err: + session.close() + return f"Error: {err}" + session.close() + + return "Tests saved successfully" + + except Exception as e: + return f"Error saving tests to file: {e}" + + def _execute(self, spec_description: str, test_file_name: str) -> str: """ Execute the write_test tool. Args: spec_description : The specification description. + test_file_name: The name of the file where the generated tests will be saved. Returns: Generated pytest unit tests or error message. @@ -65,10 +117,16 @@ def _execute(self, spec_description: str) -> str: prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] - + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - return result["content"] - + + # Save the tests to a file + save_result = self._write_tests_to_file(result["content"], test_file_name) + if not save_result.startswith("Error"): + return "Tests generated and saved successfully" + else: + return save_result + except Exception as e: logger.error(e) return f"Error generating tests: {e}" \ No newline at end of file From d1a160f324e05c3ef4e7685d059536078fb9f6a8 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 19:53:34 +0530 Subject: [PATCH 07/35] Modified write code file --- superagi/tools/code/tools.py | 177 ++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 84 deletions(-) diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py index 09b4bc51a..d681ecf40 100644 --- a/superagi/tools/code/tools.py +++ b/superagi/tools/code/tools.py @@ -11,12 +11,12 @@ from superagi.helper.resource_helper import ResourceHelper from superagi.helper.s3_helper import S3Helper from sqlalchemy.orm import sessionmaker - +import re class CodingSchema(BaseModel): spec_description: str = Field( ..., - description="Specification for generating tests.", + description="Specification for generating code which is generated by WriteSpecTool", ) class CodingTool(BaseTool): """ @@ -46,123 +46,132 @@ class Config: arbitrary_types_allowed = True - def _write_code_to_files(self, code_content: str) -> str: - try: - code_sections = code_content.split("\n[FILENAME]\n") - - for section in code_sections: - if not section.strip(): - continue - - lines = section.strip().split("\n") - file_name = lines[0].strip() - - file_name = file_name[:70] # Truncate long file names - - if len(lines) > 1 and "```" in lines[1]: - code = "\n".join(lines[2:-1]) - else: - code = "\n".join(lines[1:]) - - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + file_name - else: - final_path = os.getcwd() + "/" + file_name - - try: - with open(final_path, mode="w") as code_file: - code_file.write(code) - - with open(final_path, 'r') as code_file: - resource = ResourceHelper.make_written_file_resource(file_name=file_name, - agent_id=self.agent_id, file=code_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(code_file, path=resource.path) - logger.info(f"Code {file_name} saved successfully") - except Exception as err: - session.close() - return f"Error: {err}" - - session.close() - - return "Code saved successfully" + def write_codes_to_file(self, codes_content: str, code_file_name: str) -> str: + """ + Write the generated codes to the specified file. - except Exception as e: - return f"Error saving code to files: {e}" + Args: + codes_content: The content (code) of the code. + code_file_name: Name of the file where the code will be written. + Returns: + A string indicating if the codes were saved successfully or an error message. + """ + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = code_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + code_file_name + else: + final_path = os.getcwd() + "/" + code_file_name + + with open(final_path, mode="w") as code_file: + code_file.write(codes_content) + + with open(final_path, 'r') as code_file: + resource = ResourceHelper.make_written_file_resource(file_name=code_file_name, + agent_id=self.agent_id, file=code_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(code_file, path=resource.path) + + logger.info(f"Code {code_file_name} saved successfully") + session.close() + return "Codes saved successfully" + except Exception as e: + session.close() + return f"Error saving codes to file: {e}" + def _execute(self, spec_description: str) -> str: """ - Execute the code tool. + Execute the write_code tool. Args: - task_description : The task description. + spec_description : The specification description. + code_file_name: The name of the file where the generated codes will be saved. Returns: - Generated code or error message. + Generated codes files or error message. """ try: - prompt = """You are a super smart developer who has been asked to make a specification for a program. - + prompt = """You are a super smart developer who practices good Development for writing code according to a specification. + Your high-level goal is: {goals} - + + Use this specs for generating the code: + {spec} + You will get instructions for code to write. You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. Make sure that every detail of the architecture is, in the end, implemented as code. - + Think step by step and reason yourself to the right decisions to make sure we get it right. - You will start by laying out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. - + You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + Then you will output the content of each file including ALL code. Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that [FILENAME] is the lowercase file name including the file extension, [LANG] is the markup code block language for the code's language, and [CODE] is the code: - ``` + [FILENAME] - [LANG] + ```[LANG] [CODE] ``` - + You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. Please note that the code should be fully functional. No placeholders. - + Follow a language and framework appropriate best practice file naming convention. Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. Ensure to implement all code, if you are unsure, write a plausible implementation. Include module dependency or package manager dependency definition file. Before you finish, double check that all parts of the architecture is present in the files. - - Please generate code files based on the following specification description: - {spec} """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{task}", spec_description) + prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - - # Save the code to files - save_result = self._write_code_to_files(result["content"]) - if not save_result.startswith("Error"): - return "Code generated and saved successfully" - else: - return save_result + # Get all filenames and corresponding code blocks + regex = r"(\S+?)\n```\S+\n(.+?)```" + matches = re.finditer(regex, result["content"], re.DOTALL) + + # Save each file + for match in matches: + # Get the filename + file_name = re.sub(r'[<>"|?*]', "", match.group(1)) + + # Get the code + code = match.group(2) + + # Ensure file_name is not empty + if file_name.strip(): + save_result = self.write_codes_to_file(code, file_name) + if save_result.startswith("Error"): + return save_result + + # Get README contents and save + split_result = result["content"].split("```") + if len(split_result) > 0: + readme = split_result[0] + save_readme_result = self.write_codes_to_file(readme, "README.md") + if save_readme_result.startswith("Error"): + return save_readme_result + + return "codes generated and saved successfully" except Exception as e: logger.error(e) - return f"Error generating code: {e}" - \ No newline at end of file + return f"Error generating codes: {e}" \ No newline at end of file From 5b3305d94abbc8e0b72d86e1f76db0189850d7db Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 19:54:22 +0530 Subject: [PATCH 08/35] Added write_test file logic --- superagi/tools/code/write_test.py | 91 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 5545153aa..e01a1821a 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -11,12 +11,13 @@ from superagi.helper.resource_helper import ResourceHelper from superagi.helper.s3_helper import S3Helper from sqlalchemy.orm import sessionmaker +import re class WriteTestSchema(BaseModel): spec_description: str = Field( ..., - description="Specification for generating tests.", + description="Specification for generating tests generated by WriteSpecTool", ) test_file_name: str = Field( ..., @@ -51,47 +52,48 @@ class Config: arbitrary_types_allowed = True - def _write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: + def write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = test_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + test_file_name + else: + final_path = os.getcwd() + "/" + test_file_name + try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = test_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + test_file_name - else: - final_path = os.getcwd() + "/" + test_file_name - - try: - with open(final_path, mode="w") as test_file: - test_file.write(tests_content) - - with open(final_path, 'r') as test_file: - resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, - agent_id=self.agent_id, file=test_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(test_file, path=resource.path) - logger.info(f"Tests {test_file_name} saved successfully") - except Exception as err: - session.close() - return f"Error: {err}" + with open(final_path, mode="w") as test_file: + test_file.write(tests_content) + + with open(final_path, 'r') as test_file: + resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, + agent_id=self.agent_id, file=test_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(test_file, path=resource.path) + logger.info(f"Tests {test_file_name} saved successfully") + except Exception as err: session.close() + return f"Error: {err}" + session.close() - return "Tests saved successfully" - - except Exception as e: - return f"Error saving tests to file: {e}" + return "Tests saved successfully" + + except Exception as e: + return f"Error saving tests to file: {e}" + def _execute(self, spec_description: str, test_file_name: str) -> str: """ Execute the write_test tool. @@ -120,13 +122,20 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + # Extract the code part using regular expression + code = re.search(r'(?<=```python).*?(?=```)', result["content"], re.DOTALL) + if code: + code_content = code.group(0).strip() + else: + return "Unable to extract code from the response" + # Save the tests to a file - save_result = self._write_tests_to_file(result["content"], test_file_name) + save_result = self.write_tests_to_file(code_content, test_file_name) if not save_result.startswith("Error"): return "Tests generated and saved successfully" else: return save_result - + except Exception as e: logger.error(e) - return f"Error generating tests: {e}" \ No newline at end of file + return f"Error generating tests: {e}" From 4d9534040f74c571acecb49414064e56b0c117bd Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 19:56:05 +0530 Subject: [PATCH 09/35] Changes the file name --- superagi/tools/code/tools.py | 177 ----------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 superagi/tools/code/tools.py diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py deleted file mode 100644 index d681ecf40..000000000 --- a/superagi/tools/code/tools.py +++ /dev/null @@ -1,177 +0,0 @@ -from typing import Type, Optional, List - -from pydantic import BaseModel, Field -from superagi.config.config import get_config -from superagi.agent.agent_prompt_builder import AgentPromptBuilder -import os -from superagi.llms.base_llm import BaseLlm -from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger -from superagi.models.db import connect_db -from superagi.helper.resource_helper import ResourceHelper -from superagi.helper.s3_helper import S3Helper -from sqlalchemy.orm import sessionmaker -import re - -class CodingSchema(BaseModel): - spec_description: str = Field( - ..., - description="Specification for generating code which is generated by WriteSpecTool", - ) -class CodingTool(BaseTool): - """ - Used to generate code. - - Attributes: - llm: LLM used for code generation. - name : The name of tool. - description : The description of tool. - args_schema : The args schema. - goals : The goals. - """ - llm: Optional[BaseLlm] = None - agent_id: int = None - name = "CodingTool" - description = ( - "You will get instructions for code to write. You will write a very long answer. " - "Make sure that every detail of the architecture is, in the end, implemented as code. " - "Think step by step and reason yourself to the right decisions to make sure we get it right. " - "You will first lay out the names of the core classes, functions, methods that will be necessary, " - "as well as a quick comment on their purpose. Then you will output the content of each file including ALL code." - ) - args_schema: Type[CodingSchema] = CodingSchema - goals: List[str] = [] - - class Config: - arbitrary_types_allowed = True - - - def write_codes_to_file(self, codes_content: str, code_file_name: str) -> str: - """ - Write the generated codes to the specified file. - - Args: - codes_content: The content (code) of the code. - code_file_name: Name of the file where the code will be written. - - Returns: - A string indicating if the codes were saved successfully or an error message. - """ - try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = code_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + code_file_name - else: - final_path = os.getcwd() + "/" + code_file_name - - with open(final_path, mode="w") as code_file: - code_file.write(codes_content) - - with open(final_path, 'r') as code_file: - resource = ResourceHelper.make_written_file_resource(file_name=code_file_name, - agent_id=self.agent_id, file=code_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(code_file, path=resource.path) - - logger.info(f"Code {code_file_name} saved successfully") - session.close() - return "Codes saved successfully" - except Exception as e: - session.close() - return f"Error saving codes to file: {e}" - - def _execute(self, spec_description: str) -> str: - """ - Execute the write_code tool. - - Args: - spec_description : The specification description. - code_file_name: The name of the file where the generated codes will be saved. - - Returns: - Generated codes files or error message. - """ - try: - prompt = """You are a super smart developer who practices good Development for writing code according to a specification. - - Your high-level goal is: - {goals} - - Use this specs for generating the code: - {spec} - - You will get instructions for code to write. - You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. - Make sure that every detail of the architecture is, in the end, implemented as code. - - Think step by step and reason yourself to the right decisions to make sure we get it right. - You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. - - Then you will output the content of each file including ALL code. - Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that - [FILENAME] is the lowercase file name including the file extension, - [LANG] is the markup code block language for the code's language, and [CODE] is the code: - - [FILENAME] - ```[LANG] - [CODE] - ``` - - You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. - Please note that the code should be fully functional. No placeholders. - - Follow a language and framework appropriate best practice file naming convention. - Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. - Ensure to implement all code, if you are unsure, write a plausible implementation. - Include module dependency or package manager dependency definition file. - Before you finish, double check that all parts of the architecture is present in the files. - """ - prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{spec}", spec_description) - messages = [{"role": "system", "content": prompt}] - - result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - - # Get all filenames and corresponding code blocks - regex = r"(\S+?)\n```\S+\n(.+?)```" - matches = re.finditer(regex, result["content"], re.DOTALL) - - # Save each file - for match in matches: - # Get the filename - file_name = re.sub(r'[<>"|?*]', "", match.group(1)) - - # Get the code - code = match.group(2) - - # Ensure file_name is not empty - if file_name.strip(): - save_result = self.write_codes_to_file(code, file_name) - if save_result.startswith("Error"): - return save_result - - # Get README contents and save - split_result = result["content"].split("```") - if len(split_result) > 0: - readme = split_result[0] - save_readme_result = self.write_codes_to_file(readme, "README.md") - if save_readme_result.startswith("Error"): - return save_readme_result - - return "codes generated and saved successfully" - except Exception as e: - logger.error(e) - return f"Error generating codes: {e}" \ No newline at end of file From c9f47490b3acb304151f3d263a1343b1750a77eb Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:22:40 +0530 Subject: [PATCH 10/35] Modified the coding tool --- superagi/tools/code/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py index ca9b8ee29..679aed2c9 100644 --- a/superagi/tools/code/tools.py +++ b/superagi/tools/code/tools.py @@ -28,7 +28,7 @@ class CodingTool(BaseTool): llm: Optional[BaseLlm] = None name = "CodingTool" description = ( - "Useful for writing, reviewing, and refactoring code. Can also fix bugs and explain programming concepts." + "Useful for writing, reviewing, and refactoring code. Can also fix bugs and explain programming concepts.Follow the specs from write_spec to write the code more efficiently" ) args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] From 1e37431f690567be585069207f9eb671de41fd53 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:23:14 +0530 Subject: [PATCH 11/35] Added write spec tool for the coding tool --- superagi/tools/code/write_spec.py | 131 ++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 superagi/tools/code/write_spec.py diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py new file mode 100644 index 000000000..d5c980190 --- /dev/null +++ b/superagi/tools/code/write_spec.py @@ -0,0 +1,131 @@ +from typing import Type, Optional, List + +from pydantic import BaseModel, Field +from superagi.config.config import get_config +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os +from superagi.llms.base_llm import BaseLlm +from superagi.tools.base_tool import BaseTool +from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker + + +class WriteSpecSchema(BaseModel): + task_description: str = Field( + ..., + description="Specification task description.", + ) + + spec_file_name: str = Field( + ..., + description="Name of the file to write. Only include the file name. Don't include path." + ) + +class WriteSpecTool(BaseTool): + """ + Used to generate program specification. + + Attributes: + llm: LLM used for specification generation. + name : The name of tool. + description : The description of tool. + args_schema : The args schema. + goals : The goals. + """ + llm: Optional[BaseLlm] = None + agent_id: int = None + name = "WriteSpecTool" + description = ( + "A tool to write the spec of a program." + ) + args_schema: Type[WriteSpecSchema] = WriteSpecSchema + goals: List[str] = [] + + class Config: + arbitrary_types_allowed = True + + def _write_spec_to_file(self, spec_content: str, spec_file_name: str) -> str: + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = spec_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + spec_file_name + else: + final_path = os.getcwd() + "/" + spec_file_name + + try: + with open(final_path, mode="w") as spec_file: + spec_file.write(spec_content) + + with open(final_path, 'r') as spec_file: + resource = ResourceHelper.make_written_file_resource(file_name=spec_file_name, + agent_id=self.agent_id, file=spec_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(spec_file, path=resource.path) + logger.info(f"Specification {spec_file_name} saved successfully") + except Exception as err: + session.close() + return f"Error: {err}" + session.close() + + return "Specification saved successfully" + + except Exception as e: + return f"Error saving specification to file: {e}" + + def _execute(self, task_description: str, spec_file_name: str) -> str: + """ + Execute the write_spec tool. + + Args: + task_description : The task description. + spec_file_name: The name of the file where the generated specification will be saved. + + Returns: + Generated specification or error message. + """ + try: + prompt = """You are a super smart developer who has been asked to make a specification for a program. + + Your high-level goal is: + {goals} + + Please keep in mind the following when creating the specification: + 1. Be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. + 2. Lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + 3. List all non-standard dependencies that will have to be used. + + Write a specification for the following task: + {task} + """ + prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{task}", task_description) + messages = [{"role": "system", "content": prompt}] + + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + + # Save the specification to a file + write_result = self._write_spec_to_file(result["content"], spec_file_name) + if not write_result.startswith("Error"): + return result + '\n' + "Specification generated and saved successfully" + else: + return write_result + + except Exception as e: + logger.error(e) + return f"Error generating specification: {e}" \ No newline at end of file From 3af6fe0fcd34fe073f4250629bd2c67798d9c004 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:23:38 +0530 Subject: [PATCH 12/35] Added write test tool for the coding tool --- superagi/tools/code/write_test.py | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 superagi/tools/code/write_test.py diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py new file mode 100644 index 000000000..8f6f41e89 --- /dev/null +++ b/superagi/tools/code/write_test.py @@ -0,0 +1,74 @@ +from typing import Type, Optional, List + +from pydantic import BaseModel, Field + +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.llms.base_llm import BaseLlm +from superagi.tools.base_tool import BaseTool +from superagi.lib.logger import logger + + +class WriteTestSchema(BaseModel): + spec_description: str = Field( + ..., + description="Specification for generating tests.", + ) + + +class WriteTestTool(BaseTool): + """ + Used to generate pytest unit tests based on the specification. + + Attributes: + llm: LLM used for test generation. + name : The name of tool. + description : The description of tool. + args_schema : The args schema. + goals : The goals. + """ + llm: Optional[BaseLlm] = None + agent_id: int = None + name = "WriteTestTool" + description = ( + "You are a super smart developer using Test Driven Development to write tests according to a specification.\n" + "Please generate tests based on the above specification. The tests should be as simple as possible, " + "but still cover all the functionality.\n" + "Write it in the file" + ) + args_schema: Type[WriteTestSchema] = WriteTestSchema + goals: List[str] = [] + + class Config: + arbitrary_types_allowed = True + + def _execute(self, spec_description: str) -> str: + """ + Execute the write_test tool. + + Args: + spec_description : The specification description. + + Returns: + Generated pytest unit tests or error message. + """ + try: + prompt = """You are a super smart developer who practices Test Driven Development for writing tests according to a specification. + + Your high-level goal is: + {goals} + + Please generate pytest unit tests based on the following specification description: + {spec} + + The tests should be as simple as possible, but still cover all the functionality described in the specification. + """ + prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{spec}", spec_description) + messages = [{"role": "system", "content": prompt}] + + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + return result["content"] + + except Exception as e: + logger.error(e) + return f"Error generating tests: {e}" \ No newline at end of file From d8f6e420fed2f7a823f9e33e34e8fc33b965420a Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Tue, 20 Jun 2023 19:39:03 +0530 Subject: [PATCH 13/35] updated json reponse --- superagi/tools/code/write_spec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index d5c980190..ca3af35d1 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -122,7 +122,8 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: # Save the specification to a file write_result = self._write_spec_to_file(result["content"], spec_file_name) if not write_result.startswith("Error"): - return result + '\n' + "Specification generated and saved successfully" + result["specification"] = "Specification generated and saved successfully" + return result else: return write_result From 05aa3fc8ee802df95b133a7b2242dee869025089 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 13:57:36 +0530 Subject: [PATCH 14/35] Added Add to file logic in all file --- superagi/tools/code/tools.py | 134 ++++++++++++++++++++++++++---- superagi/tools/code/write_spec.py | 3 +- superagi/tools/code/write_test.py | 68 +++++++++++++-- 3 files changed, 180 insertions(+), 25 deletions(-) diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py index 679aed2c9..09b4bc51a 100644 --- a/superagi/tools/code/tools.py +++ b/superagi/tools/code/tools.py @@ -1,19 +1,23 @@ from typing import Type, Optional, List from pydantic import BaseModel, Field - +from superagi.config.config import get_config from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker class CodingSchema(BaseModel): - task_description: str = Field( + spec_description: str = Field( ..., - description="Coding task description.", + description="Specification for generating tests.", ) - class CodingTool(BaseTool): """ Used to generate code. @@ -26,9 +30,14 @@ class CodingTool(BaseTool): goals : The goals. """ llm: Optional[BaseLlm] = None + agent_id: int = None name = "CodingTool" description = ( - "Useful for writing, reviewing, and refactoring code. Can also fix bugs and explain programming concepts.Follow the specs from write_spec to write the code more efficiently" + "You will get instructions for code to write. You will write a very long answer. " + "Make sure that every detail of the architecture is, in the end, implemented as code. " + "Think step by step and reason yourself to the right decisions to make sure we get it right. " + "You will first lay out the names of the core classes, functions, methods that will be necessary, " + "as well as a quick comment on their purpose. Then you will output the content of each file including ALL code." ) args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] @@ -36,8 +45,66 @@ class CodingTool(BaseTool): class Config: arbitrary_types_allowed = True + + def _write_code_to_files(self, code_content: str) -> str: + try: + code_sections = code_content.split("\n[FILENAME]\n") + + for section in code_sections: + if not section.strip(): + continue + + lines = section.strip().split("\n") + file_name = lines[0].strip() + + file_name = file_name[:70] # Truncate long file names + + if len(lines) > 1 and "```" in lines[1]: + code = "\n".join(lines[2:-1]) + else: + code = "\n".join(lines[1:]) + + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + file_name + else: + final_path = os.getcwd() + "/" + file_name - def _execute(self, task_description: str): + try: + with open(final_path, mode="w") as code_file: + code_file.write(code) + + with open(final_path, 'r') as code_file: + resource = ResourceHelper.make_written_file_resource(file_name=file_name, + agent_id=self.agent_id, file=code_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(code_file, path=resource.path) + logger.info(f"Code {file_name} saved successfully") + except Exception as err: + session.close() + return f"Error: {err}" + + session.close() + + return "Code saved successfully" + + except Exception as e: + return f"Error saving code to files: {e}" + + def _execute(self, spec_description: str) -> str: """ Execute the code tool. @@ -48,23 +115,54 @@ def _execute(self, task_description: str): Generated code or error message. """ try: - prompt = """You're a top-notch coder, knowing all programming languages, software systems, and architecture. + prompt = """You are a super smart developer who has been asked to make a specification for a program. - Your high level goal is: + Your high-level goal is: {goals} - - Provide no information about who you are and focus on writing code. - Ensure code is bug and error free and explain complex concepts through comments - Respond in well-formatted markdown. Ensure code blocks are used for code sections. - - Write code to accomplish the following: - {task} + + You will get instructions for code to write. + You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. + Make sure that every detail of the architecture is, in the end, implemented as code. + + Think step by step and reason yourself to the right decisions to make sure we get it right. + You will start by laying out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + + Then you will output the content of each file including ALL code. + Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that + [FILENAME] is the lowercase file name including the file extension, + [LANG] is the markup code block language for the code's language, and [CODE] is the code: + ``` + [FILENAME] + [LANG] + [CODE] + ``` + + You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. + Please note that the code should be fully functional. No placeholders. + + Follow a language and framework appropriate best practice file naming convention. + Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. + Ensure to implement all code, if you are unsure, write a plausible implementation. + Include module dependency or package manager dependency definition file. + Before you finish, double check that all parts of the architecture is present in the files. + + Please generate code files based on the following specification description: + {spec} """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{task}", task_description) + prompt = prompt.replace("{task}", spec_description) messages = [{"role": "system", "content": prompt}] + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - return result["content"] + + # Save the code to files + save_result = self._write_code_to_files(result["content"]) + if not save_result.startswith("Error"): + return "Code generated and saved successfully" + else: + return save_result + except Exception as e: logger.error(e) - return f"Error generating text: {e}" \ No newline at end of file + return f"Error generating code: {e}" + \ No newline at end of file diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index ca3af35d1..40d04417a 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -122,8 +122,7 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: # Save the specification to a file write_result = self._write_spec_to_file(result["content"], spec_file_name) if not write_result.startswith("Error"): - result["specification"] = "Specification generated and saved successfully" - return result + return result["content"] + "Specification generated and saved successfully" else: return write_result diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 8f6f41e89..5545153aa 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -1,11 +1,16 @@ from typing import Type, Optional, List from pydantic import BaseModel, Field - +from superagi.config.config import get_config from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker class WriteTestSchema(BaseModel): @@ -13,6 +18,10 @@ class WriteTestSchema(BaseModel): ..., description="Specification for generating tests.", ) + test_file_name: str = Field( + ..., + description="Name of the file to write. Only include the file name. Don't include path." + ) class WriteTestTool(BaseTool): @@ -41,12 +50,55 @@ class WriteTestTool(BaseTool): class Config: arbitrary_types_allowed = True - def _execute(self, spec_description: str) -> str: + + def _write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = test_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + test_file_name + else: + final_path = os.getcwd() + "/" + test_file_name + + try: + with open(final_path, mode="w") as test_file: + test_file.write(tests_content) + + with open(final_path, 'r') as test_file: + resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, + agent_id=self.agent_id, file=test_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(test_file, path=resource.path) + logger.info(f"Tests {test_file_name} saved successfully") + except Exception as err: + session.close() + return f"Error: {err}" + session.close() + + return "Tests saved successfully" + + except Exception as e: + return f"Error saving tests to file: {e}" + + def _execute(self, spec_description: str, test_file_name: str) -> str: """ Execute the write_test tool. Args: spec_description : The specification description. + test_file_name: The name of the file where the generated tests will be saved. Returns: Generated pytest unit tests or error message. @@ -65,10 +117,16 @@ def _execute(self, spec_description: str) -> str: prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] - + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - return result["content"] - + + # Save the tests to a file + save_result = self._write_tests_to_file(result["content"], test_file_name) + if not save_result.startswith("Error"): + return "Tests generated and saved successfully" + else: + return save_result + except Exception as e: logger.error(e) return f"Error generating tests: {e}" \ No newline at end of file From 08586422b3b8e83e0648cafdf4a04a26d1c0f62a Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 19:53:34 +0530 Subject: [PATCH 15/35] Modified write code file --- superagi/tools/code/tools.py | 177 ++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 84 deletions(-) diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py index 09b4bc51a..d681ecf40 100644 --- a/superagi/tools/code/tools.py +++ b/superagi/tools/code/tools.py @@ -11,12 +11,12 @@ from superagi.helper.resource_helper import ResourceHelper from superagi.helper.s3_helper import S3Helper from sqlalchemy.orm import sessionmaker - +import re class CodingSchema(BaseModel): spec_description: str = Field( ..., - description="Specification for generating tests.", + description="Specification for generating code which is generated by WriteSpecTool", ) class CodingTool(BaseTool): """ @@ -46,123 +46,132 @@ class Config: arbitrary_types_allowed = True - def _write_code_to_files(self, code_content: str) -> str: - try: - code_sections = code_content.split("\n[FILENAME]\n") - - for section in code_sections: - if not section.strip(): - continue - - lines = section.strip().split("\n") - file_name = lines[0].strip() - - file_name = file_name[:70] # Truncate long file names - - if len(lines) > 1 and "```" in lines[1]: - code = "\n".join(lines[2:-1]) - else: - code = "\n".join(lines[1:]) - - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + file_name - else: - final_path = os.getcwd() + "/" + file_name - - try: - with open(final_path, mode="w") as code_file: - code_file.write(code) - - with open(final_path, 'r') as code_file: - resource = ResourceHelper.make_written_file_resource(file_name=file_name, - agent_id=self.agent_id, file=code_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(code_file, path=resource.path) - logger.info(f"Code {file_name} saved successfully") - except Exception as err: - session.close() - return f"Error: {err}" - - session.close() - - return "Code saved successfully" + def write_codes_to_file(self, codes_content: str, code_file_name: str) -> str: + """ + Write the generated codes to the specified file. - except Exception as e: - return f"Error saving code to files: {e}" + Args: + codes_content: The content (code) of the code. + code_file_name: Name of the file where the code will be written. + Returns: + A string indicating if the codes were saved successfully or an error message. + """ + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = code_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + code_file_name + else: + final_path = os.getcwd() + "/" + code_file_name + + with open(final_path, mode="w") as code_file: + code_file.write(codes_content) + + with open(final_path, 'r') as code_file: + resource = ResourceHelper.make_written_file_resource(file_name=code_file_name, + agent_id=self.agent_id, file=code_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(code_file, path=resource.path) + + logger.info(f"Code {code_file_name} saved successfully") + session.close() + return "Codes saved successfully" + except Exception as e: + session.close() + return f"Error saving codes to file: {e}" + def _execute(self, spec_description: str) -> str: """ - Execute the code tool. + Execute the write_code tool. Args: - task_description : The task description. + spec_description : The specification description. + code_file_name: The name of the file where the generated codes will be saved. Returns: - Generated code or error message. + Generated codes files or error message. """ try: - prompt = """You are a super smart developer who has been asked to make a specification for a program. - + prompt = """You are a super smart developer who practices good Development for writing code according to a specification. + Your high-level goal is: {goals} - + + Use this specs for generating the code: + {spec} + You will get instructions for code to write. You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. Make sure that every detail of the architecture is, in the end, implemented as code. - + Think step by step and reason yourself to the right decisions to make sure we get it right. - You will start by laying out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. - + You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + Then you will output the content of each file including ALL code. Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that [FILENAME] is the lowercase file name including the file extension, [LANG] is the markup code block language for the code's language, and [CODE] is the code: - ``` + [FILENAME] - [LANG] + ```[LANG] [CODE] ``` - + You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. Please note that the code should be fully functional. No placeholders. - + Follow a language and framework appropriate best practice file naming convention. Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. Ensure to implement all code, if you are unsure, write a plausible implementation. Include module dependency or package manager dependency definition file. Before you finish, double check that all parts of the architecture is present in the files. - - Please generate code files based on the following specification description: - {spec} """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{task}", spec_description) + prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - - # Save the code to files - save_result = self._write_code_to_files(result["content"]) - if not save_result.startswith("Error"): - return "Code generated and saved successfully" - else: - return save_result + # Get all filenames and corresponding code blocks + regex = r"(\S+?)\n```\S+\n(.+?)```" + matches = re.finditer(regex, result["content"], re.DOTALL) + + # Save each file + for match in matches: + # Get the filename + file_name = re.sub(r'[<>"|?*]', "", match.group(1)) + + # Get the code + code = match.group(2) + + # Ensure file_name is not empty + if file_name.strip(): + save_result = self.write_codes_to_file(code, file_name) + if save_result.startswith("Error"): + return save_result + + # Get README contents and save + split_result = result["content"].split("```") + if len(split_result) > 0: + readme = split_result[0] + save_readme_result = self.write_codes_to_file(readme, "README.md") + if save_readme_result.startswith("Error"): + return save_readme_result + + return "codes generated and saved successfully" except Exception as e: logger.error(e) - return f"Error generating code: {e}" - \ No newline at end of file + return f"Error generating codes: {e}" \ No newline at end of file From f6956645865f5e8dfbb56eb2c2e67693c476ae26 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 19:54:22 +0530 Subject: [PATCH 16/35] Added write_test file logic --- superagi/tools/code/write_test.py | 91 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 5545153aa..e01a1821a 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -11,12 +11,13 @@ from superagi.helper.resource_helper import ResourceHelper from superagi.helper.s3_helper import S3Helper from sqlalchemy.orm import sessionmaker +import re class WriteTestSchema(BaseModel): spec_description: str = Field( ..., - description="Specification for generating tests.", + description="Specification for generating tests generated by WriteSpecTool", ) test_file_name: str = Field( ..., @@ -51,47 +52,48 @@ class Config: arbitrary_types_allowed = True - def _write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: + def write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = test_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + test_file_name + else: + final_path = os.getcwd() + "/" + test_file_name + try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = test_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + test_file_name - else: - final_path = os.getcwd() + "/" + test_file_name - - try: - with open(final_path, mode="w") as test_file: - test_file.write(tests_content) - - with open(final_path, 'r') as test_file: - resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, - agent_id=self.agent_id, file=test_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(test_file, path=resource.path) - logger.info(f"Tests {test_file_name} saved successfully") - except Exception as err: - session.close() - return f"Error: {err}" + with open(final_path, mode="w") as test_file: + test_file.write(tests_content) + + with open(final_path, 'r') as test_file: + resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, + agent_id=self.agent_id, file=test_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(test_file, path=resource.path) + logger.info(f"Tests {test_file_name} saved successfully") + except Exception as err: session.close() + return f"Error: {err}" + session.close() - return "Tests saved successfully" - - except Exception as e: - return f"Error saving tests to file: {e}" + return "Tests saved successfully" + + except Exception as e: + return f"Error saving tests to file: {e}" + def _execute(self, spec_description: str, test_file_name: str) -> str: """ Execute the write_test tool. @@ -120,13 +122,20 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + # Extract the code part using regular expression + code = re.search(r'(?<=```python).*?(?=```)', result["content"], re.DOTALL) + if code: + code_content = code.group(0).strip() + else: + return "Unable to extract code from the response" + # Save the tests to a file - save_result = self._write_tests_to_file(result["content"], test_file_name) + save_result = self.write_tests_to_file(code_content, test_file_name) if not save_result.startswith("Error"): return "Tests generated and saved successfully" else: return save_result - + except Exception as e: logger.error(e) - return f"Error generating tests: {e}" \ No newline at end of file + return f"Error generating tests: {e}" From 559e00a52f69363897ac3029c7f073b7a0543baa Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Wed, 21 Jun 2023 19:56:35 +0530 Subject: [PATCH 17/35] Added write code file --- superagi/tools/code/write_code.py | 177 ++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 superagi/tools/code/write_code.py diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py new file mode 100644 index 000000000..d681ecf40 --- /dev/null +++ b/superagi/tools/code/write_code.py @@ -0,0 +1,177 @@ +from typing import Type, Optional, List + +from pydantic import BaseModel, Field +from superagi.config.config import get_config +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +import os +from superagi.llms.base_llm import BaseLlm +from superagi.tools.base_tool import BaseTool +from superagi.lib.logger import logger +from superagi.models.db import connect_db +from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.s3_helper import S3Helper +from sqlalchemy.orm import sessionmaker +import re + +class CodingSchema(BaseModel): + spec_description: str = Field( + ..., + description="Specification for generating code which is generated by WriteSpecTool", + ) +class CodingTool(BaseTool): + """ + Used to generate code. + + Attributes: + llm: LLM used for code generation. + name : The name of tool. + description : The description of tool. + args_schema : The args schema. + goals : The goals. + """ + llm: Optional[BaseLlm] = None + agent_id: int = None + name = "CodingTool" + description = ( + "You will get instructions for code to write. You will write a very long answer. " + "Make sure that every detail of the architecture is, in the end, implemented as code. " + "Think step by step and reason yourself to the right decisions to make sure we get it right. " + "You will first lay out the names of the core classes, functions, methods that will be necessary, " + "as well as a quick comment on their purpose. Then you will output the content of each file including ALL code." + ) + args_schema: Type[CodingSchema] = CodingSchema + goals: List[str] = [] + + class Config: + arbitrary_types_allowed = True + + + def write_codes_to_file(self, codes_content: str, code_file_name: str) -> str: + """ + Write the generated codes to the specified file. + + Args: + codes_content: The content (code) of the code. + code_file_name: Name of the file where the code will be written. + + Returns: + A string indicating if the codes were saved successfully or an error message. + """ + try: + engine = connect_db() + Session = sessionmaker(bind=engine) + session = Session() + + final_path = code_file_name + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + final_path = root_dir + code_file_name + else: + final_path = os.getcwd() + "/" + code_file_name + + with open(final_path, mode="w") as code_file: + code_file.write(codes_content) + + with open(final_path, 'r') as code_file: + resource = ResourceHelper.make_written_file_resource(file_name=code_file_name, + agent_id=self.agent_id, file=code_file, channel="OUTPUT") + + if resource is not None: + session.add(resource) + session.commit() + session.flush() + if resource.storage_type == "S3": + s3_helper = S3Helper() + s3_helper.upload_file(code_file, path=resource.path) + + logger.info(f"Code {code_file_name} saved successfully") + session.close() + return "Codes saved successfully" + except Exception as e: + session.close() + return f"Error saving codes to file: {e}" + + def _execute(self, spec_description: str) -> str: + """ + Execute the write_code tool. + + Args: + spec_description : The specification description. + code_file_name: The name of the file where the generated codes will be saved. + + Returns: + Generated codes files or error message. + """ + try: + prompt = """You are a super smart developer who practices good Development for writing code according to a specification. + + Your high-level goal is: + {goals} + + Use this specs for generating the code: + {spec} + + You will get instructions for code to write. + You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. + Make sure that every detail of the architecture is, in the end, implemented as code. + + Think step by step and reason yourself to the right decisions to make sure we get it right. + You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + + Then you will output the content of each file including ALL code. + Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that + [FILENAME] is the lowercase file name including the file extension, + [LANG] is the markup code block language for the code's language, and [CODE] is the code: + + [FILENAME] + ```[LANG] + [CODE] + ``` + + You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. + Please note that the code should be fully functional. No placeholders. + + Follow a language and framework appropriate best practice file naming convention. + Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. + Ensure to implement all code, if you are unsure, write a plausible implementation. + Include module dependency or package manager dependency definition file. + Before you finish, double check that all parts of the architecture is present in the files. + """ + prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{spec}", spec_description) + messages = [{"role": "system", "content": prompt}] + + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + + # Get all filenames and corresponding code blocks + regex = r"(\S+?)\n```\S+\n(.+?)```" + matches = re.finditer(regex, result["content"], re.DOTALL) + + # Save each file + for match in matches: + # Get the filename + file_name = re.sub(r'[<>"|?*]', "", match.group(1)) + + # Get the code + code = match.group(2) + + # Ensure file_name is not empty + if file_name.strip(): + save_result = self.write_codes_to_file(code, file_name) + if save_result.startswith("Error"): + return save_result + + # Get README contents and save + split_result = result["content"].split("```") + if len(split_result) > 0: + readme = split_result[0] + save_readme_result = self.write_codes_to_file(readme, "README.md") + if save_readme_result.startswith("Error"): + return save_readme_result + + return "codes generated and saved successfully" + except Exception as e: + logger.error(e) + return f"Error generating codes: {e}" \ No newline at end of file From 7853014ba87a270324fa2948e70e54f90395fba4 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Wed, 21 Jun 2023 20:16:01 +0530 Subject: [PATCH 18/35] refactoring the resource manager --- superagi/jobs/agent_executor.py | 2 +- superagi/resource_manager/manager.py | 7 +- superagi/tools/code/tools.py | 177 --------------------------- superagi/tools/code/write_code.py | 55 +-------- superagi/tools/code/write_spec.py | 43 +------ superagi/tools/code/write_test.py | 56 ++------- 6 files changed, 19 insertions(+), 321 deletions(-) delete mode 100644 superagi/tools/code/tools.py diff --git a/superagi/jobs/agent_executor.py b/superagi/jobs/agent_executor.py index a1aa835d0..b5431e879 100644 --- a/superagi/jobs/agent_executor.py +++ b/superagi/jobs/agent_executor.py @@ -234,7 +234,7 @@ def set_default_params_tools(self, tools, parsed_config, agent_id, model_api_key if hasattr(tool, 'agent_id'): tool.agent_id = agent_id if hasattr(tool, 'resource_manager'): - tool.resource_manager = ResourceManager(session=session) + tool.resource_manager = ResourceManager(session=session, agent_id=agent_id) new_tools.append(tool) return tools diff --git a/superagi/resource_manager/manager.py b/superagi/resource_manager/manager.py index 588a58f99..9abd0b53b 100644 --- a/superagi/resource_manager/manager.py +++ b/superagi/resource_manager/manager.py @@ -6,8 +6,9 @@ class ResourceManager: - def __init__(self, session: Session): + def __init__(self, session: Session, agent_id: int = None): self.session = session + self.agent_id = agent_id def write_binary_file(self, file_name: str, data): final_path = ResourceHelper.get_resource_path(file_name) @@ -18,6 +19,7 @@ def write_binary_file(self, file_name: str, data): img.close() self.write_to_s3(file_name, final_path) logger.info(f"Binary {file_name} saved successfully") + return f"Binary {file_name} saved successfully" except Exception as err: return f"Error: {err}" @@ -42,5 +44,6 @@ def write_file(self, file_name: str, content): file.close() self.write_to_s3(file_name, final_path) logger.info(f"{file_name} saved successfully") + return f"{file_name} saved successfully" except Exception as err: - return f"Error: {err}" \ No newline at end of file + return f"Error: {err}" diff --git a/superagi/tools/code/tools.py b/superagi/tools/code/tools.py deleted file mode 100644 index d681ecf40..000000000 --- a/superagi/tools/code/tools.py +++ /dev/null @@ -1,177 +0,0 @@ -from typing import Type, Optional, List - -from pydantic import BaseModel, Field -from superagi.config.config import get_config -from superagi.agent.agent_prompt_builder import AgentPromptBuilder -import os -from superagi.llms.base_llm import BaseLlm -from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger -from superagi.models.db import connect_db -from superagi.helper.resource_helper import ResourceHelper -from superagi.helper.s3_helper import S3Helper -from sqlalchemy.orm import sessionmaker -import re - -class CodingSchema(BaseModel): - spec_description: str = Field( - ..., - description="Specification for generating code which is generated by WriteSpecTool", - ) -class CodingTool(BaseTool): - """ - Used to generate code. - - Attributes: - llm: LLM used for code generation. - name : The name of tool. - description : The description of tool. - args_schema : The args schema. - goals : The goals. - """ - llm: Optional[BaseLlm] = None - agent_id: int = None - name = "CodingTool" - description = ( - "You will get instructions for code to write. You will write a very long answer. " - "Make sure that every detail of the architecture is, in the end, implemented as code. " - "Think step by step and reason yourself to the right decisions to make sure we get it right. " - "You will first lay out the names of the core classes, functions, methods that will be necessary, " - "as well as a quick comment on their purpose. Then you will output the content of each file including ALL code." - ) - args_schema: Type[CodingSchema] = CodingSchema - goals: List[str] = [] - - class Config: - arbitrary_types_allowed = True - - - def write_codes_to_file(self, codes_content: str, code_file_name: str) -> str: - """ - Write the generated codes to the specified file. - - Args: - codes_content: The content (code) of the code. - code_file_name: Name of the file where the code will be written. - - Returns: - A string indicating if the codes were saved successfully or an error message. - """ - try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = code_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + code_file_name - else: - final_path = os.getcwd() + "/" + code_file_name - - with open(final_path, mode="w") as code_file: - code_file.write(codes_content) - - with open(final_path, 'r') as code_file: - resource = ResourceHelper.make_written_file_resource(file_name=code_file_name, - agent_id=self.agent_id, file=code_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(code_file, path=resource.path) - - logger.info(f"Code {code_file_name} saved successfully") - session.close() - return "Codes saved successfully" - except Exception as e: - session.close() - return f"Error saving codes to file: {e}" - - def _execute(self, spec_description: str) -> str: - """ - Execute the write_code tool. - - Args: - spec_description : The specification description. - code_file_name: The name of the file where the generated codes will be saved. - - Returns: - Generated codes files or error message. - """ - try: - prompt = """You are a super smart developer who practices good Development for writing code according to a specification. - - Your high-level goal is: - {goals} - - Use this specs for generating the code: - {spec} - - You will get instructions for code to write. - You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. - Make sure that every detail of the architecture is, in the end, implemented as code. - - Think step by step and reason yourself to the right decisions to make sure we get it right. - You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. - - Then you will output the content of each file including ALL code. - Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that - [FILENAME] is the lowercase file name including the file extension, - [LANG] is the markup code block language for the code's language, and [CODE] is the code: - - [FILENAME] - ```[LANG] - [CODE] - ``` - - You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. - Please note that the code should be fully functional. No placeholders. - - Follow a language and framework appropriate best practice file naming convention. - Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. - Ensure to implement all code, if you are unsure, write a plausible implementation. - Include module dependency or package manager dependency definition file. - Before you finish, double check that all parts of the architecture is present in the files. - """ - prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{spec}", spec_description) - messages = [{"role": "system", "content": prompt}] - - result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - - # Get all filenames and corresponding code blocks - regex = r"(\S+?)\n```\S+\n(.+?)```" - matches = re.finditer(regex, result["content"], re.DOTALL) - - # Save each file - for match in matches: - # Get the filename - file_name = re.sub(r'[<>"|?*]', "", match.group(1)) - - # Get the code - code = match.group(2) - - # Ensure file_name is not empty - if file_name.strip(): - save_result = self.write_codes_to_file(code, file_name) - if save_result.startswith("Error"): - return save_result - - # Get README contents and save - split_result = result["content"].split("```") - if len(split_result) > 0: - readme = split_result[0] - save_readme_result = self.write_codes_to_file(readme, "README.md") - if save_readme_result.startswith("Error"): - return save_readme_result - - return "codes generated and saved successfully" - except Exception as e: - logger.error(e) - return f"Error generating codes: {e}" \ No newline at end of file diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index d681ecf40..ef2a3f0ae 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -45,54 +45,7 @@ class CodingTool(BaseTool): class Config: arbitrary_types_allowed = True - - def write_codes_to_file(self, codes_content: str, code_file_name: str) -> str: - """ - Write the generated codes to the specified file. - - Args: - codes_content: The content (code) of the code. - code_file_name: Name of the file where the code will be written. - Returns: - A string indicating if the codes were saved successfully or an error message. - """ - try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = code_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + code_file_name - else: - final_path = os.getcwd() + "/" + code_file_name - - with open(final_path, mode="w") as code_file: - code_file.write(codes_content) - - with open(final_path, 'r') as code_file: - resource = ResourceHelper.make_written_file_resource(file_name=code_file_name, - agent_id=self.agent_id, file=code_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(code_file, path=resource.path) - - logger.info(f"Code {code_file_name} saved successfully") - session.close() - return "Codes saved successfully" - except Exception as e: - session.close() - return f"Error saving codes to file: {e}" - def _execute(self, spec_description: str) -> str: """ Execute the write_code tool. @@ -159,7 +112,7 @@ def _execute(self, spec_description: str) -> str: # Ensure file_name is not empty if file_name.strip(): - save_result = self.write_codes_to_file(code, file_name) + save_result = self.resource_manager.write_file(file_name, code) if save_result.startswith("Error"): return save_result @@ -167,11 +120,11 @@ def _execute(self, spec_description: str) -> str: split_result = result["content"].split("```") if len(split_result) > 0: readme = split_result[0] - save_readme_result = self.write_codes_to_file(readme, "README.md") + save_readme_result = self.resource_manager.write_file("README.md", readme) if save_readme_result.startswith("Error"): return save_readme_result - return "codes generated and saved successfully" + return "Codes generated and saved successfully" except Exception as e: logger.error(e) - return f"Error generating codes: {e}" \ No newline at end of file + return f"Error generating codes: {e}" diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index 40d04417a..0dd38a1df 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -46,47 +46,6 @@ class WriteSpecTool(BaseTool): class Config: arbitrary_types_allowed = True - - def _write_spec_to_file(self, spec_content: str, spec_file_name: str) -> str: - try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = spec_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + spec_file_name - else: - final_path = os.getcwd() + "/" + spec_file_name - - try: - with open(final_path, mode="w") as spec_file: - spec_file.write(spec_content) - - with open(final_path, 'r') as spec_file: - resource = ResourceHelper.make_written_file_resource(file_name=spec_file_name, - agent_id=self.agent_id, file=spec_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(spec_file, path=resource.path) - logger.info(f"Specification {spec_file_name} saved successfully") - except Exception as err: - session.close() - return f"Error: {err}" - session.close() - - return "Specification saved successfully" - - except Exception as e: - return f"Error saving specification to file: {e}" def _execute(self, task_description: str, spec_file_name: str) -> str: """ @@ -120,7 +79,7 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) # Save the specification to a file - write_result = self._write_spec_to_file(result["content"], spec_file_name) + write_result = self.resource_manager.write_file(spec_file_name, result["content"]) if not write_result.startswith("Error"): return result["content"] + "Specification generated and saved successfully" else: diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index e01a1821a..1b8bbcc1b 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -5,6 +5,7 @@ from superagi.agent.agent_prompt_builder import AgentPromptBuilder import os from superagi.llms.base_llm import BaseLlm +from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger from superagi.models.db import connect_db @@ -35,6 +36,7 @@ class WriteTestTool(BaseTool): description : The description of tool. args_schema : The args schema. goals : The goals. + resource_manager: Manages the file resources """ llm: Optional[BaseLlm] = None agent_id: int = None @@ -47,53 +49,11 @@ class WriteTestTool(BaseTool): ) args_schema: Type[WriteTestSchema] = WriteTestSchema goals: List[str] = [] + resource_manager: Optional[ResourceManager] = None class Config: arbitrary_types_allowed = True - - def write_tests_to_file(self, tests_content: str, test_file_name: str) -> str: - try: - engine = connect_db() - Session = sessionmaker(bind=engine) - session = Session() - - final_path = test_file_name - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + test_file_name - else: - final_path = os.getcwd() + "/" + test_file_name - - try: - with open(final_path, mode="w") as test_file: - test_file.write(tests_content) - - with open(final_path, 'r') as test_file: - resource = ResourceHelper.make_written_file_resource(file_name=test_file_name, - agent_id=self.agent_id, file=test_file, channel="OUTPUT") - - if resource is not None: - session.add(resource) - session.commit() - session.flush() - if resource.storage_type == "S3": - s3_helper = S3Helper() - s3_helper.upload_file(test_file, path=resource.path) - logger.info(f"Tests {test_file_name} saved successfully") - except Exception as err: - session.close() - return f"Error: {err}" - session.close() - - return "Tests saved successfully" - - except Exception as e: - return f"Error saving tests to file: {e}" - - def _execute(self, spec_description: str, test_file_name: str) -> str: """ Execute the write_test tool. @@ -119,9 +79,9 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] - + result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) - + # Extract the code part using regular expression code = re.search(r'(?<=```python).*?(?=```)', result["content"], re.DOTALL) if code: @@ -130,12 +90,12 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: return "Unable to extract code from the response" # Save the tests to a file - save_result = self.write_tests_to_file(code_content, test_file_name) + save_result = self.resource_manager.write_file(test_file_name, code_content) if not save_result.startswith("Error"): return "Tests generated and saved successfully" else: return save_result - + except Exception as e: logger.error(e) - return f"Error generating tests: {e}" + return f"Error generating tests: {e}" \ No newline at end of file From 535ced6b4b27a799f1fccb4508f11bd1bcb45aca Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Wed, 21 Jun 2023 20:24:47 +0530 Subject: [PATCH 19/35] fixing resource manager tests --- tests/unit_tests/resource_manager/test_resource_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/resource_manager/test_resource_manager.py b/tests/unit_tests/resource_manager/test_resource_manager.py index e1f6e6f0e..d5c359284 100644 --- a/tests/unit_tests/resource_manager/test_resource_manager.py +++ b/tests/unit_tests/resource_manager/test_resource_manager.py @@ -22,7 +22,7 @@ def test_write_binary_file(resource_manager): patch.object(S3Helper, 'upload_file'), \ patch.object(logger, 'info') as logger_mock: result = resource_manager.write_binary_file('test.png', b'data') - assert result is None + assert result == "Binary test.png saved successfully" logger_mock.assert_called_once_with("Binary test.png saved successfully") @@ -33,5 +33,5 @@ def test_write_file(resource_manager): patch.object(S3Helper, 'upload_file'), \ patch.object(logger, 'info') as logger_mock: result = resource_manager.write_file('test.txt', 'content') - assert result is None + assert result == "test.txt saved successfully" logger_mock.assert_called_once_with("test.txt saved successfully") From 0757d1c0a85ec813f2e98b89d41088a9bf472256 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Wed, 21 Jun 2023 23:43:31 +0530 Subject: [PATCH 20/35] Fixing issues on code and spec. Adding unit test for write test --- superagi/tools/code/write_code.py | 3 ++ superagi/tools/code/write_spec.py | 3 ++ tests/unit_tests/tools/email/__init__.py | 0 tests/unit_tests/tools/test_write_test.py | 54 +++++++++++++++++++++++ 4 files changed, 60 insertions(+) delete mode 100644 tests/unit_tests/tools/email/__init__.py create mode 100644 tests/unit_tests/tools/test_write_test.py diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index ef2a3f0ae..73652ef50 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -5,6 +5,7 @@ from superagi.agent.agent_prompt_builder import AgentPromptBuilder import os from superagi.llms.base_llm import BaseLlm +from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger from superagi.models.db import connect_db @@ -28,6 +29,7 @@ class CodingTool(BaseTool): description : The description of tool. args_schema : The args schema. goals : The goals. + resource_manager: Manages the file resources """ llm: Optional[BaseLlm] = None agent_id: int = None @@ -41,6 +43,7 @@ class CodingTool(BaseTool): ) args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] + resource_manager: Optional[ResourceManager] = None class Config: arbitrary_types_allowed = True diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index 0dd38a1df..140f42596 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -5,6 +5,7 @@ from superagi.agent.agent_prompt_builder import AgentPromptBuilder import os from superagi.llms.base_llm import BaseLlm +from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool from superagi.lib.logger import logger from superagi.models.db import connect_db @@ -34,6 +35,7 @@ class WriteSpecTool(BaseTool): description : The description of tool. args_schema : The args schema. goals : The goals. + resource_manager: Manages the file resources """ llm: Optional[BaseLlm] = None agent_id: int = None @@ -43,6 +45,7 @@ class WriteSpecTool(BaseTool): ) args_schema: Type[WriteSpecSchema] = WriteSpecSchema goals: List[str] = [] + resource_manager: Optional[ResourceManager] = None class Config: arbitrary_types_allowed = True diff --git a/tests/unit_tests/tools/email/__init__.py b/tests/unit_tests/tools/email/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/unit_tests/tools/test_write_test.py b/tests/unit_tests/tools/test_write_test.py new file mode 100644 index 000000000..c2fd7455a --- /dev/null +++ b/tests/unit_tests/tools/test_write_test.py @@ -0,0 +1,54 @@ +import pytest +from unittest.mock import Mock, patch +from superagi.llms.base_llm import BaseLlm +from superagi.resource_manager.manager import ResourceManager +from superagi.lib.logger import logger +from superagi.tools.code.write_test import WriteTestTool + + +def test_write_test_tool_init(): + tool = WriteTestTool() + assert tool.llm is None + assert tool.agent_id is None + assert tool.name == "WriteTestTool" + assert tool.description is not None + assert tool.goals == [] + assert tool.resource_manager is None + + +@patch.object(logger, 'error') +@patch.object(ResourceManager, 'write_file') +def test_write_test_tool_execute(mock_write_file, mock_logger): + # Given + mock_llm = Mock(spec=BaseLlm) + llm_response = {"content": "```python\nsample_code\n```"} + mock_llm.chat_completion.return_value = llm_response + mock_write_file.return_value = "Tests generated and saved successfully" + + tool = WriteTestTool() + tool.llm = mock_llm + tool.resource_manager = ResourceManager(session=None) + + # When + result = tool._execute("spec", "test_file") + + # Then + mock_llm.chat_completion.assert_called_once() + mock_write_file.assert_called_once_with("test_file", "sample_code") + assert result == "Tests generated and saved successfully" + + +def test_write_test_tool_execute_with_exception(): + # Given + mock_llm = Mock(spec=BaseLlm) + mock_llm.chat_completion.side_effect = Exception("Mock error") + + tool = WriteTestTool() + tool.llm = mock_llm + tool.resource_manager = Mock() + + # When + tool._execute("spec", "test_file") + + # Then + mock_llm.chat_completion.assert_called_once() From ce14194f80541d3e67786822e819b033276a94d1 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 10:16:55 +0530 Subject: [PATCH 21/35] fixing the resource manager issues --- superagi/helper/resource_helper.py | 27 ++++++++++++++++++++++++++- superagi/resource_manager/manager.py | 16 +++++++++++++--- superagi/tools/code/write_code.py | 4 +++- superagi/tools/code/write_test.py | 2 +- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/superagi/helper/resource_helper.py b/superagi/helper/resource_helper.py index de7e62b54..1e818e954 100644 --- a/superagi/helper/resource_helper.py +++ b/superagi/helper/resource_helper.py @@ -31,7 +31,10 @@ def make_written_file_resource(file_name: str, agent_id: int, channel: str): else: file_type = "application/misc" - final_path = ResourceHelper.get_resource_path(file_name) + if agent_id is not None: + final_path = ResourceHelper.get_agent_resource_path(file_name, agent_id) + else: + final_path = ResourceHelper.get_resource_path(file_name) file_size = os.path.getsize(final_path) if storage_type == "S3": @@ -63,3 +66,25 @@ def get_resource_path(file_name: str): else: final_path = os.getcwd() + "/" + file_name return final_path + + @staticmethod + def get_agent_resource_path(file_name: str, agent_id: int): + """Get final path of the resource. + + Args: + file_name (str): The name of the file. + """ + root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + + if root_dir is not None: + root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir + root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" + else: + root_dir = os.getcwd() + "/" + + if agent_id is not None: + directory = os.path.dirname(root_dir + str(agent_id) + "/") + os.makedirs(directory, exist_ok=True) + root_dir = root_dir + str(agent_id) + "/" + final_path = root_dir + file_name + return final_path diff --git a/superagi/resource_manager/manager.py b/superagi/resource_manager/manager.py index 9abd0b53b..166af9884 100644 --- a/superagi/resource_manager/manager.py +++ b/superagi/resource_manager/manager.py @@ -3,6 +3,7 @@ from superagi.helper.resource_helper import ResourceHelper from superagi.helper.s3_helper import S3Helper from superagi.lib.logger import logger +import os class ResourceManager: @@ -11,8 +12,14 @@ def __init__(self, session: Session, agent_id: int = None): self.agent_id = agent_id def write_binary_file(self, file_name: str, data): - final_path = ResourceHelper.get_resource_path(file_name) - + if self.agent_id is not None: + final_path = ResourceHelper.get_agent_resource_path(file_name, self.agent_id) + else: + final_path = ResourceHelper.get_resource_path(file_name) + + if self.agent_id is not None: + directory = os.path.dirname(final_path + "/" + str(self.agent_id)) + os.makedirs(directory, exist_ok=True) try: with open(final_path, mode="wb") as img: img.write(data) @@ -36,7 +43,10 @@ def write_to_s3(self, file_name, final_path): s3_helper.upload_file(img, path=resource.path) def write_file(self, file_name: str, content): - final_path = ResourceHelper.get_resource_path(file_name) + if self.agent_id is not None: + final_path = ResourceHelper.get_agent_resource_path(file_name, self.agent_id) + else: + final_path = ResourceHelper.get_resource_path(file_name) try: with open(final_path, mode="w") as file: diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 73652ef50..80d0ce054 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -105,6 +105,7 @@ def _execute(self, spec_description: str) -> str: regex = r"(\S+?)\n```\S+\n(.+?)```" matches = re.finditer(regex, result["content"], re.DOTALL) + file_names = [] # Save each file for match in matches: # Get the filename @@ -115,6 +116,7 @@ def _execute(self, spec_description: str) -> str: # Ensure file_name is not empty if file_name.strip(): + file_names.append(file_name) save_result = self.resource_manager.write_file(file_name, code) if save_result.startswith("Error"): return save_result @@ -127,7 +129,7 @@ def _execute(self, spec_description: str) -> str: if save_readme_result.startswith("Error"): return save_readme_result - return "Codes generated and saved successfully" + return result["content"] + "\n Codes generated and saved successfully in " + ", ".join(file_names) except Exception as e: logger.error(e) return f"Error generating codes: {e}" diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 1b8bbcc1b..6215abe4e 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -92,7 +92,7 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: # Save the tests to a file save_result = self.resource_manager.write_file(test_file_name, code_content) if not save_result.startswith("Error"): - return "Tests generated and saved successfully" + return result["content"] + " \n Tests generated and saved successfully in " + test_file_name else: return save_result From 7151ed058576067da49c6d621612a23a67f4b2d9 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 11:10:29 +0530 Subject: [PATCH 22/35] fixing tests and flows --- superagi/helper/resource_helper.py | 29 +++++---- superagi/tools/code/write_code.py | 14 +++-- .../unit_tests/helper/test_resource_helper.py | 62 ++++++++++--------- .../resource_manager/test_resource_manager.py | 2 +- tests/unit_tests/tools/__init__.py | 0 tests/unit_tests/tools/test_write_code.py | 28 +++++++++ tests/unit_tests/tools/test_write_spec.py | 26 ++++++++ tests/unit_tests/tools/test_write_test.py | 4 +- 8 files changed, 114 insertions(+), 51 deletions(-) create mode 100644 tests/unit_tests/tools/__init__.py create mode 100644 tests/unit_tests/tools/test_write_code.py create mode 100644 tests/unit_tests/tools/test_write_spec.py diff --git a/superagi/helper/resource_helper.py b/superagi/helper/resource_helper.py index 1e818e954..5bb662c43 100644 --- a/superagi/helper/resource_helper.py +++ b/superagi/helper/resource_helper.py @@ -20,7 +20,7 @@ def make_written_file_resource(file_name: str, agent_id: int, channel: str): Returns: Resource: The Resource object. """ - path = get_config("RESOURCES_OUTPUT_ROOT_DIR") + path = ResourceHelper.get_root_dir() storage_type = get_config("STORAGE_TYPE") file_extension = os.path.splitext(file_name)[1][1:] @@ -33,6 +33,7 @@ def make_written_file_resource(file_name: str, agent_id: int, channel: str): if agent_id is not None: final_path = ResourceHelper.get_agent_resource_path(file_name, agent_id) + path = path + str(agent_id) + "/" else: final_path = ResourceHelper.get_resource_path(file_name) file_size = os.path.getsize(final_path) @@ -41,10 +42,10 @@ def make_written_file_resource(file_name: str, agent_id: int, channel: str): file_name_parts = file_name.split('.') file_name = file_name_parts[0] + '_' + str(datetime.datetime.now()).replace(' ', '') \ .replace('.', '').replace(':', '') + '.' + file_name_parts[1] - path = 'input' if (channel == "INPUT") else 'output' + path = 'input/' if (channel == "INPUT") else 'output/' - logger.info(path + "/" + file_name) - resource = Resource(name=file_name, path=path + "/" + file_name, storage_type=storage_type, size=file_size, + logger.info(final_path) + resource = Resource(name=file_name, path=path + file_name, storage_type=storage_type, size=file_size, type=file_type, channel="OUTPUT", agent_id=agent_id) @@ -57,15 +58,20 @@ def get_resource_path(file_name: str): Args: file_name (str): The name of the file. """ + return ResourceHelper.get_root_dir() + file_name + + @staticmethod + def get_root_dir(): + """Get root dir of the resource. + """ root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') if root_dir is not None: root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - final_path = root_dir + file_name else: - final_path = os.getcwd() + "/" + file_name - return final_path + root_dir = os.getcwd() + "/" + return root_dir @staticmethod def get_agent_resource_path(file_name: str, agent_id: int): @@ -74,14 +80,7 @@ def get_agent_resource_path(file_name: str, agent_id: int): Args: file_name (str): The name of the file. """ - root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') - - if root_dir is not None: - root_dir = root_dir if root_dir.startswith("/") else os.getcwd() + "/" + root_dir - root_dir = root_dir if root_dir.endswith("/") else root_dir + "/" - else: - root_dir = os.getcwd() + "/" - + root_dir = ResourceHelper.get_root_dir() if agent_id is not None: directory = os.path.dirname(root_dir + str(agent_id) + "/") os.makedirs(directory, exist_ok=True) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 80d0ce054..ab57d4dc6 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -107,6 +107,7 @@ def _execute(self, spec_description: str) -> str: file_names = [] # Save each file + for match in matches: # Get the filename file_name = re.sub(r'[<>"|?*]', "", match.group(1)) @@ -115,11 +116,14 @@ def _execute(self, spec_description: str) -> str: code = match.group(2) # Ensure file_name is not empty - if file_name.strip(): - file_names.append(file_name) - save_result = self.resource_manager.write_file(file_name, code) - if save_result.startswith("Error"): - return save_result + if not file_name.strip(): + continue + + file_names.append(file_name) + print(code + "RAMRAM1") + save_result = self.resource_manager.write_file(file_name, code) + if save_result.startswith("Error"): + return save_result # Get README contents and save split_result = result["content"].split("```") diff --git a/tests/unit_tests/helper/test_resource_helper.py b/tests/unit_tests/helper/test_resource_helper.py index f9ad6aa23..6c24cecb2 100644 --- a/tests/unit_tests/helper/test_resource_helper.py +++ b/tests/unit_tests/helper/test_resource_helper.py @@ -1,33 +1,39 @@ import pytest from unittest.mock import patch -from superagi.helper.resource_helper import ResourceHelper # Replace with actual import -@pytest.fixture -def resource_helper(): - with patch('superagi.helper.resource_helper.get_config') as get_config_mock, \ - patch('superagi.helper.resource_helper.os.getcwd') as get_cwd_mock, \ - patch('superagi.helper.resource_helper.os.path.getsize') as getsize_mock: - - get_config_mock.return_value = '/fake/path' - get_cwd_mock.return_value = '/fake/cwd' - getsize_mock.return_value = 100 - - yield - -def test_make_written_file_resource(resource_helper): - file_name = 'test.png' - agent_id = 1 - channel = 'INPUT' - result = ResourceHelper.make_written_file_resource(file_name, agent_id, channel) - - assert result.name == file_name - assert result.path == '/fake/path/' + file_name - assert result.size == 100 - assert result.type == 'image/png' +from superagi.helper.resource_helper import ResourceHelper + +def test_make_written_file_resource(mocker): + mocker.patch('os.getcwd', return_value='/') + # mocker.patch('os.getcwd', return_value='/') + mocker.patch('os.makedirs', return_value=None) + mocker.patch('os.path.getsize', return_value=1000) + mocker.patch('os.path.splitext', return_value=("", ".txt")) + mocker.patch('superagi.helper.resource_helper.get_config', side_effect=['/', 'local', None]) + + with patch('superagi.helper.resource_helper.logger') as logger_mock: + result = ResourceHelper.make_written_file_resource('test.txt', 1, 'INPUT') + + assert result.name == 'test.txt' + assert result.path == '/1/test.txt' + assert result.storage_type == 'local' + assert result.size == 1000 + assert result.type == 'application/txt' assert result.channel == 'OUTPUT' - assert result.agent_id == agent_id + assert result.agent_id == 1 + +def test_get_resource_path(mocker): + mocker.patch('os.getcwd', return_value='/') + mocker.patch('superagi.helper.resource_helper.get_config', side_effect=['/']) + + result = ResourceHelper.get_resource_path('test.txt') + + assert result == '/test.txt' + +def test_get_agent_resource_path(mocker): + mocker.patch('os.getcwd', return_value='/') + mocker.patch('os.makedirs') + mocker.patch('superagi.helper.resource_helper.get_config', side_effect=['/']) -def test_get_resource_path(resource_helper): - file_name = 'test.png' - result = ResourceHelper.get_resource_path(file_name) + result = ResourceHelper.get_agent_resource_path('test.txt', 1) - assert result == '/fake/path/test.png' + assert result == '/1/test.txt' diff --git a/tests/unit_tests/resource_manager/test_resource_manager.py b/tests/unit_tests/resource_manager/test_resource_manager.py index d5c359284..a4630f0d5 100644 --- a/tests/unit_tests/resource_manager/test_resource_manager.py +++ b/tests/unit_tests/resource_manager/test_resource_manager.py @@ -11,7 +11,7 @@ def resource_manager(): session_mock = Mock() resource_manager = ResourceManager(session_mock) - resource_manager.agent_id = 1 # replace with actual value + #resource_manager.agent_id = 1 # replace with actual value return resource_manager diff --git a/tests/unit_tests/tools/__init__.py b/tests/unit_tests/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit_tests/tools/test_write_code.py b/tests/unit_tests/tools/test_write_code.py new file mode 100644 index 000000000..24df0a2aa --- /dev/null +++ b/tests/unit_tests/tools/test_write_code.py @@ -0,0 +1,28 @@ +from unittest.mock import Mock, patch +import pytest + +from superagi.llms.base_llm import BaseLlm +from superagi.resource_manager.manager import ResourceManager +from superagi.tools.code.write_code import CodingTool + +class MockBaseLlm: + def chat_completion(self, messages, max_tokens): + return {"content": "File1.py\n```python\nprint('Hello World')\n```\n\nFile2.py\n```python\nprint('Hello again')\n```"} + +class TestCodingTool: + + @pytest.fixture + def tool(self): + tool = CodingTool() + tool.llm = MockBaseLlm() + tool.resource_manager = Mock() + return tool + + def test_execute(self, tool): + tool.resource_manager.write_file = Mock() + tool.resource_manager.write_file.return_value = "File write successful" + response = tool._execute("Test spec description") + assert response == "File1.py\n```python\nprint('Hello World')\n```\n\nFile2.py\n```python\nprint('Hello again')\n```\n Codes generated and saved successfully in File1.py, File2.py" + tool.resource_manager.write_file.assert_any_call("README.md", 'File1.py\n') + tool.resource_manager.write_file.assert_any_call("File1.py", "print('Hello World')\n") + tool.resource_manager.write_file.assert_any_call("File2.py", "print('Hello again')\n") \ No newline at end of file diff --git a/tests/unit_tests/tools/test_write_spec.py b/tests/unit_tests/tools/test_write_spec.py new file mode 100644 index 000000000..575cb33ff --- /dev/null +++ b/tests/unit_tests/tools/test_write_spec.py @@ -0,0 +1,26 @@ +from unittest.mock import Mock + +import pytest + +from superagi.tools.code.write_spec import WriteSpecTool + + +class MockBaseLlm: + def chat_completion(self, messages, max_tokens): + return {"content": "Generated specification"} + +class TestWriteSpecTool: + + @pytest.fixture + def tool(self): + tool = WriteSpecTool() + tool.llm = MockBaseLlm() + tool.resource_manager = Mock() + return tool + + def test_execute(self, tool): + tool.resource_manager.write_file = Mock() + tool.resource_manager.write_file.return_value = "File write successful" + response = tool._execute("Test task description", "test_spec_file.txt") + assert response == "Generated specificationSpecification generated and saved successfully" + tool.resource_manager.write_file.assert_called_once_with("test_spec_file.txt", "Generated specification") diff --git a/tests/unit_tests/tools/test_write_test.py b/tests/unit_tests/tools/test_write_test.py index c2fd7455a..76642bc8e 100644 --- a/tests/unit_tests/tools/test_write_test.py +++ b/tests/unit_tests/tools/test_write_test.py @@ -23,7 +23,7 @@ def test_write_test_tool_execute(mock_write_file, mock_logger): mock_llm = Mock(spec=BaseLlm) llm_response = {"content": "```python\nsample_code\n```"} mock_llm.chat_completion.return_value = llm_response - mock_write_file.return_value = "Tests generated and saved successfully" + mock_write_file.return_value = "Tests generated and saved successfully in test_file" tool = WriteTestTool() tool.llm = mock_llm @@ -35,7 +35,7 @@ def test_write_test_tool_execute(mock_write_file, mock_logger): # Then mock_llm.chat_completion.assert_called_once() mock_write_file.assert_called_once_with("test_file", "sample_code") - assert result == "Tests generated and saved successfully" + assert ("Tests generated and saved successfully in test_file" in result) == True def test_write_test_tool_execute_with_exception(): From 2d39774ee38a03156993000d0910a919e86b7d14 Mon Sep 17 00:00:00 2001 From: COLONAYUSH Date: Thu, 22 Jun 2023 14:07:16 +0530 Subject: [PATCH 23/35] Added __init__ file --- tests/unit_tests/tools/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/unit_tests/tools/__init__.py diff --git a/tests/unit_tests/tools/__init__.py b/tests/unit_tests/tools/__init__.py new file mode 100644 index 000000000..e69de29bb From 8c82b38d37d6e21dcada71e76349679f721eca40 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 14:11:27 +0530 Subject: [PATCH 24/35] increasing the tokens for code, spec generation --- superagi/tools/code/write_code.py | 20 +++++++++----------- superagi/tools/code/write_spec.py | 8 ++++++-- superagi/tools/code/write_test.py | 6 +++++- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index ab57d4dc6..187b846b5 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -1,18 +1,15 @@ +import re from typing import Type, Optional, List from pydantic import BaseModel, Field -from superagi.config.config import get_config + from superagi.agent.agent_prompt_builder import AgentPromptBuilder -import os +from superagi.helper.token_counter import TokenCounter +from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger -from superagi.models.db import connect_db -from superagi.helper.resource_helper import ResourceHelper -from superagi.helper.s3_helper import S3Helper -from sqlalchemy.orm import sessionmaker -import re + class CodingSchema(BaseModel): spec_description: str = Field( @@ -98,8 +95,10 @@ def _execute(self, spec_description: str) -> str: prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] - - result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + + total_tokens = TokenCounter.count_message_tokens(messages, self.llm.get_model()) + token_limit = TokenCounter.token_limit(self.llm.get_model()) + result = self.llm.chat_completion(messages, max_tokens=(token_limit - total_tokens - 100)) # Get all filenames and corresponding code blocks regex = r"(\S+?)\n```\S+\n(.+?)```" @@ -120,7 +119,6 @@ def _execute(self, spec_description: str) -> str: continue file_names.append(file_name) - print(code + "RAMRAM1") save_result = self.resource_manager.write_file(file_name, code) if save_result.startswith("Error"): return save_result diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index 140f42596..f15c89b15 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -4,6 +4,8 @@ from superagi.config.config import get_config from superagi.agent.agent_prompt_builder import AgentPromptBuilder import os + +from superagi.helper.token_counter import TokenCounter from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool @@ -78,8 +80,10 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{task}", task_description) messages = [{"role": "system", "content": prompt}] - - result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + + total_tokens = TokenCounter.count_message_tokens(messages, self.llm.get_model()) + token_limit = TokenCounter.token_limit(self.llm.get_model()) + result = self.llm.chat_completion(messages, max_tokens=(token_limit - total_tokens - 100)) # Save the specification to a file write_result = self.resource_manager.write_file(spec_file_name, result["content"]) diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 6215abe4e..969a0f8f1 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -4,6 +4,8 @@ from superagi.config.config import get_config from superagi.agent.agent_prompt_builder import AgentPromptBuilder import os + +from superagi.helper.token_counter import TokenCounter from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool @@ -80,7 +82,9 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: prompt = prompt.replace("{spec}", spec_description) messages = [{"role": "system", "content": prompt}] - result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) + total_tokens = TokenCounter.count_message_tokens(messages, self.llm.get_model()) + token_limit = TokenCounter.token_limit(self.llm.get_model()) + result = self.llm.chat_completion(messages, max_tokens=(token_limit - total_tokens - 100)) # Extract the code part using regular expression code = re.search(r'(?<=```python).*?(?=```)', result["content"], re.DOTALL) From b38f7cad4d79709193497bf7baa873b014aa30fc Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 16:22:02 +0530 Subject: [PATCH 25/35] fixing the spec->code issue --- superagi/jobs/agent_executor.py | 4 ++++ superagi/models/agent_execution_feed.py | 14 ++++++++++++++ superagi/tools/code/write_code.py | 7 ++++++- superagi/tools/code/write_test.py | 11 ++++++++++- superagi/tools/thinking/tools.py | 8 +++++++- superagi/tools/tool_response_query_manager.py | 12 ++++++++++++ 6 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 superagi/tools/tool_response_query_manager.py diff --git a/superagi/jobs/agent_executor.py b/superagi/jobs/agent_executor.py index b5431e879..8253b89dc 100644 --- a/superagi/jobs/agent_executor.py +++ b/superagi/jobs/agent_executor.py @@ -19,6 +19,7 @@ from superagi.models.tool import Tool from superagi.resource_manager.manager import ResourceManager from superagi.tools.thinking.tools import ThinkingTool +from superagi.tools.tool_response_query_manager import ToolResponseQueryManager from superagi.vector_store.embedding.openai import OpenAiEmbedding from superagi.vector_store.vector_factory import VectorFactory from superagi.helper.encyption_helper import decrypt_data @@ -235,6 +236,9 @@ def set_default_params_tools(self, tools, parsed_config, agent_id, model_api_key tool.agent_id = agent_id if hasattr(tool, 'resource_manager'): tool.resource_manager = ResourceManager(session=session, agent_id=agent_id) + if hasattr(tool, 'tool_response_manager'): + tool.tool_response_manager = ToolResponseQueryManager(session=session, agent_execution_id=parsed_config[ + "agent_execution_id"]) new_tools.append(tool) return tools diff --git a/superagi/models/agent_execution_feed.py b/superagi/models/agent_execution_feed.py index 9a9cb94f1..98acf5d26 100644 --- a/superagi/models/agent_execution_feed.py +++ b/superagi/models/agent_execution_feed.py @@ -1,4 +1,5 @@ from sqlalchemy import Column, Integer, Text, String +from sqlalchemy.orm import Session from superagi.models.base_model import DBBaseModel @@ -36,3 +37,16 @@ def __repr__(self): return f"AgentExecutionFeed(id={self.id}, " \ f"agent_execution_id={self.agent_execution_id}, " \ f"feed='{self.feed}', role='{self.role}', extra_info={self.extra_info})" + + @classmethod + def get_last_tool_response(cls, session: Session, agent_execution_id: int, tool_name: str = None): + agent_execution_feeds = session.query(AgentExecutionFeed).filter( + AgentExecutionFeed.agent_execution_id == agent_execution_id, + AgentExecutionFeed.role == "system").order_by(AgentExecutionFeed.created_at.desc()).all() + + for agent_execution_feed in agent_execution_feeds: + if tool_name and not agent_execution_feed.feed.startswith("Tool " + tool_name): + continue + if agent_execution_feed.feed.startswith("Tool"): + return agent_execution_feed.feed + return "" diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 187b846b5..60d726a63 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -9,6 +9,7 @@ from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool +from superagi.tools.tool_response_query_manager import ToolResponseQueryManager class CodingSchema(BaseModel): @@ -41,6 +42,8 @@ class CodingTool(BaseTool): args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] resource_manager: Optional[ResourceManager] = None + last_tool_response: str = "" + tool_response_manager: Optional[ToolResponseQueryManager] = None class Config: arbitrary_types_allowed = True @@ -93,7 +96,9 @@ def _execute(self, spec_description: str) -> str: Before you finish, double check that all parts of the architecture is present in the files. """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{spec}", spec_description) + spec_response = self.tool_response_manager.get_last_response("WriteSpecTool") + prompt = prompt.replace("{spec}", spec_response) + logger.info(prompt) messages = [{"role": "system", "content": prompt}] total_tokens = TokenCounter.count_message_tokens(messages, self.llm.get_model()) diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 969a0f8f1..1bfdf4c14 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -16,6 +16,8 @@ from sqlalchemy.orm import sessionmaker import re +from superagi.tools.tool_response_query_manager import ToolResponseQueryManager + class WriteTestSchema(BaseModel): spec_description: str = Field( @@ -52,6 +54,8 @@ class WriteTestTool(BaseTool): args_schema: Type[WriteTestSchema] = WriteTestSchema goals: List[str] = [] resource_manager: Optional[ResourceManager] = None + tool_response_manager: Optional[ToolResponseQueryManager] = None + class Config: arbitrary_types_allowed = True @@ -75,12 +79,17 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: Please generate pytest unit tests based on the following specification description: {spec} + + Use the below response to generate the code: + {last_tool_response} The tests should be as simple as possible, but still cover all the functionality described in the specification. """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) - prompt = prompt.replace("{spec}", spec_description) + spec_response = self.tool_response_manager.get_last_response("WriteSpecTool") + prompt = prompt.replace("{spec}", spec_response) messages = [{"role": "system", "content": prompt}] + logger.info(prompt) total_tokens = TokenCounter.count_message_tokens(messages, self.llm.get_model()) token_limit = TokenCounter.token_limit(self.llm.get_model()) diff --git a/superagi/tools/thinking/tools.py b/superagi/tools/thinking/tools.py index 7e4c68eee..a30c869cd 100644 --- a/superagi/tools/thinking/tools.py +++ b/superagi/tools/thinking/tools.py @@ -6,6 +6,7 @@ from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool +from superagi.tools.tool_response_query_manager import ToolResponseQueryManager class ThinkingSchema(BaseModel): @@ -32,6 +33,7 @@ class ThinkingTool(BaseTool): args_schema: Type[ThinkingSchema] = ThinkingSchema goals: List[str] = [] permission_required: bool = False + tool_response_manager: Optional[ToolResponseQueryManager] = None class Config: arbitrary_types_allowed = True @@ -54,13 +56,17 @@ def _execute(self, task_description: str): and the following task, `{task_description}`. + Below is last too response: + `{last_tool_response}` + Perform the task by understanding the problem, extracting variables, and being smart and efficient. Provide a descriptive response, make decisions yourself when confronted with choices and provide reasoning for ideas / decisions. """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{task_description}", task_description) - + last_tool_response = self.tool_response_manager.get_last_response() + prompt = prompt.replace("{last_tool_response}", last_tool_response) messages = [{"role": "system", "content": prompt}] result = self.llm.chat_completion(messages, max_tokens=self.max_token_limit) return result["content"] diff --git a/superagi/tools/tool_response_query_manager.py b/superagi/tools/tool_response_query_manager.py new file mode 100644 index 000000000..5a2387648 --- /dev/null +++ b/superagi/tools/tool_response_query_manager.py @@ -0,0 +1,12 @@ +from sqlalchemy.orm import Session + +from superagi.models.agent_execution_feed import AgentExecutionFeed + + +class ToolResponseQueryManager: + def __init__(self, session: Session, agent_execution_id: int): + self.session = session + self.agent_execution_id = agent_execution_id + + def get_last_response(self, tool_name: str = None): + return AgentExecutionFeed.get_last_tool_response(self.session, self.agent_execution_id, tool_name) From df9b8aaa57f0013acb41cfcc7c21a764ca4724d1 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 17:00:42 +0530 Subject: [PATCH 26/35] fixing issues --- superagi/tools/code/write_code.py | 6 ++---- superagi/tools/code/write_test.py | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 60d726a63..add29e80e 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -13,10 +13,8 @@ class CodingSchema(BaseModel): - spec_description: str = Field( - ..., - description="Specification for generating code which is generated by WriteSpecTool", - ) + pass + class CodingTool(BaseTool): """ Used to generate code. diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 1bfdf4c14..27243f96d 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -20,10 +20,6 @@ class WriteTestSchema(BaseModel): - spec_description: str = Field( - ..., - description="Specification for generating tests generated by WriteSpecTool", - ) test_file_name: str = Field( ..., description="Name of the file to write. Only include the file name. Don't include path." From 47f468c689590fefb4f2f88ffc27db042c517224 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 17:01:27 +0530 Subject: [PATCH 27/35] fixing issues --- superagi/tools/code/write_code.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index add29e80e..ea2ca7bfb 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -13,7 +13,10 @@ class CodingSchema(BaseModel): - pass + spec_description: str = Field( + ..., + description="Description of the coding task", + ) class CodingTool(BaseTool): """ From 7ca8a62d6d0906c481315d22a23dc03bb5ac060a Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 17:02:05 +0530 Subject: [PATCH 28/35] fixing issues --- superagi/tools/code/write_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 27243f96d..71cdcbce1 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -20,6 +20,10 @@ class WriteTestSchema(BaseModel): + spec_description: str = Field( + ..., + description="Description of the testing task", + ) test_file_name: str = Field( ..., description="Name of the file to write. Only include the file name. Don't include path." From 3fde397d9db9e795c03d5c82c695ce834e9a69b1 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 19:36:25 +0530 Subject: [PATCH 29/35] fixing write code and tests --- superagi/tools/code/write_code.py | 15 +++++---- superagi/tools/code/write_test.py | 34 +++++++++---------- tests/unit_tests/tools/test_write_code.py | 15 +++++++-- tests/unit_tests/tools/test_write_spec.py | 3 ++ tests/unit_tests/tools/test_write_test.py | 41 ++++++++++------------- 5 files changed, 56 insertions(+), 52 deletions(-) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index ea2ca7bfb..0bd59f0f0 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -13,7 +13,7 @@ class CodingSchema(BaseModel): - spec_description: str = Field( + code_description: str = Field( ..., description="Description of the coding task", ) @@ -43,19 +43,18 @@ class CodingTool(BaseTool): args_schema: Type[CodingSchema] = CodingSchema goals: List[str] = [] resource_manager: Optional[ResourceManager] = None - last_tool_response: str = "" tool_response_manager: Optional[ToolResponseQueryManager] = None class Config: arbitrary_types_allowed = True - def _execute(self, spec_description: str) -> str: + def _execute(self, code_description: str) -> str: """ Execute the write_code tool. Args: - spec_description : The specification description. + code_description : The coding task description. code_file_name: The name of the file where the generated codes will be saved. Returns: @@ -66,8 +65,10 @@ def _execute(self, spec_description: str) -> str: Your high-level goal is: {goals} + + Coding task description: + {code_description} - Use this specs for generating the code: {spec} You will get instructions for code to write. @@ -97,8 +98,10 @@ def _execute(self, spec_description: str) -> str: Before you finish, double check that all parts of the architecture is present in the files. """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{code_description}", code_description) spec_response = self.tool_response_manager.get_last_response("WriteSpecTool") - prompt = prompt.replace("{spec}", spec_response) + if spec_response != "": + prompt = prompt.replace("{spec}", "Use this specs for generating the code:\n" + spec_response) logger.info(prompt) messages = [{"role": "system", "content": prompt}] diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 71cdcbce1..0060b2cb3 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -1,26 +1,19 @@ +import re from typing import Type, Optional, List from pydantic import BaseModel, Field -from superagi.config.config import get_config -from superagi.agent.agent_prompt_builder import AgentPromptBuilder -import os +from superagi.agent.agent_prompt_builder import AgentPromptBuilder from superagi.helper.token_counter import TokenCounter +from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger -from superagi.models.db import connect_db -from superagi.helper.resource_helper import ResourceHelper -from superagi.helper.s3_helper import S3Helper -from sqlalchemy.orm import sessionmaker -import re - from superagi.tools.tool_response_query_manager import ToolResponseQueryManager class WriteTestSchema(BaseModel): - spec_description: str = Field( + test_description: str = Field( ..., description="Description of the testing task", ) @@ -60,12 +53,12 @@ class WriteTestTool(BaseTool): class Config: arbitrary_types_allowed = True - def _execute(self, spec_description: str, test_file_name: str) -> str: + def _execute(self, test_description: str, test_file_name: str) -> str: """ Execute the write_test tool. Args: - spec_description : The specification description. + test_description : The specification description. test_file_name: The name of the file where the generated tests will be saved. Returns: @@ -76,18 +69,21 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: Your high-level goal is: {goals} + + Test Description: + {test_description} - Please generate pytest unit tests based on the following specification description: {spec} - Use the below response to generate the code: - {last_tool_response} - The tests should be as simple as possible, but still cover all the functionality described in the specification. """ prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) + prompt = prompt.replace("{test_description}", test_description) + spec_response = self.tool_response_manager.get_last_response("WriteSpecTool") - prompt = prompt.replace("{spec}", spec_response) + if spec_response != "": + prompt = prompt.replace("{spec}", "Please generate unit tests based on the following specification description:\n" + spec_response) + messages = [{"role": "system", "content": prompt}] logger.info(prompt) @@ -96,7 +92,7 @@ def _execute(self, spec_description: str, test_file_name: str) -> str: result = self.llm.chat_completion(messages, max_tokens=(token_limit - total_tokens - 100)) # Extract the code part using regular expression - code = re.search(r'(?<=```python).*?(?=```)', result["content"], re.DOTALL) + code = re.search(r'(?<=```).*?(?=```)', result["content"], re.DOTALL) if code: code_content = code.group(0).strip() else: diff --git a/tests/unit_tests/tools/test_write_code.py b/tests/unit_tests/tools/test_write_code.py index 24df0a2aa..76f3a5877 100644 --- a/tests/unit_tests/tools/test_write_code.py +++ b/tests/unit_tests/tools/test_write_code.py @@ -4,25 +4,34 @@ from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.code.write_code import CodingTool +from superagi.tools.tool_response_query_manager import ToolResponseQueryManager + class MockBaseLlm: def chat_completion(self, messages, max_tokens): return {"content": "File1.py\n```python\nprint('Hello World')\n```\n\nFile2.py\n```python\nprint('Hello again')\n```"} + def get_model(self): + return "gpt-3.5-turbo" + class TestCodingTool: @pytest.fixture def tool(self): tool = CodingTool() tool.llm = MockBaseLlm() - tool.resource_manager = Mock() + tool.resource_manager = Mock(spec=ResourceManager) + tool.tool_response_manager = Mock(spec=ToolResponseQueryManager) return tool def test_execute(self, tool): - tool.resource_manager.write_file = Mock() tool.resource_manager.write_file.return_value = "File write successful" + tool.tool_response_manager.get_last_response.return_value = "Mocked Spec" + response = tool._execute("Test spec description") assert response == "File1.py\n```python\nprint('Hello World')\n```\n\nFile2.py\n```python\nprint('Hello again')\n```\n Codes generated and saved successfully in File1.py, File2.py" + tool.resource_manager.write_file.assert_any_call("README.md", 'File1.py\n') tool.resource_manager.write_file.assert_any_call("File1.py", "print('Hello World')\n") - tool.resource_manager.write_file.assert_any_call("File2.py", "print('Hello again')\n") \ No newline at end of file + tool.resource_manager.write_file.assert_any_call("File2.py", "print('Hello again')\n") + tool.tool_response_manager.get_last_response.assert_called_once_with("WriteSpecTool") \ No newline at end of file diff --git a/tests/unit_tests/tools/test_write_spec.py b/tests/unit_tests/tools/test_write_spec.py index 575cb33ff..05f85ed5c 100644 --- a/tests/unit_tests/tools/test_write_spec.py +++ b/tests/unit_tests/tools/test_write_spec.py @@ -9,6 +9,9 @@ class MockBaseLlm: def chat_completion(self, messages, max_tokens): return {"content": "Generated specification"} + def get_model(self): + return "gpt-3.5-turbo" + class TestWriteSpecTool: @pytest.fixture diff --git a/tests/unit_tests/tools/test_write_test.py b/tests/unit_tests/tools/test_write_test.py index 76642bc8e..3aba9c788 100644 --- a/tests/unit_tests/tools/test_write_test.py +++ b/tests/unit_tests/tools/test_write_test.py @@ -4,6 +4,7 @@ from superagi.resource_manager.manager import ResourceManager from superagi.lib.logger import logger from superagi.tools.code.write_test import WriteTestTool +from superagi.tools.tool_response_query_manager import ToolResponseQueryManager def test_write_test_tool_init(): @@ -16,39 +17,31 @@ def test_write_test_tool_init(): assert tool.resource_manager is None -@patch.object(logger, 'error') -@patch.object(ResourceManager, 'write_file') -def test_write_test_tool_execute(mock_write_file, mock_logger): +@patch('superagi.tools.code.write_test.logger') +@patch('superagi.tools.code.write_test.TokenCounter') +def test_write_test_tool_execute(mock_token_counter, mock_logger): # Given mock_llm = Mock(spec=BaseLlm) - llm_response = {"content": "```python\nsample_code\n```"} - mock_llm.chat_completion.return_value = llm_response - mock_write_file.return_value = "Tests generated and saved successfully in test_file" - - tool = WriteTestTool() - tool.llm = mock_llm - tool.resource_manager = ResourceManager(session=None) - - # When - result = tool._execute("spec", "test_file") - - # Then - mock_llm.chat_completion.assert_called_once() - mock_write_file.assert_called_once_with("test_file", "sample_code") - assert ("Tests generated and saved successfully in test_file" in result) == True + mock_llm.get_model.return_value = None + mock_llm.chat_completion.return_value = {"content": "```python\nsample_code\n```"} + mock_token_counter.count_message_tokens.return_value = 0 + mock_token_counter.token_limit.return_value = 100 + mock_resource_manager = Mock(spec=ResourceManager) + mock_resource_manager.write_file.return_value = "No error" -def test_write_test_tool_execute_with_exception(): - # Given - mock_llm = Mock(spec=BaseLlm) - mock_llm.chat_completion.side_effect = Exception("Mock error") + mock_tool_response_manager = Mock(spec=ToolResponseQueryManager) + mock_tool_response_manager.get_last_response.return_value = "" tool = WriteTestTool() tool.llm = mock_llm - tool.resource_manager = Mock() + tool.resource_manager = mock_resource_manager + tool.tool_response_manager = mock_tool_response_manager # When - tool._execute("spec", "test_file") + result = tool._execute("spec", "test_file") # Then mock_llm.chat_completion.assert_called_once() + mock_resource_manager.write_file.assert_called_once_with("test_file", "python\nsample_code") + assert "Tests generated and saved successfully in test_file" in result From f1befedd7998e02af52d98286597fe347059b231 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 19:53:51 +0530 Subject: [PATCH 30/35] fixing binary file write issue --- superagi/resource_manager/manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/superagi/resource_manager/manager.py b/superagi/resource_manager/manager.py index 166af9884..565aa6276 100644 --- a/superagi/resource_manager/manager.py +++ b/superagi/resource_manager/manager.py @@ -17,9 +17,9 @@ def write_binary_file(self, file_name: str, data): else: final_path = ResourceHelper.get_resource_path(file_name) - if self.agent_id is not None: - directory = os.path.dirname(final_path + "/" + str(self.agent_id)) - os.makedirs(directory, exist_ok=True) + # if self.agent_id is not None: + # directory = os.path.dirname(final_path + "/" + str(self.agent_id) + "/") + # os.makedirs(directory, exist_ok=True) try: with open(final_path, mode="wb") as img: img.write(data) From 45db7d61ebdfca74b5cfef54b70827b68e79b1d5 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 19:59:04 +0530 Subject: [PATCH 31/35] fixing coding issue --- superagi/tools/code/write_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 0bd59f0f0..3127790ac 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -110,7 +110,7 @@ def _execute(self, code_description: str) -> str: result = self.llm.chat_completion(messages, max_tokens=(token_limit - total_tokens - 100)) # Get all filenames and corresponding code blocks - regex = r"(\S+?)\n```\S+\n(.+?)```" + regex = r"(\S+?)\n```\S*\n(.+?)```" matches = re.finditer(regex, result["content"], re.DOTALL) file_names = [] From 6ffaefe262a5f514e314eeef02ae2ab012027a8e Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 20:40:56 +0530 Subject: [PATCH 32/35] Fixing write code prompt --- superagi/tools/code/write_code.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index 3127790ac..d322fc2d1 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -72,17 +72,14 @@ def _execute(self, code_description: str) -> str: {spec} You will get instructions for code to write. - You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. - Make sure that every detail of the architecture is, in the end, implemented as code. - - Think step by step and reason yourself to the right decisions to make sure we get it right. - You will first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. + You need to write a detailed answer. Make sure all parts of the architecture are turned into code. + Think carefully about each step and make good choices to get it right. First, list the main classes, + functions, methods you'll use and a quick comment on their purpose. Then you will output the content of each file including ALL code. Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that [FILENAME] is the lowercase file name including the file extension, - [LANG] is the markup code block language for the code's language, and [CODE] is the code: - + [LANG] is the markup code block language for the code's language, and [CODE] is the code: [FILENAME] ```[LANG] [CODE] From 71d5df3e16aa22fb3f5c9209432dc7563e39407e Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 20:58:14 +0530 Subject: [PATCH 33/35] fixing thinking tool response --- superagi/tools/thinking/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superagi/tools/thinking/tools.py b/superagi/tools/thinking/tools.py index a30c869cd..302b3374d 100644 --- a/superagi/tools/thinking/tools.py +++ b/superagi/tools/thinking/tools.py @@ -56,7 +56,7 @@ def _execute(self, task_description: str): and the following task, `{task_description}`. - Below is last too response: + Below is last tool response: `{last_tool_response}` Perform the task by understanding the problem, extracting variables, and being smart From bf0cdde8686d0e66d306266a5eabb6ad58c39985 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 21:14:47 +0530 Subject: [PATCH 34/35] minor fix --- superagi/helper/resource_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superagi/helper/resource_helper.py b/superagi/helper/resource_helper.py index 5bb662c43..0449827f0 100644 --- a/superagi/helper/resource_helper.py +++ b/superagi/helper/resource_helper.py @@ -75,7 +75,7 @@ def get_root_dir(): @staticmethod def get_agent_resource_path(file_name: str, agent_id: int): - """Get final path of the resource. + """Get agent resource path Args: file_name (str): The name of the file. From 3ac96993c9d6951e8fbb1394999a6bedcc0c0d21 Mon Sep 17 00:00:00 2001 From: TransformerOptimus Date: Thu, 22 Jun 2023 21:24:27 +0530 Subject: [PATCH 35/35] adding agent execution feed tests --- tests/unit_tests/models/__init__.py | 0 .../models/test_agent_execution_feed.py | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/unit_tests/models/__init__.py create mode 100644 tests/unit_tests/models/test_agent_execution_feed.py diff --git a/tests/unit_tests/models/__init__.py b/tests/unit_tests/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit_tests/models/test_agent_execution_feed.py b/tests/unit_tests/models/test_agent_execution_feed.py new file mode 100644 index 000000000..15924c551 --- /dev/null +++ b/tests/unit_tests/models/test_agent_execution_feed.py @@ -0,0 +1,27 @@ +import pytest +from unittest.mock import Mock, create_autospec +from sqlalchemy.orm import Session +from superagi.models.agent_execution_feed import AgentExecutionFeed + + +def test_get_last_tool_response(): + mock_session = create_autospec(Session) + agent_execution_feed_1 = AgentExecutionFeed(id=1, agent_execution_id=2, feed="Tool test1", role='system') + agent_execution_feed_2 = AgentExecutionFeed(id=2, agent_execution_id=2, feed="Tool test2", role='system') + + mock_session.query().filter().order_by().all.return_value = [agent_execution_feed_1, agent_execution_feed_2] + + result = AgentExecutionFeed.get_last_tool_response(mock_session, 2) + + assert result == agent_execution_feed_1.feed # as agent_execution_feed_1 should be the latest based on created_at + + +def test_get_last_tool_response_with_tool_name(): + mock_session = create_autospec(Session) + agent_execution_feed_1 = AgentExecutionFeed(id=1, agent_execution_id=2, feed="Tool test1", role='system') + agent_execution_feed_2 = AgentExecutionFeed(id=2, agent_execution_id=2, feed="Tool test2", role='system') + + mock_session.query().filter().order_by().all.return_value = [agent_execution_feed_1, agent_execution_feed_2] + + result = AgentExecutionFeed.get_last_tool_response(mock_session, 2, "test2") + assert result == agent_execution_feed_2.feed