Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Proper Logging #25

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
venv
openai_key.txt
wolverine.log
171 changes: 124 additions & 47 deletions wolverine.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@
import shutil
import subprocess
import sys

import openai
import logging
from termcolor import cprint

# Set up the OpenAI API
with open("openai_key.txt") as f:
openai.api_key = f.read().strip()
# Load the OpenAI API key
def load_openai_key():
with open("openai_key.txt") as f:
return f.read().strip()

# Set up logging
def configure_logging():
logging.basicConfig(
filename="wolverine.log",
format="%(asctime)s - %(levelname)s - %(message)s",
level=logging.INFO,
)


# Run the provided script and return the output and return code
def run_script(script_name, script_args):
script_args = [str(arg) for arg in script_args]
try:
Expand All @@ -24,8 +34,8 @@ def run_script(script_name, script_args):
return e.output.decode("utf-8"), e.returncode
return result.decode("utf-8"), 0


def send_error_to_gpt(file_path, args, error_message, model):
# Send the error to GPT and receive suggestions
def send_error_to_gpt(file_path, args, error_message, model, prompt_length_limit=4096):
with open(file_path, "r") as f:
file_lines = f.readlines()

Expand All @@ -50,7 +60,9 @@ def send_error_to_gpt(file_path, args, error_message, model):
"exact format as described above."
)

# print(prompt)
# Truncate the prompt if it exceeds the limit
if len(prompt) > prompt_length_limit:
prompt = prompt[:prompt_length_limit]

response = openai.ChatCompletion.create(
model=model,
Expand All @@ -65,45 +77,83 @@ def send_error_to_gpt(file_path, args, error_message, model):

return response.choices[0].message.content.strip()


# Apply the changes suggested by GPT
def apply_changes(file_path, changes_json):
try:
with open(file_path, "r") as f:
original_file_lines = f.readlines()

changes = json.loads(changes_json)

operation_changes = [change for change in changes if "operation" in change]
explanations = [
change["explanation"] for change in changes if "explanation" in change
]

operation_changes.sort(key=lambda x: x["line"], reverse=True)

file_lines = original_file_lines.copy()
for change in operation_changes:
operation = change["operation"]
line = change["line"]
content = change["content"]

if operation == "Replace":
file_lines[line - 1] = content + "\n"
elif operation == "Delete":
del file_lines[line - 1]
elif operation == "InsertAfter":
file_lines.insert(line, content + "\n")

with open(file_path, "w") as f:
f.writelines(file_lines)

# Print explanations
cprint("Explanations:", "blue")
for explanation in explanations:
cprint(f"- {explanation}", "blue")

# Show the diff
print("\nChanges:")
print_diff(original_file_lines, file_lines)
except Exception as e:
raise Exception(f"Failed to apply changes: {str(e)}")

# Apply a single change suggested by GPT interactively
def apply_change_interactive(file_path, change):
with open(file_path, "r") as f:
original_file_lines = f.readlines()

changes = json.loads(changes_json)
operation = change["operation"]
line = change["line"]
content = change["content"]

# Filter out explanation elements
operation_changes = [change for change in changes if "operation" in change]
explanations = [
change["explanation"] for change in changes if "explanation" in change
]
file_lines = original_file_lines.copy()
if operation == "Replace":
file_lines[line - 1] = content + "\n"
elif operation == "Delete":
del file_lines[line - 1]
elif operation == "InsertAfter":
file_lines.insert(line, content + "\n")

# Sort the changes in reverse line order
operation_changes.sort(key=lambda x: x["line"], reverse=True)
print("\nSuggested change:")
print_diff(original_file_lines, file_lines)

file_lines = original_file_lines.copy()
for change in operation_changes:
operation = change["operation"]
line = change["line"]
content = change["content"]

if operation == "Replace":
file_lines[line - 1] = content + "\n"
elif operation == "Delete":
del file_lines[line - 1]
elif operation == "InsertAfter":
file_lines.insert(line, content + "\n")

with open(file_path, "w") as f:
f.writelines(file_lines)

# Print explanations
cprint("Explanations:", "blue")
for explanation in explanations:
cprint(f"- {explanation}", "blue")

# Show the diff
print("\nChanges:")
while True:
decision = input("Do you want to apply this change? (y/n): ").lower()
if decision == "y":
with open(file_path, "w") as f:
f.writelines(file_lines)
logging.info(f"Applied change: {change}")
return True
elif decision == "n":
logging.info(f"Rejected change: {change}")
return False
else:
print("Invalid input. Please enter 'y' or 'n'.")

# Print the differences between two file contents
def print_diff(original_file_lines, file_lines):
diff = difflib.unified_diff(original_file_lines, file_lines, lineterm="")
for line in diff:
if line.startswith("+"):
Expand All @@ -113,17 +163,17 @@ def apply_changes(file_path, changes_json):
else:
print(line, end="")

def main(script_name, *script_args, revert=False, model="gpt-4", interactive=False):
openai.api_key = load_openai_key()

def main(script_name, *script_args, revert=False, model="gpt-4"):
if revert:
backup_file = script_name + ".bak"
if os.path.exists(backup_file):
shutil.copy(backup_file, script_name)
print(f"Reverted changes to {script_name}")
sys.exit(0)
return
else:
print(f"No backup file found for {script_name}")
sys.exit(1)
raise Exception(f"No backup file found for {script_name}")

# Make a backup of the original script
shutil.copy(script_name, script_name + ".bak")
Expand All @@ -134,9 +184,11 @@ def main(script_name, *script_args, revert=False, model="gpt-4"):
if returncode == 0:
cprint("Script ran successfully.", "blue")
print("Output:", output)
logging.info("Script ran successfully.")
break
else:
cprint("Script crashed. Trying to fix...", "blue")
logging.error(f"Script crashed with return code {returncode}.")
print("Output:", output)

json_response = send_error_to_gpt(
Expand All @@ -145,9 +197,34 @@ def main(script_name, *script_args, revert=False, model="gpt-4"):
error_message=output,
model=model,
)
apply_changes(script_name, json_response)
cprint("Changes applied. Rerunning...", "blue")

if interactive:
changes = json.loads(json_response)
operation_changes = [change for change in changes if "operation" in change]
explanations = [
change["explanation"] for change in changes if "explanation" in change
]

for change in operation_changes:
if apply_change_interactive(script_name, change):
cprint("Change applied.", "green")
else:
cprint("Change rejected.", "red")
cprint("Finished applying changes. Rerunning...", "blue")
logging.info("Finished applying changes in interactive mode.")
else:
try:
apply_changes(script_name, json_response)
cprint("Changes applied. Rerunning...", "blue")
logging.info("Changes applied.")
except Exception as e:
raise Exception(f"Failed to fix the script: {str(e)}")

if __name__ == "__main__":
fire.Fire(main)
configure_logging()
try:
fire.Fire(main)
except Exception as e:
print(str(e))
logging.error(f"Error: {str(e)}")
sys.exit(1)