diff --git a/.ropeproject/.gitkeep b/.ropeproject/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/aider/coders/architect_coder.py b/aider/coders/architect_coder.py index f3e2a38b1..b8ef4fc16 100644 --- a/aider/coders/architect_coder.py +++ b/aider/coders/architect_coder.py @@ -34,15 +34,162 @@ class ArchitectCoder(AskCoder): new_kwargs = dict(io=self.io, from_coder=self) new_kwargs.update(kwargs) - editor_coder = Coder.create(**new_kwargs) - editor_coder.cur_messages = [] - editor_coder.done_messages = [] + use_batch_editing = False # TODO: add this option to aider cli and to the configuration yaml, andread this setting from there - if self.verbose: - editor_coder.show_announcements() + if use_batch_editing: + # split the architect model response into chunks using natural delimiters (code blocka, newlines, separators, etc.) + chunks = [] + chunks = self.split_response_by_natural_delimiters(content) - editor_coder.run(with_message=content, preproc=False) + for chunk in chunks: + if not chunk.strip(): + continue + + # Create a new chat session with the editor coder llm model for each chunk of the architect model response + editor_coder = Coder.create(**new_kwargs) + editor_coder.cur_messages = [] + editor_coder.done_messages = [] + + if self.verbose: + editor_coder.show_announcements() + + editor_coder.run(with_message=chunk, preproc=False) + + self.move_back_cur_messages("I made those changes to the files.") + self.total_cost += editor_coder.total_cost + self.aider_commit_hashes += editor_coder.aider_commit_hashes + else: + # Create only one chat session with the editor coder llm model, not splitting the architect answer in chunks. + editor_coder = Coder.create(**new_kwargs) + editor_coder.cur_messages = [] + editor_coder.done_messages = [] + + if self.verbose: + editor_coder.show_announcements() + + # Run the editor coder with the entire architect model response + editor_coder.run(with_message=content, preproc=False) + + self.move_back_cur_messages("I made those changes to the files.") + self.total_cost += editor_coder.total_cost + self.aider_commit_hashes += editor_coder.aider_commit_hashes + + + def split_response_by_natural_delimiters(self, content): + """ + Splits the content into chunks using natural delimiters, with heuristics: + - Never splits inside code blocks (even nested/mixed fences). + - Detects repeated block patterns (title/tag, blank lines, filename, code block) and splits accordingly. + - Lone comments between blocks are included in both adjacent chunks. + - Groups filename fences with their following code block. + - Groups delimiters/tags with their following block, including blank lines. + - Falls back to delimiter/tag splitting if no repeated pattern is found. + """ + import re + + # Fence definitions + fence_openers = [ + r"```[\w-]*", r"~~~~[\w-]*", + r"", r"
", r"", r"", r"", r"", r""
+        ]
+        fence_closers = [
+            r"```", r"~~~~",
+            r"", r"
", r"", r"", r"", r"", r"" + ] + fence_opener_re = re.compile(rf"^({'|'.join(fence_openers)})\s*$", re.IGNORECASE) + fence_closer_re = re.compile(rf"^({'|'.join(fence_closers)})\s*$", re.IGNORECASE) + + # Patterns for tags/titles, filenames, comments, and delimiters + tag_pattern = re.compile( + r"""( + ^\[[A-Z0-9 _:\-./()]+\]$ | # [ALL CAPS/NUMERIC/UNDERSCORE/ETC] + ^<[\w\s:\-./()|=\[\]!]+>$ | # + ^<<[\w\s:\-./()|=\[\]!]+>>$ | # <> + ^<\|[\w\s:\-./()|=\[\]!]+\|>$ | # <|TAG ...|> + ^<=.*=>$ | # <=...=> + ^$ | # + ^<==\|.*\|==>$ # <==| ... |==> + )""", + re.MULTILINE | re.VERBOSE + ) + filename_pattern = re.compile(r"^[\w\./\\\-]+\.?\w*$") + comment_pattern = re.compile(r"^(#|