Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aider/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from packaging import version

__version__ = "0.88.32.dev"
__version__ = "0.88.33.dev"
safe_version = __version__

try:
Expand Down
63 changes: 51 additions & 12 deletions aider/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ def __init__(
async def cmd_model(self, args):
"Switch the Main Model to a new LLM"

model_name = args.strip()
arg_split = args.split(" ", 1)
model_name = arg_split[0].strip()
if not model_name:
announcements = "\n".join(self.coder.get_announcements())
self.io.tool_output(announcements)
Expand All @@ -111,19 +112,57 @@ async def cmd_model(self, args):
# If the user was using the old model's default, switch to the new model's default
new_edit_format = model.edit_format

raise SwitchCoder(main_model=model, edit_format=new_edit_format)
if len(arg_split) > 1:
# implement architect coder-like generation call for model
message = arg_split[1].strip()

async def cmd_editor_model(self, args):
"Switch the Editor Model to a new LLM"
# Store the original model configuration
original_main_model = self.coder.main_model
original_edit_format = self.coder.edit_format

model_name = args.strip()
model = models.Model(
self.coder.main_model.name,
editor_model=model_name,
weak_model=self.coder.main_model.weak_model.name,
)
await models.sanity_check_models(self.io, model)
raise SwitchCoder(main_model=model)
# Create a temporary coder with the new model
from aider.coders import Coder

kwargs = dict()
kwargs["main_model"] = model
kwargs["edit_format"] = new_edit_format
kwargs["suggest_shell_commands"] = False
kwargs["total_cost"] = self.coder.total_cost
kwargs["num_cache_warming_pings"] = 0
kwargs["summarize_from_coder"] = False

new_kwargs = dict(io=self.io, from_coder=self.coder)
new_kwargs.update(kwargs)

temp_coder = await Coder.create(**new_kwargs)
temp_coder.cur_messages = []
temp_coder.done_messages = []

if self.verbose:
temp_coder.show_announcements()

try:
await temp_coder.generate(user_message=message, preproc=False)
self.coder.move_back_cur_messages(
f"Model {model_name} made those changes to the files."
)
self.coder.total_cost = temp_coder.total_cost
self.coder.aider_commit_hashes = temp_coder.aider_commit_hashes

# Restore the original model configuration
raise SwitchCoder(main_model=original_main_model, edit_format=original_edit_format)
except Exception as e:
# If there's an error, still restore the original model
if not isinstance(e, SwitchCoder):
self.io.tool_error(e)
raise SwitchCoder(
main_model=original_main_model, edit_format=original_edit_format
)
else:
# Re-raise SwitchCoder if that's what was thrown
raise
else:
raise SwitchCoder(main_model=model, edit_format=new_edit_format)

async def cmd_weak_model(self, args):
"Switch the Weak Model to a new LLM"
Expand Down
35 changes: 33 additions & 2 deletions aider/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import base64
import functools
import os
import re
import shutil
import signal
import subprocess
Expand Down Expand Up @@ -32,6 +33,7 @@
from rich.columns import Columns
from rich.console import Console
from rich.markdown import Markdown
from rich.markup import escape
from rich.spinner import SPINNERS
from rich.style import Style as RichStyle
from rich.text import Text
Expand Down Expand Up @@ -294,6 +296,13 @@ class InputOutput:
bell_on_next_input = False
notifications_command = None
encoding = "utf-8"
VALID_STYLES = {"bold", "red", "green", "blue", "orange"}
VALID_OPEN_TAG_PATTERN = re.compile(
r"\\\[(" + "|".join(re.escape(s) for s in VALID_STYLES) + r")\]"
)
VALID_CLOSE_TAG_PATTERN = re.compile(
r"\\\[/(?:" + "|".join(re.escape(s) for s in VALID_STYLES) + r"|)]"
)

def __init__(
self,
Expand Down Expand Up @@ -1373,7 +1382,7 @@ def tool_output(self, *messages, log_only=False, bold=False):
if log_only:
return

messages = list(map(Text, messages))
messages = list(map(lambda message: self.escape(message), messages))
style = dict()
if self.pretty:
if self.tool_output_color:
Expand All @@ -1385,6 +1394,28 @@ def tool_output(self, *messages, log_only=False, bold=False):

self.stream_print(*messages, style=style)

def escape(self, text):
"""Formats valid Rich tags and prints invalid ones as literal text using a single regex pass."""

# 1. Escape everything initially using Rich's built-in function
escaped_text = escape(text)

if text == escaped_text:
return Text(text)

# 2. Un-escape ONLY the valid opening tags
# Replaces '\[style]' with '[style]'
unescaped_text = self.__class__.VALID_OPEN_TAG_PATTERN.sub(
lambda m: f"[{m.group(1)}]", escaped_text
)

# 3. Un-escape ONLY the valid closing tags (handles both [/style] and [/] formats)
# Replaces '\[/style]' or '\[/]' with '[/]'
final_text = self.__class__.VALID_CLOSE_TAG_PATTERN.sub(r"[/]", unescaped_text)

# 4. Print the result
return Text(final_text)

def profile(self, *messages, start=False):
if not self.verbose:
return
Expand Down Expand Up @@ -1656,7 +1687,7 @@ def format_files_for_input(self, rel_fnames, rel_read_only_fnames, rel_read_only
return "\n".join(lines) + "\n"

output = StringIO()
console = Console(file=output, force_terminal=False)
console = Console(file=output, force_terminal=False, markup=False)

# Handle read-only files
if rel_read_only_fnames or rel_read_only_stubs_fnames:
Expand Down
17 changes: 17 additions & 0 deletions aider/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,13 @@ def register_models(git_root, model_settings_fname, io, verbose=False):
io.tool_output(f" - {file_loaded}") # noqa: E221
elif verbose:
io.tool_output("No model settings files loaded")

if (
model_settings_fname
and model_settings_fname not in files_loaded
and model_settings_fname != ".aider.model.settings.yml"
):
io.tool_warning(f"Model Settings File Not Found: {model_settings_fname}")
except Exception as e:
io.tool_error(f"Error loading aider model settings: {e}")
return 1
Expand Down Expand Up @@ -411,6 +418,13 @@ def register_litellm_models(git_root, model_metadata_fname, io, verbose=False):
io.tool_output("Loaded model metadata from:")
for model_metadata_file in model_metadata_files_loaded:
io.tool_output(f" - {model_metadata_file}") # noqa: E221

if (
model_metadata_fname
and model_metadata_fname not in model_metadata_files_loaded
and model_metadata_fname != ".aider.model.metadata.json"
):
io.tool_warning(f"Model Metadata File Not Found: {model_metadata_fname}")
except Exception as e:
io.tool_error(f"Error loading model metadata models: {e}")
return 1
Expand Down Expand Up @@ -1324,6 +1338,7 @@ def get_io(pretty):
coder.suppress_announcements_for_next_prompt = True
except SystemExit:
analytics.event("exit", reason="/exit command")
sys.settrace(None)
return await graceful_exit(coder)


Expand Down Expand Up @@ -1419,6 +1434,8 @@ def load_slow_imports(swallow=True):


async def graceful_exit(coder=None, exit_code=0):
sys.settrace(None)

if coder:
if hasattr(coder, "_autosave_future"):
await coder._autosave_future
Expand Down
5 changes: 3 additions & 2 deletions aider/versioncheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ async def check_version(io, just_check=False, verbose=False):
io.tool_output(f"Current version: {current_version}")
io.tool_output(f"Latest version: {latest_version}")

is_update_available = packaging.version.parse(latest_version) > packaging.version.parse(
current_version
is_update_available = (
packaging.version.parse(latest_version).release
> packaging.version.parse(current_version).release
)
except Exception as err:
io.tool_error(f"Error checking pypi for new version: {err}")
Expand Down
Loading