|
from platform import platform |
|
from sys import version_info |
|
from typing import List, Union |
|
|
|
from langchain.schema import AIMessage, HumanMessage, SystemMessage |
|
from termcolor import colored |
|
|
|
from gpt_engineer.core.ai import AI |
|
from gpt_engineer.core.base_execution_env import BaseExecutionEnv |
|
from gpt_engineer.core.base_memory import BaseMemory |
|
from gpt_engineer.core.chat_to_files import chat_to_files_dict |
|
from gpt_engineer.core.default.paths import CODE_GEN_LOG_FILE, ENTRYPOINT_FILE |
|
from gpt_engineer.core.default.steps import curr_fn, setup_sys_prompt |
|
from gpt_engineer.core.files_dict import FilesDict |
|
from gpt_engineer.core.preprompts_holder import PrepromptsHolder |
|
|
|
|
|
Message = Union[AIMessage, HumanMessage, SystemMessage] |
|
MAX_SELF_HEAL_ATTEMPTS = 2 |
|
|
|
|
|
def get_platform_info(): |
|
"""Returns the Platform: OS, and the Python version. |
|
This is used for self healing. There are some possible areas of conflict here if |
|
you use a different version of Python in your virtualenv. A better solution would |
|
be to have this info printed from the virtualenv. |
|
""" |
|
v = version_info |
|
a = f"Python Version: {v.major}.{v.minor}.{v.micro}" |
|
b = f"\nOS: {platform()}\n" |
|
return a + b |
|
|
|
|
|
def self_heal( |
|
ai: AI, |
|
execution_env: BaseExecutionEnv, |
|
files_dict: FilesDict, |
|
preprompts_holder: PrepromptsHolder = None, |
|
) -> FilesDict: |
|
"""Attempts to execute the code from the entrypoint and if it fails, |
|
sends the error output back to the AI with instructions to fix. |
|
This code will make `MAX_SELF_HEAL_ATTEMPTS` to try and fix the code |
|
before giving up. |
|
This makes the assuption that the previous step was `gen_entrypoint`, |
|
this code could work with `simple_gen`, or `gen_clarified_code` as well. |
|
""" |
|
|
|
|
|
|
|
if ENTRYPOINT_FILE not in files_dict: |
|
raise FileNotFoundError( |
|
"The required entrypoint " |
|
+ ENTRYPOINT_FILE |
|
+ " does not exist in the code." |
|
) |
|
|
|
attempts = 0 |
|
messages = [] |
|
if preprompts_holder is None: |
|
raise AssertionError("Prepromptsholder required for self-heal") |
|
preprompts = preprompts_holder.get_preprompts() |
|
while attempts < MAX_SELF_HEAL_ATTEMPTS: |
|
command = files_dict[ENTRYPOINT_FILE] |
|
print( |
|
colored( |
|
"Do you want to execute this code? (Y/n)", |
|
"red", |
|
) |
|
) |
|
print() |
|
print(command) |
|
print() |
|
if input("").lower() not in ["", "y", "yes"]: |
|
print("Ok, not executing the code.") |
|
return files_dict |
|
print("Executing the code...") |
|
stdout_full, stderr_full, returncode = execution_env.upload(files_dict).run( |
|
f"bash {ENTRYPOINT_FILE}" |
|
) |
|
|
|
|
|
if returncode != 0: |
|
print("run.sh failed. Let's fix it.") |
|
|
|
|
|
|
|
|
|
|
|
if attempts < 1: |
|
messages: List[Message] = [SystemMessage(content=files_dict.to_chat())] |
|
messages.append(SystemMessage(content=get_platform_info())) |
|
|
|
messages.append(SystemMessage(content=stdout_full + "\n " + stderr_full)) |
|
|
|
messages = ai.next( |
|
messages, preprompts["file_format_fix"], step_name=curr_fn() |
|
) |
|
else: |
|
return files_dict |
|
|
|
files_dict = {**files_dict, **chat_to_files_dict(messages[-1].content.strip())} |
|
attempts += 1 |
|
|
|
return files_dict |
|
|
|
|
|
def clarified_gen( |
|
ai: AI, prompt: str, memory: BaseMemory, preprompts_holder: PrepromptsHolder |
|
) -> FilesDict: |
|
""" |
|
Generates code based on clarifications obtained from the user. |
|
|
|
This function processes the messages logged during the user's clarification session |
|
and uses them, along with the system's prompts, to guide the AI in generating code. |
|
The generated code is saved to a specified workspace. |
|
|
|
Parameters: |
|
- ai (AI): An instance of the AI model, responsible for processing and generating the code. |
|
- dbs (DBs): An instance containing the database configurations, which includes system |
|
and input prompts. |
|
|
|
Returns: |
|
- List[dict]: A list of message dictionaries capturing the AI's interactions and generated |
|
outputs during the code generation process. |
|
""" |
|
preprompts = preprompts_holder.get_preprompts() |
|
messages: List[Message] = [SystemMessage(content=preprompts["clarify"])] |
|
user_input = prompt |
|
while True: |
|
messages = ai.next(messages, user_input, step_name=curr_fn()) |
|
msg = messages[-1].content.strip() |
|
|
|
if "nothing to clarify" in msg.lower(): |
|
break |
|
|
|
if msg.lower().startswith("no"): |
|
print("Nothing to clarify.") |
|
break |
|
|
|
print('(answer in text, or "c" to move on)\n') |
|
user_input = input("") |
|
print() |
|
|
|
if not user_input or user_input == "c": |
|
print("(letting gpt-engineer make its own assumptions)") |
|
print() |
|
messages = ai.next( |
|
messages, |
|
"Make your own assumptions and state them explicitly before starting", |
|
step_name=curr_fn(), |
|
) |
|
print() |
|
|
|
user_input += """ |
|
\n\n |
|
Is anything else unclear? If yes, ask another question.\n |
|
Otherwise state: "Nothing to clarify" |
|
""" |
|
|
|
print() |
|
|
|
messages = [ |
|
SystemMessage(content=setup_sys_prompt(preprompts)), |
|
] + messages[ |
|
1: |
|
] |
|
messages = ai.next( |
|
messages, |
|
preprompts["generate"].replace("FILE_FORMAT", preprompts["file_format"]), |
|
step_name=curr_fn(), |
|
) |
|
print() |
|
chat = messages[-1].content.strip() |
|
memory[CODE_GEN_LOG_FILE] = chat |
|
files_dict = chat_to_files_dict(chat) |
|
return files_dict |
|
|
|
|
|
def lite_gen( |
|
ai: AI, prompt: str, memory: BaseMemory, preprompts_holder: PrepromptsHolder |
|
) -> FilesDict: |
|
""" |
|
Executes the AI model using the main prompt and saves the generated results. |
|
|
|
This function invokes the AI model by feeding it the main prompt. After the |
|
AI processes and generates the output, the function saves this output to the |
|
specified workspace. The AI's output is also tracked using the current function's |
|
name to provide context. |
|
|
|
Parameters: |
|
- ai (AI): An instance of the AI model. |
|
- dbs (DBs): An instance containing the database configurations, including input prompts |
|
and file formatting preferences. |
|
|
|
Returns: |
|
- List[Message]: A list of message objects encapsulating the AI's output. |
|
|
|
Note: |
|
The function assumes the `ai.start` method and the `to_files` utility to be correctly |
|
set up and functional. Ensure these prerequisites before invoking `lite_gen`. |
|
""" |
|
preprompts = preprompts_holder.get_preprompts() |
|
messages = ai.start(prompt, preprompts["file_format"], step_name=curr_fn()) |
|
chat = messages[-1].content.strip() |
|
memory[CODE_GEN_LOG_FILE] = chat |
|
files_dict = chat_to_files_dict(chat) |
|
return files_dict |
|
|