diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 95b449726..9222c3e25 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - - repo: https://github.com/pycqa/isort + - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort diff --git a/HISTORY.md b/HISTORY.md index b942f392b..70f86560a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,13 @@ # Release history +### v0.39.0 + +- Use `--sonnet` for Claude 3.5 Sonnet, which is the top model on [aider's LLM code editing leaderboard](https://aider.chat/docs/leaderboards/#claude-35-sonnet-takes-the-top-spot). +- All `AIDER_xxx` environment variables can now be set in `.env` (by @jpshack-at-palomar). +- Use `--llm-history-file` to log raw messages sent to the LLM (by @daniel-vainsencher). +- Commit messages are no longer prefixed with "aider:". Instead the git author and committer names have "(aider)" added. + ### v0.38.0 - Use `--vim` for [vim keybindings](https://aider.chat/docs/commands.html#vi) in the chat. diff --git a/README.md b/README.md index 6c30587c5..3bdbc20a4 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ Aider lets you pair program with LLMs, to edit code in your local git repository. Start a new project or work with an existing git repo. -Aider works best with GPT-4o and Claude 3 Opus -and can [connect to almost any LLM](https://aider.chat/docs/llms.html). +Aider can [connect to almost any LLM](https://aider.chat/docs/llms.html). +and works best with GPT-4o, Claude 3.5 Sonnet, Claude 3 Opus and DeepSeek Coder V2.

@@ -79,20 +84,13 @@ Pair program with AI. - [Code with your voice](https://aider.chat/docs/voice.html). -## State of the art +## Top tier performance -Aider has the -[top score on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html). +[Aider has the one of the top scores on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html). SWE Bench is a challenging software engineering benchmark where aider solved *real* GitHub issues from popular open source projects like django, scikitlearn, matplotlib, etc. -

- - aider swe bench - -

- ## More info - [Documentation](https://aider.chat/) diff --git a/aider/__init__.py b/aider/__init__.py index b56245168..57bacca7f 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.38.1-dev" +__version__ = "0.39.1-dev" diff --git a/aider/__main__.py b/aider/__main__.py new file mode 100644 index 000000000..40e2b013f --- /dev/null +++ b/aider/__main__.py @@ -0,0 +1,4 @@ +from .main import main + +if __name__ == "__main__": + main() diff --git a/aider/args.py b/aider/args.py index 070905bb4..fb395e90c 100644 --- a/aider/args.py +++ b/aider/args.py @@ -7,11 +7,30 @@ import sys import configargparse from aider import __version__, models -from aider.args_formatter import MarkdownHelpFormatter, YamlHelpFormatter +from aider.args_formatter import ( + DotEnvFormatter, + MarkdownHelpFormatter, + YamlHelpFormatter, +) from .dump import dump # noqa: F401 +def default_env_file(git_root): + return os.path.join(git_root, ".env") if git_root else ".env" + + +def get_preparser(git_root): + parser = configargparse.ArgumentParser(add_help=False) + parser.add_argument( + "--env-file", + metavar="ENV_FILE", + default=default_env_file(git_root), + help="Specify the .env file to load (default: .env in git root)", + ) + return parser + + def get_parser(default_config_files, git_root): parser = configargparse.ArgumentParser( description="aider is GPT powered coding in your terminal", @@ -28,10 +47,7 @@ def get_parser(default_config_files, git_root): help="Log the conversation with the LLM to this file (for example, .aider.llm.history)", ) group.add_argument( - "files", - metavar="FILE", - nargs="*", - help="files to edit with an LLM (optional)" + "files", metavar="FILE", nargs="*", help="files to edit with an LLM (optional)" ) group.add_argument( "--openai-api-key", @@ -60,7 +76,7 @@ def get_parser(default_config_files, git_root): const=opus_model, help=f"Use {opus_model} model for the main chat", ) - sonnet_model = "claude-3-sonnet-20240229" + sonnet_model = "claude-3-5-sonnet-20240620" group.add_argument( "--sonnet", action="store_const", @@ -142,12 +158,24 @@ def get_parser(default_config_files, git_root): env_var="OPENAI_ORGANIZATION_ID", help="Specify the OpenAI organization ID", ) + group.add_argument( + "--model-settings-file", + metavar="MODEL_SETTINGS_FILE", + default=None, + help="Specify a file with aider model settings for unknown models", + ) group.add_argument( "--model-metadata-file", - metavar="MODEL_FILE", + metavar="MODEL_METADATA_FILE", default=None, help="Specify a file with context window and costs for unknown models", ) + group.add_argument( + "--verify-ssl", + action=argparse.BooleanOptionalAction, + default=True, + help="Verify the SSL cert when connecting to models (default: True)", + ) group.add_argument( "--edit-format", metavar="EDIT_FORMAT", @@ -184,11 +212,12 @@ def get_parser(default_config_files, git_root): " max_chat_history_tokens." ), ) - default_env_file = os.path.join(git_root, ".env") if git_root else ".env" + # This is a duplicate of the argument in the preparser and is a no-op by this time of + # argument parsing, but it's here so that the help is displayed as expected. group.add_argument( "--env-file", metavar="ENV_FILE", - default=default_env_file, + default=default_env_file(git_root), help="Specify the .env file to load (default: .env in git root)", ) @@ -501,11 +530,27 @@ def get_sample_yaml(): return parser.format_help() +def get_sample_dotenv(): + os.environ["COLUMNS"] = "120" + sys.argv = ["aider"] + parser = get_parser([], None) + + # This instantiates all the action.env_var values + parser.parse_known_args() + + parser.formatter_class = DotEnvFormatter + + return argparse.ArgumentParser.format_help(parser) + return parser.format_help() + + def main(): arg = sys.argv[1] if len(sys.argv[1:]) else None if arg == "md": print(get_md_help()) + elif arg == "dotenv": + print(get_sample_dotenv()) else: print(get_sample_yaml()) diff --git a/aider/args_formatter.py b/aider/args_formatter.py index 2503cd007..cc7e3e73d 100644 --- a/aider/args_formatter.py +++ b/aider/args_formatter.py @@ -1,8 +1,83 @@ import argparse +from aider import urls + from .dump import dump # noqa: F401 +class DotEnvFormatter(argparse.HelpFormatter): + def start_section(self, heading): + res = "\n\n" + res += "#" * (len(heading) + 3) + res += f"\n# {heading}" + super().start_section(res) + + def _format_usage(self, usage, actions, groups, prefix): + return "" + + def _format_text(self, text): + return f""" +########################################################## +# Sample aider .env file. +# Place at the root of your git repo. +# Or use `aider --env ` to specify. +########################################################## + +################# +# LLM parameters: +# +# Include xxx_API_KEY parameters and other params needed for your LLMs. +# See {urls.llms} for details. + +## OpenAI +#OPENAI_API_KEY= + +## Anthropic +#ANTHROPIC_API_KEY= + +##... +""" + + def _format_action(self, action): + if not action.option_strings: + return "" + + if not action.env_var: + return + + parts = [""] + + default = action.default + if default == argparse.SUPPRESS: + default = "" + elif isinstance(default, str): + pass + elif isinstance(default, list) and not default: + default = "" + elif action.default is not None: + default = "true" if default else "false" + else: + default = "" + + if action.help: + parts.append(f"## {action.help}") + + if action.env_var: + env_var = action.env_var + if default: + parts.append(f"#{env_var}={default}\n") + else: + parts.append(f"#{env_var}=\n") + + return "\n".join(parts) + "\n" + + def _format_action_invocation(self, action): + return "" + + def _format_args(self, action, default_metavar): + return "" + + class YamlHelpFormatter(argparse.HelpFormatter): def start_section(self, heading): res = "\n\n" diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index b529636ca..76df78985 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -913,9 +913,9 @@ class Coder: res = ["", ""] res.append(f"Model {self.main_model.name} has hit a token limit!") res.append("") - res.append(f"Input tokens: {input_tokens} of {max_input_tokens}{inp_err}") - res.append(f"Output tokens: {output_tokens} of {max_output_tokens}{out_err}") - res.append(f"Total tokens: {total_tokens} of {max_input_tokens}{tot_err}") + res.append(f"Input tokens: {input_tokens:,} of {max_input_tokens:,}{inp_err}") + res.append(f"Output tokens: {output_tokens:,} of {max_output_tokens:,}{out_err}") + res.append(f"Total tokens: {total_tokens:,} of {max_input_tokens:,}{tot_err}") if output_tokens >= max_output_tokens: res.append("") diff --git a/aider/coders/editblock_coder.py b/aider/coders/editblock_coder.py index 017e40408..557e692b6 100644 --- a/aider/coders/editblock_coder.py +++ b/aider/coders/editblock_coder.py @@ -414,16 +414,8 @@ def find_original_update_blocks(content, fence=DEFAULT_FENCE): processed.append(cur) # original_marker - filename = strip_filename(processed[-2].splitlines()[-1], fence) - try: - if not filename: - filename = strip_filename(processed[-2].splitlines()[-2], fence) - if not filename: - if current_filename: - filename = current_filename - else: - raise ValueError(missing_filename_err.format(fence=fence)) - except IndexError: + filename = find_filename(processed[-2].splitlines(), fence) + if not filename: if current_filename: filename = current_filename else: @@ -460,6 +452,35 @@ def find_original_update_blocks(content, fence=DEFAULT_FENCE): raise ValueError(f"{processed}\n^^^ Error parsing SEARCH/REPLACE block.") +def find_filename(lines, fence): + """ + Deepseek Coder v2 has been doing this: + + + ```python + word_count.py + ``` + ```python + <<<<<<< SEARCH + ... + + This is a more flexible search back for filenames. + """ + # Go back through the 3 preceding lines + lines.reverse() + lines = lines[:3] + + for line in lines: + # If we find a filename, done + filename = strip_filename(line, fence) + if filename: + return filename + + # Only continue as long as we keep seeing fences + if not line.startswith(fence[0]): + return + + if __name__ == "__main__": edit = """ Here's the change: diff --git a/aider/commands.py b/aider/commands.py index f6b0c3cba..00189fecd 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -332,7 +332,7 @@ class Commands: last_commit = self.coder.repo.repo.head.commit if ( - not last_commit.message.startswith("aider:") + not last_commit.author.name.endswith(" (aider)") or last_commit.hexsha[:7] != self.coder.last_aider_commit_hash ): self.io.tool_error("The last commit was not made by aider in this chat session.") diff --git a/aider/main.py b/aider/main.py index c4d211fc7..3acaf5afc 100644 --- a/aider/main.py +++ b/aider/main.py @@ -5,12 +5,13 @@ import sys from pathlib import Path import git +import httpx from dotenv import load_dotenv from prompt_toolkit.enums import EditingMode from streamlit.web import cli from aider import __version__, models, utils -from aider.args import get_parser +from aider.args import get_parser, get_preparser from aider.coders import Coder from aider.commands import SwitchModel from aider.io import InputOutput @@ -124,12 +125,18 @@ def check_gitignore(git_root, io, ask=True): def format_settings(parser, args): show = scrub_sensitive_info(args, parser.format_values()) + # clean up the headings for consistency w/ new lines + heading_env = "Environment Variables:" + heading_defaults = "Defaults:" + if heading_env in show: + show = show.replace(heading_env, "\n" + heading_env) + show = show.replace(heading_defaults, "\n" + heading_defaults) show += "\n" show += "Option settings:\n" for arg, val in sorted(vars(args).items()): if val: val = scrub_sensitive_info(args, str(val)) - show += f" - {arg}: {val}\n" + show += f" - {arg}: {val}\n" # noqa: E221 return show @@ -205,7 +212,48 @@ def parse_lint_cmds(lint_cmds, io): return return res +def generate_search_path_list(default_fname, git_root, command_line_file): + files = [] + default_file = Path(default_fname) + files.append(Path.home() / default_file) # homedir + if git_root: + files.append(Path(git_root) / default_file) # git root + if command_line_file: + files.append(command_line_file) + files.append(default_file.resolve()) + files = list(map(str, files)) + files = list(dict.fromkeys(files)) + + return files + +def register_models(git_root, model_settings_fname, io): + model_settings_files = generate_search_path_list(".aider.models.yml", git_root, model_settings_fname) + + try: + files_loaded = models.register_models(model_settings_files) + if len(files_loaded) > 0: + io.tool_output(f"Loaded {len(files_loaded)} model settings file(s)") + for file_loaded in files_loaded: + io.tool_output(f" - {file_loaded}") + except Exception as e: + io.tool_error(f"Error loading aider model settings: {e}") + return 1 + + return None +def register_litellm_models(git_root, model_metadata_fname, io): + model_metatdata_files = generate_search_path_list(".aider.litellm.models.json", git_root, model_metadata_fname) + + try: + model_metadata_files_loaded = models.register_litellm_models(model_metatdata_files) + if len(model_metadata_files_loaded) > 0: + io.tool_output(f"Loaded {len(model_metadata_files_loaded)} litellm model file(s)") + for model_metadata_file in model_metadata_files_loaded: + io.tool_output(f" - {model_metadata_file}") + except Exception as e: + io.tool_error(f"Error loading litellm models: {e}") + return 1 + def main(argv=None, input=None, output=None, force_git_root=None, return_coder=False): if argv is None: argv = sys.argv[1:] @@ -225,9 +273,18 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F default_config_files.append(Path.home() / conf_fname) # homedir default_config_files = list(map(str, default_config_files)) + preparser = get_preparser(git_root) + pre_args, _ = preparser.parse_known_args(argv) + + # Load the .env file specified in the arguments + load_dotenv(pre_args.env_file) + parser = get_parser(default_config_files, git_root) args = parser.parse_args(argv) + if not args.verify_ssl: + litellm.client_session = httpx.Client(verify=False) + if args.gui and not return_coder: launch_gui(argv) return @@ -320,9 +377,6 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F cmd_line = scrub_sensitive_info(args, cmd_line) io.tool_output(cmd_line, log_only=True) - if args.env_file: - load_dotenv(args.env_file) - if args.anthropic_api_key: os.environ["ANTHROPIC_API_KEY"] = args.anthropic_api_key @@ -337,26 +391,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F if args.openai_organization_id: os.environ["OPENAI_ORGANIZATION"] = args.openai_organization_id - model_def_files = [] - model_def_fname = Path(".aider.models.json") - model_def_files.append(Path.home() / model_def_fname) # homedir - if git_root: - model_def_files.append(Path(git_root) / model_def_fname) # git root - if args.model_metadata_file: - model_def_files.append(args.model_metadata_file) - model_def_files.append(model_def_fname.resolve()) - model_def_files = list(map(str, model_def_files)) - model_def_files = list(dict.fromkeys(model_def_files)) - try: - model_metadata_files_loaded = models.register_models(model_def_files) - if len(model_metadata_files_loaded) > 0: - io.tool_output(f"Loaded {len(model_metadata_files_loaded)} model file(s)") - for model_metadata_file in model_metadata_files_loaded: - io.tool_output(f" - {model_metadata_file}") - except Exception as e: - io.tool_error(f"Error loading model info/cost: {e}") - return 1 - + register_models(git_root, args.model_settings_file, io) + register_litellm_models(git_root, args.model_metadata_file, io) + main_model = models.Model(args.model, weak_model=args.weak_model) lint_cmds = parse_lint_cmds(args.lint_cmd, io) diff --git a/aider/models.py b/aider/models.py index f51b38bc4..bed59ea7e 100644 --- a/aider/models.py +++ b/aider/models.py @@ -1,5 +1,6 @@ import difflib import json +import yaml import math import os import sys @@ -178,6 +179,43 @@ MODEL_SETTINGS = [ "whole", weak_model_name="claude-3-haiku-20240307", ), + ModelSettings( + "claude-3-5-sonnet-20240620", + "diff", + weak_model_name="claude-3-haiku-20240307", + use_repo_map=True, + ), + ModelSettings( + "anthropic/claude-3-5-sonnet-20240620", + "diff", + weak_model_name="claude-3-haiku-20240307", + use_repo_map=True, + ), + ModelSettings( + "openrouter/anthropic/claude-3.5-sonnet", + "diff", + weak_model_name="openrouter/anthropic/claude-3-haiku-20240307", + use_repo_map=True, + ), + # Vertex AI Claude models + ModelSettings( + "vertex_ai/claude-3-5-sonnet@20240620", + "diff", + weak_model_name="vertex_ai/claude-3-haiku@20240307", + use_repo_map=True, + ), + ModelSettings( + "vertex_ai/claude-3-opus@20240229", + "diff", + weak_model_name="vertex_ai/claude-3-haiku@20240307", + use_repo_map=True, + send_undo_reply=True, + ), + ModelSettings( + "vertex_ai/claude-3-sonnet@20240229", + "whole", + weak_model_name="vertex_ai/claude-3-haiku@20240307", + ), # Cohere ModelSettings( "command-r-plus", @@ -218,7 +256,7 @@ MODEL_SETTINGS = [ send_undo_reply=True, ), ModelSettings( - "openai/deepseek-chat", + "deepseek/deepseek-chat", "diff", use_repo_map=True, send_undo_reply=True, @@ -226,7 +264,15 @@ MODEL_SETTINGS = [ reminder_as_sys_msg=True, ), ModelSettings( - "deepseek/deepseek-chat", + "deepseek/deepseek-coder", + "diff", + use_repo_map=True, + send_undo_reply=True, + examples_as_sys_msg=True, + reminder_as_sys_msg=True, + ), + ModelSettings( + "openrouter/deepseek/deepseek-coder", "diff", use_repo_map=True, send_undo_reply=True, @@ -425,23 +471,47 @@ class Model: return validate_variables(["GROQ_API_KEY"]) return res - - -def register_models(model_def_fnames): - model_metadata_files_loaded = [] - for model_def_fname in model_def_fnames: - if not os.path.exists(model_def_fname): + +def register_models(model_settings_fnames): + files_loaded = [] + for model_settings_fname in model_settings_fnames: + if not os.path.exists(model_settings_fname): continue - model_metadata_files_loaded.append(model_def_fname) + try: - with open(model_def_fname, "r") as model_def_file: + with open(model_settings_fname, "r") as model_settings_file: + model_settings_list = yaml.safe_load(model_settings_file) + + for model_settings_dict in model_settings_list: + model_settings = ModelSettings(**model_settings_dict) + existing_model_settings = next((ms for ms in MODEL_SETTINGS if ms.name == model_settings.name), None) + + if existing_model_settings: + MODEL_SETTINGS.remove(existing_model_settings) + MODEL_SETTINGS.append(model_settings) + except Exception as e: + raise Exception(f"Error loading model settings from {model_settings_fname}: {e}") + files_loaded.append(model_settings_fname) + + return files_loaded + + +def register_litellm_models(model_fnames): + files_loaded = [] + for model_fname in model_fnames: + if not os.path.exists(model_fname): + continue + + try: + with open(model_fname, "r") as model_def_file: model_def = json.load(model_def_file) - except json.JSONDecodeError as e: - raise Exception(f"Error loading model definition from {model_def_fname}: {e}") + litellm.register_model(model_def) + except Exception as e: + raise Exception(f"Error loading model definition from {model_fname}: {e}") + + files_loaded.append(model_fname) - litellm.register_model(model_def) - - return model_metadata_files_loaded + return files_loaded def validate_variables(vars): diff --git a/aider/repo.py b/aider/repo.py index 214d2c6d9..38f5ef8f5 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -88,11 +88,15 @@ class GitRepo: else: cmd += ["-a"] + original_user_name = self.repo.config_reader().get_value("user", "name") + original_committer_name_env = os.environ.get("GIT_COMMITTER_NAME") + + committer_name = f"{original_user_name} (aider)" + os.environ["GIT_COMMITTER_NAME"] = committer_name + if aider_edits: - user_name = self.repo.config_reader().get_value("user", "name") - committer_name = f"{user_name} (aider)" - original_committer_name = os.environ.get("GIT_COMMITTER_NAME") - os.environ["GIT_COMMITTER_NAME"] = committer_name + original_auther_name_env = os.environ.get("GIT_AUTHOR_NAME") + os.environ["GIT_AUTHOR_NAME"] = committer_name self.repo.git.commit(cmd) commit_hash = self.repo.head.commit.hexsha[:7] @@ -100,10 +104,15 @@ class GitRepo: # Restore the original GIT_COMMITTER_NAME if aider_edits: - if original_committer_name is not None: - os.environ["GIT_COMMITTER_NAME"] = original_committer_name + if original_auther_name_env is not None: + os.environ["GIT_AUTHOR_NAME"] = original_auther_name_env else: - del os.environ["GIT_COMMITTER_NAME"] + del os.environ["GIT_AUTHOR_NAME"] + + if original_committer_name_env is not None: + os.environ["GIT_COMMITTER_NAME"] = original_committer_name_env + else: + del os.environ["GIT_COMMITTER_NAME"] return commit_hash, commit_message diff --git a/aider/tests/test_commands.py b/aider/tests/test_commands.py index 8cc67b6d8..f6629f481 100644 --- a/aider/tests/test_commands.py +++ b/aider/tests/test_commands.py @@ -523,16 +523,20 @@ class TestCommands(TestCase): other_path.write_text("other content") repo.git.add(str(other_path)) + os.environ["GIT_AUTHOR_NAME"] = "Foo (aider)" + # Create and commit a file filename = "test_file.txt" file_path = Path(repo_dir) / filename file_path.write_text("first content") repo.git.add(filename) - repo.git.commit("-m", "aider: first commit") + repo.git.commit("-m", "first commit") file_path.write_text("second content") repo.git.add(filename) - repo.git.commit("-m", "aider: second commit") + repo.git.commit("-m", "second commit") + + del os.environ["GIT_AUTHOR_NAME"] # Store the commit hash last_commit_hash = repo.head.commit.hexsha[:7] diff --git a/aider/tests/test_editblock.py b/aider/tests/test_editblock.py index 0c1143232..40a0d4572 100644 --- a/aider/tests/test_editblock.py +++ b/aider/tests/test_editblock.py @@ -398,6 +398,32 @@ Hope you like it! ], ) + def test_deepseek_coder_v2_filename_mangling(self): + edit = """ +Here's the change: + + ```python +foo.txt +``` +```python +<<<<<<< SEARCH +one +======= +two +>>>>>>> REPLACE +``` + +Hope you like it! +""" + + edits = list(eb.find_original_update_blocks(edit)) + self.assertEqual( + edits, + [ + ("foo.txt", "one\n", "two\n"), + ], + ) + if __name__ == "__main__": unittest.main() diff --git a/aider/tests/test_main.py b/aider/tests/test_main.py index d319a78dd..b576d3754 100644 --- a/aider/tests/test_main.py +++ b/aider/tests/test_main.py @@ -1,7 +1,7 @@ import os -import shutil import subprocess import tempfile +from io import StringIO from pathlib import Path from unittest import TestCase from unittest.mock import MagicMock, patch @@ -13,24 +13,28 @@ from prompt_toolkit.output import DummyOutput from aider.dump import dump # noqa: F401 from aider.io import InputOutput from aider.main import check_gitignore, main, setup_git -from aider.utils import GitTemporaryDirectory, make_repo +from aider.utils import GitTemporaryDirectory, IgnorantTemporaryDirectory, make_repo class TestMain(TestCase): def setUp(self): + self.original_env = os.environ.copy() os.environ["OPENAI_API_KEY"] = "deadbeef" self.original_cwd = os.getcwd() - self.tempdir = tempfile.mkdtemp() + self.tempdir_obj = IgnorantTemporaryDirectory() + self.tempdir = self.tempdir_obj.name os.chdir(self.tempdir) def tearDown(self): os.chdir(self.original_cwd) - shutil.rmtree(self.tempdir, ignore_errors=True) + self.tempdir_obj.cleanup() + os.environ.clear() + os.environ.update(self.original_env) def test_main_with_empty_dir_no_files_on_command(self): main(["--no-git"], input=DummyInput(), output=DummyOutput()) - def test_main_with_empty_dir_new_file(self): + def test_main_with_emptqy_dir_new_file(self): main(["foo.txt", "--yes", "--no-git"], input=DummyInput(), output=DummyOutput()) self.assertTrue(os.path.exists("foo.txt")) @@ -237,3 +241,82 @@ class TestMain(TestCase): main(["--message", test_message]) args, kwargs = MockInputOutput.call_args self.assertEqual(args[1], None) + + def test_dark_mode_sets_code_theme(self): + # Mock Coder.create to capture the configuration + with patch("aider.coders.Coder.create") as MockCoder: + main(["--dark-mode", "--no-git"], input=DummyInput(), output=DummyOutput()) + # Ensure Coder.create was called + MockCoder.assert_called_once() + # Check if the code_theme setting is for dark mode + _, kwargs = MockCoder.call_args + self.assertEqual(kwargs["code_theme"], "monokai") + + def test_light_mode_sets_code_theme(self): + # Mock Coder.create to capture the configuration + with patch("aider.coders.Coder.create") as MockCoder: + main(["--light-mode", "--no-git"], input=DummyInput(), output=DummyOutput()) + # Ensure Coder.create was called + MockCoder.assert_called_once() + # Check if the code_theme setting is for light mode + _, kwargs = MockCoder.call_args + self.assertEqual(kwargs["code_theme"], "default") + + def create_env_file(self, file_name, content): + env_file_path = Path(self.tempdir) / file_name + env_file_path.write_text(content) + return env_file_path + + def test_env_file_flag_sets_automatic_variable(self): + env_file_path = self.create_env_file(".env.test", "AIDER_DARK_MODE=True") + with patch("aider.coders.Coder.create") as MockCoder: + main( + ["--env-file", str(env_file_path), "--no-git"], + input=DummyInput(), + output=DummyOutput(), + ) + MockCoder.assert_called_once() + # Check if the color settings are for dark mode + _, kwargs = MockCoder.call_args + self.assertEqual(kwargs["code_theme"], "monokai") + + def test_default_env_file_sets_automatic_variable(self): + self.create_env_file(".env", "AIDER_DARK_MODE=True") + with patch("aider.coders.Coder.create") as MockCoder: + main(["--no-git"], input=DummyInput(), output=DummyOutput()) + # Ensure Coder.create was called + MockCoder.assert_called_once() + # Check if the color settings are for dark mode + _, kwargs = MockCoder.call_args + self.assertEqual(kwargs["code_theme"], "monokai") + + def test_false_vals_in_env_file(self): + self.create_env_file(".env", "AIDER_SHOW_DIFFS=off") + with patch("aider.coders.Coder.create") as MockCoder: + main(["--no-git"], input=DummyInput(), output=DummyOutput()) + MockCoder.assert_called_once() + _, kwargs = MockCoder.call_args + self.assertEqual(kwargs["show_diffs"], False) + + def test_true_vals_in_env_file(self): + self.create_env_file(".env", "AIDER_SHOW_DIFFS=on") + with patch("aider.coders.Coder.create") as MockCoder: + main(["--no-git"], input=DummyInput(), output=DummyOutput()) + MockCoder.assert_called_once() + _, kwargs = MockCoder.call_args + self.assertEqual(kwargs["show_diffs"], True) + + def test_verbose_mode_lists_env_vars(self): + self.create_env_file(".env", "AIDER_DARK_MODE=on") + with patch("sys.stdout", new_callable=StringIO) as mock_stdout: + main(["--no-git", "--verbose"], input=DummyInput(), output=DummyOutput()) + output = mock_stdout.getvalue() + relevant_output = "\n".join( + line + for line in output.splitlines() + if "AIDER_DARK_MODE" in line or "dark_mode" in line + ) # this bit just helps failing assertions to be easier to read + self.assertIn("AIDER_DARK_MODE", relevant_output) + self.assertIn("dark_mode", relevant_output) + self.assertRegex(relevant_output, r"AIDER_DARK_MODE:\s+on") + self.assertRegex(relevant_output, r"dark_mode:\s+True") diff --git a/aider/tests/test_repo.py b/aider/tests/test_repo.py index 961fe8ee9..92d074b9a 100644 --- a/aider/tests/test_repo.py +++ b/aider/tests/test_repo.py @@ -1,4 +1,5 @@ import os +import platform import tempfile import unittest from pathlib import Path @@ -141,6 +142,10 @@ class TestRepo(unittest.TestCase): def test_commit_with_custom_committer_name(self, mock_send): mock_send.return_value = '"a good commit message"' + # Cleanup of the git temp dir explodes on windows + if platform.system() == "Windows": + return + with GitTemporaryDirectory(): # new repo raw_repo = git.Repo() @@ -152,7 +157,8 @@ class TestRepo(unittest.TestCase): raw_repo.git.add(str(fname)) raw_repo.git.commit("-m", "initial commit") - git_repo = GitRepo(InputOutput(), None, None) + io = InputOutput() + git_repo = GitRepo(io, None, None) # commit a change fname.write_text("new content") @@ -160,11 +166,23 @@ class TestRepo(unittest.TestCase): # check the committer name commit = raw_repo.head.commit + self.assertEqual(commit.author.name, "Test User (aider)") + self.assertEqual(commit.committer.name, "Test User (aider)") + + # commit a change without aider_edits + fname.write_text("new content again!") + git_repo.commit(fnames=[str(fname)], aider_edits=False) + + # check the committer name + commit = raw_repo.head.commit + self.assertEqual(commit.author.name, "Test User") self.assertEqual(commit.committer.name, "Test User (aider)") # check that the original committer name is restored original_committer_name = os.environ.get("GIT_COMMITTER_NAME") self.assertIsNone(original_committer_name) + original_author_name = os.environ.get("GIT_AUTHOR_NAME") + self.assertIsNone(original_author_name) def test_get_tracked_files(self): # Create a temporary directory diff --git a/aider/urls.py b/aider/urls.py index d2863e705..8e21742d3 100644 --- a/aider/urls.py +++ b/aider/urls.py @@ -6,3 +6,4 @@ enable_playwright = "https://aider.chat/docs/install/optional.html#enable-playwr favicon = "https://aider.chat/assets/icons/favicon-32x32.png" model_warnings = "https://aider.chat/docs/llms/warnings.html" token_limits = "https://aider.chat/docs/troubleshooting/token-limits.html" +llms = "https://aider.chat/docs/llms.html" diff --git a/aider/utils.py b/aider/utils.py index 7636eb119..6d097fbe7 100644 --- a/aider/utils.py +++ b/aider/utils.py @@ -17,11 +17,17 @@ class IgnorantTemporaryDirectory: return self.temp_dir.__enter__() def __exit__(self, exc_type, exc_val, exc_tb): + self.cleanup() + + def cleanup(self): try: - self.temp_dir.__exit__(exc_type, exc_val, exc_tb) + self.temp_dir.cleanup() except (OSError, PermissionError): pass # Ignore errors (Windows) + def __getattr__(self, item): + return getattr(self.temp_dir, item) + class ChdirTemporaryDirectory(IgnorantTemporaryDirectory): def __init__(self): diff --git a/requirements.txt b/requirements.txt index e7bff9826..f99a01346 100644 --- a/requirements.txt +++ b/requirements.txt @@ -54,7 +54,7 @@ diskcache==5.6.3 # via -r requirements.in distro==1.9.0 # via openai -filelock==3.15.1 +filelock==3.15.3 # via huggingface-hub flake8==7.1.0 # via -r requirements.in @@ -70,14 +70,14 @@ gitpython==3.1.43 # via # -r requirements.in # streamlit -google-ai-generativelanguage==0.6.4 +google-ai-generativelanguage==0.6.5 # via google-generativeai google-api-core[grpc]==2.19.0 # via # google-ai-generativelanguage # google-api-python-client # google-generativeai -google-api-python-client==2.133.0 +google-api-python-client==2.134.0 # via google-generativeai google-auth==2.30.0 # via @@ -88,7 +88,7 @@ google-auth==2.30.0 # google-generativeai google-auth-httplib2==0.2.0 # via google-api-python-client -google-generativeai==0.6.0 +google-generativeai==0.7.0 # via -r requirements.in googleapis-common-protos==1.63.1 # via @@ -122,7 +122,9 @@ idna==3.7 # httpx # requests # yarl -importlib-metadata==7.1.0 +ijson==3.3.0 + # via litellm +importlib-metadata==7.2.0 # via litellm jinja2==3.1.4 # via @@ -135,7 +137,7 @@ jsonschema==4.22.0 # altair jsonschema-specifications==2023.12.1 # via jsonschema -litellm==1.40.15 +litellm==1.40.21 # via -r requirements.in markdown-it-py==3.0.0 # via rich @@ -151,7 +153,7 @@ multidict==6.0.5 # yarl networkx==3.2.1 # via -r requirements.in -numpy==1.26.4 +numpy==2.0.0 # via # -r requirements.in # altair @@ -160,7 +162,7 @@ numpy==1.26.4 # pydeck # scipy # streamlit -openai==1.34.0 +openai==1.35.3 # via # -r requirements.in # litellm @@ -186,7 +188,7 @@ playwright==1.44.0 # via -r requirements.in prompt-toolkit==3.0.47 # via -r requirements.in -proto-plus==1.23.0 +proto-plus==1.24.0 # via # google-ai-generativelanguage # google-api-core @@ -280,9 +282,9 @@ soundfile==0.12.1 # via -r requirements.in soupsieve==2.5 # via beautifulsoup4 -streamlit==1.35.0 +streamlit==1.36.0 # via -r requirements.in -tenacity==8.3.0 +tenacity==8.4.1 # via streamlit tiktoken==0.7.0 # via @@ -320,7 +322,7 @@ tzdata==2024.1 # via pandas uritemplate==4.1.1 # via google-api-python-client -urllib3==2.2.1 +urllib3==2.2.2 # via requests watchdog==4.0.1 # via -r requirements.in diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index 4fe32efba..fc16c4c66 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -14,6 +14,7 @@ cog $ARG \ README.md \ website/index.md \ website/HISTORY.md \ + website/docs/dotenv.md \ website/docs/commands.md \ website/docs/languages.md \ website/docs/options.md \ diff --git a/website/HISTORY.md b/website/HISTORY.md index dc52bc7ff..4e84bf086 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -1,5 +1,6 @@ --- title: Release history +parent: More info nav_order: 999 --- @@ -11,6 +12,13 @@ cog.out(text) # Release history +### v0.39.0 + +- Use `--sonnet` for Claude 3.5 Sonnet, which is the top model on [aider's LLM code editing leaderboard](https://aider.chat/docs/leaderboards/#claude-35-sonnet-takes-the-top-spot). +- All `AIDER_xxx` environment variables can now be set in `.env` (by @jpshack-at-palomar). +- Use `--llm-history-file` to log raw messages sent to the LLM (by @daniel-vainsencher). +- Commit messages are no longer prefixed with "aider:". Instead the git author and committer names have "(aider)" added. + ### v0.38.0 - Use `--vim` for [vim keybindings](https://aider.chat/docs/commands.html#vi) in the chat. diff --git a/website/_config.yml b/website/_config.yml index 74021c3fe..15e28ec7f 100644 --- a/website/_config.yml +++ b/website/_config.yml @@ -4,6 +4,7 @@ url: "https://aider.chat" plugins: - jekyll-redirect-from - jekyll-sitemap + - jekyll-feed defaults: - scope: @@ -19,6 +20,7 @@ exclude: - "**/OLD/**" - "OLD/**" - vendor + - feed.xml aux_links: "GitHub": diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index 82119110a..c7c4a0efc 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -44,29 +44,6 @@ seconds_per_case: 23.1 total_cost: 0.0000 -- dirname: 2024-06-17-14-45-54--deepseek-coder2-whole - test_cases: 133 - model: DeepSeek Coder V2 - edit_format: whole - commit_hash: ca8672b - pass_rate_1: 63.9 - pass_rate_2: 75.2 - 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: 1 - indentation_errors: 0 - exhausted_context_windows: 0 - test_timeouts: 7 - command: aider --model deepseek/deepseek-coder - date: 2024-06-17 - versions: 0.38.1-dev - seconds_per_case: 21.1 - total_cost: 0.0537 - - dirname: 2024-05-03-20-47-24--gemini-1.5-pro-diff-fenced test_cases: 133 model: gemini-1.5-pro-latest @@ -611,4 +588,97 @@ date: 2024-06-08 versions: 0.37.1-dev seconds_per_case: 280.6 - total_cost: 0.0000 \ No newline at end of file + total_cost: 0.0000 + +- dirname: 2024-06-20-15-09-26--claude-3.5-sonnet-whole + test_cases: 133 + model: claude-3.5-sonnet (whole) + edit_format: whole + commit_hash: 068609e + pass_rate_1: 61.7 + pass_rate_2: 78.2 + percent_cases_well_formed: 100.0 + error_outputs: 4 + num_malformed_responses: 0 + num_with_malformed_responses: 0 + user_asks: 2 + lazy_comments: 0 + syntax_errors: 0 + indentation_errors: 0 + exhausted_context_windows: 0 + test_timeouts: 0 + command: aider --model openrouter/anthropic/claude-3.5-sonnet --edit-format whole + date: 2024-06-20 + versions: 0.38.1-dev + seconds_per_case: 15.4 + total_cost: 0.0000 + +- dirname: 2024-06-20-15-16-41--claude-3.5-sonnet-diff + test_cases: 133 + model: claude-3.5-sonnet (diff) + edit_format: diff + commit_hash: 068609e-dirty + pass_rate_1: 57.9 + pass_rate_2: 74.4 + percent_cases_well_formed: 97.0 + error_outputs: 48 + num_malformed_responses: 11 + num_with_malformed_responses: 4 + user_asks: 0 + lazy_comments: 0 + syntax_errors: 0 + indentation_errors: 0 + exhausted_context_windows: 0 + test_timeouts: 1 + command: aider --model openrouter/anthropic/claude-3.5-sonnet + date: 2024-06-20 + versions: 0.38.1-dev + seconds_per_case: 21.6 + total_cost: 0.0000 + +- dirname: 2024-06-17-14-45-54--deepseek-coder2-whole + test_cases: 133 + model: DeepSeek Coder V2 (whole) + edit_format: whole + commit_hash: ca8672b + pass_rate_1: 63.9 + pass_rate_2: 75.2 + 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: 1 + indentation_errors: 0 + exhausted_context_windows: 0 + test_timeouts: 7 + command: aider --model deepseek/deepseek-coder + date: 2024-06-17 + versions: 0.38.1-dev + seconds_per_case: 21.1 + total_cost: 0.0537 + +- dirname: 2024-06-21-15-29-08--deepseek-coder2-diff-again3 + test_cases: 133 + model: DeepSeek Coder V2 (diff) + edit_format: diff + commit_hash: 515ab3e + pass_rate_1: 58.6 + pass_rate_2: 66.2 + percent_cases_well_formed: 98.5 + error_outputs: 23 + num_malformed_responses: 5 + num_with_malformed_responses: 2 + user_asks: 2 + lazy_comments: 0 + syntax_errors: 0 + indentation_errors: 1 + exhausted_context_windows: 0 + test_timeouts: 2 + command: aider --model deepseek/deepseek-coder + date: 2024-06-21 + versions: 0.39.1-dev + seconds_per_case: 30.2 + total_cost: 0.0857 + \ No newline at end of file diff --git a/website/_data/refactor_leaderboard.yml b/website/_data/refactor_leaderboard.yml index db4d3483f..11773ac39 100644 --- a/website/_data/refactor_leaderboard.yml +++ b/website/_data/refactor_leaderboard.yml @@ -143,4 +143,25 @@ seconds_per_case: 67.8 total_cost: 20.4889 - \ No newline at end of file + +- dirname: 2024-06-20-16-39-18--refac-claude-3.5-sonnet-diff + test_cases: 89 + model: claude-3.5-sonnet (diff) + edit_format: diff + commit_hash: e5e07f9 + pass_rate_1: 55.1 + percent_cases_well_formed: 70.8 + error_outputs: 240 + num_malformed_responses: 54 + num_with_malformed_responses: 26 + user_asks: 10 + lazy_comments: 2 + syntax_errors: 0 + indentation_errors: 3 + exhausted_context_windows: 0 + test_timeouts: 0 + command: aider --model openrouter/anthropic/claude-3.5-sonnet + date: 2024-06-20 + versions: 0.38.1-dev + seconds_per_case: 51.9 + total_cost: 0.0000 \ No newline at end of file diff --git a/website/_includes/get-started.md b/website/_includes/get-started.md index 498d310dc..e31d2ca3a 100644 --- a/website/_includes/get-started.md +++ b/website/_includes/get-started.md @@ -10,7 +10,12 @@ $ cd /to/your/git/repo $ export OPENAI_API_KEY=your-key-goes-here $ aider -# Or, work with Claude 3 Opus on your repo +# Or, work with Anthropic's models $ export ANTHROPIC_API_KEY=your-key-goes-here + +# Claude 3 Opus $ aider --opus + +# Claude 3.5 Sonnet +$ aider --sonnet ``` diff --git a/website/_includes/head_custom.html b/website/_includes/head_custom.html index a6d4cca22..95a83cf10 100644 --- a/website/_includes/head_custom.html +++ b/website/_includes/head_custom.html @@ -5,6 +5,7 @@ {% endif %} + diff --git a/website/assets/sample.aider.conf.yml b/website/assets/sample.aider.conf.yml index 896cda62a..08bc54e0f 100644 --- a/website/assets/sample.aider.conf.yml +++ b/website/assets/sample.aider.conf.yml @@ -28,7 +28,7 @@ ## Use claude-3-opus-20240229 model for the main chat #opus: false -## Use claude-3-sonnet-20240229 model for the main chat +## Use claude-3-5-sonnet-20240620 model for the main chat #sonnet: false ## Use gpt-4-0613 model for the main chat @@ -64,9 +64,15 @@ ## Specify the OpenAI organization ID #openai-organization-id: +## Specify a file with aider model settings for unknown models +#model-settings-file: + ## Specify a file with context window and costs for unknown models #model-metadata-file: +## Verify the SSL cert when connecting to models (default: True) +#verify-ssl: true + ## Specify what edit format the LLM should use (default depends on model) #edit-format: diff --git a/website/assets/sample.env b/website/assets/sample.env new file mode 100644 index 000000000..8ac091b96 --- /dev/null +++ b/website/assets/sample.env @@ -0,0 +1,232 @@ +########################################################## +# Sample aider .env file. +# Place at the root of your git repo. +# Or use `aider --env ` to specify. +########################################################## + +################# +# LLM parameters: +# +# Include xxx_API_KEY parameters and other params needed for your LLMs. +# See https://aider.chat/docs/llms.html for details. + +## OpenAI +#OPENAI_API_KEY= + +## Anthropic +#ANTHROPIC_API_KEY= + +##... + +####### +# Main: + +## Log the conversation with the LLM to this file (for example, .aider.llm.history) +#AIDER_LLM_HISTORY_FILE= + +## Specify the OpenAI API key +#OPENAI_API_KEY= + +## Specify the Anthropic API key +#ANTHROPIC_API_KEY= + +## Specify the model to use for the main chat (default: gpt-4o) +#AIDER_MODEL=gpt-4o + +## Use claude-3-opus-20240229 model for the main chat +#AIDER_OPUS= + +## Use claude-3-5-sonnet-20240620 model for the main chat +#AIDER_SONNET= + +## Use gpt-4-0613 model for the main chat +#AIDER_4= + +## Use gpt-4o model for the main chat +#AIDER_4O= + +## Use gpt-4-1106-preview model for the main chat +#AIDER_4_TURBO= + +## Use gpt-3.5-turbo model for the main chat +#AIDER_35TURBO= + +################# +# Model Settings: + +## List known models which match the (partial) MODEL name +#AIDER_MODELS= + +## Specify the api base url +#OPENAI_API_BASE= + +## Specify the api_type +#OPENAI_API_TYPE= + +## Specify the api_version +#OPENAI_API_VERSION= + +## Specify the deployment_id +#OPENAI_API_DEPLOYMENT_ID= + +## Specify the OpenAI organization ID +#OPENAI_ORGANIZATION_ID= + +## Specify a file with aider model settings for unknown models +#AIDER_MODEL_SETTINGS_FILE= + +## Specify a file with context window and costs for unknown models +#AIDER_MODEL_METADATA_FILE= + +## Verify the SSL cert when connecting to models (default: True) +#AIDER_VERIFY_SSL=true + +## Specify what edit format the LLM should use (default depends on model) +#AIDER_EDIT_FORMAT= + +## Specify the model to use for commit messages and chat history summarization (default depends on --model) +#AIDER_WEAK_MODEL= + +## Only work with models that have meta-data available (default: True) +#AIDER_SHOW_MODEL_WARNINGS=true + +## Max number of tokens to use for repo map, use 0 to disable (default: 1024) +#AIDER_MAP_TOKENS=true + +## Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens. +#AIDER_MAX_CHAT_HISTORY_TOKENS= + +## Specify the .env file to load (default: .env in git root) +#AIDER_ENV_FILE=.env + +################ +# History Files: + +## Specify the chat input history file (default: .aider.input.history) +#AIDER_INPUT_HISTORY_FILE=.aider.input.history + +## Specify the chat history file (default: .aider.chat.history.md) +#AIDER_CHAT_HISTORY_FILE=.aider.chat.history.md + +## Restore the previous chat history messages (default: False) +#AIDER_RESTORE_CHAT_HISTORY=false + +################## +# Output Settings: + +## Use colors suitable for a dark terminal background (default: False) +#AIDER_DARK_MODE=false + +## Use colors suitable for a light terminal background (default: False) +#AIDER_LIGHT_MODE=false + +## Enable/disable pretty, colorized output (default: True) +#AIDER_PRETTY=true + +## Enable/disable streaming responses (default: True) +#AIDER_STREAM=true + +## Set the color for user input (default: #00cc00) +#AIDER_USER_INPUT_COLOR=#00cc00 + +## Set the color for tool output (default: None) +#AIDER_TOOL_OUTPUT_COLOR= + +## Set the color for tool error messages (default: red) +#AIDER_TOOL_ERROR_COLOR=#FF2222 + +## Set the color for assistant output (default: #0088ff) +#AIDER_ASSISTANT_OUTPUT_COLOR=#0088ff + +## Set the markdown code theme (default: default, other options include monokai, solarized-dark, solarized-light) +#AIDER_CODE_THEME=default + +## Show diffs when committing changes (default: False) +#AIDER_SHOW_DIFFS=false + +############### +# Git Settings: + +## Enable/disable looking for a git repo (default: True) +#AIDER_GIT=true + +## Enable/disable adding .aider* to .gitignore (default: True) +#AIDER_GITIGNORE=true + +## Specify the aider ignore file (default: .aiderignore in git root) +#AIDER_AIDERIGNORE=.aiderignore + +## Enable/disable auto commit of LLM changes (default: True) +#AIDER_AUTO_COMMITS=true + +## Enable/disable commits when repo is found dirty (default: True) +#AIDER_DIRTY_COMMITS=true + +## Perform a dry run without modifying files (default: False) +#AIDER_DRY_RUN=false + +######################## +# Fixing and committing: + +## Commit all pending changes with a suitable commit message, then exit +#AIDER_COMMIT=false + +## Lint and fix provided files, or dirty files if none provided +#AIDER_LINT=false + +## Specify lint commands to run for different languages, eg: "python: flake8 --select=..." (can be used multiple times) +#AIDER_LINT_CMD= + +## Enable/disable automatic linting after changes (default: True) +#AIDER_AUTO_LINT=true + +## Specify command to run tests +#AIDER_TEST_CMD= + +## Enable/disable automatic testing after changes (default: False) +#AIDER_AUTO_TEST=false + +## Run tests and fix problems found +#AIDER_TEST=false + +################# +# Other Settings: + +## Use VI editing mode in the terminal (default: False) +#AIDER_VIM=false + +## Specify the language for voice using ISO 639-1 code (default: auto) +#AIDER_VOICE_LANGUAGE=en + +## Check for updates and return status in the exit code +#AIDER_CHECK_UPDATE=false + +## Skips checking for the update when the program runs +#AIDER_SKIP_CHECK_UPDATE=false + +## Apply the changes from the given file instead of running the chat (debug) +#AIDER_APPLY= + +## Always say yes to every confirmation +#AIDER_YES= + +## Enable verbose output +#AIDER_VERBOSE=false + +## Print the repo map and exit (debug) +#AIDER_SHOW_REPO_MAP=false + +## Print the system prompts and exit (debug) +#AIDER_SHOW_PROMPTS=false + +## Specify a single message to send the LLM, process reply then exit (disables chat mode) +#AIDER_MESSAGE= + +## Specify a file containing the message to send the LLM, process reply, then exit (disables chat mode) +#AIDER_MESSAGE_FILE= + +## Specify the encoding for input and output (default: utf-8) +#AIDER_ENCODING=utf-8 + +## Run aider in your browser +#AIDER_GUI=false diff --git a/website/docs/aider_conf.md b/website/docs/aider_conf.md index b13208cc7..2751042f4 100644 --- a/website/docs/aider_conf.md +++ b/website/docs/aider_conf.md @@ -56,7 +56,7 @@ cog.outl("```") ## Use claude-3-opus-20240229 model for the main chat #opus: false -## Use claude-3-sonnet-20240229 model for the main chat +## Use claude-3-5-sonnet-20240620 model for the main chat #sonnet: false ## Use gpt-4-0613 model for the main chat @@ -92,9 +92,15 @@ cog.outl("```") ## Specify the OpenAI organization ID #openai-organization-id: +## Specify a file with aider model settings for unknown models +#model-settings-file: + ## Specify a file with context window and costs for unknown models #model-metadata-file: +## Verify the SSL cert when connecting to models (default: True) +#verify-ssl: true + ## Specify what edit format the LLM should use (default depends on model) #edit-format: diff --git a/website/docs/dotenv.md b/website/docs/dotenv.md index e45fc5783..66c26287c 100644 --- a/website/docs/dotenv.md +++ b/website/docs/dotenv.md @@ -4,12 +4,12 @@ nav_order: 900 description: Using a .env file to store LLM API keys for aider. --- -# Storing LLM params in .env +# Config with .env You can use a `.env` file to store API keys and other settings for the models you use with aider. -You currently can not set general aider options -in the `.env` file, only LLM environment variables. +You can also set many general aider options +in the `.env` file. {% include special-keys.md %} @@ -17,17 +17,253 @@ Aider will look for a `.env` file in the root of your git repo or in the current directory. You can give it an explicit file to load with the `--env-file ` parameter. -Here is an example `.env` file: +Below is a sample `.env` file, which you +can also +[download from GitHub](https://github.com/paul-gauthier/aider/blob/main/website/assets/sample.env). + ``` -OPENAI_API_KEY= -ANTHROPIC_API_KEY= -GROQ_API_KEY= -OPENROUTER_API_KEY= +########################################################## +# Sample aider .env file. +# Place at the root of your git repo. +# Or use `aider --env ` to specify. +########################################################## -AZURE_API_KEY= -AZURE_API_VERSION=2023-05-15 -AZURE_API_BASE=https://example-endpoint.openai.azure.com +################# +# LLM parameters: +# +# Include xxx_API_KEY parameters and other params needed for your LLMs. +# See https://aider.chat/docs/llms.html for details. -OLLAMA_API_BASE=http://127.0.0.1:11434 +## OpenAI +#OPENAI_API_KEY= + +## Anthropic +#ANTHROPIC_API_KEY= + +##... + +####### +# Main: + +## Log the conversation with the LLM to this file (for example, .aider.llm.history) +#AIDER_LLM_HISTORY_FILE= + +## Specify the OpenAI API key +#OPENAI_API_KEY= + +## Specify the Anthropic API key +#ANTHROPIC_API_KEY= + +## Specify the model to use for the main chat (default: gpt-4o) +#AIDER_MODEL=gpt-4o + +## Use claude-3-opus-20240229 model for the main chat +#AIDER_OPUS= + +## Use claude-3-5-sonnet-20240620 model for the main chat +#AIDER_SONNET= + +## Use gpt-4-0613 model for the main chat +#AIDER_4= + +## Use gpt-4o model for the main chat +#AIDER_4O= + +## Use gpt-4-1106-preview model for the main chat +#AIDER_4_TURBO= + +## Use gpt-3.5-turbo model for the main chat +#AIDER_35TURBO= + +################# +# Model Settings: + +## List known models which match the (partial) MODEL name +#AIDER_MODELS= + +## Specify the api base url +#OPENAI_API_BASE= + +## Specify the api_type +#OPENAI_API_TYPE= + +## Specify the api_version +#OPENAI_API_VERSION= + +## Specify the deployment_id +#OPENAI_API_DEPLOYMENT_ID= + +## Specify the OpenAI organization ID +#OPENAI_ORGANIZATION_ID= + +## Specify a file with aider model settings for unknown models +#AIDER_MODEL_SETTINGS_FILE= + +## Specify a file with context window and costs for unknown models +#AIDER_MODEL_METADATA_FILE= + +## Verify the SSL cert when connecting to models (default: True) +#AIDER_VERIFY_SSL=true + +## Specify what edit format the LLM should use (default depends on model) +#AIDER_EDIT_FORMAT= + +## Specify the model to use for commit messages and chat history summarization (default depends on --model) +#AIDER_WEAK_MODEL= + +## Only work with models that have meta-data available (default: True) +#AIDER_SHOW_MODEL_WARNINGS=true + +## Max number of tokens to use for repo map, use 0 to disable (default: 1024) +#AIDER_MAP_TOKENS=true + +## Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens. +#AIDER_MAX_CHAT_HISTORY_TOKENS= + +## Specify the .env file to load (default: .env in git root) +#AIDER_ENV_FILE=.env + +################ +# History Files: + +## Specify the chat input history file (default: .aider.input.history) +#AIDER_INPUT_HISTORY_FILE=.aider.input.history + +## Specify the chat history file (default: .aider.chat.history.md) +#AIDER_CHAT_HISTORY_FILE=.aider.chat.history.md + +## Restore the previous chat history messages (default: False) +#AIDER_RESTORE_CHAT_HISTORY=false + +################## +# Output Settings: + +## Use colors suitable for a dark terminal background (default: False) +#AIDER_DARK_MODE=false + +## Use colors suitable for a light terminal background (default: False) +#AIDER_LIGHT_MODE=false + +## Enable/disable pretty, colorized output (default: True) +#AIDER_PRETTY=true + +## Enable/disable streaming responses (default: True) +#AIDER_STREAM=true + +## Set the color for user input (default: #00cc00) +#AIDER_USER_INPUT_COLOR=#00cc00 + +## Set the color for tool output (default: None) +#AIDER_TOOL_OUTPUT_COLOR= + +## Set the color for tool error messages (default: red) +#AIDER_TOOL_ERROR_COLOR=#FF2222 + +## Set the color for assistant output (default: #0088ff) +#AIDER_ASSISTANT_OUTPUT_COLOR=#0088ff + +## Set the markdown code theme (default: default, other options include monokai, solarized-dark, solarized-light) +#AIDER_CODE_THEME=default + +## Show diffs when committing changes (default: False) +#AIDER_SHOW_DIFFS=false + +############### +# Git Settings: + +## Enable/disable looking for a git repo (default: True) +#AIDER_GIT=true + +## Enable/disable adding .aider* to .gitignore (default: True) +#AIDER_GITIGNORE=true + +## Specify the aider ignore file (default: .aiderignore in git root) +#AIDER_AIDERIGNORE=.aiderignore + +## Enable/disable auto commit of LLM changes (default: True) +#AIDER_AUTO_COMMITS=true + +## Enable/disable commits when repo is found dirty (default: True) +#AIDER_DIRTY_COMMITS=true + +## Perform a dry run without modifying files (default: False) +#AIDER_DRY_RUN=false + +######################## +# Fixing and committing: + +## Commit all pending changes with a suitable commit message, then exit +#AIDER_COMMIT=false + +## Lint and fix provided files, or dirty files if none provided +#AIDER_LINT=false + +## Specify lint commands to run for different languages, eg: "python: flake8 --select=..." (can be used multiple times) +#AIDER_LINT_CMD= + +## Enable/disable automatic linting after changes (default: True) +#AIDER_AUTO_LINT=true + +## Specify command to run tests +#AIDER_TEST_CMD= + +## Enable/disable automatic testing after changes (default: False) +#AIDER_AUTO_TEST=false + +## Run tests and fix problems found +#AIDER_TEST=false + +################# +# Other Settings: + +## Use VI editing mode in the terminal (default: False) +#AIDER_VIM=false + +## Specify the language for voice using ISO 639-1 code (default: auto) +#AIDER_VOICE_LANGUAGE=en + +## Check for updates and return status in the exit code +#AIDER_CHECK_UPDATE=false + +## Skips checking for the update when the program runs +#AIDER_SKIP_CHECK_UPDATE=false + +## Apply the changes from the given file instead of running the chat (debug) +#AIDER_APPLY= + +## Always say yes to every confirmation +#AIDER_YES= + +## Enable verbose output +#AIDER_VERBOSE=false + +## Print the repo map and exit (debug) +#AIDER_SHOW_REPO_MAP=false + +## Print the system prompts and exit (debug) +#AIDER_SHOW_PROMPTS=false + +## Specify a single message to send the LLM, process reply then exit (disables chat mode) +#AIDER_MESSAGE= + +## Specify a file containing the message to send the LLM, process reply, then exit (disables chat mode) +#AIDER_MESSAGE_FILE= + +## Specify the encoding for input and output (default: utf-8) +#AIDER_ENCODING=utf-8 + +## Run aider in your browser +#AIDER_GUI=false ``` + + + diff --git a/website/docs/faq.md b/website/docs/faq.md index a1443b1e8..f32872828 100644 --- a/website/docs/faq.md +++ b/website/docs/faq.md @@ -1,9 +1,9 @@ --- -nav_order: 85 +nav_order: 90 description: Frequently asked questions about aider. --- -# Frequently asked questions +# FAQ {: .no_toc } - TOC diff --git a/website/docs/git.md b/website/docs/git.md index b25418b93..ea2b145fe 100644 --- a/website/docs/git.md +++ b/website/docs/git.md @@ -1,4 +1,5 @@ --- +parent: More info nav_order: 800 description: Aider is tightly integrated with git. --- @@ -14,14 +15,21 @@ Aider is tightly integrated with git, which makes it easy to: Aider specifically uses git in these ways: - - It asks to create a git repo if you launch it in a directory without one. - - Whenever aider edits a file, it commits those changes with a descriptive commit message. This makes it easy to undo or review aider's changes. - - Aider takes special care before editing files that already have uncommitted changes (dirty files). Aider will first commit any preexisting changes with a descriptive commit message. This keeps your edits separate from aider's edits, and makes sure you never lose your work if aider makes an inappropriate change. +- It asks to create a git repo if you launch it in a directory without one. +- Whenever aider edits a file, it commits those changes with a descriptive commit message. This makes it easy to undo or review aider's changes. +These commits will have "(aider)" appended to their git author and git committer metadata. +- Aider takes special care before editing files that already have uncommitted changes (dirty files). Aider will first commit any preexisting changes with a descriptive commit message. +This keeps your edits separate from aider's edits, and makes sure you never lose your work if aider makes an inappropriate change. +These commits will have "(aider)" appended to their git committer metadata. + +## In-chat commands Aider also allows you to use in-chat commands to `/diff` or `/undo` the last change. To do more complex management of your git history, you cat use raw `git` commands, either by using `/git` within the chat, or with standard git tools outside of aider. +## Disabling git integration + While it is not recommended, you can disable aider's use of git in a few ways: - `--no-auto-commits` will stop aider from git committing each of its changes. diff --git a/website/docs/languages.md b/website/docs/languages.md index 889e4caa9..d74f748c9 100644 --- a/website/docs/languages.md +++ b/website/docs/languages.md @@ -1,4 +1,5 @@ --- +parent: More info nav_order: 900 description: Aider supports pretty much all popular coding languages. --- diff --git a/website/docs/leaderboards/index.md b/website/docs/leaderboards/index.md index 4e3a2a59a..78d2abafc 100644 --- a/website/docs/leaderboards/index.md +++ b/website/docs/leaderboards/index.md @@ -16,22 +16,19 @@ While [aider can connect to almost any LLM](/docs/llms.html), it works best with models that score well on the benchmarks. -## DeepSeek Coder V2 beats GPT-4o, Opus +## Claude 3.5 Sonnet takes the top spot -The new -[DeepSeek Coder V2](https://aider.chat/docs/llms/deepseek.html) -model is now atop aider's code editing leaderboard! +Claude 3.5 Sonnet is now the top ranked model on aider's code editing leaderboard. +DeepSeek Coder V2 only spent 4 days in the top spot. -It's worth noting that DeepSeek Coder V2 is only capable of using aider's "whole" edit format. -This means it returns a modified full copy of each file when it makes changes. -Most other strong models are able to use aider's "diff" editing format, -which allows them to return diffs of edits -- saving time and token costs. - -Models which use the "whole" edit format can only edit files -which fit within their output token limits. -These output limits are often as low as 4k tokens, even for models -with very large context windows. +The new Sonnet came in 3rd on aider's refactoring leaderboard, behind GPT-4o and Opus. +Sonnet ranked #1 when using the "whole" editing format, +but it also scored very well with +aider's "diff" editing format. +This format allows it to return code changes as diffs -- saving time and token costs, +and making it practical to work with larger source files. +As such, aider uses "diff" by default with this new Sonnet model. ## Code editing leaderboard diff --git a/website/docs/llms.md b/website/docs/llms.md index aecb49f27..37cd82f7a 100644 --- a/website/docs/llms.md +++ b/website/docs/llms.md @@ -14,17 +14,21 @@ description: Aider can connect to most LLMs for AI pair programming. ## Best models {: .no_toc } -**Aider works best with [GPT-4o](/docs/llms/openai.html) and -[Claude 3 Opus](/docs/llms/anthropic.html),** -as they are the very best models for editing code. +Aider works best with these models, which are skilled at editing code: + +- [GPT-4o](/docs/llms/openai.html) +- [Claude 3.5 Sonnet](/docs/llms/anthropic.html) +- [Claude 3 Opus](/docs/llms/anthropic.html) +- [DeepSeek Coder V2](/docs/llms/deepseek.html) + ## Free models {: .no_toc } Aider works with a number of **free** API providers: -- The [DeepSeek Coder V2](/docs/llms/deepseek.html) model gets the top score on aider's code editing benchmark. DeepSeek currently offers 5M free tokens of API usage. -- Google's [Gemini 1.5 Pro](/docs/llms/gemini.html) is the most capable free model to use with aider, with +- The [DeepSeek Coder V2](/docs/llms/deepseek.html) model gets the top score on aider's code editing benchmark. DeepSeek has been offering 5M free tokens of API usage. +- Google's [Gemini 1.5 Pro](/docs/llms/gemini.html) works with aider, with code editing capabilities similar to GPT-3.5. - You can use [Llama 3 70B on Groq](/docs/llms/groq.html) which is comparable to GPT-3.5 in code editing performance. - Cohere also offers free API access to their [Command-R+ model](/docs/llms/cohere.html), which works with aider as a *very basic* coding assistant. diff --git a/website/docs/llms/anthropic.md b/website/docs/llms/anthropic.md index c2d2ff78f..027feef44 100644 --- a/website/docs/llms/anthropic.md +++ b/website/docs/llms/anthropic.md @@ -22,7 +22,7 @@ setx ANTHROPIC_API_KEY # Windows # Claude 3 Opus aider --opus -# Claude 3 Sonnet +# Claude 3.5 Sonnet aider --sonnet # List models available from Anthropic diff --git a/website/docs/llms/warnings.md b/website/docs/llms/warnings.md index 213f7cb8d..c33acffd2 100644 --- a/website/docs/llms/warnings.md +++ b/website/docs/llms/warnings.md @@ -8,10 +8,45 @@ nav_order: 900 {% include model-warnings.md %} +## Adding settings for missing models +You can register model settings used by aider for unknown models. +Create a `.aider.models.yml` file in one of these locations: + +- Your home directory. +- The root if your git repo. +- The current directory where you launch aider. +- Or specify a specific file with the `--model-settings-file ` switch. + +If the files above exist, they will be loaded in that order. +Files loaded last will take priority. + +The yaml file should be a a list of dictionary objects for each model, as follows: + +``` +- name: "gpt-3.5-turbo" + edit_format: "whole" + weak_model_name: "gpt-3.5-turbo" + use_repo_map: false + send_undo_reply: false + accepts_images: false + lazy: false + reminder_as_sys_msg: true + examples_as_sys_msg: false +- name: "gpt-4-turbo-2024-04-09" + edit_format: "udiff" + weak_model_name: "gpt-3.5-turbo" + use_repo_map: true + send_undo_reply: true + accepts_images: true + lazy: true + reminder_as_sys_msg: true + examples_as_sys_msg: false +``` + ## Specifying context window size and token costs You can register context window limits and costs for models that aren't known -to aider. Create a `.aider.models.json` file in one of these locations: +to aider. Create a `.aider.litellm.models.json` file in one of these locations: - Your home directory. - The root if your git repo. diff --git a/website/docs/more-info.md b/website/docs/more-info.md new file mode 100644 index 000000000..3b40cb9e1 --- /dev/null +++ b/website/docs/more-info.md @@ -0,0 +1,8 @@ +--- +has_children: true +nav_order: 85 +--- + +# More info + +See below for more info about aider, including some advanced topics. diff --git a/website/docs/options.md b/website/docs/options.md index 300330894..5b880ad16 100644 --- a/website/docs/options.md +++ b/website/docs/options.md @@ -19,8 +19,10 @@ usage: aider [-h] [--llm-history-file] [--openai-api-key] [--4] [--4o] [--4-turbo] [--35turbo] [--models] [--openai-api-base] [--openai-api-type] [--openai-api-version] [--openai-api-deployment-id] - [--openai-organization-id] [--model-metadata-file] - [--edit-format] [--weak-model] + [--openai-organization-id] [--model-settings-file] + [--model-metadata-file] + [--verify-ssl | --no-verify-ssl] [--edit-format] + [--weak-model] [--show-model-warnings | --no-show-model-warnings] [--map-tokens] [--max-chat-history-tokens] [--env-file] [--input-history-file] [--chat-history-file] @@ -75,7 +77,7 @@ Use claude-3-opus-20240229 model for the main chat Environment variable: `AIDER_OPUS` ### `--sonnet` -Use claude-3-sonnet-20240229 model for the main chat +Use claude-3-5-sonnet-20240620 model for the main chat Environment variable: `AIDER_SONNET` ### `--4` @@ -128,10 +130,22 @@ Environment variable: `OPENAI_API_DEPLOYMENT_ID` Specify the OpenAI organization ID Environment variable: `OPENAI_ORGANIZATION_ID` -### `--model-metadata-file MODEL_FILE` +### `--model-settings-file MODEL_SETTINGS_FILE` +Specify a file with aider model settings for unknown models +Environment variable: `AIDER_MODEL_SETTINGS_FILE` + +### `--model-metadata-file MODEL_METADATA_FILE` Specify a file with context window and costs for unknown models Environment variable: `AIDER_MODEL_METADATA_FILE` +### `--verify-ssl` +Verify the SSL cert when connecting to models (default: True) +Default: True +Environment variable: `AIDER_VERIFY_SSL` +Aliases: + - `--verify-ssl` + - `--no-verify-ssl` + ### `--edit-format EDIT_FORMAT` Specify what edit format the LLM should use (default depends on model) Environment variable: `AIDER_EDIT_FORMAT` diff --git a/website/docs/repomap.md b/website/docs/repomap.md index aefedc5dc..359243103 100644 --- a/website/docs/repomap.md +++ b/website/docs/repomap.md @@ -1,4 +1,5 @@ --- +parent: More info highlight_image: /assets/robot-ast.png nav_order: 900 description: Aider uses a map of your git repository to provide code context to LLMs. diff --git a/website/docs/scripting.md b/website/docs/scripting.md index 2d6149d31..984ecb864 100644 --- a/website/docs/scripting.md +++ b/website/docs/scripting.md @@ -1,4 +1,5 @@ --- +parent: More info nav_order: 900 description: You can script aider via the command line or python. --- diff --git a/website/docs/troubleshooting/edit-errors.md b/website/docs/troubleshooting/edit-errors.md index 7c7a4a359..dbbd527dd 100644 --- a/website/docs/troubleshooting/edit-errors.md +++ b/website/docs/troubleshooting/edit-errors.md @@ -33,8 +33,8 @@ so editing errors are probably unavoidable. ## Reduce distractions Many LLM now have very large context windows, -but filling them with irrelevant code often -cofuses the model. +but filling them with irrelevant code or conversation +can cofuse the model. - Don't add too many files to the chat, *just* add the files you think need to be edited. Aider also sends the LLM a [map of your entire git repo](https://aider.chat/docs/repomap.html), so other relevant code will be included automatically. diff --git a/website/index.md b/website/index.md index 0a362d291..cbf8498df 100644 --- a/website/index.md +++ b/website/index.md @@ -20,8 +20,8 @@ cog.out(text) Aider lets you pair program with LLMs, to edit code in your local git repository. Start a new project or work with an existing git repo. -Aider works best with GPT-4o and Claude 3 Opus -and can [connect to almost any LLM](https://aider.chat/docs/llms.html). +Aider can [connect to almost any LLM](https://aider.chat/docs/llms.html). +and works best with GPT-4o, Claude 3.5 Sonnet, Claude 3 Opus and DeepSeek Coder V2.

@@ -93,20 +98,13 @@ Pair program with AI. - [Code with your voice](https://aider.chat/docs/voice.html). -## State of the art +## Top tier performance -Aider has the -[top score on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html). +[Aider has the one of the top scores on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html). SWE Bench is a challenging software engineering benchmark where aider solved *real* GitHub issues from popular open source projects like django, scikitlearn, matplotlib, etc. -

- - aider swe bench - -

- ## More info - [Documentation](https://aider.chat/)