Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
samholt committed Apr 20, 2024
1 parent 8a9a060 commit 2cd6576
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 67 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
default_stages: [ commit ]
exclude: '.*__init__\.py$'

# Install
# 1. pip install metagpt[dev]
Expand Down
1 change: 1 addition & 0 deletions l2mac/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from l2mac.core import generate_codebase, generate_book, generate_custom
103 changes: 38 additions & 65 deletions l2mac/l2mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,50 +210,18 @@ def get_function_names_as_str(self):
fns = process_functions_into_function_names(self.functions)
return ", ".join([f"`{fn}`" for fn in fns])

def get_file_names(self):
# Simple implementation, for now, can be improved.
return self.get_file_names()

def run(self, steps: int = 10):
return self._run(steps=steps)

def _run(self, steps: int = 10):
self.reset()
if not self.load_from_checkpoint:
self.meta_messages = [self.system_message]
task_description = self.prompt_task
first_message = f"""
You will get instructions for code to write.
First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.
Do not comment on what every file does. Please note that the code should be fully functional. No placeholders.
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. The code should be fully functional. Make sure that code in different files are compatible with each other.
When writing code if you are unsure, write a plausible implementation.
Include module dependency or package manager dependency definition file.
Useful to know:
For Python, you always create an appropriate requirements.txt file.
Always add a comment briefly describing the purpose of the function definition.
Add comments explaining very complex bits of logic.
Always follow the best practices for the requested languages for folder/file structure and how to package the project.
You can use any package and any other packages you wish to install.
You cannot use any databases as none are setup in the local environment, instead mock a database with an in memory dictionary to store data. No data saved to disk will persist between steps or write operations.
When writing a test, make the filename start with the prefix 'test_'.
When putting files in folders, always be sure to include a file called __init__.py where relevant, or put all files in the same working directory. Always prefer the most simplest approach.
Always add a readme on how to run the code, or a .sh file to run the code.
Python toolbelt preferences:
- pytest
- dataclasses
- flask
Objective:```
{task_description}
```
Understand the problem, by creating an extremely detailed step-by-step plan, where each step is long (multiple sentences) and in total includes every single feature requirement specified above, feel free to copy directly from it. Use no more than {steps} steps in the plan. Create additional tests, checks and evaluation at each step when applicable to help make an excellent code implementation, where all the code is fully functional. Use best software design practices, and you can output large amounts of code at once. Please include a last sentence to create and run tests when implementing or writing code in that same step. You will receive no human input at any stage, so you cannot use a human to test. Only create a detailed plan to begin with, which includes designing and running tests to check that they all pass. Please be sure to include all of the specified feature requirements in the following plan.
"""
first_message = self.l2mac_prompts.first_message.format(prompt_task=self.prompt_task, steps=steps)
self.meta_messages.append({"role": "user", "content": first_message})
steps = []
# Loop until we get a multi-step plan, as sometimes the first plan is not multi-step, and only a single step.
Expand All @@ -273,9 +241,7 @@ def _run(self, steps: int = 10):
current_dialog.append(
{
"role": "user",
"content": """
Please reflect on the plan, and increase the number of generated steps to that of 100 or so very detailed steps that include all the feature requirements.
""",
"content": self.l2mac_prompts.reflect_on_prompt_program,
}
)
# Could reflect and improve plan etc a few times here.
Expand Down Expand Up @@ -318,9 +284,13 @@ def _run(self, steps: int = 10):
self.sub_messages.append(
{
"role": "user",
"content": f"""
Objective: Execute sub task step: {step}.\n\n Note: Condition any new code files on the existing code files: {list(self.file_dict.keys())}. Fully implement these features in the code, no placeholders. You can now optionally view the existing files if you need to view them to complete the current task step. You have a limited context window so be selective about which files you view, only view the files you think you might need to view.\n\nSummary output of previous step: ""{previous_step_output_summary}""\n\nRespond now only with a function call of one of the following functions provided: {self.get_function_names_as_str()}, and if you want to output code only use the `write_files` function to output code.
""",
"content": self.l2mac_prompts.control_unit_execute_instruction.format(
step=step,
file_names=self.get_file_names(),
test_writing_advice=self.l2mac_prompts.test_writing_advice,
previous_step_output_summary=previous_step_output_summary,
functions_provided=self.get_function_names_as_str(),
),
}
)
has_completed_sub_step = False
Expand All @@ -340,40 +310,36 @@ def _run(self, steps: int = 10):
self.max_tokens
- num_tokens_consumed_by_chat_request(messages=self.sub_messages, functions=self.functions)
) < 700:
# self.sub_messages = self.sub_messages[:-1]
self.sub_messages.pop(3)
self.sub_messages.append(
{
"role": "user",
"content": """
You have exhausted your context window. Reflect on your progress. Provide a short concise response, of two sentences maximum, this will be used to restart this step from the beginning without the previous messages.""",
"content": self.l2mac_prompts.control_unit_exhaust_context_window,
}
)
# self.sub_messages.append({"role": "user", "content": f"""
# You have exhausted your context window. Please state only which files are necessary to view to complete this task, i.e. those files which the newly written files import from. Also reflect on your progress. Provide a short concise response, of two sentences maximum, this will be used to restart this step from the beginning without the previous messages.""", "function_call": 'none'})
response_message = self.get_llm_response(self.sub_messages, tool_choice=None)
summary_step_message = response_message["content"]
# if 'maximum context' in e.args[0]:
self.re_tries += 1
if self.re_tries > self.max_re_tries:
# raise e
has_completed_sub_step = True
self.logger.warning(
f"[WARNING] Maximum re-tries reached: {self.re_tries}/{self.max_re_tries}, skipping step"
)
# break
raise ValueError(
f"[ERROR] Maximum re-tries reached: {self.re_tries}/{self.max_re_tries}, stopping run."
)
# Restart the step. Should control re-try times.
self.sub_messages = deepcopy(self.base_dialog)
# Following is 63 tokens
self.sub_messages.append(
{
"role": "user",
"content": f"""
Objective: Execute sub task step: {step} \n\n Note: Condition any new code files on the existing code files: {list(self.file_dict.keys())} Fully implement these features in the code, no placeholders. You can now optionally view the existing files if you need to view them to complete the current task step. You have a limited context window so be selective about which files you view, only view the files you think you might need to view. Ensure each test case is well-documented with comments explaining the scenario it covers. Do not write tests for large numbers and large inputs, if they exist delete them. If a test is failing the error could be the code, or the test is incorrect, so feel free to overwrite and change the tests when they are incorrect, to make all tests pass. Avoid making complicated tests. If a test repeatedly fails delete the test. \n\n {summary_step_message}\n\nRespond now only with a function call of one of the following functions provided: {self.get_function_names_as_str()}, and if you want to output code only use the `write_files` function to output code.
""",
"content": self.l2mac_prompts.control_unit_execute_instruction.format(
step=step,
file_names=self.get_file_names(),
test_writing_advice=self.l2mac_prompts.test_writing_advice,
previous_step_output_summary=summary_step_message,
functions_provided=self.get_function_names_as_str(),
),
}
)
response_message = self.get_llm_response(self.sub_messages)
Expand All @@ -395,7 +361,7 @@ def _run(self, steps: int = 10):
self.sub_messages.append(
{
"role": "user",
"content": """Please provide a one or two sentence summary of the output of this step, which is useful for the next step. Your response will be used when starting the next step without any of the previous messages.""",
"content": self.l2mac_prompts.control_unit_instruction_complete_summarize_output,
}
)
elif (
Expand All @@ -406,27 +372,34 @@ def _run(self, steps: int = 10):
self.sub_messages.append(
{
"role": "user",
"content": f"""{json.loads(function_return_message['content'])['message']}
Reflect and write the full complete corrected code to correct the code. Only use the functions you have been provided with, and if you want to output code only use the `write_files` function to output code. Condition it on existing code: {list(self.file_dict.keys())}\n Ensure each test case is well-documented with comments explaining the scenario it covers. Do not write tests for large numbers and large inputs, if they exist delete them. If a test is failing the error could be the code, or the test is incorrect, so feel free to overwrite and change the tests when they are incorrect, to make all tests pass. Avoid making complicated tests. If a test repeatedly fails delete the test. \nRespond now only with a function call of one of the following functions provided: {self.get_function_names_as_str()}, and if you want to output code only use the `write_files` function to output code.""",
"content": self.l2mac_prompts.control_unit_instruction_erroring_fix_the_code.format(
error_message=json.loads(function_return_message["content"])["message"],
file_names=self.get_file_names(),
test_writing_advice=self.l2mac_prompts.test_writing_advice,
functions_provided=self.get_function_names_as_str(),
),
}
)
else:
self.sub_messages.append(
{
"role": "user",
"content": f"""Has the sub task step been completed of: ```
{step}
``` \n\n If yes, call the function `sub_task_step_complete`, otherwise reflect and correct the full code to complete the task. Only use the functions you have been provided with, and if you want to output code only use the `write_files` function to output code. Condition it on existing code: {list(self.file_dict.keys())} Fully implement these features in the code, no placeholders. If you have not viewed the files before writing to them, please view them, to make sure you are writing to the correct files.\nEnsure each test case is well-documented with comments explaining the scenario it covers. Do not write tests for large numbers and large inputs, if they exist delete them. If a test is failing the error could be the code, or the test is incorrect, so feel free to overwrite and change the tests when they are incorrect, to make all tests pass. Avoid making complicated tests. If a test repeatedly fails delete the test. \nRespond now only with a function call of one of the following functions provided: {self.get_function_names_as_str()}, and if you want to output code only use the `write_files` function to output code.""",
"content": self.l2mac_prompts.control_unit_cycle_message_to_check_if_instruction_complete.format(
step=step,
file_names=self.get_file_names(),
functions_provided=self.get_function_names_as_str(),
),
}
)
else:
self.sub_messages.append(
{
"role": "user",
"content": f"""Has the sub task step been completed of: ```
{step}
``` \n\n If yes, call the function `sub_task_step_complete`, otherwise reflect and correct the full code to complete the task. Only use the functions you have been provided with, and if you want to output code only use the `write_files` function to output code. Condition it on existing code: {list(self.file_dict.keys())} Fully implement these features in the code, no placeholders. If you have not viewed the files before writing to them, please view them, to make sure you are writing to the correct files.\nEnsure each test case is well-documented with comments explaining the scenario it covers. Do not write tests for large numbers and large inputs, if they exist delete them. If a test is failing the error could be the code, or the test is incorrect, so feel free to overwrite and change the tests when they are incorrect, to make all tests pass. Avoid making complicated tests. If a test repeatedly fails delete the test. \nRespond now only with a function call of one of the following functions provided: {self.get_function_names_as_str()}, and if you want to output code only use the `write_files` function to output code.""",
"content": self.l2mac_prompts.control_unit_cycle_message_to_check_if_instruction_complete.format(
step=step,
file_names=self.get_file_names(),
functions_provided=self.get_function_names_as_str(),
),
}
)
write_files_from_dict(self.file_dict, base_dir=f"{self.folder_path}")
Expand Down
Loading

0 comments on commit 2cd6576

Please sign in to comment.