diff --git a/src/options/generate/generator.py b/src/options/generate/generator.py index 21cb508..d5246c6 100644 --- a/src/options/generate/generator.py +++ b/src/options/generate/generator.py @@ -9,6 +9,9 @@ from src.constants import FILE_AND_TAG_PAIRS, NUM_IMPLEMENTATION_STRATEGIES, MAX from src.options.generate.prompt_tasks import general_guidelines, executor_file_task, \ not_allowed_executor, chain_of_thought_optimization, test_executor_file_task, requirements_file_task, \ docker_file_task, not_allowed_docker +from src.options.generate.templates import template_generate_microservice_name, template_generate_possible_packages, \ + template_solve_code_issue, \ + template_solve_dependency_issue, template_is_dependency_issue, template_generate_playground from src.utils.io import persist_file, get_all_microservice_files_with_content, get_microservice_path from src.utils.string_tools import print_colored @@ -33,27 +36,24 @@ class Generator: def write_config_yml(self, microservice_name, dest_folder): config_content = f''' - jtype: {microservice_name} - py_modules: - - microservice.py - metas: - name: {microservice_name} - ''' +jtype: {microservice_name} +py_modules: + - microservice.py +metas: + name: {microservice_name} +''' with open(os.path.join(dest_folder, 'config.yml'), 'w') as f: f.write(config_content) - def files_to_string(self, file_name_to_content): + def files_to_string(self, file_name_to_content, restrict_keys=None): all_microservice_files_string = '' for file_name, tag in FILE_AND_TAG_PAIRS: - if file_name in file_name_to_content: - all_microservice_files_string += f'**{file_name}**\n' - all_microservice_files_string += f'```{tag}\n' - all_microservice_files_string += file_name_to_content[file_name] - all_microservice_files_string += '\n```' + if file_name in file_name_to_content and (not restrict_keys or file_name in restrict_keys): + all_microservice_files_string += f'**{file_name}**\n```{tag}\n{file_name_to_content[file_name]}\n```\n\n' return all_microservice_files_string - def wrap_content_in_code_block(self, microservice_content, file_name, tag): - return f'**{file_name}**\n```{tag}\n{microservice_content}\n```\n\n' + # def wrap_content_in_code_block(self, microservice_content, file_name, tag): + # return f'**{file_name}**\n```{tag}\n{microservice_content}\n```\n\n' def generate_microservice( self, @@ -149,30 +149,15 @@ class Generator: print_colored('', '############# Playground #############', 'blue') file_name_to_content = get_all_microservice_files_with_content(microservice_path) - user_query = ( - general_guidelines() - + self.wrap_content_in_code_block(file_name_to_content['microservice.py'], 'microservice.py', 'python') - + self.wrap_content_in_code_block(file_name_to_content['test_microservice.py'], 'test_microservice.py', - 'python') - + f''' -Create a playground for the executor {microservice_name} using streamlit. -The playground must look like it was made by a professional designer. -All the ui elements are well thought out to make them visually appealing and easy to use. -This is an example how you can connect to the executor assuming the document (d) is already defined: -``` -from jina import Client, Document, DocumentArray -client = Client(host=host) -response = client.post('/', inputs=DocumentArray([d])) # always use '/' -print(response[0].text) # can also be blob in case of image/audio..., this should be visualized in the streamlit app -``` -Note that the response will always be in response[0].text -You must provide the complete file with the exact same syntax to wrap the code. -The playground (app.py) must read the host from sys.argv because it will be started with a custom host: streamlit run app.py -- --host grpc://... -The playground (app.py) must not let the user configure the host on the ui. -''' - ) conversation = self.gpt_session.get_conversation([]) - conversation.chat(user_query) + conversation.chat( + template_generate_playground.format( + general_guidelines=general_guidelines(), + code_files_wrapped=self.files_to_string(file_name_to_content, ['microservice.py', 'test_microservice.py']), + microservice_name=microservice_name, + + ) + ) playground_content_raw = conversation.chat(chain_of_thought_optimization('python', 'app.py', 'the playground')) playground_content = self.extract_content_from_result(playground_content_raw, 'app.py', match_single_block=True) persist_file(playground_content, os.path.join(microservice_path, 'app.py')) @@ -208,10 +193,15 @@ The playground (app.py) must not let the user configure the host on the ui. key: val for key, val in file_name_to_content.items() if key in ['requirements.txt', 'Dockerfile'] }) - user_query = self.get_user_query_dependency_issue(all_files_string, error) + user_query = template_solve_dependency_issue.format( + description=description, error=error, all_files_string=all_files_string, + not_allowed_docker=not_allowed_docker() + ) else: - user_query = self.get_user_query_code_issue(description, error, file_name_to_content, - test) + user_query = template_solve_code_issue.format( + description=description, error=error, all_files_string=self.files_to_string(file_name_to_content), + not_allowed_executor=not_allowed_executor() + ) conversation = self.gpt_session.get_conversation() returned_files_raw = conversation.chat(user_query) for file_name, tag in FILE_AND_TAG_PAIRS: @@ -221,55 +211,6 @@ The playground (app.py) must not let the user configure the host on the ui. for file_name, content in file_name_to_content.items(): persist_file(content, os.path.join(next_microservice_path, file_name)) - def get_user_query_dependency_issue(self, all_files_string, error): - user_query = ( - f''' -Your task is to provide guidance on how to solve an error that occurred during the Docker build process. -The error message is: -**microservice.log** -``` -{error} -``` -To solve this error, you should: -1. Identify the type of error by examining the stack trace. -2. Suggest how to solve it. -3. Your suggestion must include the files that need to be changed, but not files that don't need to be changed. -For files that need to be changed, you must provide the complete file with the exact same syntax to wrap the code. -Obey the following rules: {not_allowed_docker()} - -You are given the following files: - -{all_files_string}" -''' - ) - return user_query - - def get_user_query_code_issue(self, description, error, file_name_to_content, test): - all_files_string = self.files_to_string(file_name_to_content) - return f''' -General rules: {not_allowed_executor()} -Here is the description of the task the executor must solve: -{description} - -Here is the test scenario the executor must pass:\n{test} -Here are all the files I use: -{all_files_string} - - -This is the error I encounter currently during the docker build process: -{error} - -Look at the stack trace of the current error. First, think about what kind of error is this? -Then think about possible reasons which might have caused it. Then suggest how to -solve it. Output all the files that need change. -Don't output files that don't need change. If you output a file, then write the -complete file. Use the exact same syntax to wrap the code: -**...** -```... -...code... -``` -''' - class MaxDebugTimeReachedException(BaseException): pass @@ -280,68 +221,21 @@ complete file. Use the exact same syntax to wrap the code: print_colored('', 'Is it a dependency issue?', 'blue') conversation = self.gpt_session.get_conversation([]) - answer = conversation.chat( - f'Your task is to assist in identifying the root cause of a Docker build error for a python application. ' - f'The error message is as follows::\n\n{error}\n\n' - f'The docker file is as follows:\n\n{docker_file}\n\n' - f'Is this a dependency installation failure? Answer with "yes" or "no".' - ) + answer = conversation.chat(template_is_dependency_issue.format(error=error, docker_file=docker_file)) return 'yes' in answer.lower() def generate_microservice_name(self, description): conversation = self.gpt_session.get_conversation() - user_query = f''' -Generate a name for the executor matching the description: -"{description}" -The executor name must fulfill the following criteria: -- camel case -- start with a capital letter -- only consists of lower and upper case characters -- end with Executor. - -The output is a the raw string wrapped into ``` and starting with **name.txt** like this: -**name.txt** -``` -PDFParserExecutor -``` -''' - name_raw = conversation.chat(user_query) + name_raw = conversation.chat(template_generate_microservice_name.format(description=description)) name = self.extract_content_from_result(name_raw, 'name.txt') return name def get_possible_packages(self, description): print_colored('', '############# What packages to use? #############', 'blue') - user_query = f''' -Here is the task description of the problem you need to solve: -"{description}" -1. Write down all the non-trivial subtasks you need to solve. -2. Find out what is the core problem to solve. -3. List up to 15 Python packages that are specifically designed or have functionalities to solve the complete core problem. -4. For each of the 15 package think if it fulfills the following requirements: -a) specifically designed or have functionalities to solve the complete core problem. -b) has a stable api among different versions -c) does not have system requirements -d) can solve the task when running in a docker container -e) the implementation of the core problem using the package would obey the following rules: -{not_allowed_executor()} -When answering, just write "yes" or "no". - -5. Output the most suitable 5 python packages starting with the best one. -If the package is mentioned in the description, then it is automatically the best one. - -The output must be a list of lists wrapped into ``` and starting with **packages.csv** like this: -**packages.csv** -``` -package1 -package2 -package3 -package4 -package5 -... -``` -''' conversation = self.gpt_session.get_conversation() - packages_raw = conversation.chat(user_query) + packages_raw = conversation.chat( + template_generate_possible_packages.format(description=description, not_allowed_executor=not_allowed_executor()) + ) packages_csv_string = self.extract_content_from_result(packages_raw, 'packages.csv') packages = [package.split(',') for package in packages_csv_string.split('\n')] packages = packages[:NUM_IMPLEMENTATION_STRATEGIES] diff --git a/src/options/generate/prompt_system.py b/src/options/generate/prompt_system.py index 64ad82e..d13b307 100644 --- a/src/options/generate/prompt_system.py +++ b/src/options/generate/prompt_system.py @@ -71,8 +71,7 @@ print(response[0].text) ```''' -system_message_base = f''' -It is the year 2021. +system_message_base = '''It is the year 2021. You are a principal engineer working at Jina - an open source company. You accurately satisfy all of the user's requirements. -''' \ No newline at end of file +Your goal is to build a microservice that: {description}''' \ No newline at end of file diff --git a/src/options/generate/templates.py b/src/options/generate/templates.py new file mode 100644 index 0000000..d334b3b --- /dev/null +++ b/src/options/generate/templates.py @@ -0,0 +1,132 @@ +from langchain import PromptTemplate + +template_generate_microservice_name = PromptTemplate.from_template( + '''Generate a name for the executor matching the description: +"{description}" +The executor name must fulfill the following criteria: +- camel case +- start with a capital letter +- only consists of lower and upper case characters +- end with Executor. + +The output is a the raw string wrapped into ``` and starting with **name.txt** like this: +**name.txt** +``` +PDFParserExecutor +```''' +) + + +template_generate_possible_packages = PromptTemplate.from_template( + '''Here is the task description of the problem you need to solve: +"{description}" +1. Write down all the non-trivial subtasks you need to solve. +2. Find out what is the core problem to solve. +3. List up to 15 Python packages that are specifically designed or have functionalities to solve the complete core problem. +4. For each of the 15 package think if it fulfills the following requirements: +a) specifically designed or have functionalities to solve the complete core problem. +b) has a stable api among different versions +c) does not have system requirements +d) can solve the task when running in a docker container +e) the implementation of the core problem using the package would obey the following rules: +{not_allowed_executor} +When answering, just write "yes" or "no". + +5. Output the most suitable 5 python packages starting with the best one. +If the package is mentioned in the description, then it is automatically the best one. + +The output must be a list of lists wrapped into ``` and starting with **packages.csv** like this: +**packages.csv** +``` +package1 +package2 +package3 +package4 +package5 +... +``` +''') + + +template_solve_code_issue = PromptTemplate.from_template( + '''General rules: {not_allowed_executor} +Here is the description of the task the executor must solve: +{description} + +Here is the test scenario the executor must pass:\n{test} +Here are all the files I use: +{all_files_string} + + +This is the error I encounter currently during the docker build process: +{error} + +Look at the stack trace of the current error. First, think about what kind of error is this? +Then think about possible reasons which might have caused it. Then suggest how to +solve it. Output all the files that need change. +Don't output files that don't need change. If you output a file, then write the +complete file. Use the exact same syntax to wrap the code: +**...** +```... +...code... +``` +''' +) + + +template_solve_dependency_issue = PromptTemplate.from_template( + '''Your task is to provide guidance on how to solve an error that occurred during the Docker build process. +The error message is: +**microservice.log** +``` +{error} +``` +To solve this error, you should: +1. Identify the type of error by examining the stack trace. +2. Suggest how to solve it. +3. Your suggestion must include the files that need to be changed, but not files that don't need to be changed. +For files that need to be changed, you must provide the complete file with the exact same syntax to wrap the code. +Obey the following rules: {not_allowed_docker} + +You are given the following files: + +{all_files_string} +''' +) + + +template_is_dependency_issue = PromptTemplate.from_template( + '''Your task is to assist in identifying the root cause of a Docker build error for a python application. +The error message is as follows: + +{error} + +The docker file is as follows: + +{docker_file} + +Is this a dependency installation failure? Answer with "yes" or "no".''' +) + + +template_generate_playground = PromptTemplate.from_template( + '''{general_guidelines} + +{code_files_wrapped} + +Create a playground for the executor {microservice_name} using streamlit. +The playground must look like it was made by a professional designer. +All the ui elements are well thought out to make them visually appealing and easy to use. +This is an example how you can connect to the executor assuming the document (d) is already defined: +``` +from jina import Client, Document, DocumentArray +client = Client(host=host) +response = client.post('/', inputs=DocumentArray([d])) # always use '/' +print(response[0].text) # can also be blob in case of image/audio..., this should be visualized in the streamlit app +``` +Note that the response will always be in response[0].text +You must provide the complete file with the exact same syntax to wrap the code. +The playground (app.py) must read the host from sys.argv because it will be started with a custom host: streamlit run app.py -- --host grpc://... +The playground (app.py) must not let the user configure the host on the ui. +''' +)