diff --git a/aider/coders/__init__.py b/aider/coders/__init__.py index 637dc343c..196238e3a 100644 --- a/aider/coders/__init__.py +++ b/aider/coders/__init__.py @@ -3,6 +3,7 @@ from .base_coder import Coder from .editblock_coder import EditBlockCoder from .editblock_fenced_coder import EditBlockFencedCoder from .help_coder import HelpCoder +from .senior_coder import SeniorCoder # from .single_wholefile_func_coder import SingleWholeFileFunctionCoder from .udiff_coder import UnifiedDiffCoder @@ -17,4 +18,5 @@ __all__ = [ WholeFileCoder, UnifiedDiffCoder, # SingleWholeFileFunctionCoder, + SeniorCoder, ] diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 0e1a6eaa8..536b6ff87 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -1187,6 +1187,7 @@ class Coder: self.cur_messages += [dict(role="assistant", content=content)] return + self.reply_completed() edited = self.apply_updates() self.update_cur_messages() @@ -1238,6 +1239,9 @@ class Coder: else: self.reflected_message = add_rel_files_message + def reply_completed(self): + pass + def show_exhausted_error(self): output_tokens = 0 if self.partial_response_content: diff --git a/aider/coders/junior_editblock_prompts.py b/aider/coders/junior_editblock_prompts.py index 0e8431f16..a511a6b66 100644 --- a/aider/coders/junior_editblock_prompts.py +++ b/aider/coders/junior_editblock_prompts.py @@ -1,166 +1,16 @@ # flake8: noqa: E501 -from .base_prompts import CoderPrompts +from .editblock_prompts import EditBlockPrompts -class EditBlockPrompts(CoderPrompts): +class JuniorEditBlockPrompts(EditBlockPrompts): main_system = """Act as an expert software developer who edits source code. {lazy_prompt} Describe each change with a *SEARCH/REPLACE block* per the examples below. All changes to files must use this *SEARCH/REPLACE block* format. ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*! -{shell_cmd_prompt} """ - shell_cmd_prompt = """ -4. *Concisely* suggest any shell commands the user might want to run in ```bash blocks. - -Just suggest shell commands this way, not example code. -Only suggest complete shell commands that area ready to execute, without placeholders. -Only suggest at most a few shell commands at a time, not more than 1-3. - -Use the appropriate shell based on the user's system info: -{platform} -Examples of when to suggest shell commands: - -- If you changed a self-contained html file, suggest an OS-appropriate command to open a browser to view it to see the updated content. -- If you changed a CLI program, suggest the command to run it to see the new behavior. -- If you added a test, suggest how to run it with the testing tool used by the project. -- Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations. -- If your code changes add new dependencies, suggest the command to install them. -- Etc. -""" - - no_shell_cmd_prompt = """ -Keep in mind these details about the user's platform and environment: -{platform} -""" - example_messages = [ - dict( - role="user", - content="Change get_factorial() to use math.factorial", - ), - dict( - role="assistant", - content=""" -mathweb/flask/app.py -{fence[0]}python -<<<<<<< SEARCH -from flask import Flask -======= -import math -from flask import Flask ->>>>>>> REPLACE -{fence[1]} - -mathweb/flask/app.py -{fence[0]}python -<<<<<<< SEARCH -def factorial(n): - "compute factorial" - - if n == 0: - return 1 - else: - return n * factorial(n-1) - -======= ->>>>>>> REPLACE -{fence[1]} - -mathweb/flask/app.py -{fence[0]}python -<<<<<<< SEARCH - return str(factorial(n)) -======= - return str(math.factorial(n)) ->>>>>>> REPLACE -{fence[1]} -""", - ), - dict( - role="user", - content="Refactor hello() into its own file.", - ), - dict( - role="assistant", - content=""" -hello.py -{fence[0]}python -<<<<<<< SEARCH -======= -def hello(): - "print a greeting" - - print("hello") ->>>>>>> REPLACE -{fence[1]} - -main.py -{fence[0]}python -<<<<<<< SEARCH -def hello(): - "print a greeting" - - print("hello") -======= -from hello import hello ->>>>>>> REPLACE -{fence[1]} -""", - ), - ] - - system_reminder = """# *SEARCH/REPLACE block* Rules: - -Every *SEARCH/REPLACE block* must use this format: -1. The *FULL* file path alone on a line, verbatim. No bold asterisks, no quotes around it, no escaping of characters, etc. -2. The opening fence and code language, eg: {fence[0]}python -3. The start of search block: <<<<<<< SEARCH -4. A contiguous chunk of lines to search for in the existing source code -5. The dividing line: ======= -6. The lines to replace into the source code -7. The end of the replace block: >>>>>>> REPLACE -8. The closing fence: {fence[1]} - -Use the *FULL* file path, as shown to you by the user. - -Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc. -If the file contains code or other data wrapped/escaped in json/xml/quotes or other containers, you need to propose edits to the literal contents of the file, including the container markup. - -*SEARCH/REPLACE* blocks will replace *all* matching occurrences. -Include enough lines to make the SEARCH blocks uniquely match the lines to change. - -Keep *SEARCH/REPLACE* blocks concise. -Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file. -Include just the changing lines, and a few surrounding lines if needed for uniqueness. -Do not include long runs of unchanging lines in *SEARCH/REPLACE* blocks. - -Only create *SEARCH/REPLACE* blocks for files that the user has added to the chat! - -To move code within a file, use 2 *SEARCH/REPLACE* blocks: 1 to delete it from its current location, 1 to insert it in the new location. - -Pay attention to which filenames the user wants you to edit, especially if they are asking you to create a new file. - -If you want to put code in a new file, use a *SEARCH/REPLACE block* with: -- A new file path, including dir name if needed -- An empty `SEARCH` section -- The new file's contents in the `REPLACE` section - -To rename files which have been added to the chat, use shell commands at the end of your response. - -{lazy_prompt} -ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*! -{shell_cmd_reminder} -""" - - shell_cmd_reminder = """ -Examples of when to suggest shell commands: - -- If you changed a self-contained html file, suggest an OS-appropriate command to open a browser to view it to see the updated content. -- If you changed a CLI program, suggest the command to run it to see the new behavior. -- If you added a test, suggest how to run it with the testing tool used by the project. -- Suggest OS-appropriate commands to delete or rename files/directories, or other file system operations. -- If your code changes add new dependencies, suggest the command to install them. -- Etc. -""" + shell_cmd_prompt = "" + no_shell_cmd_prompt = "" + shell_cmd_reminder = "" diff --git a/aider/coders/junior_whole_prompts.py b/aider/coders/junior_whole_prompts.py index c124ad22f..90f03d100 100644 --- a/aider/coders/junior_whole_prompts.py +++ b/aider/coders/junior_whole_prompts.py @@ -1,55 +1,10 @@ # flake8: noqa: E501 -from .base_prompts import CoderPrompts +from .wholefile_prompts import WholeFilePrompts -class WholeFilePrompts(CoderPrompts): +class WholeFilePrompts(WholeFilePrompts): main_system = """Act as an expert software developer and make changes to source code. {lazy_prompt} Output a copy of each file that needs changes. """ - - example_messages = [ - dict( - role="user", - content="Change the greeting to be more casual", - ), - dict( - role="assistant", - content=""" -show_greeting.py -{fence[0]} -import sys - -def greeting(name): - print(f"Hey {{name}}") - -if __name__ == '__main__': - greeting(sys.argv[1]) -{fence[1]} -""", - ), - ] - - system_reminder = """You MUST return the entire content of the updated file. -You MUST use this *file listing* format: - -path/to/filename.js -{fence[0]} -// entire file content ... -// ... goes in between -{fence[1]} - -Every *file listing* MUST use this format: -- First line: the filename with any originally provided path -- Second line: opening {fence[0]} -- ... entire content of the file ... -- Final line: closing {fence[1]} - -*NEVER* skip, omit or elide content from a *file listing* using "..." or by adding comments like "... rest of code..."! -Create a new file you MUST return a *file listing* which includes an appropriate filename, including any appropriate path. - -{lazy_prompt} -""" - - redacted_edit_message = "No changes are needed." diff --git a/aider/coders/senior_coder.py b/aider/coders/senior_coder.py index a2fac48b7..d606d6bfc 100644 --- a/aider/coders/senior_coder.py +++ b/aider/coders/senior_coder.py @@ -1,7 +1,25 @@ +from aider.models import Model + from .ask_coder import AskCoder +from .base_coder import Coder from .senior_prompts import SeniorPrompts class SeniorCoder(AskCoder): edit_format = "senior" gpt_prompts = SeniorPrompts() + + def reply_completed(self): + content = self.partial_response_content + + kwargs = dict(self.original_kwargs) + kwargs["edit_format"] = self.main_model.junior_edit_format + kwargs["suggest_shell_commands"] = False + + junior_coder = Coder.create( + main_model=Model(self.main_model.junior_model_name), + io=self.io, + **kwargs, + ) + junior_coder.run(with_message=content, preproc=False) + self.move_back_cur_messages("I made those changes to the files.") diff --git a/aider/coders/senior_prompts.py b/aider/coders/senior_prompts.py index 91ee8a6af..ae64a30fa 100644 --- a/aider/coders/senior_prompts.py +++ b/aider/coders/senior_prompts.py @@ -3,12 +3,15 @@ from .base_prompts import CoderPrompts -class AskPrompts(CoderPrompts): +class SeniorPrompts(CoderPrompts): main_system = """Act as an expert senior engineer and provide direction to your junior engineer. Study the change request and the current code. Describe how to modify the code to complete the request. -Explain all needed code changes clearly and completely, but concisely. The junior engineer will rely solely on your instructions, so make them unambiguous and complete. +Explain all needed code changes clearly and completely, but concisely. +Just show the changes needed. + +DO NOT show the entire updated function/file/etc! Always reply in the same language as the change request. """ diff --git a/aider/models.py b/aider/models.py index cae1defbd..797017f74 100644 --- a/aider/models.py +++ b/aider/models.py @@ -81,6 +81,8 @@ class ModelSettings: use_system_prompt: bool = True use_temperature: bool = True streaming: bool = True + junior_model_name: Optional[str] = None + junior_edit_format: Optional[str] = None # https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo @@ -259,6 +261,8 @@ MODEL_SETTINGS = [ "claude-3-5-sonnet-20240620", "diff", weak_model_name="claude-3-haiku-20240307", + junior_model_name="claude-3-haiku-20240307", + junior_edit_format="whole", use_repo_map=True, examples_as_sys_msg=True, accepts_images=True,