Merge branch 'main' into watch

This commit is contained in:
Paul Gauthier 2024-11-26 10:30:25 -08:00
commit 139d89d817
78 changed files with 5012 additions and 2667 deletions

3
.gitignore vendored
View file

@ -12,4 +12,5 @@ _site
.jekyll-metadata .jekyll-metadata
aider/__version__.py aider/__version__.py
.venv/ .venv/
.#* .#*
.gitattributes

View file

@ -187,8 +187,8 @@ pytest
You can also run specific test files or test cases by providing the file path or test name: You can also run specific test files or test cases by providing the file path or test name:
``` ```
pytest aider/tests/test_coder.py pytest tests/basic/test_coder.py
pytest aider/tests/test_coder.py::TestCoder::test_specific_case pytest tests/basic/test_coder.py::TestCoder::test_specific_case
``` ```
#### Continuous Integration #### Continuous Integration

View file

@ -1,6 +1,54 @@
# Release history # Release history
### Aider v0.65.0
- Added `--alias` config to define [custom model aliases](https://aider.chat/docs/config/model-aliases.html).
- Added `--[no-]detect-urls` flag to disable detecting and offering to scrape URLs found in the chat.
- Ollama models now default to an 8k context window.
- Added [RepoMap support for Dart language](https://aider.chat/docs/languages.html) by @malkoG.
- Ask 2.5% of users if they want to opt-in to [analytics](https://aider.chat/docs/more/analytics.html).
- Skip suggesting files that share names with files already in chat.
- `/editor` returns and prefill the file content into the prompt, so you can use `/editor` to compose messages that start with `/commands`, etc.
- Enhanced error handling for analytics.
- Improved handling of UnknownEditFormat exceptions with helpful documentation links.
- Bumped dependencies to pick up grep-ast 0.4.0 for Dart language support.
- Aider wrote 81% of the code in this release.
### Aider v0.64.1
- Disable streaming for o1 on OpenRouter.
### Aider v0.64.0
- Added [`/editor` command](https://aider.chat/docs/usage/commands.html) to open system editor for writing prompts, by @thehunmonkgroup.
- Full support for `gpt-4o-2024-11-20`.
- Stream o1 models by default.
- `/run` and suggested shell commands are less mysterious and now confirm that they "Added XX lines of output to the chat."
- Ask 1% of users if they want to opt-in to [analytics](https://aider.chat/docs/more/analytics.html).
- Added support for [optional multiline input tags](https://aider.chat/docs/usage/commands.html#entering-multi-line-chat-messages) with matching closing tags.
- Improved [model settings configuration](https://aider.chat/docs/config/adv-model-settings.html#global-extra-params) with support for global `extra_params` for `litellm.completion()`.
- Architect mode now asks to add files suggested by the LLM.
- Fixed bug in fuzzy model name matching.
- Added Timeout exception to handle API provider timeouts.
- Added `--show-release-notes` to control release notes display on first run of new version.
- Save empty dict to cache file on model metadata download failure, to delay retry.
- Improved error handling and code formatting.
- Aider wrote 74% of the code in this release.
### Aider v0.63.2
- Fixed bug in fuzzy model name matching when litellm provider info is missing.
- Modified model metadata file loading to allow override of resource file.
- Allow recursive loading of dirs using `--read`.
- Updated dependency versions to pick up litellm fix for ollama models.
- Added exponential backoff retry when writing files to handle editor file locks.
- Updated Qwen 2.5 Coder 32B model configuration.
### Aider v0.63.1
- Fixed bug in git ignored file handling.
- Improved error handling for git operations.
### Aider v0.63.0 ### Aider v0.63.0
- Support for Qwen 2.5 Coder 32B. - Support for Qwen 2.5 Coder 32B.

View file

@ -1,6 +1,6 @@
try: try:
from aider.__version__ import __version__ from aider.__version__ import __version__
except Exception: except Exception:
__version__ = "0.63.1.dev" __version__ = "0.65.1.dev"
__all__ = [__version__] __all__ = [__version__]

View file

@ -5,7 +5,7 @@ import time
import uuid import uuid
from pathlib import Path from pathlib import Path
from mixpanel import Mixpanel from mixpanel import Mixpanel, MixpanelException
from posthog import Posthog from posthog import Posthog
from aider import __version__ from aider import __version__
@ -62,8 +62,47 @@ class Analytics:
self.permanently_disable = True self.permanently_disable = True
self.save_data() self.save_data()
def need_to_ask(self): def need_to_ask(self, args_analytics):
return not self.asked_opt_in and not self.permanently_disable if args_analytics is False:
return False
could_ask = not self.asked_opt_in and not self.permanently_disable
if not could_ask:
return False
if args_analytics is True:
return True
assert args_analytics is None, args_analytics
if not self.user_id:
return False
PERCENT = 2.5
return self.is_uuid_in_percentage(self.user_id, PERCENT)
def is_uuid_in_percentage(self, uuid_str, percent):
"""Check if a UUID string falls within the first X percent of the UUID space.
Args:
uuid_str: UUID string to test
percent: Percentage threshold (0-100)
Returns:
bool: True if UUID falls within the first X percent
"""
if not (0 <= percent <= 100):
raise ValueError("Percentage must be between 0 and 100")
if not uuid_str:
return False
# Convert percentage to hex threshold (1% = "04...", 10% = "1a...", etc)
# Using first 6 hex digits
if percent == 0:
return False
threshold = format(int(0xFFFFFF * percent / 100), "06x")
return uuid_str[:6] <= threshold
def get_data_file_path(self): def get_data_file_path(self):
data_file = Path.home() / ".aider" / "analytics.json" data_file = Path.home() / ".aider" / "analytics.json"
@ -120,7 +159,7 @@ class Analytics:
return None return None
def event(self, event_name, main_model=None, **kwargs): def event(self, event_name, main_model=None, **kwargs):
if not (self.mp or self.ph) and not self.logfile: if not self.mp and not self.ph and not self.logfile:
return return
properties = {} properties = {}
@ -143,7 +182,10 @@ class Analytics:
properties["aider_version"] = __version__ properties["aider_version"] = __version__
if self.mp: if self.mp:
self.mp.track(self.user_id, event_name, dict(properties)) try:
self.mp.track(self.user_id, event_name, dict(properties))
except MixpanelException:
self.mp = None # Disable mixpanel on connection errors
if self.ph: if self.ph:
self.ph.capture(self.user_id, event_name, dict(properties)) self.ph.capture(self.user_id, event_name, dict(properties))

View file

@ -193,6 +193,12 @@ def get_parser(default_config_files, git_root):
default=".aider.model.metadata.json", default=".aider.model.metadata.json",
help="Specify a file with context window and costs for unknown models", help="Specify a file with context window and costs for unknown models",
) )
group.add_argument(
"--alias",
action="append",
metavar="ALIAS:MODEL",
help="Add a model alias (can be used multiple times)",
)
group.add_argument( group.add_argument(
"--verify-ssl", "--verify-ssl",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
@ -562,8 +568,8 @@ def get_parser(default_config_files, git_root):
group.add_argument( group.add_argument(
"--analytics", "--analytics",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
default=False, default=None,
help="Enable/disable analytics for one session (default: False)", help="Enable/disable analytics for current session (default: random)",
) )
group.add_argument( group.add_argument(
"--analytics-log", "--analytics-log",
@ -620,6 +626,12 @@ def get_parser(default_config_files, git_root):
help="Check for new aider versions on launch", help="Check for new aider versions on launch",
default=True, default=True,
) )
group.add_argument(
"--show-release-notes",
action=argparse.BooleanOptionalAction,
help="Show release notes on first run of new version (default: None, ask user)",
default=None,
)
group.add_argument( group.add_argument(
"--install-main-branch", "--install-main-branch",
action="store_true", action="store_true",
@ -732,6 +744,16 @@ def get_parser(default_config_files, git_root):
default=True, default=True,
help="Enable/disable fancy input with history and completion (default: True)", help="Enable/disable fancy input with history and completion (default: True)",
) )
group.add_argument(
"--detect-urls",
action=argparse.BooleanOptionalAction,
default=True,
help="Enable/disable detection and offering to add URLs to chat (default: True)",
)
group.add_argument(
"--editor",
help="Specify which editor to use for the /editor command",
)
########## ##########
group = parser.add_argument_group("Voice Settings") group = parser.add_argument_group("Voice Settings")

View file

@ -37,6 +37,15 @@ from ..dump import dump # noqa: F401
from .chat_chunks import ChatChunks from .chat_chunks import ChatChunks
class UnknownEditFormat(ValueError):
def __init__(self, edit_format, valid_formats):
self.edit_format = edit_format
self.valid_formats = valid_formats
super().__init__(
f"Unknown edit format {edit_format}. Valid formats are: {', '.join(valid_formats)}"
)
class MissingAPIKeyError(ValueError): class MissingAPIKeyError(ValueError):
pass pass
@ -91,6 +100,7 @@ class Coder:
cache_warming_thread = None cache_warming_thread = None
num_cache_warming_pings = 0 num_cache_warming_pings = 0
suggest_shell_commands = True suggest_shell_commands = True
detect_urls = True
ignore_mentions = None ignore_mentions = None
chat_language = None chat_language = None
@ -156,7 +166,12 @@ class Coder:
res.original_kwargs = dict(kwargs) res.original_kwargs = dict(kwargs)
return res return res
raise ValueError(f"Unknown edit format {edit_format}") valid_formats = [
str(c.edit_format)
for c in coders.__all__
if hasattr(c, "edit_format") and c.edit_format is not None
]
raise UnknownEditFormat(edit_format, valid_formats)
def clone(self, **kwargs): def clone(self, **kwargs):
new_coder = Coder.create(from_coder=self, **kwargs) new_coder = Coder.create(from_coder=self, **kwargs)
@ -267,6 +282,7 @@ class Coder:
num_cache_warming_pings=0, num_cache_warming_pings=0,
suggest_shell_commands=True, suggest_shell_commands=True,
chat_language=None, chat_language=None,
detect_urls=True,
): ):
# Fill in a dummy Analytics if needed, but it is never .enable()'d # Fill in a dummy Analytics if needed, but it is never .enable()'d
self.analytics = analytics if analytics is not None else Analytics() self.analytics = analytics if analytics is not None else Analytics()
@ -280,6 +296,7 @@ class Coder:
self.ignore_mentions = set() self.ignore_mentions = set()
self.suggest_shell_commands = suggest_shell_commands self.suggest_shell_commands = suggest_shell_commands
self.detect_urls = detect_urls
self.num_cache_warming_pings = num_cache_warming_pings self.num_cache_warming_pings = num_cache_warming_pings
@ -812,6 +829,9 @@ class Coder:
def check_for_urls(self, inp: str) -> List[str]: def check_for_urls(self, inp: str) -> List[str]:
"""Check input for URLs and offer to add them to the chat.""" """Check input for URLs and offer to add them to the chat."""
if not self.detect_urls:
return []
url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*[^\s,.])") url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*[^\s,.])")
urls = list(set(url_pattern.findall(inp))) # Use set to remove duplicates urls = list(set(url_pattern.findall(inp))) # Use set to remove duplicates
added_urls = [] added_urls = []
@ -962,7 +982,7 @@ class Coder:
if self.chat_language: if self.chat_language:
language = self.chat_language language = self.chat_language
else: else:
language = "in the same language they are using" language = "the same language they are using"
prompt = prompt.format( prompt = prompt.format(
fence=self.fence, fence=self.fence,
@ -1059,7 +1079,7 @@ class Coder:
max_input_tokens = self.main_model.info.get("max_input_tokens") or 0 max_input_tokens = self.main_model.info.get("max_input_tokens") or 0
# Add the reminder prompt if we still have room to include it. # Add the reminder prompt if we still have room to include it.
if ( if (
max_input_tokens is None not max_input_tokens
or total_tokens < max_input_tokens or total_tokens < max_input_tokens
and self.gpt_prompts.system_reminder and self.gpt_prompts.system_reminder
): ):
@ -1405,9 +1425,18 @@ class Coder:
addable_rel_fnames = self.get_addable_relative_files() addable_rel_fnames = self.get_addable_relative_files()
# Get basenames of files already in chat or read-only
existing_basenames = {os.path.basename(f) for f in self.get_inchat_relative_files()} | {
os.path.basename(self.get_rel_fname(f)) for f in self.abs_read_only_fnames
}
mentioned_rel_fnames = set() mentioned_rel_fnames = set()
fname_to_rel_fnames = {} fname_to_rel_fnames = {}
for rel_fname in addable_rel_fnames: for rel_fname in addable_rel_fnames:
# Skip files that share a basename with files already in chat
if os.path.basename(rel_fname) in existing_basenames:
continue
normalized_rel_fname = rel_fname.replace("\\", "/") normalized_rel_fname = rel_fname.replace("\\", "/")
normalized_words = set(word.replace("\\", "/") for word in words) normalized_words = set(word.replace("\\", "/") for word in words)
if normalized_rel_fname in normalized_words: if normalized_rel_fname in normalized_words:
@ -2061,9 +2090,10 @@ class Coder:
if output: if output:
accumulated_output += f"Output from {command}\n{output}\n" accumulated_output += f"Output from {command}\n{output}\n"
if accumulated_output.strip() and not self.io.confirm_ask( if accumulated_output.strip() and self.io.confirm_ask(
"Add command output to the chat?", allow_never=True "Add command output to the chat?", allow_never=True
): ):
accumulated_output = "" num_lines = len(accumulated_output.strip().splitlines())
line_plural = "line" if num_lines == 1 else "lines"
return accumulated_output self.io.tool_output(f"Added {num_lines} {line_plural} of output to the chat.")
return accumulated_output

View file

@ -14,6 +14,7 @@ from prompt_toolkit.completion import Completion, PathCompleter
from prompt_toolkit.document import Document from prompt_toolkit.document import Document
from aider import models, prompts, voice from aider import models, prompts, voice
from aider.editor import pipe_editor
from aider.format_settings import format_settings from aider.format_settings import format_settings
from aider.help import Help, install_help_extra from aider.help import Help, install_help_extra
from aider.llm import litellm from aider.llm import litellm
@ -45,7 +46,15 @@ class Commands:
) )
def __init__( def __init__(
self, io, coder, voice_language=None, verify_ssl=True, args=None, parser=None, verbose=False self,
io,
coder,
voice_language=None,
verify_ssl=True,
args=None,
parser=None,
verbose=False,
editor=None,
): ):
self.io = io self.io = io
self.coder = coder self.coder = coder
@ -60,6 +69,7 @@ class Commands:
self.voice_language = voice_language self.voice_language = voice_language
self.help = None self.help = None
self.editor = editor
def cmd_model(self, args): def cmd_model(self, args):
"Switch to a new LLM" "Switch to a new LLM"
@ -868,7 +878,6 @@ class Commands:
exit_status, combined_output = run_cmd( exit_status, combined_output = run_cmd(
args, verbose=self.verbose, error_print=self.io.tool_error args, verbose=self.verbose, error_print=self.io.tool_error
) )
instructions = None
if combined_output is None: if combined_output is None:
return return
@ -876,36 +885,22 @@ class Commands:
if add_on_nonzero_exit: if add_on_nonzero_exit:
add = exit_status != 0 add = exit_status != 0
else: else:
self.io.tool_output() add = self.io.confirm_ask("Add command output to the chat?")
response = self.io.prompt_ask(
"Add the output to the chat?\n(Y)es/(n)o/message with instructions:",
).strip()
self.io.tool_output()
if response.lower() in ["yes", "y"]:
add = True
elif response.lower() in ["no", "n"]:
add = False
else:
add = True
instructions = response
if response.strip():
self.io.user_input(response, log_only=True)
self.io.add_to_input_history(response)
if add: if add:
for line in combined_output.splitlines(): num_lines = len(combined_output.strip().splitlines())
self.io.tool_output(line, log_only=True) line_plural = "line" if num_lines == 1 else "lines"
self.io.tool_output(f"Added {num_lines} {line_plural} of output to the chat.")
msg = prompts.run_output.format( msg = prompts.run_output.format(
command=args, command=args,
output=combined_output, output=combined_output,
) )
if instructions: self.coder.cur_messages += [
msg = instructions + "\n\n" + msg dict(role="user", content=msg),
dict(role="assistant", content="Ok."),
return msg ]
def cmd_exit(self, args): def cmd_exit(self, args):
"Exit the application" "Exit the application"
@ -1366,6 +1361,13 @@ class Commands:
report_github_issue(issue_text, title=title, confirm=False) report_github_issue(issue_text, title=title, confirm=False)
def cmd_editor(self, initial_content=""):
"Open an editor to write a prompt"
user_input = pipe_editor(initial_content, suffix="md", editor=self.editor)
if user_input.strip():
self.io.set_placeholder(user_input.rstrip())
def expand_subdir(file_path): def expand_subdir(file_path):
if file_path.is_file(): if file_path.is_file():

View file

@ -50,7 +50,6 @@ def diff_partial_update(lines_orig, lines_updated, final=False, fname=None):
# dump(lines_orig) # dump(lines_orig)
# dump(lines_updated) # dump(lines_updated)
assert_newlines(lines_orig)
assert_newlines(lines_orig) assert_newlines(lines_orig)
num_orig_lines = len(lines_orig) num_orig_lines = len(lines_orig)

146
aider/editor.py Normal file
View file

@ -0,0 +1,146 @@
"""
Editor module for handling system text editor interactions.
This module provides functionality to:
- Discover and launch the system's configured text editor
- Create and manage temporary files for editing
- Handle editor preferences from environment variables
- Support cross-platform editor operations
"""
import os
import platform
import shlex
import subprocess
import tempfile
from rich.console import Console
DEFAULT_EDITOR_NIX = "vi"
DEFAULT_EDITOR_OS_X = "vim"
DEFAULT_EDITOR_WINDOWS = "notepad"
console = Console()
def print_status_message(success, message, style=None):
"""
Print a status message with appropriate styling.
:param success: Whether the operation was successful
:param message: The message to display
:param style: Optional style override. If None, uses green for success and red for failure
"""
if style is None:
style = "bold green" if success else "bold red"
console.print(message, style=style)
print("")
def write_temp_file(
input_data="",
suffix=None,
prefix=None,
dir=None,
):
"""
Create a temporary file with the given input data.
:param input_data: Content to write to the temporary file
:param suffix: Optional file extension (without the dot)
:param prefix: Optional prefix for the temporary filename
:param dir: Optional directory to create the file in
:return: Path to the created temporary file
:raises: OSError if file creation or writing fails
"""
kwargs = {"prefix": prefix, "dir": dir}
if suffix:
kwargs["suffix"] = f".{suffix}"
fd, filepath = tempfile.mkstemp(**kwargs)
try:
with os.fdopen(fd, "w") as f:
f.write(input_data)
except Exception:
os.close(fd)
raise
return filepath
def get_environment_editor(default=None):
"""
Fetches the preferred editor from the environment variables.
This function checks the following environment variables in order to
determine the user's preferred editor:
- VISUAL
- EDITOR
:param default: The default editor to return if no environment variable is set.
:type default: str or None
:return: The preferred editor as specified by environment variables or the default value.
:rtype: str or None
"""
editor = os.environ.get("VISUAL", os.environ.get("EDITOR", default))
return editor
def discover_editor(editor_override=None):
"""
Discovers and returns the appropriate editor command as a list of arguments.
Handles cases where the editor command includes arguments, including quoted arguments
with spaces (e.g. 'vim -c "set noswapfile"').
:return: A list of command parts ready for subprocess execution
:rtype: list[str]
"""
system = platform.system()
if system == "Windows":
default_editor = DEFAULT_EDITOR_WINDOWS
elif system == "Darwin":
default_editor = DEFAULT_EDITOR_OS_X
else:
default_editor = DEFAULT_EDITOR_NIX
if editor_override:
editor = editor_override
else:
editor = get_environment_editor(default_editor)
try:
return shlex.split(editor)
except ValueError as e:
raise RuntimeError(f"Invalid editor command format '{editor}': {e}")
def pipe_editor(input_data="", suffix=None, editor=None):
"""
Opens the system editor with optional input data and returns the edited content.
This function creates a temporary file with the provided input data, opens it in
the system editor, waits for the user to make changes and close the editor, then
reads and returns the modified content. The temporary file is deleted afterwards.
:param input_data: Initial content to populate the editor with
:type input_data: str
:param suffix: Optional file extension for the temporary file (e.g. '.txt', '.md')
:type suffix: str or None
:return: The edited content after the editor is closed
:rtype: str
"""
filepath = write_temp_file(input_data, suffix)
command_parts = discover_editor(editor)
command_parts.append(filepath)
subprocess.call(command_parts)
with open(filepath, "r") as f:
output_data = f.read()
try:
os.remove(filepath)
except PermissionError:
print_status_message(
False,
(
f"WARNING: Unable to delete temporary file {filepath!r}. You may need to delete it"
" manually."
),
)
return output_data

View file

@ -40,6 +40,11 @@ EXCEPTIONS = [
ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."), ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."),
ExInfo("UnprocessableEntityError", True, None), ExInfo("UnprocessableEntityError", True, None),
ExInfo("UnsupportedParamsError", True, None), ExInfo("UnsupportedParamsError", True, None),
ExInfo(
"Timeout",
True,
"The API provider timed out without returning a response. They may be down or overloaded.",
),
] ]

View file

@ -7,4 +7,5 @@ exclude_website_pats = [
"docs/unified-diffs.md", "docs/unified-diffs.md",
"docs/leaderboards/index.md", "docs/leaderboards/index.md",
"assets/**", "assets/**",
"**/.DS_Store",
] ]

View file

@ -1,6 +1,7 @@
import base64 import base64
import os import os
import threading import threading
import time
import webbrowser import webbrowser
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
@ -199,6 +200,7 @@ class InputOutput:
editingmode=EditingMode.EMACS, editingmode=EditingMode.EMACS,
fancy_input=True, fancy_input=True,
): ):
self.placeholder = None
self.never_prompts = set() self.never_prompts = set()
self.interrupted_partial_input = None self.interrupted_partial_input = None
self.editingmode = editingmode self.editingmode = editingmode
@ -336,14 +338,36 @@ class InputOutput:
self.tool_error("Use --encoding to set the unicode encoding.") self.tool_error("Use --encoding to set the unicode encoding.")
return return
def write_text(self, filename, content): def write_text(self, filename, content, max_retries=5, initial_delay=0.1):
"""
Writes content to a file, retrying with progressive backoff if the file is locked.
:param filename: Path to the file to write.
:param content: Content to write to the file.
:param max_retries: Maximum number of retries if a file lock is encountered.
:param initial_delay: Initial delay (in seconds) before the first retry.
"""
if self.dry_run: if self.dry_run:
return return
try:
with open(str(filename), "w", encoding=self.encoding) as f: delay = initial_delay
f.write(content) for attempt in range(max_retries):
except OSError as err: try:
self.tool_error(f"Unable to write file {filename}: {err}") with open(str(filename), "w", encoding=self.encoding) as f:
f.write(content)
return # Successfully wrote the file
except PermissionError as err:
if attempt < max_retries - 1:
time.sleep(delay)
delay *= 2 # Exponential backoff
else:
self.tool_error(
f"Unable to write file {filename} after {max_retries} attempts: {err}"
)
raise
except OSError as err:
self.tool_error(f"Unable to write file {filename}: {err}")
raise
def rule(self): def rule(self):
if self.pretty: if self.pretty:
@ -434,30 +458,33 @@ class InputOutput:
show = ". " show = ". "
try: try:
try: if self.prompt_session:
if self.prompt_session: # Use placeholder if set, then clear it
line = self.prompt_session.prompt( default = self.placeholder or ""
show, self.placeholder = None
completer=completer_instance,
reserve_space_for_menu=4,
complete_style=CompleteStyle.MULTI_COLUMN,
style=style,
key_bindings=kb,
)
else:
line = input(show)
# Check if we were interrupted by a file change line = self.prompt_session.prompt(
if self.changed_files: show,
res = process_file_changes(self.changed_files) default=default,
self.changed_files = None completer=completer_instance,
return res reserve_space_for_menu=4,
complete_style=CompleteStyle.MULTI_COLUMN,
style=style,
key_bindings=kb,
)
else:
line = input(show)
except EOFError: # Check if we were interrupted by a file change
return "" if self.changed_files:
except Exception as err: res = process_file_changes(self.changed_files)
dump(err) self.changed_files = None
return res
except EOFError:
return ""
except Exception as err:
dump(err)
except UnicodeEncodeError as err: except UnicodeEncodeError as err:
self.tool_error(str(err)) self.tool_error(str(err))
return "" return ""
@ -466,13 +493,38 @@ class InputOutput:
stop_event.set() stop_event.set()
watcher.join() # Thread should exit quickly due to stop_event watcher.join() # Thread should exit quickly due to stop_event
if line and line[0] == "{" and not multiline_input: if line.strip("\r\n") and not multiline_input:
multiline_input = True stripped = line.strip("\r\n")
inp += line[1:] + "\n" if stripped == "{":
multiline_input = True
multiline_tag = None
inp += ""
elif stripped[0] == "{":
# Extract tag if it exists (only alphanumeric chars)
tag = "".join(c for c in stripped[1:] if c.isalnum())
if stripped == "{" + tag:
multiline_input = True
multiline_tag = tag
inp += ""
else:
inp = line
break
else:
inp = line
break
continue continue
elif line and line[-1] == "}" and multiline_input: elif multiline_input and line.strip():
inp += line[:-1] + "\n" if multiline_tag:
break # Check if line is exactly "tag}"
if line.strip("\r\n") == f"{multiline_tag}}}":
break
else:
inp += line + "\n"
# Check if line is exactly "}"
elif line.strip("\r\n") == "}":
break
else:
inp += line + "\n"
elif multiline_input: elif multiline_input:
inp += line + "\n" inp += line + "\n"
else: else:
@ -488,8 +540,8 @@ class InputOutput:
return return
FileHistory(self.input_history_file).append_string(inp) FileHistory(self.input_history_file).append_string(inp)
# Also add to the in-memory history if it exists # Also add to the in-memory history if it exists
if hasattr(self, "session") and hasattr(self.session, "history"): if self.prompt_session and self.prompt_session.history:
self.session.history.append_string(inp) self.prompt_session.history.append_string(inp)
def get_input_history(self): def get_input_history(self):
if not self.input_history_file: if not self.input_history_file:
@ -506,14 +558,17 @@ class InputOutput:
log_file.write(f"{role.upper()} {timestamp}\n") log_file.write(f"{role.upper()} {timestamp}\n")
log_file.write(content + "\n") log_file.write(content + "\n")
def display_user_input(self, inp):
if self.pretty and self.user_input_color:
style = dict(style=self.user_input_color)
else:
style = dict()
self.console.print(Text(inp), **style)
def user_input(self, inp, log_only=True): def user_input(self, inp, log_only=True):
if not log_only: if not log_only:
if self.pretty and self.user_input_color: self.display_user_input(inp)
style = dict(style=self.user_input_color)
else:
style = dict()
self.console.print(Text(inp), **style)
prefix = "####" prefix = "####"
if inp: if inp:
@ -533,11 +588,11 @@ class InputOutput:
hist = "\n" + content.strip() + "\n\n" hist = "\n" + content.strip() + "\n\n"
self.append_chat_history(hist) self.append_chat_history(hist)
def offer_url(self, url, prompt="Open URL for more info?"): def offer_url(self, url, prompt="Open URL for more info?", allow_never=True):
"""Offer to open a URL in the browser, returns True if opened.""" """Offer to open a URL in the browser, returns True if opened."""
if url in self.never_prompts: if url in self.never_prompts:
return False return False
if self.confirm_ask(prompt, subject=url, allow_never=True): if self.confirm_ask(prompt, subject=url, allow_never=allow_never):
webbrowser.open(url) webbrowser.open(url)
return True return True
return False return False
@ -736,6 +791,10 @@ class InputOutput:
self.console.print(show_resp) self.console.print(show_resp)
def set_placeholder(self, placeholder):
"""Set a one-time placeholder text for the next input prompt."""
self.placeholder = placeholder
def print(self, message=""): def print(self, message=""):
print(message) print(message)

View file

@ -5,6 +5,8 @@ import re
import sys import sys
import threading import threading
import traceback import traceback
import webbrowser
from dataclasses import fields
from pathlib import Path from pathlib import Path
import git import git
@ -16,11 +18,13 @@ from aider import __version__, models, urls, utils
from aider.analytics import Analytics from aider.analytics import Analytics
from aider.args import get_parser from aider.args import get_parser
from aider.coders import Coder from aider.coders import Coder
from aider.coders.base_coder import UnknownEditFormat
from aider.commands import Commands, SwitchCoder from aider.commands import Commands, SwitchCoder
from aider.format_settings import format_settings, scrub_sensitive_info from aider.format_settings import format_settings, scrub_sensitive_info
from aider.history import ChatSummary from aider.history import ChatSummary
from aider.io import InputOutput from aider.io import InputOutput
from aider.llm import litellm # noqa: F401; properly init litellm on launch from aider.llm import litellm # noqa: F401; properly init litellm on launch
from aider.models import ModelSettings
from aider.repo import ANY_GIT_ERROR, GitRepo from aider.repo import ANY_GIT_ERROR, GitRepo
from aider.report import report_uncaught_exceptions from aider.report import report_uncaught_exceptions
from aider.versioncheck import check_version, install_from_main_branch, install_upgrade from aider.versioncheck import check_version, install_from_main_branch, install_upgrade
@ -332,14 +336,16 @@ def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"):
def register_litellm_models(git_root, model_metadata_fname, io, verbose=False): def register_litellm_models(git_root, model_metadata_fname, io, verbose=False):
model_metatdata_files = generate_search_path_list( model_metatdata_files = []
".aider.model.metadata.json", git_root, model_metadata_fname
)
# Add the resource file path # Add the resource file path
resource_metadata = importlib_resources.files("aider.resources").joinpath("model-metadata.json") resource_metadata = importlib_resources.files("aider.resources").joinpath("model-metadata.json")
model_metatdata_files.append(str(resource_metadata)) model_metatdata_files.append(str(resource_metadata))
model_metatdata_files += generate_search_path_list(
".aider.model.metadata.json", git_root, model_metadata_fname
)
try: try:
model_metadata_files_loaded = models.register_litellm_models(model_metatdata_files) model_metadata_files_loaded = models.register_litellm_models(model_metatdata_files)
if len(model_metadata_files_loaded) > 0 and verbose: if len(model_metadata_files_loaded) > 0 and verbose:
@ -503,8 +509,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
io.tool_warning("Terminal does not support pretty output (UnicodeDecodeError)") io.tool_warning("Terminal does not support pretty output (UnicodeDecodeError)")
analytics = Analytics(logfile=args.analytics_log, permanently_disable=args.analytics_disable) analytics = Analytics(logfile=args.analytics_log, permanently_disable=args.analytics_disable)
if args.analytics: if args.analytics is not False:
if analytics.need_to_ask(): if analytics.need_to_ask(args.analytics):
io.tool_output( io.tool_output(
"Aider respects your privacy and never collects your code, chat messages, keys or" "Aider respects your privacy and never collects your code, chat messages, keys or"
" personal info." " personal info."
@ -540,7 +546,14 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
all_files = args.files + (args.file or []) all_files = args.files + (args.file or [])
fnames = [str(Path(fn).resolve()) for fn in all_files] fnames = [str(Path(fn).resolve()) for fn in all_files]
read_only_fnames = [str(Path(fn).resolve()) for fn in (args.read or [])] read_only_fnames = []
for fn in args.read or []:
path = Path(fn).resolve()
if path.is_dir():
read_only_fnames.extend(str(f) for f in path.rglob("*") if f.is_file())
else:
read_only_fnames.append(str(path))
if len(all_files) > 1: if len(all_files) > 1:
good = True good = True
for fname in all_files: for fname in all_files:
@ -603,7 +616,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
cmd_line = scrub_sensitive_info(args, cmd_line) cmd_line = scrub_sensitive_info(args, cmd_line)
io.tool_output(cmd_line, log_only=True) io.tool_output(cmd_line, log_only=True)
check_and_load_imports(io, verbose=args.verbose) is_first_run = is_first_run_of_new_version(io, verbose=args.verbose)
check_and_load_imports(io, is_first_run, verbose=args.verbose)
if args.anthropic_api_key: if args.anthropic_api_key:
os.environ["ANTHROPIC_API_KEY"] = args.anthropic_api_key os.environ["ANTHROPIC_API_KEY"] = args.anthropic_api_key
@ -622,6 +636,18 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
register_models(git_root, args.model_settings_file, io, verbose=args.verbose) register_models(git_root, args.model_settings_file, io, verbose=args.verbose)
register_litellm_models(git_root, args.model_metadata_file, io, verbose=args.verbose) register_litellm_models(git_root, args.model_metadata_file, io, verbose=args.verbose)
# Process any command line aliases
if args.alias:
for alias_def in args.alias:
# Split on first colon only
parts = alias_def.split(":", 1)
if len(parts) != 2:
io.tool_error(f"Invalid alias format: {alias_def}")
io.tool_output("Format should be: alias:model-name")
return 1
alias, model = parts
models.MODEL_ALIASES[alias.strip()] = model.strip()
if not args.model: if not args.model:
args.model = "gpt-4o-2024-08-06" args.model = "gpt-4o-2024-08-06"
if os.environ.get("ANTHROPIC_API_KEY"): if os.environ.get("ANTHROPIC_API_KEY"):
@ -635,9 +661,15 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
) )
if args.verbose: if args.verbose:
io.tool_output("Model info:") io.tool_output("Model metadata:")
io.tool_output(json.dumps(main_model.info, indent=4)) io.tool_output(json.dumps(main_model.info, indent=4))
io.tool_output("Model settings:")
for attr in sorted(fields(ModelSettings), key=lambda x: x.name):
val = getattr(main_model, attr.name)
val = json.dumps(val, indent=4)
io.tool_output(f"{attr.name}: {val}")
lint_cmds = parse_lint_cmds(args.lint_cmd, io) lint_cmds = parse_lint_cmds(args.lint_cmd, io)
if lint_cmds is None: if lint_cmds is None:
return 1 return 1
@ -678,7 +710,13 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
return 1 return 1
commands = Commands( commands = Commands(
io, None, verify_ssl=args.verify_ssl, args=args, parser=parser, verbose=args.verbose io,
None,
verify_ssl=args.verify_ssl,
args=args,
parser=parser,
verbose=args.verbose,
editor=args.editor,
) )
summarizer = ChatSummary( summarizer = ChatSummary(
@ -726,7 +764,12 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
num_cache_warming_pings=args.cache_keepalive_pings, num_cache_warming_pings=args.cache_keepalive_pings,
suggest_shell_commands=args.suggest_shell_commands, suggest_shell_commands=args.suggest_shell_commands,
chat_language=args.chat_language, chat_language=args.chat_language,
detect_urls=args.detect_urls,
) )
except UnknownEditFormat as err:
io.tool_error(str(err))
io.offer_url(urls.edit_formats, "Open documentation about edit formats?")
return 1
except ValueError as err: except ValueError as err:
io.tool_error(str(err)) io.tool_error(str(err))
return 1 return 1
@ -788,6 +831,18 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
io.tool_output('Use /help <question> for help, run "aider --help" to see cmd line args') io.tool_output('Use /help <question> for help, run "aider --help" to see cmd line args')
if args.show_release_notes is True:
io.tool_output(f"Opening release notes: {urls.release_notes}")
io.tool_output()
webbrowser.open(urls.release_notes)
elif args.show_release_notes is None and is_first_run:
io.tool_output()
io.offer_url(
urls.release_notes,
"Would you like to see what's new in this version?",
allow_never=False,
)
if git_root and Path.cwd().resolve() != Path(git_root).resolve(): if git_root and Path.cwd().resolve() != Path(git_root).resolve():
io.tool_warning( io.tool_warning(
"Note: in-chat filenames are always relative to the git working dir, not the current" "Note: in-chat filenames are always relative to the git working dir, not the current"
@ -843,7 +898,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
coder.show_announcements() coder.show_announcements()
def check_and_load_imports(io, verbose=False): def is_first_run_of_new_version(io, verbose=False):
"""Check if this is the first run of a new version/executable combination"""
installs_file = Path.home() / ".aider" / "installs.json" installs_file = Path.home() / ".aider" / "installs.json"
key = (__version__, sys.executable) key = (__version__, sys.executable)
@ -864,7 +920,26 @@ def check_and_load_imports(io, verbose=False):
if verbose: if verbose:
io.tool_output("Installs file does not exist, creating new dictionary") io.tool_output("Installs file does not exist, creating new dictionary")
if str(key) not in installs: is_first_run = str(key) not in installs
if is_first_run:
installs[str(key)] = True
installs_file.parent.mkdir(parents=True, exist_ok=True)
with open(installs_file, "w") as f:
json.dump(installs, f, indent=4)
return is_first_run
except Exception as e:
io.tool_warning(f"Error checking version: {e}")
if verbose:
io.tool_output(f"Full exception details: {traceback.format_exc()}")
return True # Safer to assume it's a first run if we hit an error
def check_and_load_imports(io, is_first_run, verbose=False):
try:
if is_first_run:
if verbose: if verbose:
io.tool_output( io.tool_output(
"First run for this version and executable, loading imports synchronously" "First run for this version and executable, loading imports synchronously"
@ -875,13 +950,8 @@ def check_and_load_imports(io, verbose=False):
io.tool_error(str(err)) io.tool_error(str(err))
io.tool_output("Error loading required imports. Did you install aider properly?") io.tool_output("Error loading required imports. Did you install aider properly?")
io.offer_url(urls.install_properly, "Open documentation url for more info?") io.offer_url(urls.install_properly, "Open documentation url for more info?")
sys.exit(1) sys.exit(1)
installs[str(key)] = True
installs_file.parent.mkdir(parents=True, exist_ok=True)
with open(installs_file, "w") as f:
json.dump(installs, f, indent=4)
if verbose: if verbose:
io.tool_output("Imports loaded and installs file updated") io.tool_output("Imports loaded and installs file updated")
else: else:
@ -890,8 +960,9 @@ def check_and_load_imports(io, verbose=False):
thread = threading.Thread(target=load_slow_imports) thread = threading.Thread(target=load_slow_imports)
thread.daemon = True thread.daemon = True
thread.start() thread.start()
except Exception as e: except Exception as e:
io.tool_warning(f"Error in checking imports: {e}") io.tool_warning(f"Error in loading imports: {e}")
if verbose: if verbose:
io.tool_output(f"Full exception details: {traceback.format_exc()}") io.tool_output(f"Full exception details: {traceback.format_exc()}")

View file

@ -61,6 +61,23 @@ claude-3-5-sonnet-20241022
ANTHROPIC_MODELS = [ln.strip() for ln in ANTHROPIC_MODELS.splitlines() if ln.strip()] ANTHROPIC_MODELS = [ln.strip() for ln in ANTHROPIC_MODELS.splitlines() if ln.strip()]
# Mapping of model aliases to their canonical names
MODEL_ALIASES = {
# Claude models
"sonnet": "claude-3-sonnet-20241022",
"haiku": "claude-3-haiku-20241022",
"opus": "claude-3-opus-20240229",
# GPT models
"4": "gpt-4-0613",
"4o": "gpt-4o-2024-08-06",
"4-turbo": "gpt-4-1106-preview",
"35turbo": "gpt-3.5-turbo",
"35-turbo": "gpt-3.5-turbo",
"3": "gpt-3.5-turbo",
# Other models
"deepseek": "deepseek/deepseek-coder",
}
@dataclass @dataclass
class ModelSettings: class ModelSettings:
@ -161,6 +178,22 @@ MODEL_SETTINGS = [
lazy=True, lazy=True,
reminder="sys", reminder="sys",
), ),
ModelSettings(
"gpt-4o-2024-11-20",
"diff",
weak_model_name="gpt-4o-mini",
use_repo_map=True,
lazy=True,
reminder="sys",
),
ModelSettings(
"openai/gpt-4o-2024-11-20",
"diff",
weak_model_name="gpt-4o-mini",
use_repo_map=True,
lazy=True,
reminder="sys",
),
ModelSettings( ModelSettings(
"gpt-4o", "gpt-4o",
"diff", "diff",
@ -629,7 +662,6 @@ MODEL_SETTINGS = [
reminder="user", reminder="user",
use_system_prompt=False, use_system_prompt=False,
use_temperature=False, use_temperature=False,
streaming=False,
), ),
ModelSettings( ModelSettings(
"azure/o1-mini", "azure/o1-mini",
@ -641,7 +673,6 @@ MODEL_SETTINGS = [
reminder="user", reminder="user",
use_system_prompt=False, use_system_prompt=False,
use_temperature=False, use_temperature=False,
streaming=False,
), ),
ModelSettings( ModelSettings(
"o1-mini", "o1-mini",
@ -653,7 +684,6 @@ MODEL_SETTINGS = [
reminder="user", reminder="user",
use_system_prompt=False, use_system_prompt=False,
use_temperature=False, use_temperature=False,
streaming=False,
), ),
ModelSettings( ModelSettings(
"openai/o1-preview", "openai/o1-preview",
@ -665,7 +695,6 @@ MODEL_SETTINGS = [
reminder="user", reminder="user",
use_system_prompt=False, use_system_prompt=False,
use_temperature=False, use_temperature=False,
streaming=False,
), ),
ModelSettings( ModelSettings(
"azure/o1-preview", "azure/o1-preview",
@ -677,7 +706,6 @@ MODEL_SETTINGS = [
reminder="user", reminder="user",
use_system_prompt=False, use_system_prompt=False,
use_temperature=False, use_temperature=False,
streaming=False,
), ),
ModelSettings( ModelSettings(
"o1-preview", "o1-preview",
@ -689,7 +717,6 @@ MODEL_SETTINGS = [
reminder="user", reminder="user",
use_system_prompt=False, use_system_prompt=False,
use_temperature=False, use_temperature=False,
streaming=False,
), ),
ModelSettings( ModelSettings(
"openrouter/openai/o1-mini", "openrouter/openai/o1-mini",
@ -762,6 +789,11 @@ class ModelInfoManager:
pass pass
except Exception as ex: except Exception as ex:
print(str(ex)) print(str(ex))
try:
# Save empty dict to cache file on failure
self.cache_file.write_text("{}")
except OSError:
pass
def get_model_from_cached_json_db(self, model): def get_model_from_cached_json_db(self, model):
if not self.content: if not self.content:
@ -804,11 +836,17 @@ model_info_manager = ModelInfoManager()
class Model(ModelSettings): class Model(ModelSettings):
def __init__(self, model, weak_model=None, editor_model=None, editor_edit_format=None): def __init__(self, model, weak_model=None, editor_model=None, editor_edit_format=None):
self.name = model # Map any alias to its canonical name
self.name = MODEL_ALIASES.get(model, model)
self.max_chat_history_tokens = 1024 self.max_chat_history_tokens = 1024
self.weak_model = None self.weak_model = None
self.editor_model = None self.editor_model = None
# Find the extra settings
self.extra_model_settings = next(
(ms for ms in MODEL_SETTINGS if ms.name == "aider/extra_params"), None
)
self.info = self.get_model_info(model) self.info = self.get_model_info(model)
# Are all needed keys/params available? # Are all needed keys/params available?
@ -836,17 +874,44 @@ class Model(ModelSettings):
def get_model_info(self, model): def get_model_info(self, model):
return model_info_manager.get_model_info(model) return model_info_manager.get_model_info(model)
def _copy_fields(self, source):
"""Helper to copy fields from a ModelSettings instance to self"""
for field in fields(ModelSettings):
val = getattr(source, field.name)
setattr(self, field.name, val)
def configure_model_settings(self, model): def configure_model_settings(self, model):
# Look for exact model match
exact_match = False
for ms in MODEL_SETTINGS: for ms in MODEL_SETTINGS:
# direct match, or match "provider/<model>" # direct match, or match "provider/<model>"
if model == ms.name: if model == ms.name:
for field in fields(ModelSettings): self._copy_fields(ms)
val = getattr(ms, field.name) exact_match = True
setattr(self, field.name, val) break # Continue to apply overrides
return # <--
model = model.lower() model = model.lower()
# If no exact match, try generic settings
if not exact_match:
self.apply_generic_model_settings(model)
# Apply override settings last if they exist
if self.extra_model_settings and self.extra_model_settings.extra_params:
# Initialize extra_params if it doesn't exist
if not self.extra_params:
self.extra_params = {}
# Deep merge the extra_params dicts
for key, value in self.extra_model_settings.extra_params.items():
if isinstance(value, dict) and isinstance(self.extra_params.get(key), dict):
# For nested dicts, merge recursively
self.extra_params[key] = {**self.extra_params[key], **value}
else:
# For non-dict values, simply update
self.extra_params[key] = value
def apply_generic_model_settings(self, model):
if ("llama3" in model or "llama-3" in model) and "70b" in model: if ("llama3" in model or "llama-3" in model) and "70b" in model:
self.edit_format = "diff" self.edit_format = "diff"
self.use_repo_map = True self.use_repo_map = True
@ -868,17 +933,19 @@ class Model(ModelSettings):
if "gpt-3.5" in model or "gpt-4" in model: if "gpt-3.5" in model or "gpt-4" in model:
self.reminder = "sys" self.reminder = "sys"
return # <--
if "3.5-sonnet" in model or "3-5-sonnet" in model: if "3.5-sonnet" in model or "3-5-sonnet" in model:
self.edit_format = "diff" self.edit_format = "diff"
self.use_repo_map = True self.use_repo_map = True
self.examples_as_sys_msg = True self.examples_as_sys_msg = True
self.reminder = "user" self.reminder = "user"
return # <--
if model.startswith("o1-") or "/o1-" in model: if model.startswith("o1-") or "/o1-" in model:
self.use_system_prompt = False self.use_system_prompt = False
self.use_temperature = False self.use_temperature = False
self.streaming = False return # <--
if ( if (
"qwen" in model "qwen" in model
@ -886,14 +953,17 @@ class Model(ModelSettings):
and ("2.5" in model or "2-5" in model) and ("2.5" in model or "2-5" in model)
and "32b" in model and "32b" in model
): ):
"openrouter/qwen/qwen-2.5-coder-32b-instruct",
self.edit_format = "diff" self.edit_format = "diff"
self.editor_edit_format = "editor-diff" self.editor_edit_format = "editor-diff"
self.use_repo_map = True self.use_repo_map = True
if model.startswith("ollama/") or model.startswith("ollama_chat/"):
self.extra_params = dict(num_ctx=8 * 1024)
return # <--
# use the defaults # use the defaults
if self.edit_format == "diff": if self.edit_format == "diff":
self.use_repo_map = True self.use_repo_map = True
return # <--
def __str__(self): def __str__(self):
return self.name return self.name
@ -1050,6 +1120,9 @@ def register_models(model_settings_fnames):
if not os.path.exists(model_settings_fname): if not os.path.exists(model_settings_fname):
continue continue
if not Path(model_settings_fname).read_text().strip():
continue
try: try:
with open(model_settings_fname, "r") as model_settings_file: with open(model_settings_fname, "r") as model_settings_file:
model_settings_list = yaml.safe_load(model_settings_file) model_settings_list = yaml.safe_load(model_settings_file)
@ -1167,7 +1240,10 @@ def fuzzy_match_models(name):
model = model.lower() model = model.lower()
if attrs.get("mode") != "chat": if attrs.get("mode") != "chat":
continue continue
provider = (attrs["litellm_provider"] + "/").lower() provider = attrs.get("litellm_provider", "").lower()
if not provider:
continue
provider += "/"
if model.startswith(provider): if model.startswith(provider):
fq_model = model fq_model = model

View file

@ -0,0 +1,91 @@
(class_definition
name: (identifier) @name.definition.class) @definition.class
(method_signature
(function_signature)) @definition.method
(type_alias
(type_identifier) @name.definition.type) @definition.type
(method_signature
(getter_signature
name: (identifier) @name.definition.method)) @definition.method
(method_signature
(setter_signature
name: (identifier) @name.definition.method)) @definition.method
(method_signature
(function_signature
name: (identifier) @name.definition.method)) @definition.method
(method_signature
(factory_constructor_signature
(identifier) @name.definition.method)) @definition.method
(method_signature
(constructor_signature
name: (identifier) @name.definition.method)) @definition.method
(method_signature
(operator_signature)) @definition.method
(method_signature) @definition.method
(mixin_declaration
(mixin)
(identifier) @name.definition.mixin) @definition.mixin
(extension_declaration
name: (identifier) @name.definition.extension) @definition.extension
(enum_declaration
name: (identifier) @name.definition.enum) @definition.enum
(function_signature
name: (identifier) @name.definition.function) @definition.function
(new_expression
(type_identifier) @name.reference.class) @reference.class
(initialized_variable_definition
name: (identifier)
value: (identifier) @name.reference.class
value: (selector
"!"?
(argument_part
(arguments
(argument)*))?)?) @reference.class
(assignment_expression
left: (assignable_expression
(identifier)
(unconditional_assignable_selector
"."
(identifier) @name.reference.call))) @reference.call
(assignment_expression
left: (assignable_expression
(identifier)
(conditional_assignable_selector
"?."
(identifier) @name.reference.call))) @reference.call
((identifier) @name
(selector
"!"?
(conditional_assignable_selector
"?." (identifier) @name.reference.call)?
(unconditional_assignable_selector
"."? (identifier) @name.reference.call)?
(argument_part
(arguments
(argument)*))?)*
(cascade_section
(cascade_selector
(identifier)) @name.reference.call
(argument_part
(arguments
(argument)*))?)?) @reference.call

View file

@ -334,8 +334,11 @@ class GitRepo:
def git_ignored_file(self, path): def git_ignored_file(self, path):
if not self.repo: if not self.repo:
return return
if self.repo.ignored(path): try:
return True if self.repo.ignored(path):
return True
except ANY_GIT_ERROR:
return False
def ignored_file(self, fname): def ignored_file(self, fname):
self.refresh_aider_ignore() self.refresh_aider_ignore()

View file

@ -1,11 +0,0 @@
{
"openrouter/qwen/qwen-2.5-coder-32b-instruct": {
"max_tokens": 33792,
"max_input_tokens": 33792,
"max_output_tokens": 33792,
"input_cost_per_token": 0.00000018,
"output_cost_per_token": 0.00000018,
"litellm_provider": "openrouter",
"mode": "chat",
},
}

View file

@ -12,3 +12,5 @@ github_issues = "https://github.com/Aider-AI/aider/issues/new"
git_index_version = "https://github.com/Aider-AI/aider/issues/211" git_index_version = "https://github.com/Aider-AI/aider/issues/211"
install_properly = "https://aider.chat/docs/troubleshooting/imports.html" install_properly = "https://aider.chat/docs/troubleshooting/imports.html"
analytics = "https://aider.chat/docs/more/analytics.html" analytics = "https://aider.chat/docs/more/analytics.html"
release_notes = "https://aider.chat/HISTORY.html#release-notes"
edit_formats = "https://aider.chat/docs/more/edit-formats.html"

View file

@ -12,7 +12,9 @@ description: Release notes and stats on aider writing its own code.
The above The above
[stats are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed) [stats are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed)
in the aider repo. of the aider repo.
## Release notes
<!--[[[cog <!--[[[cog
# This page is a copy of HISTORY.md, adding the front matter above. # This page is a copy of HISTORY.md, adding the front matter above.
@ -22,6 +24,54 @@ cog.out(text)
]]]--> ]]]-->
### Aider v0.65.0
- Added `--alias` config to define [custom model aliases](https://aider.chat/docs/config/model-aliases.html).
- Added `--[no-]detect-urls` flag to disable detecting and offering to scrape URLs found in the chat.
- Ollama models now default to an 8k context window.
- Added [RepoMap support for Dart language](https://aider.chat/docs/languages.html) by @malkoG.
- Ask 2.5% of users if they want to opt-in to [analytics](https://aider.chat/docs/more/analytics.html).
- Skip suggesting files that share names with files already in chat.
- `/editor` returns and prefill the file content into the prompt, so you can use `/editor` to compose messages that start with `/commands`, etc.
- Enhanced error handling for analytics.
- Improved handling of UnknownEditFormat exceptions with helpful documentation links.
- Bumped dependencies to pick up grep-ast 0.4.0 for Dart language support.
- Aider wrote 81% of the code in this release.
### Aider v0.64.1
- Disable streaming for o1 on OpenRouter.
### Aider v0.64.0
- Added [`/editor` command](https://aider.chat/docs/usage/commands.html) to open system editor for writing prompts, by @thehunmonkgroup.
- Full support for `gpt-4o-2024-11-20`.
- Stream o1 models by default.
- `/run` and suggested shell commands are less mysterious and now confirm that they "Added XX lines of output to the chat."
- Ask 1% of users if they want to opt-in to [analytics](https://aider.chat/docs/more/analytics.html).
- Added support for [optional multiline input tags](https://aider.chat/docs/usage/commands.html#entering-multi-line-chat-messages) with matching closing tags.
- Improved [model settings configuration](https://aider.chat/docs/config/adv-model-settings.html#global-extra-params) with support for global `extra_params` for `litellm.completion()`.
- Architect mode now asks to add files suggested by the LLM.
- Fixed bug in fuzzy model name matching.
- Added Timeout exception to handle API provider timeouts.
- Added `--show-release-notes` to control release notes display on first run of new version.
- Save empty dict to cache file on model metadata download failure, to delay retry.
- Improved error handling and code formatting.
- Aider wrote 74% of the code in this release.
### Aider v0.63.2
- Fixed bug in fuzzy model name matching when litellm provider info is missing.
- Modified model metadata file loading to allow override of resource file.
- Allow recursive loading of dirs using `--read`.
- Updated dependency versions to pick up litellm fix for ollama models.
- Added exponential backoff retry when writing files to handle editor file locks.
- Updated Qwen 2.5 Coder 32B model configuration.
### Aider v0.63.1
- Fixed bug in git ignored file handling.
- Improved error handling for git operations.
### Aider v0.63.0 ### Aider v0.63.0

View file

@ -1801,8 +1801,8 @@
Paul Gauthier (aider): 113 Paul Gauthier (aider): 113
start_tag: v0.44.0 start_tag: v0.44.0
total_lines: 266 total_lines: 266
- aider_percentage: 47.04 - aider_percentage: 53.3
aider_total: 254 aider_total: 339
end_date: '2024-07-29' end_date: '2024-07-29'
end_tag: v0.46.0 end_tag: v0.46.0
file_counts: file_counts:
@ -1853,6 +1853,9 @@
aider/scrape.py: aider/scrape.py:
Paul Gauthier: 3 Paul Gauthier: 3
Paul Gauthier (aider): 32 Paul Gauthier (aider): 32
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 11
Paul Gauthier (aider): 85
benchmark/Dockerfile: benchmark/Dockerfile:
Your Name: 1 Your Name: 1
tests/basic/test_coder.py: tests/basic/test_coder.py:
@ -1868,13 +1871,13 @@
Paul Gauthier (aider): 73 Paul Gauthier (aider): 73
grand_total: grand_total:
Charles Joachim: 4 Charles Joachim: 4
Paul Gauthier: 209 Paul Gauthier: 220
Paul Gauthier (aider): 204 Paul Gauthier (aider): 289
Your Name: 73 Your Name: 73
Your Name (aider): 50 Your Name (aider): 50
start_tag: v0.45.0 start_tag: v0.45.0
total_lines: 540 total_lines: 636
- aider_percentage: 59.12 - aider_percentage: 57.16
aider_total: 415 aider_total: 415
end_date: '2024-07-31' end_date: '2024-07-31'
end_tag: v0.47.0 end_tag: v0.47.0
@ -1920,6 +1923,8 @@
Paul Gauthier (aider): 2 Paul Gauthier (aider): 2
aider/utils.py: aider/utils.py:
Paul Gauthier: 7 Paul Gauthier: 7
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 24
docker/Dockerfile: docker/Dockerfile:
Paul Gauthier: 19 Paul Gauthier: 19
Paul Gauthier (aider): 21 Paul Gauthier (aider): 21
@ -1944,10 +1949,10 @@
tests/basic/test_repomap.py: tests/basic/test_repomap.py:
Paul Gauthier: 1 Paul Gauthier: 1
grand_total: grand_total:
Paul Gauthier: 287 Paul Gauthier: 311
Paul Gauthier (aider): 415 Paul Gauthier (aider): 415
start_tag: v0.46.0 start_tag: v0.46.0
total_lines: 702 total_lines: 726
- aider_percentage: 44.44 - aider_percentage: 44.44
aider_total: 276 aider_total: 276
end_date: '2024-08-06' end_date: '2024-08-06'
@ -2011,8 +2016,8 @@
paul-gauthier: 1 paul-gauthier: 1
start_tag: v0.47.0 start_tag: v0.47.0
total_lines: 621 total_lines: 621
- aider_percentage: 61.52 - aider_percentage: 61.2
aider_total: 478 aider_total: 489
end_date: '2024-08-10' end_date: '2024-08-10'
end_tag: v0.49.0 end_tag: v0.49.0
file_counts: file_counts:
@ -2055,6 +2060,9 @@
aider/versioncheck.py: aider/versioncheck.py:
Paul Gauthier: 3 Paul Gauthier: 3
Paul Gauthier (aider): 11 Paul Gauthier (aider): 11
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 11
Paul Gauthier (aider): 11
docker/Dockerfile: docker/Dockerfile:
Paul Gauthier: 5 Paul Gauthier: 5
Paul Gauthier (aider): 2 Paul Gauthier (aider): 2
@ -2075,10 +2083,10 @@
Paul Gauthier: 1 Paul Gauthier: 1
Paul Gauthier (aider): 49 Paul Gauthier (aider): 49
grand_total: grand_total:
Paul Gauthier: 299 Paul Gauthier: 310
Paul Gauthier (aider): 478 Paul Gauthier (aider): 489
start_tag: v0.48.0 start_tag: v0.48.0
total_lines: 777 total_lines: 799
- aider_percentage: 66.05 - aider_percentage: 66.05
aider_total: 214 aider_total: 214
end_date: '2024-08-13' end_date: '2024-08-13'
@ -2135,8 +2143,8 @@
Paul Gauthier (aider): 201 Paul Gauthier (aider): 201
start_tag: v0.49.0 start_tag: v0.49.0
total_lines: 324 total_lines: 324
- aider_percentage: 56.25 - aider_percentage: 52.49
aider_total: 450 aider_total: 580
end_date: '2024-08-20' end_date: '2024-08-20'
end_tag: v0.51.0 end_tag: v0.51.0
file_counts: file_counts:
@ -2170,6 +2178,14 @@
Paul Gauthier: 3 Paul Gauthier: 3
aider/utils.py: aider/utils.py:
Paul Gauthier (aider): 6 Paul Gauthier (aider): 6
aider/website/_includes/code-in-json-benchmark.js:
Paul Gauthier: 101
Paul Gauthier (aider): 64
aider/website/_includes/code-in-json-syntax.js:
Paul Gauthier: 73
Paul Gauthier (aider): 66
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
benchmark/benchmark.py: benchmark/benchmark.py:
Paul Gauthier: 7 Paul Gauthier: 7
benchmark/over_time.py: benchmark/over_time.py:
@ -2188,11 +2204,11 @@
Paul Gauthier: 15 Paul Gauthier: 15
Paul Gauthier (aider): 104 Paul Gauthier (aider): 104
grand_total: grand_total:
Paul Gauthier: 350 Paul Gauthier: 525
Paul Gauthier (aider): 450 Paul Gauthier (aider): 580
start_tag: v0.50.0 start_tag: v0.50.0
total_lines: 800 total_lines: 1105
- aider_percentage: 68.19 - aider_percentage: 68.1
aider_total: 521 aider_total: 521
end_date: '2024-08-23' end_date: '2024-08-23'
end_tag: v0.52.0 end_tag: v0.52.0
@ -2231,6 +2247,8 @@
Paul Gauthier (aider): 9 Paul Gauthier (aider): 9
aider/versioncheck.py: aider/versioncheck.py:
Paul Gauthier: 2 Paul Gauthier: 2
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
benchmark/benchmark.py: benchmark/benchmark.py:
Paul Gauthier: 1 Paul Gauthier: 1
scripts/blame.py: scripts/blame.py:
@ -2250,13 +2268,13 @@
tests/basic/test_wholefile.py: tests/basic/test_wholefile.py:
Paul Gauthier: 8 Paul Gauthier: 8
grand_total: grand_total:
Paul Gauthier: 242 Paul Gauthier: 243
Paul Gauthier (aider): 521 Paul Gauthier (aider): 521
pcamp: 1 pcamp: 1
start_tag: v0.51.0 start_tag: v0.51.0
total_lines: 764 total_lines: 765
- aider_percentage: 58.61 - aider_percentage: 61.4
aider_total: 405 aider_total: 455
end_date: '2024-08-27' end_date: '2024-08-27'
end_tag: v0.53.0 end_tag: v0.53.0
file_counts: file_counts:
@ -2321,14 +2339,16 @@
tests/basic/test_repomap.py: tests/basic/test_repomap.py:
Paul Gauthier: 4 Paul Gauthier: 4
Paul Gauthier (aider): 63 Paul Gauthier (aider): 63
tests/fixtures/sample-code-base/sample.js:
Paul Gauthier (aider): 50
tests/fixtures/sample-code-base/sample.py: tests/fixtures/sample-code-base/sample.py:
Paul Gauthier (aider): 68 Paul Gauthier (aider): 68
grand_total: grand_total:
Paul Gauthier: 286 Paul Gauthier: 286
Paul Gauthier (aider): 405 Paul Gauthier (aider): 455
start_tag: v0.52.0 start_tag: v0.52.0
total_lines: 691 total_lines: 741
- aider_percentage: 63.95 - aider_percentage: 63.75
aider_total: 204 aider_total: 204
end_date: '2024-08-28' end_date: '2024-08-28'
end_tag: v0.54.0 end_tag: v0.54.0
@ -2364,6 +2384,8 @@
aider/versioncheck.py: aider/versioncheck.py:
Paul Gauthier: 1 Paul Gauthier: 1
Paul Gauthier (aider): 13 Paul Gauthier (aider): 13
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
tests/basic/test_coder.py: tests/basic/test_coder.py:
Paul Gauthier: 36 Paul Gauthier: 36
Paul Gauthier (aider): 27 Paul Gauthier (aider): 27
@ -2376,11 +2398,11 @@
Paul Gauthier: 1 Paul Gauthier: 1
grand_total: grand_total:
Antti Kaihola: 4 Antti Kaihola: 4
Paul Gauthier: 111 Paul Gauthier: 112
Paul Gauthier (aider): 204 Paul Gauthier (aider): 204
start_tag: v0.53.0 start_tag: v0.53.0
total_lines: 319 total_lines: 320
- aider_percentage: 52.9 - aider_percentage: 52.87
aider_total: 811 aider_total: 811
end_date: '2024-09-04' end_date: '2024-09-04'
end_tag: v0.55.0 end_tag: v0.55.0
@ -2450,6 +2472,8 @@
aider/voice.py: aider/voice.py:
Paul Gauthier: 7 Paul Gauthier: 7
Paul Gauthier (aider): 9 Paul Gauthier (aider): 9
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
scripts/versionbump.py: scripts/versionbump.py:
Paul Gauthier: 9 Paul Gauthier: 9
tests/basic/test_coder.py: tests/basic/test_coder.py:
@ -2478,11 +2502,11 @@
grand_total: grand_total:
Antti Kaihola: 12 Antti Kaihola: 12
Nikolay Sedelnikov: 45 Nikolay Sedelnikov: 45
Paul Gauthier: 665 Paul Gauthier: 666
Paul Gauthier (aider): 811 Paul Gauthier (aider): 811
start_tag: v0.54.0 start_tag: v0.54.0
total_lines: 1533 total_lines: 1534
- aider_percentage: 55.6 - aider_percentage: 55.4
aider_total: 154 aider_total: 154
end_date: '2024-09-09' end_date: '2024-09-09'
end_tag: v0.56.0 end_tag: v0.56.0
@ -2517,6 +2541,8 @@
aider/report.py: aider/report.py:
Paul Gauthier: 2 Paul Gauthier: 2
Paul Gauthier (aider): 20 Paul Gauthier (aider): 20
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
benchmark/benchmark.py: benchmark/benchmark.py:
Paul Gauthier: 1 Paul Gauthier: 1
tests/basic/test_linter.py: tests/basic/test_linter.py:
@ -2526,13 +2552,13 @@
Paul Gauthier: 2 Paul Gauthier: 2
Paul Gauthier (aider): 9 Paul Gauthier (aider): 9
grand_total: grand_total:
Paul Gauthier: 108 Paul Gauthier: 109
Paul Gauthier (aider): 154 Paul Gauthier (aider): 154
fry69: 15 fry69: 15
start_tag: v0.55.0 start_tag: v0.55.0
total_lines: 277 total_lines: 278
- aider_percentage: 69.98 - aider_percentage: 70.36
aider_total: 394 aider_total: 406
end_date: '2024-09-21' end_date: '2024-09-21'
end_tag: v0.57.0 end_tag: v0.57.0
file_counts: file_counts:
@ -2575,6 +2601,10 @@
Paul Gauthier: 3 Paul Gauthier: 3
aider/utils.py: aider/utils.py:
Paul Gauthier: 2 Paul Gauthier: 2
aider/website/docs/leaderboards/index.md:
Anjor Kanekar: 1
Paul Gauthier: 1
Paul Gauthier (aider): 12
benchmark/benchmark.py: benchmark/benchmark.py:
Paul Gauthier: 4 Paul Gauthier: 4
scripts/issues.py: scripts/issues.py:
@ -2592,15 +2622,16 @@
Paul Gauthier: 18 Paul Gauthier: 18
Paul Gauthier (aider): 20 Paul Gauthier (aider): 20
grand_total: grand_total:
Anjor Kanekar: 1
Christian Clauss: 2 Christian Clauss: 2
Jay Alammar: 1 Jay Alammar: 1
Jay Alammar (aider): 13 Jay Alammar (aider): 13
Krazer: 33 Krazer: 33
Paul Gauthier: 133 Paul Gauthier: 134
Paul Gauthier (aider): 381 Paul Gauthier (aider): 393
start_tag: v0.56.0 start_tag: v0.56.0
total_lines: 563 total_lines: 577
- aider_percentage: 53.45 - aider_percentage: 47.95
aider_total: 712 aider_total: 712
end_date: '2024-09-29' end_date: '2024-09-29'
end_tag: v0.58.0 end_tag: v0.58.0
@ -2677,6 +2708,8 @@
Mike Bailey: 17 Mike Bailey: 17
Paul Gauthier: 2 Paul Gauthier: 2
Paul Gauthier (aider): 10 Paul Gauthier (aider): 10
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 153
benchmark/benchmark.py: benchmark/benchmark.py:
Paul Gauthier: 25 Paul Gauthier: 25
Paul Gauthier (aider): 29 Paul Gauthier (aider): 29
@ -2705,14 +2738,14 @@
grand_total: grand_total:
Jonathan Ellis: 2 Jonathan Ellis: 2
Mike Bailey: 18 Mike Bailey: 18
Paul Gauthier: 376 Paul Gauthier: 529
Paul Gauthier (aider): 712 Paul Gauthier (aider): 712
Stein Martin Hustad: 26 Stein Martin Hustad: 26
fry69: 197 fry69: 197
rti: 1 rti: 1
start_tag: v0.57.0 start_tag: v0.57.0
total_lines: 1332 total_lines: 1485
- aider_percentage: 76.79 - aider_percentage: 75.44
aider_total: 172 aider_total: 172
end_date: '2024-10-04' end_date: '2024-10-04'
end_tag: v0.59.0 end_tag: v0.59.0
@ -2747,6 +2780,8 @@
Paul Gauthier: 2 Paul Gauthier: 2
aider/versioncheck.py: aider/versioncheck.py:
Paul Gauthier: 1 Paul Gauthier: 1
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 4
scripts/issues.py: scripts/issues.py:
Paul Gauthier: 1 Paul Gauthier: 1
scripts/update-docs.sh: scripts/update-docs.sh:
@ -2762,11 +2797,11 @@
tests/help/test_help.py: tests/help/test_help.py:
Paul Gauthier: 1 Paul Gauthier: 1
grand_total: grand_total:
Paul Gauthier: 52 Paul Gauthier: 56
Paul Gauthier (aider): 172 Paul Gauthier (aider): 172
start_tag: v0.58.0 start_tag: v0.58.0
total_lines: 224 total_lines: 228
- aider_percentage: 49.12 - aider_percentage: 48.95
aider_total: 140 aider_total: 140
end_date: '2024-10-22' end_date: '2024-10-22'
end_tag: v0.60.0 end_tag: v0.60.0
@ -2804,6 +2839,8 @@
Paul Gauthier: 3 Paul Gauthier: 3
aider/sendchat.py: aider/sendchat.py:
Paul Gauthier: 3 Paul Gauthier: 3
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
tests/basic/test_editblock.py: tests/basic/test_editblock.py:
Paul Gauthier: 23 Paul Gauthier: 23
tests/basic/test_main.py: tests/basic/test_main.py:
@ -2813,12 +2850,12 @@
Paul Gauthier (aider): 46 Paul Gauthier (aider): 46
grand_total: grand_total:
Jonathan Ellis: 10 Jonathan Ellis: 10
Paul Gauthier: 93 Paul Gauthier: 94
Paul Gauthier (aider): 140 Paul Gauthier (aider): 140
Sven Grunewaldt: 24 Sven Grunewaldt: 24
fry69: 18 fry69: 18
start_tag: v0.59.0 start_tag: v0.59.0
total_lines: 285 total_lines: 286
- aider_percentage: 67.61 - aider_percentage: 67.61
aider_total: 860 aider_total: 860
end_date: '2024-11-01' end_date: '2024-11-01'
@ -2895,8 +2932,8 @@
kAIto47802: 4 kAIto47802: 4
start_tag: v0.60.0 start_tag: v0.60.0
total_lines: 1272 total_lines: 1272
- aider_percentage: 84.0 - aider_percentage: 82.42
aider_total: 63 aider_total: 75
end_date: '2024-11-04' end_date: '2024-11-04'
end_tag: v0.62.0 end_tag: v0.62.0
file_counts: file_counts:
@ -2911,12 +2948,15 @@
aider/models.py: aider/models.py:
Paul Gauthier: 5 Paul Gauthier: 5
Paul Gauthier (aider): 45 Paul Gauthier (aider): 45
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 4
Paul Gauthier (aider): 12
grand_total: grand_total:
Paul Gauthier: 12 Paul Gauthier: 16
Paul Gauthier (aider): 63 Paul Gauthier (aider): 75
start_tag: v0.61.0 start_tag: v0.61.0
total_lines: 75 total_lines: 91
- aider_percentage: 55.16 - aider_percentage: 55.08
aider_total: 385 aider_total: 385
end_date: '2024-11-13' end_date: '2024-11-13'
end_tag: v0.63.0 end_tag: v0.63.0
@ -2952,6 +2992,8 @@
aider/sendchat.py: aider/sendchat.py:
Paul Gauthier: 17 Paul Gauthier: 17
Paul Gauthier (aider): 4 Paul Gauthier (aider): 4
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
scripts/issues.py: scripts/issues.py:
Paul Gauthier: 4 Paul Gauthier: 4
Paul Gauthier (aider): 195 Paul Gauthier (aider): 195
@ -2975,7 +3017,155 @@
Paul Gauthier: 1 Paul Gauthier: 1
grand_total: grand_total:
Logan Attwood: 29 Logan Attwood: 29
Paul Gauthier: 284 Paul Gauthier: 285
Paul Gauthier (aider): 385 Paul Gauthier (aider): 385
start_tag: v0.62.0 start_tag: v0.62.0
total_lines: 698 total_lines: 699
- aider_percentage: 73.15
aider_total: 880
end_date: '2024-11-21'
end_tag: v0.64.0
file_counts:
aider/__init__.py:
Paul Gauthier: 1
aider/analytics.py:
Paul Gauthier: 20
Paul Gauthier (aider): 21
aider/args.py:
Paul Gauthier: 2
Paul Gauthier (aider): 10
aider/coders/base_coder.py:
Paul Gauthier: 15
Paul Gauthier (aider): 3
caetanominuzzo: 1
aider/commands.py:
Chad Phillips: 4
Paul Gauthier: 5
Paul Gauthier (aider): 19
aider/editor.py:
Chad Phillips: 133
Paul Gauthier (aider): 13
aider/exceptions.py:
Paul Gauthier: 5
aider/help_pats.py:
Paul Gauthier: 1
aider/io.py:
Chad Phillips: 9
Paul Gauthier (aider): 41
mw: 21
aider/main.py:
Paul Gauthier: 21
Paul Gauthier (aider): 41
aider/models.py:
Paul Gauthier: 41
Paul Gauthier (aider): 33
aider/repo.py:
Paul Gauthier (aider): 5
aider/urls.py:
Paul Gauthier: 1
aider/website/_includes/edit-leaderboard.js:
Paul Gauthier (aider): 97
aider/website/_includes/quant-chart.js:
Paul Gauthier: 3
Paul Gauthier (aider): 66
aider/website/_includes/refactor-leaderboard.js:
Paul Gauthier (aider): 90
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
Paul Gauthier (aider): 10
aider/website/share/index.md:
Paul Gauthier (aider): 29
benchmark/over_time.py:
Paul Gauthier: 11
Paul Gauthier (aider): 162
scripts/blame.py:
Paul Gauthier: 1
Paul Gauthier (aider): 2
scripts/issues.py:
Paul Gauthier: 5
Paul Gauthier (aider): 12
scripts/versionbump.py:
Paul Gauthier: 7
tests/basic/test_analytics.py:
Paul Gauthier: 12
Paul Gauthier (aider): 30
tests/basic/test_commands.py:
Paul Gauthier (aider): 4
tests/basic/test_editor.py:
Paul Gauthier (aider): 129
tests/basic/test_main.py:
Paul Gauthier (aider): 8
tests/basic/test_models.py:
Paul Gauthier: 3
Paul Gauthier (aider): 55
grand_total:
Chad Phillips: 146
Paul Gauthier: 155
Paul Gauthier (aider): 880
caetanominuzzo: 1
mw: 21
start_tag: v0.63.0
total_lines: 1203
- aider_percentage: 81.11
aider_total: 584
end_date: '2024-11-26'
end_tag: v0.65.0
file_counts:
aider/__init__.py:
Paul Gauthier: 1
aider/analytics.py:
Paul Gauthier: 2
Paul Gauthier (aider): 5
aider/args.py:
Paul Gauthier (aider): 12
aider/coders/base_coder.py:
Paul Gauthier: 1
Paul Gauthier (aider): 31
aider/commands.py:
Paul Gauthier: 2
aider/io.py:
Paul Gauthier: 3
Paul Gauthier (aider): 9
aider/main.py:
Paul Gauthier: 15
Paul Gauthier (aider): 19
aider/models.py:
Paul Gauthier: 9
Paul Gauthier (aider): 17
aider/queries/tree-sitter-dart-tags.scm:
malkoG: 91
aider/urls.py:
Paul Gauthier (aider): 1
aider/website/_includes/quant-chart.js:
Paul Gauthier (aider): 76
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
benchmark/benchmark.py:
Paul Gauthier (aider): 10
benchmark/docker.sh:
Paul Gauthier (aider): 1
benchmark/over_time.py:
Paul Gauthier: 1
Paul Gauthier (aider): 157
scripts/update-docs.sh:
Paul Gauthier: 1
scripts/update-history.py:
Paul Gauthier: 8
Paul Gauthier (aider): 64
tests/basic/test_coder.py:
Paul Gauthier (aider): 81
tests/basic/test_editor.py:
Paul Gauthier (aider): 16
tests/basic/test_main.py:
Paul Gauthier: 1
Paul Gauthier (aider): 42
tests/basic/test_models.py:
Paul Gauthier (aider): 30
tests/basic/test_repomap.py:
Paul Gauthier (aider): 13
grand_total:
Paul Gauthier: 45
Paul Gauthier (aider): 584
malkoG: 91
start_tag: v0.64.0
total_lines: 720

View file

@ -1,7 +1,7 @@
- dirname: 2024-05-01-20-05-59--direct-opus-filenames-outside-fence - dirname: 2024-05-01-20-05-59--direct-opus-filenames-outside-fence
test_cases: 133 test_cases: 133
model: claude-3-opus-20240229 model: claude-3-opus-20240229
released: 2024-02-29 _released: 2024-02-29
edit_format: diff edit_format: diff
commit_hash: f4b1797-dirty, f4b1797 commit_hash: f4b1797-dirty, f4b1797
pass_rate_1: 53.4 pass_rate_1: 53.4
@ -46,7 +46,8 @@
- dirname: 2024-05-03-20-47-24--gemini-1.5-pro-diff-fenced - dirname: 2024-05-03-20-47-24--gemini-1.5-pro-diff-fenced
test_cases: 133 test_cases: 133
model: gemini-1.5-pro-latest released: 2024-05-03
model: gemini-1.5-pro-001
edit_format: diff-fenced edit_format: diff-fenced
commit_hash: 3a48dfb, 5d32dd7 commit_hash: 3a48dfb, 5d32dd7
pass_rate_1: 45.9 pass_rate_1: 45.9
@ -274,7 +275,7 @@
- dirname: 2024-05-03-22-24-48--openrouter--llama3-diff-examples-sys-msg - dirname: 2024-05-03-22-24-48--openrouter--llama3-diff-examples-sys-msg
test_cases: 132 test_cases: 132
model: llama3-70b-8192 model: llama3-70b-8192
released: 2024-04-18 _released: 2024-04-18
edit_format: diff edit_format: diff
commit_hash: b5bb453 commit_hash: b5bb453
pass_rate_1: 38.6 pass_rate_1: 38.6
@ -297,7 +298,7 @@
- dirname: 2024-05-06-18-31-08--command-r-plus-whole-final - dirname: 2024-05-06-18-31-08--command-r-plus-whole-final
test_cases: 133 test_cases: 133
model: command-r-plus model: command-r-plus
released: 2024-04-04 _released: 2024-04-04
edit_format: whole edit_format: whole
commit_hash: fc3a43e-dirty commit_hash: fc3a43e-dirty
pass_rate_1: 21.8 pass_rate_1: 21.8
@ -410,6 +411,7 @@
- dirname: 2024-06-08-22-37-55--qwen2-72b-instruct-whole - dirname: 2024-06-08-22-37-55--qwen2-72b-instruct-whole
test_cases: 133 test_cases: 133
model: Qwen2 72B Instruct model: Qwen2 72B Instruct
released: 2024-06-08
edit_format: whole edit_format: whole
commit_hash: 02c7335-dirty, 1a97498-dirty commit_hash: 02c7335-dirty, 1a97498-dirty
pass_rate_1: 44.4 pass_rate_1: 44.4
@ -623,7 +625,7 @@
commit_hash: d31eef3-dirty commit_hash: d31eef3-dirty
pass_rate_1: 40.6 pass_rate_1: 40.6
pass_rate_2: 55.6 pass_rate_2: 55.6
released: 2024-07-18 _released: 2024-07-18
percent_cases_well_formed: 100.0 percent_cases_well_formed: 100.0
error_outputs: 1 error_outputs: 1
num_malformed_responses: 0 num_malformed_responses: 0
@ -671,7 +673,7 @@
commit_hash: f7ce78b-dirty commit_hash: f7ce78b-dirty
pass_rate_1: 46.6 pass_rate_1: 46.6
pass_rate_2: 63.9 pass_rate_2: 63.9
released: 2024-07-23 _released: 2024-07-23
percent_cases_well_formed: 92.5 percent_cases_well_formed: 92.5
error_outputs: 84 error_outputs: 84
num_malformed_responses: 19 num_malformed_responses: 19
@ -691,6 +693,7 @@
- dirname: 2024-07-24-06-30-29--llama-405b-whole - dirname: 2024-07-24-06-30-29--llama-405b-whole
test_cases: 133 test_cases: 133
model: llama-3.1-405b-instruct (whole) model: llama-3.1-405b-instruct (whole)
_released: 2024-07-23
edit_format: whole edit_format: whole
commit_hash: a362dea-dirty commit_hash: a362dea-dirty
pass_rate_1: 48.9 pass_rate_1: 48.9
@ -698,7 +701,6 @@
percent_cases_well_formed: 100.0 percent_cases_well_formed: 100.0
error_outputs: 0 error_outputs: 0
num_malformed_responses: 0 num_malformed_responses: 0
released: 2024-07-23
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 0 user_asks: 0
lazy_comments: 0 lazy_comments: 0
@ -770,7 +772,7 @@
percent_cases_well_formed: 100.0 percent_cases_well_formed: 100.0
error_outputs: 27 error_outputs: 27
num_malformed_responses: 0 num_malformed_responses: 0
released: 2024-07-23 _released: 2024-07-23
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 23 user_asks: 23
lazy_comments: 8 lazy_comments: 8
@ -796,7 +798,7 @@
num_malformed_responses: 0 num_malformed_responses: 0
num_with_malformed_responses: 0 num_with_malformed_responses: 0
user_asks: 0 user_asks: 0
released: 2024-07-23 _released: 2024-07-23
lazy_comments: 0 lazy_comments: 0
syntax_errors: 0 syntax_errors: 0
indentation_errors: 0 indentation_errors: 0
@ -835,6 +837,7 @@
- dirname: 2024-08-28-07-10-50--gemini-1.5-pro-exp-0827-diff-fenced - dirname: 2024-08-28-07-10-50--gemini-1.5-pro-exp-0827-diff-fenced
test_cases: 133 test_cases: 133
model: gemini-1.5-pro-exp-0827 model: gemini-1.5-pro-exp-0827
released: 2024-08-27
edit_format: diff-fenced edit_format: diff-fenced
commit_hash: d8adc75 commit_hash: d8adc75
pass_rate_1: 54.9 pass_rate_1: 54.9
@ -946,7 +949,7 @@
versions: 0.54.13.dev versions: 0.54.13.dev
seconds_per_case: 8.3 seconds_per_case: 8.3
total_cost: 0.0000 total_cost: 0.0000
released: 2024-09-04 _released: 2024-09-04
- dirname: 2024-09-04-16-17-33--yi-coder-9b-chat-q4_0-whole - dirname: 2024-09-04-16-17-33--yi-coder-9b-chat-q4_0-whole
test_cases: 133 test_cases: 133
@ -973,6 +976,7 @@
- dirname: 2024-09-05-14-50-11--deepseek-sep5-no-shell - dirname: 2024-09-05-14-50-11--deepseek-sep5-no-shell
test_cases: 133 test_cases: 133
released: 2024-09-05
model: DeepSeek V2.5 model: DeepSeek V2.5
edit_format: diff edit_format: diff
commit_hash: 1279c86 commit_hash: 1279c86
@ -1112,6 +1116,7 @@
- dirname: 2024-09-21-16-45-11--o1-preview-flex-sr-markers - dirname: 2024-09-21-16-45-11--o1-preview-flex-sr-markers
test_cases: 133 test_cases: 133
model: o1-preview model: o1-preview
_released: 2024-09-12
edit_format: diff edit_format: diff
commit_hash: 5493654-dirty commit_hash: 5493654-dirty
pass_rate_1: 57.9 pass_rate_1: 57.9
@ -1204,6 +1209,7 @@
- dirname: 2024-09-24-16-26-45--gemini-1.5-pro-002-diff-fenced - dirname: 2024-09-24-16-26-45--gemini-1.5-pro-002-diff-fenced
test_cases: 133 test_cases: 133
model: gemini-1.5-pro-002 model: gemini-1.5-pro-002
released: 2024-09-24
edit_format: diff-fenced edit_format: diff-fenced
commit_hash: 6b5fe9b, 3edcd71 commit_hash: 6b5fe9b, 3edcd71
pass_rate_1: 49.6 pass_rate_1: 49.6
@ -1477,6 +1483,7 @@
- dirname: 2024-10-04-16-30-08--chatgpt-4o-latest-diff-oct4 - dirname: 2024-10-04-16-30-08--chatgpt-4o-latest-diff-oct4
test_cases: 133 test_cases: 133
model: openai/chatgpt-4o-latest model: openai/chatgpt-4o-latest
released: 2024-10-04
edit_format: diff edit_format: diff
commit_hash: af10953 commit_hash: af10953
pass_rate_1: 56.4 pass_rate_1: 56.4
@ -1592,6 +1599,7 @@
- dirname: 2024-10-22-17-45-28--sonnet-1022-diff-fixed-model-settings - dirname: 2024-10-22-17-45-28--sonnet-1022-diff-fixed-model-settings
test_cases: 133 test_cases: 133
model: claude-3-5-sonnet-20241022 model: claude-3-5-sonnet-20241022
released: 2024-10-22
edit_format: diff edit_format: diff
commit_hash: 3b14eb9 commit_hash: 3b14eb9
pass_rate_1: 69.2 pass_rate_1: 69.2
@ -1615,6 +1623,7 @@
- dirname: 2024-11-04-19-19-32--haiku35-diff-ex-as-sys-false - dirname: 2024-11-04-19-19-32--haiku35-diff-ex-as-sys-false
test_cases: 133 test_cases: 133
model: claude-3-5-haiku-20241022 model: claude-3-5-haiku-20241022
released: 2024-10-22
edit_format: diff edit_format: diff
commit_hash: 03bbdb0-dirty commit_hash: 03bbdb0-dirty
pass_rate_1: 61.7 pass_rate_1: 61.7
@ -1773,32 +1782,10 @@
seconds_per_case: 18.3 seconds_per_case: 18.3
total_cost: 0.0000 total_cost: 0.0000
- dirname: 2024-11-09-10-57-11--Qwen2.5-Coder-32B-Instruct
test_cases: 133
model: Qwen2.5-Coder-32B-Instruct (whole)
edit_format: whole
commit_hash: ec9982a
pass_rate_1: 60.9
pass_rate_2: 73.7
percent_cases_well_formed: 100.0
error_outputs: 1
num_malformed_responses: 0
num_with_malformed_responses: 0
user_asks: 1
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 1
test_timeouts: 1
command: aider --model openai/Qwen2.5-Coder-32B-Instruct
date: 2024-11-09
versions: 0.59.2.dev
seconds_per_case: 26.6
total_cost: 0.0000
- dirname: 2024-11-09-11-09-15--Qwen2.5-Coder-32B-Instruct - dirname: 2024-11-09-11-09-15--Qwen2.5-Coder-32B-Instruct
test_cases: 133 test_cases: 133
model: Qwen2.5-Coder-32B-Instruct (diff) model: Qwen2.5-Coder-32B-Instruct
released: 2024-11-12
edit_format: diff edit_format: diff
commit_hash: ec9982a commit_hash: ec9982a
pass_rate_1: 59.4 pass_rate_1: 59.4
@ -1813,8 +1800,149 @@
indentation_errors: 0 indentation_errors: 0
exhausted_context_windows: 0 exhausted_context_windows: 0
test_timeouts: 3 test_timeouts: 3
command: aider --model openai/Qwen2.5-Coder-32B-Instruct command: aider --model openai/hf:Qwen/Qwen2.5-Coder-32B-Instruct --openai-api-base https://glhf.chat/api/openai/v1
date: 2024-11-09 date: 2024-11-09
versions: 0.59.2.dev versions: 0.59.2.dev
seconds_per_case: 22.5 seconds_per_case: 22.5
total_cost: 0.0000 total_cost: 0.0000
- dirname: 2024-11-20-14-57-11--mistral-2411-direct-diff
test_cases: 133
model: Mistral Large (2411)
released: 2024-11-18
edit_format: diff
commit_hash: dba844c
pass_rate_1: 46.6
pass_rate_2: 65.4
percent_cases_well_formed: 96.2
error_outputs: 8
num_malformed_responses: 8
num_with_malformed_responses: 5
user_asks: 5
lazy_comments: 0
syntax_errors: 0
indentation_errors: 1
exhausted_context_windows: 0
test_timeouts: 1
command: aider --model mistral/mistral-large-latest
date: 2024-11-20
versions: 0.63.3.dev
seconds_per_case: 24.9
total_cost: 3.2334
- dirname: 2024-11-20-19-28-30--gpt-4o-2024-11-20
test_cases: 133
model: gpt-4o-2024-11-20
released: 2024-11-20
edit_format: diff
commit_hash: 2ac0776-dirty
pass_rate_1: 58.6
pass_rate_2: 71.4
percent_cases_well_formed: 99.2
error_outputs: 1
num_malformed_responses: 1
num_with_malformed_responses: 1
user_asks: 4
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 5
command: aider --model openai/gpt-4o-2024-11-20
date: 2024-11-20
versions: 0.63.3.dev
seconds_per_case: 6.0
total_cost: 0.0000
- dirname: 2024-09-20-21-47-17--qwen2.5-32b-instruct-q8_0-whole
test_cases: 133
model: ollama/qwen2.5:32b-instruct-q8_0
edit_format: whole
commit_hash: 2753ac6
pass_rate_1: 46.6
pass_rate_2: 58.6
percent_cases_well_formed: 100.0
error_outputs: 0
num_malformed_responses: 0
num_with_malformed_responses: 0
user_asks: 1
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 2
command: aider --model ollama/qwen2.5:32b-instruct-q8_0
date: 2024-09-20
versions: 0.56.1.dev
seconds_per_case: 1763.7
total_cost: 0.0000
- dirname: 2024-11-20-15-17-37--qwen25-32b-or-diff
test_cases: 133
model: openrouter/qwen/qwen-2.5-coder-32b-instruct
edit_format: diff
commit_hash: e917424
pass_rate_1: 49.6
pass_rate_2: 65.4
percent_cases_well_formed: 84.2
error_outputs: 43
num_malformed_responses: 31
num_with_malformed_responses: 21
user_asks: 43
lazy_comments: 0
syntax_errors: 2
indentation_errors: 2
exhausted_context_windows: 12
test_timeouts: 2
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
date: 2024-11-20
versions: 0.63.3.dev
seconds_per_case: 40.7
total_cost: 0.1497
- dirname: 2024-11-21-17-46-36--gemini-exp-1121-diff
test_cases: 133
model: gemini-exp-1121
released: 2024-11-21
edit_format: diff
commit_hash: e94961a
pass_rate_1: 46.6
pass_rate_2: 57.9
percent_cases_well_formed: 83.5
error_outputs: 101
num_malformed_responses: 101
num_with_malformed_responses: 22
user_asks: 5
lazy_comments: 0
syntax_errors: 0
indentation_errors: 2
exhausted_context_windows: 0
test_timeouts: 3
command: aider --model gemini/gemini-exp-1121
date: 2024-11-21
versions: 0.63.3.dev
seconds_per_case: 60.3
total_cost: 0.0000
- dirname: 2024-11-15-20-33-31--gemini-exp-1114-diff
test_cases: 133
model: gemini-exp-1114
released: 2024-11-14
edit_format: diff
commit_hash: 0bf17a4
pass_rate_1: 50.4
pass_rate_2: 60.9
percent_cases_well_formed: 85.7
error_outputs: 70
num_malformed_responses: 70
num_with_malformed_responses: 19
user_asks: 2
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 4
command: aider --model gemini/gemini-exp-1114
date: 2024-11-15
versions: 0.63.2.dev
seconds_per_case: 38.6

View file

@ -0,0 +1,299 @@
- dirname: 2024-11-09-11-09-15--Qwen2.5-Coder-32B-Instruct
test_cases: 133
model: "HuggingFace via GLHF: BF16"
released: 2024-11-12
edit_format: diff
commit_hash: ec9982a
pass_rate_1: 59.4
pass_rate_2: 71.4
percent_cases_well_formed: 94.7
error_outputs: 17
num_malformed_responses: 17
num_with_malformed_responses: 7
user_asks: 1
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 3
command: aider --model openai/hf:Qwen/Qwen2.5-Coder-32B-Instruct --openai-api-base https://glhf.chat/api/openai/v1
date: 2024-11-09
versions: 0.59.2.dev
seconds_per_case: 22.5
total_cost: 0.0000
- dirname: 2024-11-22-18-56-13--ollama-qwen2.5-coder:32b-instruct-fp16
test_cases: 132
model: "Ollama: fp16"
edit_format: diff
commit_hash: f06452c-dirty, 6a0a97c-dirty, 4e9ae16-dirty, 5506d0f-dirty
pass_rate_1: 58.3
pass_rate_2: 71.4
percent_cases_well_formed: 90.2
error_outputs: 27
num_malformed_responses: 26
num_with_malformed_responses: 13
user_asks: 2
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 0
command: aider --model ollama/qwen2.5-coder:32b-instruct-fp16
date: 2024-11-22
versions: 0.64.2.dev
seconds_per_case: 119.6
total_cost: 0.0000
- dirname: 2024-11-22-14-53-26--hyperbolic-qwen25coder32binstruct
test_cases: 133
model: "Hyperbolic: BF16"
edit_format: diff
commit_hash: f9ef161, 17aef7b-dirty
pass_rate_1: 57.9
pass_rate_2: 69.2
percent_cases_well_formed: 91.7
error_outputs: 30
num_malformed_responses: 29
num_with_malformed_responses: 11
user_asks: 9
lazy_comments: 0
syntax_errors: 4
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 2
command: aider --model openai/Qwen/Qwen2.5-Coder-32B-Instruct --openai-api-base https://api.hyperbolic.xyz/v1/
date: 2024-11-22
versions: 0.64.2.dev
seconds_per_case: 33.2
total_cost: 0.0000
- dirname: 2024-11-22-17-53-35--qwen25-coder-32b-Instruct-4bit
test_cases: 133
model: "mlx-community: 4bit"
edit_format: diff
commit_hash: a16dcab-dirty
pass_rate_1: 60.2
pass_rate_2: 72.2
percent_cases_well_formed: 88.7
error_outputs: 31
num_malformed_responses: 30
num_with_malformed_responses: 15
user_asks: 6
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 1
test_timeouts: 0
command: aider --model openai/mlx-community/Qwen2.5-Coder-32B-Instruct-4bit
date: 2024-11-23
versions: 0.64.2.dev
seconds_per_case: 53.4
total_cost: 0.0000
- dirname: 2024-11-23-15-07-20--qwen25-coder-32b-Instruct-8bit
test_cases: 133
model: "mlx-community: 8bit"
edit_format: diff
commit_hash: a16dcab-dirty
pass_rate_1: 59.4
pass_rate_2: 72.2
percent_cases_well_formed: 92.5
error_outputs: 20
num_malformed_responses: 15
num_with_malformed_responses: 10
user_asks: 7
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 5
test_timeouts: 2
command: aider --model openai/mlx-community/Qwen2.5-Coder-32B-Instruct-8bit
date: 2024-11-23
versions: 0.64.2.dev
seconds_per_case: 98.4
total_cost: 0.0000
- dirname: 2024-11-24-22-18-18--or-all-or-fixed-blank-messages2
test_cases: 133
model: "OpenRouter: multiple"
edit_format: diff
commit_hash: 0c59d32
pass_rate_1: 57.1
pass_rate_2: 67.7
percent_cases_well_formed: 95.5
error_outputs: 56
num_malformed_responses: 10
num_with_malformed_responses: 6
user_asks: 14
lazy_comments: 0
syntax_errors: 6
indentation_errors: 0
exhausted_context_windows: 3
test_timeouts: 1
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 21.2
total_cost: 0.1420
- dirname: 2024-11-23-21-08-53--ollama-qwen2.5-coder:32b-instruct-q4_K_M-8kctx
test_cases: 133
model: "Ollama: q4_K_M"
edit_format: diff
commit_hash: baa1335-dirty, e63df83-dirty, ff8c1aa-dirty
pass_rate_1: 54.9
pass_rate_2: 66.9
percent_cases_well_formed: 94.0
error_outputs: 21
num_malformed_responses: 21
num_with_malformed_responses: 8
user_asks: 5
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 3
command: aider --model ollama/qwen2.5-coder:32b-instruct-q4_K_M
date: 2024-11-23
versions: 0.64.2.dev
seconds_per_case: 35.7
total_cost: 0.0000
- dirname: 2024-11-24-02-23-32--deepinfra-qwen-diff
test_cases: 133
model: "Deepinfra: BF16"
edit_format: diff
commit_hash: bb78e2f
pass_rate_1: 58.6
pass_rate_2: 72.2
percent_cases_well_formed: 94.7
error_outputs: 15
num_malformed_responses: 13
num_with_malformed_responses: 7
user_asks: 3
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 2
test_timeouts: 3
command: aider --model deepinfra/Qwen/Qwen2.5-Coder-32B-Instruct
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 17.5
total_cost: 0.0000
- dirname: 2024-11-24-04-12-58--fireworks-qwen-diff
test_cases: 133
model: "Fireworks: unknown"
edit_format: diff
commit_hash: 757eac0
pass_rate_1: 57.9
pass_rate_2: 72.2
percent_cases_well_formed: 94.0
error_outputs: 23
num_malformed_responses: 19
num_with_malformed_responses: 8
user_asks: 8
lazy_comments: 0
syntax_errors: 6
indentation_errors: 0
exhausted_context_windows: 4
test_timeouts: 1
command: aider --model fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 10.4
total_cost: 0.5759
- dirname: 2024-11-24-02-04-59--ollama-qwen2.5-coder:32b-instruct-q2_K-8kctx
test_cases: 133
model: "Ollama: q2_K"
edit_format: diff
commit_hash: 757eac0, bb78e2f, 8d0ba40-dirty, 1d09e96
pass_rate_1: 48.9
pass_rate_2: 61.7
percent_cases_well_formed: 91.7
error_outputs: 32
num_malformed_responses: 32
num_with_malformed_responses: 11
user_asks: 8
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
test_timeouts: 1
command: aider --model ollama/qwen2.5-coder:32b-instruct-q2_K
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 97.8
total_cost: 0.0000
- dirname: 2024-11-24-14-56-49--qwen25-32b-or-fireworks
test_cases: 133
model: "Fireworks via OpenRouter: unknown"
edit_format: diff
commit_hash: c2f184f
pass_rate_1: 55.6
pass_rate_2: 67.7
percent_cases_well_formed: 94.0
error_outputs: 39
num_malformed_responses: 24
num_with_malformed_responses: 8
user_asks: 13
lazy_comments: 0
syntax_errors: 1
indentation_errors: 1
exhausted_context_windows: 7
test_timeouts: 4
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 16.1
total_cost: 0.1391
- dirname: 2024-11-24-22-03-19--or-hyperbolic-or-fixed-blank-messages2
test_cases: 133
model: "Hyperbolic via OpenRouter: BF16"
edit_format: diff
commit_hash: 0c59d32
pass_rate_1: 55.6
pass_rate_2: 68.4
percent_cases_well_formed: 89.5
error_outputs: 28
num_malformed_responses: 24
num_with_malformed_responses: 14
user_asks: 29
lazy_comments: 0
syntax_errors: 1
indentation_errors: 0
exhausted_context_windows: 4
test_timeouts: 1
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 41.5
total_cost: 0.1402
- dirname: 2024-11-24-15-00-50--qwen25-32b-or-deepinfra
test_cases: 133
model: "Deepinfra via OpenRouter: BF16"
edit_format: diff
commit_hash: c2f184f
pass_rate_1: 57.1
pass_rate_2: 69.9
percent_cases_well_formed: 89.5
error_outputs: 35
num_malformed_responses: 31
num_with_malformed_responses: 14
user_asks: 11
lazy_comments: 0
syntax_errors: 1
indentation_errors: 1
exhausted_context_windows: 4
test_timeouts: 1
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
date: 2024-11-24
versions: 0.64.2.dev
seconds_per_case: 28.5
total_cost: 0.1390

View file

@ -0,0 +1,97 @@
document.addEventListener('DOMContentLoaded', function () {
var ctx = document.getElementById('editChart').getContext('2d');
const HIGHTLIGHT_MODEL = 'no no no no';
var leaderboardData = {
labels: [],
datasets: [{
label: 'Percent completed correctly',
data: [],
backgroundColor: function(context) {
const label = context.chart.data.labels[context.dataIndex] || '';
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 0.2)' : 'rgba(54, 162, 235, 0.2)';
},
borderColor: function(context) {
const label = context.chart.data.labels[context.dataIndex] || '';
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)';
},
borderWidth: 1
}]
};
var allData = [];
{% for row in edit_sorted %}
allData.push({
model: '{{ row.model }}',
pass_rate_2: {{ row.pass_rate_2 }},
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
});
{% endfor %}
function updateChart() {
var selectedRows = document.querySelectorAll('tr.selected');
var showAll = selectedRows.length === 0;
leaderboardData.labels = [];
leaderboardData.datasets[0].data = [];
allData.forEach(function(row, index) {
var rowElement = document.getElementById('edit-row-' + index);
if (showAll) {
rowElement.classList.remove('selected');
}
if (showAll || rowElement.classList.contains('selected')) {
leaderboardData.labels.push(row.model);
leaderboardData.datasets[0].data.push(row.pass_rate_2);
}
});
leaderboardChart.update();
}
var tableBody = document.querySelector('table tbody');
allData.forEach(function(row, index) {
var tr = tableBody.children[index];
tr.id = 'edit-row-' + index;
tr.style.cursor = 'pointer';
tr.onclick = function() {
this.classList.toggle('selected');
updateChart();
};
});
var leaderboardChart = new Chart(ctx, {
type: 'bar',
data: leaderboardData,
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
updateChart();
// Add search functionality for edit table
document.getElementById('editSearchInput').addEventListener('keyup', function() {
var searchWords = this.value.toLowerCase().split(' ').filter(word => word.length > 0);
var tableBody = document.querySelector('table:first-of-type tbody');
var rows = tableBody.getElementsByTagName('tr');
leaderboardData.labels = [];
leaderboardData.datasets[0].data = [];
for (var i = 0; i < rows.length; i++) {
var rowText = rows[i].textContent;
if (searchWords.every(word => rowText.toLowerCase().includes(word))) {
rows[i].style.display = '';
leaderboardData.labels.push(allData[i].model);
leaderboardData.datasets[0].data.push(allData[i].pass_rate_2);
} else {
rows[i].style.display = 'none';
}
}
leaderboardChart.update();
});
});

View file

View file

@ -1,5 +1,15 @@
You can send long, multi-line messages in the chat in a few ways: You can send long, multi-line messages in the chat in a few ways:
- Paste a multi-line message directly into the chat. - Paste a multi-line message directly into the chat.
- Enter `{` alone on the first line to start a multiline message and `}` alone on the last line to end it. - Enter `{` alone on the first line to start a multiline message and `}` alone on the last line to end it.
- Or, start with `{tag` (where "tag" is any sequence of letters/numbers) and end with `tag}`. This is useful when you need to include closing braces `}` in your message.
- Use Meta-ENTER to start a new line without sending the message (Esc+ENTER in some environments). - Use Meta-ENTER to start a new line without sending the message (Esc+ENTER in some environments).
- Use `/paste` to paste text from the clipboard into the chat. - Use `/paste` to paste text from the clipboard into the chat.
- Use the `/editor` command to open your editor to create the next chat message. See [editor configuration docs](/docs/config/editor.html) for more info.
Example with a tag:
```
{python
def hello():
print("Hello}") # Note: contains a brace
python}
```

View file

@ -0,0 +1,95 @@
document.addEventListener('DOMContentLoaded', function () {
var ctx = document.getElementById('quantChart').getContext('2d');
var allData = [];
{% for row in site.data.quant %}
allData.push({
model: '{{ row.model }}',
pass_rate_2: {{ row.pass_rate_2 }}
});
{% endfor %}
// Sort data by pass_rate_2 in descending order
allData.sort((a, b) => b.pass_rate_2 - a.pass_rate_2);
var chart;
function updateChart(filterText) {
var filteredData = allData.filter(row =>
row.model.toLowerCase().includes(filterText.toLowerCase())
);
var chartData = {
labels: filteredData.map(row => row.model),
datasets: [{
label: 'Percent completed correctly',
data: filteredData.map(row => row.pass_rate_2),
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
};
if (chart) {
chart.data = chartData;
chart.update();
} else {
chart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: {
plugins: {
legend: {
display: false
},
title: {
display: true,
text: 'Aider code editing benchmark',
font: {
size: 16
}
}
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Percent completed correctly',
font: {
size: 14
}
},
ticks: {
font: {
size: 16
}
}
},
x: {
ticks: {
font: {
size: 16
}
},
title: {
display: true,
text: 'Provider: quantization',
font: {
size: 14
}
}
}
}
}
});
}
}
// Initial chart render
updateChart('');
// Connect search input to chart filtering
document.getElementById('quantSearchInput').addEventListener('keyup', function() {
updateChart(this.value);
});
});

View file

@ -0,0 +1,90 @@
document.addEventListener('DOMContentLoaded', function () {
var ctx = document.getElementById('refacChart').getContext('2d');
var leaderboardData = {
labels: [],
datasets: [{
label: 'Percent completed correctly',
data: [],
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
};
var allData = [];
{% for row in refac_sorted %}
allData.push({
model: '{{ row.model }}',
pass_rate_1: {{ row.pass_rate_1 }},
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
});
{% endfor %}
function updateChart() {
var selectedRows = document.querySelectorAll('tr.selected');
var showAll = selectedRows.length === 0;
leaderboardData.labels = [];
leaderboardData.datasets[0].data = [];
allData.forEach(function(row, index) {
var rowElement = document.getElementById('refac-row-' + index);
if (showAll) {
rowElement.classList.remove('selected');
}
if (showAll || rowElement.classList.contains('selected')) {
leaderboardData.labels.push(row.model);
leaderboardData.datasets[0].data.push(row.pass_rate_1);
}
});
leaderboardChart.update();
}
var tableBody = document.querySelectorAll('table tbody')[1];
allData.forEach(function(row, index) {
var tr = tableBody.children[index];
tr.id = 'refac-row-' + index;
tr.style.cursor = 'pointer';
tr.onclick = function() {
this.classList.toggle('selected');
updateChart();
};
});
var leaderboardChart = new Chart(ctx, {
type: 'bar',
data: leaderboardData,
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
updateChart();
// Add search functionality for refactoring table
document.getElementById('refacSearchInput').addEventListener('keyup', function() {
var searchWords = this.value.toLowerCase().split(' ').filter(word => word.length > 0);
var tableBody = document.querySelectorAll('table tbody')[1];
var rows = tableBody.getElementsByTagName('tr');
leaderboardData.labels = [];
leaderboardData.datasets[0].data = [];
for (var i = 0; i < rows.length; i++) {
var rowText = rows[i].textContent;
if (searchWords.every(word => rowText.toLowerCase().includes(word))) {
rows[i].style.display = '';
leaderboardData.labels.push(allData[i].model);
leaderboardData.datasets[0].data.push(allData[i].pass_rate_1);
} else {
rows[i].style.display = 'none';
}
}
leaderboardChart.update();
});
});

View file

@ -0,0 +1,169 @@
---
title: Details matter with open source models
excerpt: Open source LLMs are becoming very powerful, but pay attention to how you (or your provider) are serving the model. It can affect code editing skill.
highlight_image: /assets/quantization.jpg
draft: false
nav_exclude: true
---
{% if page.date %}
<p class="post-date">{{ page.date | date: "%B %d, %Y" }}</p>
{% endif %}
# Details matter with open source models
{: .no_toc }
Open source models like Qwen 2.5 32B Instruct are performing very well on
aider's code editing benchmark, rivaling closed source frontier models.
But pay attention to how your model is being served and quantized,
as it can impact code editing skill.
Open source models are often available at a variety of quantizations,
and can be served with different token limits.
These details matter when working with code.
The graph and table below compares different versions of the Qwen 2.5 Coder 32B Instruct model,
served both locally and from a variety of cloud providers.
- The [HuggingFace BF16 weights](https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct) served via [glhf.chat](https://glhf.chat).
- [4bit and 8bit quants for mlx](https://t.co/cwX3DYX35D).
- The results from [OpenRouter's mix of providers](https://openrouter.ai/qwen/qwen-2.5-coder-32b-instruct/providers) which serve the model with different levels of quantization.
- Results from individual providers served via OpenRouter and directly to their own APIs.
- Ollama locally serving different quantizations from the [Ollama model library](https://ollama.com/library/qwen2.5-coder:32b-instruct-q4_K_M).
The best versions of the model rival GPT-4o, while the worst performer
is more like the older GPT-4 Turbo.
Suboptimal choices in quantization and token limits can
easily produce far worse results.
This benchmarking effort highlighted a number of pitfalls and details which
can have a significant impact on the model's ability to correctly edit code:
- Quantization -- Open source models are often available at dozens of different quantizations.
- Context window -- Cloud providers can decide how large a context window to accept,
and they often choose differently. Ollama defaults to a tiny 2k context window,
and silently discards data that exceeds it.
- Output token limits -- Open source models are often served with wildly
differing output token limits. This has a direct impact on how much code the
model can write or edit in a response.
- Buggy cloud providers -- Between Qwen and DeepSeep, there were
multiple cloud providers with broken or buggy API endpoints that seemed
to be returning result different from expected based on the advertised
quantization and context sizes.
### Sections
{: .no_toc }
- TOC
{:toc}
## Benchmark results
<canvas id="quantChart" width="800" height="600" style="margin: 20px 0"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
{% include quant-chart.js %}
</script>
<input type="text" id="quantSearchInput" placeholder="Search..." style="width: 100%; max-width: 800px; margin: 10px auto; padding: 8px; display: block; border: 1px solid #ddd; border-radius: 4px;">
<table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;">
<thead style="background-color: #f2f2f2;">
<tr>
<th style="padding: 8px; text-align: left;">Model</th>
<th style="padding: 8px; text-align: center;">Percent completed correctly</th>
<th style="padding: 8px; text-align: center;">Percent using correct edit format</th>
<th style="padding: 8px; text-align: left;">Command</th>
<th style="padding: 8px; text-align: center;">Edit format</th>
</tr>
</thead>
<tbody>
{% assign quant_sorted = site.data.quant | sort: 'pass_rate_2' | reverse %}
{% for row in quant_sorted %}
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 8px;">{{ row.model }}</td>
<td style="padding: 8px; text-align: center;">{{ row.pass_rate_2 }}%</td>
<td style="padding: 8px; text-align: center;">{{ row.percent_cases_well_formed }}%</td>
<td style="padding: 8px;"><code>{{ row.command }}</code></td>
<td style="padding: 8px; text-align: center;">{{ row.edit_format }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<style>
tr.selected {
color: #0056b3;
}
table {
table-layout: fixed;
}
td, th {
word-wrap: break-word;
overflow-wrap: break-word;
}
td:nth-child(3), td:nth-child(4) {
font-size: 12px;
}
</style>
<script>
document.getElementById('quantSearchInput').addEventListener('keyup', function() {
var input = this.value.toLowerCase();
var rows = document.querySelectorAll('tbody tr');
rows.forEach(function(row) {
var text = row.textContent.toLowerCase();
if(text.includes(input)) {
row.style.display = '';
row.classList.add('selected');
} else {
row.style.display = 'none';
row.classList.remove('selected');
}
});
});
</script>
## Setting Ollama's context window size
[Ollama uses a 2k context window by default](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-specify-the-context-window-size),
which is very small for working with aider.
Unlike most other LLM servers, Ollama does not throw an error if you submit
a request that exceeds the context window.
Instead, it just silently truncates the request by discarding the "oldest" messages
in the chat to make it fit within the context window.
All of the Ollama results above were collected with at least an 8k context window, which
is large enough to attempt all the coding problems in the benchmark.
Aider sets Ollama's context window to 8k by default.
You can change the Ollama server's context window with a
[`.aider.model.settings.yml` file](https://aider.chat/docs/config/adv-model-settings.html#model-settings)
like this:
```
- name: ollama/qwen2.5-coder:32b-instruct-fp16
extra_params:
num_ctx: 8192
```
## Choosing providers with OpenRouter
OpenRouter allows you to ignore specific providers in your
[preferences](https://openrouter.ai/settings/preferences).
This can be used to limit your OpenRouter requests to be
served by only your preferred providers.
## Notes
This article went through many revisions as I received feedback from
numerous members of the community.
Here are some of the noteworthy learnings and changes:
- The first version of this article included incorrect Ollama models.
- Earlier Ollama results used the too small default 2k context window,
artificially harming the benchmark results.
- The benchmark results appear to have uncovered a problem in the way
OpenRouter was communicating with Hyperbolic.
They fixed the issue 11/24/24, shortly after it was pointed out.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Before After
Before After

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

File diff suppressed because it is too large Load diff

View file

@ -86,6 +86,14 @@
## Specify a file with context window and costs for unknown models ## Specify a file with context window and costs for unknown models
#model-metadata-file: .aider.model.metadata.json #model-metadata-file: .aider.model.metadata.json
## Add a model alias (can be used multiple times)
#alias: xxx
## Specify multiple values like this:
#alias:
# - xxx
# - yyy
# - zzz
## Verify the SSL cert when connecting to models (default: True) ## Verify the SSL cert when connecting to models (default: True)
#verify-ssl: true #verify-ssl: true
@ -271,8 +279,8 @@
############ ############
# Analytics: # Analytics:
## Enable/disable analytics for one session (default: False) ## Enable/disable analytics for current session (default: random)
#analytics: false #analytics: xxx
## Specify a file to log analytics events ## Specify a file to log analytics events
#analytics-log: xxx #analytics-log: xxx
@ -314,6 +322,9 @@
## Check for new aider versions on launch ## Check for new aider versions on launch
#check-update: true #check-update: true
## Show release notes on first run of new version (default: None, ask user)
#show-release-notes: xxx
## Install the latest version from the main branch ## Install the latest version from the main branch
#install-main-branch: false #install-main-branch: false
@ -365,6 +376,12 @@
## Enable/disable fancy input with history and completion (default: True) ## Enable/disable fancy input with history and completion (default: True)
#fancy-input: true #fancy-input: true
## Enable/disable detection and offering to add URLs to chat (default: True)
#detect-urls: true
## Specify which editor to use for the /editor command
#editor: xxx
################# #################
# Voice Settings: # Voice Settings:

View file

@ -90,6 +90,9 @@
## Specify a file with context window and costs for unknown models ## Specify a file with context window and costs for unknown models
#AIDER_MODEL_METADATA_FILE=.aider.model.metadata.json #AIDER_MODEL_METADATA_FILE=.aider.model.metadata.json
## Add a model alias (can be used multiple times)
#AIDER_ALIAS=
## Verify the SSL cert when connecting to models (default: True) ## Verify the SSL cert when connecting to models (default: True)
#AIDER_VERIFY_SSL=true #AIDER_VERIFY_SSL=true
@ -270,8 +273,8 @@
############ ############
# Analytics: # Analytics:
## Enable/disable analytics for one session (default: False) ## Enable/disable analytics for current session (default: random)
#AIDER_ANALYTICS=false #AIDER_ANALYTICS=
## Specify a file to log analytics events ## Specify a file to log analytics events
#AIDER_ANALYTICS_LOG= #AIDER_ANALYTICS_LOG=
@ -300,6 +303,9 @@
## Check for new aider versions on launch ## Check for new aider versions on launch
#AIDER_CHECK_UPDATE=true #AIDER_CHECK_UPDATE=true
## Show release notes on first run of new version (default: None, ask user)
#AIDER_SHOW_RELEASE_NOTES=
## Install the latest version from the main branch ## Install the latest version from the main branch
#AIDER_INSTALL_MAIN_BRANCH=false #AIDER_INSTALL_MAIN_BRANCH=false
@ -348,6 +354,12 @@
## Enable/disable fancy input with history and completion (default: True) ## Enable/disable fancy input with history and completion (default: True)
#AIDER_FANCY_INPUT=true #AIDER_FANCY_INPUT=true
## Enable/disable detection and offering to add URLs to chat (default: True)
#AIDER_DETECT_URLS=true
## Specify which editor to use for the /editor command
#AIDER_EDITOR=
################# #################
# Voice Settings: # Voice Settings:

View file

@ -11,6 +11,13 @@ description: Configuring advanced settings for LLMs.
In most cases, you can safely ignore aider's warning about unknown context In most cases, you can safely ignore aider's warning about unknown context
window size and model costs. window size and model costs.
{: .note }
Aider never *enforces* token limits, it only *reports* token limit errors
from the API provider.
You probably don't need to
configure aider with the proper token limits
for unusual models.
But, you can register context window limits and costs for models that aren't known But, you can register context window limits and costs for models that aren't known
to aider. Create a `.aider.model.metadata.json` file in one of these locations: to aider. Create a `.aider.model.metadata.json` file in one of these locations:
@ -55,8 +62,10 @@ These model settings are pre-configured for most popular models.
But it can sometimes be helpful to override them or add settings for But it can sometimes be helpful to override them or add settings for
a model that aider doesn't know about. a model that aider doesn't know about.
To do that,
create a `.aider.model.settings.yml` file in one of these locations: ### Configuration file locations
You can override or add settings for any model by creating a `.aider.model.settings.yml` file in one of these locations:
- Your home directory. - Your home directory.
- The root if your git repo. - The root if your git repo.
@ -66,9 +75,31 @@ create a `.aider.model.settings.yml` file in one of these locations:
If the files above exist, they will be loaded in that order. If the files above exist, they will be loaded in that order.
Files loaded last will take priority. Files loaded last will take priority.
The yaml file should be a a list of dictionary objects for each model. The yaml file should be a list of dictionary objects for each model.
For example, below are all the pre-configured model settings
to give a sense for the settings which are supported.
### Global extra params
You can use the special model name `aider/extra_params` to define
`extra_params` that will be passed to `litellm.completion()` for all models.
Only the `extra_params` dict is used from this special model name.
For example:
```yaml
- name: aider/extra_params
extra_params:
extra_headers:
Custom-Header: value
max_tokens: 8192
```
These settings will be merged with any model-specific settings, with the
`aider/extra_params` settings taking precedence for any direct conflicts.
### Example model settings
Below are all the pre-configured model settings to give a sense for the settings which are supported.
You can also look at the `ModelSettings` class in You can also look at the `ModelSettings` class in
[models.py](https://github.com/Aider-AI/aider/blob/main/aider/models.py) [models.py](https://github.com/Aider-AI/aider/blob/main/aider/models.py)
@ -241,6 +272,38 @@ cog.out("```\n")
use_system_prompt: true use_system_prompt: true
use_temperature: true use_temperature: true
weak_model_name: gpt-4o-mini weak_model_name: gpt-4o-mini
- cache_control: false
caches_by_default: false
edit_format: diff
editor_edit_format: null
editor_model_name: null
examples_as_sys_msg: false
extra_params: null
lazy: true
name: gpt-4o-2024-11-20
reminder: sys
send_undo_reply: false
streaming: true
use_repo_map: true
use_system_prompt: true
use_temperature: true
weak_model_name: gpt-4o-mini
- cache_control: false
caches_by_default: false
edit_format: diff
editor_edit_format: null
editor_model_name: null
examples_as_sys_msg: false
extra_params: null
lazy: true
name: openai/gpt-4o-2024-11-20
reminder: sys
send_undo_reply: false
streaming: true
use_repo_map: true
use_system_prompt: true
use_temperature: true
weak_model_name: gpt-4o-mini
- cache_control: false - cache_control: false
caches_by_default: false caches_by_default: false
edit_format: diff edit_format: diff
@ -1057,7 +1120,7 @@ cog.out("```\n")
name: openai/o1-mini name: openai/o1-mini
reminder: user reminder: user
send_undo_reply: false send_undo_reply: false
streaming: false streaming: true
use_repo_map: true use_repo_map: true
use_system_prompt: false use_system_prompt: false
use_temperature: false use_temperature: false
@ -1073,7 +1136,7 @@ cog.out("```\n")
name: azure/o1-mini name: azure/o1-mini
reminder: user reminder: user
send_undo_reply: false send_undo_reply: false
streaming: false streaming: true
use_repo_map: true use_repo_map: true
use_system_prompt: false use_system_prompt: false
use_temperature: false use_temperature: false
@ -1089,7 +1152,7 @@ cog.out("```\n")
name: o1-mini name: o1-mini
reminder: user reminder: user
send_undo_reply: false send_undo_reply: false
streaming: false streaming: true
use_repo_map: true use_repo_map: true
use_system_prompt: false use_system_prompt: false
use_temperature: false use_temperature: false
@ -1105,7 +1168,7 @@ cog.out("```\n")
name: openai/o1-preview name: openai/o1-preview
reminder: user reminder: user
send_undo_reply: false send_undo_reply: false
streaming: false streaming: true
use_repo_map: true use_repo_map: true
use_system_prompt: false use_system_prompt: false
use_temperature: false use_temperature: false
@ -1121,7 +1184,7 @@ cog.out("```\n")
name: azure/o1-preview name: azure/o1-preview
reminder: user reminder: user
send_undo_reply: false send_undo_reply: false
streaming: false streaming: true
use_repo_map: true use_repo_map: true
use_system_prompt: false use_system_prompt: false
use_temperature: false use_temperature: false
@ -1137,7 +1200,7 @@ cog.out("```\n")
name: o1-preview name: o1-preview
reminder: user reminder: user
send_undo_reply: false send_undo_reply: false
streaming: false streaming: true
use_repo_map: true use_repo_map: true
use_system_prompt: false use_system_prompt: false
use_temperature: false use_temperature: false

View file

@ -142,6 +142,14 @@ cog.outl("```")
## Specify a file with context window and costs for unknown models ## Specify a file with context window and costs for unknown models
#model-metadata-file: .aider.model.metadata.json #model-metadata-file: .aider.model.metadata.json
## Add a model alias (can be used multiple times)
#alias: xxx
## Specify multiple values like this:
#alias:
# - xxx
# - yyy
# - zzz
## Verify the SSL cert when connecting to models (default: True) ## Verify the SSL cert when connecting to models (default: True)
#verify-ssl: true #verify-ssl: true
@ -327,8 +335,8 @@ cog.outl("```")
############ ############
# Analytics: # Analytics:
## Enable/disable analytics for one session (default: False) ## Enable/disable analytics for current session (default: random)
#analytics: false #analytics: xxx
## Specify a file to log analytics events ## Specify a file to log analytics events
#analytics-log: xxx #analytics-log: xxx
@ -370,6 +378,9 @@ cog.outl("```")
## Check for new aider versions on launch ## Check for new aider versions on launch
#check-update: true #check-update: true
## Show release notes on first run of new version (default: None, ask user)
#show-release-notes: xxx
## Install the latest version from the main branch ## Install the latest version from the main branch
#install-main-branch: false #install-main-branch: false
@ -421,6 +432,12 @@ cog.outl("```")
## Enable/disable fancy input with history and completion (default: True) ## Enable/disable fancy input with history and completion (default: True)
#fancy-input: true #fancy-input: true
## Enable/disable detection and offering to add URLs to chat (default: True)
#detect-urls: true
## Specify which editor to use for the /editor command
#editor: xxx
################# #################
# Voice Settings: # Voice Settings:

View file

@ -132,6 +132,9 @@ cog.outl("```")
## Specify a file with context window and costs for unknown models ## Specify a file with context window and costs for unknown models
#AIDER_MODEL_METADATA_FILE=.aider.model.metadata.json #AIDER_MODEL_METADATA_FILE=.aider.model.metadata.json
## Add a model alias (can be used multiple times)
#AIDER_ALIAS=
## Verify the SSL cert when connecting to models (default: True) ## Verify the SSL cert when connecting to models (default: True)
#AIDER_VERIFY_SSL=true #AIDER_VERIFY_SSL=true
@ -312,8 +315,8 @@ cog.outl("```")
############ ############
# Analytics: # Analytics:
## Enable/disable analytics for one session (default: False) ## Enable/disable analytics for current session (default: random)
#AIDER_ANALYTICS=false #AIDER_ANALYTICS=
## Specify a file to log analytics events ## Specify a file to log analytics events
#AIDER_ANALYTICS_LOG= #AIDER_ANALYTICS_LOG=
@ -342,6 +345,9 @@ cog.outl("```")
## Check for new aider versions on launch ## Check for new aider versions on launch
#AIDER_CHECK_UPDATE=true #AIDER_CHECK_UPDATE=true
## Show release notes on first run of new version (default: None, ask user)
#AIDER_SHOW_RELEASE_NOTES=
## Install the latest version from the main branch ## Install the latest version from the main branch
#AIDER_INSTALL_MAIN_BRANCH=false #AIDER_INSTALL_MAIN_BRANCH=false
@ -390,6 +396,12 @@ cog.outl("```")
## Enable/disable fancy input with history and completion (default: True) ## Enable/disable fancy input with history and completion (default: True)
#AIDER_FANCY_INPUT=true #AIDER_FANCY_INPUT=true
## Enable/disable detection and offering to add URLs to chat (default: True)
#AIDER_DETECT_URLS=true
## Specify which editor to use for the /editor command
#AIDER_EDITOR=
################# #################
# Voice Settings: # Voice Settings:

View file

@ -0,0 +1,127 @@
---
parent: Configuration
nav_order: 15
description: How to configure a custom editor for aider's /editor command
---
# Editor configuration
Aider allows you to configure your preferred text editor for use with the `/editor` command. The editor must be capable of running in "blocking mode", meaning the command line will wait until you close the editor before proceeding.
## Using `--editor`
You can specify the text editor with the `--editor` switch or using
`editor:` in aider's
[yaml config file](https://aider.chat/docs/config/aider_conf.html).
## Environment variables
Aider checks the following environment variables in order to determine which editor to use:
1. `AIDER_EDITOR`
2. `VISUAL`
3. `EDITOR`
## Default behavior
If no editor is configured, aider will use these platform-specific defaults:
- Windows: `notepad`
- macOS: `vim`
- Linux/Unix: `vi`
## Using a custom editor
You can set your preferred editor in your shell's configuration file (e.g., `.bashrc`, `.zshrc`):
```bash
export AIDER_EDITOR=vim
```
## Popular Editors by Platform
### macOS
1. **vim**
```bash
export AIDER_EDITOR=vim
```
2. **Emacs**
```bash
export AIDER_EDITOR=emacs
```
3. **VSCode**
```bash
export AIDER_EDITOR="code --wait"
```
4. **Sublime Text**
```bash
export AIDER_EDITOR="subl --wait"
```
5. **BBEdit**
```bash
export AIDER_EDITOR="bbedit --wait"
```
### Linux
1. **vim**
```bash
export AIDER_EDITOR=vim
```
2. **Emacs**
```bash
export AIDER_EDITOR=emacs
```
3. **nano**
```bash
export AIDER_EDITOR=nano
```
4. **VSCode**
```bash
export AIDER_EDITOR="code --wait"
```
5. **Sublime Text**
```bash
export AIDER_EDITOR="subl --wait"
```
### Windows
1. **Notepad**
```bat
set AIDER_EDITOR=notepad
```
2. **VSCode**
```bat
set AIDER_EDITOR="code --wait"
```
3. **Notepad++**
```bat
set AIDER_EDITOR="notepad++ -multiInst -notabbar -nosession -noPlugin -waitForClose"
```
## Editor command arguments
Some editors require specific command-line arguments to operate in blocking mode. The `--wait` flag (or equivalent) is commonly used to make the editor block until the file is closed.
## Troubleshooting
If you encounter issues with your editor not blocking (returning to the prompt immediately), verify that:
1. Your editor supports blocking mode
2. You've included the necessary command-line arguments for blocking mode
3. The editor command is properly quoted if it contains spaces or special characters, e.g.:
```bash
export AIDER_EDITOR="code --wait"
```

View file

@ -0,0 +1,72 @@
---
parent: Configuration
nav_order: 1000
description: Assign convenient short names to models.
---
# Model Aliases
Model aliases allow you to create shorthand names for models you frequently use. This is particularly useful for models with long names or when you want to standardize model usage across your team.
## Command Line Usage
You can define aliases when launching aider using the `--alias` option:
```bash
aider --alias "fast:gpt-3.5-turbo" --alias "smart:gpt-4"
```
Multiple aliases can be defined by using the `--alias` option multiple times. Each alias definition should be in the format `alias:model-name`.
## Configuration File
You can also define aliases in your [`.aider.conf.yml` file](https://aider.chat/docs/config/aider_conf.html):
```yaml
alias:
- "fast:gpt-3.5-turbo"
- "smart:gpt-4"
- "hacker:claude-3-sonnet-20240229"
```
## Using Aliases
Once defined, you can use the alias instead of the full model name:
```bash
aider --model fast # Uses gpt-3.5-turbo
aider --model smart # Uses gpt-4
```
## Built-in Aliases
Aider includes some built-in aliases for convenience:
<!--[[[cog
import cog
from aider.models import MODEL_ALIASES
for alias, model in sorted(MODEL_ALIASES.items()):
cog.outl(f"- `{alias}`: {model}")
]]]-->
- `3`: gpt-3.5-turbo
- `35-turbo`: gpt-3.5-turbo
- `35turbo`: gpt-3.5-turbo
- `4`: gpt-4-0613
- `4-turbo`: gpt-4-1106-preview
- `4o`: gpt-4o-2024-08-06
- `deepseek`: deepseek/deepseek-coder
- `haiku`: claude-3-haiku-20241022
- `opus`: claude-3-opus-20240229
- `sonnet`: claude-3-sonnet-20241022
<!--[[[end]]]-->
## Priority
If the same alias is defined in multiple places, the priority is:
1. Command line aliases (highest priority)
2. Configuration file aliases
3. Built-in aliases (lowest priority)
This allows you to override built-in aliases with your own preferences.

View file

@ -32,9 +32,9 @@ usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model]
[--openai-api-type] [--openai-api-version] [--openai-api-type] [--openai-api-version]
[--openai-api-deployment-id] [--openai-organization-id] [--openai-api-deployment-id] [--openai-organization-id]
[--model-settings-file] [--model-metadata-file] [--model-settings-file] [--model-metadata-file]
[--verify-ssl | --no-verify-ssl] [--edit-format] [--alias] [--verify-ssl | --no-verify-ssl]
[--architect] [--weak-model] [--editor-model] [--edit-format] [--architect] [--weak-model]
[--editor-edit-format] [--editor-model] [--editor-edit-format]
[--show-model-warnings | --no-show-model-warnings] [--show-model-warnings | --no-show-model-warnings]
[--max-chat-history-tokens] [--env-file] [--max-chat-history-tokens] [--env-file]
[--cache-prompts | --no-cache-prompts] [--cache-prompts | --no-cache-prompts]
@ -66,14 +66,16 @@ usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model]
[--analytics-disable] [--file] [--read] [--vim] [--analytics-disable] [--file] [--read] [--vim]
[--chat-language] [--version] [--just-check-update] [--chat-language] [--version] [--just-check-update]
[--check-update | --no-check-update] [--check-update | --no-check-update]
[--show-release-notes | --no-show-release-notes]
[--install-main-branch] [--upgrade] [--apply] [--install-main-branch] [--upgrade] [--apply]
[--apply-clipboard-edits] [--yes-always] [-v] [--apply-clipboard-edits] [--yes-always] [-v]
[--show-repo-map] [--show-prompts] [--exit] [--message] [--show-repo-map] [--show-prompts] [--exit] [--message]
[--message-file] [--load] [--encoding] [-c] [--message-file] [--load] [--encoding] [-c]
[--gui | --no-gui | --browser | --no-browser] [--gui | --no-gui | --browser | --no-browser]
[--suggest-shell-commands | --no-suggest-shell-commands] [--suggest-shell-commands | --no-suggest-shell-commands]
[--fancy-input | --no-fancy-input] [--voice-format] [--fancy-input | --no-fancy-input]
[--voice-language] [--detect-urls | --no-detect-urls] [--editor]
[--voice-format] [--voice-language]
``` ```
@ -190,6 +192,10 @@ Specify a file with context window and costs for unknown models
Default: .aider.model.metadata.json Default: .aider.model.metadata.json
Environment variable: `AIDER_MODEL_METADATA_FILE` Environment variable: `AIDER_MODEL_METADATA_FILE`
### `--alias ALIAS:MODEL`
Add a model alias (can be used multiple times)
Environment variable: `AIDER_ALIAS`
### `--verify-ssl` ### `--verify-ssl`
Verify the SSL cert when connecting to models (default: True) Verify the SSL cert when connecting to models (default: True)
Default: True Default: True
@ -510,8 +516,7 @@ Environment variable: `AIDER_TEST`
## Analytics: ## Analytics:
### `--analytics` ### `--analytics`
Enable/disable analytics for one session (default: False) Enable/disable analytics for current session (default: random)
Default: False
Environment variable: `AIDER_ANALYTICS` Environment variable: `AIDER_ANALYTICS`
Aliases: Aliases:
- `--analytics` - `--analytics`
@ -561,6 +566,13 @@ Aliases:
- `--check-update` - `--check-update`
- `--no-check-update` - `--no-check-update`
### `--show-release-notes`
Show release notes on first run of new version (default: None, ask user)
Environment variable: `AIDER_SHOW_RELEASE_NOTES`
Aliases:
- `--show-release-notes`
- `--no-show-release-notes`
### `--install-main-branch` ### `--install-main-branch`
Install the latest version from the main branch Install the latest version from the main branch
Default: False Default: False
@ -666,6 +678,18 @@ Aliases:
- `--fancy-input` - `--fancy-input`
- `--no-fancy-input` - `--no-fancy-input`
### `--detect-urls`
Enable/disable detection and offering to add URLs to chat (default: True)
Default: True
Environment variable: `AIDER_DETECT_URLS`
Aliases:
- `--detect-urls`
- `--no-detect-urls`
### `--editor VALUE`
Specify which editor to use for the /editor command
Environment variable: `AIDER_EDITOR`
## Voice Settings: ## Voice Settings:
### `--voice-format VOICE_FORMAT` ### `--voice-format VOICE_FORMAT`

View file

@ -60,6 +60,23 @@ directory you start in.
You can also create a `.aiderignore` file to tell aider You can also create a `.aiderignore` file to tell aider
to ignore parts of the repo that aren't relevant to your task. to ignore parts of the repo that aren't relevant to your task.
This file conforms to `.gitignore` syntax and conventions. This file conforms to `.gitignore` syntax and conventions.
For example, to focus only on specific directories in a monorepo,
you could create a `.aiderignore` file with:
```
# Ignore everything
/*
# Allow specific directories and their contents
!foo/
!bar/
!baz/
# Allow nested files under these directories
!foo/**
!bar/**
!baz/**
```
You can use `--aiderignore <filename>` to name a specific file You can use `--aiderignore <filename>` to name a specific file
to use for ignore patterns. to use for ignore patterns.
@ -193,7 +210,7 @@ You can also refer to the
## How are the "aider wrote xx% of code" stats computed? ## How are the "aider wrote xx% of code" stats computed?
[Aider is tightly integrated with git](/docs/git.html) so all [Aider is tightly integrated with git](/docs/git.html) so all
one of aider's code changes are committed to the repo with proper attribution. of aider's code changes are committed to the repo with proper attribution.
The The
[stats are computed](https://github.com/Aider-AI/aider/blob/main/scripts/blame.py) [stats are computed](https://github.com/Aider-AI/aider/blob/main/scripts/blame.py)
by doing something like `git blame` on the repo, by doing something like `git blame` on the repo,

View file

@ -62,6 +62,7 @@ cog.out(get_supported_languages_md())
| cpp | .cc | ✓ | ✓ | | cpp | .cc | ✓ | ✓ |
| cpp | .cpp | ✓ | ✓ | | cpp | .cpp | ✓ | ✓ |
| css | .css | | ✓ | | css | .css | | ✓ |
| dart | .dart | ✓ | ✓ |
| dockerfile | .dockerfile | | ✓ | | dockerfile | .dockerfile | | ✓ |
| dot | .dot | | ✓ | | dot | .dot | | ✓ |
| elisp | .el | ✓ | ✓ | | elisp | .el | ✓ | ✓ |

View file

@ -31,6 +31,8 @@ This measures the LLM's coding ability, and whether it can
write new code that integrates into existing code. write new code that integrates into existing code.
The model also has to successfully apply all its changes to the source file without human intervention. The model also has to successfully apply all its changes to the source file without human intervention.
<input type="text" id="editSearchInput" placeholder="Search..." style="width: 100%; max-width: 800px; margin: 10px auto; padding: 8px; display: block; border: 1px solid #ddd; border-radius: 4px;">
<table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;"> <table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;">
<thead style="background-color: #f2f2f2;"> <thead style="background-color: #f2f2f2;">
<tr> <tr>
@ -58,81 +60,7 @@ The model also has to successfully apply all its changes to the source file with
<canvas id="editChart" width="800" height="450" style="margin-top: 20px"></canvas> <canvas id="editChart" width="800" height="450" style="margin-top: 20px"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { {% include edit-leaderboard.js %}
var ctx = document.getElementById('editChart').getContext('2d');
const HIGHTLIGHT_MODEL = 'no no no no';
var leaderboardData = {
labels: [],
datasets: [{
label: 'Percent completed correctly',
data: [],
backgroundColor: function(context) {
const label = context.chart.data.labels[context.dataIndex] || '';
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 0.2)' : 'rgba(54, 162, 235, 0.2)';
},
borderColor: function(context) {
const label = context.chart.data.labels[context.dataIndex] || '';
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)';
},
borderWidth: 1
}]
};
var allData = [];
{% for row in edit_sorted %}
allData.push({
model: '{{ row.model }}',
pass_rate_2: {{ row.pass_rate_2 }},
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
});
{% endfor %}
function updateChart() {
var selectedRows = document.querySelectorAll('tr.selected');
var showAll = selectedRows.length === 0;
leaderboardData.labels = [];
leaderboardData.datasets[0].data = [];
allData.forEach(function(row, index) {
var rowElement = document.getElementById('edit-row-' + index);
if (showAll) {
rowElement.classList.remove('selected');
}
if (showAll || rowElement.classList.contains('selected')) {
leaderboardData.labels.push(row.model);
leaderboardData.datasets[0].data.push(row.pass_rate_2);
}
});
leaderboardChart.update();
}
var tableBody = document.querySelector('table tbody');
allData.forEach(function(row, index) {
var tr = tableBody.children[index];
tr.id = 'edit-row-' + index;
tr.style.cursor = 'pointer';
tr.onclick = function() {
this.classList.toggle('selected');
updateChart();
};
});
var leaderboardChart = new Chart(ctx, {
type: 'bar',
data: leaderboardData,
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
updateChart();
});
</script> </script>
<style> <style>
tr.selected { tr.selected {
@ -158,6 +86,8 @@ The refactoring benchmark requires a large context window to
work with large source files. work with large source files.
Therefore, results are available for fewer models. Therefore, results are available for fewer models.
<input type="text" id="refacSearchInput" placeholder="Search..." style="width: 100%; max-width: 800px; margin: 10px auto; padding: 8px; display: block; border: 1px solid #ddd; border-radius: 4px;">
<table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;"> <table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;">
<thead style="background-color: #f2f2f2;"> <thead style="background-color: #f2f2f2;">
<tr> <tr>
@ -185,74 +115,7 @@ Therefore, results are available for fewer models.
<canvas id="refacChart" width="800" height="450" style="margin-top: 20px"></canvas> <canvas id="refacChart" width="800" height="450" style="margin-top: 20px"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { {% include refactor-leaderboard.js %}
var ctx = document.getElementById('refacChart').getContext('2d');
var leaderboardData = {
labels: [],
datasets: [{
label: 'Percent completed correctly',
data: [],
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
};
var allData = [];
{% for row in refac_sorted %}
allData.push({
model: '{{ row.model }}',
pass_rate_1: {{ row.pass_rate_1 }},
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
});
{% endfor %}
function updateChart() {
var selectedRows = document.querySelectorAll('tr.selected');
var showAll = selectedRows.length === 0;
leaderboardData.labels = [];
leaderboardData.datasets[0].data = [];
allData.forEach(function(row, index) {
var rowElement = document.getElementById('refac-row-' + index);
if (showAll) {
rowElement.classList.remove('selected');
}
if (showAll || rowElement.classList.contains('selected')) {
leaderboardData.labels.push(row.model);
leaderboardData.datasets[0].data.push(row.pass_rate_1);
}
});
leaderboardChart.update();
}
var tableBody = document.querySelectorAll('table tbody')[1];
allData.forEach(function(row, index) {
var tr = tableBody.children[index];
tr.id = 'refac-row-' + index;
tr.style.cursor = 'pointer';
tr.onclick = function() {
this.classList.toggle('selected');
updateChart();
};
});
var leaderboardChart = new Chart(ctx, {
type: 'bar',
data: leaderboardData,
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
updateChart();
});
</script> </script>
@ -318,6 +181,6 @@ mod_dates = [get_last_modified_date(file) for file in files]
latest_mod_date = max(mod_dates) latest_mod_date = max(mod_dates)
cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}") cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}")
]]]--> ]]]-->
November 11, 2024. November 24, 2024.
<!--[[[end]]]--> <!--[[[end]]]-->
</p> </p>

View file

@ -1,22 +0,0 @@
---
parent: Connecting to LLMs
nav_order: 850
---
# Editing format
Aider uses different "edit formats" to collect code edits from different LLMs.
The "whole" format is the easiest for an LLM to use, but it uses a lot of tokens
and may limit how large a file can be edited.
Models which can use one of the diff formats are much more efficient,
using far fewer tokens.
Models that use a diff-like format are able to
edit larger files with less cost and without hitting token limits.
Aider is configured to use the best edit format for the popular OpenAI and Anthropic models
and the [other models recommended on the LLM page](https://aider.chat/docs/llms.html).
For lesser known models aider will default to using the "whole" editing format
since it is the easiest format for an LLM to use.
If you would like to experiment with the more advanced formats, you can
use these switches: `--edit-format diff` or `--edit-format udiff`.

View file

@ -0,0 +1,26 @@
---
parent: Connecting to LLMs
nav_order: 400
---
# LM Studio
To use LM Studio:
```
python -m pip install -U aider-chat
export LM_STUDIO_API_KEY=<key> # Mac/Linux
setx LM_STUDIO_API_KEY <key> # Windows, restart shell after setx
export LM_STUDIO_API_BASE=<url> # Mac/Linux
setx LM_STUDIO_API_BASE <url> # Windows, restart shell after setx
aider --model lm_studio/<your-model-name>
```
See the [model warnings](warnings.html)
section for information on warnings which will occur
when working with models that aider is not familiar with.

View file

@ -20,24 +20,49 @@ python -m pip install -U aider-chat
export OLLAMA_API_BASE=http://127.0.0.1:11434 # Mac/Linux export OLLAMA_API_BASE=http://127.0.0.1:11434 # Mac/Linux
setx OLLAMA_API_BASE http://127.0.0.1:11434 # Windows, restart shell after setx setx OLLAMA_API_BASE http://127.0.0.1:11434 # Windows, restart shell after setx
aider --model ollama/<model> aider --model ollama_chat/<model>
``` ```
In particular, `llama3:70b` works well with aider: {: .note }
Using `ollama_chat/` is recommended over `ollama/`.
```
ollama pull llama3:70b
ollama serve
# In another terminal window...
export OLLAMA_API_BASE=http://127.0.0.1:11434 # Mac/Linux
setx OLLAMA_API_BASE http://127.0.0.1:11434 # Windows, restart shell after setx
aider --model ollama/llama3:70b
```
See the [model warnings](warnings.html) See the [model warnings](warnings.html)
section for information on warnings which will occur section for information on warnings which will occur
when working with models that aider is not familiar with. when working with models that aider is not familiar with.
## API Key
If you are using an ollama that requires an API key you can set `OLLAMA_API_KEY`:
```
export OLLAMA_API_KEY=<api-key> # Mac/Linux
setx OLLAMA_API_KEY <api-key> # Windows, restart shell after setx
```
## Setting the context window size
[Ollama uses a 2k context window by default](https://github.com/ollama/ollama/blob/main/docs/faq.md#how-can-i-specify-the-context-window-size),
which is very small for working with aider.
Aider sets Ollama's context window to 8k by default.
If you would like
a larger context window
you can use a
[`.aider.model.settings.yml` file](https://aider.chat/docs/config/adv-model-settings.html#model-settings)
like this:
```
- name: ollama/qwen2.5-coder:32b-instruct-fp16
extra_params:
num_ctx: 8192
```
Unlike most other LLM servers, Ollama does not throw an error if you submit
a request that exceeds the context window.
Instead, it just silently truncates the request by discarding the "oldest" messages
in the chat to make it fit within the context window.
So if your context window is too small, you won't get an error.
Aider will probably just fail to work well and experience
a lot of
[file editing problems](https://aider.chat/docs/troubleshooting/edit-errors.html).

View file

@ -0,0 +1,24 @@
---
parent: Connecting to LLMs
nav_order: 400
---
# xAI
You'll need a [xAI API key](https://console.x.ai.).
To use xAI:
```
python -m pip install -U aider-chat
export XAI_API_KEY=<key> # Mac/Linux
setx XAI_API_KEY <key> # Windows, restart shell after setx
aider --model xai/grok-beta
# List models available from xAI
aider --list-models xai/
```

View file

@ -31,7 +31,7 @@ features and commands are most used.
It also helps uncover bugs that users are experiencing, so that they can be fixed It also helps uncover bugs that users are experiencing, so that they can be fixed
in upcoming releases. in upcoming releases.
## Enabling & disabling analytics ## Disabling analytics
You can opt out of analytics forever by running this command one time: You can opt out of analytics forever by running this command one time:
@ -39,10 +39,27 @@ You can opt out of analytics forever by running this command one time:
aider --analytics-disable aider --analytics-disable
``` ```
To enable analytics for a single session, you can run aider with `--analytics`. ## Enabling analytics
This will *not* have any effect if you have permanently disabled analytics with the previous command.
The first time, you will need to agree to opt-in. The `--[no-]analytics` switch controls whether analytics are enabled for the
current session:
- `--analytics` will turn on analytics for the current session.
This will *not* have any effect if you have permanently disabled analytics
with `--analytics-disable`.
If this is the first time you have enabled analytics, aider
will confirm you wish to opt-in to analytics.
- `--no-analytics` will turn off analytics for the current session.
- By default, if you don't provide `--analytics` or `--no-analytics`,
aider will enable analytics for a random subset of users.
This will never happen if you have permanently disabled analytics
with `--analytics-disable`.
Randomly selected users will be asked if they wish to opt-in to analytics.
## Opting in
The first time analytics are enabled, you will need to agree to opt-in.
``` ```
aider --analytics aider --analytics
@ -53,13 +70,8 @@ For more info: https://aider.chat/docs/more/analytics.html
Allow collection of anonymous analytics to help improve aider? (Y)es/(N)o [Yes]: Allow collection of anonymous analytics to help improve aider? (Y)es/(N)o [Yes]:
``` ```
If you've added `analytics: true` to your If you say "no", analytics will be permanently disabled.
[yaml config file](/docs/config/aider_conf.html),
you can disable analytics for a single session, you can run:
```
aider --no-analytics
```
## Details about data being collected ## Details about data being collected

View file

@ -57,7 +57,6 @@ cog.out(model_list)
]]]--> ]]]-->
- anthropic.claude-3-5-haiku-20241022-v1:0 - anthropic.claude-3-5-haiku-20241022-v1:0
- anthropic.claude-3-5-sonnet-20241022-v2:0 - anthropic.claude-3-5-sonnet-20241022-v2:0
- anthropic/claude-3-5-sonnet-20241022
- claude-3-5-haiku-20241022 - claude-3-5-haiku-20241022
- claude-3-5-sonnet-20240620 - claude-3-5-sonnet-20240620
- claude-3-5-sonnet-20241022 - claude-3-5-sonnet-20241022
@ -91,11 +90,17 @@ cog.out(model_list)
- openrouter/anthropic/claude-3.5-sonnet - openrouter/anthropic/claude-3.5-sonnet
- us.anthropic.claude-3-5-haiku-20241022-v1:0 - us.anthropic.claude-3-5-haiku-20241022-v1:0
- us.anthropic.claude-3-5-sonnet-20241022-v2:0 - us.anthropic.claude-3-5-sonnet-20241022-v2:0
- vertex_ai/claude-3-5-haiku
- vertex_ai/claude-3-5-haiku@20241022 - vertex_ai/claude-3-5-haiku@20241022
- vertex_ai/claude-3-5-sonnet
- vertex_ai/claude-3-5-sonnet-v2
- vertex_ai/claude-3-5-sonnet-v2@20241022 - vertex_ai/claude-3-5-sonnet-v2@20241022
- vertex_ai/claude-3-5-sonnet@20240620 - vertex_ai/claude-3-5-sonnet@20240620
- vertex_ai/claude-3-haiku
- vertex_ai/claude-3-haiku@20240307 - vertex_ai/claude-3-haiku@20240307
- vertex_ai/claude-3-opus
- vertex_ai/claude-3-opus@20240229 - vertex_ai/claude-3-opus@20240229
- vertex_ai/claude-3-sonnet
- vertex_ai/claude-3-sonnet@20240229 - vertex_ai/claude-3-sonnet@20240229
<!--[[[end]]]--> <!--[[[end]]]-->

View file

@ -95,3 +95,6 @@ io = InputOutput(yes=True)
coder = Coder.create(model=model, fnames=fnames, io=io) coder = Coder.create(model=model, fnames=fnames, io=io)
``` ```
{: .note }
The scripting API is not officially supported or documented and may
change without warning.

View file

@ -19,31 +19,7 @@ LLM edits that are "almost" correctly formatted.
But sometimes the LLM just won't cooperate. But sometimes the LLM just won't cooperate.
In these cases, here are some things you might try. In these cases, here are some things you might try.
## Use a capable model ## Don't add too many files
If possible try using GPT-4o, Claude 3.5 Sonnet or Claude 3 Opus,
as they are the strongest and most capable models.
Weaker models
are more prone to
disobeying the system prompt instructions.
Most local models are just barely capable of working with aider,
so editing errors are probably unavoidable.
Local models which have been quantized are even more likely to have problems
because they are not capable enough to follow aider's system prompts.
## Try the whole format
Run aider with `--edit-format whole` if the model is using a different edit format.
You can see which edit format it is using in the announce lines:
```
Aider v0.50.2-dev
Models: claude-3-5-sonnet-20240620 with ♾️ diff edit format
```
## Reduce distractions
Many LLMs now have very large context windows, Many LLMs now have very large context windows,
but filling them with irrelevant code or conversation but filling them with irrelevant code or conversation
@ -55,6 +31,38 @@ Aider also sends the LLM a [map of your entire git repo](https://aider.chat/docs
- Use `/clear` to remove the conversation history, again to help the LLM focus. - Use `/clear` to remove the conversation history, again to help the LLM focus.
- Use `/tokens` to see how many tokens you are using for each message. - Use `/tokens` to see how many tokens you are using for each message.
## Use a more capable model
If possible try using GPT-4o, Claude 3.5 Sonnet or Claude 3 Opus,
as they are the strongest and most capable models.
Weaker models
are more prone to
disobeying the system prompt instructions.
Most local models are just barely capable of working with aider,
so editing errors are probably unavoidable.
## Local models: context window and quantization
Be especially careful about the
[Ollama context window](https://aider.chat/docs/llms/ollama.html#setting-the-context-window-size)
when working with local models.
It defaults to be very small and silently discards data if you exceed it.
Local models which have been quantized are more likely to have editing problems
because they are not capable enough to follow aider's system prompts.
## Try the whole edit format
Run aider with `--edit-format whole` if were using a different edit format.
You can see which edit format it is using in the announce lines:
```
Aider v0.50.2-dev
Models: claude-3-5-sonnet-20240620 with ♾️ diff edit format
```
## More help ## More help
{% include help.md %} {% include help.md %}

View file

@ -33,6 +33,13 @@ To reduce output tokens:
For more info: https://aider.chat/docs/token-limits.html For more info: https://aider.chat/docs/token-limits.html
``` ```
{: .note }
Aider never *enforces* token limits, it only *reports* token limit errors
from the API provider.
You probably don't need to
[configure aider with the proper token limits](http://0.0.0.0:4000/docs/config/adv-model-settings.html#context-window-size-and-token-costs)
for unusual models.
## Input tokens & context window size ## Input tokens & context window size
The most common problem is trying to send too much data to a The most common problem is trying to send too much data to a

View file

@ -3,7 +3,14 @@ parent: Usage
nav_order: 50 nav_order: 50
description: Control aider with in-chat commands like /add, /model, etc. description: Control aider with in-chat commands like /add, /model, etc.
--- ---
# In-chat commands # In-chat commands
{: .no_toc }
- TOC
{:toc}
## Slash commands
Aider supports commands from within the chat, which all start with `/`. Aider supports commands from within the chat, which all start with `/`.
@ -24,6 +31,7 @@ cog.out(get_help_md())
| **/copy** | Copy the last assistant message to the clipboard | | **/copy** | Copy the last assistant message to the clipboard |
| **/diff** | Display the diff of changes since the last message | | **/diff** | Display the diff of changes since the last message |
| **/drop** | Remove files from the chat session to free up context space | | **/drop** | Remove files from the chat session to free up context space |
| **/editor** | Open an editor to write a prompt |
| **/exit** | Exit the application | | **/exit** | Exit the application |
| **/git** | Run a git command (output excluded from chat) | | **/git** | Run a git command (output excluded from chat) |
| **/help** | Ask questions about aider | | **/help** | Ask questions about aider |

View file

@ -1,7 +1,7 @@
--- ---
parent: Usage parent: Usage
nav_order: 60 nav_order: 60
description: Using the chat, ask and help chat modes. description: Using the code, architect, ask and help chat modes.
--- ---
# Chat modes # Chat modes

View file

@ -2,6 +2,12 @@
nav_exclude: true nav_exclude: true
--- ---
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com;
connect-src http: https:;
style-src 'self' 'unsafe-inline';">
# Shared aider chat transcript # Shared aider chat transcript
A user has shared the following transcript of a pair programming chat session A user has shared the following transcript of a pair programming chat session
@ -37,11 +43,29 @@ print("goodbye")
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
<script> <script>
function isValidUrl(url) {
try {
const urlObj = new URL(url);
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
} catch {
return false;
}
}
// Configure marked with secure defaults
marked.setOptions({
headerIds: false,
mangle: false
});
window.onload = function() { window.onload = function() {
var urlParams = new URLSearchParams(window.location.search); var urlParams = new URLSearchParams(window.location.search);
var conv = urlParams.get('mdurl'); var conv = urlParams.get('mdurl');
if (!conv) { if (!conv || !isValidUrl(conv)) {
document.querySelector('#shared-transcript').innerHTML =
'<div style="color: red; padding: 1em;">Error: Invalid or missing URL provided</div>';
return; return;
} }
document.getElementById('mdurl').href = conv; document.getElementById('mdurl').href = conv;
@ -63,11 +87,14 @@ window.onload = function() {
return line; return line;
}).join('\n'); }).join('\n');
var html = marked.parse(markdown); var html = marked.parse(markdown);
var sanitizedHtml = DOMPurify.sanitize(html);
var divElement = document.querySelector('#shared-transcript'); var divElement = document.querySelector('#shared-transcript');
divElement.innerHTML = html; divElement.innerHTML = sanitizedHtml;
}) })
.catch(error => { .catch(error => {
console.error('Error fetching markdown:', error); console.error('Error fetching markdown:', error);
document.querySelector('#shared-transcript').innerHTML =
'<div style="color: red; padding: 1em;">Error: Failed to load chat transcript</div>';
}); });
} }
</script> </script>

View file

@ -155,6 +155,9 @@ def main(
tries: int = typer.Option(2, "--tries", "-r", help="Number of tries for running tests"), tries: int = typer.Option(2, "--tries", "-r", help="Number of tries for running tests"),
threads: int = typer.Option(1, "--threads", "-t", help="Number of threads to run in parallel"), threads: int = typer.Option(1, "--threads", "-t", help="Number of threads to run in parallel"),
num_tests: int = typer.Option(-1, "--num-tests", "-n", help="Number of tests to run"), num_tests: int = typer.Option(-1, "--num-tests", "-n", help="Number of tests to run"),
num_ctx: Optional[int] = typer.Option(
None, "--num-ctx", help="Override model context window size"
),
exercises_dir: str = typer.Option( exercises_dir: str = typer.Option(
EXERCISES_DIR_DEFAULT, "--exercises-dir", help="Directory with exercise files" EXERCISES_DIR_DEFAULT, "--exercises-dir", help="Directory with exercise files"
), ),
@ -247,6 +250,7 @@ def main(
max_apply_update_errors, max_apply_update_errors,
editor_model, editor_model,
editor_edit_format, editor_edit_format,
num_ctx,
) )
all_results.append(results) all_results.append(results)
@ -526,6 +530,7 @@ def run_test_real(
max_apply_update_errors, max_apply_update_errors,
editor_model, editor_model,
editor_edit_format, editor_edit_format,
num_ctx=None,
): ):
if not os.path.isdir(testdir): if not os.path.isdir(testdir):
print("Not a dir:", testdir) print("Not a dir:", testdir)
@ -588,6 +593,11 @@ def run_test_real(
editor_model=editor_model, editor_model=editor_model,
editor_edit_format=editor_edit_format, editor_edit_format=editor_edit_format,
) )
if num_ctx:
if not main_model.extra_params:
main_model.extra_params = {}
main_model.extra_params["num_ctx"] = num_ctx
edit_format = edit_format or main_model.edit_format edit_format = edit_format or main_model.edit_format
dump(main_model) dump(main_model)

View file

@ -2,6 +2,7 @@
docker run \ docker run \
-it --rm \ -it --rm \
--add-host=host.docker.internal:host-gateway \
-v `pwd`:/aider \ -v `pwd`:/aider \
-v `pwd`/tmp.benchmarks/.:/benchmarks \ -v `pwd`/tmp.benchmarks/.:/benchmarks \
-e OPENAI_API_KEY=$OPENAI_API_KEY \ -e OPENAI_API_KEY=$OPENAI_API_KEY \

View file

@ -1,121 +1,168 @@
from dataclasses import dataclass
from datetime import date
from typing import Dict, List, Tuple
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import yaml import yaml
from imgcat import imgcat from imgcat import imgcat
from matplotlib import rc from matplotlib import rc
from aider.dump import dump # noqa: 401
@dataclass
class ModelData:
name: str
release_date: date
pass_rate: float
@property
def color(self) -> str:
model = self.name.lower()
if "gemini" in model and "pro" in model:
return "magenta"
if "qwen" in model:
return "darkblue"
if "mistral" in model:
return "cyan"
if "haiku" in model:
return "pink"
if "deepseek" in model:
return "brown"
if "sonnet" in model:
return "orange"
if "-4o" in model:
return "purple"
if "gpt-4" in model:
return "red"
if "gpt-3.5" in model:
return "green"
return "lightblue"
@property
def legend_label(self) -> str:
model = self.name.lower()
if "gemini" in model and "pro" in model:
return "Gemini 1.5 Pro"
if "claude-3-sonnet" in model:
return "Sonnet"
if "o1-preview" in model:
return "O1 Preview"
if "gpt-3.5" in model:
return "GPT-3.5 Turbo"
if "gpt-4-" in model and "-4o" not in model:
return "GPT-4"
if "qwen" in model:
return "Qwen"
if "-4o" in model:
return "GPT-4o"
if "haiku" in model:
return "Haiku"
if "deepseek" in model:
return "DeepSeek"
if "mistral" in model:
return "Mistral"
return model
def get_model_color(model): class BenchmarkPlotter:
default = "lightblue" LABEL_FONT_SIZE = 16
if model == "gpt-4o-mini": def __init__(self):
return default self.setup_plot_style()
if "-4o" in model: def setup_plot_style(self):
return "purple" plt.rcParams["hatch.linewidth"] = 0.5
plt.rcParams["hatch.color"] = "#444444"
rc("font", **{"family": "sans-serif", "sans-serif": ["Helvetica"], "size": 10})
plt.rcParams["text.color"] = "#444444"
if "gpt-4" in model: def load_data(self, yaml_file: str) -> List[ModelData]:
return "red" with open(yaml_file, "r") as file:
data = yaml.safe_load(file)
if "gpt-3.5" in model: models = []
return "green" for entry in data:
if "released" in entry and "pass_rate_2" in entry:
model = ModelData(
name=entry["model"].split("(")[0].strip(),
release_date=entry["released"],
pass_rate=entry["pass_rate_2"],
)
models.append(model)
return models
return default def create_figure(self) -> Tuple[plt.Figure, plt.Axes]:
fig, ax = plt.subplots(figsize=(12, 8))
ax.grid(axis="y", zorder=0, lw=0.2)
for spine in ax.spines.values():
spine.set_edgecolor("#DDDDDD")
spine.set_linewidth(0.5)
return fig, ax
def plot_model_series(self, ax: plt.Axes, models: List[ModelData]):
# Group models by color
color_groups: Dict[str, List[ModelData]] = {}
for model in models:
if model.color not in color_groups:
color_groups[model.color] = []
color_groups[model.color].append(model)
def plot_over_time(yaml_file): # Plot each color group
with open(yaml_file, "r") as file: for color, group in color_groups.items():
data = yaml.safe_load(file) sorted_group = sorted(group, key=lambda x: x.release_date)
dates = [m.release_date for m in sorted_group]
rates = [m.pass_rate for m in sorted_group]
dates = [] # Plot line
pass_rates = [] ax.plot(dates, rates, c=color, alpha=0.5, linewidth=1)
models = []
print("Debug: Raw data from YAML file:") # Plot points
print(data) ax.scatter(dates, rates, c=color, alpha=0.5, s=120)
for entry in data: # Add label for first point
if "released" in entry and "pass_rate_2" in entry: first_model = sorted_group[0]
dates.append(entry["released"]) ax.annotate(
pass_rates.append(entry["pass_rate_2"]) first_model.legend_label,
models.append(entry["model"].split("(")[0].strip()) (first_model.release_date, first_model.pass_rate),
xytext=(10, 5),
textcoords="offset points",
color=color,
alpha=0.8,
fontsize=self.LABEL_FONT_SIZE,
)
print("Debug: Processed data:") def set_labels_and_style(self, ax: plt.Axes):
print("Dates:", dates) ax.set_xlabel("Model release date", fontsize=18, color="#555")
print("Pass rates:", pass_rates) ax.set_ylabel(
print("Models:", models) "Aider code editing benchmark,\npercent completed correctly", fontsize=18, color="#555"
if not dates or not pass_rates:
print(
"Error: No data to plot. Check if the YAML file is empty or if the data is in the"
" expected format."
) )
return ax.set_title("LLM code editing skill by model release date", fontsize=20)
ax.set_ylim(30, 90)
plt.xticks(fontsize=14, rotation=45, ha="right")
plt.tight_layout(pad=1.0)
plt.rcParams["hatch.linewidth"] = 0.5 def save_and_display(self, fig: plt.Figure):
plt.rcParams["hatch.color"] = "#444444" plt.savefig("aider/website/assets/models-over-time.png")
plt.savefig("aider/website/assets/models-over-time.svg")
imgcat(fig)
rc("font", **{"family": "sans-serif", "sans-serif": ["Helvetica"], "size": 10}) def plot(self, yaml_file: str):
plt.rcParams["text.color"] = "#444444" models = self.load_data(yaml_file)
fig, ax = self.create_figure()
fig, ax = plt.subplots(figsize=(12, 6)) # Increase figure size for better visibility self.plot_model_series(ax, models)
self.set_labels_and_style(ax)
print("Debug: Figure created. Plotting data...") self.save_and_display(fig)
ax.grid(axis="y", zorder=0, lw=0.2)
for spine in ax.spines.values():
spine.set_edgecolor("#DDDDDD")
spine.set_linewidth(0.5)
colors = [get_model_color(model) for model in models]
# Separate data points by color
purple_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "purple"]
red_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "red"]
green_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "green"]
# Plot lines for purple, red, and green points
if purple_points:
purple_dates, purple_rates = zip(*sorted(purple_points))
ax.plot(purple_dates, purple_rates, c="purple", alpha=0.5, linewidth=1)
if red_points:
red_dates, red_rates = zip(*sorted(red_points))
ax.plot(red_dates, red_rates, c="red", alpha=0.5, linewidth=1)
if green_points:
green_dates, green_rates = zip(*sorted(green_points))
ax.plot(green_dates, green_rates, c="green", alpha=0.5, linewidth=1)
# Plot all points
ax.scatter(dates, pass_rates, c=colors, alpha=0.5, s=120)
for i, model in enumerate(models):
ax.annotate(
model,
(dates[i], pass_rates[i]),
fontsize=8,
alpha=0.75,
xytext=(5, 5),
textcoords="offset points",
)
ax.set_xlabel("Model release date", fontsize=18, color="#555")
ax.set_ylabel(
"Aider code editing benchmark,\npercent completed correctly", fontsize=18, color="#555"
)
ax.set_title("LLM code editing skill by model release date", fontsize=20)
ax.set_ylim(0, 100) # Adjust y-axis limit to accommodate higher values
plt.xticks(fontsize=14, rotation=45, ha="right") # Rotate x-axis labels for better readability
plt.tight_layout(pad=3.0)
print("Debug: Saving figures...")
plt.savefig("tmp_over_time.png")
plt.savefig("tmp_over_time.svg")
print("Debug: Displaying figure with imgcat...")
imgcat(fig)
print("Debug: Figure generation complete.")
# Example usage def main():
plot_over_time("aider/website/_data/edit_leaderboard.yml") plotter = BenchmarkPlotter()
models = plotter.load_data("aider/website/_data/edit_leaderboard.yml")
# Print release dates and model names
for model in sorted(models, key=lambda x: x.release_date):
print(f"{model.release_date}: {model.name}")
plotter.plot("aider/website/_data/edit_leaderboard.yml")
if __name__ == "__main__":
main()

View file

@ -58,6 +58,7 @@ include = ["aider*", "aider.website"]
"docs/unified-diffs.md", "docs/unified-diffs.md",
"docs/leaderboards/index.md", "docs/leaderboards/index.md",
"assets/**", "assets/**",
"**/.DS_Store",
# [[[end]]] # [[[end]]]
] ]

View file

@ -6,7 +6,7 @@
# #
aiohappyeyeballs==2.4.3 aiohappyeyeballs==2.4.3
# via aiohttp # via aiohttp
aiohttp==3.10.10 aiohttp==3.11.7
# via litellm # via litellm
aiosignal==1.3.1 aiosignal==1.3.1
# via aiohttp # via aiohttp
@ -62,11 +62,11 @@ gitdb==4.0.11
# via gitpython # via gitpython
gitpython==3.1.43 gitpython==3.1.43
# via -r requirements/requirements.in # via -r requirements/requirements.in
grep-ast==0.3.3 grep-ast==0.4.0
# via -r requirements/requirements.in # via -r requirements/requirements.in
h11==0.14.0 h11==0.14.0
# via httpcore # via httpcore
httpcore==1.0.6 httpcore==1.0.7
# via httpx # via httpx
httpx==0.27.2 httpx==0.27.2
# via openai # via openai
@ -86,9 +86,9 @@ importlib-resources==6.4.5
# via -r requirements/requirements.in # via -r requirements/requirements.in
jinja2==3.1.4 jinja2==3.1.4
# via litellm # via litellm
jiter==0.7.0 jiter==0.7.1
# via openai # via openai
json5==0.9.25 json5==0.9.28
# via -r requirements/requirements.in # via -r requirements/requirements.in
jsonschema==4.23.0 jsonschema==4.23.0
# via # via
@ -96,7 +96,7 @@ jsonschema==4.23.0
# litellm # litellm
jsonschema-specifications==2024.10.1 jsonschema-specifications==2024.10.1
# via jsonschema # via jsonschema
litellm==1.51.2 litellm==1.52.16
# via -r requirements/requirements.in # via -r requirements/requirements.in
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via rich # via rich
@ -120,9 +120,9 @@ numpy==1.26.4
# via # via
# -r requirements/requirements.in # -r requirements/requirements.in
# scipy # scipy
openai==1.53.0 openai==1.55.1
# via litellm # via litellm
packaging==24.1 packaging==24.2
# via # via
# -r requirements/requirements.in # -r requirements/requirements.in
# huggingface-hub # huggingface-hub
@ -134,12 +134,14 @@ pexpect==4.9.0
# via -r requirements/requirements.in # via -r requirements/requirements.in
pillow==10.4.0 pillow==10.4.0
# via -r requirements/requirements.in # via -r requirements/requirements.in
posthog==3.7.0 posthog==3.7.3
# via -r requirements/requirements.in # via -r requirements/requirements.in
prompt-toolkit==3.0.48 prompt-toolkit==3.0.48
# via -r requirements/requirements.in # via -r requirements/requirements.in
propcache==0.2.0 propcache==0.2.0
# via yarl # via
# aiohttp
# yarl
psutil==6.1.0 psutil==6.1.0
# via -r requirements/requirements.in # via -r requirements/requirements.in
ptyprocess==0.7.0 ptyprocess==0.7.0
@ -148,11 +150,11 @@ pycodestyle==2.12.1
# via flake8 # via flake8
pycparser==2.22 pycparser==2.22
# via cffi # via cffi
pydantic==2.9.2 pydantic==2.10.2
# via # via
# litellm # litellm
# openai # openai
pydantic-core==2.23.4 pydantic-core==2.27.1
# via pydantic # via pydantic
pydub==0.25.1 pydub==0.25.1
# via -r requirements/requirements.in # via -r requirements/requirements.in
@ -176,7 +178,7 @@ referencing==0.35.1
# via # via
# jsonschema # jsonschema
# jsonschema-specifications # jsonschema-specifications
regex==2024.9.11 regex==2024.11.6
# via tiktoken # via tiktoken
requests==2.32.3 requests==2.32.3
# via # via
@ -185,9 +187,9 @@ requests==2.32.3
# mixpanel # mixpanel
# posthog # posthog
# tiktoken # tiktoken
rich==13.9.3 rich==13.9.4
# via -r requirements/requirements.in # via -r requirements/requirements.in
rpds-py==0.20.1 rpds-py==0.21.0
# via # via
# jsonschema # jsonschema
# referencing # referencing
@ -217,7 +219,7 @@ tokenizers==0.19.1
# via # via
# -r requirements/requirements.in # -r requirements/requirements.in
# litellm # litellm
tqdm==4.66.6 tqdm==4.67.1
# via # via
# huggingface-hub # huggingface-hub
# openai # openai
@ -239,7 +241,7 @@ urllib3==2.2.3
# requests # requests
wcwidth==0.2.13 wcwidth==0.2.13
# via prompt-toolkit # via prompt-toolkit
yarl==1.17.1 yarl==1.18.0
# via aiohttp # via aiohttp
zipp==3.20.2 zipp==3.21.0
# via importlib-metadata # via importlib-metadata

View file

@ -4,109 +4,109 @@
# #
# pip-compile --constraint=requirements.txt --constraint=requirements/requirements-dev.txt --constraint=requirements/requirements-help.txt --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in # pip-compile --constraint=requirements.txt --constraint=requirements/requirements-dev.txt --constraint=requirements/requirements-help.txt --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in
# #
altair==5.4.1 altair==5.5.0
# via streamlit # via streamlit
attrs==24.2.0 attrs==24.2.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# jsonschema # jsonschema
# referencing # referencing
blinker==1.8.2 blinker==1.9.0
# via streamlit # via streamlit
cachetools==5.5.0 cachetools==5.5.0
# via streamlit # via streamlit
certifi==2024.8.30 certifi==2024.8.30
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# requests # requests
charset-normalizer==3.4.0 charset-normalizer==3.4.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# requests # requests
click==8.1.7 click==8.1.7
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# streamlit # streamlit
gitdb==4.0.11 gitdb==4.0.11
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# gitpython # gitpython
gitpython==3.1.43 gitpython==3.1.43
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# streamlit # streamlit
idna==3.10 idna==3.10
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# requests # requests
jinja2==3.1.4 jinja2==3.1.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# altair # altair
# pydeck # pydeck
jsonschema==4.23.0 jsonschema==4.23.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# altair # altair
jsonschema-specifications==2024.10.1 jsonschema-specifications==2024.10.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# jsonschema # jsonschema
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# rich # rich
markupsafe==3.0.2 markupsafe==3.0.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# jinja2 # jinja2
mdurl==0.1.2 mdurl==0.1.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# markdown-it-py # markdown-it-py
narwhals==1.12.1 narwhals==1.14.2
# via altair # via altair
numpy==1.26.4 numpy==1.26.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# pandas # pandas
# pydeck # pydeck
# streamlit # streamlit
packaging==24.1 packaging==24.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# altair # altair
@ -117,27 +117,27 @@ pandas==2.2.3
# streamlit # streamlit
pillow==10.4.0 pillow==10.4.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# streamlit # streamlit
protobuf==5.28.3 protobuf==5.28.3
# via streamlit # via streamlit
pyarrow==18.0.0 pyarrow==18.1.0
# via streamlit # via streamlit
pydeck==0.9.1 pydeck==0.9.1
# via streamlit # via streamlit
pygments==2.18.0 pygments==2.18.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# rich # rich
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# pandas # pandas
pytz==2024.2 pytz==2024.2
@ -146,41 +146,41 @@ pytz==2024.2
# pandas # pandas
referencing==0.35.1 referencing==0.35.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# jsonschema # jsonschema
# jsonschema-specifications # jsonschema-specifications
requests==2.32.3 requests==2.32.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# streamlit # streamlit
rich==13.9.3 rich==13.9.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# streamlit # streamlit
rpds-py==0.20.1 rpds-py==0.21.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# jsonschema # jsonschema
# referencing # referencing
six==1.16.0 six==1.16.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# python-dateutil # python-dateutil
smmap==5.0.1 smmap==5.0.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# gitdb # gitdb
streamlit==1.39.0 streamlit==1.40.2
# via -r requirements/requirements-browser.in # via -r requirements/requirements-browser.in
tenacity==8.5.0 tenacity==8.5.0
# via # via
@ -188,12 +188,12 @@ tenacity==8.5.0
# streamlit # streamlit
toml==0.10.2 toml==0.10.2
# via streamlit # via streamlit
tornado==6.4.1 tornado==6.4.2
# via streamlit # via streamlit
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# altair # altair
@ -204,8 +204,8 @@ tzdata==2024.2
# pandas # pandas
urllib3==2.2.3 urllib3==2.2.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt
# requests # requests

View file

@ -12,27 +12,27 @@ build==1.2.2.post1
# via pip-tools # via pip-tools
certifi==2024.8.30 certifi==2024.8.30
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# requests # requests
cfgv==3.4.0 cfgv==3.4.0
# via pre-commit # via pre-commit
charset-normalizer==3.4.0 charset-normalizer==3.4.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# requests # requests
click==8.1.7 click==8.1.7
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# pip-tools # pip-tools
# typer # typer
codespell==2.3.0 codespell==2.3.0
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
cogapp==3.4.1 cogapp==3.4.1
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
contourpy==1.3.0 contourpy==1.3.1
# via matplotlib # via matplotlib
cycler==0.12.1 cycler==0.12.1
# via matplotlib # via matplotlib
@ -48,28 +48,28 @@ docutils==0.21.2
# sphinx-rtd-theme # sphinx-rtd-theme
filelock==3.16.1 filelock==3.16.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# virtualenv # virtualenv
fonttools==4.54.1 fonttools==4.55.0
# via matplotlib # via matplotlib
identify==2.6.1 identify==2.6.3
# via pre-commit # via pre-commit
idna==3.10 idna==3.10
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# requests # requests
imagesize==1.4.1 imagesize==1.4.1
# via sphinx # via sphinx
imgcat==0.5.0 imgcat==0.6.0
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
iniconfig==2.0.0 iniconfig==2.0.0
# via pytest # via pytest
jinja2==3.1.4 jinja2==3.1.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# sphinx # sphinx
kiwisolver==1.4.7 kiwisolver==1.4.7
# via matplotlib # via matplotlib
@ -77,20 +77,20 @@ lox==0.12.0
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# rich # rich
markupsafe==3.0.2 markupsafe==3.0.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# jinja2 # jinja2
matplotlib==3.9.2 matplotlib==3.9.2
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
mdurl==0.1.2 mdurl==0.1.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# markdown-it-py # markdown-it-py
multiprocess==0.70.17 multiprocess==0.70.17
# via pathos # via pathos
@ -98,15 +98,15 @@ nodeenv==1.9.1
# via pre-commit # via pre-commit
numpy==1.26.4 numpy==1.26.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# contourpy # contourpy
# matplotlib # matplotlib
# pandas # pandas
packaging==24.1 packaging==24.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# build # build
# matplotlib # matplotlib
# pytest # pytest
@ -117,8 +117,8 @@ pathos==0.3.3
# via lox # via lox
pillow==10.4.0 pillow==10.4.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# matplotlib # matplotlib
pip-tools==7.4.1 pip-tools==7.4.1
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
@ -134,8 +134,8 @@ pre-commit==4.0.1
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
pygments==2.18.0 pygments==2.18.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# rich # rich
# sphinx # sphinx
pyparsing==3.2.0 pyparsing==3.2.0
@ -148,26 +148,26 @@ pytest==8.3.3
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# matplotlib # matplotlib
# pandas # pandas
pytz==2024.2 pytz==2024.2
# via pandas # via pandas
pyyaml==6.0.2 pyyaml==6.0.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# pre-commit # pre-commit
requests==2.32.3 requests==2.32.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# sphinx # sphinx
rich==13.9.3 rich==13.9.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# typer # typer
semver==3.0.2 semver==3.0.2
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
@ -175,8 +175,8 @@ shellingham==1.5.4
# via typer # via typer
six==1.16.0 six==1.16.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# python-dateutil # python-dateutil
snowballstemmer==2.2.0 snowballstemmer==2.2.0
# via sphinx # via sphinx
@ -184,7 +184,7 @@ sphinx==8.1.3
# via # via
# sphinx-rtd-theme # sphinx-rtd-theme
# sphinxcontrib-jquery # sphinxcontrib-jquery
sphinx-rtd-theme==3.0.1 sphinx-rtd-theme==3.0.2
# via lox # via lox
sphinxcontrib-applehelp==2.0.0 sphinxcontrib-applehelp==2.0.0
# via sphinx # via sphinx
@ -200,23 +200,23 @@ sphinxcontrib-qthelp==2.0.0
# via sphinx # via sphinx
sphinxcontrib-serializinghtml==2.0.0 sphinxcontrib-serializinghtml==2.0.0
# via sphinx # via sphinx
typer==0.12.5 typer==0.13.1
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# typer # typer
tzdata==2024.2 tzdata==2024.2
# via pandas # via pandas
urllib3==2.2.3 urllib3==2.2.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# requests # requests
virtualenv==20.27.1 virtualenv==20.28.0
# via pre-commit # via pre-commit
wheel==0.44.0 wheel==0.45.1
# via pip-tools # via pip-tools
# The following packages are considered to be unsafe in a requirements file: # The following packages are considered to be unsafe in a requirements file:

View file

@ -6,79 +6,81 @@
# #
aiohappyeyeballs==2.4.3 aiohappyeyeballs==2.4.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# aiohttp # aiohttp
aiohttp==3.10.10 aiohttp==3.11.7
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# huggingface-hub # huggingface-hub
# llama-index-core # llama-index-core
aiosignal==1.3.1 aiosignal==1.3.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# aiohttp # aiohttp
annotated-types==0.7.0 annotated-types==0.7.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# pydantic # pydantic
anyio==4.6.2.post1 anyio==4.6.2.post1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# httpx # httpx
attrs==24.2.0 attrs==24.2.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# aiohttp # aiohttp
certifi==2024.8.30 certifi==2024.8.30
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# httpcore # httpcore
# httpx # httpx
# requests # requests
charset-normalizer==3.4.0 charset-normalizer==3.4.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# requests # requests
click==8.1.7 click==8.1.7
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# nltk # nltk
dataclasses-json==0.6.7 dataclasses-json==0.6.7
# via llama-index-core # via llama-index-core
deprecated==1.2.14 deprecated==1.2.15
# via llama-index-core # via llama-index-core
dirtyjson==1.0.8 dirtyjson==1.0.8
# via llama-index-core # via llama-index-core
filelock==3.16.1 filelock==3.16.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# huggingface-hub # huggingface-hub
# torch # torch
# transformers # transformers
filetype==1.2.0
# via llama-index-core
frozenlist==1.5.0 frozenlist==1.5.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# aiohttp # aiohttp
# aiosignal # aiosignal
fsspec==2024.10.0 fsspec==2024.10.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# huggingface-hub # huggingface-hub
# llama-index-core # llama-index-core
# torch # torch
@ -88,31 +90,31 @@ greenlet==3.0.3
# sqlalchemy # sqlalchemy
h11==0.14.0 h11==0.14.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# httpcore # httpcore
httpcore==1.0.6 httpcore==1.0.7
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# httpx # httpx
httpx==0.27.2 httpx==0.27.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# llama-index-core # llama-index-core
huggingface-hub[inference]==0.26.2 huggingface-hub[inference]==0.26.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# llama-index-embeddings-huggingface # llama-index-embeddings-huggingface
# sentence-transformers # sentence-transformers
# tokenizers # tokenizers
# transformers # transformers
idna==3.10 idna==3.10
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# anyio # anyio
# httpx # httpx
@ -120,34 +122,34 @@ idna==3.10
# yarl # yarl
jinja2==3.1.4 jinja2==3.1.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# torch # torch
joblib==1.4.2 joblib==1.4.2
# via # via
# nltk # nltk
# scikit-learn # scikit-learn
llama-index-core==0.11.21 llama-index-core==0.12.0
# via # via
# -r requirements/requirements-help.in # -r requirements/requirements-help.in
# llama-index-embeddings-huggingface # llama-index-embeddings-huggingface
llama-index-embeddings-huggingface==0.3.1 llama-index-embeddings-huggingface==0.4.0
# via -r requirements/requirements-help.in # via -r requirements/requirements-help.in
markupsafe==3.0.2 markupsafe==3.0.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# jinja2 # jinja2
marshmallow==3.23.0 marshmallow==3.23.1
# via dataclasses-json # via dataclasses-json
mpmath==1.3.0 mpmath==1.3.0
# via sympy # via sympy
multidict==6.1.0 multidict==6.1.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# aiohttp # aiohttp
# yarl # yarl
mypy-extensions==1.0.0 mypy-extensions==1.0.0
@ -156,70 +158,71 @@ nest-asyncio==1.6.0
# via llama-index-core # via llama-index-core
networkx==3.2.1 networkx==3.2.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# llama-index-core # llama-index-core
# torch # torch
nltk==3.9.1 nltk==3.9.1
# via llama-index-core # via llama-index-core
numpy==1.26.4 numpy==1.26.4
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# llama-index-core # llama-index-core
# scikit-learn # scikit-learn
# scipy # scipy
# transformers # transformers
packaging==24.1 packaging==24.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# huggingface-hub # huggingface-hub
# marshmallow # marshmallow
# transformers # transformers
pillow==10.4.0 pillow==10.4.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# llama-index-core # llama-index-core
# sentence-transformers # sentence-transformers
propcache==0.2.0 propcache==0.2.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt # aiohttp
# yarl # yarl
pydantic==2.9.2 pydantic==2.10.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# llama-index-core # llama-index-core
pydantic-core==2.23.4 pydantic-core==2.27.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# pydantic # pydantic
pyyaml==6.0.2 pyyaml==6.0.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# huggingface-hub # huggingface-hub
# llama-index-core # llama-index-core
# transformers # transformers
regex==2024.9.11 regex==2024.11.6
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# nltk # nltk
# tiktoken # tiktoken
# transformers # transformers
requests==2.32.3 requests==2.32.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# huggingface-hub # huggingface-hub
# llama-index-core # llama-index-core
@ -231,16 +234,16 @@ scikit-learn==1.5.2
# via sentence-transformers # via sentence-transformers
scipy==1.13.1 scipy==1.13.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# scikit-learn # scikit-learn
# sentence-transformers # sentence-transformers
sentence-transformers==3.2.1 sentence-transformers==3.3.1
# via llama-index-embeddings-huggingface # via llama-index-embeddings-huggingface
sniffio==1.3.1 sniffio==1.3.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# anyio # anyio
# httpx # httpx
sqlalchemy[asyncio]==2.0.36 sqlalchemy[asyncio]==2.0.36
@ -255,20 +258,20 @@ threadpoolctl==3.5.0
# via scikit-learn # via scikit-learn
tiktoken==0.8.0 tiktoken==0.8.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# llama-index-core # llama-index-core
tokenizers==0.19.1 tokenizers==0.19.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# transformers # transformers
torch==2.2.2 torch==2.2.2
# via sentence-transformers # via sentence-transformers
tqdm==4.66.6 tqdm==4.67.1
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# huggingface-hub # huggingface-hub
# llama-index-core # llama-index-core
# nltk # nltk
@ -278,8 +281,8 @@ transformers==4.44.2
# via sentence-transformers # via sentence-transformers
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# huggingface-hub # huggingface-hub
# llama-index-core # llama-index-core
@ -294,16 +297,16 @@ typing-inspect==0.9.0
# llama-index-core # llama-index-core
urllib3==2.2.3 urllib3==2.2.3
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# requests # requests
wrapt==1.16.0 wrapt==1.17.0
# via # via
# deprecated # deprecated
# llama-index-core # llama-index-core
yarl==1.17.1 yarl==1.18.0
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# aiohttp # aiohttp

View file

@ -15,8 +15,8 @@ pyee==12.0.0
# via playwright # via playwright
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# -c /Users/gauthier/Projects/aider/requirements.txt
# -c requirements.txt # -c requirements.txt
# -c requirements/../requirements.txt
# -c requirements/requirements-browser.txt # -c requirements/requirements-browser.txt
# -c requirements/requirements-dev.txt # -c requirements/requirements-dev.txt
# -c requirements/requirements-help.txt # -c requirements/requirements-help.txt

View file

@ -15,6 +15,6 @@ RUN bundle install --retry 5 --jobs 20
ENTRYPOINT [ "docker-entrypoint.sh" ] ENTRYPOINT [ "docker-entrypoint.sh" ]
# bundle exec jekyll serve --force_polling -H 0.0.0.0 -P 4000 # bundle exec jekyll serve --force_polling -H 0.0.0.0 -P 4000
CMD [ "bundle", "exec", "jekyll", "serve", "--force_polling", "-H", "0.0.0.0", "-P", "4000" ] CMD [ "bundle", "exec", "jekyll", "serve", "--verbose", "--trace", "--force_polling", "-H", "0.0.0.0", "-P", "4000" ]

View file

@ -23,8 +23,10 @@ def blame(start_tag, end_tag=None):
files = [ files = [
f f
for f in files for f in files
if f.endswith((".py", ".scm", ".sh", "Dockerfile", "Gemfile")) if f.endswith((".js", ".py", ".scm", ".sh", "Dockerfile", "Gemfile"))
or (f.startswith(".github/workflows/") and f.endswith(".yml")) or (f.startswith(".github/workflows/") and f.endswith(".yml"))
or f == "aider/website/share/index.md"
or f == "aider/website/docs/leaderboards/index.md"
] ]
files = [f for f in files if not f.endswith("prompts.py")] files = [f for f in files if not f.endswith("prompts.py")]

View file

@ -22,15 +22,27 @@ def has_been_reopened(issue_number):
# Load environment variables from .env file # Load environment variables from .env file
load_dotenv() load_dotenv()
DUPLICATE_COMMENT = """Thanks for trying aider and filing this issue. BOT_SUFFIX = """Note: A [bot script](https://github.com/Aider-AI/aider/blob/main/scripts/issues.py) made these updates to the issue.
""" # noqa
DUPLICATE_COMMENT = (
"""Thanks for trying aider and filing this issue.
This looks like a duplicate of #{oldest_issue_number}. Please see the comments there for more information, and feel free to continue the discussion within that issue. This looks like a duplicate of #{oldest_issue_number}. Please see the comments there for more information, and feel free to continue the discussion within that issue.
I'm going to close this issue for now. But please let me know if you think this is actually a distinct issue and I will reopen this issue.""" # noqa I'm going to close this issue for now. But please let me know if you think this is actually a distinct issue and I will reopen this issue.""" # noqa
+ BOT_SUFFIX
)
STALE_COMMENT = """I'm labeling this issue as stale because it has been open for 2 weeks with no activity. If there are no additional comments, it will be closed in 7 days.""" # noqa STALE_COMMENT = (
"""I'm labeling this issue as stale because it has been open for 2 weeks with no activity. If there are no additional comments, I will close it in 7 days.""" # noqa
+ BOT_SUFFIX
)
CLOSE_STALE_COMMENT = """I'm closing this issue because it has been stalled for 3 weeks with no activity. Feel free to add a comment here and we can re-open it. Or feel free to file a new issue at any time.""" # noqa CLOSE_STALE_COMMENT = (
"""I'm closing this issue because it has been stalled for 3 weeks with no activity. Feel free to add a comment here and we can re-open it. Or feel free to file a new issue at any time.""" # noqa
+ BOT_SUFFIX
)
# GitHub API configuration # GitHub API configuration
GITHUB_API_URL = "https://api.github.com" GITHUB_API_URL = "https://api.github.com"
@ -279,7 +291,7 @@ def handle_stale_closing(all_issues, auto_yes):
continue continue
# Add closing comment # Add closing comment
comment_url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/comments" comment_url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/comments" # noqa
response = requests.post( response = requests.post(
comment_url, headers=headers, json={"body": CLOSE_STALE_COMMENT} comment_url, headers=headers, json={"body": CLOSE_STALE_COMMENT}
) )

View file

@ -24,6 +24,7 @@ cog $ARG \
aider/website/docs/config/options.md \ aider/website/docs/config/options.md \
aider/website/docs/config/aider_conf.md \ aider/website/docs/config/aider_conf.md \
aider/website/docs/config/adv-model-settings.md \ aider/website/docs/config/adv-model-settings.md \
aider/website/docs/config/model-aliases.md \
aider/website/docs/leaderboards/index.md \ aider/website/docs/leaderboards/index.md \
aider/website/docs/llms/other.md \ aider/website/docs/llms/other.md \
aider/website/docs/more/infinite-output.md \ aider/website/docs/more/infinite-output.md \

72
scripts/update-history.py Executable file
View file

@ -0,0 +1,72 @@
#!/usr/bin/env python3
import os
import re
import subprocess
import tempfile
from aider import __version__
def get_base_version():
# Parse current version like "0.64.2.dev" to get major.minor
match = re.match(r"(\d+\.\d+)", __version__)
if not match:
raise ValueError(f"Could not parse version: {__version__}")
return match.group(1) + ".0"
def run_git_log():
base_ver = get_base_version()
cmd = [
"git",
"log",
"-p",
f"v{base_ver}..HEAD",
"--",
"aider/",
":!aider/website/",
":!scripts/",
":!HISTORY.md",
]
result = subprocess.run(cmd, capture_output=True, text=True)
return result.stdout
def main():
# Get the git log output
diff_content = run_git_log()
# Save to temporary file
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".diff") as tmp:
tmp.write(diff_content)
tmp_path = tmp.name
# Run blame to get aider percentage
blame_result = subprocess.run(["python3", "scripts/blame.py"], capture_output=True, text=True)
aider_line = blame_result.stdout.strip().split("\n")[-1] # Get last line with percentage
# Construct and run the aider command
message = f"""
Update the history with changes shown in the diffs.
Describe actual user-facing changes, not every single commit that was made implementing them.
Don't edit or duplicate changes that have existing history entries, just add any new items not already listed.
Be sure to attribute changes to the proper .x version.
Changes in the .x-dev version should be listed under a "### main branch" heading
Also, add this as the last bullet under the "### main branch" section:
{aider_line}
""" # noqa
cmd = ["aider", "HISTORY.md", "--read", tmp_path, "--msg", message, "--no-auto-commit"]
subprocess.run(cmd)
# Run update-docs.sh after aider
subprocess.run(["scripts/update-docs.sh"])
# Cleanup
os.unlink(tmp_path)
if __name__ == "__main__":
main()

View file

@ -124,7 +124,10 @@ def main():
for cmd in git_commands: for cmd in git_commands:
print(f"Running: {' '.join(cmd)}") print(f"Running: {' '.join(cmd)}")
if not dry_run: if not dry_run:
subprocess.run(cmd, check=True) subprocess.run(
cmd,
check=True,
)
new_dev_version = f"{incremented_version}.dev" new_dev_version = f"{incremented_version}.dev"
updated_dev_content = re.sub( updated_dev_content = re.sub(

View file

@ -91,10 +91,49 @@ def test_system_info(temp_data_dir):
def test_need_to_ask(temp_data_dir): def test_need_to_ask(temp_data_dir):
analytics = Analytics() analytics = Analytics()
assert analytics.need_to_ask() is True assert analytics.need_to_ask(True) is True
assert analytics.need_to_ask(False) is False
analytics.user_id = "111"
assert analytics.need_to_ask(None) is False
analytics.user_id = "000"
assert analytics.need_to_ask(None) is True
analytics.asked_opt_in = True analytics.asked_opt_in = True
assert analytics.need_to_ask() is False assert analytics.need_to_ask(True) is False
analytics.permanently_disable = True analytics.permanently_disable = True
assert analytics.need_to_ask() is False assert analytics.need_to_ask(True) is False
def test_is_uuid_in_percentage():
analytics = Analytics()
# Test basic percentage thresholds
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 1) is True
assert analytics.is_uuid_in_percentage("01999000000000000000000000000000", 1) is True
assert analytics.is_uuid_in_percentage("02000000000000000000000000000000", 1) is True
assert analytics.is_uuid_in_percentage("02910000000000000000000000000001", 1) is False
assert analytics.is_uuid_in_percentage("03000000000000000000000000000000", 1) is False
assert analytics.is_uuid_in_percentage("ff000000000000000000000000000000", 1) is False
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 10) is True
assert analytics.is_uuid_in_percentage("19000000000000000000000000000000", 10) is True
assert analytics.is_uuid_in_percentage("1a000000000000000000000000000000", 10) is False
assert analytics.is_uuid_in_percentage("ff000000000000000000000000000000", 10) is False
# Test edge cases
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 0) is False
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 100) is True
assert analytics.is_uuid_in_percentage("ffffffffffffffffffffffffffffffff", 100) is True
# Test invalid inputs
with pytest.raises(ValueError):
analytics.is_uuid_in_percentage("00000000000000000000000000000000", -1)
with pytest.raises(ValueError):
analytics.is_uuid_in_percentage("00000000000000000000000000000000", 101)
# Test empty/None UUID
assert analytics.is_uuid_in_percentage("", 50) is False
assert analytics.is_uuid_in_percentage(None, 50) is False

View file

@ -7,6 +7,7 @@ from unittest.mock import MagicMock, patch
import git import git
from aider.coders import Coder from aider.coders import Coder
from aider.coders.base_coder import UnknownEditFormat
from aider.dump import dump # noqa: F401 from aider.dump import dump # noqa: F401
from aider.io import InputOutput from aider.io import InputOutput
from aider.models import Model from aider.models import Model
@ -168,6 +169,37 @@ class TestCoder(unittest.TestCase):
self.assertEqual(coder.abs_fnames, set([str(fname.resolve())])) self.assertEqual(coder.abs_fnames, set([str(fname.resolve())]))
def test_skip_duplicate_basename_mentions(self):
with GitTemporaryDirectory():
io = InputOutput(pretty=False, yes=True)
coder = Coder.create(self.GPT35, None, io)
# Create files with same basename in different directories
fname1 = Path("dir1") / "file.txt"
fname2 = Path("dir2") / "file.txt"
fname3 = Path("dir3") / "unique.txt"
for fname in [fname1, fname2, fname3]:
fname.parent.mkdir(parents=True, exist_ok=True)
fname.touch()
# Add one file to chat
coder.add_rel_fname(str(fname1))
# Mock get_tracked_files to return all files
mock = MagicMock()
mock.return_value = set([str(fname1), str(fname2), str(fname3)])
coder.repo.get_tracked_files = mock
# Check that file mentions skip files with duplicate basenames
mentioned = coder.get_file_mentions(f"Check {fname2} and {fname3}")
self.assertEqual(mentioned, {str(fname3)})
# Add a read-only file with same basename
coder.abs_read_only_fnames.add(str(fname2.resolve()))
mentioned = coder.get_file_mentions(f"Check {fname1} and {fname3}")
self.assertEqual(mentioned, {str(fname3)})
def test_check_for_file_mentions_read_only(self): def test_check_for_file_mentions_read_only(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
io = InputOutput( io = InputOutput(
@ -821,32 +853,55 @@ This command will print 'Hello, World!' to the console."""
with GitTemporaryDirectory(): with GitTemporaryDirectory():
io = InputOutput(yes=True) io = InputOutput(yes=True)
coder = Coder.create(self.GPT35, "diff", io=io, suggest_shell_commands=False) coder = Coder.create(self.GPT35, "diff", io=io, suggest_shell_commands=False)
self.assertFalse(coder.suggest_shell_commands)
def mock_send(*args, **kwargs): def test_detect_urls_enabled(self):
coder.partial_response_content = """Here's a shell command to run: with GitTemporaryDirectory():
io = InputOutput(yes=True)
coder = Coder.create(self.GPT35, "diff", io=io, detect_urls=True)
coder.commands.scraper = MagicMock()
coder.commands.scraper.scrape = MagicMock(return_value="some content")
```bash # Test with a message containing a URL
echo "Hello, World!" message = "Check out https://example.com"
``` coder.check_for_urls(message)
coder.commands.scraper.scrape.assert_called_once_with("https://example.com")
This command will print 'Hello, World!' to the console.""" def test_detect_urls_disabled(self):
coder.partial_response_function_call = dict() with GitTemporaryDirectory():
return [] io = InputOutput(yes=True)
coder = Coder.create(self.GPT35, "diff", io=io, detect_urls=False)
coder.commands.scraper = MagicMock()
coder.commands.scraper.scrape = MagicMock(return_value="some content")
coder.send = mock_send # Test with a message containing a URL
message = "Check out https://example.com"
result = coder.check_for_urls(message)
self.assertEqual(result, [])
coder.commands.scraper.scrape.assert_not_called()
# Mock the handle_shell_commands method to check if it's called def test_unknown_edit_format_exception(self):
coder.handle_shell_commands = MagicMock() # Test the exception message format
invalid_format = "invalid_format"
valid_formats = ["diff", "whole", "map"]
exc = UnknownEditFormat(invalid_format, valid_formats)
expected_msg = (
f"Unknown edit format {invalid_format}. Valid formats are: {', '.join(valid_formats)}"
)
self.assertEqual(str(exc), expected_msg)
# Run the coder with a message def test_unknown_edit_format_creation(self):
coder.run(with_message="Suggest a shell command") # Test that creating a Coder with invalid edit format raises the exception
io = InputOutput(yes=True)
invalid_format = "invalid_format"
# Check if the shell command was added to the list with self.assertRaises(UnknownEditFormat) as cm:
self.assertEqual(len(coder.shell_commands), 1) Coder.create(self.GPT35, invalid_format, io=io)
self.assertEqual(coder.shell_commands[0].strip(), 'echo "Hello, World!"')
# Check if handle_shell_commands was called with the correct argument exc = cm.exception
coder.handle_shell_commands.assert_not_called() self.assertEqual(exc.edit_format, invalid_format)
self.assertIsInstance(exc.valid_formats, list)
self.assertTrue(len(exc.valid_formats) > 0)
def test_coder_create_with_new_file_oserror(self): def test_coder_create_with_new_file_oserror(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():

View file

@ -1068,8 +1068,10 @@ class TestCommands(TestCase):
io.prompt_ask = lambda *args, **kwargs: "y" io.prompt_ask = lambda *args, **kwargs: "y"
# Test the cmd_run method with a command that should not raise an error # Test the cmd_run method with a command that should not raise an error
result = commands.cmd_run("exit 1", add_on_nonzero_exit=True) commands.cmd_run("exit 1", add_on_nonzero_exit=True)
self.assertIn("I ran this command", result)
# Check that the output was added to cur_messages
self.assertTrue(any("exit 1" in msg["content"] for msg in coder.cur_messages))
def test_cmd_add_drop_untracked_files(self): def test_cmd_add_drop_untracked_files(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():

129
tests/basic/test_editor.py Normal file
View file

@ -0,0 +1,129 @@
import os
from unittest.mock import MagicMock, patch
import pytest
from aider.editor import (
DEFAULT_EDITOR_NIX,
DEFAULT_EDITOR_OS_X,
DEFAULT_EDITOR_WINDOWS,
discover_editor,
get_environment_editor,
pipe_editor,
print_status_message,
write_temp_file,
)
def test_get_environment_editor():
# Test with no environment variables set
with patch.dict(os.environ, {}, clear=True):
assert get_environment_editor("default") == "default"
# Test EDITOR precedence
with patch.dict(os.environ, {"EDITOR": "vim"}):
assert get_environment_editor() == "vim"
# Test VISUAL overrides EDITOR
with patch.dict(os.environ, {"EDITOR": "vim", "VISUAL": "code"}):
assert get_environment_editor() == "code"
def test_discover_editor_defaults():
with patch("platform.system") as mock_system:
# Test Windows default
mock_system.return_value = "Windows"
with patch.dict(os.environ, {}, clear=True):
assert discover_editor() == [DEFAULT_EDITOR_WINDOWS]
# Test macOS default
mock_system.return_value = "Darwin"
with patch.dict(os.environ, {}, clear=True):
assert discover_editor() == [DEFAULT_EDITOR_OS_X]
# Test Linux default
mock_system.return_value = "Linux"
with patch.dict(os.environ, {}, clear=True):
assert discover_editor() == [DEFAULT_EDITOR_NIX]
def test_write_temp_file():
# Test basic file creation
content = "test content"
filepath = write_temp_file(content)
assert os.path.exists(filepath)
with open(filepath, "r") as f:
assert f.read() == content
os.remove(filepath)
# Test with suffix
filepath = write_temp_file("content", suffix="txt")
assert filepath.endswith(".txt")
os.remove(filepath)
# Test with prefix
filepath = write_temp_file("content", prefix="test_")
assert os.path.basename(filepath).startswith("test_")
os.remove(filepath)
def test_print_status_message(capsys):
# Test success message
print_status_message(True, "Success!")
captured = capsys.readouterr()
assert "Success!" in captured.out
# Test failure message
print_status_message(False, "Failed!")
captured = capsys.readouterr()
assert "Failed!" in captured.out
def test_discover_editor_override():
# Test editor override
assert discover_editor("code") == ["code"]
assert discover_editor('vim -c "set noswapfile"') == ["vim", "-c", "set noswapfile"]
# Test invalid editor command
with pytest.raises(RuntimeError):
discover_editor('vim "unclosed quote')
def test_pipe_editor():
# Test with default editor
test_content = "Initial content"
modified_content = "Modified content"
# Mock the file operations and editor call
with (
patch("aider.editor.write_temp_file") as mock_write,
patch("builtins.open") as mock_open,
patch("os.remove") as mock_remove,
patch("subprocess.call") as mock_subprocess,
):
# Setup mocks
mock_write.return_value = "temp.txt"
mock_file = MagicMock()
mock_file.__enter__.return_value.read.return_value = modified_content
mock_open.return_value = mock_file
# Test with default editor
result = pipe_editor(test_content)
assert result == modified_content
mock_write.assert_called_with(test_content, None)
mock_subprocess.assert_called()
# Test with custom editor
result = pipe_editor(test_content, editor="code")
assert result == modified_content
mock_subprocess.assert_called()
# Test with suffix
result = pipe_editor(test_content, suffix="md")
assert result == modified_content
mock_write.assert_called_with(test_content, "md")
# Test cleanup on permission error
mock_remove.side_effect = PermissionError
result = pipe_editor(test_content)
assert result == modified_content

View file

@ -45,7 +45,7 @@ class TestMain(TestCase):
self.webbrowser_patcher.stop() self.webbrowser_patcher.stop()
def test_main_with_empty_dir_no_files_on_command(self): def test_main_with_empty_dir_no_files_on_command(self):
main(["--no-git", "--exit"], input=DummyInput(), output=DummyOutput()) main(["--no-git", "--exit", "--yes"], input=DummyInput(), output=DummyOutput())
def test_main_with_emptqy_dir_new_file(self): def test_main_with_emptqy_dir_new_file(self):
main(["foo.txt", "--yes", "--no-git", "--exit"], input=DummyInput(), output=DummyOutput()) main(["foo.txt", "--yes", "--no-git", "--exit"], input=DummyInput(), output=DummyOutput())
@ -332,7 +332,7 @@ class TestMain(TestCase):
def test_false_vals_in_env_file(self): def test_false_vals_in_env_file(self):
self.create_env_file(".env", "AIDER_SHOW_DIFFS=off") self.create_env_file(".env", "AIDER_SHOW_DIFFS=off")
with patch("aider.coders.Coder.create") as MockCoder: with patch("aider.coders.Coder.create") as MockCoder:
main(["--no-git"], input=DummyInput(), output=DummyOutput()) main(["--no-git", "--yes"], input=DummyInput(), output=DummyOutput())
MockCoder.assert_called_once() MockCoder.assert_called_once()
_, kwargs = MockCoder.call_args _, kwargs = MockCoder.call_args
self.assertEqual(kwargs["show_diffs"], False) self.assertEqual(kwargs["show_diffs"], False)
@ -340,7 +340,7 @@ class TestMain(TestCase):
def test_true_vals_in_env_file(self): def test_true_vals_in_env_file(self):
self.create_env_file(".env", "AIDER_SHOW_DIFFS=on") self.create_env_file(".env", "AIDER_SHOW_DIFFS=on")
with patch("aider.coders.Coder.create") as MockCoder: with patch("aider.coders.Coder.create") as MockCoder:
main(["--no-git"], input=DummyInput(), output=DummyOutput()) main(["--no-git", "--yes"], input=DummyInput(), output=DummyOutput())
MockCoder.assert_called_once() MockCoder.assert_called_once()
_, kwargs = MockCoder.call_args _, kwargs = MockCoder.call_args
self.assertEqual(kwargs["show_diffs"], True) self.assertEqual(kwargs["show_diffs"], True)
@ -381,7 +381,11 @@ class TestMain(TestCase):
def test_verbose_mode_lists_env_vars(self): def test_verbose_mode_lists_env_vars(self):
self.create_env_file(".env", "AIDER_DARK_MODE=on") self.create_env_file(".env", "AIDER_DARK_MODE=on")
with patch("sys.stdout", new_callable=StringIO) as mock_stdout: with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
main(["--no-git", "--verbose", "--exit"], input=DummyInput(), output=DummyOutput()) main(
["--no-git", "--verbose", "--exit", "--yes"],
input=DummyInput(),
output=DummyOutput(),
)
output = mock_stdout.getvalue() output = mock_stdout.getvalue()
relevant_output = "\n".join( relevant_output = "\n".join(
line line
@ -633,6 +637,49 @@ class TestMain(TestCase):
) )
self.assertTrue(coder.suggest_shell_commands) self.assertTrue(coder.suggest_shell_commands)
def test_detect_urls_default(self):
with GitTemporaryDirectory():
coder = main(
["--exit", "--yes"],
input=DummyInput(),
output=DummyOutput(),
return_coder=True,
)
self.assertTrue(coder.detect_urls)
def test_detect_urls_disabled(self):
with GitTemporaryDirectory():
coder = main(
["--no-detect-urls", "--exit", "--yes"],
input=DummyInput(),
output=DummyOutput(),
return_coder=True,
)
self.assertFalse(coder.detect_urls)
def test_detect_urls_enabled(self):
with GitTemporaryDirectory():
coder = main(
["--detect-urls", "--exit", "--yes"],
input=DummyInput(),
output=DummyOutput(),
return_coder=True,
)
self.assertTrue(coder.detect_urls)
def test_invalid_edit_format(self):
with GitTemporaryDirectory():
with patch("aider.io.InputOutput.offer_url") as mock_offer_url:
result = main(
["--edit-format", "not-a-real-format", "--exit", "--yes"],
input=DummyInput(),
output=DummyOutput(),
)
self.assertEqual(result, 1) # main() should return 1 on error
mock_offer_url.assert_called_once()
args, _ = mock_offer_url.call_args
self.assertEqual(args[0], "https://aider.chat/docs/more/edit-formats.html")
def test_chat_language_spanish(self): def test_chat_language_spanish(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
coder = main( coder = main(

View file

@ -2,8 +2,10 @@ import unittest
from unittest.mock import ANY, MagicMock, patch from unittest.mock import ANY, MagicMock, patch
from aider.models import ( from aider.models import (
ANTHROPIC_BETA_HEADER,
Model, Model,
ModelInfoManager, ModelInfoManager,
register_models,
sanity_check_model, sanity_check_model,
sanity_check_models, sanity_check_models,
) )
@ -80,7 +82,9 @@ class TestModels(unittest.TestCase):
) # Should return True because there's a problem with the editor model ) # Should return True because there's a problem with the editor model
mock_io.tool_warning.assert_called_with(ANY) # Ensure a warning was issued mock_io.tool_warning.assert_called_with(ANY) # Ensure a warning was issued
warning_messages = [call.args[0] for call in mock_io.tool_warning.call_args_list] warning_messages = [
warning_call.args[0] for warning_call in mock_io.tool_warning.call_args_list
]
print("Warning messages:", warning_messages) # Add this line print("Warning messages:", warning_messages) # Add this line
self.assertGreaterEqual(mock_io.tool_warning.call_count, 1) # Expect two warnings self.assertGreaterEqual(mock_io.tool_warning.call_count, 1) # Expect two warnings
@ -88,6 +92,87 @@ class TestModels(unittest.TestCase):
any("bogus-model" in msg for msg in warning_messages) any("bogus-model" in msg for msg in warning_messages)
) # Check that one of the warnings mentions the bogus model ) # Check that one of the warnings mentions the bogus model
def test_model_aliases(self):
# Test common aliases
model = Model("4")
self.assertEqual(model.name, "gpt-4-0613")
model = Model("4o")
self.assertEqual(model.name, "gpt-4o-2024-08-06")
model = Model("35turbo")
self.assertEqual(model.name, "gpt-3.5-turbo")
model = Model("35-turbo")
self.assertEqual(model.name, "gpt-3.5-turbo")
model = Model("3")
self.assertEqual(model.name, "gpt-3.5-turbo")
model = Model("sonnet")
self.assertEqual(model.name, "claude-3-sonnet-20241022")
model = Model("haiku")
self.assertEqual(model.name, "claude-3-haiku-20241022")
model = Model("opus")
self.assertEqual(model.name, "claude-3-opus-20240229")
# Test non-alias passes through unchanged
model = Model("gpt-4")
self.assertEqual(model.name, "gpt-4")
def test_aider_extra_model_settings(self):
import tempfile
import yaml
# Create temporary YAML file with test settings
test_settings = [
{
"name": "aider/extra_params",
"extra_params": {
"extra_headers": {"Foo": "bar"},
"some_param": "some value",
},
},
]
# Write to a regular file instead of NamedTemporaryFile
# for better cross-platform compatibility
tmp = tempfile.mktemp(suffix=".yml")
try:
with open(tmp, "w") as f:
yaml.dump(test_settings, f)
# Register the test settings
register_models([tmp])
# Test that defaults are applied when no exact match
model = Model("claude-3-5-sonnet-20240620")
# Test that both the override and existing headers are present
model = Model("claude-3-5-sonnet-20240620")
self.assertEqual(model.extra_params["extra_headers"]["Foo"], "bar")
self.assertEqual(
model.extra_params["extra_headers"]["anthropic-beta"],
ANTHROPIC_BETA_HEADER,
)
self.assertEqual(model.extra_params["some_param"], "some value")
self.assertEqual(model.extra_params["max_tokens"], 8192)
# Test that exact match overrides defaults but not overrides
model = Model("gpt-4")
self.assertEqual(model.extra_params["extra_headers"]["Foo"], "bar")
self.assertEqual(model.extra_params["some_param"], "some value")
finally:
# Clean up the temporary file
import os
try:
os.unlink(tmp)
except OSError:
pass
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -373,6 +373,19 @@ class TestRepoMapAllLanguages(unittest.TestCase):
def test_get_repo_map_all_languages(self): def test_get_repo_map_all_languages(self):
language_files = { language_files = {
"dart": (
"test.dart",
"""void main() {
print('Hello, World!');
}
class Greeter {
String sayHello(String name) {
return 'Hello, $name!';
}
}
""",
),
"c": ( "c": (
"test.c", "test.c",
( (