From 4c702a0ffb5ed2fcffb6b409852a4c56c7913cb0 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Tue, 10 Dec 2024 13:48:20 +0800 Subject: [PATCH 01/21] Implement MOACoder --- aider/args.py | 6 + aider/coders/__init__.py | 2 + aider/coders/base_coder.py | 15 ++ aider/coders/mixture_of_architects_coder.py | 244 ++++++++++++++++++++ aider/coders/mixture_prompts.py | 47 ++++ aider/main.py | 11 +- 6 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 aider/coders/mixture_of_architects_coder.py create mode 100644 aider/coders/mixture_prompts.py diff --git a/aider/args.py b/aider/args.py index 529ffffb8b6..8befee90fcc 100644 --- a/aider/args.py +++ b/aider/args.py @@ -179,6 +179,12 @@ def get_parser(default_config_files, git_root): default=[], ) group = parser.add_argument_group("Model settings") + group.add_argument( + "--moa", + metavar="MODEL", + nargs="+", + help="Use Mixture of Architects with multiple models", + ) group.add_argument( "--list-models", "--models", diff --git a/aider/coders/__init__.py b/aider/coders/__init__.py index e9d334bc99f..1d6b98e218d 100644 --- a/aider/coders/__init__.py +++ b/aider/coders/__init__.py @@ -6,6 +6,7 @@ from .editor_editblock_coder import EditorEditBlockCoder from .editor_whole_coder import EditorWholeFileCoder from .help_coder import HelpCoder +from .mixture_of_architects_coder import MixtureOfArchitectsCoder from .udiff_coder import UnifiedDiffCoder from .wholefile_coder import WholeFileCoder @@ -23,4 +24,5 @@ ArchitectCoder, EditorEditBlockCoder, EditorWholeFileCoder, + MixtureOfArchitectsCoder, ] diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index ab662aebd5a..44a175ec1ca 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -162,6 +162,15 @@ def create( kwargs = use_kwargs + # Check for mixture of architects case first + if "architect_models" in kwargs: + for coder in coders.__all__: + if coder.edit_format == "mixture": + res = coder(main_model, io, **kwargs) + res.original_kwargs = dict(kwargs) + return res + + # Normal case - find coder matching edit_format for coder in coders.__all__: if hasattr(coder, "edit_format") and coder.edit_format == edit_format: res = coder(main_model, io, **kwargs) @@ -210,6 +219,12 @@ def get_announcements(self): output = f"Weak model: {weak_model.name}" lines.append(output) + # Mixture of Architects info + if self.edit_format == "mixture" and hasattr(self, "architects"): + for arch in self.architects[1:]: # Skip alpha since it's already shown as main model + output = f"Architect {arch.name.upper()}: {arch.model.name}" + lines.append(output) + # Repo if self.repo: rel_repo_dir = self.repo.get_rel_repo_dir() diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py new file mode 100644 index 00000000000..18ed142c992 --- /dev/null +++ b/aider/coders/mixture_of_architects_coder.py @@ -0,0 +1,244 @@ +import re +from .base_coder import Coder +from .mixture_prompts import MixturePrompts +from .ask_coder import AskCoder + + +class ArchitectAgent: + def __init__(self, name, model): + self.name = name # NATO name (alpha, bravo, etc) + self.model = model + self.active = True + self.last_response: str | None = None + + +class MixtureOfArchitectsCoder(Coder): + edit_format = "mixture" + gpt_prompts = MixturePrompts() + + def __init__(self, main_model, io, architect_models=None, **kwargs): + super().__init__(main_model, io, **kwargs) + + # The main_model is always the first architect (alpha) + self.architects = [ArchitectAgent("alpha", main_model)] + + # Add additional architect models with NATO names + nato_names = ["bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel"] + if architect_models: + for i, amodel in enumerate(architect_models): + name = nato_names[i] if i < len(nato_names) else f"agent{i+2}" + self.architects.append(ArchitectAgent(name, amodel)) + + def get_architect_prompt(self, architect): + """Get the system prompt customized for this architect""" + prompt = self.gpt_prompts.main_system.format( + architect_name=architect.name.upper(), + language=self.chat_language or "the same language they are using", + ) + return prompt + + def get_architect_response(self, architect, context): + """Get response from individual architect with proper prompting""" + try: + # Add architect name to the context + full_context = f"You are architect {architect.name.upper()}.\n\n{context}" + + # Add other architects' previous responses to the context + other_responses = [] + for other_arch in self.architects: + if ( + other_arch != architect + and other_arch.active + and other_arch.last_response + ): + # Extract just the proposal content from the last response + proposal_match = re.search( + r"(.*?)", + other_arch.last_response, + re.DOTALL, + ) + if proposal_match: + proposal = proposal_match.group(1).strip() + other_responses.append( + f"\n{proposal}\n" + ) + + if other_responses: + full_context += "\nPrevious proposals from other architects:\n\n" + full_context += "\n".join(other_responses) + + # Create AskCoder with architect-specific system prompt + ask_coder = AskCoder.create( + main_model=architect.model, + io=self.io, + fnames=list(self.abs_fnames), + read_only_fnames=list(self.abs_read_only_fnames), + repo=self.repo, + map_tokens=self.repo_map.max_map_tokens if self.repo_map else 0, + summarize_from_coder=False, + stream=False, # Explicitly disable streaming + ) + ask_coder.auto_commit = self.auto_commits + + # Override AskCoder's prompts with MixturePrompts + ask_coder.gpt_prompts = MixturePrompts() + + # Run with empty message since we already set up the messages + response = ask_coder.run(with_message=full_context, preproc=False) + + # Add debug logging + if not response.strip(): + self.io.tool_warning(f"Warning: Empty response from {architect.name}") + + return architect, response + + except Exception as e: + self.io.tool_error( + f"Error getting response from {architect.name}: {str(e)}" + ) + return architect, f"Error: {str(e)}" + + def run_discussion_round(self, user_message): + try: + # Create context for all architects + base_context = f"User message: {user_message}\n\n" + + # Get active architects + active_architects = [arch for arch in self.architects if arch.active] + if not active_architects: + self.io.tool_error("No active architects remaining!") + return + + # Debug: Show which architects are active + self.io.tool_output( + f"Active architects: {[arch.name for arch in active_architects]}" + ) + + # Process architects sequentially instead of concurrently + all_responses = {} + for arch in active_architects: + self.io.tool_output(f"Waiting for {arch.name}'s response...") + try: + # Get response directly instead of using ThreadPoolExecutor + arch, response = self.get_architect_response(arch, base_context) + + if not response.strip(): + self.io.tool_warning(f"Empty response from {arch.name}") + continue + + arch.last_response = response + all_responses[arch.name] = response + self.io.tool_output( + f"Received {arch.name}'s response ({len(response)} chars)" + ) + except Exception as e: + self.io.tool_error( + f"Failed to get response from {arch.name}: {str(e)}" + ) + + # Show all architects' proposals after all are complete + for arch in active_architects: + if arch.last_response: + self.io.rule() + self.io.tool_output(f"{arch.name.upper()}'s proposal:", bold=True) + self.io.tool_output(f"\n{arch.last_response}\n") + + # Add final divider + self.io.rule() + finally: + self.io.tool_output("Discussion round complete.") + + def preproc_user_input(self, inp): + if not inp: + return + + # Check for special mixture commands first + words = inp.strip().split() + if words: + cmd = words[0].lower() + args = " ".join(words[1:]) + + if cmd in ["/drop", "/discuss", "/code"]: + cmd = cmd[1:] # strip the / + return self.handle_discussion_commands(cmd, args) + + # Fall back to normal command processing + return super().preproc_user_input(inp) + + def run_one(self, user_message, preproc): + self.init_before_message() + + if preproc: + message = self.preproc_user_input(user_message) + else: + message = user_message + + # If no special command was handled, treat as discussion by default + if message: + self.run_discussion_round(message) + + def handle_discussion_commands(self, cmd, args): + """ + Handle special mixture of architects commands: + /drop - Remove an architect from the discussion + /discuss - Start a new discussion round + /code - Move to implementation phase + """ + if cmd == "drop": + nato_name = args.strip().lower() + for arch in self.architects: + if arch.name == nato_name: + arch.active = False + self.io.tool_output(f"Dropped architect {nato_name}") + return + + elif cmd == "discuss": + self.run_discussion_round(args) + return + + elif cmd == "code": + self.run_coding_phase(args) + return + + return False + + def run_coding_phase(self, message): + # Combine active architects' responses + combined_response = f"User request: {message}\n\n" + combined_response += "Based on the discussion between architects:\n\n" + for arch in self.architects: + if arch.active and arch.last_response: + combined_response += ( + f"From {arch.name.upper()}:\n{arch.last_response}\n\n" + ) + + # Use editor coder like ArchitectCoder does + kwargs = dict() + editor_model = self.main_model.editor_model or self.main_model + kwargs["main_model"] = editor_model + kwargs["edit_format"] = self.main_model.editor_edit_format + kwargs["suggest_shell_commands"] = False + kwargs["map_tokens"] = 0 + kwargs["total_cost"] = self.total_cost + kwargs["cache_prompts"] = False + kwargs["num_cache_warming_pings"] = 0 + kwargs["summarize_from_coder"] = False + + new_kwargs = dict(io=self.io) + new_kwargs.update(kwargs) + + editor_coder = Coder.create(**new_kwargs) + editor_coder.auto_commit = self.auto_commits + editor_coder.cur_messages = [] + editor_coder.done_messages = [] + + if self.verbose: + editor_coder.show_announcements() + + editor_coder.run(with_message=combined_response, preproc=False) + + self.move_back_cur_messages( + "Changes have been applied based on architects' consensus." + ) + self.total_cost = editor_coder.total_cost + self.aider_commit_hashes = editor_coder.aider_commit_hashes diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py new file mode 100644 index 00000000000..d0d5451a437 --- /dev/null +++ b/aider/coders/mixture_prompts.py @@ -0,0 +1,47 @@ +from .architect_prompts import ArchitectPrompts + + +class MixturePrompts(ArchitectPrompts): + main_system = """Act as an expert architect engineer who is part of a team of architects. +You will collaborate with other architects to design solutions while also communicating with the user. +Study the change request and the current code. +Describe how to modify the code to complete the request. + +You will be told your architect name in the context of each request. +When you receive your name, use it to identify yourself in your responses. + +You are communicating with both the user and other architects: +- The user can see your entire message +- Other architects can only see what's inside your tags +- Put implementation details AND suggestions for other architects inside the tags +- You may address the user directly outside the tags + +Your response should be clear and complete, but concise. +Just show the changes needed. +DO NOT show the entire updated function/file/etc! + +Always reply to the user in {language}. + +Use XML tags to structure your response like this: + +Your detailed implementation proposal here... +Include any suggestions or responses to other architects here... + + +IMPORTANT: +- Only the content inside the tags will be visible to other architects +- The user will see your entire message, both inside and outside the tags +- Always put ALL implementation details inside the tags +""" + + # Keep other prompts from ArchitectPrompts + files_content_prefix = ArchitectPrompts.files_content_prefix + files_content_assistant_reply = ArchitectPrompts.files_content_assistant_reply + files_no_full_files = ArchitectPrompts.files_no_full_files + files_no_full_files_with_repo_map = ( + ArchitectPrompts.files_no_full_files_with_repo_map + ) + files_no_full_files_with_repo_map_reply = ( + ArchitectPrompts.files_no_full_files_with_repo_map_reply + ) + repo_content_prefix = ArchitectPrompts.repo_content_prefix diff --git a/aider/main.py b/aider/main.py index 51377d0feca..5dd7eb5ba30 100644 --- a/aider/main.py +++ b/aider/main.py @@ -806,7 +806,7 @@ def get_io(pretty): args.stream = False try: - coder = Coder.create( + coder_kwargs = dict( main_model=main_model, edit_format=args.edit_format, io=io, @@ -838,6 +838,15 @@ def get_io(pretty): detect_urls=args.detect_urls, auto_copy_context=args.copy_paste, ) + + if args.moa: + # Parse space-separated model names and convert to Model objects + architect_model_names = args.moa + architect_models = [models.Model(m) for m in architect_model_names] + coder_kwargs["architect_models"] = architect_models + coder = Coder.create(**coder_kwargs) + else: + coder = Coder.create(**coder_kwargs) except UnknownEditFormat as err: io.tool_error(str(err)) io.offer_url(urls.edit_formats, "Open documentation about edit formats?") From a0ab8c95cc45ec973accf61bd611e5ebdeadea25 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Sat, 14 Dec 2024 01:32:26 +0800 Subject: [PATCH 02/21] Add fix keeping discussion across discussions --- aider/coders/mixture_of_architects_coder.py | 168 +++++++++++++++----- 1 file changed, 130 insertions(+), 38 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 18ed142c992..bacd72b4228 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -19,6 +19,9 @@ class MixtureOfArchitectsCoder(Coder): def __init__(self, main_model, io, architect_models=None, **kwargs): super().__init__(main_model, io, **kwargs) + # Add conversation history tracking + self.discussion_messages = [] # List to store the full conversation + # The main_model is always the first architect (alpha) self.architects = [ArchitectAgent("alpha", main_model)] @@ -37,37 +40,102 @@ def get_architect_prompt(self, architect): ) return prompt - def get_architect_response(self, architect, context): + def get_architect_response(self, architect, current_user_message): """Get response from individual architect with proper prompting""" try: - # Add architect name to the context - full_context = f"You are architect {architect.name.upper()}.\n\n{context}" - - # Add other architects' previous responses to the context - other_responses = [] + # Build architect-specific conversation history + full_context = f"You are architect {architect.name.upper()}.\n\n" + + # Group messages by conversation round + rounds = [] + current_round = [] + + for msg in self.discussion_messages: + if msg["role"] == "user": + if current_round: + rounds.append(current_round) + current_round = [msg] + else: + current_round.append(msg) + + if current_round: + rounds.append(current_round) + + # Build context specific to this architect + for round_msgs in rounds: + user_msg = next(msg for msg in round_msgs if msg["role"] == "user") + full_context += "\n" + full_context += user_msg["content"] + + # For each user message, add the other architects' proposals + other_proposals = [] + for msg in round_msgs: + if msg["role"] == "assistant": + if msg["name"] == architect.name.upper(): + # Add this architect's own response + full_context += "\n\nYour previous response:\n" + full_context += msg["content"] + elif architect.name == "alpha": # Alpha sees all proposals + proposal_match = re.search( + r"(.*?)", + msg["content"], + re.DOTALL, + ) + if proposal_match: + other_proposals.append( + f"\nProposal from {msg['name']}:\n{proposal_match.group(1).strip()}" + ) + elif ( + msg["name"] == "ALPHA" + ): # Other architects only see Alpha's proposals + proposal_match = re.search( + r"(.*?)", + msg["content"], + re.DOTALL, + ) + if proposal_match: + other_proposals.append( + f"\nProposal from ALPHA:\n{proposal_match.group(1).strip()}" + ) + + if other_proposals: + full_context += "".join(other_proposals) + full_context += "\n\n\n" + + # Add current context + full_context += f"Current user request:\n{current_user_message}\n" + + # Add other architects' current proposals if they exist + current_proposals = [] for other_arch in self.architects: if ( other_arch != architect and other_arch.active and other_arch.last_response - ): - # Extract just the proposal content from the last response + and (architect.name == "alpha" or other_arch.name == "alpha") + ): # Only show Alpha to others + proposal_match = re.search( r"(.*?)", other_arch.last_response, re.DOTALL, ) if proposal_match: - proposal = proposal_match.group(1).strip() - other_responses.append( - f"\n{proposal}\n" + current_proposals.append( + f"\nCurrent proposal from {other_arch.name.upper()}:\n{proposal_match.group(1).strip()}" ) - if other_responses: - full_context += "\nPrevious proposals from other architects:\n\n" - full_context += "\n".join(other_responses) + if current_proposals: + full_context += "".join(current_proposals) + + # Debug output if verbose + if self.verbose: + self.io.tool_output(f"\nDebug: Context being sent to {architect.name}:") + self.io.tool_output("-" * 40) + self.io.tool_output(full_context) + self.io.tool_output("-" * 40 + "\n") - # Create AskCoder with architect-specific system prompt + # Create and configure AskCoder ask_coder = AskCoder.create( main_model=architect.model, io=self.io, @@ -76,17 +144,13 @@ def get_architect_response(self, architect, context): repo=self.repo, map_tokens=self.repo_map.max_map_tokens if self.repo_map else 0, summarize_from_coder=False, - stream=False, # Explicitly disable streaming + stream=True, ) - ask_coder.auto_commit = self.auto_commits - - # Override AskCoder's prompts with MixturePrompts + ask_coder.auto_commits = self.auto_commits ask_coder.gpt_prompts = MixturePrompts() - # Run with empty message since we already set up the messages response = ask_coder.run(with_message=full_context, preproc=False) - # Add debug logging if not response.strip(): self.io.tool_warning(f"Warning: Empty response from {architect.name}") @@ -100,8 +164,8 @@ def get_architect_response(self, architect, context): def run_discussion_round(self, user_message): try: - # Create context for all architects - base_context = f"User message: {user_message}\n\n" + # Store user message + self.discussion_messages.append({"role": "user", "content": user_message}) # Get active architects active_architects = [arch for arch in self.architects if arch.active] @@ -115,19 +179,25 @@ def run_discussion_round(self, user_message): ) # Process architects sequentially instead of concurrently - all_responses = {} for arch in active_architects: self.io.tool_output(f"Waiting for {arch.name}'s response...") try: - # Get response directly instead of using ThreadPoolExecutor - arch, response = self.get_architect_response(arch, base_context) + arch, response = self.get_architect_response(arch, user_message) if not response.strip(): self.io.tool_warning(f"Empty response from {arch.name}") continue arch.last_response = response - all_responses[arch.name] = response + # Store architect's response in discussion history + self.discussion_messages.append( + { + "role": "assistant", + "name": arch.name.upper(), + "content": response, + } + ) + self.io.tool_output( f"Received {arch.name}'s response ({len(response)} chars)" ) @@ -136,8 +206,7 @@ def run_discussion_round(self, user_message): f"Failed to get response from {arch.name}: {str(e)}" ) - # Show all architects' proposals after all are complete - for arch in active_architects: + # Show architect's proposal immediately if arch.last_response: self.io.rule() self.io.tool_output(f"{arch.name.upper()}'s proposal:", bold=True) @@ -203,14 +272,37 @@ def handle_discussion_commands(self, cmd, args): return False def run_coding_phase(self, message): - # Combine active architects' responses - combined_response = f"User request: {message}\n\n" - combined_response += "Based on the discussion between architects:\n\n" - for arch in self.architects: - if arch.active and arch.last_response: - combined_response += ( - f"From {arch.name.upper()}:\n{arch.last_response}\n\n" - ) + # Add the final code implementation request to the discussion + if message.strip(): + self.discussion_messages.append( + { + "role": "user", + "content": f"Please implement the following: {message}", + } + ) + + # Format the full conversation history with XML fences + combined_response = "Full discussion history:\n\n" + for msg in self.discussion_messages: + if msg["role"] == "user": + combined_response += "\n" + combined_response += msg["content"] + combined_response += "\n\n\n" + else: + combined_response += f"\n" + combined_response += msg["content"] + combined_response += f"\n\n\n" + + combined_response += ( + "\nBased on the above discussion, please implement the requested changes." + ) + + # Debug print the combined response + if self.verbose: + self.io.tool_output("\nDebug: Combined response being sent to editor:") + self.io.tool_output("-" * 40) + self.io.tool_output(combined_response) + self.io.tool_output("-" * 40 + "\n") # Use editor coder like ArchitectCoder does kwargs = dict() @@ -228,7 +320,7 @@ def run_coding_phase(self, message): new_kwargs.update(kwargs) editor_coder = Coder.create(**new_kwargs) - editor_coder.auto_commit = self.auto_commits + editor_coder.auto_commits = self.auto_commits editor_coder.cur_messages = [] editor_coder.done_messages = [] From fe86266a2c58975a85c093c7bc6a1977350f8214 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Sat, 14 Dec 2024 02:51:46 +0800 Subject: [PATCH 03/21] Fix content orchestration for discussion in MOA --- aider/coders/mixture_of_architects_coder.py | 72 ++++---------- docs/MOA.md | 103 ++++++++++++++++++++ 2 files changed, 122 insertions(+), 53 deletions(-) create mode 100644 docs/MOA.md diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index bacd72b4228..2ea234a56e3 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -61,72 +61,38 @@ def get_architect_response(self, architect, current_user_message): if current_round: rounds.append(current_round) - # Build context specific to this architect + # Build context from rounds for round_msgs in rounds: user_msg = next(msg for msg in round_msgs if msg["role"] == "user") full_context += "\n" full_context += user_msg["content"] + full_context += "\n\n\n" - # For each user message, add the other architects' proposals - other_proposals = [] + # Add architects' responses/proposals for msg in round_msgs: if msg["role"] == "assistant": if msg["name"] == architect.name.upper(): - # Add this architect's own response - full_context += "\n\nYour previous response:\n" + # Include full response for the current architect + full_context += f"\n" full_context += msg["content"] - elif architect.name == "alpha": # Alpha sees all proposals + full_context += "\n\n\n" + else: + # Only include proposal content from other architects + content = msg["content"] proposal_match = re.search( - r"(.*?)", - msg["content"], - re.DOTALL, + r"(.*?)", content, re.DOTALL ) if proposal_match: - other_proposals.append( - f"\nProposal from {msg['name']}:\n{proposal_match.group(1).strip()}" - ) - elif ( - msg["name"] == "ALPHA" - ): # Other architects only see Alpha's proposals - proposal_match = re.search( - r"(.*?)", - msg["content"], - re.DOTALL, - ) - if proposal_match: - other_proposals.append( - f"\nProposal from ALPHA:\n{proposal_match.group(1).strip()}" - ) + proposal_content = proposal_match.group(1).strip() + full_context += f"\n" + full_context += proposal_content + full_context += "\n\n\n" - if other_proposals: - full_context += "".join(other_proposals) - full_context += "\n\n\n" - - # Add current context - full_context += f"Current user request:\n{current_user_message}\n" - - # Add other architects' current proposals if they exist - current_proposals = [] - for other_arch in self.architects: - if ( - other_arch != architect - and other_arch.active - and other_arch.last_response - and (architect.name == "alpha" or other_arch.name == "alpha") - ): # Only show Alpha to others - - proposal_match = re.search( - r"(.*?)", - other_arch.last_response, - re.DOTALL, - ) - if proposal_match: - current_proposals.append( - f"\nCurrent proposal from {other_arch.name.upper()}:\n{proposal_match.group(1).strip()}" - ) - - if current_proposals: - full_context += "".join(current_proposals) + # Only add current message if it's not already the last user message + if not rounds or rounds[-1][0]["content"] != current_user_message: + full_context += "\n" + full_context += current_user_message + full_context += "\n\n" # Debug output if verbose if self.verbose: diff --git a/docs/MOA.md b/docs/MOA.md new file mode 100644 index 00000000000..98ca95fa6b4 --- /dev/null +++ b/docs/MOA.md @@ -0,0 +1,103 @@ +# Mixture of Architects (MOA) + +## Overview + +The Mixture of Architects (MOA) is a collaborative AI architecture where multiple LLM "architects" work together to solve programming tasks. Each architect maintains its own conversation thread while being able to see and respond to other architects' proposals. + +## Core Concepts + +### Architects +- Multiple architects (LLMs) participate in the discussion +- Each architect is identified by a NATO phonetic name (alpha, bravo, charlie, etc.) +- The first architect (alpha) is always the main model +- Each architect sees all other architects' proposals, enabling true collaborative discussion + +### Discussion Flow + +The discussion proceeds in rounds, with each round following this pattern: + +1. User submits a query/request +2. Architects respond sequentially: + - Each architect sees: + - Original user query + - All previous architects' proposals (XML fenced) + - Each architect provides: + - Their analysis/instructions + - Their own proposal (in XML fence) + - Can reference, support, critique or object to other architects' proposals + +For example, in a 3-architect system: + +#### Alpha's View +``` +User Query 1 +└── Alpha Response + Proposal + └── Bravo's Proposal + └── Charlie's Proposal + └── User Query 2 + └── Alpha Response + Proposal + └── Bravo's Proposal + └── Charlie's Proposal +``` + +#### Bravo's View +``` +User Query 1 + Alpha's Proposal +└── Bravo Response + Proposal + └── Charlie's Proposal + └── User Query 2 + Alpha's Proposal + └── Bravo Response + Proposal + └── Charlie's Proposal +``` + +#### Charlie's View +``` +User Query 1 + Alpha's Proposal + Bravo's Proposal +└── Charlie Response + Proposal + └── User Query 2 + Alpha's Proposal + Bravo's Proposal + └── Charlie Response + Proposal +``` + +### Commands + +Users can interact with MOA using three main commands: + +1. `/discuss ` (or just type normally) - Start/continue a discussion round +2. `/code ` - Move to implementation phase +3. `/drop ` - Remove an architect from the discussion + +### Implementation Phase + +When moving to implementation (`/code`), the entire discussion history is compiled chronologically with full context: + +``` +User Query 1 +└── Alpha Full Response (analysis + proposal) + └── Bravo Full Response (analysis + proposal) + └── Charlie Full Response (analysis + proposal) + └── User Query 2 + └── Alpha Full Response (analysis + proposal) + └── Bravo Full Response (analysis + proposal) + └── Charlie Full Response (analysis + proposal) +``` + +The complete history, including all analyses and proposals, is passed to the editor coder. The editor coder then decides how to implement the changes based on: +- The full discussion history +- The final user message +- Their own analysis of the proposals + +## XML Fencing + +The system uses XML fencing to maintain clear boundaries: + +- `` - Contains user queries +- `` - Contains an architect's specific proposal +- `` - Contains full architect responses + +## Collaborative Design + +Key aspects of MOA: +- All architects see all proposals +- Architects can directly reference and critique each others' proposals +- No formal consensus mechanism - the editor coder makes implementation decisions +- The user guides the final implementation through their `/code` message From b4741274cc07caf7d1cbc4d6e84b872b0d594326 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Sat, 14 Dec 2024 03:09:51 +0800 Subject: [PATCH 04/21] Add drop and clear for moa --- aider/coders/mixture_of_architects_coder.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 2ea234a56e3..717efa359a5 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -193,7 +193,7 @@ def preproc_user_input(self, inp): cmd = words[0].lower() args = " ".join(words[1:]) - if cmd in ["/drop", "/discuss", "/code"]: + if cmd in ["/drop", "/discuss", "/code", "/clear", "/reset"]: cmd = cmd[1:] # strip the / return self.handle_discussion_commands(cmd, args) @@ -218,8 +218,22 @@ def handle_discussion_commands(self, cmd, args): /drop - Remove an architect from the discussion /discuss - Start a new discussion round /code - Move to implementation phase + /clear - Clear chat and discussion history + /reset - Drop files and clear all history """ - if cmd == "drop": + if cmd == "clear": + self.discussion_messages = [] + self.io.tool_output("Chat history and discussion history cleared.") + return + elif cmd == "reset": + self.abs_fnames = set() + self.abs_read_only_fnames = set() + self.discussion_messages = [] + self.io.tool_output( + "All files dropped, chat history and discussion history cleared." + ) + return + elif cmd == "drop": nato_name = args.strip().lower() for arch in self.architects: if arch.name == nato_name: From c792c6fc78859591cf8aae7b944cb9779535d964 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Sat, 14 Dec 2024 04:01:51 +0800 Subject: [PATCH 05/21] Restructure for chat interface instead of single user sending --- aider/coders/mixture_of_architects_coder.py | 121 +++++++++++--------- 1 file changed, 67 insertions(+), 54 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 717efa359a5..f88202ac940 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -43,13 +43,23 @@ def get_architect_prompt(self, architect): def get_architect_response(self, architect, current_user_message): """Get response from individual architect with proper prompting""" try: - # Build architect-specific conversation history - full_context = f"You are architect {architect.name.upper()}.\n\n" + # Create and configure AskCoder + ask_coder = AskCoder.create( + main_model=architect.model, + io=self.io, + fnames=list(self.abs_fnames), + read_only_fnames=list(self.abs_read_only_fnames), + repo=self.repo, + map_tokens=self.repo_map.max_map_tokens if self.repo_map else 0, + summarize_from_coder=False, + stream=True, + ) + ask_coder.auto_commits = self.auto_commits + ask_coder.gpt_prompts = MixturePrompts() # Group messages by conversation round rounds = [] current_round = [] - for msg in self.discussion_messages: if msg["role"] == "user": if current_round: @@ -57,65 +67,66 @@ def get_architect_response(self, architect, current_user_message): current_round = [msg] else: current_round.append(msg) - if current_round: rounds.append(current_round) - # Build context from rounds + # Build the conversation messages for round_msgs in rounds: user_msg = next(msg for msg in round_msgs if msg["role"] == "user") - full_context += "\n" - full_context += user_msg["content"] - full_context += "\n\n\n" - # Add architects' responses/proposals + # Combine user message with other architects' proposals + user_content = "\n" + user_content += user_msg["content"] + user_content += "\n\n\n" + + # Add other architects' proposals from this round + for msg in round_msgs: + if ( + msg["role"] == "assistant" + and msg["name"] != architect.name.upper() + ): + content = msg["content"] + proposal_match = re.search( + r"(.*?)", content, re.DOTALL + ) + if proposal_match: + proposal_content = proposal_match.group(1).strip() + user_content += f"\n" + user_content += proposal_content + user_content += "\n\n\n" + + ask_coder.cur_messages.append({"role": "user", "content": user_content}) + + # Add this architect's own response if they had one for msg in round_msgs: - if msg["role"] == "assistant": - if msg["name"] == architect.name.upper(): - # Include full response for the current architect - full_context += f"\n" - full_context += msg["content"] - full_context += "\n\n\n" - else: - # Only include proposal content from other architects - content = msg["content"] - proposal_match = re.search( - r"(.*?)", content, re.DOTALL - ) - if proposal_match: - proposal_content = proposal_match.group(1).strip() - full_context += f"\n" - full_context += proposal_content - full_context += "\n\n\n" - - # Only add current message if it's not already the last user message - if not rounds or rounds[-1][0]["content"] != current_user_message: - full_context += "\n" - full_context += current_user_message - full_context += "\n\n" + if ( + msg["role"] == "assistant" + and msg["name"] == architect.name.upper() + ): + ask_coder.cur_messages.append( + {"role": "assistant", "content": msg["content"]} + ) # Debug output if verbose if self.verbose: - self.io.tool_output(f"\nDebug: Context being sent to {architect.name}:") - self.io.tool_output("-" * 40) - self.io.tool_output(full_context) - self.io.tool_output("-" * 40 + "\n") - # Create and configure AskCoder - ask_coder = AskCoder.create( - main_model=architect.model, - io=self.io, - fnames=list(self.abs_fnames), - read_only_fnames=list(self.abs_read_only_fnames), - repo=self.repo, - map_tokens=self.repo_map.max_map_tokens if self.repo_map else 0, - summarize_from_coder=False, - stream=True, - ) - ask_coder.auto_commits = self.auto_commits - ask_coder.gpt_prompts = MixturePrompts() - - response = ask_coder.run(with_message=full_context, preproc=False) + self.io.rule() + self.io.tool_output( + f"\nDebug: Messages being sent to {architect.name}:", bold=True + ) + self.io.tool_output("-" * 40) + for msg in ask_coder.cur_messages: + self.io.tool_output(f"{msg['role'].upper()}:") + self.io.tool_output(msg["content"]) + self.io.tool_output("-" * 40) + + # Pass the current message with XML tags as with_message + formatted_message = f""" + You are arhitect {architect.name} + + {current_user_message} + """ + response = ask_coder.run(with_message=formatted_message, preproc=False) if not response.strip(): self.io.tool_warning(f"Warning: Empty response from {architect.name}") @@ -140,13 +151,15 @@ def run_discussion_round(self, user_message): return # Debug: Show which architects are active + self.io.rule() self.io.tool_output( f"Active architects: {[arch.name for arch in active_architects]}" ) # Process architects sequentially instead of concurrently for arch in active_architects: - self.io.tool_output(f"Waiting for {arch.name}'s response...") + self.io.tool_output(f"Waiting for {arch.name}'s response...", bold=True) + self.io.rule() try: arch, response = self.get_architect_response(arch, user_message) @@ -173,9 +186,9 @@ def run_discussion_round(self, user_message): ) # Show architect's proposal immediately - if arch.last_response: + if self.verbose and arch.last_response: self.io.rule() - self.io.tool_output(f"{arch.name.upper()}'s proposal:", bold=True) + self.io.tool_output(f"{arch.name.upper()}'s Response:", bold=True) self.io.tool_output(f"\n{arch.last_response}\n") # Add final divider From 9eb5cfc44b6ac5b7f11a0c67f4f358e0953f45e7 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Mon, 16 Dec 2024 00:52:58 +0800 Subject: [PATCH 06/21] Modify moa prompt for better collaboration --- aider/coders/mixture_prompts.py | 59 ++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index d0d5451a437..039ca2a27fa 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -2,37 +2,50 @@ class MixturePrompts(ArchitectPrompts): - main_system = """Act as an expert architect engineer who is part of a team of architects. -You will collaborate with other architects to design solutions while also communicating with the user. -Study the change request and the current code. -Describe how to modify the code to complete the request. + main_system = """ You are an AI architect, part of a team collaborating to design software solutions. Your role is to analyze, enhance, and build upon the ideas of your fellow architects while addressing the user's needs. + Your name will be provided by the user -You will be told your architect name in the context of each request. -When you receive your name, use it to identify yourself in your responses. +Please respond to the user in the following language: {language} -You are communicating with both the user and other architects: -- The user can see your entire message -- Other architects can only see what's inside your tags -- Put implementation details AND suggestions for other architects inside the tags -- You may address the user directly outside the tags +When formulating your response, follow these steps: -Your response should be clear and complete, but concise. -Just show the changes needed. -DO NOT show the entire updated function/file/etc! +1. Review the user's query and any previous architects' proposals carefully. -Always reply to the user in {language}. +2. Wrap your analysis in tags: + + +- Summarize the user's requirements and constraints +- Evaluate the strengths and weaknesses of previous proposals (if any) +- Identify areas for improvement or expansion +- Brainstorm multiple potential solutions (at least 3) +- Evaluate each potential solution against the user's requirements +- Select the best solution and justify your choice +- Plan your enhancements or revisions in detail + + +3. Formulate your proposal using the following structure: -Use XML tags to structure your response like this: -Your detailed implementation proposal here... -Include any suggestions or responses to other architects here... + +[If you're not the first architect, explain your changes or additions to the previous proposal here. Be specific about what you're modifying and why.] + + +[Your detailed implementation proposal goes here. Include code snippets, architectural decisions, and explanations as needed.] + +[Address any open questions or suggestions for other architects here.] -IMPORTANT: -- Only the content inside the tags will be visible to other architects -- The user will see your entire message, both inside and outside the tags -- Always put ALL implementation details inside the tags -""" +4. Outside the tags, you may address the user directly with any clarifying questions or additional information. + +Remember: +- Only the content inside the tags will be visible to other architects. +- The user will see your entire message, both inside and outside the tags. +- Always include ALL implementation details inside the tags. +- Focus on enhancing and refining existing ideas rather than creating entirely new solutions unless absolutely necessary. +- Clearly explain the rationale behind your changes or additions. +- Be concise but thorough in your explanations. +- Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. + """ # Keep other prompts from ArchitectPrompts files_content_prefix = ArchitectPrompts.files_content_prefix From b3ecd6fba03e0b28c8145b7aad3e55284d9a23a8 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Mon, 16 Dec 2024 21:52:35 +0800 Subject: [PATCH 07/21] Modify moa prompt to build on previous solutions instead of creating new ones --- aider/coders/mixture_prompts.py | 58 +++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index 039ca2a27fa..b7c3e15523f 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -2,37 +2,40 @@ class MixturePrompts(ArchitectPrompts): - main_system = """ You are an AI architect, part of a team collaborating to design software solutions. Your role is to analyze, enhance, and build upon the ideas of your fellow architects while addressing the user's needs. - Your name will be provided by the user + main_system = """You are an AI architect, part of a team collaborating to design software solutions. Your role is to analyze, enhance, and build upon the ideas of your fellow architects while addressing the user's needs. Your name will be provided by the user. -Please respond to the user in the following language: {language} +Please respond to the user in the following language: + +{language} + When formulating your response, follow these steps: -1. Review the user's query and any previous architects' proposals carefully. +1. Carefully review the user's query and any previous architects' proposals. -2. Wrap your analysis in tags: +2. Conduct a thorough analysis and wrap it inside tags: - -- Summarize the user's requirements and constraints +- List out all of the user's requirements and constraints explicitly - Evaluate the strengths and weaknesses of previous proposals (if any) -- Identify areas for improvement or expansion -- Brainstorm multiple potential solutions (at least 3) -- Evaluate each potential solution against the user's requirements -- Select the best solution and justify your choice -- Plan your enhancements or revisions in detail - +- Identify specific areas for improvement or expansion in the existing proposals +- Brainstorm multiple potential solutions (at least 3) that build upon the previous proposals +- For each potential solution: + * Describe the solution in detail + * Evaluate how well it meets each of the user's requirements + * Consider potential challenges or trade-offs +- Select the best solution, justifying your choice based on how it improves upon previous proposals and addresses challenges +- Plan your enhancements or revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions 3. Formulate your proposal using the following structure: -[If you're not the first architect, explain your changes or additions to the previous proposal here. Be specific about what you're modifying and why.] +[Explain your changes or additions to the previous proposal here. Be specific about what you're modifying and why. Focus on how your changes improve upon and refine the existing solution.] -[Your detailed implementation proposal goes here. Include code snippets, architectural decisions, and explanations as needed.] +[Your detailed implementation proposal goes here. Include code snippets, architectural decisions, and explanations as needed. Ensure that your proposal builds upon and enhances the previous architects' work.] -[Address any open questions or suggestions for other architects here.] +[Address any open questions or suggestions for other architects here, encouraging further collaboration and refinement.] 4. Outside the tags, you may address the user directly with any clarifying questions or additional information. @@ -42,10 +45,29 @@ class MixturePrompts(ArchitectPrompts): - The user will see your entire message, both inside and outside the tags. - Always include ALL implementation details inside the tags. - Focus on enhancing and refining existing ideas rather than creating entirely new solutions unless absolutely necessary. -- Clearly explain the rationale behind your changes or additions. +- Clearly explain the rationale behind your changes or additions, emphasizing how they improve upon previous proposals. - Be concise but thorough in your explanations. - Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. - """ +- Actively collaborate with other architects by referencing and improving upon their specific ideas and suggestions. + +Example output structure (generic, without specific content): + + +[Thorough analysis of the problem and previous proposals] + + + + +[Specific changes and improvements to previous proposals] + + +[Detailed implementation proposal that builds upon and refines existing ideas] + +[Open questions and suggestions for further collaboration] + + +[Any direct communication with the user, if necessary] +""" # Keep other prompts from ArchitectPrompts files_content_prefix = ArchitectPrompts.files_content_prefix From f05e0210020fb8d6eadb5b4d499ffc8edac99d0f Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Wed, 18 Dec 2024 16:58:56 +0800 Subject: [PATCH 08/21] Modify moa prompt for cleaner solution building --- aider/coders/mixture_prompts.py | 7 ++-- crypto_square.py | 0 house.py | 36 ++++++++++++++++++++ pascals_triangle.py | 0 satellite.py | 59 +++++++++++++++++++++++++++++++++ word_count.py | 0 6 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 crypto_square.py create mode 100644 house.py create mode 100644 pascals_triangle.py create mode 100644 satellite.py create mode 100644 word_count.py diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index b7c3e15523f..19c6f188870 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -18,13 +18,13 @@ class MixturePrompts(ArchitectPrompts): - List out all of the user's requirements and constraints explicitly - Evaluate the strengths and weaknesses of previous proposals (if any) - Identify specific areas for improvement or expansion in the existing proposals -- Brainstorm multiple potential solutions (at least 3) that build upon the previous proposals -- For each potential solution: +- Brainstorm a potential solution that builds upon the previous proposals +- For your potential solution: * Describe the solution in detail * Evaluate how well it meets each of the user's requirements * Consider potential challenges or trade-offs -- Select the best solution, justifying your choice based on how it improves upon previous proposals and addresses challenges - Plan your enhancements or revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions +- Make sure your proposal aligns with the user's requirements and does not go beyond the scope of the query 3. Formulate your proposal using the following structure: @@ -47,6 +47,7 @@ class MixturePrompts(ArchitectPrompts): - Focus on enhancing and refining existing ideas rather than creating entirely new solutions unless absolutely necessary. - Clearly explain the rationale behind your changes or additions, emphasizing how they improve upon previous proposals. - Be concise but thorough in your explanations. +- For code revisions and enhancements, be brief by only showing the changes needed. - Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. - Actively collaborate with other architects by referencing and improving upon their specific ideas and suggestions. diff --git a/crypto_square.py b/crypto_square.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/house.py b/house.py new file mode 100644 index 00000000000..e9b4af12756 --- /dev/null +++ b/house.py @@ -0,0 +1,36 @@ +def recite(start_verse, end_verse): + # Define the components of the rhyme + subjects = [ + ("house that Jack built", ""), + ("malt", "lay in"), + ("rat", "ate"), + ("cat", "killed"), + ("dog", "worried"), + ("cow with the crumpled horn", "tossed"), + ("maiden all forlorn", "milked"), + ("man all tattered and torn", "kissed"), + ("priest all shaven and shorn", "married"), + ("rooster that crowed in the morn", "woke"), + ("farmer sowing his corn", "kept"), + ("horse and the hound and the horn", "belonged to") + ] + + def build_verse(verse_num): + """Recursively builds a single verse""" + if verse_num == 0: + return "the " + subjects[0][0] + "." + + current_subject, current_action = subjects[verse_num] + return f"the {current_subject}\nthat {current_action} " + build_verse(verse_num - 1) + + def create_full_verse(verse_num): + """Creates a complete verse with the 'This is' prefix""" + return "This is " + build_verse(verse_num) + + # Input validation + if not (1 <= start_verse <= end_verse <= len(subjects)): + raise ValueError("Invalid verse numbers") + + # Generate requested verses + verses = [create_full_verse(i - 1) for i in range(start_verse, end_verse + 1)] + return "\n\n".join(verses) diff --git a/pascals_triangle.py b/pascals_triangle.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/satellite.py b/satellite.py new file mode 100644 index 00000000000..1e3ac328d07 --- /dev/null +++ b/satellite.py @@ -0,0 +1,59 @@ +class TreeNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + +def validate_traversals(preorder, inorder): + """Validate the traversal inputs.""" + if len(preorder) != len(inorder): + raise ValueError("traversals must have the same length") + + # Convert to sets once for both uniqueness and equality checks + preorder_set = set(preorder) + inorder_set = set(inorder) + + if len(preorder) != len(preorder_set): + raise ValueError("traversals must contain unique items") + + if preorder_set != inorder_set: + raise ValueError("traversals must have the same elements") + +def build_tree_helper(preorder, inorder, pre_start, pre_end, in_start, in_end): + """Helper function that builds tree using index ranges instead of slicing.""" + if pre_start > pre_end or in_start > in_end: + return None + + # Root is always the first element of preorder section + root = TreeNode(preorder[pre_start]) + + # Find root in inorder traversal + root_idx = inorder.index(preorder[pre_start]) + left_size = root_idx - in_start + + # Recursively build left and right subtrees + root.left = build_tree_helper( + preorder, inorder, + pre_start + 1, pre_start + left_size, + in_start, root_idx - 1 + ) + + root.right = build_tree_helper( + preorder, inorder, + pre_start + left_size + 1, pre_end, + root_idx + 1, in_end + ) + + return root + +def tree_from_traversals(preorder, inorder): + """Reconstruct binary tree from its preorder and inorder traversals.""" + # Validate inputs first + validate_traversals(preorder, inorder) + + # Build the tree using index ranges + return build_tree_helper( + preorder, inorder, + 0, len(preorder) - 1, + 0, len(inorder) - 1 + ) diff --git a/word_count.py b/word_count.py new file mode 100644 index 00000000000..e69de29bb2d From 49eb1d2bcf9e9dcf84cb2573c6f50b233122d9c2 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Wed, 18 Dec 2024 16:59:41 +0800 Subject: [PATCH 09/21] Fix moa committing issues, and integrate with benchmark harness --- aider/coders/mixture_of_architects_coder.py | 11 +++++++- benchmark/benchmark.py | 31 +++++++++++++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index f88202ac940..cb3c31580be 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -52,7 +52,7 @@ def get_architect_response(self, architect, current_user_message): repo=self.repo, map_tokens=self.repo_map.max_map_tokens if self.repo_map else 0, summarize_from_coder=False, - stream=True, + stream=self.stream, ) ask_coder.auto_commits = self.auto_commits ask_coder.gpt_prompts = MixturePrompts() @@ -195,6 +195,10 @@ def run_discussion_round(self, user_message): self.io.rule() finally: self.io.tool_output("Discussion round complete.") + # Yes is proxy for auto running code, As proxy for benchmarking + # TODO: Replace with a better testing strategy + if self.io.yes: + self.run_coding_phase(user_message) def preproc_user_input(self, inp): if not inp: @@ -308,14 +312,19 @@ def run_coding_phase(self, message): kwargs["cache_prompts"] = False kwargs["num_cache_warming_pings"] = 0 kwargs["summarize_from_coder"] = False + kwargs["stream"] = self.stream + kwargs["auto_commits"] = self.auto_commits new_kwargs = dict(io=self.io) new_kwargs.update(kwargs) editor_coder = Coder.create(**new_kwargs) + editor_coder.abs_fnames = set(self.abs_fnames) + editor_coder.abs_read_only_fnames = set(self.abs_read_only_fnames) editor_coder.auto_commits = self.auto_commits editor_coder.cur_messages = [] editor_coder.done_messages = [] + editor_coder.repo = self.repo if self.verbose: editor_coder.show_announcements() diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index e6cf5a9d2ab..a3623e994cf 100755 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -195,6 +195,9 @@ def main( num_ctx: Optional[int] = typer.Option( None, "--num-ctx", help="Override model context window size" ), + moa: Optional[List[str]] = typer.Option( + None, "--moa", help="List of additional architect models" + ), exercises_dir: str = typer.Option( EXERCISES_DIR_DEFAULT, "--exercises-dir", help="Directory with exercise files" ), @@ -294,6 +297,7 @@ def main( editor_edit_format, num_ctx, sleep, + moa, ) all_results.append(results) @@ -317,6 +321,9 @@ def main( max_apply_update_errors, editor_model, editor_edit_format, + num_ctx, + sleep, + moa, ) all_results = run_test_threaded.gather(tqdm=True) @@ -577,6 +584,7 @@ def run_test_real( editor_edit_format, num_ctx=None, sleep=0, + moa=None, ): if not os.path.isdir(testdir): print("Not a dir:", testdir) @@ -651,10 +659,10 @@ def run_test_real( show_fnames = ",".join(map(str, fnames)) print("fnames:", show_fnames) - coder = Coder.create( - main_model, - edit_format, - io, + coder_kwargs = dict( + main_model=main_model, + edit_format=edit_format, + io=io, fnames=fnames, use_git=False, stream=False, @@ -663,6 +671,14 @@ def run_test_real( cache_prompts=True, suggest_shell_commands=False, ) + + # Add architect_models if moa parameter provided + if moa: + # moa is already a list of models + architect_models = [models.Model(m) for m in moa] + coder_kwargs["architect_models"] = architect_models + + coder = Coder.create(**coder_kwargs) coder.max_apply_update_errors = max_apply_update_errors coder.show_announcements() @@ -730,10 +746,15 @@ def run_test_real( instructions = errors instructions += prompts.test_failures.format(file_list=file_list) + # For MOA Benchmark, add the MOA models to the model name + model_name = main_model.name + if moa: + model_name = f"{model_name}, {', '.join(moa)}" + results = dict( testdir=str(testdir), testcase=testdir.name, - model=main_model.name, + model=model_name, edit_format=edit_format, tests_outcomes=test_outcomes, cost=coder.total_cost, From 3db7fa171cd41a338eb3918979827a2578ab9229 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Wed, 18 Dec 2024 21:54:23 +0800 Subject: [PATCH 10/21] Add developer message for openrouter o1 models --- aider/coders/base_coder.py | 4 ++++ aider/models.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 44a175ec1ca..6547eaa21e6 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -1102,6 +1102,10 @@ def format_chat_chunks(self): chunks.system = [ dict(role="system", content=main_sys), ] + elif self.main_model.use_developer_message: + chunks.system = [ + dict(role="developer", content=main_sys), + ] else: chunks.system = [ dict(role="user", content=main_sys), diff --git a/aider/models.py b/aider/models.py index c8dcfdfbc1f..95e9a5f23bc 100644 --- a/aider/models.py +++ b/aider/models.py @@ -95,6 +95,7 @@ class ModelSettings: cache_control: bool = False caches_by_default: bool = False use_system_prompt: bool = True + use_developer_message: bool = False use_temperature: bool = True streaming: bool = True editor_model_name: Optional[str] = None @@ -674,6 +675,19 @@ class ModelSettings: reminder="sys", editor_edit_format="editor-diff", ), + ModelSettings( + "openrouter/openai/o1", + "diff", + weak_model_name="openrouter/openai/gpt-4o-mini", + use_repo_map=True, + lazy=True, + reminder="sys", + editor_edit_format="editor-diff", + use_system_prompt=False, + use_temperature=False, + use_developer_message = True + ), + ModelSettings( "openai/o1-mini", "whole", From 085a6d75be28f3d62b10ef6dd378cda0f5143156 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Fri, 20 Dec 2024 05:06:02 +0800 Subject: [PATCH 11/21] Minor moa prompt changes --- aider/coders/mixture_prompts.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index 19c6f188870..2ff48c0c0a2 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -18,12 +18,13 @@ class MixturePrompts(ArchitectPrompts): - List out all of the user's requirements and constraints explicitly - Evaluate the strengths and weaknesses of previous proposals (if any) - Identify specific areas for improvement or expansion in the existing proposals -- Brainstorm a potential solution that builds upon the previous proposals +- Brainstorm a solution that builds upon the previous proposals - For your potential solution: * Describe the solution in detail * Evaluate how well it meets each of the user's requirements * Consider potential challenges or trade-offs - Plan your enhancements or revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions +- Address proposal questions or suggestions from other architects, encouraging further collaboration - Make sure your proposal aligns with the user's requirements and does not go beyond the scope of the query 3. Formulate your proposal using the following structure: @@ -48,8 +49,10 @@ class MixturePrompts(ArchitectPrompts): - Clearly explain the rationale behind your changes or additions, emphasizing how they improve upon previous proposals. - Be concise but thorough in your explanations. - For code revisions and enhancements, be brief by only showing the changes needed. +- For changes, DO NOT show the entire updated function/file/etc! - Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. - Actively collaborate with other architects by referencing and improving upon their specific ideas and suggestions. +- Always refer to the provided code context as the current state of the code. Proposal changes from architects are suggestions, and not implemented yet. Example output structure (generic, without specific content): From 373633734e229cde2fce52516fde5f05485c8e90 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Fri, 20 Dec 2024 22:24:58 +0800 Subject: [PATCH 12/21] Add resiliency for proposal tag observed failure modes --- aider/coders/mixture_of_architects_coder.py | 39 ++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index cb3c31580be..457fdb00d49 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -12,6 +12,34 @@ def __init__(self, name, model): self.last_response: str | None = None +def extract_proposal_content(content, architect_name): + """ + Extracts proposal content from the given content string. + + Args: + content: The string content to extract from. + architect_name: The name of the architect. + + Returns: + A string containing the extracted proposal content, + wrapped in tags. + """ + # Try to get properly fenced content first + proposal_match = re.search(r"(.*?)", content, re.DOTALL) + if proposal_match: + proposal_content = proposal_match.group(1).strip() + else: + # Fallback: Try to get content after tag + proposal_start = content.find("") + if proposal_start != -1: + proposal_content = content[proposal_start + len("") :].strip() + else: + # Last resort: Use the entire response + proposal_content = content.strip() + + return f"\n{proposal_content}\n\n\n" + + class MixtureOfArchitectsCoder(Coder): edit_format = "mixture" gpt_prompts = MixturePrompts() @@ -85,15 +113,10 @@ def get_architect_response(self, architect, current_user_message): msg["role"] == "assistant" and msg["name"] != architect.name.upper() ): - content = msg["content"] - proposal_match = re.search( - r"(.*?)", content, re.DOTALL + # Use the helper function to extract proposal content + user_content += extract_proposal_content( + msg["content"], msg["name"] ) - if proposal_match: - proposal_content = proposal_match.group(1).strip() - user_content += f"\n" - user_content += proposal_content - user_content += "\n\n\n" ask_coder.cur_messages.append({"role": "user", "content": user_content}) From 6a2c289a99a804e967fa4283e3b30452a09325bc Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Fri, 20 Dec 2024 22:59:22 +0800 Subject: [PATCH 13/21] Change removing architect into /ignore --- aider/coders/mixture_of_architects_coder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 457fdb00d49..6959b375a21 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -233,7 +233,7 @@ def preproc_user_input(self, inp): cmd = words[0].lower() args = " ".join(words[1:]) - if cmd in ["/drop", "/discuss", "/code", "/clear", "/reset"]: + if cmd in ["/ignore", "/discuss", "/code", "/clear", "/reset"]: cmd = cmd[1:] # strip the / return self.handle_discussion_commands(cmd, args) @@ -255,7 +255,7 @@ def run_one(self, user_message, preproc): def handle_discussion_commands(self, cmd, args): """ Handle special mixture of architects commands: - /drop - Remove an architect from the discussion + /ignore - Remove an architect from the discussion /discuss - Start a new discussion round /code - Move to implementation phase /clear - Clear chat and discussion history @@ -273,12 +273,12 @@ def handle_discussion_commands(self, cmd, args): "All files dropped, chat history and discussion history cleared." ) return - elif cmd == "drop": + elif cmd == "ignore": nato_name = args.strip().lower() for arch in self.architects: if arch.name == nato_name: arch.active = False - self.io.tool_output(f"Dropped architect {nato_name}") + self.io.tool_output(f"Ignored architect {nato_name}") return elif cmd == "discuss": From c8fb8cec23fa3d38bfcd196f84ad0970b58a0a15 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Tue, 24 Dec 2024 22:57:10 +0800 Subject: [PATCH 14/21] Minor prompt edits to try wrangle creeping enhancements --- aider/coders/mixture_prompts.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index f86e7c7eb58..6dd92c63457 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -23,9 +23,10 @@ class MixturePrompts(ArchitectPrompts): * Describe the solution in detail. * Evaluate how well it meets each of the user's requirements. * Consider potential challenges or trade-offs. -- Plan your enhancements or revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions. +- Plan your revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions. - Address proposal questions or suggestions from other architects, encouraging further collaboration. -- Make sure your proposal aligns with the user's requirements and does not go beyond the scope of the query. +- Make sure your proposal aligns with the user's requirements **and does not add any non-essential features outside the given scope**. +- **If you find the existing proposals correct and complete, explicitly state that the solution is sufficient and no further revisions are necessary.** 3. Formulate your proposal using the following structure: @@ -42,7 +43,7 @@ class MixturePrompts(ArchitectPrompts): 2. ```next code snippet``` -Include only the necessary changes or additions. If you support a prior instruction from another architect without changes, state your agreement explicitly and direct the user to refer to that architect's instruction without repeating the code. For example: +Include only the necessary changes or additions. **Do not add new or "nice-to-have" features (e.g., optional accessibility improvements, helper middleware) unless they are strictly necessary to meet the user's requirements or correct functionality.** If you support a prior instruction from another architect without changes, state your agreement explicitly and direct the user to refer to that architect's instruction without repeating the code. For example: 1. "Refer to Architect A’s instruction for this step, as it is correct and does not require changes." @@ -53,7 +54,7 @@ class MixturePrompts(ArchitectPrompts): Clearly state when you are building on, modifying, or diverging from prior proposals. Avoid duplicating code snippets if they are already correct and referenced.] -[Address any open questions or suggestions for further collaboration among architects.] +[Address any open questions or suggestions for further collaboration among architects. **If you agree there are no more necessary improvements, explicitly say the plan is complete.**] 4. Outside the tags, you may address the user directly with any clarifying questions or additional information. @@ -64,13 +65,16 @@ class MixturePrompts(ArchitectPrompts): - Always include ALL implementation details inside the tags. - Show only the necessary changes to the code, never the entire code. - Do not duplicate proposals from other architects unless proposing changes or enhancements to them. +- **Do not introduce features or modifications beyond the user's explicit requirements or scope.** If unsure, ask the user for clarification or omit the feature. - Explicitly note when you are proposing a different approach than a previous architect's proposal. -- Explicitly acknowledge and support previous instructions where applicable. If supporting a previous instruction without changes, state it clearly and refer the user to that instruction without repeating the code. +- Explicitly acknowledge and support previous instructions where applicable. If supporting a previous instruction without changes, state it clearly and refer to that instruction without repeating the code. - Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. - Actively collaborate with other architects by referencing their ideas and improving upon them. - Always refer to the provided code context as the current state. Consider previous proposals as suggested but not yet implemented. - The style of your instructions should be concise and unambiguous to guide an "editor engineer" who will make changes based on your instructions. +**If no further changes are needed to meet the user’s requirements, conclude that the task is complete and refrain from proposing additional or out-of-scope features.** + Example output structure (generic, without specific content): @@ -97,11 +101,10 @@ class MixturePrompts(ArchitectPrompts): "As proposed by Architect A, this step is sufficient and requires no changes. Refer to their instruction." Only show what must be modified or added.] -[Questions or suggestions for further collaboration] +[Questions or suggestions for further collaboration or a statement that the proposal is complete and meets all requirements] [Any direct communication with the user, if necessary] - """ # Keep other prompts from ArchitectPrompts From fb3ee7eb099ecbe2a4f15a973fd234389526139e Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Mon, 30 Dec 2024 17:14:00 +0800 Subject: [PATCH 15/21] Minor prompt edits to wrangle scope creep in aoi section --- aider/coders/mixture_prompts.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index 6dd92c63457..0f1467511be 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -17,7 +17,7 @@ class MixturePrompts(ArchitectPrompts): - List out all of the user's requirements and constraints explicitly. - Evaluate the strengths and weaknesses of previous proposals (if any). -- Identify specific areas for improvement or expansion in the existing proposals. +- Identify specific areas for improvement or expansion in the existing proposals. **Areas of improvement or expansion does not include non-essential features outside the user's requirements unless specifically asked by the user** - Brainstorm a solution that builds upon the previous proposals. - For your potential solution: * Describe the solution in detail. @@ -32,10 +32,16 @@ class MixturePrompts(ArchitectPrompts): -[Explain your changes or additions to the previous proposal here. Be specific about what you're modifying and why. Focus on how your changes improve upon and refine the existing solution. If a previous proposal sufficiently addresses a particular issue, acknowledge it explicitly and refer to the previous architect's instruction without duplicating the code. If you propose a different approach, explicitly state how it differs and why you believe it is better.] +[Explain your changes or additions to the previous proposal here. +Be specific about what you're modifying and why. +Focus on how your changes improve upon and refine the existing solution. +If a previous proposal sufficiently addresses a particular issue, acknowledge it explicitly and refer to the previous architect's instruction without duplicating the code. +If you propose a different approach, explicitly state how it differs and why you believe it is better.] -[Your detailed implementation proposal goes here. Use numbered instructions for clarity and conciseness. Each instruction should include a short description and, if applicable, the corresponding code snippet. For example: +[Your detailed implementation proposal goes here. +Use numbered instructions for clarity and conciseness. +Each instruction should include a short description and, if applicable, the corresponding code snippet. For example: 1. ```code snippet``` @@ -43,7 +49,10 @@ class MixturePrompts(ArchitectPrompts): 2. ```next code snippet``` -Include only the necessary changes or additions. **Do not add new or "nice-to-have" features (e.g., optional accessibility improvements, helper middleware) unless they are strictly necessary to meet the user's requirements or correct functionality.** If you support a prior instruction from another architect without changes, state your agreement explicitly and direct the user to refer to that architect's instruction without repeating the code. For example: +Include only the necessary changes or additions. +**Do not add new or "nice-to-have" features (e.g., optional accessibility improvements, helper middleware) unless they are strictly necessary to meet the user's requirements or correct functionality.** +If you support a prior instruction from another architect without changes, state your agreement explicitly and direct the user to refer to that architect's instruction without repeating the code. +For example: 1. "Refer to Architect A’s instruction for this step, as it is correct and does not require changes." From c3047771fc49583c857611b7cbbd803b06b16225 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Tue, 31 Dec 2024 17:54:18 +0800 Subject: [PATCH 16/21] Switch to prompt to use mini diffs for changes --- aider/coders/mixture_prompts.py | 78 +++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index 0f1467511be..ec9c0730d8a 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -17,16 +17,16 @@ class MixturePrompts(ArchitectPrompts): - List out all of the user's requirements and constraints explicitly. - Evaluate the strengths and weaknesses of previous proposals (if any). -- Identify specific areas for improvement or expansion in the existing proposals. **Areas of improvement or expansion does not include non-essential features outside the user's requirements unless specifically asked by the user** +- Identify specific areas for improvement or expansion in the existing proposals. **Areas of improvement or expansion must remain strictly within the user's stated requirements. Do not propose new or “nice-to-have” features unless the user explicitly requests them.** - Brainstorm a solution that builds upon the previous proposals. - For your potential solution: * Describe the solution in detail. * Evaluate how well it meets each of the user's requirements. * Consider potential challenges or trade-offs. - Plan your revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions. -- Address proposal questions or suggestions from other architects, encouraging further collaboration. -- Make sure your proposal aligns with the user's requirements **and does not add any non-essential features outside the given scope**. - **If you find the existing proposals correct and complete, explicitly state that the solution is sufficient and no further revisions are necessary.** +- Address proposal questions or suggestions from other architects, encouraging further collaboration. If multiple architects have offered conflicting approaches, compare them thoughtfully and combine or choose the best approach with justification. If additional user clarification is needed, request it. +- Make sure your proposal aligns with the user's requirements **and does not add any non-essential features outside the given scope**. 3. Formulate your proposal using the following structure: @@ -41,15 +41,31 @@ class MixturePrompts(ArchitectPrompts): [Your detailed implementation proposal goes here. Use numbered instructions for clarity and conciseness. -Each instruction should include a short description and, if applicable, the corresponding code snippet. For example: - -1. -```code snippet``` - -2. -```next code snippet``` - -Include only the necessary changes or additions. +Each instruction should include a short description and, if applicable, provide minimal diff-style code changes. + +When providing these code changes: +1. **Use multiple separate diff blocks for separate locations** if changes are scattered in non-adjacent parts of the file. +2. **Show only the lines that changed plus as few surrounding lines of context as necessary** (ideally one or two lines above and below). If more context is needed for clarity, it is allowed, but keep it concise. +3. Do not repeat code that remains unchanged unless it is necessary to provide context for the changed lines. +4. Use a diff format like: + + 1. + ```diff + [lines of context above] + - console.log("Old line"); + + console.log("New line"); + [lines of context below] + ``` + + 2. + ```diff + [lines of context above] + - console.log("Another old line"); + + console.log("Another new line"); + [lines of context below] + ``` + +This approach helps reviewers spot changes more easily without reviewing the full code again. **Do not add new or "nice-to-have" features (e.g., optional accessibility improvements, helper middleware) unless they are strictly necessary to meet the user's requirements or correct functionality.** If you support a prior instruction from another architect without changes, state your agreement explicitly and direct the user to refer to that architect's instruction without repeating the code. For example: @@ -59,14 +75,19 @@ class MixturePrompts(ArchitectPrompts): 2. "Adding to Architect A’s proposal, this adjustment will ensure compatibility." -```additional code snippet``` + ```diff + [one or two lines of context above] + - console.log("Another old line"); + + console.log("Code adjustments here"); + [one or two lines of context below] + ``` -Clearly state when you are building on, modifying, or diverging from prior proposals. Avoid duplicating code snippets if they are already correct and referenced.] +Clearly state when you are building on, modifying, or diverging from prior proposals. Avoid duplicating code snippets if they are already correct and referenced. -[Address any open questions or suggestions for further collaboration among architects. **If you agree there are no more necessary improvements, explicitly say the plan is complete.**] +[Address any open questions or suggestions for further collaboration among architects. **If you agree there are no more necessary improvements, explicitly say “No further changes are necessary, and I believe this meets all user requirements.”**] -4. Outside the tags, you may address the user directly with any clarifying questions or additional information. +4. **Outside** the tags, you may address the user directly with any clarifying questions or additional information. For example, you might ask for more details if two architects’ proposals conflict. Remember: - Only the content inside the tags will be visible to other architects. @@ -78,11 +99,11 @@ class MixturePrompts(ArchitectPrompts): - Explicitly note when you are proposing a different approach than a previous architect's proposal. - Explicitly acknowledge and support previous instructions where applicable. If supporting a previous instruction without changes, state it clearly and refer to that instruction without repeating the code. - Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. -- Actively collaborate with other architects by referencing their ideas and improving upon them. +- **Actively collaborate** with other architects by referencing their ideas and improving upon them. If multiple proposals are conflicting, compare them in and unify or choose the best approach. - Always refer to the provided code context as the current state. Consider previous proposals as suggested but not yet implemented. - The style of your instructions should be concise and unambiguous to guide an "editor engineer" who will make changes based on your instructions. -**If no further changes are needed to meet the user’s requirements, conclude that the task is complete and refrain from proposing additional or out-of-scope features.** +**If no further changes are needed to meet the user’s requirements, conclude that the task is complete by stating “No further changes are necessary, and I believe this meets all user requirements.” and refrain from proposing additional or out-of-scope features.** Example output structure (generic, without specific content): @@ -98,10 +119,20 @@ class MixturePrompts(ArchitectPrompts): [Detailed instructions for changes, using numbered steps for clarity. Each step should contain a description and, if applicable, the corresponding code snippet. For example: 1. -```code snippet``` + ```diff + [lines of context above] + - console.log("Old line"); + + console.log("New line"); + [lines of context below] + ``` 2. -```next code snippet``` + ```diff + [lines of context above] + - console.log("Another old line"); + + console.log("Another new line"); + [lines of context below] + ``` 3. "Refer to Architect B’s instruction for this step, as it is correct and does not require changes." @@ -110,11 +141,12 @@ class MixturePrompts(ArchitectPrompts): "As proposed by Architect A, this step is sufficient and requires no changes. Refer to their instruction." Only show what must be modified or added.] -[Questions or suggestions for further collaboration or a statement that the proposal is complete and meets all requirements] +[Questions or suggestions for further collaboration or a statement that the proposal is complete and meets all requirements, for example: +“No further changes are necessary, and I believe this meets all user requirements.”] -[Any direct communication with the user, if necessary] - """ +[Any direct communication with the user, if necessary] +""" # Keep other prompts from ArchitectPrompts files_content_prefix = ArchitectPrompts.files_content_prefix From 7a73ddf4a29eed7f51339f92237c44c0936adcc8 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Thu, 2 Jan 2025 21:11:36 +0800 Subject: [PATCH 17/21] Switch to minmaxing user requirements for prompt --- aider/coders/mixture_prompts.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/aider/coders/mixture_prompts.py b/aider/coders/mixture_prompts.py index ec9c0730d8a..976b70c6dc1 100644 --- a/aider/coders/mixture_prompts.py +++ b/aider/coders/mixture_prompts.py @@ -2,7 +2,9 @@ class MixturePrompts(ArchitectPrompts): - main_system = """ You are an AI architect, part of a team collaborating to design software solutions. Your role is to analyze, enhance, and build upon the ideas of your fellow architects while addressing the user's needs. Your name will be provided by the user. + main_system = """You are an AI architect, part of a team collaborating to design software solutions. + Your role is to analyze, enhance, and build upon the ideas of your fellow architects **in the simplest way possible** while addressing the user's needs. + Your name will be provided by the user. Please respond to the user in the following language: @@ -17,16 +19,18 @@ class MixturePrompts(ArchitectPrompts): - List out all of the user's requirements and constraints explicitly. - Evaluate the strengths and weaknesses of previous proposals (if any). -- Identify specific areas for improvement or expansion in the existing proposals. **Areas of improvement or expansion must remain strictly within the user's stated requirements. Do not propose new or “nice-to-have” features unless the user explicitly requests them.** -- Brainstorm a solution that builds upon the previous proposals. +- Identify specific areas for improvement or expansion in the existing proposals. **Areas of improvement or expansion must remain strictly within the user's stated requirements.** +- **Always favor the simplest viable solution** that directly addresses the user’s needs. **Avoid adding complexity or “nice-to-have” features** unless the user explicitly requests them. +- Brainstorm a solution that builds upon the previous proposals **only to the extent necessary** to fulfill the user's requirements. - For your potential solution: * Describe the solution in detail. * Evaluate how well it meets each of the user's requirements. - * Consider potential challenges or trade-offs. -- Plan your revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions. + * Consider potential challenges or trade-offs, emphasizing straightforward resolutions. +- **Do not propose out-of-scope features or over-engineer.** Keep your solution concise and directly tied to the requirements. +- Plan your revisions in detail, focusing on refining existing ideas rather than creating entirely new solutions. **If the simplest approach from previous architects already meets the user's needs, state that no further changes are needed.** - **If you find the existing proposals correct and complete, explicitly state that the solution is sufficient and no further revisions are necessary.** - Address proposal questions or suggestions from other architects, encouraging further collaboration. If multiple architects have offered conflicting approaches, compare them thoughtfully and combine or choose the best approach with justification. If additional user clarification is needed, request it. -- Make sure your proposal aligns with the user's requirements **and does not add any non-essential features outside the given scope**. +- Make sure your proposal aligns with the user's requirements **without expanding beyond them**. 3. Formulate your proposal using the following structure: @@ -34,9 +38,9 @@ class MixturePrompts(ArchitectPrompts): [Explain your changes or additions to the previous proposal here. Be specific about what you're modifying and why. -Focus on how your changes improve upon and refine the existing solution. +Focus on how your changes **simplify** or refine the existing solution, rather than expanding it. If a previous proposal sufficiently addresses a particular issue, acknowledge it explicitly and refer to the previous architect's instruction without duplicating the code. -If you propose a different approach, explicitly state how it differs and why you believe it is better.] +If you propose a different approach, explicitly state how it differs and why you believe it is **simpler** and better.] [Your detailed implementation proposal goes here. @@ -66,7 +70,7 @@ class MixturePrompts(ArchitectPrompts): ``` This approach helps reviewers spot changes more easily without reviewing the full code again. -**Do not add new or "nice-to-have" features (e.g., optional accessibility improvements, helper middleware) unless they are strictly necessary to meet the user's requirements or correct functionality.** +**Do not add new or "nice-to-have" features** unless they are strictly necessary to meet the user's requirements or correct functionality. If you support a prior instruction from another architect without changes, state your agreement explicitly and direct the user to refer to that architect's instruction without repeating the code. For example: @@ -96,9 +100,7 @@ class MixturePrompts(ArchitectPrompts): - Show only the necessary changes to the code, never the entire code. - Do not duplicate proposals from other architects unless proposing changes or enhancements to them. - **Do not introduce features or modifications beyond the user's explicit requirements or scope.** If unsure, ask the user for clarification or omit the feature. -- Explicitly note when you are proposing a different approach than a previous architect's proposal. -- Explicitly acknowledge and support previous instructions where applicable. If supporting a previous instruction without changes, state it clearly and refer to that instruction without repeating the code. -- Ensure your proposal aligns with the user's requirements and builds upon the team's collective knowledge. +- **Strive for the most straightforward, minimal solution** that fulfills the user’s requirements. - **Actively collaborate** with other architects by referencing their ideas and improving upon them. If multiple proposals are conflicting, compare them in and unify or choose the best approach. - Always refer to the provided code context as the current state. Consider previous proposals as suggested but not yet implemented. - The style of your instructions should be concise and unambiguous to guide an "editor engineer" who will make changes based on your instructions. From 1950546c82082168206b574f8b0c272b134121ee Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Fri, 10 Jan 2025 17:09:13 +0800 Subject: [PATCH 18/21] Rewrite messages orchestration in discussion phase --- aider/coders/mixture_of_architects_coder.py | 97 +++++++++++---------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 6959b375a21..dbf280d57fd 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -85,50 +85,55 @@ def get_architect_response(self, architect, current_user_message): ask_coder.auto_commits = self.auto_commits ask_coder.gpt_prompts = MixturePrompts() - # Group messages by conversation round - rounds = [] - current_round = [] for msg in self.discussion_messages: - if msg["role"] == "user": - if current_round: - rounds.append(current_round) - current_round = [msg] + if ask_coder.cur_messages: + last_msg_is_user = ask_coder.cur_messages[-1]["role"] == "user" else: - current_round.append(msg) - if current_round: - rounds.append(current_round) - - # Build the conversation messages - for round_msgs in rounds: - user_msg = next(msg for msg in round_msgs if msg["role"] == "user") - - # Combine user message with other architects' proposals - user_content = "\n" - user_content += user_msg["content"] - user_content += "\n\n\n" - - # Add other architects' proposals from this round - for msg in round_msgs: - if ( - msg["role"] == "assistant" - and msg["name"] != architect.name.upper() - ): - # Use the helper function to extract proposal content - user_content += extract_proposal_content( - msg["content"], msg["name"] - ) - - ask_coder.cur_messages.append({"role": "user", "content": user_content}) + last_msg_is_user = False - # Add this architect's own response if they had one - for msg in round_msgs: - if ( - msg["role"] == "assistant" - and msg["name"] == architect.name.upper() - ): - ask_coder.cur_messages.append( - {"role": "assistant", "content": msg["content"]} + match msg["role"]: + case "user": + fenced_content = ( + f"\n{msg['content']}\n\n\n" ) + if last_msg_is_user: + latest_user_content = ask_coder.cur_messages[-1]["content"] + latest_user_content += fenced_content + ask_coder.cur_messages[-1]["content"] = latest_user_content + else: + ask_coder.cur_messages.append( + {"role": "user", "content": fenced_content} + ) + case "assistant": + # If its the current architect, then we use role=assistant + if msg["name"] == architect.name.upper(): + ask_coder.cur_messages.append( + {"role": "assistant", "content": msg["content"]} + ) + else: + # If the not current architect, then we inject in user side + # append to the last user message + if last_msg_is_user: + + latest_user_content = ask_coder.cur_messages[-1][ + "content" + ] + latest_user_content += extract_proposal_content( + msg["content"], msg["name"] + ) + ask_coder.cur_messages[-1][ + "content" + ] = latest_user_content + # or create a new user message + else: + ask_coder.cur_messages.append( + { + "role": "user", + "content": extract_proposal_content( + msg["content"], msg["name"] + ), + } + ) # Debug output if verbose if self.verbose: @@ -221,7 +226,7 @@ def run_discussion_round(self, user_message): # Yes is proxy for auto running code, As proxy for benchmarking # TODO: Replace with a better testing strategy if self.io.yes: - self.run_coding_phase(user_message) + self.run_coding_phase("lets implement best simplest solution") def preproc_user_input(self, inp): if not inp: @@ -297,12 +302,12 @@ def run_coding_phase(self, message): self.discussion_messages.append( { "role": "user", - "content": f"Please implement the following: {message}", + "content": f"{message}", } ) # Format the full conversation history with XML fences - combined_response = "Full discussion history:\n\n" + combined_response = "" for msg in self.discussion_messages: if msg["role"] == "user": combined_response += "\n" @@ -311,11 +316,7 @@ def run_coding_phase(self, message): else: combined_response += f"\n" combined_response += msg["content"] - combined_response += f"\n\n\n" - - combined_response += ( - "\nBased on the above discussion, please implement the requested changes." - ) + combined_response += "\n\n\n" # Debug print the combined response if self.verbose: From c6024dd2b2d8fca24f2c2617c498bd76cf75e249 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Tue, 14 Jan 2025 01:19:04 +0800 Subject: [PATCH 19/21] Add compiler step --- aider/coders/compiler_coder.py | 9 +++ aider/coders/compiler_prompts.py | 78 +++++++++++++++++++++ aider/coders/mixture_of_architects_coder.py | 50 +++++++++---- aider/models.py | 2 + 4 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 aider/coders/compiler_coder.py create mode 100644 aider/coders/compiler_prompts.py diff --git a/aider/coders/compiler_coder.py b/aider/coders/compiler_coder.py new file mode 100644 index 00000000000..370de72bbbc --- /dev/null +++ b/aider/coders/compiler_coder.py @@ -0,0 +1,9 @@ +from .ask_coder import AskCoder +from .compiler_prompts import CompilerPrompts + + +class CompilerCoder(AskCoder): + """Compiles implementation instructions from architects' proposals.""" + + edit_format = "ask" + gpt_prompts = CompilerPrompts() diff --git a/aider/coders/compiler_prompts.py b/aider/coders/compiler_prompts.py new file mode 100644 index 00000000000..793bb3ad815 --- /dev/null +++ b/aider/coders/compiler_prompts.py @@ -0,0 +1,78 @@ +from .base_prompts import CoderPrompts + + +class CompilerPrompts(CoderPrompts): + main_system = '''Act as an expert code implementation compiler. +Your role is to analyze proposals from multiple architects and compile them into a clear, organized set of implementation instructions. + +Focus ONLY on compiling the specific implementation details and changes proposed by the architects. +Do not attempt to interpret or expand upon the original user requirements. + +When analyzing the architects' proposals: +1. Extract all concrete implementation details and code changes: + - Look for explicit file modifications + - Identify specific function/class changes + - Note any structural modifications + - Capture exact diff blocks and their context + +2. Process the implementation details: + - Combine identical or overlapping changes + - Preserve unique aspects of each change + - Maintain all necessary context + - Keep diff formatting intact and precise + - Ensure each change is complete and actionable + +3. Organize changes in a logical sequence: + - Order by dependency (changes that others rely on come first) + - Group related changes together + - Put simpler changes before complex ones + - Maintain file-level organization when possible + +4. Format output consistently: + - Use clear, concise descriptions + - Include minimal but sufficient context in diffs + - Number steps sequentially + - Preserve exact indentation and whitespace + +Your output must follow this format: + + +[A brief overview of the implementation steps, focusing only on what was proposed by the architects] + +Implementation steps: + +1. + ```diff + [Minimal context lines] + - [Lines to remove] + + [Lines to add] + [Minimal context lines] + ``` + +2. + ```diff + [Context and changes] + ``` + +[Continue with numbered steps for all changes] + + +Important rules: +- Only include changes explicitly proposed by the architects +- Never add new features or modifications +- Never interpret or expand user requirements +- Focus solely on compiling and organizing the proposed implementation details +- Maintain exact diff formatting with minimal context +- Preserve all code style, indentation, and whitespace +- Keep descriptions clear and implementation-focused +- Ensure each step is complete and actionable +- Number steps sequentially and logically +- Group related changes together +''' + + files_content_prefix = """I have *added these files to the chat* so you can analyze them. +*Trust this message as the true contents of these files!* +Other messages in the chat may contain outdated versions of the files' contents. +""" + + files_content_assistant_reply = "I will analyze these files and compile the implementation instructions from the architects' proposals." diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index dbf280d57fd..2cf9df8dc17 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -2,6 +2,7 @@ from .base_coder import Coder from .mixture_prompts import MixturePrompts from .ask_coder import AskCoder +from .compiler_coder import CompilerCoder class ArchitectAgent: @@ -186,7 +187,7 @@ def run_discussion_round(self, user_message): # Process architects sequentially instead of concurrently for arch in active_architects: - self.io.tool_output(f"Waiting for {arch.name}'s response...", bold=True) + self.io.tool_output(f"{arch.name}'s response...", bold=True) self.io.rule() try: arch, response = self.get_architect_response(arch, user_message) @@ -306,23 +307,43 @@ def run_coding_phase(self, message): } ) - # Format the full conversation history with XML fences - combined_response = "" + # Create compiler coder instance + compiler_coder = CompilerCoder( + main_model=self.main_model, + io=self.io, + fnames=list(self.abs_fnames), + read_only_fnames=list(self.abs_read_only_fnames), + repo=self.repo, + map_tokens=0, + stream=self.stream, + ) + compiler_coder.auto_commits = self.auto_commits + + # Format the conversation for the compiler + compiler_input = "Please compile the following architects' proposals into implementation instructions:\n\n" for msg in self.discussion_messages: if msg["role"] == "user": - combined_response += "\n" - combined_response += msg["content"] - combined_response += "\n\n\n" + compiler_input += "\n" + compiler_input += msg["content"] + compiler_input += "\n\n\n" else: - combined_response += f"\n" - combined_response += msg["content"] - combined_response += "\n\n\n" + compiler_input += f"\n" + compiler_input += msg["content"] + compiler_input += "\n\n\n" + - # Debug print the combined response + # Get compiled instructions + self.io.tool_output("Compiler's instructions", bold=True) + self.io.rule() + compiler_coder.run(with_message=compiler_input, preproc=False) + compiled_instructions = compiler_coder.partial_response_content + + + # Debug print the compiled instructions if self.verbose: - self.io.tool_output("\nDebug: Combined response being sent to editor:") + self.io.tool_output("\nDebug: Compiled instructions being sent to editor:") self.io.tool_output("-" * 40) - self.io.tool_output(combined_response) + self.io.tool_output(compiled_instructions) self.io.tool_output("-" * 40 + "\n") # Use editor coder like ArchitectCoder does @@ -353,7 +374,10 @@ def run_coding_phase(self, message): if self.verbose: editor_coder.show_announcements() - editor_coder.run(with_message=combined_response, preproc=False) + + self.io.tool_output("Coder's output", bold=True) + self.io.rule() + editor_coder.run(with_message=compiled_instructions, preproc=False) self.move_back_cur_messages( "Changes have been applied based on architects' consensus." diff --git a/aider/models.py b/aider/models.py index 9a989693865..f10564788b7 100644 --- a/aider/models.py +++ b/aider/models.py @@ -794,6 +794,7 @@ class ModelSettings: use_repo_map=True, streaming=False, use_temperature=False, + use_developer_message = True # extra_params=dict(extra_body=dict(reasoning_effort="high")), ), ModelSettings( @@ -805,6 +806,7 @@ class ModelSettings: use_repo_map=True, streaming=False, use_temperature=False, + use_developer_message=True # extra_params=dict(extra_body=dict(reasoning_effort="high")), ), ModelSettings( From a3be0dbaa2850ef5e09039694d91345154167c71 Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Fri, 17 Jan 2025 12:04:22 +0800 Subject: [PATCH 20/21] Add assertion prompt for full implementation of coder from compiler --- aider/coders/mixture_of_architects_coder.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 2cf9df8dc17..2cba2b7dd9e 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -337,6 +337,7 @@ def run_coding_phase(self, message): self.io.rule() compiler_coder.run(with_message=compiler_input, preproc=False) compiled_instructions = compiler_coder.partial_response_content + compiled_instructions += "\n\nCompletely implement all steps in the instructions above. Do not return to me until you have done so." # Debug print the compiled instructions From 3c608fb2260308b3f2413bd7d68f326e89bbefbf Mon Sep 17 00:00:00 2001 From: Gil Emmanuel Bancud Date: Tue, 21 Jan 2025 09:50:46 +0800 Subject: [PATCH 21/21] Fix R1 user message turn issues --- aider/coders/mixture_of_architects_coder.py | 23 ++++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/aider/coders/mixture_of_architects_coder.py b/aider/coders/mixture_of_architects_coder.py index 2cba2b7dd9e..7d47dac0b50 100644 --- a/aider/coders/mixture_of_architects_coder.py +++ b/aider/coders/mixture_of_architects_coder.py @@ -82,6 +82,7 @@ def get_architect_response(self, architect, current_user_message): map_tokens=self.repo_map.max_map_tokens if self.repo_map else 0, summarize_from_coder=False, stream=self.stream, + verbose=self.verbose, ) ask_coder.auto_commits = self.auto_commits ask_coder.gpt_prompts = MixturePrompts() @@ -150,11 +151,19 @@ def get_architect_response(self, architect, current_user_message): self.io.tool_output("-" * 40) # Pass the current message with XML tags as with_message - formatted_message = f""" - You are arhitect {architect.name} - - {current_user_message} - """ + if ask_coder.cur_messages[-1].get("role") == "user": + architect_assignment = f""" You are architect {architect.name} + + {current_user_message} + """ + ask_coder.cur_messages[-1]["content"] += architect_assignment + ask_coder.cur_messages.append( + {"role": "assistant", "content": "I am architect {architect.name}"} + ) + formatted_message = ( + f"\n{current_user_message}\n" + ) + response = ask_coder.run(with_message=formatted_message, preproc=False) if not response.strip(): @@ -331,7 +340,6 @@ def run_coding_phase(self, message): compiler_input += msg["content"] compiler_input += "\n\n\n" - # Get compiled instructions self.io.tool_output("Compiler's instructions", bold=True) self.io.rule() @@ -339,7 +347,6 @@ def run_coding_phase(self, message): compiled_instructions = compiler_coder.partial_response_content compiled_instructions += "\n\nCompletely implement all steps in the instructions above. Do not return to me until you have done so." - # Debug print the compiled instructions if self.verbose: self.io.tool_output("\nDebug: Compiled instructions being sent to editor:") @@ -360,6 +367,7 @@ def run_coding_phase(self, message): kwargs["summarize_from_coder"] = False kwargs["stream"] = self.stream kwargs["auto_commits"] = self.auto_commits + kwargs["verbose"] = self.verbose new_kwargs = dict(io=self.io) new_kwargs.update(kwargs) @@ -375,7 +383,6 @@ def run_coding_phase(self, message): if self.verbose: editor_coder.show_announcements() - self.io.tool_output("Coder's output", bold=True) self.io.rule() editor_coder.run(with_message=compiled_instructions, preproc=False)