From eea80d1d9c8588ca635428c8c01f9aa347993d28 Mon Sep 17 00:00:00 2001 From: Zvonimir Sabljic Date: Fri, 11 Aug 2023 10:53:46 +0200 Subject: [PATCH] A couple of fixes --- euclid/const/llm.py | 2 +- euclid/database/database.py | 40 +++++++++++++------ euclid/helpers/Project.py | 14 ++++++- euclid/helpers/agents/Developer.py | 8 ++-- euclid/helpers/cli.py | 11 ++--- euclid/prompts/dev_ops/ran_command.prompt | 2 +- .../development/implement_changes.prompt | 2 +- euclid/utils/llm_connection.py | 2 +- 8 files changed, 53 insertions(+), 28 deletions(-) diff --git a/euclid/const/llm.py b/euclid/const/llm.py index 9c10992..6fc84c8 100644 --- a/euclid/const/llm.py +++ b/euclid/const/llm.py @@ -1,4 +1,4 @@ -MIN_TOKENS_FOR_GPT_RESPONSE = 60 +MIN_TOKENS_FOR_GPT_RESPONSE = 600 MAX_GPT_MODEL_TOKENS = 8192 MAX_QUESTIONS = 3 END_RESPONSE = "EVERYTHING_CLEAR" diff --git a/euclid/database/database.py b/euclid/database/database.py index 6d00d0b..3af5f90 100644 --- a/euclid/database/database.py +++ b/euclid/database/database.py @@ -179,11 +179,11 @@ def hash_and_save_step(Model, app_id, hash_data_args, data_fields, message): try: inserted_id = (Model - .insert(**data_to_insert) - .on_conflict(conflict_target=[Model.app, Model.hash_id], - preserve=fields_to_preserve, - update={}) - .execute()) + .insert(**data_to_insert) + .on_conflict(conflict_target=[Model.app, Model.hash_id], + preserve=fields_to_preserve, + update=data_fields) + .execute()) record = Model.get_by_id(inserted_id) print(colored(f"{message} with id {record.id}", "yellow")) @@ -266,18 +266,19 @@ def get_user_input_from_hash_id(project, query): return user_input def delete_all_subsequent_steps(project): - delete_subsequent_steps(DevelopmentSteps, project.checkpoints['last_development_step'], 'previous_step') - delete_subsequent_steps(CommandRuns, project.checkpoints['last_command_run'], 'previous_step') - delete_subsequent_steps(UserInputs, project.checkpoints['last_user_input'], 'previous_step') + delete_subsequent_steps(DevelopmentSteps, project.checkpoints['last_development_step']) + delete_subsequent_steps(CommandRuns, project.checkpoints['last_command_run']) + delete_subsequent_steps(UserInputs, project.checkpoints['last_user_input']) -def delete_subsequent_steps(model, step, step_field_name): +def delete_subsequent_steps(model, step): if step is None: return print(colored(f"Deleting subsequent {model.__name__} steps after {step.id}", "red")) - subsequent_step = model.get_or_none(**{step_field_name: step.id}) - if subsequent_step: - delete_subsequent_steps(model, subsequent_step, step_field_name) - subsequent_step.delete_instance() + subsequent_steps = model.select().where(model.previous_step == step.id) + for subsequent_step in subsequent_steps: + if subsequent_step: + delete_subsequent_steps(model, subsequent_step) + subsequent_step.delete_instance() def get_all_connected_steps(step, previous_step_field_name): """Recursively get all steps connected to the given step.""" @@ -288,6 +289,11 @@ def get_all_connected_steps(step, previous_step_field_name): prev_step = getattr(prev_step, previous_step_field_name) return connected_steps +def delete_all_steps_from_app(app): + models = [DevelopmentSteps, CommandRuns, UserInputs] + for model in models: + model.delete().where(model.app == app).execute() + def delete_unconnected_steps_from(step, previous_step_field_name): if step is None: return @@ -303,6 +309,14 @@ def delete_unconnected_steps_from(step, previous_step_field_name): print(colored(f"Deleting unconnected {step.__class__.__name__} step {unconnected_step.id}", "red")) unconnected_step.delete_instance() +def save_file_description(project, path, name, description): + (File.insert(app=project.app, path=path, name=name, description=description) + .on_conflict( + conflict_target=[File.app, File.name, File.path], + preserve=[], + update={'description': description}) + .execute()) + def create_tables(): with database: database.create_tables([ diff --git a/euclid/helpers/Project.py b/euclid/helpers/Project.py index bf98a40..eba0717 100644 --- a/euclid/helpers/Project.py +++ b/euclid/helpers/Project.py @@ -3,7 +3,7 @@ import os from termcolor import colored from const.common import IGNORE_FOLDERS from database.models.app import App -from database.database import get_app, delete_unconnected_steps_from +from database.database import get_app, delete_unconnected_steps_from, delete_all_steps_from_app from utils.questionary import styled_text from helpers.files import get_files_content, clear_directory from helpers.cli import build_directory_tree @@ -30,8 +30,16 @@ class Project: 'last_command_run': None, 'last_development_step': None, } + if 'skip_until_dev_step' in args: + self.skip_until_dev_step = args['skip_until_dev_step'] + if args['skip_until_dev_step'] == '0': + delete_all_steps_from_app(args['app_id']) + self.skip_steps = False + else: + self.skip_until_dev_step = None + self.skip_steps = True self.skip_steps = False if ('skip_until_dev_step' in args and args['skip_until_dev_step'] == '0') else True - self.skip_until_dev_step = args['skip_until_dev_step'] if 'skip_until_dev_step' in args else None + # TODO make flexible # self.root_path = get_parent_folder('euclid') self.root_path = '' @@ -99,6 +107,8 @@ class Project: file_path = file_path.replace('./', '', 1).rstrip(file_name) if not file_path.endswith('/'): file_path = file_path + '/' + if not file_path.startswith('/'): + file_path = '/' + file_path return self.root_path + file_path + file_name def save_files_snapshot(self, development_step_id): diff --git a/euclid/helpers/agents/Developer.py b/euclid/helpers/agents/Developer.py index 07d9a85..bde8919 100644 --- a/euclid/helpers/agents/Developer.py +++ b/euclid/helpers/agents/Developer.py @@ -154,19 +154,19 @@ class Developer(Agent): 'development/task/step_check.prompt', {}, GET_TEST_TYPE) - + if test_type == 'command_test': run_command_until_success(command['command'], command['timeout'], convo) elif test_type == 'automated_test': code_monkey.implement_code_changes(convo, automated_test_description, 0) elif test_type == 'manual_test': # TODO make the message better - response = self.project.ask_for_human_intervention( + user_feedback = self.project.ask_for_human_intervention( 'Message from Euclid: I need your help. Can you please test if this was successful?', manual_test_description ) - if response is not None and response != 'DONE': - self.test_code_changes(code_monkey, convo) + if user_feedback is not None: + debug(convo, user_input=user_feedback, issue_description=manual_test_description) def implement_step(self, convo, step_index, type, description): # TODO remove hardcoded folder path diff --git a/euclid/helpers/cli.py b/euclid/helpers/cli.py index 2a72fee..207b3e7 100644 --- a/euclid/helpers/cli.py +++ b/euclid/helpers/cli.py @@ -45,6 +45,12 @@ def execute_command(project, command, timeout=5000): timeout = min(max(timeout, MIN_COMMAND_RUN_TIME), MAX_COMMAND_RUN_TIME) print(colored(f'Can i execute the command: `{command}` with {timeout}ms timeout?', 'white', attrs=['bold'])) + + answer = styled_text( + project, + 'If yes, just press ENTER and if not, please paste the output of running this command here and press ENTER' + ) + project.command_runs_count += 1 command_run = get_command_run_from_hash_id(project, command) if command_run is not None and project.skip_steps: @@ -53,11 +59,6 @@ def execute_command(project, command, timeout=5000): print(colored(f'Restoring command run response id {command_run.id}:\n```\n{command_run.cli_response}```', 'yellow')) return command_run.cli_response - answer = styled_text( - project, - 'If yes, just press ENTER and if not, please paste the output of running this command here and press ENTER' - ) - return_value = None if answer != '': diff --git a/euclid/prompts/dev_ops/ran_command.prompt b/euclid/prompts/dev_ops/ran_command.prompt index 40f473e..c0cb0cf 100644 --- a/euclid/prompts/dev_ops/ran_command.prompt +++ b/euclid/prompts/dev_ops/ran_command.prompt @@ -1,4 +1,4 @@ -I ran the command `{{ command }}` and for this response from CLI: +{{ additional_info }}I ran the command `{{ command }}` and for this response from CLI: ``` {{ cli_response }} ``` diff --git a/euclid/prompts/development/implement_changes.prompt b/euclid/prompts/development/implement_changes.prompt index c415418..d5d77f1 100644 --- a/euclid/prompts/development/implement_changes.prompt +++ b/euclid/prompts/development/implement_changes.prompt @@ -9,6 +9,6 @@ Here is how files look now: {% endfor %} {% endif %} -Now, think step by step and apply the needed changes into appropriate files and return the changed files. +Now, think step by step and apply the needed changes for step #{{ step_index }} - {{ step_description }}. Within the file modifications, anything needs to be written by the user, add the comment in the same line as the code that starts with `// INPUT_REQUIRED {input_description}` where `input_description` is a description of what needs to be added here by the user. Finally, you can save the modified files on the disk by calling `save_files` function. \ No newline at end of file diff --git a/euclid/utils/llm_connection.py b/euclid/utils/llm_connection.py index b106e56..7b6d063 100644 --- a/euclid/utils/llm_connection.py +++ b/euclid/utils/llm_connection.py @@ -182,7 +182,7 @@ def stream_gpt_completion(data, req_type): if function_calls['arguments'] != '': logger.info(f'Response via function call: {function_calls["arguments"]}') function_calls['arguments'] = load_data_to_json(function_calls['arguments']) - return return_result({'function_calls': function_calls}); + return return_result({'function_calls': function_calls}) logger.info(f'Response message: {gpt_response}') new_code = postprocessing(gpt_response, req_type) # TODO add type dynamically return return_result({'text': new_code})