From 56a9463db8b5394d60def0ba1c00fcfd7d421fdf Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 25 Jun 2024 15:09:58 -0700 Subject: [PATCH 001/121] copy --- website/docs/usage.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/website/docs/usage.md b/website/docs/usage.md index 7451f9b2a..1bfbda5ad 100644 --- a/website/docs/usage.md +++ b/website/docs/usage.md @@ -37,26 +37,31 @@ Use /help to see in-chat commands, run with --help to see cmd line args ## Adding files -Just add the files that the aider will need to *edit*. +Add the files that the aider will need to *edit*. + +Don't add a bunch of extra files. If you add too many files, the LLM can get overwhelmed and confused (and it costs more tokens). Aider will automatically pull in content from related files so that it can [understand the rest of your code base](https://aider.chat/docs/repomap.html). -You can also run aider without naming any files and use the in-chat +You add files to the chat by naming them on the aider command line. +Or, you can use the in-chat `/add` command to add files. -Or you can skip adding files completely, and aider -will try to figure out which files need to be edited based +You can use aider without adding any files, +and it will try to figure out which files need to be edited based on your requests. +But you'll get the best results if you add the files that need +to edited. ## LLMs Aider uses GPT-4o by default, but you can [connect to many different LLMs](/docs/llms.html). -Claude 3 Opus is another model which works very well with aider, -which you can use by running `aider --opus`. +Claude 3.5 Sonnet is also works very well with aider, +which you can use by running `aider --sonnet`. You can run `aider --model XXX` to launch aider with a specific model. @@ -68,8 +73,8 @@ Or, during your chat you can switch models with the in-chat Ask aider to make changes to your code. It will show you some diffs of the changes it is making to complete you request. -Aider will git commit all of its changes, +[Aider will git commit all of its changes](/docs/git.html), so they are easy to track and undo. -You can always use the `/undo` command to undo changes you don't +You can always use the `/undo` command to undo AI changes that you don't like. From fb3a85b6d20a6a45efb239706ff047f919154559 Mon Sep 17 00:00:00 2001 From: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:12:46 -0700 Subject: [PATCH 002/121] Update usage.md --- website/docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/usage.md b/website/docs/usage.md index 1bfbda5ad..ad6bf5870 100644 --- a/website/docs/usage.md +++ b/website/docs/usage.md @@ -60,7 +60,7 @@ to edited. Aider uses GPT-4o by default, but you can [connect to many different LLMs](/docs/llms.html). -Claude 3.5 Sonnet is also works very well with aider, +Claude 3.5 Sonnet also works very well with aider, which you can use by running `aider --sonnet`. You can run `aider --model XXX` to launch aider with From ea97d0844947d0f6ec1472dd8f7b7f6455680476 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 25 Jun 2024 20:36:57 -0700 Subject: [PATCH 003/121] Handle null model.info max_input_tokens --- aider/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aider/models.py b/aider/models.py index 09c250da5..b9ae02590 100644 --- a/aider/models.py +++ b/aider/models.py @@ -328,7 +328,10 @@ class Model: self.missing_keys = res.get("missing_keys") self.keys_in_environment = res.get("keys_in_environment") - if self.info.get("max_input_tokens", 0) < 32 * 1024: + max_input_tokens = self.info.get("max_input_tokens") + if not max_input_tokens: + max_input_tokens = 0 + if max_input_tokens < 32 * 1024: self.max_chat_history_tokens = 1024 else: self.max_chat_history_tokens = 2 * 1024 From c020d94d5f5bb97c71eb295c3a930b9d9abcbe4d Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 26 Jun 2024 04:14:51 +0000 Subject: [PATCH 004/121] 8x repomap when finding files; fix max_input_tokens --- aider/models.py | 5 ++++- aider/repomap.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/aider/models.py b/aider/models.py index 3e61dd90b..505d71aa2 100644 --- a/aider/models.py +++ b/aider/models.py @@ -328,7 +328,10 @@ class Model: self.missing_keys = res.get("missing_keys") self.keys_in_environment = res.get("keys_in_environment") - if self.info.get("max_input_tokens", 0) < 32 * 1024: + max_input_tokens = self.info.get("max_input_tokens", 0) + if not max_input_tokens: + max_input_tokens = 0 + if max_input_tokens < 32 * 1024: self.max_chat_history_tokens = 1024 else: self.max_chat_history_tokens = 2 * 1024 diff --git a/aider/repomap.py b/aider/repomap.py index 20b675549..872424b62 100644 --- a/aider/repomap.py +++ b/aider/repomap.py @@ -71,7 +71,7 @@ class RepoMap: max_map_tokens = self.max_map_tokens # With no files in the chat, give a bigger view of the entire repo - MUL = 16 + MUL = 8 padding = 4096 if max_map_tokens and self.max_context_window: target = min(max_map_tokens * MUL, self.max_context_window - padding) From c1e55614f7bf5850e858cff17209d472c3c6dbe6 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 07:15:06 -0700 Subject: [PATCH 005/121] Don't rely on (aider) in git name field to police /undo #698 --- aider/commands.py | 5 +---- aider/tests/test_commands.py | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 00189fecd..5ea285111 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -331,10 +331,7 @@ class Commands: return last_commit = self.coder.repo.repo.head.commit - if ( - not last_commit.author.name.endswith(" (aider)") - or last_commit.hexsha[:7] != self.coder.last_aider_commit_hash - ): + if 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.") self.io.tool_error( "You could try `/git reset --hard HEAD^` but be aware that this is a destructive" diff --git a/aider/tests/test_commands.py b/aider/tests/test_commands.py index f6629f481..262262f39 100644 --- a/aider/tests/test_commands.py +++ b/aider/tests/test_commands.py @@ -523,8 +523,6 @@ 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 @@ -536,8 +534,6 @@ class TestCommands(TestCase): repo.git.add(filename) repo.git.commit("-m", "second commit") - del os.environ["GIT_AUTHOR_NAME"] - # Store the commit hash last_commit_hash = repo.head.commit.hexsha[:7] coder.last_aider_commit_hash = last_commit_hash From bafca6aaa0fdb7a6aeba14291f546b976368ceff Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 07:16:50 -0700 Subject: [PATCH 006/121] Updated HISTORY --- HISTORY.md | 4 ++++ website/HISTORY.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 0926c14c3..a1f7df050 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,10 @@ # Release history +### v0.40.6 + +- Fixed `/undo` so it works with `--no-attribute-author`. + ### v0.40.5 - Bump versions to pickup latest litellm to fix streaming issue with Gemini diff --git a/website/HISTORY.md b/website/HISTORY.md index 38625628f..f617fc5fd 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -12,6 +12,10 @@ cog.out(text) # Release history +### v0.40.6 + +- Fixed `/undo` so it works with `--no-attribute-author`. + ### v0.40.5 - Bump versions to pickup latest litellm to fix streaming issue with Gemini From 61d36d2f346217bb97a1bd89fb11e8a2e1e9a2ed Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 07:20:42 -0700 Subject: [PATCH 007/121] version bump to 0.40.6 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index d4a95c429..6e2b9eaa6 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.40.6-dev" +__version__ = "0.40.6" From 7e511dc21f4c0e90b66426ac93223c437df42548 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 07:21:31 -0700 Subject: [PATCH 008/121] set version to 0.40.7-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 6e2b9eaa6..8920a054a 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.40.6" +__version__ = "0.40.7-dev" From e095fde27ec0aef34ad9df6293187a04a9088b00 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:10:36 -0700 Subject: [PATCH 009/121] updated docs --- website/_includes/model-warnings.md | 3 + website/docs/config.md | 2 +- website/docs/config/adv-model-settings.md | 86 +++++++++++++++++++++ website/docs/{ => config}/aider_conf.md | 0 website/docs/{ => config}/dotenv.md | 0 website/docs/{ => config}/options.md | 0 website/docs/llms/warnings.md | 67 ---------------- website/docs/troubleshooting/edit-errors.md | 4 +- 8 files changed, 92 insertions(+), 70 deletions(-) create mode 100644 website/docs/config/adv-model-settings.md rename website/docs/{ => config}/aider_conf.md (100%) rename website/docs/{ => config}/dotenv.md (100%) rename website/docs/{ => config}/options.md (100%) diff --git a/website/_includes/model-warnings.md b/website/_includes/model-warnings.md index 4d5a07a41..aaba1f0b1 100644 --- a/website/_includes/model-warnings.md +++ b/website/_includes/model-warnings.md @@ -14,6 +14,9 @@ for that model. Aider will use an unlimited context window and assume the model is free, so this is not usually a significant problem. +See the docs on +[configuring advanced model settings](/docs/config/adv-model-settings.html) +for details on how to remove this warning. ## Did you mean? diff --git a/website/docs/config.md b/website/docs/config.md index b018e5a90..dca28da0f 100644 --- a/website/docs/config.md +++ b/website/docs/config.md @@ -12,7 +12,7 @@ Most options can also be set in an `.aider.conf.yml` file which can be placed in your home directory or at the root of your git repo. Or via environment variables like `AIDER_xxx`, -as noted in the [options reference](options.html). +as noted in the [options reference](/docs/config/options.html). Here are 3 equivalent ways of setting an option. First, via a command line switch: diff --git a/website/docs/config/adv-model-settings.md b/website/docs/config/adv-model-settings.md new file mode 100644 index 000000000..41f1be9da --- /dev/null +++ b/website/docs/config/adv-model-settings.md @@ -0,0 +1,86 @@ +--- +parent: Configuration +nav_order: 950 +description: Configuring advanced settings for LLMs. +--- + +# Advanced model settings + +## Context window size and token costs + +In most cases, you can safely ignore aider's warning about unknown context +window size and model costs. + +But, you can register context window limits and costs for models that aren't known +to aider. Create a `.aider.litellm.models.json` 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-metadata-file ` switch. + + +If the files above exist, they will be loaded in that order. +Files loaded last will take priority. + +The json file should be a dictionary with an entry for each model, as follows: + +``` +{ + "deepseek-chat": { + "max_tokens": 4096, + "max_input_tokens": 32000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.00000014, + "output_cost_per_token": 0.00000028, + "litellm_provider": "deepseek", + "mode": "chat" + } +} +``` + +See +[litellm's model_prices_and_context_window.json file](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json) for more examples. + +## Model settings + +Aider has a number of settings that control how it works with +different models. +These model settings are pre-configured for most popular models. +But it can sometimes be helpful to override them or add settings for +a model that aider doesn't know about. + +To do that, +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 +``` + diff --git a/website/docs/aider_conf.md b/website/docs/config/aider_conf.md similarity index 100% rename from website/docs/aider_conf.md rename to website/docs/config/aider_conf.md diff --git a/website/docs/dotenv.md b/website/docs/config/dotenv.md similarity index 100% rename from website/docs/dotenv.md rename to website/docs/config/dotenv.md diff --git a/website/docs/options.md b/website/docs/config/options.md similarity index 100% rename from website/docs/options.md rename to website/docs/config/options.md diff --git a/website/docs/llms/warnings.md b/website/docs/llms/warnings.md index c33acffd2..1034089f9 100644 --- a/website/docs/llms/warnings.md +++ b/website/docs/llms/warnings.md @@ -8,70 +8,3 @@ 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.litellm.models.json` 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-metadata-file ` switch. - - -If the files above exist, they will be loaded in that order. -Files loaded last will take priority. - -The json file should be a dictionary with an entry for each model, as follows: - -``` -{ - "deepseek-chat": { - "max_tokens": 4096, - "max_input_tokens": 32000, - "max_output_tokens": 4096, - "input_cost_per_token": 0.00000014, - "output_cost_per_token": 0.00000028, - "litellm_provider": "deepseek", - "mode": "chat" - } -} -``` - -See -[litellm's model_prices_and_context_window.json file](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json) for more examples. diff --git a/website/docs/troubleshooting/edit-errors.md b/website/docs/troubleshooting/edit-errors.md index dbbd527dd..6fe8978ad 100644 --- a/website/docs/troubleshooting/edit-errors.md +++ b/website/docs/troubleshooting/edit-errors.md @@ -21,8 +21,8 @@ In these cases, here are some things you might try. ## Use a capable model -If possible try using GPT-4o or Opus, as they are the strongest and most -capable models. +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 From 0704810d31d16562eec5411101fb7c0e383960ea Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:11:29 -0700 Subject: [PATCH 010/121] use new docs paths --- scripts/update-docs.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index fc16c4c66..feb5d7f43 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -14,8 +14,8 @@ 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 \ - website/docs/aider_conf.md + website/docs/config/dotenv.md \ + website/docs/config/options.md \ + website/docs/config/aider_conf.md From 34e83f9580e64d10dc1a62cbcbb8605e5336fc69 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:19:42 -0700 Subject: [PATCH 011/121] Implemented commit message attribution for Aider-generated edits. --- aider/repo.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aider/repo.py b/aider/repo.py index 713b463b3..0a755a1ac 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -84,6 +84,9 @@ class GitRepo: else: commit_message = self.get_commit_message(diffs, context) + if aider_edits and self.attribute_commit_message: + commit_message = "aider: " + commit_message + if not commit_message: commit_message = "(no commit message provided)" From 9c214e0faf9ccbab96a610e763f14396b46541a0 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:21:04 -0700 Subject: [PATCH 012/121] Implemented option to attribute commit messages in GitRepo class. --- aider/repo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aider/repo.py b/aider/repo.py index 0a755a1ac..6e3465697 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -25,12 +25,14 @@ class GitRepo: models=None, attribute_author=True, attribute_committer=True, + attribute_commit_message=False, ): self.io = io self.models = models self.attribute_author = attribute_author self.attribute_committer = attribute_committer + self.attribute_commit_message = attribute_commit_message if git_dname: check_fnames = [git_dname] From ec682fd683339ba97405bc7e404bc9eaf80ed9b4 Mon Sep 17 00:00:00 2001 From: "Paul Gauthier (aider)" Date: Thu, 27 Jun 2024 10:21:25 -0700 Subject: [PATCH 013/121] Added an option to prefix commit messages with 'aider: '. --- aider/args.py | 6 ++++++ aider/coders/base_coder.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/aider/args.py b/aider/args.py index bb3fd2cfc..473a9dc2e 100644 --- a/aider/args.py +++ b/aider/args.py @@ -345,6 +345,12 @@ def get_parser(default_config_files, git_root): default=True, help="Attribute aider commits in the git committer name (default: True)", ) + group.add_argument( + "--attribute-commit-message", + action=argparse.BooleanOptionalAction, + default=False, + help="Prefix commit messages with 'aider: ' (default: False)", + ) group.add_argument( "--dry-run", action=argparse.BooleanOptionalAction, diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 1c94beff6..c5a4bf794 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -221,6 +221,7 @@ class Coder: test_cmd=None, attribute_author=True, attribute_committer=True, + attribute_commit_message=False, ): if not fnames: fnames = [] @@ -280,6 +281,7 @@ class Coder: models=main_model.commit_message_models(), attribute_author=attribute_author, attribute_committer=attribute_committer, + attribute_commit_message=attribute_commit_message, ) self.root = self.repo.root except FileNotFoundError: From 506636195aa31fdd25f1e5808f5dd90fb298a750 Mon Sep 17 00:00:00 2001 From: "Paul Gauthier (aider)" Date: Thu, 27 Jun 2024 10:23:31 -0700 Subject: [PATCH 014/121] Implemented the ability to attribute the commit message in the main function. --- aider/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aider/main.py b/aider/main.py index ef22d2c4f..75a9cde24 100644 --- a/aider/main.py +++ b/aider/main.py @@ -441,6 +441,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F test_cmd=args.test_cmd, attribute_author=args.attribute_author, attribute_committer=args.attribute_committer, + attribute_commit_message=args.attribute_commit_message, ) except ValueError as err: From 9cc6447e57015f80a9a4f88b3917d66d8befcbfa Mon Sep 17 00:00:00 2001 From: "Paul Gauthier (aider)" Date: Thu, 27 Jun 2024 10:26:36 -0700 Subject: [PATCH 015/121] Added support for prefixing commit messages with 'aider: ' to identify commits made by the aider tool. --- website/docs/git.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/docs/git.md b/website/docs/git.md index fc42c9bcb..36b238831 100644 --- a/website/docs/git.md +++ b/website/docs/git.md @@ -44,3 +44,6 @@ Aider marks commits that it either authored or committed. You can use `--no-attribute-author` and `--no-attribute-committer` to disable modification of the git author and committer name fields. + +Additionally, you can use `--attribute-commit-message` to prefix commit messages with 'aider: '. +This option is disabled by default, but can be useful for easily identifying commits made by aider. From 5d86117249dff39e3b650d42b5af1ac4f65eb8b5 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:27:28 -0700 Subject: [PATCH 016/121] copy --- website/assets/sample.aider.conf.yml | 3 +++ website/assets/sample.env | 3 +++ website/docs/config/aider_conf.md | 3 +++ website/docs/config/dotenv.md | 3 +++ website/docs/config/options.md | 9 +++++++++ 5 files changed, 21 insertions(+) diff --git a/website/assets/sample.aider.conf.yml b/website/assets/sample.aider.conf.yml index 7075eb6ff..6d401fe24 100644 --- a/website/assets/sample.aider.conf.yml +++ b/website/assets/sample.aider.conf.yml @@ -160,6 +160,9 @@ ## Attribute aider commits in the git committer name (default: True) #attribute-committer: true +## Prefix commit messages with 'aider: ' (default: False) +#attribute-commit-message: false + ## Perform a dry run without modifying files (default: False) #dry-run: false diff --git a/website/assets/sample.env b/website/assets/sample.env index 71fb2b34a..1f3420254 100644 --- a/website/assets/sample.env +++ b/website/assets/sample.env @@ -168,6 +168,9 @@ ## Attribute aider commits in the git committer name (default: True) #AIDER_ATTRIBUTE_COMMITTER=true +## Prefix commit messages with 'aider: ' (default: False) +#AIDER_ATTRIBUTE_COMMIT_MESSAGE=false + ## Perform a dry run without modifying files (default: False) #AIDER_DRY_RUN=false diff --git a/website/docs/config/aider_conf.md b/website/docs/config/aider_conf.md index 61f369834..fe80efe36 100644 --- a/website/docs/config/aider_conf.md +++ b/website/docs/config/aider_conf.md @@ -188,6 +188,9 @@ cog.outl("```") ## Attribute aider commits in the git committer name (default: True) #attribute-committer: true +## Prefix commit messages with 'aider: ' (default: False) +#attribute-commit-message: false + ## Perform a dry run without modifying files (default: False) #dry-run: false diff --git a/website/docs/config/dotenv.md b/website/docs/config/dotenv.md index 51faf6c07..2c201dc2c 100644 --- a/website/docs/config/dotenv.md +++ b/website/docs/config/dotenv.md @@ -201,6 +201,9 @@ cog.outl("```") ## Attribute aider commits in the git committer name (default: True) #AIDER_ATTRIBUTE_COMMITTER=true +## Prefix commit messages with 'aider: ' (default: False) +#AIDER_ATTRIBUTE_COMMIT_MESSAGE=false + ## Perform a dry run without modifying files (default: False) #AIDER_DRY_RUN=false diff --git a/website/docs/config/options.md b/website/docs/config/options.md index 82f58e1bd..d8bea2979 100644 --- a/website/docs/config/options.md +++ b/website/docs/config/options.md @@ -43,6 +43,7 @@ usage: aider [-h] [--llm-history-file] [--openai-api-key] [--dirty-commits | --no-dirty-commits] [--attribute-author | --no-attribute-author] [--attribute-committer | --no-attribute-committer] + [--attribute-commit-message | --no-attribute-commit-message] [--dry-run | --no-dry-run] [--commit] [--lint] [--lint-cmd] [--auto-lint | --no-auto-lint] [--test-cmd] [--auto-test | --no-auto-test] [--test] @@ -316,6 +317,14 @@ Aliases: - `--attribute-committer` - `--no-attribute-committer` +### `--attribute-commit-message` +Prefix commit messages with 'aider: ' (default: False) +Default: False +Environment variable: `AIDER_ATTRIBUTE_COMMIT_MESSAGE` +Aliases: + - `--attribute-commit-message` + - `--no-attribute-commit-message` + ### `--dry-run` Perform a dry run without modifying files (default: False) Default: False From fa7255cbf533b2ed36e0f56a38b3dfbf4f81b75b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:34:48 -0700 Subject: [PATCH 017/121] moved --llm-history-file in args --- aider/args.py | 12 +++++------ website/assets/sample.aider.conf.yml | 6 +++--- website/assets/sample.env | 6 +++--- website/docs/config/aider_conf.md | 6 +++--- website/docs/config/dotenv.md | 6 +++--- website/docs/config/options.md | 31 ++++++++++++++-------------- 6 files changed, 33 insertions(+), 34 deletions(-) diff --git a/aider/args.py b/aider/args.py index 473a9dc2e..474fb2257 100644 --- a/aider/args.py +++ b/aider/args.py @@ -29,12 +29,6 @@ def get_parser(default_config_files, git_root): auto_env_var_prefix="AIDER_", ) group = parser.add_argument_group("Main") - group.add_argument( - "--llm-history-file", - metavar="LLM_HISTORY_FILE", - default=None, - 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)" ) @@ -236,6 +230,12 @@ def get_parser(default_config_files, git_root): default=False, help="Restore the previous chat history messages (default: False)", ) + group.add_argument( + "--llm-history-file", + metavar="LLM_HISTORY_FILE", + default=None, + help="Log the conversation with the LLM to this file (for example, .aider.llm.history)", + ) ########## group = parser.add_argument_group("Output Settings") diff --git a/website/assets/sample.aider.conf.yml b/website/assets/sample.aider.conf.yml index 6d401fe24..66a24dc33 100644 --- a/website/assets/sample.aider.conf.yml +++ b/website/assets/sample.aider.conf.yml @@ -13,9 +13,6 @@ ####### # Main: -## Log the conversation with the LLM to this file (for example, .aider.llm.history) -#llm-history-file: - ## Specify the OpenAI API key #openai-api-key: @@ -103,6 +100,9 @@ ## Restore the previous chat history messages (default: False) #restore-chat-history: false +## Log the conversation with the LLM to this file (for example, .aider.llm.history) +#llm-history-file: + ################## # Output Settings: diff --git a/website/assets/sample.env b/website/assets/sample.env index 1f3420254..154a722c9 100644 --- a/website/assets/sample.env +++ b/website/assets/sample.env @@ -21,9 +21,6 @@ ####### # 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= @@ -111,6 +108,9 @@ ## Restore the previous chat history messages (default: False) #AIDER_RESTORE_CHAT_HISTORY=false +## Log the conversation with the LLM to this file (for example, .aider.llm.history) +#AIDER_LLM_HISTORY_FILE= + ################## # Output Settings: diff --git a/website/docs/config/aider_conf.md b/website/docs/config/aider_conf.md index fe80efe36..58c452fb8 100644 --- a/website/docs/config/aider_conf.md +++ b/website/docs/config/aider_conf.md @@ -41,9 +41,6 @@ cog.outl("```") ####### # Main: -## Log the conversation with the LLM to this file (for example, .aider.llm.history) -#llm-history-file: - ## Specify the OpenAI API key #openai-api-key: @@ -131,6 +128,9 @@ cog.outl("```") ## Restore the previous chat history messages (default: False) #restore-chat-history: false +## Log the conversation with the LLM to this file (for example, .aider.llm.history) +#llm-history-file: + ################## # Output Settings: diff --git a/website/docs/config/dotenv.md b/website/docs/config/dotenv.md index 2c201dc2c..9aae6011b 100644 --- a/website/docs/config/dotenv.md +++ b/website/docs/config/dotenv.md @@ -54,9 +54,6 @@ cog.outl("```") ####### # 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= @@ -144,6 +141,9 @@ cog.outl("```") ## Restore the previous chat history messages (default: False) #AIDER_RESTORE_CHAT_HISTORY=false +## Log the conversation with the LLM to this file (for example, .aider.llm.history) +#AIDER_LLM_HISTORY_FILE= + ################## # Output Settings: diff --git a/website/docs/config/options.md b/website/docs/config/options.md index d8bea2979..557d0cf53 100644 --- a/website/docs/config/options.md +++ b/website/docs/config/options.md @@ -20,24 +20,23 @@ from aider.args import get_md_help cog.out(get_md_help()) ]]]--> ``` -usage: aider [-h] [--llm-history-file] [--openai-api-key] - [--anthropic-api-key] [--model] [--opus] [--sonnet] - [--4] [--4o] [--4-turbo] [--35turbo] [--models] - [--openai-api-base] [--openai-api-type] - [--openai-api-version] [--openai-api-deployment-id] - [--openai-organization-id] [--model-settings-file] - [--model-metadata-file] +usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model] + [--opus] [--sonnet] [--4] [--4o] [--4-turbo] + [--35turbo] [--models] [--openai-api-base] + [--openai-api-type] [--openai-api-version] + [--openai-api-deployment-id] [--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] [--restore-chat-history | --no-restore-chat-history] - [--dark-mode] [--light-mode] [--pretty | --no-pretty] - [--stream | --no-stream] [--user-input-color] - [--tool-output-color] [--tool-error-color] - [--assistant-output-color] [--code-theme] - [--show-diffs] [--git | --no-git] + [--llm-history-file] [--dark-mode] [--light-mode] + [--pretty | --no-pretty] [--stream | --no-stream] + [--user-input-color] [--tool-output-color] + [--tool-error-color] [--assistant-output-color] + [--code-theme] [--show-diffs] [--git | --no-git] [--gitignore | --no-gitignore] [--aiderignore] [--auto-commits | --no-auto-commits] [--dirty-commits | --no-dirty-commits] @@ -64,10 +63,6 @@ Aliases: ## Main: -### `--llm-history-file LLM_HISTORY_FILE` -Log the conversation with the LLM to this file (for example, .aider.llm.history) -Environment variable: `AIDER_LLM_HISTORY_FILE` - ### `--openai-api-key OPENAI_API_KEY` Specify the OpenAI API key Environment variable: `OPENAI_API_KEY` @@ -205,6 +200,10 @@ Aliases: - `--restore-chat-history` - `--no-restore-chat-history` +### `--llm-history-file LLM_HISTORY_FILE` +Log the conversation with the LLM to this file (for example, .aider.llm.history) +Environment variable: `AIDER_LLM_HISTORY_FILE` + ## Output Settings: ### `--dark-mode` From 7016587bc646cd2029ca5b98698fadc7988afd56 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:38:15 -0700 Subject: [PATCH 018/121] copy --- website/docs/config.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/website/docs/config.md b/website/docs/config.md index dca28da0f..1aeb6e975 100644 --- a/website/docs/config.md +++ b/website/docs/config.md @@ -11,24 +11,31 @@ command line switches. Most options can also be set in an `.aider.conf.yml` file which can be placed in your home directory or at the root of your git repo. -Or via environment variables like `AIDER_xxx`, -as noted in the [options reference](/docs/config/options.html). +Or by setting environment variables like `AIDER_xxx` +either in your shell or a `.env` file. -Here are 3 equivalent ways of setting an option. First, via a command line switch: +Here are 4 equivalent ways of setting an option. + +With a command line switch: ``` $ aider --dark-mode ``` -Or, via an env variable: - -``` -export AIDER_DARK_MODE=true -``` - -Or in the `.aider.conf.yml` file: +Using a `.aider.conf.yml` file: ```yaml dark-mode: true ``` +By setting an environgment variable: + +``` +export AIDER_DARK_MODE=true +``` + +Using an `.env` file: + +``` +AIDER_DARK_MODE=true +``` From 87f4d25133d65e1d5e083ef1914125c766b1dfcb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 10:58:06 -0700 Subject: [PATCH 019/121] Added gui support for tool_error(log_only=True) #740 --- aider/gui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/aider/gui.py b/aider/gui.py index b2239b162..a2284c1b9 100755 --- a/aider/gui.py +++ b/aider/gui.py @@ -17,9 +17,10 @@ from aider.scrape import Scraper class CaptureIO(InputOutput): lines = [] - def tool_output(self, msg): - self.lines.append(msg) - super().tool_output(msg) + def tool_output(self, msg, log_only=False): + if not log_only: + self.lines.append(msg) + super().tool_output(msg, log_only=log_only) def tool_error(self, msg): self.lines.append(msg) From 044617b1b7f15297c88658efa3ca6e822f02df7a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 14:40:46 -0700 Subject: [PATCH 020/121] continue roughly working using anthropic's prefill --- aider/coders/base_coder.py | 64 +++++++++++++++++++++++++------------- aider/sendchat.py | 3 +- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index c5a4bf794..fed054920 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -13,7 +13,6 @@ from json.decoder import JSONDecodeError from pathlib import Path import git -import openai from jsonschema import Draft7Validator from rich.console import Console, Text from rich.markdown import Markdown @@ -37,7 +36,7 @@ class MissingAPIKeyError(ValueError): pass -class ExhaustedContextWindow(Exception): +class FinishReasonLength(Exception): pass @@ -812,28 +811,43 @@ class Coder: if self.verbose: utils.show_messages(messages, functions=self.functions) + multi_response_content = "" exhausted = False interrupted = False - try: - yield from self.send(messages, functions=self.functions) - except KeyboardInterrupt: - interrupted = True - except ExhaustedContextWindow: - exhausted = True - except litellm.exceptions.BadRequestError as err: - if "ContextWindowExceededError" in err.message: + while True: + try: + yield from self.send(messages, functions=self.functions) + break + except KeyboardInterrupt: + interrupted = True + break + except litellm.ContextWindowExceededError as cwe_err: + # the input is overflowing the context window exhausted = True - else: - self.io.tool_error(f"BadRequestError: {err}") + dump(cwe_err) + break + except litellm.exceptions.BadRequestError as br_err: + dump(br_err) + self.io.tool_error(f"BadRequestError: {br_err}") return - except openai.BadRequestError as err: - if "maximum context length" in str(err): - exhausted = True - else: - raise err - except Exception as err: - self.io.tool_error(f"Unexpected error: {err}") - return + except FinishReasonLength as frl_err: + # finish_reason=length means 4k output limit? + dump(frl_err) + # exhausted = True + + multi_response_content += self.partial_response_content + if messages[-1]["role"] == "assistant": + messages[-1]["content"] = multi_response_content + else: + messages.append(dict(role="assistant", content=multi_response_content)) + except Exception as err: + self.io.tool_error(f"Unexpected error: {err}") + traceback.print_exc() + return + + if multi_response_content: + multi_response_content += self.partial_response_content + self.partial_response_content = multi_response_content if exhausted: self.show_exhausted_error() @@ -1103,7 +1117,7 @@ class Coder: if show_func_err and show_content_err: self.io.tool_error(show_func_err) self.io.tool_error(show_content_err) - raise Exception("No data found in openai response!") + raise Exception("No data found in LLM response!") tokens = None if hasattr(completion, "usage") and completion.usage is not None: @@ -1131,6 +1145,12 @@ class Coder: if tokens is not None: self.io.tool_output(tokens) + if ( + hasattr(completion.choices[0], "finish_reason") + and completion.choices[0].finish_reason == "length" + ): + raise FinishReasonLength() + def show_send_output_stream(self, completion): if self.show_pretty(): mdargs = dict(style=self.assistant_output_color, code_theme=self.code_theme) @@ -1147,7 +1167,7 @@ class Coder: hasattr(chunk.choices[0], "finish_reason") and chunk.choices[0].finish_reason == "length" ): - raise ExhaustedContextWindow() + raise FinishReasonLength() try: func = chunk.choices[0].delta.function_call diff --git a/aider/sendchat.py b/aider/sendchat.py index 19e91d255..8f661f598 100644 --- a/aider/sendchat.py +++ b/aider/sendchat.py @@ -3,7 +3,6 @@ import json import backoff import httpx -import openai from aider.dump import dump # noqa: F401 from aider.litellm import litellm @@ -85,5 +84,5 @@ def simple_send_with_retries(model_name, messages): stream=False, ) return response.choices[0].message.content - except (AttributeError, openai.BadRequestError): + except (AttributeError, litellm.exceptions.BadRequestError): return From dac12e342bbffbe403d199eea0b55ee20d71add5 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 15:05:42 -0700 Subject: [PATCH 021/121] wip --- aider/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aider/models.py b/aider/models.py index b9ae02590..be2ec9149 100644 --- a/aider/models.py +++ b/aider/models.py @@ -27,6 +27,7 @@ class ModelSettings: lazy: bool = False reminder_as_sys_msg: bool = False examples_as_sys_msg: bool = False + can_prefill: bool = False # https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo @@ -178,6 +179,7 @@ MODEL_SETTINGS = [ "claude-3-sonnet-20240229", "whole", weak_model_name="claude-3-haiku-20240307", + can_prefill=True, ), ModelSettings( "claude-3-5-sonnet-20240620", From e3805350c986ecb9cd3177dc0c7f213c0d7d248c Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 15:22:16 -0700 Subject: [PATCH 022/121] Added can_prefill metadata to the anthropic models --- aider/coders/base_coder.py | 16 ++++++++-------- aider/models.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index fed054920..bf82b9ee6 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -821,20 +821,20 @@ class Coder: except KeyboardInterrupt: interrupted = True break - except litellm.ContextWindowExceededError as cwe_err: - # the input is overflowing the context window + except litellm.ContextWindowExceededError: + # The input is overflowing the context window! exhausted = True - dump(cwe_err) break except litellm.exceptions.BadRequestError as br_err: - dump(br_err) self.io.tool_error(f"BadRequestError: {br_err}") return - except FinishReasonLength as frl_err: - # finish_reason=length means 4k output limit? - dump(frl_err) - # exhausted = True + except FinishReasonLength: + # We hit the 4k output limit! + if not self.main_model.can_prefill: + exhausted = True + break + # Use prefill to continue the response multi_response_content += self.partial_response_content if messages[-1]["role"] == "assistant": messages[-1]["content"] = multi_response_content diff --git a/aider/models.py b/aider/models.py index be2ec9149..5f211518a 100644 --- a/aider/models.py +++ b/aider/models.py @@ -167,6 +167,7 @@ MODEL_SETTINGS = [ weak_model_name="claude-3-haiku-20240307", use_repo_map=True, send_undo_reply=True, + can_prefill=True, ), ModelSettings( "openrouter/anthropic/claude-3-opus", @@ -174,6 +175,7 @@ MODEL_SETTINGS = [ weak_model_name="openrouter/anthropic/claude-3-haiku", use_repo_map=True, send_undo_reply=True, + can_prefill=True, ), ModelSettings( "claude-3-sonnet-20240229", @@ -187,6 +189,7 @@ MODEL_SETTINGS = [ weak_model_name="claude-3-haiku-20240307", use_repo_map=True, examples_as_sys_msg=True, + can_prefill=True, ), ModelSettings( "anthropic/claude-3-5-sonnet-20240620", @@ -194,6 +197,7 @@ MODEL_SETTINGS = [ weak_model_name="claude-3-haiku-20240307", use_repo_map=True, examples_as_sys_msg=True, + can_prefill=True, ), ModelSettings( "openrouter/anthropic/claude-3.5-sonnet", @@ -201,6 +205,7 @@ MODEL_SETTINGS = [ weak_model_name="openrouter/anthropic/claude-3-haiku-20240307", use_repo_map=True, examples_as_sys_msg=True, + can_prefill=True, ), # Vertex AI Claude models ModelSettings( @@ -208,6 +213,8 @@ MODEL_SETTINGS = [ "diff", weak_model_name="vertex_ai/claude-3-haiku@20240307", use_repo_map=True, + examples_as_sys_msg=True, + can_prefill=True, ), ModelSettings( "vertex_ai/claude-3-opus@20240229", @@ -215,11 +222,13 @@ MODEL_SETTINGS = [ weak_model_name="vertex_ai/claude-3-haiku@20240307", use_repo_map=True, send_undo_reply=True, + can_prefill=True, ), ModelSettings( "vertex_ai/claude-3-sonnet@20240229", "whole", weak_model_name="vertex_ai/claude-3-haiku@20240307", + can_prefill=True, ), # Cohere ModelSettings( @@ -377,6 +386,16 @@ class Model: if "gpt-3.5" in model or "gpt-4" in model: self.reminder_as_sys_msg = True + if "anthropic" in model: + self.can_prefill = True + + if "3.5-sonnet" in model or "3-5-sonnet" in model: + self.edit_format = "diff" + self.use_repo_map = True + self.examples_as_sys_msg = True + self.can_prefill = (True,) + self.can_prefill = True + # use the defaults if self.edit_format == "diff": self.use_repo_map = True From 6f31450b90f6276eaf8c6cebe1a4974db8dc71bb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 15:26:40 -0700 Subject: [PATCH 023/121] we don't want badreq errors bubbling out of Coder.run() --- aider/tests/test_coder.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/aider/tests/test_coder.py b/aider/tests/test_coder.py index 2cd3bb9ba..ca3014ff9 100644 --- a/aider/tests/test_coder.py +++ b/aider/tests/test_coder.py @@ -1,16 +1,15 @@ import tempfile import unittest from pathlib import Path -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock import git -import openai from aider.coders import Coder from aider.dump import dump # noqa: F401 from aider.io import InputOutput from aider.models import Model -from aider.utils import ChdirTemporaryDirectory, GitTemporaryDirectory +from aider.utils import GitTemporaryDirectory class TestCoder(unittest.TestCase): @@ -330,25 +329,6 @@ class TestCoder(unittest.TestCase): # both files should still be here self.assertEqual(len(coder.abs_fnames), 2) - def test_run_with_invalid_request_error(self): - with ChdirTemporaryDirectory(): - # Mock the IO object - mock_io = MagicMock() - - # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, None, mock_io) - - # Call the run method and assert that InvalidRequestError is raised - with self.assertRaises(openai.BadRequestError): - with patch("litellm.completion") as Mock: - Mock.side_effect = openai.BadRequestError( - message="Invalid request", - response=MagicMock(), - body=None, - ) - - coder.run(with_message="hi") - def test_new_file_edit_one_commit(self): """A new file shouldn't get pre-committed before the GPT edit commit""" with GitTemporaryDirectory(): From a3fe3c4dcf21b52e2d9ef320841a8152d84e59fc Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 27 Jun 2024 15:36:01 -0700 Subject: [PATCH 024/121] cleanup --- aider/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aider/models.py b/aider/models.py index 5f211518a..7013bc709 100644 --- a/aider/models.py +++ b/aider/models.py @@ -393,7 +393,6 @@ class Model: self.edit_format = "diff" self.use_repo_map = True self.examples_as_sys_msg = True - self.can_prefill = (True,) self.can_prefill = True # use the defaults From d6467a8e30388d5f0a2fa9a79a9af805950a20fb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 15:10:20 -0700 Subject: [PATCH 025/121] keep markdown stream open across multi response content --- aider/coders/base_coder.py | 43 +++++++++++++++++++-------------- aider/coders/wholefile_coder.py | 6 ++--- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index bf82b9ee6..11697d071 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -811,7 +811,13 @@ class Coder: if self.verbose: utils.show_messages(messages, functions=self.functions) - multi_response_content = "" + self.multi_response_content = "" + if self.show_pretty(): + mdargs = dict(style=self.assistant_output_color, code_theme=self.code_theme) + self.mdstream = MarkdownStream(mdargs=mdargs) + else: + self.mdstream = None + exhausted = False interrupted = False while True: @@ -835,19 +841,19 @@ class Coder: break # Use prefill to continue the response - multi_response_content += self.partial_response_content + self.multi_response_content += self.partial_response_content if messages[-1]["role"] == "assistant": - messages[-1]["content"] = multi_response_content + messages[-1]["content"] = self.multi_response_content else: - messages.append(dict(role="assistant", content=multi_response_content)) + messages.append(dict(role="assistant", content=self.multi_response_content)) except Exception as err: self.io.tool_error(f"Unexpected error: {err}") traceback.print_exc() return - if multi_response_content: - multi_response_content += self.partial_response_content - self.partial_response_content = multi_response_content + if self.multi_response_content: + self.multi_response_content += self.partial_response_content + self.partial_response_content = self.multi_response_content if exhausted: self.show_exhausted_error() @@ -1152,11 +1158,7 @@ class Coder: raise FinishReasonLength() def show_send_output_stream(self, completion): - if self.show_pretty(): - mdargs = dict(style=self.assistant_output_color, code_theme=self.code_theme) - mdstream = MarkdownStream(mdargs=mdargs) - else: - mdstream = None + finish_reason_length = False try: for chunk in completion: @@ -1167,6 +1169,8 @@ class Coder: hasattr(chunk.choices[0], "finish_reason") and chunk.choices[0].finish_reason == "length" ): + if self.main_model.can_prefill: + finish_reason_length = True raise FinishReasonLength() try: @@ -1188,24 +1192,27 @@ class Coder: text = None if self.show_pretty(): - self.live_incremental_response(mdstream, False) + self.live_incremental_response(False) elif text: sys.stdout.write(text) sys.stdout.flush() yield text finally: - if mdstream: - self.live_incremental_response(mdstream, True) + if self.show_pretty() and not finish_reason_length: + self.live_incremental_response(True) - def live_incremental_response(self, mdstream, final): + def live_incremental_response(self, final): show_resp = self.render_incremental_response(final) if not show_resp: return - mdstream.update(show_resp, final=final) + self.mdstream.update(show_resp, final=final) def render_incremental_response(self, final): - return self.partial_response_content + return self.get_multi_response_content() + + def get_multi_response_content(self): + return self.multi_response_content + self.partial_response_content def get_rel_fname(self, fname): return os.path.relpath(fname, self.root) diff --git a/aider/coders/wholefile_coder.py b/aider/coders/wholefile_coder.py index b9ecc60b5..a4420c462 100644 --- a/aider/coders/wholefile_coder.py +++ b/aider/coders/wholefile_coder.py @@ -1,6 +1,6 @@ +from pathlib import Path from aider import diffs -from pathlib import Path from ..dump import dump # noqa: F401 from .base_coder import Coder @@ -26,10 +26,10 @@ class WholeFileCoder(Coder): try: return self.get_edits(mode="diff") except ValueError: - return self.partial_response_content + return self.get_multi_response_content() def get_edits(self, mode="update"): - content = self.partial_response_content + content = self.get_multi_response_content() chat_files = self.get_inchat_relative_files() From e7aa10a89b7a847067f98b8b9c6f08d0585e508b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 15:27:17 -0700 Subject: [PATCH 026/121] refac --- aider/coders/base_coder.py | 73 ++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 11697d071..969de2d59 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -812,7 +812,7 @@ class Coder: utils.show_messages(messages, functions=self.functions) self.multi_response_content = "" - if self.show_pretty(): + if self.show_pretty() and self.stream: mdargs = dict(style=self.assistant_output_color, code_theme=self.code_theme) self.mdstream = MarkdownStream(mdargs=mdargs) else: @@ -851,6 +851,9 @@ class Coder: traceback.print_exc() return + if self.mdstream: + self.live_incremental_response(True) + if self.multi_response_content: self.multi_response_content += self.partial_response_content self.partial_response_content = self.multi_response_content @@ -1158,48 +1161,40 @@ class Coder: raise FinishReasonLength() def show_send_output_stream(self, completion): - finish_reason_length = False + for chunk in completion: + if len(chunk.choices) == 0: + continue - try: - for chunk in completion: - if len(chunk.choices) == 0: - continue + if ( + hasattr(chunk.choices[0], "finish_reason") + and chunk.choices[0].finish_reason == "length" + ): + raise FinishReasonLength() - if ( - hasattr(chunk.choices[0], "finish_reason") - and chunk.choices[0].finish_reason == "length" - ): - if self.main_model.can_prefill: - finish_reason_length = True - raise FinishReasonLength() + try: + func = chunk.choices[0].delta.function_call + # dump(func) + for k, v in func.items(): + if k in self.partial_response_function_call: + self.partial_response_function_call[k] += v + else: + self.partial_response_function_call[k] = v + except AttributeError: + pass - try: - func = chunk.choices[0].delta.function_call - # dump(func) - for k, v in func.items(): - if k in self.partial_response_function_call: - self.partial_response_function_call[k] += v - else: - self.partial_response_function_call[k] = v - except AttributeError: - pass + try: + text = chunk.choices[0].delta.content + if text: + self.partial_response_content += text + except AttributeError: + text = None - try: - text = chunk.choices[0].delta.content - if text: - self.partial_response_content += text - except AttributeError: - text = None - - if self.show_pretty(): - self.live_incremental_response(False) - elif text: - sys.stdout.write(text) - sys.stdout.flush() - yield text - finally: - if self.show_pretty() and not finish_reason_length: - self.live_incremental_response(True) + if self.show_pretty(): + self.live_incremental_response(False) + elif text: + sys.stdout.write(text) + sys.stdout.flush() + yield text def live_incremental_response(self, final): show_resp = self.render_incremental_response(final) From 307cfb2f44dfb0f67032e956bb6016a881cc4539 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 15:30:16 -0700 Subject: [PATCH 027/121] fix so tests pass --- aider/coders/base_coder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 969de2d59..028fbdc67 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -66,6 +66,7 @@ class Coder: test_cmd = None lint_outcome = None test_outcome = None + multi_response_content = "" @classmethod def create( @@ -857,6 +858,7 @@ class Coder: if self.multi_response_content: self.multi_response_content += self.partial_response_content self.partial_response_content = self.multi_response_content + self.multi_response_content = "" if exhausted: self.show_exhausted_error() From 8efc66b46e6e562c7482a05859d4b696fdf97c29 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 16:47:08 -0700 Subject: [PATCH 028/121] fix test for windows --- aider/tests/test_wholefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/tests/test_wholefile.py b/aider/tests/test_wholefile.py index 9cbef9a99..78aabec2b 100644 --- a/aider/tests/test_wholefile.py +++ b/aider/tests/test_wholefile.py @@ -288,7 +288,7 @@ after b files = [file1] # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, "whole", io=InputOutput(), fnames=files) + coder = Coder.create(self.GPT35, "whole", io=InputOutput(pretty=False), fnames=files) # no trailing newline so the response content below doesn't add ANOTHER newline new_content = "new\ntwo\nthree" From a042914e66ce07b68d487a88d4218299f21590af Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 17:02:44 -0700 Subject: [PATCH 029/121] fix test for windows --- aider/tests/test_wholefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/tests/test_wholefile.py b/aider/tests/test_wholefile.py index 78aabec2b..3ebec1571 100644 --- a/aider/tests/test_wholefile.py +++ b/aider/tests/test_wholefile.py @@ -288,7 +288,7 @@ after b files = [file1] # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, "whole", io=InputOutput(pretty=False), fnames=files) + coder = Coder.create(self.GPT35, "whole", io=InputOutput(), fnames=files, stream=False) # no trailing newline so the response content below doesn't add ANOTHER newline new_content = "new\ntwo\nthree" From 6f1e26157e6ec424ee6ce029022c05374f46d350 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 17:09:08 -0700 Subject: [PATCH 030/121] fix test for windows --- aider/coders/base_coder.py | 61 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 028fbdc67..476bda8ee 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -821,39 +821,40 @@ class Coder: exhausted = False interrupted = False - while True: - try: - yield from self.send(messages, functions=self.functions) - break - except KeyboardInterrupt: - interrupted = True - break - except litellm.ContextWindowExceededError: - # The input is overflowing the context window! - exhausted = True - break - except litellm.exceptions.BadRequestError as br_err: - self.io.tool_error(f"BadRequestError: {br_err}") - return - except FinishReasonLength: - # We hit the 4k output limit! - if not self.main_model.can_prefill: + try: + while True: + try: + yield from self.send(messages, functions=self.functions) + break + except KeyboardInterrupt: + interrupted = True + break + except litellm.ContextWindowExceededError: + # The input is overflowing the context window! exhausted = True break + except litellm.exceptions.BadRequestError as br_err: + self.io.tool_error(f"BadRequestError: {br_err}") + return + except FinishReasonLength: + # We hit the 4k output limit! + if not self.main_model.can_prefill: + exhausted = True + break - # Use prefill to continue the response - self.multi_response_content += self.partial_response_content - if messages[-1]["role"] == "assistant": - messages[-1]["content"] = self.multi_response_content - else: - messages.append(dict(role="assistant", content=self.multi_response_content)) - except Exception as err: - self.io.tool_error(f"Unexpected error: {err}") - traceback.print_exc() - return - - if self.mdstream: - self.live_incremental_response(True) + # Use prefill to continue the response + self.multi_response_content += self.partial_response_content + if messages[-1]["role"] == "assistant": + messages[-1]["content"] = self.multi_response_content + else: + messages.append(dict(role="assistant", content=self.multi_response_content)) + except Exception as err: + self.io.tool_error(f"Unexpected error: {err}") + traceback.print_exc() + return + finally: + if self.mdstream: + self.live_incremental_response(True) if self.multi_response_content: self.multi_response_content += self.partial_response_content From a84dca870e518829d6cee6dc8f480f1241acce26 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 18:08:33 -0700 Subject: [PATCH 031/121] fix test for windows --- aider/coders/base_coder.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 476bda8ee..e4e6ceba7 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -856,10 +856,10 @@ class Coder: if self.mdstream: self.live_incremental_response(True) - if self.multi_response_content: - self.multi_response_content += self.partial_response_content - self.partial_response_content = self.multi_response_content - self.multi_response_content = "" + if self.multi_response_content: + self.multi_response_content += self.partial_response_content + self.partial_response_content = self.multi_response_content + self.multi_response_content = "" if exhausted: self.show_exhausted_error() @@ -1201,9 +1201,6 @@ class Coder: def live_incremental_response(self, final): show_resp = self.render_incremental_response(final) - if not show_resp: - return - self.mdstream.update(show_resp, final=final) def render_incremental_response(self, final): From f752faff1172c10403ba9ef17bcbae90b30851ea Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Fri, 28 Jun 2024 18:14:00 -0700 Subject: [PATCH 032/121] fix test for windows --- aider/tests/test_wholefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/tests/test_wholefile.py b/aider/tests/test_wholefile.py index 3ebec1571..9fc47cc89 100644 --- a/aider/tests/test_wholefile.py +++ b/aider/tests/test_wholefile.py @@ -278,7 +278,7 @@ after b updated_content = f.read() self.assertEqual(updated_content, new_content) - def test_full_edit(self): + def __test_full_edit(self): # Create a few temporary files _, file1 = tempfile.mkstemp() From c8dafa2a1ac1949a1403b370dac303d101857848 Mon Sep 17 00:00:00 2001 From: Henry Fraser Date: Sat, 29 Jun 2024 12:35:30 +1000 Subject: [PATCH 033/121] Fix tiny typo --- website/docs/scripting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/scripting.md b/website/docs/scripting.md index 984ecb864..2ab0af8a4 100644 --- a/website/docs/scripting.md +++ b/website/docs/scripting.md @@ -80,7 +80,7 @@ See the [Coder.create() and Coder.__init__() methods](https://github.com/paul-gauthier/aider/blob/main/aider/coders/base_coder.py) for all the supported arguments. -It can also be helpful to set the equivalend of `--yes` by doing this: +It can also be helpful to set the equivalent of `--yes` by doing this: ``` from aider.io import InputOutput From e50c42c40705e06a3004ac5c6675ca8693b1a446 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 08:21:00 -0700 Subject: [PATCH 034/121] restored test_full_edit --- aider/tests/test_wholefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/tests/test_wholefile.py b/aider/tests/test_wholefile.py index 9fc47cc89..3ebec1571 100644 --- a/aider/tests/test_wholefile.py +++ b/aider/tests/test_wholefile.py @@ -278,7 +278,7 @@ after b updated_content = f.read() self.assertEqual(updated_content, new_content) - def __test_full_edit(self): + def test_full_edit(self): # Create a few temporary files _, file1 = tempfile.mkstemp() From 2303cef65ba52c4aa033b154b95701e8301328cb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 08:21:26 -0700 Subject: [PATCH 035/121] bump dep versions --- dev-requirements.in | 2 +- dev-requirements.txt | 24 ++++++++++++------------ requirements.in | 2 +- requirements.txt | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/dev-requirements.in b/dev-requirements.in index 6f67a0f91..48dfe58c0 100644 --- a/dev-requirements.in +++ b/dev-requirements.in @@ -1,5 +1,5 @@ # -# pip-compile --output-file=dev-requirements.txt dev-requirements.in +# pip-compile --output-file=dev-requirements.txt dev-requirements.in --upgrade # pytest pip-tools diff --git a/dev-requirements.txt b/dev-requirements.txt index 19c3acee8..3c679cc6a 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -10,7 +10,7 @@ babel==2.15.0 # via sphinx build==1.2.1 # via pip-tools -certifi==2024.2.2 +certifi==2024.6.2 # via requests cfgv==3.4.0 # via pre-commit @@ -36,9 +36,9 @@ docutils==0.20.1 # via # sphinx # sphinx-rtd-theme -filelock==3.14.0 +filelock==3.15.4 # via virtualenv -fonttools==4.51.0 +fonttools==4.53.0 # via matplotlib identify==2.5.36 # via pre-commit @@ -54,7 +54,7 @@ jinja2==3.1.4 # via sphinx kiwisolver==1.4.5 # via matplotlib -lox==0.11.0 +lox==0.12.0 # via -r dev-requirements.in markdown-it-py==3.0.0 # via rich @@ -66,14 +66,14 @@ mdurl==0.1.2 # via markdown-it-py multiprocess==0.70.16 # via pathos -nodeenv==1.8.0 +nodeenv==1.9.1 # via pre-commit -numpy==1.26.4 +numpy==2.0.0 # via # contourpy # matplotlib # pandas -packaging==24.0 +packaging==24.1 # via # build # matplotlib @@ -107,7 +107,7 @@ pyproject-hooks==1.1.0 # via # build # pip-tools -pytest==8.2.1 +pytest==8.2.2 # via -r dev-requirements.in python-dateutil==2.9.0.post0 # via @@ -117,7 +117,7 @@ pytz==2024.1 # via pandas pyyaml==6.0.1 # via pre-commit -requests==2.32.0 +requests==2.32.3 # via sphinx rich==13.7.1 # via typer @@ -149,13 +149,13 @@ sphinxcontrib-serializinghtml==1.1.10 # via sphinx typer==0.12.3 # via -r dev-requirements.in -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via typer tzdata==2024.1 # via pandas -urllib3==2.2.1 +urllib3==2.2.2 # via requests -virtualenv==20.26.2 +virtualenv==20.26.3 # via pre-commit wheel==0.43.0 # via pip-tools diff --git a/requirements.in b/requirements.in index 6f6569053..72b929e84 100644 --- a/requirements.in +++ b/requirements.in @@ -1,5 +1,5 @@ # -# pip-compile requirements.in +# pip-compile requirements.in --upgrade # configargparse GitPython diff --git a/requirements.txt b/requirements.txt index 94e6574f8..321dd1ae0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,7 +62,7 @@ frozenlist==1.4.1 # via # aiohttp # aiosignal -fsspec==2024.6.0 +fsspec==2024.6.1 # via huggingface-hub gitdb==4.0.11 # via gitpython @@ -70,14 +70,14 @@ gitpython==3.1.43 # via # -r requirements.in # streamlit -google-ai-generativelanguage==0.6.5 +google-ai-generativelanguage==0.6.6 # via google-generativeai google-api-core[grpc]==2.19.1 # via # google-ai-generativelanguage # google-api-python-client # google-generativeai -google-api-python-client==2.134.0 +google-api-python-client==2.135.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.7.0 +google-generativeai==0.7.1 # via -r requirements.in googleapis-common-protos==1.63.2 # via @@ -139,7 +139,7 @@ jsonschema==4.22.0 # altair jsonschema-specifications==2023.12.1 # via jsonschema -litellm==1.40.26 +litellm==1.41.0 # via -r requirements.in markdown-it-py==3.0.0 # via rich @@ -164,7 +164,7 @@ numpy==2.0.0 # pydeck # scipy # streamlit -openai==1.35.3 +openai==1.35.7 # via # -r requirements.in # litellm From 1a455e799ef7f01635438ee1d22d74b6856b0cb6 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 08:36:36 -0700 Subject: [PATCH 036/121] cleanup mdstream --- aider/coders/base_coder.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index e4e6ceba7..64a260524 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -855,6 +855,7 @@ class Coder: finally: if self.mdstream: self.live_incremental_response(True) + self.mdstream = None if self.multi_response_content: self.multi_response_content += self.partial_response_content From 2428c60456d1292d053e0b926a9395f320e4bfa0 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 08:40:18 -0700 Subject: [PATCH 037/121] added debug output --- aider/mdstream.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aider/mdstream.py b/aider/mdstream.py index 947777651..87c25f860 100755 --- a/aider/mdstream.py +++ b/aider/mdstream.py @@ -95,7 +95,12 @@ class MarkdownStream: show = lines[num_printed:num_lines] show = "".join(show) show = Text.from_ansi(show) - self.live.console.print(show) + try: + self.live.console.print(show) + except UnicodeEncodeError as err: + print(err) + print(show) + print(repr(show)) self.printed = lines[:num_lines] From 677ab78aa2135f92f48a00df7d0a50095983719d Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 09:01:55 -0700 Subject: [PATCH 038/121] better debug --- aider/mdstream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/mdstream.py b/aider/mdstream.py index 87c25f860..d89b4e012 100755 --- a/aider/mdstream.py +++ b/aider/mdstream.py @@ -99,8 +99,8 @@ class MarkdownStream: self.live.console.print(show) except UnicodeEncodeError as err: print(err) - print(show) print(repr(show)) + raise err self.printed = lines[:num_lines] From 27a600632894105c8faa9b4aea482c30a13fc707 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 09:08:59 -0700 Subject: [PATCH 039/121] better debug --- aider/mdstream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aider/mdstream.py b/aider/mdstream.py index d89b4e012..9d71a76f0 100755 --- a/aider/mdstream.py +++ b/aider/mdstream.py @@ -98,8 +98,8 @@ class MarkdownStream: try: self.live.console.print(show) except UnicodeEncodeError as err: - print(err) - print(repr(show)) + print("unicode error", err) + print("show string", repr(show.encode("utf-8"))) raise err self.printed = lines[:num_lines] From a888f0dcf2717d2f927111469da95c28e4e0f359 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 09:13:22 -0700 Subject: [PATCH 040/121] fix test for windows --- aider/mdstream.py | 7 +------ aider/tests/test_wholefile.py | 5 ++++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/aider/mdstream.py b/aider/mdstream.py index 9d71a76f0..947777651 100755 --- a/aider/mdstream.py +++ b/aider/mdstream.py @@ -95,12 +95,7 @@ class MarkdownStream: show = lines[num_printed:num_lines] show = "".join(show) show = Text.from_ansi(show) - try: - self.live.console.print(show) - except UnicodeEncodeError as err: - print("unicode error", err) - print("show string", repr(show.encode("utf-8"))) - raise err + self.live.console.print(show) self.printed = lines[:num_lines] diff --git a/aider/tests/test_wholefile.py b/aider/tests/test_wholefile.py index 3ebec1571..10e996dec 100644 --- a/aider/tests/test_wholefile.py +++ b/aider/tests/test_wholefile.py @@ -288,7 +288,9 @@ after b files = [file1] # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, "whole", io=InputOutput(), fnames=files, stream=False) + coder = Coder.create( + self.GPT35, "whole", io=InputOutput(), fnames=files, stream=False, pretty=False + ) # no trailing newline so the response content below doesn't add ANOTHER newline new_content = "new\ntwo\nthree" @@ -307,6 +309,7 @@ Do this: return [] coder.send = MagicMock(side_effect=mock_send) + coder.live_incremental_response = lambda x: x # Call the run method with a message coder.run(with_message="hi") From fd62a29b1ab5d7ed91a2483f319b3b8f7b7750f2 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 09:22:05 -0700 Subject: [PATCH 041/121] fix test for windows --- aider/tests/test_coder.py | 14 ++++++++------ aider/tests/test_editblock.py | 3 ++- aider/tests/test_wholefile.py | 1 - 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/aider/tests/test_coder.py b/aider/tests/test_coder.py index ca3014ff9..cb021cd17 100644 --- a/aider/tests/test_coder.py +++ b/aider/tests/test_coder.py @@ -219,7 +219,7 @@ class TestCoder(unittest.TestCase): files = [file1, file2] # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, None, io=InputOutput(), fnames=files) + coder = Coder.create(self.GPT35, None, io=InputOutput(), fnames=files, pretty=False) def mock_send(*args, **kwargs): coder.partial_response_content = "ok" @@ -246,7 +246,7 @@ class TestCoder(unittest.TestCase): files = [file1, file2] # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, None, io=InputOutput(), fnames=files) + coder = Coder.create(self.GPT35, None, io=InputOutput(), fnames=files, pretty=False) def mock_send(*args, **kwargs): coder.partial_response_content = "ok" @@ -337,7 +337,7 @@ class TestCoder(unittest.TestCase): fname = Path("file.txt") io = InputOutput(yes=True) - coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname)]) + coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname)], pretty=False) self.assertTrue(fname.exists()) @@ -394,7 +394,9 @@ new fname1.write_text("ONE\n") io = InputOutput(yes=True) - coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname1), str(fname2)]) + coder = Coder.create( + self.GPT35, "diff", io=io, fnames=[str(fname1), str(fname2)], pretty=False + ) def mock_send(*args, **kwargs): coder.partial_response_content = f""" @@ -447,7 +449,7 @@ TWO fname2.write_text("OTHER\n") io = InputOutput(yes=True) - coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname)]) + coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname)], pretty=False) def mock_send(*args, **kwargs): coder.partial_response_content = f""" @@ -525,7 +527,7 @@ three repo.git.commit("-m", "initial") io = InputOutput(yes=True) - coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname)]) + coder = Coder.create(self.GPT35, "diff", io=io, fnames=[str(fname)], pretty=False) def mock_send(*args, **kwargs): coder.partial_response_content = f""" diff --git a/aider/tests/test_editblock.py b/aider/tests/test_editblock.py index 40a0d4572..92083c0b1 100644 --- a/aider/tests/test_editblock.py +++ b/aider/tests/test_editblock.py @@ -297,7 +297,7 @@ These changes replace the `subprocess.run` patches with `subprocess.check_output files = [file1] # Initialize the Coder object with the mocked IO and mocked repo - coder = Coder.create(self.GPT35, "diff", io=InputOutput(), fnames=files) + coder = Coder.create(self.GPT35, "diff", io=InputOutput(), fnames=files, pretty=False) def mock_send(*args, **kwargs): coder.partial_response_content = f""" @@ -340,6 +340,7 @@ new io=InputOutput(dry_run=True), fnames=files, dry_run=True, + pretty=False, ) def mock_send(*args, **kwargs): diff --git a/aider/tests/test_wholefile.py b/aider/tests/test_wholefile.py index 10e996dec..56742c71d 100644 --- a/aider/tests/test_wholefile.py +++ b/aider/tests/test_wholefile.py @@ -309,7 +309,6 @@ Do this: return [] coder.send = MagicMock(side_effect=mock_send) - coder.live_incremental_response = lambda x: x # Call the run method with a message coder.run(with_message="hi") From fe4d75b1237d0e6e28277b516cff6f3e9a3ae237 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 09:39:30 -0700 Subject: [PATCH 042/121] Updated HISTORY --- HISTORY.md | 127 ++++++++++++++++++++++++--------------------- website/HISTORY.md | 127 ++++++++++++++++++++++++--------------------- 2 files changed, 134 insertions(+), 120 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a1f7df050..e7c09f80d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,21 +1,28 @@ # Release history -### v0.40.6 +### Aider v0.41.0 -- Fixed `/undo` so it works with `--no-attribute-author`. +- Allow Claude 3.5 Sonnet to stream back >4k tokens! + - It is the first model capable of writing such large coherent, useful code edits. +- Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". +- Bumped dependency versions. -### v0.40.5 +### Aider v0.40.6 + +- Fixed `/undo` so it works regardless of `--attribute` settings. + +### Aider v0.40.5 - Bump versions to pickup latest litellm to fix streaming issue with Gemini - https://github.com/BerriAI/litellm/issues/4408 -### v0.40.1 +### Aider v0.40.1 - Improved context awareness of repomap. - Restored proper `--help` functionality. -### v0.40.0 +### Aider v0.40.0 - Improved prompting to discourage Sonnet from wasting tokens emitting unchanging code (#705). - Improved error info for token limit errors. @@ -24,14 +31,14 @@ - Improved invocation of flake8 linter for python code. -### v0.39.0 +### Aider 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 +### Aider v0.38.0 - Use `--vim` for [vim keybindings](https://aider.chat/docs/commands.html#vi) in the chat. - [Add LLM metadata](https://aider.chat/docs/llms/warnings.html#specifying-context-window-size-and-token-costs) via `.aider.models.json` file (by @caseymcc). @@ -42,7 +49,7 @@ - Documentation updates, moved into website/ subdir. - Moved tests/ into aider/tests/. -### v0.37.0 +### Aider v0.37.0 - Repo map is now optimized based on text of chat history as well as files added to chat. - Improved prompts when no files have been added to chat to solicit LLM file suggestions. @@ -53,7 +60,7 @@ - Detect supported audio sample rates for `/voice`. - Other small bug fixes. -### v0.36.0 +### Aider v0.36.0 - [Aider can now lint your code and fix any errors](https://aider.chat/2024/05/22/linting.html). - Aider automatically lints and fixes after every LLM edit. @@ -66,7 +73,7 @@ - Aider will automatically attempt to fix any test failures. -### v0.35.0 +### Aider v0.35.0 - Aider now uses GPT-4o by default. - GPT-4o tops the [aider LLM code editing leaderboard](https://aider.chat/docs/leaderboards/) at 72.9%, versus 68.4% for Opus. @@ -75,7 +82,7 @@ - Improved reflection feedback to LLMs using the diff edit format. - Improved retries on `httpx` errors. -### v0.34.0 +### Aider v0.34.0 - Updated prompting to use more natural phrasing about files, the git repo, etc. Removed reliance on read-write/read-only terminology. - Refactored prompting to unify some phrasing across edit formats. @@ -85,11 +92,11 @@ - Bugfix: catch and retry on all litellm exceptions. -### v0.33.0 +### Aider v0.33.0 - Added native support for [Deepseek models](https://aider.chat/docs/llms.html#deepseek) using `DEEPSEEK_API_KEY` and `deepseek/deepseek-chat`, etc rather than as a generic OpenAI compatible API. -### v0.32.0 +### Aider v0.32.0 - [Aider LLM code editing leaderboards](https://aider.chat/docs/leaderboards/) that rank popular models according to their ability to edit code. - Leaderboards include GPT-3.5/4 Turbo, Opus, Sonnet, Gemini 1.5 Pro, Llama 3, Deepseek Coder & Command-R+. @@ -98,31 +105,31 @@ - Improved retry handling on errors from model APIs. - Benchmark outputs results in YAML, compatible with leaderboard. -### v0.31.0 +### Aider v0.31.0 - [Aider is now also AI pair programming in your browser!](https://aider.chat/2024/05/02/browser.html) Use the `--browser` switch to launch an experimental browser based version of aider. - Switch models during the chat with `/model ` and search the list of available models with `/models `. -### v0.30.1 +### Aider v0.30.1 - Adding missing `google-generativeai` dependency -### v0.30.0 +### Aider v0.30.0 - Added [Gemini 1.5 Pro](https://aider.chat/docs/llms.html#free-models) as a recommended free model. - Allow repo map for "whole" edit format. - Added `--models ` to search the available models. - Added `--no-show-model-warnings` to silence model warnings. -### v0.29.2 +### Aider v0.29.2 - Improved [model warnings](https://aider.chat/docs/llms.html#model-warnings) for unknown or unfamiliar models -### v0.29.1 +### Aider v0.29.1 - Added better support for groq/llama3-70b-8192 -### v0.29.0 +### Aider v0.29.0 - Added support for [directly connecting to Anthropic, Cohere, Gemini and many other LLM providers](https://aider.chat/docs/llms.html). - Added `--weak-model ` which allows you to specify which model to use for commit messages and chat history summarization. @@ -136,32 +143,32 @@ - Fixed crash when operating in a repo in a detached HEAD state. - Fix: Use the same default model in CLI and python scripting. -### v0.28.0 +### Aider v0.28.0 - Added support for new `gpt-4-turbo-2024-04-09` and `gpt-4-turbo` models. - Benchmarked at 61.7% on Exercism benchmark, comparable to `gpt-4-0613` and worse than the `gpt-4-preview-XXXX` models. See [recent Exercism benchmark results](https://aider.chat/2024/03/08/claude-3.html). - Benchmarked at 34.1% on the refactoring/laziness benchmark, significantly worse than the `gpt-4-preview-XXXX` models. See [recent refactor bencmark results](https://aider.chat/2024/01/25/benchmarks-0125.html). - Aider continues to default to `gpt-4-1106-preview` as it performs best on both benchmarks, and significantly better on the refactoring/laziness benchmark. -### v0.27.0 +### Aider v0.27.0 - Improved repomap support for typescript, by @ryanfreckleton. - Bugfix: Only /undo the files which were part of the last commit, don't stomp other dirty files - Bugfix: Show clear error message when OpenAI API key is not set. - Bugfix: Catch error for obscure languages without tags.scm file. -### v0.26.1 +### Aider v0.26.1 - Fixed bug affecting parsing of git config in some environments. -### v0.26.0 +### Aider v0.26.0 - Use GPT-4 Turbo by default. - Added `-3` and `-4` switches to use GPT 3.5 or GPT-4 (non-Turbo). - Bug fix to avoid reflecting local git errors back to GPT. - Improved logic for opening git repo on launch. -### v0.25.0 +### Aider v0.25.0 - Issue a warning if user adds too much code to the chat. - https://aider.chat/docs/faq.html#how-can-i-add-all-the-files-to-the-chat @@ -171,18 +178,18 @@ - Show the user a FAQ link if edits fail to apply. - Made past articles part of https://aider.chat/blog/ -### v0.24.1 +### Aider v0.24.1 - Fixed bug with cost computations when --no-steam in effect -### v0.24.0 +### Aider v0.24.0 - New `/web ` command which scrapes the url, turns it into fairly clean markdown and adds it to the chat. - Updated all OpenAI model names, pricing info - Default GPT 3.5 model is now `gpt-3.5-turbo-0125`. - Bugfix to the `!` alias for `/run`. -### v0.23.0 +### Aider v0.23.0 - Added support for `--model gpt-4-0125-preview` and OpenAI's alias `--model gpt-4-turbo-preview`. The `--4turbo` switch remains an alias for `--model gpt-4-1106-preview` at this time. - New `/test` command that runs a command and adds the output to the chat on non-zero exit status. @@ -192,25 +199,25 @@ - Added `--openrouter` as a shortcut for `--openai-api-base https://openrouter.ai/api/v1` - Fixed bug preventing use of env vars `OPENAI_API_BASE, OPENAI_API_TYPE, OPENAI_API_VERSION, OPENAI_API_DEPLOYMENT_ID`. -### v0.22.0 +### Aider v0.22.0 - Improvements for unified diff editing format. - Added ! as an alias for /run. - Autocomplete for /add and /drop now properly quotes filenames with spaces. - The /undo command asks GPT not to just retry reverted edit. -### v0.21.1 +### Aider v0.21.1 - Bugfix for unified diff editing format. - Added --4turbo and --4 aliases for --4-turbo. -### v0.21.0 +### Aider v0.21.0 - Support for python 3.12. - Improvements to unified diff editing format. - New `--check-update` arg to check if updates are available and exit with status code. -### v0.20.0 +### Aider v0.20.0 - Add images to the chat to automatically use GPT-4 Vision, by @joshuavial @@ -218,22 +225,22 @@ - Improved unicode encoding for `/run` command output, by @ctoth - Prevent false auto-commits on Windows, by @ctoth -### v0.19.1 +### Aider v0.19.1 - Removed stray debug output. -### v0.19.0 +### Aider v0.19.0 - [Significantly reduced "lazy" coding from GPT-4 Turbo due to new unified diff edit format](https://aider.chat/docs/unified-diffs.html) - Score improves from 20% to 61% on new "laziness benchmark". - Aider now uses unified diffs by default for `gpt-4-1106-preview`. - New `--4-turbo` command line switch as a shortcut for `--model gpt-4-1106-preview`. -### v0.18.1 +### Aider v0.18.1 - Upgraded to new openai python client v1.3.7. -### v0.18.0 +### Aider v0.18.0 - Improved prompting for both GPT-4 and GPT-4 Turbo. - Far fewer edit errors from GPT-4 Turbo (`gpt-4-1106-preview`). @@ -241,7 +248,7 @@ - Fixed bug where in-chat files were marked as both read-only and ready-write, sometimes confusing GPT. - Fixed bug to properly handle repos with submodules. -### v0.17.0 +### Aider v0.17.0 - Support for OpenAI's new 11/06 models: - gpt-4-1106-preview with 128k context window @@ -253,19 +260,19 @@ - Fixed crash bug when `/add` used on file matching `.gitignore` - Fixed misc bugs to catch and handle unicode decoding errors. -### v0.16.3 +### Aider v0.16.3 - Fixed repo-map support for C#. -### v0.16.2 +### Aider v0.16.2 - Fixed docker image. -### v0.16.1 +### Aider v0.16.1 - Updated tree-sitter dependencies to streamline the pip install process -### v0.16.0 +### Aider v0.16.0 - [Improved repository map using tree-sitter](https://aider.chat/docs/repomap.html) - Switched from "edit block" to "search/replace block", which reduced malformed edit blocks. [Benchmarked](https://aider.chat/docs/benchmarks.html) at 66.2%, no regression. @@ -273,7 +280,7 @@ - Bugfix to properly handle malformed `/add` wildcards. -### v0.15.0 +### Aider v0.15.0 - Added support for `.aiderignore` file, which instructs aider to ignore parts of the git repo. - New `--commit` cmd line arg, which just commits all pending changes with a sensible commit message generated by gpt-3.5. @@ -281,13 +288,13 @@ - `/run` and `/git` now accept full shell commands, like: `/run (cd subdir; ls)` - Restored missing `--encoding` cmd line switch. -### v0.14.2 +### Aider v0.14.2 - Easily [run aider from a docker image](https://aider.chat/docs/docker.html) - Fixed bug with chat history summarization. - Fixed bug if `soundfile` package not available. -### v0.14.1 +### Aider v0.14.1 - /add and /drop handle absolute filenames and quoted filenames - /add checks to be sure files are within the git repo (or root) @@ -295,14 +302,14 @@ - Fixed /add bug in when aider launched in repo subdir - Show models supported by api/key if requested model isn't available -### v0.14.0 +### Aider v0.14.0 - [Support for Claude2 and other LLMs via OpenRouter](https://aider.chat/docs/faq.html#accessing-other-llms-with-openrouter) by @joshuavial - Documentation for [running the aider benchmarking suite](https://github.com/paul-gauthier/aider/tree/main/benchmark) - Aider now requires Python >= 3.9 -### v0.13.0 +### Aider v0.13.0 - [Only git commit dirty files that GPT tries to edit](https://aider.chat/docs/faq.html#how-did-v0130-change-git-usage) - Send chat history as prompt/context for Whisper voice transcription @@ -310,14 +317,14 @@ - Late-bind importing `sounddevice`, as it was slowing down aider startup - Improved --foo/--no-foo switch handling for command line and yml config settings -### v0.12.0 +### Aider v0.12.0 - [Voice-to-code](https://aider.chat/docs/voice.html) support, which allows you to code with your voice. - Fixed bug where /diff was causing crash. - Improved prompting for gpt-4, refactor of editblock coder. - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 63.2% for gpt-4/diff, no regression. -### v0.11.1 +### Aider v0.11.1 - Added a progress bar when initially creating a repo map. - Fixed bad commit message when adding new file to empty repo. @@ -326,7 +333,7 @@ - Fixed /commit bug from repo refactor, added test coverage. - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 53.4% for gpt-3.5/whole (no regression). -### v0.11.0 +### Aider v0.11.0 - Automatically summarize chat history to avoid exhausting context window. - More detail on dollar costs when running with `--no-stream` @@ -334,12 +341,12 @@ - Defend against GPT-3.5 or non-OpenAI models suggesting filenames surrounded by asterisks. - Refactored GitRepo code out of the Coder class. -### v0.10.1 +### Aider v0.10.1 - /add and /drop always use paths relative to the git root - Encourage GPT to use language like "add files to the chat" to ask users for permission to edit them. -### v0.10.0 +### Aider v0.10.0 - Added `/git` command to run git from inside aider chats. - Use Meta-ENTER (Esc+ENTER in some environments) to enter multiline chat messages. @@ -351,7 +358,7 @@ - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 64.7% for gpt-4/diff (no regression) -### v0.9.0 +### Aider v0.9.0 - Support for the OpenAI models in [Azure](https://aider.chat/docs/faq.html#azure) - Added `--show-repo-map` @@ -360,7 +367,7 @@ - Bugfix: recognize and add files in subdirectories mentioned by user or GPT - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 53.8% for gpt-3.5-turbo/whole (no regression) -### v0.8.3 +### Aider v0.8.3 - Added `--dark-mode` and `--light-mode` to select colors optimized for terminal background - Install docs link to [NeoVim plugin](https://github.com/joshuavial/aider.nvim) by @joshuavial @@ -371,11 +378,11 @@ - Bugfix/improvement to /add and /drop to recurse selected directories - Bugfix for live diff output when using "whole" edit format -### v0.8.2 +### Aider v0.8.2 - Disabled general availability of gpt-4 (it's rolling out, not 100% available yet) -### v0.8.1 +### Aider v0.8.1 - Ask to create a git repo if none found, to better track GPT's code changes - Glob wildcards are now supported in `/add` and `/drop` commands @@ -387,7 +394,7 @@ - Bugfix for chats with multiple files - Bugfix in editblock coder prompt -### v0.8.0 +### Aider v0.8.0 - [Benchmark comparing code editing in GPT-3.5 and GPT-4](https://aider.chat/docs/benchmarks.html) - Improved Windows support: @@ -400,15 +407,15 @@ - Added `--code-theme` switch to control the pygments styling of code blocks (by @kwmiebach) - Better status messages explaining the reason when ctags is disabled -### v0.7.2: +### Aider v0.7.2: - Fixed a bug to allow aider to edit files that contain triple backtick fences. -### v0.7.1: +### Aider v0.7.1: - Fixed a bug in the display of streaming diffs in GPT-3.5 chats -### v0.7.0: +### Aider v0.7.0: - Graceful handling of context window exhaustion, including helpful tips. - Added `--message` to give GPT that one instruction and then exit after it replies and any edits are performed. @@ -422,13 +429,13 @@ - Initial experiments show that using functions makes 3.5 less competent at coding. - Limit automatic retries when GPT returns a malformed edit response. -### v0.6.2 +### Aider v0.6.2 * Support for `gpt-3.5-turbo-16k`, and all OpenAI chat models * Improved ability to correct when gpt-4 omits leading whitespace in code edits * Added `--openai-api-base` to support API proxies, etc. -### v0.5.0 +### Aider v0.5.0 - Added support for `gpt-3.5-turbo` and `gpt-4-32k`. - Added `--map-tokens` to set a token budget for the repo map, along with a PageRank based algorithm for prioritizing which files and identifiers to include in the map. diff --git a/website/HISTORY.md b/website/HISTORY.md index f617fc5fd..fa1c8c7fd 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -12,21 +12,28 @@ cog.out(text) # Release history -### v0.40.6 +### Aider v0.41.0 -- Fixed `/undo` so it works with `--no-attribute-author`. +- Allow Claude 3.5 Sonnet to stream back >4k tokens! + - It is the first model capable of writing such large coherent, useful code edits. +- Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". +- Bumped dependency versions. -### v0.40.5 +### Aider v0.40.6 + +- Fixed `/undo` so it works regardless of `--attribute` settings. + +### Aider v0.40.5 - Bump versions to pickup latest litellm to fix streaming issue with Gemini - https://github.com/BerriAI/litellm/issues/4408 -### v0.40.1 +### Aider v0.40.1 - Improved context awareness of repomap. - Restored proper `--help` functionality. -### v0.40.0 +### Aider v0.40.0 - Improved prompting to discourage Sonnet from wasting tokens emitting unchanging code (#705). - Improved error info for token limit errors. @@ -35,14 +42,14 @@ cog.out(text) - Improved invocation of flake8 linter for python code. -### v0.39.0 +### Aider 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 +### Aider v0.38.0 - Use `--vim` for [vim keybindings](https://aider.chat/docs/commands.html#vi) in the chat. - [Add LLM metadata](https://aider.chat/docs/llms/warnings.html#specifying-context-window-size-and-token-costs) via `.aider.models.json` file (by @caseymcc). @@ -53,7 +60,7 @@ cog.out(text) - Documentation updates, moved into website/ subdir. - Moved tests/ into aider/tests/. -### v0.37.0 +### Aider v0.37.0 - Repo map is now optimized based on text of chat history as well as files added to chat. - Improved prompts when no files have been added to chat to solicit LLM file suggestions. @@ -64,7 +71,7 @@ cog.out(text) - Detect supported audio sample rates for `/voice`. - Other small bug fixes. -### v0.36.0 +### Aider v0.36.0 - [Aider can now lint your code and fix any errors](https://aider.chat/2024/05/22/linting.html). - Aider automatically lints and fixes after every LLM edit. @@ -77,7 +84,7 @@ cog.out(text) - Aider will automatically attempt to fix any test failures. -### v0.35.0 +### Aider v0.35.0 - Aider now uses GPT-4o by default. - GPT-4o tops the [aider LLM code editing leaderboard](https://aider.chat/docs/leaderboards/) at 72.9%, versus 68.4% for Opus. @@ -86,7 +93,7 @@ cog.out(text) - Improved reflection feedback to LLMs using the diff edit format. - Improved retries on `httpx` errors. -### v0.34.0 +### Aider v0.34.0 - Updated prompting to use more natural phrasing about files, the git repo, etc. Removed reliance on read-write/read-only terminology. - Refactored prompting to unify some phrasing across edit formats. @@ -96,11 +103,11 @@ cog.out(text) - Bugfix: catch and retry on all litellm exceptions. -### v0.33.0 +### Aider v0.33.0 - Added native support for [Deepseek models](https://aider.chat/docs/llms.html#deepseek) using `DEEPSEEK_API_KEY` and `deepseek/deepseek-chat`, etc rather than as a generic OpenAI compatible API. -### v0.32.0 +### Aider v0.32.0 - [Aider LLM code editing leaderboards](https://aider.chat/docs/leaderboards/) that rank popular models according to their ability to edit code. - Leaderboards include GPT-3.5/4 Turbo, Opus, Sonnet, Gemini 1.5 Pro, Llama 3, Deepseek Coder & Command-R+. @@ -109,31 +116,31 @@ cog.out(text) - Improved retry handling on errors from model APIs. - Benchmark outputs results in YAML, compatible with leaderboard. -### v0.31.0 +### Aider v0.31.0 - [Aider is now also AI pair programming in your browser!](https://aider.chat/2024/05/02/browser.html) Use the `--browser` switch to launch an experimental browser based version of aider. - Switch models during the chat with `/model ` and search the list of available models with `/models `. -### v0.30.1 +### Aider v0.30.1 - Adding missing `google-generativeai` dependency -### v0.30.0 +### Aider v0.30.0 - Added [Gemini 1.5 Pro](https://aider.chat/docs/llms.html#free-models) as a recommended free model. - Allow repo map for "whole" edit format. - Added `--models ` to search the available models. - Added `--no-show-model-warnings` to silence model warnings. -### v0.29.2 +### Aider v0.29.2 - Improved [model warnings](https://aider.chat/docs/llms.html#model-warnings) for unknown or unfamiliar models -### v0.29.1 +### Aider v0.29.1 - Added better support for groq/llama3-70b-8192 -### v0.29.0 +### Aider v0.29.0 - Added support for [directly connecting to Anthropic, Cohere, Gemini and many other LLM providers](https://aider.chat/docs/llms.html). - Added `--weak-model ` which allows you to specify which model to use for commit messages and chat history summarization. @@ -147,32 +154,32 @@ cog.out(text) - Fixed crash when operating in a repo in a detached HEAD state. - Fix: Use the same default model in CLI and python scripting. -### v0.28.0 +### Aider v0.28.0 - Added support for new `gpt-4-turbo-2024-04-09` and `gpt-4-turbo` models. - Benchmarked at 61.7% on Exercism benchmark, comparable to `gpt-4-0613` and worse than the `gpt-4-preview-XXXX` models. See [recent Exercism benchmark results](https://aider.chat/2024/03/08/claude-3.html). - Benchmarked at 34.1% on the refactoring/laziness benchmark, significantly worse than the `gpt-4-preview-XXXX` models. See [recent refactor bencmark results](https://aider.chat/2024/01/25/benchmarks-0125.html). - Aider continues to default to `gpt-4-1106-preview` as it performs best on both benchmarks, and significantly better on the refactoring/laziness benchmark. -### v0.27.0 +### Aider v0.27.0 - Improved repomap support for typescript, by @ryanfreckleton. - Bugfix: Only /undo the files which were part of the last commit, don't stomp other dirty files - Bugfix: Show clear error message when OpenAI API key is not set. - Bugfix: Catch error for obscure languages without tags.scm file. -### v0.26.1 +### Aider v0.26.1 - Fixed bug affecting parsing of git config in some environments. -### v0.26.0 +### Aider v0.26.0 - Use GPT-4 Turbo by default. - Added `-3` and `-4` switches to use GPT 3.5 or GPT-4 (non-Turbo). - Bug fix to avoid reflecting local git errors back to GPT. - Improved logic for opening git repo on launch. -### v0.25.0 +### Aider v0.25.0 - Issue a warning if user adds too much code to the chat. - https://aider.chat/docs/faq.html#how-can-i-add-all-the-files-to-the-chat @@ -182,18 +189,18 @@ cog.out(text) - Show the user a FAQ link if edits fail to apply. - Made past articles part of https://aider.chat/blog/ -### v0.24.1 +### Aider v0.24.1 - Fixed bug with cost computations when --no-steam in effect -### v0.24.0 +### Aider v0.24.0 - New `/web ` command which scrapes the url, turns it into fairly clean markdown and adds it to the chat. - Updated all OpenAI model names, pricing info - Default GPT 3.5 model is now `gpt-3.5-turbo-0125`. - Bugfix to the `!` alias for `/run`. -### v0.23.0 +### Aider v0.23.0 - Added support for `--model gpt-4-0125-preview` and OpenAI's alias `--model gpt-4-turbo-preview`. The `--4turbo` switch remains an alias for `--model gpt-4-1106-preview` at this time. - New `/test` command that runs a command and adds the output to the chat on non-zero exit status. @@ -203,25 +210,25 @@ cog.out(text) - Added `--openrouter` as a shortcut for `--openai-api-base https://openrouter.ai/api/v1` - Fixed bug preventing use of env vars `OPENAI_API_BASE, OPENAI_API_TYPE, OPENAI_API_VERSION, OPENAI_API_DEPLOYMENT_ID`. -### v0.22.0 +### Aider v0.22.0 - Improvements for unified diff editing format. - Added ! as an alias for /run. - Autocomplete for /add and /drop now properly quotes filenames with spaces. - The /undo command asks GPT not to just retry reverted edit. -### v0.21.1 +### Aider v0.21.1 - Bugfix for unified diff editing format. - Added --4turbo and --4 aliases for --4-turbo. -### v0.21.0 +### Aider v0.21.0 - Support for python 3.12. - Improvements to unified diff editing format. - New `--check-update` arg to check if updates are available and exit with status code. -### v0.20.0 +### Aider v0.20.0 - Add images to the chat to automatically use GPT-4 Vision, by @joshuavial @@ -229,22 +236,22 @@ cog.out(text) - Improved unicode encoding for `/run` command output, by @ctoth - Prevent false auto-commits on Windows, by @ctoth -### v0.19.1 +### Aider v0.19.1 - Removed stray debug output. -### v0.19.0 +### Aider v0.19.0 - [Significantly reduced "lazy" coding from GPT-4 Turbo due to new unified diff edit format](https://aider.chat/docs/unified-diffs.html) - Score improves from 20% to 61% on new "laziness benchmark". - Aider now uses unified diffs by default for `gpt-4-1106-preview`. - New `--4-turbo` command line switch as a shortcut for `--model gpt-4-1106-preview`. -### v0.18.1 +### Aider v0.18.1 - Upgraded to new openai python client v1.3.7. -### v0.18.0 +### Aider v0.18.0 - Improved prompting for both GPT-4 and GPT-4 Turbo. - Far fewer edit errors from GPT-4 Turbo (`gpt-4-1106-preview`). @@ -252,7 +259,7 @@ cog.out(text) - Fixed bug where in-chat files were marked as both read-only and ready-write, sometimes confusing GPT. - Fixed bug to properly handle repos with submodules. -### v0.17.0 +### Aider v0.17.0 - Support for OpenAI's new 11/06 models: - gpt-4-1106-preview with 128k context window @@ -264,19 +271,19 @@ cog.out(text) - Fixed crash bug when `/add` used on file matching `.gitignore` - Fixed misc bugs to catch and handle unicode decoding errors. -### v0.16.3 +### Aider v0.16.3 - Fixed repo-map support for C#. -### v0.16.2 +### Aider v0.16.2 - Fixed docker image. -### v0.16.1 +### Aider v0.16.1 - Updated tree-sitter dependencies to streamline the pip install process -### v0.16.0 +### Aider v0.16.0 - [Improved repository map using tree-sitter](https://aider.chat/docs/repomap.html) - Switched from "edit block" to "search/replace block", which reduced malformed edit blocks. [Benchmarked](https://aider.chat/docs/benchmarks.html) at 66.2%, no regression. @@ -284,7 +291,7 @@ cog.out(text) - Bugfix to properly handle malformed `/add` wildcards. -### v0.15.0 +### Aider v0.15.0 - Added support for `.aiderignore` file, which instructs aider to ignore parts of the git repo. - New `--commit` cmd line arg, which just commits all pending changes with a sensible commit message generated by gpt-3.5. @@ -292,13 +299,13 @@ cog.out(text) - `/run` and `/git` now accept full shell commands, like: `/run (cd subdir; ls)` - Restored missing `--encoding` cmd line switch. -### v0.14.2 +### Aider v0.14.2 - Easily [run aider from a docker image](https://aider.chat/docs/docker.html) - Fixed bug with chat history summarization. - Fixed bug if `soundfile` package not available. -### v0.14.1 +### Aider v0.14.1 - /add and /drop handle absolute filenames and quoted filenames - /add checks to be sure files are within the git repo (or root) @@ -306,14 +313,14 @@ cog.out(text) - Fixed /add bug in when aider launched in repo subdir - Show models supported by api/key if requested model isn't available -### v0.14.0 +### Aider v0.14.0 - [Support for Claude2 and other LLMs via OpenRouter](https://aider.chat/docs/faq.html#accessing-other-llms-with-openrouter) by @joshuavial - Documentation for [running the aider benchmarking suite](https://github.com/paul-gauthier/aider/tree/main/benchmark) - Aider now requires Python >= 3.9 -### v0.13.0 +### Aider v0.13.0 - [Only git commit dirty files that GPT tries to edit](https://aider.chat/docs/faq.html#how-did-v0130-change-git-usage) - Send chat history as prompt/context for Whisper voice transcription @@ -321,14 +328,14 @@ cog.out(text) - Late-bind importing `sounddevice`, as it was slowing down aider startup - Improved --foo/--no-foo switch handling for command line and yml config settings -### v0.12.0 +### Aider v0.12.0 - [Voice-to-code](https://aider.chat/docs/voice.html) support, which allows you to code with your voice. - Fixed bug where /diff was causing crash. - Improved prompting for gpt-4, refactor of editblock coder. - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 63.2% for gpt-4/diff, no regression. -### v0.11.1 +### Aider v0.11.1 - Added a progress bar when initially creating a repo map. - Fixed bad commit message when adding new file to empty repo. @@ -337,7 +344,7 @@ cog.out(text) - Fixed /commit bug from repo refactor, added test coverage. - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 53.4% for gpt-3.5/whole (no regression). -### v0.11.0 +### Aider v0.11.0 - Automatically summarize chat history to avoid exhausting context window. - More detail on dollar costs when running with `--no-stream` @@ -345,12 +352,12 @@ cog.out(text) - Defend against GPT-3.5 or non-OpenAI models suggesting filenames surrounded by asterisks. - Refactored GitRepo code out of the Coder class. -### v0.10.1 +### Aider v0.10.1 - /add and /drop always use paths relative to the git root - Encourage GPT to use language like "add files to the chat" to ask users for permission to edit them. -### v0.10.0 +### Aider v0.10.0 - Added `/git` command to run git from inside aider chats. - Use Meta-ENTER (Esc+ENTER in some environments) to enter multiline chat messages. @@ -362,7 +369,7 @@ cog.out(text) - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 64.7% for gpt-4/diff (no regression) -### v0.9.0 +### Aider v0.9.0 - Support for the OpenAI models in [Azure](https://aider.chat/docs/faq.html#azure) - Added `--show-repo-map` @@ -371,7 +378,7 @@ cog.out(text) - Bugfix: recognize and add files in subdirectories mentioned by user or GPT - [Benchmarked](https://aider.chat/docs/benchmarks.html) at 53.8% for gpt-3.5-turbo/whole (no regression) -### v0.8.3 +### Aider v0.8.3 - Added `--dark-mode` and `--light-mode` to select colors optimized for terminal background - Install docs link to [NeoVim plugin](https://github.com/joshuavial/aider.nvim) by @joshuavial @@ -382,11 +389,11 @@ cog.out(text) - Bugfix/improvement to /add and /drop to recurse selected directories - Bugfix for live diff output when using "whole" edit format -### v0.8.2 +### Aider v0.8.2 - Disabled general availability of gpt-4 (it's rolling out, not 100% available yet) -### v0.8.1 +### Aider v0.8.1 - Ask to create a git repo if none found, to better track GPT's code changes - Glob wildcards are now supported in `/add` and `/drop` commands @@ -398,7 +405,7 @@ cog.out(text) - Bugfix for chats with multiple files - Bugfix in editblock coder prompt -### v0.8.0 +### Aider v0.8.0 - [Benchmark comparing code editing in GPT-3.5 and GPT-4](https://aider.chat/docs/benchmarks.html) - Improved Windows support: @@ -411,15 +418,15 @@ cog.out(text) - Added `--code-theme` switch to control the pygments styling of code blocks (by @kwmiebach) - Better status messages explaining the reason when ctags is disabled -### v0.7.2: +### Aider v0.7.2: - Fixed a bug to allow aider to edit files that contain triple backtick fences. -### v0.7.1: +### Aider v0.7.1: - Fixed a bug in the display of streaming diffs in GPT-3.5 chats -### v0.7.0: +### Aider v0.7.0: - Graceful handling of context window exhaustion, including helpful tips. - Added `--message` to give GPT that one instruction and then exit after it replies and any edits are performed. @@ -433,13 +440,13 @@ cog.out(text) - Initial experiments show that using functions makes 3.5 less competent at coding. - Limit automatic retries when GPT returns a malformed edit response. -### v0.6.2 +### Aider v0.6.2 * Support for `gpt-3.5-turbo-16k`, and all OpenAI chat models * Improved ability to correct when gpt-4 omits leading whitespace in code edits * Added `--openai-api-base` to support API proxies, etc. -### v0.5.0 +### Aider v0.5.0 - Added support for `gpt-3.5-turbo` and `gpt-4-32k`. - Added `--map-tokens` to set a token budget for the repo map, along with a PageRank based algorithm for prioritizing which files and identifiers to include in the map. From 85ef60f0834e08d1dfea027e9981619d248ad1e6 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sat, 29 Jun 2024 17:01:31 -0700 Subject: [PATCH 043/121] Try and avoid seaming errors when asking Sonnet to continue --- aider/coders/base_coder.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 64a260524..e43fc98b1 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -807,8 +807,6 @@ class Coder: messages = self.format_messages() - self.io.log_llm_history("TO LLM", format_messages(messages)) - if self.verbose: utils.show_messages(messages, functions=self.functions) @@ -842,8 +840,8 @@ class Coder: exhausted = True break - # Use prefill to continue the response - self.multi_response_content += self.partial_response_content + self.multi_response_content = self.get_multi_response_content() + if messages[-1]["role"] == "assistant": messages[-1]["content"] = self.multi_response_content else: @@ -857,10 +855,8 @@ class Coder: self.live_incremental_response(True) self.mdstream = None - if self.multi_response_content: - self.multi_response_content += self.partial_response_content - self.partial_response_content = self.multi_response_content - self.multi_response_content = "" + self.partial_response_content = self.get_multi_response_content(True) + self.multi_response_content = "" if exhausted: self.show_exhausted_error() @@ -880,8 +876,6 @@ class Coder: self.io.tool_output() - self.io.log_llm_history("LLM RESPONSE", format_content("ASSISTANT", content)) - if interrupted: content += "\n^C KeyboardInterrupt" self.cur_messages += [dict(role="assistant", content=content)] @@ -1074,6 +1068,8 @@ class Coder: self.partial_response_content = "" self.partial_response_function_call = dict() + self.io.log_llm_history("TO LLM", format_messages(messages)) + interrupted = False try: hash_object, completion = send_with_retries( @@ -1089,6 +1085,11 @@ class Coder: self.keyboard_interrupt() interrupted = True finally: + self.io.log_llm_history( + "LLM RESPONSE", + format_content("ASSISTANT", self.partial_response_content), + ) + if self.partial_response_content: self.io.ai_output(self.partial_response_content) elif self.partial_response_function_call: @@ -1207,8 +1208,13 @@ class Coder: def render_incremental_response(self, final): return self.get_multi_response_content() - def get_multi_response_content(self): - return self.multi_response_content + self.partial_response_content + def get_multi_response_content(self, final=False): + cur = self.multi_response_content + new = self.partial_response_content + + if new.rstrip() != new and not final: + new = new.rstrip() + return cur + new def get_rel_fname(self, fname): return os.path.relpath(fname, self.root) From 16856bb4caa80b527219a0ae0b24c52881569553 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sun, 30 Jun 2024 13:02:23 -0700 Subject: [PATCH 044/121] Give the commit message model the chat context #748 --- aider/coders/base_coder.py | 4 ++-- aider/repo.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index e43fc98b1..22694cf1e 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -1436,8 +1436,8 @@ class Coder: return context def auto_commit(self, edited): - # context = self.get_context_from_history(self.cur_messages) - res = self.repo.commit(fnames=edited, aider_edits=True) + context = self.get_context_from_history(self.cur_messages) + res = self.repo.commit(fnames=edited, context=context, aider_edits=True) if res: commit_hash, commit_message = res self.last_aider_commit_hash = commit_hash diff --git a/aider/repo.py b/aider/repo.py index 6e3465697..558343865 100644 --- a/aider/repo.py +++ b/aider/repo.py @@ -93,8 +93,8 @@ class GitRepo: commit_message = "(no commit message provided)" full_commit_message = commit_message - if context: - full_commit_message += "\n\n# Aider chat conversation:\n\n" + context + # if context: + # full_commit_message += "\n\n# Aider chat conversation:\n\n" + context cmd = ["-m", full_commit_message, "--no-verify"] if fnames: From 637a6bce7388ba69cfb16c529820d0ed1e6011cd Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Sun, 30 Jun 2024 13:06:51 -0700 Subject: [PATCH 045/121] copy --- website/docs/repomap.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/website/docs/repomap.md b/website/docs/repomap.md index 359243103..0147e1148 100644 --- a/website/docs/repomap.md +++ b/website/docs/repomap.md @@ -86,8 +86,13 @@ files which have dependencies. Aider optimizes the repo map by selecting the most important parts of the codebase which will -fit into the token budget assigned by the user -(via the `--map-tokens` switch, which defaults to 1k tokens). +fit into the active token budget. + +The token budget is +influenced by the `--map-tokens` switch, which defaults to 1k tokens. +Aider adjusts the size of the repo map dynamically based on the state of the chat. It will usually stay within that setting's value. But it does expand the repo map +significantly at times, especially when no files have been added to the chat and aider needs to understand the entire repo as best as possible. + The sample map shown above doesn't contain *every* class, method and function from those files. From 65f50cf6d0f79ef3b5fe4939ee174510c5c27ce0 Mon Sep 17 00:00:00 2001 From: "Amir Elaguizy (aider)" Date: Mon, 1 Jul 2024 07:48:33 -0500 Subject: [PATCH 046/121] Reordered the output of files in chat and repo to improve readability. --- aider/commands.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 5ea285111..c5ffdd82c 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -613,14 +613,14 @@ class Commands: self.io.tool_output("\nNo files in chat or git repo.") return - if chat_files: - self.io.tool_output("Files in chat:\n") - for file in chat_files: + if other_files: + self.io.tool_output("Repo files not in the chat:\n") + for file in other_files: self.io.tool_output(f" {file}") - if other_files: - self.io.tool_output("\nRepo files not in the chat:\n") - for file in other_files: + if chat_files: + self.io.tool_output("\nFiles in chat:\n") + for file in chat_files: self.io.tool_output(f" {file}") def cmd_help(self, args): From 590ec419930fc46f687b61fc8b204204718c1abe Mon Sep 17 00:00:00 2001 From: Mitsuki Ogasahara Date: Mon, 1 Jul 2024 22:05:50 +0900 Subject: [PATCH 047/121] feat: add image support for Claude 3.5 --- aider/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aider/models.py b/aider/models.py index 7013bc709..423f3f80b 100644 --- a/aider/models.py +++ b/aider/models.py @@ -190,6 +190,7 @@ MODEL_SETTINGS = [ use_repo_map=True, examples_as_sys_msg=True, can_prefill=True, + accepts_images=True, ), ModelSettings( "anthropic/claude-3-5-sonnet-20240620", @@ -206,6 +207,7 @@ MODEL_SETTINGS = [ use_repo_map=True, examples_as_sys_msg=True, can_prefill=True, + accepts_images=True, ), # Vertex AI Claude models ModelSettings( @@ -215,6 +217,7 @@ MODEL_SETTINGS = [ use_repo_map=True, examples_as_sys_msg=True, can_prefill=True, + accepts_images=True, ), ModelSettings( "vertex_ai/claude-3-opus@20240229", From 693c4bc57cbaa73c349607344e1c21c318b2db43 Mon Sep 17 00:00:00 2001 From: Mitsuki Ogasahara Date: Mon, 1 Jul 2024 22:20:32 +0900 Subject: [PATCH 048/121] fix: Add image support of Claude 3.5 to doc --- website/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/index.md b/website/index.md index cbf8498df..afd519f7a 100644 --- a/website/index.md +++ b/website/index.md @@ -93,7 +93,7 @@ and can [connect to almost any LLM](https://aider.chat/docs/llms.html). - Edit files in your editor while chatting with aider, and it will always use the latest version. Pair program with AI. -- Add images to the chat (GPT-4o, GPT-4 Turbo, etc). +- Add images to the chat (GPT-4o, GPT-4 Turbo, Claude 3.5 Sonnet, etc). - Add URLs to the chat and aider will read their content. - [Code with your voice](https://aider.chat/docs/voice.html). From 02db0e0ed7dab89ea7692d5314c64fe0714c5dca Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 11:28:31 -0300 Subject: [PATCH 049/121] add visison support to openrouter gpt-4o --- aider/models.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/aider/models.py b/aider/models.py index 423f3f80b..565890501 100644 --- a/aider/models.py +++ b/aider/models.py @@ -296,6 +296,16 @@ MODEL_SETTINGS = [ examples_as_sys_msg=True, reminder_as_sys_msg=True, ), + ModelSettings( + "openrouter/openai/gpt-4o", + "diff", + weak_model_name="openrouter/openai/gpt-3.5-turbo", + use_repo_map=True, + send_undo_reply=True, + accepts_images=True, + lazy=True, + reminder_as_sys_msg=True, + ), ] From 9a2d6e3ca8da3a6cde85e12a256853a328b9b213 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 11:29:06 -0300 Subject: [PATCH 050/121] updated docs --- website/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/index.md b/website/index.md index afd519f7a..cbf8498df 100644 --- a/website/index.md +++ b/website/index.md @@ -93,7 +93,7 @@ and can [connect to almost any LLM](https://aider.chat/docs/llms.html). - Edit files in your editor while chatting with aider, and it will always use the latest version. Pair program with AI. -- Add images to the chat (GPT-4o, GPT-4 Turbo, Claude 3.5 Sonnet, etc). +- Add images to the chat (GPT-4o, GPT-4 Turbo, etc). - Add URLs to the chat and aider will read their content. - [Code with your voice](https://aider.chat/docs/voice.html). From 43e95c1aeca902ec193a3c9685ebd7894e3b79f6 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 11:30:34 -0300 Subject: [PATCH 051/121] remove refs to 5M free deepseek tokens #760 --- website/docs/llms.md | 1 - website/docs/llms/deepseek.md | 1 - 2 files changed, 2 deletions(-) diff --git a/website/docs/llms.md b/website/docs/llms.md index 37cd82f7a..939bbef87 100644 --- a/website/docs/llms.md +++ b/website/docs/llms.md @@ -27,7 +27,6 @@ Aider works best with these models, which are skilled at editing code: 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 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. diff --git a/website/docs/llms/deepseek.md b/website/docs/llms/deepseek.md index 7ff93ca88..df7790694 100644 --- a/website/docs/llms/deepseek.md +++ b/website/docs/llms/deepseek.md @@ -7,7 +7,6 @@ nav_order: 500 Aider can connect to the DeepSeek.com API. The DeepSeek Coder V2 model gets the top score on aider's code editing benchmark. -DeepSeek appears to grant 5M tokens of free API usage to new accounts. ``` pip install aider-chat From d229b27bb0278ee700afec0f0a9a2a7d701f1141 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 14:03:54 -0300 Subject: [PATCH 052/121] Updated HISTORY and docs --- HISTORY.md | 3 +++ README.md | 4 ++-- website/HISTORY.md | 3 +++ website/docs/images-urls.md | 41 +++++++++++++++++++++++++++++++++++++ website/index.md | 4 ++-- 5 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 website/docs/images-urls.md diff --git a/HISTORY.md b/HISTORY.md index e7c09f80d..aecc220df 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,7 +5,10 @@ - Allow Claude 3.5 Sonnet to stream back >4k tokens! - It is the first model capable of writing such large coherent, useful code edits. + - Do large refactors or generate multiple files of new code in one go. +- Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". +- Fixed regression in quality of one-line commit messages. - Bumped dependency versions. ### Aider v0.40.6 diff --git a/README.md b/README.md index 3bdbc20a4..0f40212dd 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,8 @@ and can [connect to almost any LLM](https://aider.chat/docs/llms.html). - Edit files in your editor while chatting with aider, and it will always use the latest version. Pair program with AI. -- Add images to the chat (GPT-4o, GPT-4 Turbo, etc). -- Add URLs to the chat and aider will read their content. +- [Add images to the chat](https://aider.chat/docs/images-urls.html) (GPT-4o, Claude 3.5 Sonnet, etc). +- [Add URLs to the chat](https://aider.chat/docs/images-urls.html) and aider will read their content. - [Code with your voice](https://aider.chat/docs/voice.html). diff --git a/website/HISTORY.md b/website/HISTORY.md index fa1c8c7fd..fbcb8e73f 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -16,7 +16,10 @@ cog.out(text) - Allow Claude 3.5 Sonnet to stream back >4k tokens! - It is the first model capable of writing such large coherent, useful code edits. + - Do large refactors or generate multiple files of new code in one go. +- Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". +- Fixed regression in quality of one-line commit messages. - Bumped dependency versions. ### Aider v0.40.6 diff --git a/website/docs/images-urls.md b/website/docs/images-urls.md new file mode 100644 index 000000000..3fb526e09 --- /dev/null +++ b/website/docs/images-urls.md @@ -0,0 +1,41 @@ +--- +parent: Usage +nav_order: 700 +description: Add images and URLs to the aider coding chat. +--- + +# Images & URLs + +You can add images and URLs to the aider chat. + +## Images + +Aider supports working with image files for many vision-capable models +like GPT-4o and Claude 3.5 Sonnet. +Adding images to a chat can be helpful in many situations: + +- Add screenshots of web pages or UIs that you want aider to build or modify. +- Show aider a mockup of a UI you want to build. +- Screenshot an error message that is otherwise hard to copy & paste as text. +- Etc. + +To add images to the chat: + +- Use `/add ` from within the chat +- Launch aider with image filenames on the command line: `aider --sonnet ` + +## URLs + +Aider can scrape the text from URLs and add it to the chat. +This can be helpful to: + +- Include documentation pages for less popular APIs. +- Include the latest docs for libraries or packages that are newer than the model's training cutoff date. +- Etc. + +To add URLs to the chat: + +- Use `/web ` +- Just paste the URL into the chat and aider will ask if you want to add it. + + diff --git a/website/index.md b/website/index.md index cbf8498df..e70259f77 100644 --- a/website/index.md +++ b/website/index.md @@ -93,8 +93,8 @@ and can [connect to almost any LLM](https://aider.chat/docs/llms.html). - Edit files in your editor while chatting with aider, and it will always use the latest version. Pair program with AI. -- Add images to the chat (GPT-4o, GPT-4 Turbo, etc). -- Add URLs to the chat and aider will read their content. +- [Add images to the chat](https://aider.chat/docs/images-urls.html) (GPT-4o, Claude 3.5 Sonnet, etc). +- [Add URLs to the chat](https://aider.chat/docs/images-urls.html) and aider will read their content. - [Code with your voice](https://aider.chat/docs/voice.html). From 2cd680cba7ccfa31b0dbede5ebe0a104f76e29e7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 14:12:00 -0300 Subject: [PATCH 053/121] Automatically retry on Anthropic overloaded_error --- HISTORY.md | 1 + aider/sendchat.py | 1 + 2 files changed, 2 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index aecc220df..6595d69d3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,6 +9,7 @@ - Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". - Fixed regression in quality of one-line commit messages. +- Automatically retry on Anthropic `overloaded_error`. - Bumped dependency versions. ### Aider v0.40.6 diff --git a/aider/sendchat.py b/aider/sendchat.py index 8f661f598..78e16ae64 100644 --- a/aider/sendchat.py +++ b/aider/sendchat.py @@ -40,6 +40,7 @@ def should_giveup(e): litellm.exceptions.RateLimitError, litellm.exceptions.ServiceUnavailableError, litellm.exceptions.Timeout, + litellm.llms.anthropic.AnthropicError, ), giveup=should_giveup, max_time=60, From c0a89c5cb8def63873a6f9a2e5a7a9c123184a0d Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 14:12:10 -0300 Subject: [PATCH 054/121] copy --- website/HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/HISTORY.md b/website/HISTORY.md index fbcb8e73f..55d3bbe24 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -20,6 +20,7 @@ cog.out(text) - Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". - Fixed regression in quality of one-line commit messages. +- Automatically retry on Anthropic `overloaded_error`. - Bumped dependency versions. ### Aider v0.40.6 From 6dc7a8b4a4a1f9319948c0bcdac8a069e9f6948a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 15:16:06 -0300 Subject: [PATCH 055/121] Use sonnet by default; updated docs --- HISTORY.md | 1 + README.md | 10 +-- aider/args.py | 7 +- aider/main.py | 5 ++ website/HISTORY.md | 1 + website/_includes/get-started.md | 10 +-- website/_posts/2024-07-01-sonnet-not-lazy.md | 93 ++++++++++++++++++++ website/assets/sample.aider.conf.yml | 4 +- website/assets/sample.env | 4 +- website/docs/config/aider_conf.md | 4 +- website/docs/config/dotenv.md | 4 +- website/docs/config/options.md | 3 +- website/docs/llms/anthropic.md | 6 +- website/docs/llms/openai.md | 2 +- website/index.md | 10 +-- 15 files changed, 125 insertions(+), 39 deletions(-) create mode 100644 website/_posts/2024-07-01-sonnet-not-lazy.md diff --git a/HISTORY.md b/HISTORY.md index 6595d69d3..3d43f6cff 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,7 @@ - Allow Claude 3.5 Sonnet to stream back >4k tokens! - It is the first model capable of writing such large coherent, useful code edits. - Do large refactors or generate multiple files of new code in one go. +- Aider now uses `claude-3-5-sonnet-20240620` by default if `ANTHROPIC_API_KEY` is set in the environment. - Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". - Fixed regression in quality of one-line commit messages. diff --git a/README.md b/README.md index 0f40212dd..2d036b08f 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ and works best with GPT-4o, Claude 3.5 Sonnet, Claude 3 Opus and DeepSeek Coder # Because this page is rendered by GitHub as the repo README cog.out(open("website/_includes/get-started.md").read()) ]]]--> + You can get started quickly like this: ``` @@ -43,14 +44,9 @@ $ cd /to/your/git/repo $ export OPENAI_API_KEY=your-key-goes-here $ aider -# Or, work with Anthropic's models +# Work with Claude 3.5 Sonnet on your repo $ export ANTHROPIC_API_KEY=your-key-goes-here - -# Claude 3 Opus -$ aider --opus - -# Claude 3.5 Sonnet -$ aider --sonnet +$ aider ``` diff --git a/aider/args.py b/aider/args.py index 474fb2257..7d4e6a160 100644 --- a/aider/args.py +++ b/aider/args.py @@ -6,7 +6,7 @@ import sys import configargparse -from aider import __version__, models +from aider import __version__ from aider.args_formatter import ( DotEnvFormatter, MarkdownHelpFormatter, @@ -44,12 +44,11 @@ def get_parser(default_config_files, git_root): env_var="ANTHROPIC_API_KEY", help="Specify the Anthropic API key", ) - default_model = models.DEFAULT_MODEL_NAME group.add_argument( "--model", metavar="MODEL", - default=default_model, - help=f"Specify the model to use for the main chat (default: {default_model})", + default=None, + help="Specify the model to use for the main chat", ) opus_model = "claude-3-opus-20240229" group.add_argument( diff --git a/aider/main.py b/aider/main.py index 75a9cde24..a7a6ce67a 100644 --- a/aider/main.py +++ b/aider/main.py @@ -403,6 +403,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F register_models(git_root, args.model_settings_file, io) register_litellm_models(git_root, args.model_metadata_file, io) + if not args.model: + args.model = "gpt-4o" + if os.environ.get("ANTHROPIC_API_KEY"): + args.model = "claude-3-5-sonnet-20240620" + main_model = models.Model(args.model, weak_model=args.weak_model) lint_cmds = parse_lint_cmds(args.lint_cmd, io) diff --git a/website/HISTORY.md b/website/HISTORY.md index 55d3bbe24..7e35864e6 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -17,6 +17,7 @@ cog.out(text) - Allow Claude 3.5 Sonnet to stream back >4k tokens! - It is the first model capable of writing such large coherent, useful code edits. - Do large refactors or generate multiple files of new code in one go. +- Aider now uses `claude-3-5-sonnet-20240620` by default if `ANTHROPIC_API_KEY` is set in the environment. - Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". - Fixed regression in quality of one-line commit messages. diff --git a/website/_includes/get-started.md b/website/_includes/get-started.md index e31d2ca3a..5e756f846 100644 --- a/website/_includes/get-started.md +++ b/website/_includes/get-started.md @@ -1,3 +1,4 @@ + You can get started quickly like this: ``` @@ -10,12 +11,7 @@ $ cd /to/your/git/repo $ export OPENAI_API_KEY=your-key-goes-here $ aider -# Or, work with Anthropic's models +# Work with Claude 3.5 Sonnet on your repo $ export ANTHROPIC_API_KEY=your-key-goes-here - -# Claude 3 Opus -$ aider --opus - -# Claude 3.5 Sonnet -$ aider --sonnet +$ aider ``` diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md new file mode 100644 index 000000000..fc7284111 --- /dev/null +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -0,0 +1,93 @@ +--- +title: Sonnet is the opposite of lazy +excerpt: Claude 3.5 Sonnet represents a step change in AI coding. +#highlight_image: /assets/linting.jpg +draft: true +nav_exclude: true +--- +{% if page.date %} + +{% endif %} + + +# Sonnet is the opposite of lazy + +Claude 3.5 Sonnet represents a step change +in AI coding. +It is so industrious, diligent and hard working that +it has caused multiple problems for aider. +It's been worth the effort to adapt aider to work well +with Sonnet, +because the result is surprisingly powerful. + +Sonnet's amazing work ethic caused a few problems: + +1. Sonnet is capable of outputting a very large amount of correct, +complete code in one response. +So much that it can easily blow through the 4k output token limit +on API responses, which truncates its coding in mid-stream. +2. Similarly, Sonnet can specify large sequences of edits in one go, +like changing a majority of lines while refactoring a large file. +Again, this regularly triggered the 4k output limit +and resulted in a failed edits. +3. Sonnet is not shy about quoting large chunks of an +existing file to perform a SEARCH & REPLACE edit across +a long span of lines. +This can be wasteful and also trigger the 4k output limit. + + +## Good problems + +Problems (1) and (2) are "good problems" +in the sense that Sonnet is +able to write more high quality code than any other model! + +Aider now allows Sonnet to return code in multiple 4k token +responses. +This gets all the upsides of Sonnet's prolific coding skills, +without being constrained by the 4k output token limit. + + +## Wasting tokens + +Problem (3) does cause some real downsides. + +Faced with a few small changes spread far apart in +a source file, +Sonnet would often prefer to do one giant SEARCH/REPLACE +operation of the ~entire file. +This wastes a tremendous amount of tokens, +time and money -- and risks hitting the 4k output limit. +It would be far faster and less expensive to instead +do a few surgical edits. + +Aider now prompts Sonnet to discourage these long-winded +SEARCH/REPLACE operations +and promotes much more concise edits. + + +## Aider with Sonnet + +[The latest release of aider](https://aider.chat/HISTORY.html#aider-v0410) +has specialized support for Claude 3.5 Sonnet: + +- Aider allows Sonnet to produce as much code as it wants, +by automatically and seamlessly spreading the response +out over a sequence of 4k token API responses. +- Aider carefully prompts Sonnet to be concise and +return only changing sections of code. +This reduces Sonnet's tendency to waste time, tokens and money +returning large chunks of unchanging code. +- Aider now uses `claude-3-5-sonnet-20240620` by default if `ANTHROPIC_API_KEY` is set in the environment. + +You can use aider with Sonnet like this: + +``` +pip install aider-chat + +export ANTHROPIC_API_KEY= # Mac/Linux +setx ANTHROPIC_API_KEY # Windows + +aider +``` + diff --git a/website/assets/sample.aider.conf.yml b/website/assets/sample.aider.conf.yml index 66a24dc33..f6b3233ac 100644 --- a/website/assets/sample.aider.conf.yml +++ b/website/assets/sample.aider.conf.yml @@ -19,8 +19,8 @@ ## Specify the Anthropic API key #anthropic-api-key: -## Specify the model to use for the main chat (default: gpt-4o) -#model: gpt-4o +## Specify the model to use for the main chat +#model: ## Use claude-3-opus-20240229 model for the main chat #opus: false diff --git a/website/assets/sample.env b/website/assets/sample.env index 154a722c9..72cc98a3a 100644 --- a/website/assets/sample.env +++ b/website/assets/sample.env @@ -27,8 +27,8 @@ ## Specify the Anthropic API key #ANTHROPIC_API_KEY= -## Specify the model to use for the main chat (default: gpt-4o) -#AIDER_MODEL=gpt-4o +## Specify the model to use for the main chat +#AIDER_MODEL= ## Use claude-3-opus-20240229 model for the main chat #AIDER_OPUS= diff --git a/website/docs/config/aider_conf.md b/website/docs/config/aider_conf.md index 58c452fb8..428ff55d2 100644 --- a/website/docs/config/aider_conf.md +++ b/website/docs/config/aider_conf.md @@ -47,8 +47,8 @@ cog.outl("```") ## Specify the Anthropic API key #anthropic-api-key: -## Specify the model to use for the main chat (default: gpt-4o) -#model: gpt-4o +## Specify the model to use for the main chat +#model: ## Use claude-3-opus-20240229 model for the main chat #opus: false diff --git a/website/docs/config/dotenv.md b/website/docs/config/dotenv.md index 9aae6011b..dfede0e8e 100644 --- a/website/docs/config/dotenv.md +++ b/website/docs/config/dotenv.md @@ -60,8 +60,8 @@ cog.outl("```") ## Specify the Anthropic API key #ANTHROPIC_API_KEY= -## Specify the model to use for the main chat (default: gpt-4o) -#AIDER_MODEL=gpt-4o +## Specify the model to use for the main chat +#AIDER_MODEL= ## Use claude-3-opus-20240229 model for the main chat #AIDER_OPUS= diff --git a/website/docs/config/options.md b/website/docs/config/options.md index 557d0cf53..310999899 100644 --- a/website/docs/config/options.md +++ b/website/docs/config/options.md @@ -72,8 +72,7 @@ Specify the Anthropic API key Environment variable: `ANTHROPIC_API_KEY` ### `--model MODEL` -Specify the model to use for the main chat (default: gpt-4o) -Default: gpt-4o +Specify the model to use for the main chat Environment variable: `AIDER_MODEL` ### `--opus` diff --git a/website/docs/llms/anthropic.md b/website/docs/llms/anthropic.md index 027feef44..22a45fa30 100644 --- a/website/docs/llms/anthropic.md +++ b/website/docs/llms/anthropic.md @@ -19,12 +19,12 @@ pip install aider-chat export ANTHROPIC_API_KEY= # Mac/Linux setx ANTHROPIC_API_KEY # Windows +# Aider uses Claude 3.5 Sonnet by default (or use --sonnet) +aider + # Claude 3 Opus aider --opus -# Claude 3.5 Sonnet -aider --sonnet - # List models available from Anthropic aider --models anthropic/ ``` diff --git a/website/docs/llms/openai.md b/website/docs/llms/openai.md index 40fee02e4..c4cd2d917 100644 --- a/website/docs/llms/openai.md +++ b/website/docs/llms/openai.md @@ -19,7 +19,7 @@ pip install aider-chat export OPENAI_API_KEY= # Mac/Linux setx OPENAI_API_KEY # Windows -# GPT-4o is the best model, used by default +# Aider uses gpt-4o by default (or use --4o) aider # GPT-4 Turbo (1106) diff --git a/website/index.md b/website/index.md index e70259f77..a8abc461a 100644 --- a/website/index.md +++ b/website/index.md @@ -45,6 +45,7 @@ and works best with GPT-4o, Claude 3.5 Sonnet, Claude 3 Opus and DeepSeek Coder # Because this page is rendered by GitHub as the repo README cog.out(open("website/_includes/get-started.md").read()) --> + You can get started quickly like this: ``` @@ -57,14 +58,9 @@ $ cd /to/your/git/repo $ export OPENAI_API_KEY=your-key-goes-here $ aider -# Or, work with Anthropic's models +# Work with Claude 3.5 Sonnet on your repo $ export ANTHROPIC_API_KEY=your-key-goes-here - -# Claude 3 Opus -$ aider --opus - -# Claude 3.5 Sonnet -$ aider --sonnet +$ aider ``` From 7396e3883e15249567bf543995373cea141a7912 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 15:26:19 -0300 Subject: [PATCH 056/121] added photo --- HISTORY.md | 4 ++-- website/HISTORY.md | 4 ++-- website/_posts/2024-07-01-sonnet-not-lazy.md | 2 ++ website/assets/sonnet-not-lazy.jpg | Bin 0 -> 214104 bytes 4 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 website/assets/sonnet-not-lazy.jpg diff --git a/HISTORY.md b/HISTORY.md index 3d43f6cff..09c20ff23 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,11 +3,11 @@ ### Aider v0.41.0 -- Allow Claude 3.5 Sonnet to stream back >4k tokens! +- [Allow Claude 3.5 Sonnet to stream back >4k tokens!](https://aider.chat/2024/07/01/sonnet-not-lazy.html) - It is the first model capable of writing such large coherent, useful code edits. - Do large refactors or generate multiple files of new code in one go. - Aider now uses `claude-3-5-sonnet-20240620` by default if `ANTHROPIC_API_KEY` is set in the environment. -- Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). +- [Enabled image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". - Fixed regression in quality of one-line commit messages. - Automatically retry on Anthropic `overloaded_error`. diff --git a/website/HISTORY.md b/website/HISTORY.md index 7e35864e6..199e7391c 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -14,11 +14,11 @@ cog.out(text) ### Aider v0.41.0 -- Allow Claude 3.5 Sonnet to stream back >4k tokens! +- [Allow Claude 3.5 Sonnet to stream back >4k tokens!](https://aider.chat/2024/07/01/sonnet-not-lazy.html) - It is the first model capable of writing such large coherent, useful code edits. - Do large refactors or generate multiple files of new code in one go. - Aider now uses `claude-3-5-sonnet-20240620` by default if `ANTHROPIC_API_KEY` is set in the environment. -- Enabled [image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). +- [Enabled image support](https://aider.chat/docs/images-urls.html) for 3.5 Sonnet and for GPT-4o & 3.5 Sonnet via OpenRouter (by @yamitzky). - Added `--attribute-commit-message` to prefix aider's commit messages with "aider:". - Fixed regression in quality of one-line commit messages. - Automatically retry on Anthropic `overloaded_error`. diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index fc7284111..8593dcacb 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -12,6 +12,8 @@ nav_exclude: true # Sonnet is the opposite of lazy +[![sonnet is the opposite of lazy](/assets/sonnet-not-lazy.jpg)](https://aider.chat/assets/sonnet-not-lazy.jpg) + Claude 3.5 Sonnet represents a step change in AI coding. It is so industrious, diligent and hard working that diff --git a/website/assets/sonnet-not-lazy.jpg b/website/assets/sonnet-not-lazy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2fbde9f4c98efa5ab4b14e214081ce15bf901f42 GIT binary patch literal 214104 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

DeL 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�yo^ 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 Cu1nVd literal 0 HcmV?d00001 From 7c746fe901d984618c5548fe4c01d98df8afbdd7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 16:16:19 -0300 Subject: [PATCH 057/121] copy --- website/_data/refactor_leaderboard.yml | 30 +++++++++--------- website/_posts/2024-07-01-sonnet-not-lazy.md | 33 +++++++++++++------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/website/_data/refactor_leaderboard.yml b/website/_data/refactor_leaderboard.yml index 11773ac39..8a4aacfda 100644 --- a/website/_data/refactor_leaderboard.yml +++ b/website/_data/refactor_leaderboard.yml @@ -143,25 +143,25 @@ seconds_per_case: 67.8 total_cost: 20.4889 - -- dirname: 2024-06-20-16-39-18--refac-claude-3.5-sonnet-diff +- dirname: 2024-07-01-18-30-33--refac-claude-3.5-sonnet-diff-not-lazy 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 + commit_hash: 7396e38-dirty + pass_rate_1: 64.0 + percent_cases_well_formed: 76.4 + error_outputs: 176 + num_malformed_responses: 39 + num_with_malformed_responses: 21 + user_asks: 11 lazy_comments: 2 - syntax_errors: 0 - indentation_errors: 3 + syntax_errors: 4 + indentation_errors: 0 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 + date: 2024-07-01 + versions: 0.40.7-dev + seconds_per_case: 42.8 + total_cost: 11.5242 + \ No newline at end of file diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 8593dcacb..86fcbb3dc 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -1,26 +1,34 @@ --- title: Sonnet is the opposite of lazy excerpt: Claude 3.5 Sonnet represents a step change in AI coding. -#highlight_image: /assets/linting.jpg -draft: true +highlight_image: /assets/sonnet-not-lazy.jpg nav_exclude: true --- + +[![sonnet is the opposite of lazy](/assets/sonnet-not-lazy.jpg)](https://aider.chat/assets/sonnet-not-lazy.jpg) + {% if page.date %}

{% endif %} - # Sonnet is the opposite of lazy -[![sonnet is the opposite of lazy](/assets/sonnet-not-lazy.jpg)](https://aider.chat/assets/sonnet-not-lazy.jpg) - Claude 3.5 Sonnet represents a step change in AI coding. It is so industrious, diligent and hard working that it has caused multiple problems for aider. + It's been worth the effort to adapt aider to work well with Sonnet, because the result is surprisingly powerful. +Sonnet's score on +[aider's refactoring benchmark](https://aider.chat/docs/leaderboards/#code-refactoring-leaderboard) +jumped from 55.1% up to 64.0% +as a result of the changes discussed below. +This moved Sonnet into second place, ahead of GPT-4o and +behind only Opus. + +## Problems Sonnet's amazing work ethic caused a few problems: @@ -31,7 +39,7 @@ on API responses, which truncates its coding in mid-stream. 2. Similarly, Sonnet can specify large sequences of edits in one go, like changing a majority of lines while refactoring a large file. Again, this regularly triggered the 4k output limit -and resulted in a failed edits. +and resulted in failed edits. 3. Sonnet is not shy about quoting large chunks of an existing file to perform a SEARCH & REPLACE edit across a long span of lines. @@ -57,7 +65,7 @@ Problem (3) does cause some real downsides. Faced with a few small changes spread far apart in a source file, Sonnet would often prefer to do one giant SEARCH/REPLACE -operation of the ~entire file. +operation of almost the entire file. This wastes a tremendous amount of tokens, time and money -- and risks hitting the 4k output limit. It would be far faster and less expensive to instead @@ -76,13 +84,16 @@ has specialized support for Claude 3.5 Sonnet: - Aider allows Sonnet to produce as much code as it wants, by automatically and seamlessly spreading the response out over a sequence of 4k token API responses. -- Aider carefully prompts Sonnet to be concise and -return only changing sections of code. +- Aider carefully prompts Sonnet to be concise when proposing +code edits. This reduces Sonnet's tendency to waste time, tokens and money returning large chunks of unchanging code. -- Aider now uses `claude-3-5-sonnet-20240620` by default if `ANTHROPIC_API_KEY` is set in the environment. +- Aider now uses Claude 3.5 Sonnet by default if the `ANTHROPIC_API_KEY` is set in the environment. -You can use aider with Sonnet like this: +See +[aider's install instructions](https://aider.chat/docs/install.html) +for more details, but +you can get started quickly with aider and Sonnet like this: ``` pip install aider-chat From 24802693c48d1602eba4c7ba0beb2ec88612b14b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 16:18:28 -0300 Subject: [PATCH 058/121] version bump to 0.41.0 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 8920a054a..22ffde200 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.40.7-dev" +__version__ = "0.41.0" From 1a6ea77976cb0d41fd9f6396b47ca505fefecd16 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 16:19:16 -0300 Subject: [PATCH 059/121] set version to 0.41.1-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 22ffde200..aef8c9771 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.41.0" +__version__ = "0.41.1-dev" From e3d6bba6c18e0037e0739b66ee845abdaa8a21ee Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 16:37:16 -0300 Subject: [PATCH 060/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 44 ++++++++++++-------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 86fcbb3dc..52091702f 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -15,8 +15,11 @@ nav_exclude: true Claude 3.5 Sonnet represents a step change in AI coding. -It is so industrious, diligent and hard working that -it has caused multiple problems for aider. +It is incredibly industrious, diligent and hard working. +Unexpectedly, +this initially presented a few challenges +that prevented aider from taking maximum advantage of +Sonnet's capabilities. It's been worth the effort to adapt aider to work well with Sonnet, @@ -28,46 +31,53 @@ as a result of the changes discussed below. This moved Sonnet into second place, ahead of GPT-4o and behind only Opus. -## Problems +## Hitting the 4k token output limit -Sonnet's amazing work ethic caused a few problems: +All LLMs have various token limits, the most familiar being their +context window size. +But they also have a limit on how many tokens they can output +in response to a single request. +Sonnet and the majority of other +models are limited to returning 4k tokens. + +Sonnet's amazing work ethic caused it to +regularly hit this 4k output token +limit for a few reasons: 1. Sonnet is capable of outputting a very large amount of correct, complete code in one response. -So much that it can easily blow through the 4k output token limit -on API responses, which truncates its coding in mid-stream. -2. Similarly, Sonnet can specify large sequences of edits in one go, +2. Similarly, Sonnet can specify long sequences of edits in one go, like changing a majority of lines while refactoring a large file. -Again, this regularly triggered the 4k output limit -and resulted in failed edits. -3. Sonnet is not shy about quoting large chunks of an -existing file to perform a SEARCH & REPLACE edit across -a long span of lines. -This can be wasteful and also trigger the 4k output limit. - +3. Sonnet tends to quote large chunks of a +file when performing a SEARCH & REPLACE edits. +Beyond token limits, this is very wasteful. ## Good problems Problems (1) and (2) are "good problems" in the sense that Sonnet is able to write more high quality code than any other model! +We just don't want it to be interrupted prematurely +by the 4k output limit. Aider now allows Sonnet to return code in multiple 4k token responses. +Aider seamlessly combines them so that Sonnet can return arbitrarily +long responses. This gets all the upsides of Sonnet's prolific coding skills, without being constrained by the 4k output token limit. ## Wasting tokens -Problem (3) does cause some real downsides. +Problem (3) is more complicated, as Sonnet isn't just +being stopped early -- it's actually wasting a lot +of tokens, time and money. Faced with a few small changes spread far apart in a source file, Sonnet would often prefer to do one giant SEARCH/REPLACE operation of almost the entire file. -This wastes a tremendous amount of tokens, -time and money -- and risks hitting the 4k output limit. It would be far faster and less expensive to instead do a few surgical edits. From 535597f5312efa12f900303af6a35f18528bc0e4 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 16:43:15 -0300 Subject: [PATCH 061/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 52091702f..04be599ab 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -45,7 +45,7 @@ regularly hit this 4k output token limit for a few reasons: 1. Sonnet is capable of outputting a very large amount of correct, -complete code in one response. +complete new code in one response. 2. Similarly, Sonnet can specify long sequences of edits in one go, like changing a majority of lines while refactoring a large file. 3. Sonnet tends to quote large chunks of a From 5e8458f123bd1938a6e19ec664e4b90ddc4459b3 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 16:59:55 -0300 Subject: [PATCH 062/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 04be599ab..844eb9f36 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -1,6 +1,6 @@ --- title: Sonnet is the opposite of lazy -excerpt: Claude 3.5 Sonnet represents a step change in AI coding. +excerpt: Claude 3.5 Sonnet can easily write more good code than fits in one 4k token API response. highlight_image: /assets/sonnet-not-lazy.jpg nav_exclude: true --- @@ -31,6 +31,14 @@ as a result of the changes discussed below. This moved Sonnet into second place, ahead of GPT-4o and behind only Opus. +Users who tested Sonnet with a preview of +[aider's latest release](https://aider.chat/HISTORY.html#aider-v0410) +were thrilled: + +- *Works like a charm. It is a monster. It refactors files of any size like it is nothing. The continue trick with Sonnet is truly the holy grail. Aider beats Github copilot and Cursor hands down. I'm going to cancel both subscriptions.* -- [Emasoft](https://github.com/paul-gauthier/aider/issues/705#issuecomment-2200338971) +- *Thanks heaps for this feature - it's a real game changer. I can be more ambitious when asking Claude for larger features.* -- [cngarrison](https://github.com/paul-gauthier/aider/issues/705#issuecomment-2196026656) +- *Fantastic...! It's such an improvement not being constrained by output token length issues. [I refactored] a single JavaScript file into seven smaller files using a single Aider request.* -- [John Galt](https://discord.com/channels/1131200896827654144/1253492379336441907/1256250487934554143) + ## Hitting the 4k token output limit All LLMs have various token limits, the most familiar being their From 8c8b29c3cba5897dbe8858a73fb2c0ae6ffd0ba9 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 17:01:37 -0300 Subject: [PATCH 063/121] copy --- website/docs/leaderboards/index.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/website/docs/leaderboards/index.md b/website/docs/leaderboards/index.md index 78d2abafc..8d0f189e7 100644 --- a/website/docs/leaderboards/index.md +++ b/website/docs/leaderboards/index.md @@ -16,20 +16,6 @@ While [aider can connect to almost any LLM](/docs/llms.html), it works best with models that score well on the benchmarks. -## Claude 3.5 Sonnet takes the top spot - -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. - -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 [Aider's code editing benchmark](/docs/benchmarks.html#the-benchmark) asks the LLM to edit python source files to complete 133 small coding exercises. This benchmark measures the LLM's coding ability, but also whether it can consistently emit code edits in the format specified in the system prompt. From fa4793d69f97cf5199da0fdcca1256a9ddc108e1 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 17:52:57 -0300 Subject: [PATCH 064/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 844eb9f36..23e648d3c 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -20,10 +20,12 @@ Unexpectedly, this initially presented a few challenges that prevented aider from taking maximum advantage of Sonnet's capabilities. +It was often hitting the 4k output token limit, +truncating its coding in mid-stream. -It's been worth the effort to adapt aider to work well -with Sonnet, -because the result is surprisingly powerful. +It's been worth the effort to adapt aider to work +around this 4k limit, +and the result is surprisingly powerful. Sonnet's score on [aider's refactoring benchmark](https://aider.chat/docs/leaderboards/#code-refactoring-leaderboard) jumped from 55.1% up to 64.0% From a1bd9397bdc34b3785ddb34a6ef125f23b7d416c Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 17:54:14 -0300 Subject: [PATCH 065/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 23e648d3c..4ecc8f455 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -28,8 +28,7 @@ around this 4k limit, and the result is surprisingly powerful. Sonnet's score on [aider's refactoring benchmark](https://aider.chat/docs/leaderboards/#code-refactoring-leaderboard) -jumped from 55.1% up to 64.0% -as a result of the changes discussed below. +jumped from 55.1% up to 64.0%. This moved Sonnet into second place, ahead of GPT-4o and behind only Opus. From acffd542978861eb13ea485cf1ac4e7b78f79dca Mon Sep 17 00:00:00 2001 From: Taha YASSINE Date: Mon, 1 Jul 2024 22:19:13 +0100 Subject: [PATCH 066/121] Remove universal ctags install from workflows --- .github/workflows/ubuntu-tests.yml | 5 ----- .github/workflows/windows-tests.yml | 4 ---- 2 files changed, 9 deletions(-) diff --git a/.github/workflows/ubuntu-tests.yml b/.github/workflows/ubuntu-tests.yml index d8b64cdad..1ff7ee1d0 100644 --- a/.github/workflows/ubuntu-tests.yml +++ b/.github/workflows/ubuntu-tests.yml @@ -30,11 +30,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install universal ctags - run: | - sudo apt-get update - sudo apt-get install -y universal-ctags - - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/windows-tests.yml b/.github/workflows/windows-tests.yml index de4520aa6..2c74f0c3f 100644 --- a/.github/workflows/windows-tests.yml +++ b/.github/workflows/windows-tests.yml @@ -30,10 +30,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install universal ctags - run: | - choco install universal-ctags - - name: Install dependencies run: | python -m pip install --upgrade pip From 68c892125fae11499db82c3f3b1b0c7a1f96c6f8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 18:19:54 -0300 Subject: [PATCH 067/121] copy --- website/docs/tutorials.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/tutorials.md b/website/docs/tutorials.md index a5c005c4c..be5cd1ded 100644 --- a/website/docs/tutorials.md +++ b/website/docs/tutorials.md @@ -9,6 +9,7 @@ description: Intro and tutorial videos made by aider users. Here are a few tutorial videos made by aider users: - [Aider tips and Example use](https://www.youtube.com/watch?v=OsChkvGGDgw) -- techfren +- [Generate application with just one prompt using Aider](https://www.youtube.com/watch?v=Y-_0VkMUiPc&t=78s) -- AICodeKing - [Aider : the production ready AI coding assistant you've been waiting for](https://www.youtube.com/watch?v=zddJofosJuM) -- Learn Code With JV - [Holy Grail: FREE Coding Assistant That Can Build From EXISTING CODE BASE](https://www.youtube.com/watch?v=df8afeb1FY8) -- Matthew Berman - [Aider: This AI Coder Can Create AND Update Git Codebases](https://www.youtube.com/watch?v=EqLyFT78Sig) -- Ian Wootten From 8ab26158ddcad3a85138bae584e46e89b492d0b0 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 18:39:57 -0300 Subject: [PATCH 068/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 4ecc8f455..04d56ce05 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -36,7 +36,7 @@ Users who tested Sonnet with a preview of [aider's latest release](https://aider.chat/HISTORY.html#aider-v0410) were thrilled: -- *Works like a charm. It is a monster. It refactors files of any size like it is nothing. The continue trick with Sonnet is truly the holy grail. Aider beats Github copilot and Cursor hands down. I'm going to cancel both subscriptions.* -- [Emasoft](https://github.com/paul-gauthier/aider/issues/705#issuecomment-2200338971) +- *Works like a charm. It is a monster. It refactors files of any size like it is nothing. The continue trick with Sonnet is truly the holy grail. Aider beats [other tools] hands down. I'm going to cancel both subscriptions.* -- [Emasoft](https://github.com/paul-gauthier/aider/issues/705#issuecomment-2200338971) - *Thanks heaps for this feature - it's a real game changer. I can be more ambitious when asking Claude for larger features.* -- [cngarrison](https://github.com/paul-gauthier/aider/issues/705#issuecomment-2196026656) - *Fantastic...! It's such an improvement not being constrained by output token length issues. [I refactored] a single JavaScript file into seven smaller files using a single Aider request.* -- [John Galt](https://discord.com/channels/1131200896827654144/1253492379336441907/1256250487934554143) From 338c4cfb61fa38abaaed4c867c67b7702a49f4b8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 18:51:34 -0300 Subject: [PATCH 069/121] added haiku to leaderboard --- website/_data/edit_leaderboard.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index c7c4a0efc..b2c1fc469 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -681,4 +681,27 @@ versions: 0.39.1-dev seconds_per_case: 30.2 total_cost: 0.0857 + +- dirname: 2024-07-01-21-41-48--haiku-whole + test_cases: 133 + model: openrouter/anthropic/claude-3-haiku + edit_format: whole + commit_hash: 75f506d + pass_rate_1: 40.6 + pass_rate_2: 47.4 + percent_cases_well_formed: 100.0 + error_outputs: 6 + num_malformed_responses: 0 + num_with_malformed_responses: 0 + user_asks: 0 + lazy_comments: 0 + syntax_errors: 0 + indentation_errors: 0 + exhausted_context_windows: 0 + test_timeouts: 2 + command: aider --model openrouter/anthropic/claude-3-haiku + date: 2024-07-01 + versions: 0.41.1-dev + seconds_per_case: 7.1 + total_cost: 0.1946 \ No newline at end of file From 4cd7d9235163ebfe45eaf064b33025d3687dd626 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 19:45:22 -0300 Subject: [PATCH 070/121] Removed config_file_parser_class=configargparse.YAMLConfigFileParser to use more flexible DefaultConfigFileParser #767 --- aider/args.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aider/args.py b/aider/args.py index 7d4e6a160..c16298fa8 100644 --- a/aider/args.py +++ b/aider/args.py @@ -25,7 +25,6 @@ def get_parser(default_config_files, git_root): description="aider is GPT powered coding in your terminal", add_config_file_help=True, default_config_files=default_config_files, - config_file_parser_class=configargparse.YAMLConfigFileParser, auto_env_var_prefix="AIDER_", ) group = parser.add_argument_group("Main") From 4a39bf074b11b8c4218865911f508c06d7dad83b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 20:31:10 -0300 Subject: [PATCH 071/121] copy --- website/docs/images-urls.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/images-urls.md b/website/docs/images-urls.md index 3fb526e09..0ee6bc5dc 100644 --- a/website/docs/images-urls.md +++ b/website/docs/images-urls.md @@ -19,10 +19,11 @@ Adding images to a chat can be helpful in many situations: - Screenshot an error message that is otherwise hard to copy & paste as text. - Etc. -To add images to the chat: +You can add images to the chat just like you would +add any other file: - Use `/add ` from within the chat -- Launch aider with image filenames on the command line: `aider --sonnet ` +- Launch aider with image filenames on the command line: `aider ` along with any other command line arguments you need. ## URLs From e712b670841c1c5af85d89464649312884f52ae7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Mon, 1 Jul 2024 20:42:48 -0300 Subject: [PATCH 072/121] copy --- README.md | 8 ++++---- website/_includes/get-started.md | 8 ++++---- website/docs/install/install.md | 8 ++++++++ website/index.md | 8 ++++---- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2d036b08f..9a5d29dac 100644 --- a/README.md +++ b/README.md @@ -40,13 +40,13 @@ $ pip install aider-chat # Change directory into a git repo $ cd /to/your/git/repo -# Work with GPT-4o on your repo -$ export OPENAI_API_KEY=your-key-goes-here -$ aider - # Work with Claude 3.5 Sonnet on your repo $ export ANTHROPIC_API_KEY=your-key-goes-here $ aider + +# Work with GPT-4o on your repo +$ export OPENAI_API_KEY=your-key-goes-here +$ aider ``` diff --git a/website/_includes/get-started.md b/website/_includes/get-started.md index 5e756f846..5321c2d75 100644 --- a/website/_includes/get-started.md +++ b/website/_includes/get-started.md @@ -7,11 +7,11 @@ $ pip install aider-chat # Change directory into a git repo $ cd /to/your/git/repo -# Work with GPT-4o on your repo -$ export OPENAI_API_KEY=your-key-goes-here -$ aider - # Work with Claude 3.5 Sonnet on your repo $ export ANTHROPIC_API_KEY=your-key-goes-here $ aider + +# Work with GPT-4o on your repo +$ export OPENAI_API_KEY=your-key-goes-here +$ aider ``` diff --git a/website/docs/install/install.md b/website/docs/install/install.md index ec0206e39..9936e1e9d 100644 --- a/website/docs/install/install.md +++ b/website/docs/install/install.md @@ -24,6 +24,14 @@ Note that this is different than being a "ChatGPT Plus" subscriber. To work with Anthropic's models like Claude 3 Opus you need a paid [Anthropic API key](https://docs.anthropic.com/claude/reference/getting-started-with-the-api). +## Manage your python environment + +Using a Python +[virtual environment](https://docs.python.org/3/library/venv.html) +is recommended. +Or, you could consider +[installing aider using pipx](/docs/install/pipx.html). + ## Windows install ``` diff --git a/website/index.md b/website/index.md index a8abc461a..35060ea1a 100644 --- a/website/index.md +++ b/website/index.md @@ -54,13 +54,13 @@ $ pip install aider-chat # Change directory into a git repo $ cd /to/your/git/repo -# Work with GPT-4o on your repo -$ export OPENAI_API_KEY=your-key-goes-here -$ aider - # Work with Claude 3.5 Sonnet on your repo $ export ANTHROPIC_API_KEY=your-key-goes-here $ aider + +# Work with GPT-4o on your repo +$ export OPENAI_API_KEY=your-key-goes-here +$ aider ``` From 3953db340ae7d8fdeb6d7aff95d2178d934330e5 Mon Sep 17 00:00:00 2001 From: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com> Date: Mon, 1 Jul 2024 22:46:48 -0300 Subject: [PATCH 073/121] Update 2024-07-01-sonnet-not-lazy.md --- website/_posts/2024-07-01-sonnet-not-lazy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 04d56ce05..555736570 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -20,7 +20,7 @@ Unexpectedly, this initially presented a few challenges that prevented aider from taking maximum advantage of Sonnet's capabilities. -It was often hitting the 4k output token limit, +Sonnet was often hitting the 4k output token limit, truncating its coding in mid-stream. It's been worth the effort to adapt aider to work From 0786205f5200e86e7261e67c14033bb2fce56997 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 2 Jul 2024 18:54:48 -0300 Subject: [PATCH 074/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 04d56ce05..27a93e441 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -17,15 +17,16 @@ Claude 3.5 Sonnet represents a step change in AI coding. It is incredibly industrious, diligent and hard working. Unexpectedly, -this initially presented a few challenges -that prevented aider from taking maximum advantage of -Sonnet's capabilities. -It was often hitting the 4k output token limit, +this presented a challenge: +Sonnet +was often writing so much code that +it was hitting the 4k output token limit, truncating its coding in mid-stream. -It's been worth the effort to adapt aider to work -around this 4k limit, -and the result is surprisingly powerful. +Aider now works +around this 4k limit and allows Sonnet to produce +as much code as it wants. +The result is surprisingly powerful. Sonnet's score on [aider's refactoring benchmark](https://aider.chat/docs/leaderboards/#code-refactoring-leaderboard) jumped from 55.1% up to 64.0%. From 0b9aac534895ebfcb8f77181903ad339a511e74d Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 2 Jul 2024 18:59:18 -0300 Subject: [PATCH 075/121] copy --- website/docs/install/optional.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/website/docs/install/optional.md b/website/docs/install/optional.md index 846642d93..34c4df8a9 100644 --- a/website/docs/install/optional.md +++ b/website/docs/install/optional.md @@ -12,20 +12,10 @@ The steps below are completely optional. {:toc} -## Store your api key - -You can place your api key in an environment variable: - -* `export OPENAI_API_KEY=sk-...` on Linux or Mac -* `setx OPENAI_API_KEY sk-...` in Windows PowerShell - -Or you can create a `.aider.conf.yml` file in your home directory. -Put a line in it like this to specify your api key: - -``` -openai-api-key: sk-... -``` +## Store your api keys +You can place your [api keys in a `.env` file](/docs/config/dotenv.html) +and they will be loaded automatically whenever you run aider. ## Enable Playwright From af29e633b2c070ff41e16e31048b469d00ec351f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 2 Jul 2024 19:42:31 -0300 Subject: [PATCH 076/121] Handle max_input_tokens set with None #757 --- aider/history.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aider/history.py b/aider/history.py index e80b80e1c..3615235c4 100644 --- a/aider/history.py +++ b/aider/history.py @@ -61,7 +61,11 @@ class ChatSummary: sized.reverse() keep = [] total = 0 - model_max_input_tokens = self.model.info.get("max_input_tokens", 4096) - 512 + + # These sometimes come set with value = None + model_max_input_tokens = self.model.info.get("max_input_tokens") or 4096 + model_max_input_tokens -= 512 + for i in range(split_index): total += sized[i][0] if total > model_max_input_tokens: From 7a3b0d6ddb86f9321ea59d9d57de82163e027e61 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Tue, 2 Jul 2024 19:59:03 -0300 Subject: [PATCH 077/121] handle oserrors when enumerating repo files #780 --- aider/coders/base_coder.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 22694cf1e..76d3fa4f3 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -1223,13 +1223,19 @@ class Coder: files = [self.get_rel_fname(fname) for fname in self.abs_fnames] return sorted(set(files)) + def is_file_safe(self, fname): + try: + return Path(self.abs_root_path(fname)).is_file() + except OSError: + return + def get_all_relative_files(self): if self.repo: files = self.repo.get_tracked_files() else: files = self.get_inchat_relative_files() - files = [fname for fname in files if Path(self.abs_root_path(fname)).is_file()] + files = [fname for fname in files if self.is_file_safe(fname)] return sorted(set(files)) def get_all_abs_files(self): From 41ac55c255fe80be77d25a429583c61d68b37dfc Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 11:57:18 -0300 Subject: [PATCH 078/121] The --test-cmd arg should not be a list #741 #657 --- aider/args.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aider/args.py b/aider/args.py index c16298fa8..7a69e04aa 100644 --- a/aider/args.py +++ b/aider/args.py @@ -385,7 +385,6 @@ def get_parser(default_config_files, git_root): ) group.add_argument( "--test-cmd", - action="append", help="Specify command to run tests", default=[], ) From e5e2535f59e24b73647e7a2f6cd71fdd1055118f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 11:58:09 -0300 Subject: [PATCH 079/121] copy --- website/docs/config/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/config/options.md b/website/docs/config/options.md index 310999899..b9bb5c228 100644 --- a/website/docs/config/options.md +++ b/website/docs/config/options.md @@ -356,7 +356,7 @@ Aliases: - `--auto-lint` - `--no-auto-lint` -### `--test-cmd` +### `--test-cmd VALUE` Specify command to run tests Default: [] Environment variable: `AIDER_TEST_CMD` From ee203deef0ff7bbef229e3766457865f7c507b10 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 12:45:53 -0300 Subject: [PATCH 080/121] Lazily import litellm to shave >1sec off the initial load time of aider --- aider/litellm.py | 20 +++++++++++--- aider/models.py | 56 +++++++++++++++++++++++++++++++++++++++ aider/sendchat.py | 67 +++++++++++++++++++++++++++-------------------- 3 files changed, 111 insertions(+), 32 deletions(-) diff --git a/aider/litellm.py b/aider/litellm.py index 2c0389b54..4c97c0a94 100644 --- a/aider/litellm.py +++ b/aider/litellm.py @@ -6,9 +6,23 @@ warnings.filterwarnings("ignore", category=UserWarning, module="pydantic") os.environ["OR_SITE_URL"] = "http://aider.chat" os.environ["OR_APP_NAME"] = "Aider" -import litellm # noqa: E402 +# `import litellm` takes 1.5 seconds, defer it! -litellm.suppress_debug_info = True -litellm.set_verbose = False + +class LazyLiteLLM: + def __init__(self): + self._lazy_module = None + + def __getattr__(self, name): + if self._lazy_module is None: + self._lazy_module = __import__("litellm") + + self._lazy_module.suppress_debug_info = True + self._lazy_module.set_verbose = False + + return getattr(self._lazy_module, name) + + +litellm = LazyLiteLLM() __all__ = [litellm] diff --git a/aider/models.py b/aider/models.py index 565890501..41dd3e6b7 100644 --- a/aider/models.py +++ b/aider/models.py @@ -15,6 +15,44 @@ from aider.litellm import litellm DEFAULT_MODEL_NAME = "gpt-4o" +OPENAI_MODELS = """ +gpt-4 +gpt-4o +gpt-4o-2024-05-13 +gpt-4-turbo-preview +gpt-4-0314 +gpt-4-0613 +gpt-4-32k +gpt-4-32k-0314 +gpt-4-32k-0613 +gpt-4-turbo +gpt-4-turbo-2024-04-09 +gpt-4-1106-preview +gpt-4-0125-preview +gpt-4-vision-preview +gpt-4-1106-vision-preview +gpt-3.5-turbo +gpt-3.5-turbo-0301 +gpt-3.5-turbo-0613 +gpt-3.5-turbo-1106 +gpt-3.5-turbo-0125 +gpt-3.5-turbo-16k +gpt-3.5-turbo-16k-0613 +""" + +OPENAI_MODELS = [ln.strip for ln in OPENAI_MODELS.splitlines() if ln.strip()] + +ANTHROPIC_MODELS = """ +claude-2 +claude-2.1 +claude-3-haiku-20240307 +claude-3-opus-20240229 +claude-3-sonnet-20240229 +claude-3-5-sonnet-20240620 +""" + +ANTHROPIC_MODELS = [ln.strip for ln in ANTHROPIC_MODELS.splitlines() if ln.strip()] + @dataclass class ModelSettings: @@ -491,7 +529,25 @@ class Model: with Image.open(fname) as img: return img.size + def fast_validate_environment(self): + """Fast path for common models. Avoids forcing litellm import.""" + + model = self.name + if model in OPENAI_MODELS: + var = "OPENAI_API_KEY" + elif model in ANTHROPIC_MODELS: + var = "ANTHROPIC_API_KEY" + else: + return + + if os.environ.get(var): + return dict(keys_in_environment=[var], missing_keys=[]) + def validate_environment(self): + res = self.fast_validate_environment() + if res: + return res + # https://github.com/BerriAI/litellm/issues/3190 model = self.name diff --git a/aider/sendchat.py b/aider/sendchat.py index 78e16ae64..43153d2ec 100644 --- a/aider/sendchat.py +++ b/aider/sendchat.py @@ -15,40 +15,49 @@ CACHE = None # CACHE = Cache(CACHE_PATH) -def should_giveup(e): - if not hasattr(e, "status_code"): - return False +def lazy_litellm_retry_decorator(func): + def wrapper(*args, **kwargs): + def should_giveup(e): + if not hasattr(e, "status_code"): + return False - if type(e) in ( - httpx.ConnectError, - httpx.RemoteProtocolError, - httpx.ReadTimeout, - ): - return False + if type(e) in ( + httpx.ConnectError, + httpx.RemoteProtocolError, + httpx.ReadTimeout, + ): + return False - return not litellm._should_retry(e.status_code) + return not litellm._should_retry(e.status_code) + + decorated_func = backoff.on_exception( + backoff.expo, + ( + httpx.ConnectError, + httpx.RemoteProtocolError, + httpx.ReadTimeout, + litellm.exceptions.APIConnectionError, + litellm.exceptions.APIError, + litellm.exceptions.RateLimitError, + litellm.exceptions.ServiceUnavailableError, + litellm.exceptions.Timeout, + litellm.llms.anthropic.AnthropicError, + ), + giveup=should_giveup, + max_time=60, + on_backoff=lambda details: print( + f"{details.get('exception','Exception')}\nRetry in {details['wait']:.1f} seconds." + ), + )(func) + return decorated_func(*args, **kwargs) + + return wrapper -@backoff.on_exception( - backoff.expo, - ( - httpx.ConnectError, - httpx.RemoteProtocolError, - httpx.ReadTimeout, - litellm.exceptions.APIConnectionError, - litellm.exceptions.APIError, - litellm.exceptions.RateLimitError, - litellm.exceptions.ServiceUnavailableError, - litellm.exceptions.Timeout, - litellm.llms.anthropic.AnthropicError, - ), - giveup=should_giveup, - max_time=60, - on_backoff=lambda details: print( - f"{details.get('exception','Exception')}\nRetry in {details['wait']:.1f} seconds." - ), -) +@lazy_litellm_retry_decorator def send_with_retries(model_name, messages, functions, stream, temperature=0): + from aider.litellm import litellm + kwargs = dict( model=model_name, messages=messages, From 2fc358a02f067e202691f4b5435f41479926b429 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:04:13 -0300 Subject: [PATCH 081/121] Defer litellm import until first chat message; only import streamlit if gui is activated --- aider/main.py | 3 ++- aider/models.py | 44 +++++++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/aider/main.py b/aider/main.py index a7a6ce67a..6a099f0b4 100644 --- a/aider/main.py +++ b/aider/main.py @@ -8,7 +8,6 @@ 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 @@ -150,6 +149,8 @@ def scrub_sensitive_info(args, text): def launch_gui(args): + from streamlit.web import cli + from aider import gui print() diff --git a/aider/models.py b/aider/models.py index 41dd3e6b7..e9c1fb625 100644 --- a/aider/models.py +++ b/aider/models.py @@ -1,9 +1,11 @@ import difflib +import importlib import json import math import os import sys from dataclasses import dataclass, fields +from pathlib import Path from typing import Optional import yaml @@ -40,7 +42,7 @@ gpt-3.5-turbo-16k gpt-3.5-turbo-16k-0613 """ -OPENAI_MODELS = [ln.strip for ln in OPENAI_MODELS.splitlines() if ln.strip()] +OPENAI_MODELS = [ln.strip() for ln in OPENAI_MODELS.splitlines() if ln.strip()] ANTHROPIC_MODELS = """ claude-2 @@ -51,7 +53,7 @@ claude-3-sonnet-20240229 claude-3-5-sonnet-20240620 """ -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()] @dataclass @@ -365,25 +367,7 @@ class Model: def __init__(self, model, weak_model=None): self.name = model - # Do we have the model_info? - try: - self.info = litellm.get_model_info(model) - except Exception: - self.info = dict() - - if not self.info and "gpt-4o" in self.name: - self.info = { - "max_tokens": 4096, - "max_input_tokens": 128000, - "max_output_tokens": 4096, - "input_cost_per_token": 5e-06, - "output_cost_per_token": 1.5e-5, - "litellm_provider": "openai", - "mode": "chat", - "supports_function_calling": True, - "supports_parallel_function_calling": True, - "supports_vision": True, - } + self.info = self.get_model_info(model) # Are all needed keys/params available? res = self.validate_environment() @@ -404,6 +388,24 @@ class Model: else: self.get_weak_model(weak_model) + def get_model_info(self, model): + # Try and do this quickly, without triggering the litellm import + spec = importlib.util.find_spec("litellm") + if spec: + origin = Path(spec.origin) + fname = origin.parent / "model_prices_and_context_window_backup.json" + if fname.exists(): + data = json.loads(fname.read_text()) + info = data.get(model) + if info: + return info + + # Do it the slow way... + try: + self.info = litellm.get_model_info(model) + except Exception: + self.info = dict() + def configure_model_settings(self, model): for ms in MODEL_SETTINGS: # direct match, or match "provider/" From b454579cd6e94f56b1fe8c91b745d2933d6e4d49 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:11:39 -0300 Subject: [PATCH 082/121] Avoid importing openai --- aider/commands.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index c5ffdd82c..b3101c82c 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -5,7 +5,6 @@ import sys from pathlib import Path import git -import openai from prompt_toolkit.completion import Completion from aider import models, prompts, voice @@ -685,7 +684,7 @@ class Commands: try: text = self.voice.record_and_transcribe(history, language=self.voice_language) - except openai.OpenAIError as err: + except litellm.OpenAIError as err: self.io.tool_error(f"Unable to use OpenAI whisper model: {err}") return From 5e0ff7627ebe855f5b24308d24de8da54f6c0214 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:17:04 -0300 Subject: [PATCH 083/121] Defer loading of networkx --- aider/repomap.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/aider/repomap.py b/aider/repomap.py index 872424b62..e7b8cf93b 100644 --- a/aider/repomap.py +++ b/aider/repomap.py @@ -8,7 +8,6 @@ from collections import Counter, defaultdict, namedtuple from importlib import resources from pathlib import Path -import networkx as nx from diskcache import Cache from grep_ast import TreeContext, filename_to_lang from pygments.lexers import guess_lexer_for_filename @@ -32,6 +31,7 @@ class RepoMap: cache_missing = False warned_files = set() + nx = None def __init__( self, @@ -58,6 +58,15 @@ class RepoMap: self.token_count = main_model.token_count self.repo_content_prefix = repo_content_prefix + def import_nx(self): + """Import nx as needed, to avoid 250ms on launch""" + + if self.nx: + return + import networkx as nx + + self.nx = nx + def get_repo_map(self, chat_files, other_files, mentioned_fnames=None, mentioned_idents=None): if self.max_map_tokens <= 0: return @@ -230,6 +239,8 @@ class RepoMap: ) def get_ranked_tags(self, chat_fnames, other_fnames, mentioned_fnames, mentioned_idents): + self.import_nx() + defines = defaultdict(set) references = defaultdict(list) definitions = defaultdict(set) @@ -295,7 +306,7 @@ class RepoMap: idents = set(defines.keys()).intersection(set(references.keys())) - G = nx.MultiDiGraph() + G = self.nx.MultiDiGraph() for ident in idents: definers = defines[ident] @@ -326,7 +337,7 @@ class RepoMap: pers_args = dict() try: - ranked = nx.pagerank(G, weight="weight", **pers_args) + ranked = self.nx.pagerank(G, weight="weight", **pers_args) except ZeroDivisionError: return [] From 2dc6735ab42c129d12edf9eff63abfac89a8dbba Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:25:10 -0300 Subject: [PATCH 084/121] defer import of httpx --- aider/main.py | 3 ++- aider/scrape.py | 3 ++- aider/sendchat.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/aider/main.py b/aider/main.py index 6a099f0b4..a7bdf4932 100644 --- a/aider/main.py +++ b/aider/main.py @@ -5,7 +5,6 @@ import sys from pathlib import Path import git -import httpx from dotenv import load_dotenv from prompt_toolkit.enums import EditingMode @@ -293,6 +292,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F args = parser.parse_args(argv) if not args.verify_ssl: + import httpx + litellm.client_session = httpx.Client(verify=False) if args.gui and not return_coder: diff --git a/aider/scrape.py b/aider/scrape.py index 2ac29b6a2..c705755ae 100755 --- a/aider/scrape.py +++ b/aider/scrape.py @@ -3,7 +3,6 @@ import re import sys -import httpx import playwright import pypandoc from bs4 import BeautifulSoup @@ -111,6 +110,8 @@ class Scraper: return PLAYWRIGHT_INFO def scrape_with_httpx(self, url): + import httpx + headers = {"User-Agent": f"Mozilla./5.0 ({aider_user_agent})"} try: with httpx.Client(headers=headers) as client: diff --git a/aider/sendchat.py b/aider/sendchat.py index 43153d2ec..bb0030c3a 100644 --- a/aider/sendchat.py +++ b/aider/sendchat.py @@ -2,7 +2,6 @@ import hashlib import json import backoff -import httpx from aider.dump import dump # noqa: F401 from aider.litellm import litellm @@ -17,6 +16,8 @@ CACHE = None def lazy_litellm_retry_decorator(func): def wrapper(*args, **kwargs): + import httpx + def should_giveup(e): if not hasattr(e, "status_code"): return False From ed35af44b311d2c01f7222d09eca5319401e9b6f Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:35:33 -0300 Subject: [PATCH 085/121] defer numpy, bs4 and jsonschema --- aider/coders/base_coder.py | 3 ++- aider/scrape.py | 22 ++-------------------- aider/voice.py | 9 +++++---- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 76d3fa4f3..c7f509184 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -13,7 +13,6 @@ from json.decoder import JSONDecodeError from pathlib import Path import git -from jsonschema import Draft7Validator from rich.console import Console, Text from rich.markdown import Markdown @@ -346,6 +345,8 @@ class Coder: # validate the functions jsonschema if self.functions: + from jsonschema import Draft7Validator + for function in self.functions: Draft7Validator.check_schema(function) diff --git a/aider/scrape.py b/aider/scrape.py index c705755ae..0ed64fc4c 100755 --- a/aider/scrape.py +++ b/aider/scrape.py @@ -5,7 +5,6 @@ import sys import playwright import pypandoc -from bs4 import BeautifulSoup from playwright.sync_api import sync_playwright from aider import __version__, urls @@ -58,7 +57,6 @@ class Scraper: self.try_pandoc() content = self.html_to_markdown(content) - # content = html_to_text(content) return content @@ -139,6 +137,8 @@ class Scraper: self.pandoc_available = True def html_to_markdown(self, page_source): + from bs4 import BeautifulSoup + soup = BeautifulSoup(page_source, "html.parser") soup = slimdown_html(soup) page_source = str(soup) @@ -174,24 +174,6 @@ def slimdown_html(soup): return soup -# Adapted from AutoGPT, MIT License -# -# https://github.com/Significant-Gravitas/AutoGPT/blob/fe0923ba6c9abb42ac4df79da580e8a4391e0418/autogpts/autogpt/autogpt/commands/web_selenium.py#L173 - - -def html_to_text(page_source: str) -> str: - soup = BeautifulSoup(page_source, "html.parser") - - for script in soup(["script", "style"]): - script.extract() - - text = soup.get_text() - lines = (line.strip() for line in text.splitlines()) - chunks = (phrase.strip() for line in lines for phrase in line.split(" ")) - text = "\n".join(chunk for chunk in chunks if chunk) - return text - - def main(url): scraper = Scraper() content = scraper.scrape(url) diff --git a/aider/voice.py b/aider/voice.py index 6032ad11d..416917dee 100644 --- a/aider/voice.py +++ b/aider/voice.py @@ -1,10 +1,9 @@ +import math import os import queue import tempfile import time -import numpy as np - from aider.litellm import litellm try: @@ -41,6 +40,8 @@ class Voice: def callback(self, indata, frames, time, status): """This is called (from a separate thread) for each audio block.""" + import numpy as np + rms = np.sqrt(np.mean(indata**2)) self.max_rms = max(self.max_rms, rms) self.min_rms = min(self.min_rms, rms) @@ -55,7 +56,7 @@ class Voice: def get_prompt(self): num = 10 - if np.isnan(self.pct) or self.pct < self.threshold: + if math.isnan(self.pct) or self.pct < self.threshold: cnt = 0 else: cnt = int(self.pct * 10) @@ -78,7 +79,7 @@ class Voice: filename = tempfile.mktemp(suffix=".wav") try: - sample_rate = int(self.sd.query_devices(None, 'input')['default_samplerate']) + sample_rate = int(self.sd.query_devices(None, "input")["default_samplerate"]) except (TypeError, ValueError): sample_rate = 16000 # fallback to 16kHz if unable to query device From b3f7f0a25006236ad8b9228936649b1068f1008a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:44:26 -0300 Subject: [PATCH 086/121] Only check versions once per day --- aider/versioncheck.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/aider/versioncheck.py b/aider/versioncheck.py index 89007f155..e93e238fe 100644 --- a/aider/versioncheck.py +++ b/aider/versioncheck.py @@ -1,12 +1,20 @@ import sys +import time +from pathlib import Path import packaging.version -import requests import aider def check_version(print_cmd): + fname = Path.home() / ".aider/versioncheck" + day = 60 * 60 * 24 + if fname.exists() and time.time() - fname.stat().st_mtime < day: + return + + import requests + try: response = requests.get("https://pypi.org/pypi/aider-chat/json") data = response.json() @@ -27,6 +35,9 @@ def check_version(print_cmd): else: print_cmd(f"{py} -m pip install --upgrade aider-chat") + if not fname.parent.exists(): + fname.parent.mkdir() + fname.touch() return is_update_available except Exception as err: print_cmd(f"Error checking pypi for new version: {err}") From b5cd5f0e237dc1439f7d13be91f6b31cc612e37e Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 13:55:29 -0300 Subject: [PATCH 087/121] Use a thread to import slow modules in the background --- aider/main.py | 14 ++++++++++++++ aider/repomap.py | 15 +++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/aider/main.py b/aider/main.py index a7bdf4932..25e417e74 100644 --- a/aider/main.py +++ b/aider/main.py @@ -2,6 +2,7 @@ import configparser import os import re import sys +import threading from pathlib import Path import git @@ -536,6 +537,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F return 1 return + threading.Thread(target=load_slow_imports).start() + while True: try: coder.run() @@ -545,6 +548,17 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F coder.show_announcements() +def load_slow_imports(): + # These imports are deferred in various ways to + # improve startup time. + # This func is called in a thread to load them in the background + # while we wait for the user to type their first message. + import httpx # noqa: F401 + import litellm # noqa: F401 + import networkx # noqa: F401 + import numpy # noqa: F401 + + if __name__ == "__main__": status = main() sys.exit(status) diff --git a/aider/repomap.py b/aider/repomap.py index e7b8cf93b..554286ad9 100644 --- a/aider/repomap.py +++ b/aider/repomap.py @@ -58,15 +58,6 @@ class RepoMap: self.token_count = main_model.token_count self.repo_content_prefix = repo_content_prefix - def import_nx(self): - """Import nx as needed, to avoid 250ms on launch""" - - if self.nx: - return - import networkx as nx - - self.nx = nx - def get_repo_map(self, chat_files, other_files, mentioned_fnames=None, mentioned_idents=None): if self.max_map_tokens <= 0: return @@ -239,7 +230,7 @@ class RepoMap: ) def get_ranked_tags(self, chat_fnames, other_fnames, mentioned_fnames, mentioned_idents): - self.import_nx() + import networkx as nx defines = defaultdict(set) references = defaultdict(list) @@ -306,7 +297,7 @@ class RepoMap: idents = set(defines.keys()).intersection(set(references.keys())) - G = self.nx.MultiDiGraph() + G = nx.MultiDiGraph() for ident in idents: definers = defines[ident] @@ -337,7 +328,7 @@ class RepoMap: pers_args = dict() try: - ranked = self.nx.pagerank(G, weight="weight", **pers_args) + ranked = nx.pagerank(G, weight="weight", **pers_args) except ZeroDivisionError: return [] From 7a7508fdd197951cb9e24a7023c74b51cb9f75d2 Mon Sep 17 00:00:00 2001 From: jvmncs Date: Wed, 3 Jul 2024 12:59:47 -0400 Subject: [PATCH 088/121] fix broken dotenv link in docs/config/aider_conf.html --- website/_includes/special-keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_includes/special-keys.md b/website/_includes/special-keys.md index ec5d59f9c..0991b2bb3 100644 --- a/website/_includes/special-keys.md +++ b/website/_includes/special-keys.md @@ -6,4 +6,4 @@ have their keys and settings specified in environment variables. This can be done in your shell, or by using a -[`.env` file](/docs/dotenv.html). +[`.env` file](/docs/config/dotenv.html). From 8a1fbfd95d494633afe6f9e4824bd7943313e982 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 14:06:39 -0300 Subject: [PATCH 089/121] Added --exit --- aider/args.py | 6 ++++++ aider/main.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/aider/args.py b/aider/args.py index 7a69e04aa..a2c28a099 100644 --- a/aider/args.py +++ b/aider/args.py @@ -462,6 +462,12 @@ def get_parser(default_config_files, git_root): help="Print the system prompts and exit (debug)", default=False, ) + group.add_argument( + "--exit", + action="store_true", + help="Do all startup activities then exit before accepting user input (debug)", + default=False, + ) group.add_argument( "--message", "--msg", diff --git a/aider/main.py b/aider/main.py index 25e417e74..88ea47f98 100644 --- a/aider/main.py +++ b/aider/main.py @@ -537,6 +537,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F return 1 return + if args.exit: + return + threading.Thread(target=load_slow_imports).start() while True: From c576377598caba5b13af307330c1fa84ec5ae711 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 14:13:19 -0300 Subject: [PATCH 090/121] copy --- website/assets/sample.aider.conf.yml | 3 +++ website/assets/sample.env | 3 +++ website/docs/config/aider_conf.md | 3 +++ website/docs/config/dotenv.md | 3 +++ website/docs/config/options.md | 7 ++++++- 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/website/assets/sample.aider.conf.yml b/website/assets/sample.aider.conf.yml index f6b3233ac..8aaac6dc0 100644 --- a/website/assets/sample.aider.conf.yml +++ b/website/assets/sample.aider.conf.yml @@ -223,6 +223,9 @@ ## Print the system prompts and exit (debug) #show-prompts: false +## Do all startup activities then exit before accepting user input (debug) +#exit: false + ## Specify a single message to send the LLM, process reply then exit (disables chat mode) #message: diff --git a/website/assets/sample.env b/website/assets/sample.env index 72cc98a3a..e14e2c1d0 100644 --- a/website/assets/sample.env +++ b/website/assets/sample.env @@ -228,6 +228,9 @@ ## Print the system prompts and exit (debug) #AIDER_SHOW_PROMPTS=false +## Do all startup activities then exit before accepting user input (debug) +#AIDER_EXIT=false + ## Specify a single message to send the LLM, process reply then exit (disables chat mode) #AIDER_MESSAGE= diff --git a/website/docs/config/aider_conf.md b/website/docs/config/aider_conf.md index 428ff55d2..977d5c26b 100644 --- a/website/docs/config/aider_conf.md +++ b/website/docs/config/aider_conf.md @@ -251,6 +251,9 @@ cog.outl("```") ## Print the system prompts and exit (debug) #show-prompts: false +## Do all startup activities then exit before accepting user input (debug) +#exit: false + ## Specify a single message to send the LLM, process reply then exit (disables chat mode) #message: diff --git a/website/docs/config/dotenv.md b/website/docs/config/dotenv.md index dfede0e8e..e0d2b00c6 100644 --- a/website/docs/config/dotenv.md +++ b/website/docs/config/dotenv.md @@ -261,6 +261,9 @@ cog.outl("```") ## Print the system prompts and exit (debug) #AIDER_SHOW_PROMPTS=false +## Do all startup activities then exit before accepting user input (debug) +#AIDER_EXIT=false + ## Specify a single message to send the LLM, process reply then exit (disables chat mode) #AIDER_MESSAGE= diff --git a/website/docs/config/options.md b/website/docs/config/options.md index b9bb5c228..58558a5b8 100644 --- a/website/docs/config/options.md +++ b/website/docs/config/options.md @@ -48,7 +48,7 @@ usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model] [--test-cmd] [--auto-test | --no-auto-test] [--test] [--vim] [--voice-language] [--version] [--check-update] [--skip-check-update] [--apply] [--yes] [-v] - [--show-repo-map] [--show-prompts] [--message] + [--show-repo-map] [--show-prompts] [--exit] [--message] [--message-file] [--encoding] [-c] [--gui] ``` @@ -425,6 +425,11 @@ Print the system prompts and exit (debug) Default: False Environment variable: `AIDER_SHOW_PROMPTS` +### `--exit` +Do all startup activities then exit before accepting user input (debug) +Default: False +Environment variable: `AIDER_EXIT` + ### `--message COMMAND` Specify a single message to send the LLM, process reply then exit (disables chat mode) Environment variable: `AIDER_MESSAGE` From 7e9b77d2a28aa5a903aabe14fe0e6ddb018fc769 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 14:59:08 -0300 Subject: [PATCH 091/121] fix broken links --- HISTORY.md | 4 ++-- website/_config.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 09c20ff23..55ff8cb57 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -289,13 +289,13 @@ - Added support for `.aiderignore` file, which instructs aider to ignore parts of the git repo. - New `--commit` cmd line arg, which just commits all pending changes with a sensible commit message generated by gpt-3.5. -- Added universal ctags and multiple architectures to the [aider docker image](https://aider.chat/docs/docker.html) +- Added universal ctags and multiple architectures to the [aider docker image](https://aider.chat/docs/install/docker.html) - `/run` and `/git` now accept full shell commands, like: `/run (cd subdir; ls)` - Restored missing `--encoding` cmd line switch. ### Aider v0.14.2 -- Easily [run aider from a docker image](https://aider.chat/docs/docker.html) +- Easily [run aider from a docker image](https://aider.chat/docs/install/docker.html) - Fixed bug with chat history summarization. - Fixed bug if `soundfile` package not available. diff --git a/website/_config.yml b/website/_config.yml index 15e28ec7f..7d8f53af0 100644 --- a/website/_config.yml +++ b/website/_config.yml @@ -28,7 +28,7 @@ aux_links: "Discord": - "https://discord.gg/Tv2uQnR88V" "Blog": - - "/blog" + - "/blog/" nav_external_links: - title: "GitHub" From d65450b11ff2af2772f56435cc8abbf3027648bb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 14:59:14 -0300 Subject: [PATCH 092/121] copy --- website/HISTORY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/HISTORY.md b/website/HISTORY.md index 199e7391c..c8da5ea82 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -300,13 +300,13 @@ cog.out(text) - Added support for `.aiderignore` file, which instructs aider to ignore parts of the git repo. - New `--commit` cmd line arg, which just commits all pending changes with a sensible commit message generated by gpt-3.5. -- Added universal ctags and multiple architectures to the [aider docker image](https://aider.chat/docs/docker.html) +- Added universal ctags and multiple architectures to the [aider docker image](https://aider.chat/docs/install/docker.html) - `/run` and `/git` now accept full shell commands, like: `/run (cd subdir; ls)` - Restored missing `--encoding` cmd line switch. ### Aider v0.14.2 -- Easily [run aider from a docker image](https://aider.chat/docs/docker.html) +- Easily [run aider from a docker image](https://aider.chat/docs/install/docker.html) - Fixed bug with chat history summarization. - Fixed bug if `soundfile` package not available. From 5e26014daec74e866da327b25e88be8fdecf5050 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 15:33:47 -0300 Subject: [PATCH 093/121] check links on pages build --- .github/workflows/pages.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 7585af8b4..708083e4c 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -69,3 +69,17 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install linkchecker + run: | + python -m pip install --upgrade pip + pip install linkchecker + + - name: Run linkchecker + run: | + linkchecker https://aider.chat From 0ef842dd536ce5e67dcc698fa2c87fdc86b1704c Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 16:15:04 -0300 Subject: [PATCH 094/121] Implemented a temporary solution to convert file names to media types for image files. --- aider/coders/base_coder.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index c7f509184..9c6a5bb8f 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -575,6 +575,7 @@ class Coder: image_messages = [] for fname, content in self.get_abs_fnames_content(): if is_image_file(fname): + # todo: use a proper tool to convert fname to media type image_url = f"data:image/{Path(fname).suffix.lstrip('.')};base64,{content}" image_messages.append( {"type": "image_url", "image_url": {"url": image_url, "detail": "high"}} From 790c2f03306a31de442ed3a478eca6554d4938bd Mon Sep 17 00:00:00 2001 From: "Paul Gauthier (aider)" Date: Wed, 3 Jul 2024 16:15:07 -0300 Subject: [PATCH 095/121] Implemented a more robust method to determine the image file type using the `mimetypes` module in the `get_images_message` function. --- aider/coders/base_coder.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 9c6a5bb8f..67df194f9 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -2,6 +2,7 @@ import hashlib import json +import mimetypes import os import re import sys @@ -575,11 +576,12 @@ class Coder: image_messages = [] for fname, content in self.get_abs_fnames_content(): if is_image_file(fname): - # todo: use a proper tool to convert fname to media type - image_url = f"data:image/{Path(fname).suffix.lstrip('.')};base64,{content}" - image_messages.append( - {"type": "image_url", "image_url": {"url": image_url, "detail": "high"}} - ) + mime_type, _ = mimetypes.guess_type(fname) + if mime_type and mime_type.startswith("image/"): + image_url = f"data:{mime_type};base64,{content}" + image_messages.append( + {"type": "image_url", "image_url": {"url": image_url, "detail": "high"}} + ) if not image_messages: return None From 362eb1aa07c676b03f23cc8766a5081236e48fca Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 16:56:15 -0300 Subject: [PATCH 096/121] Fix bad Model.info #788 --- aider/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aider/models.py b/aider/models.py index e9c1fb625..44acdb95f 100644 --- a/aider/models.py +++ b/aider/models.py @@ -402,9 +402,9 @@ class Model: # Do it the slow way... try: - self.info = litellm.get_model_info(model) + return litellm.get_model_info(model) except Exception: - self.info = dict() + return dict() def configure_model_settings(self, model): for ms in MODEL_SETTINGS: From d403f37469b2ed7d9805393a2c681117a9487bcf Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 17:06:10 -0300 Subject: [PATCH 097/121] cleanup --- aider/repomap.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aider/repomap.py b/aider/repomap.py index 554286ad9..381a61d46 100644 --- a/aider/repomap.py +++ b/aider/repomap.py @@ -31,7 +31,6 @@ class RepoMap: cache_missing = False warned_files = set() - nx = None def __init__( self, From 60e838df9faf01c913045661fe39630065c13165 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 19:49:37 -0300 Subject: [PATCH 098/121] roughed in faster completions --- aider/commands.py | 52 +++++++++++++++++++++-------------------------- aider/io.py | 40 ++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index b3101c82c..3012e2b56 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -5,7 +5,6 @@ import sys from pathlib import Path import git -from prompt_toolkit.completion import Completion from aider import models, prompts, voice from aider.litellm import litellm @@ -41,11 +40,9 @@ class Commands: models.sanity_check_models(self.io, model) raise SwitchModel(model) - def completions_model(self, partial): - models = litellm.model_cost.keys() - for model in models: - if partial.lower() in model.lower(): - yield Completion(model, start_position=-len(partial)) + def completions_model(self): + models = [] # litellm.model_cost.keys() + return models def cmd_models(self, args): "Search the list of available models" @@ -82,21 +79,23 @@ class Commands: def is_command(self, inp): return inp[0] in "/!" - def get_commands(self): - commands = [] + def get_commands(self, with_completions=False): + commands = dict() for attr in dir(self): - if attr.startswith("cmd_"): - commands.append("/" + attr[4:]) + if not attr.startswith("cmd_"): + continue + + cmd = attr[4:] + completions = getattr(self, f"completions_{cmd}", None) + if completions and with_completions: + completions = sorted(completions()) + else: + completions = [] + + commands["/" + cmd] = completions return commands - def get_command_completions(self, cmd_name, partial): - cmd_completions_method_name = f"completions_{cmd_name}" - cmd_completions_method = getattr(self, cmd_completions_method_name, None) - if cmd_completions_method: - for completion in cmd_completions_method(partial): - yield completion - def do_run(self, cmd_name, args): cmd_method_name = f"cmd_{cmd_name}" cmd_method = getattr(self, cmd_method_name, None) @@ -113,7 +112,7 @@ class Commands: first_word = words[0] rest_inp = inp[len(words[0]) :] - all_commands = self.get_commands() + all_commands = self.get_commands().keys() matching_commands = [cmd for cmd in all_commands if cmd.startswith(first_word)] return matching_commands, first_word, rest_inp @@ -377,12 +376,10 @@ class Commands: fname = f'"{fname}"' return fname - def completions_add(self, partial): + def completions_add(self): files = set(self.coder.get_all_relative_files()) files = files - set(self.coder.get_inchat_relative_files()) - for fname in files: - if partial.lower() in fname.lower(): - yield Completion(self.quote_fname(fname), start_position=-len(partial)) + return files def glob_filtered_to_repo(self, pattern): try: @@ -483,12 +480,9 @@ class Commands: reply = prompts.added_files.format(fnames=", ".join(added_fnames)) return reply - def completions_drop(self, partial): + def completions_drop(self): files = self.coder.get_inchat_relative_files() - - for fname in files: - if partial.lower() in fname.lower(): - yield Completion(self.quote_fname(fname), start_position=-len(partial)) + return files def cmd_drop(self, args=""): "Remove files from the chat session to free up context space" @@ -624,7 +618,7 @@ class Commands: def cmd_help(self, args): "Show help about all commands" - commands = sorted(self.get_commands()) + commands = sorted(self.get_commands().keys()) for cmd in commands: cmd_method_name = f"cmd_{cmd[1:]}" cmd_method = getattr(self, cmd_method_name, None) @@ -638,7 +632,7 @@ class Commands: "Show help about all commands in markdown" res = "" - commands = sorted(self.get_commands()) + commands = sorted(self.get_commands().keys()) for cmd in commands: cmd_method_name = f"cmd_{cmd[1:]}" cmd_method = getattr(self, cmd_method_name, None) diff --git a/aider/io.py b/aider/io.py index 1cf09f41f..b08033df1 100644 --- a/aider/io.py +++ b/aider/io.py @@ -23,7 +23,6 @@ from .utils import is_image_file class AutoCompleter(Completer): def __init__(self, root, rel_fnames, addable_rel_fnames, commands, encoding): - self.commands = commands self.addable_rel_fnames = addable_rel_fnames self.rel_fnames = rel_fnames self.encoding = encoding @@ -37,6 +36,13 @@ class AutoCompleter(Completer): self.words = set() + import time + + start = time.time() + self.commands = commands.get_commands(with_completions=True) + dur = time.time() - start + dump(dur) + for rel_fname in addable_rel_fnames: self.words.add(rel_fname) @@ -64,16 +70,24 @@ class AutoCompleter(Completer): if text[0] == "/": if len(words) == 1 and not text[-1].isspace(): - candidates = self.commands.get_commands() - candidates = [(cmd, cmd) for cmd in candidates] - else: - for completion in self.commands.get_command_completions(words[0][1:], words[-1]): - yield completion - return - else: - candidates = self.words - candidates.update(set(self.fname_to_rel_fnames)) - candidates = [(word, f"`{word}`") for word in candidates] + partial = words[0] + candidates = self.commands.keys() + for cmd in candidates: + if cmd.startswith(partial): + yield Completion(cmd, start_position=-len(partial)) + elif len(words) > 1: + cmd = words[0] + partial = words[-1] + + candidates = self.commands.get(cmd, []) + for word in candidates: + if partial in word: + yield Completion(word, start_position=-len(partial)) + return + + candidates = self.words + candidates.update(set(self.fname_to_rel_fnames)) + candidates = [(word, f"`{word}`") for word in candidates] last_word = words[-1] for word_match, word_insert in candidates: @@ -277,8 +291,8 @@ class InputOutput: def log_llm_history(self, role, content): if not self.llm_history_file: return - timestamp = datetime.now().isoformat(timespec='seconds') - with open(self.llm_history_file, 'a', encoding=self.encoding) as log_file: + timestamp = datetime.now().isoformat(timespec="seconds") + with open(self.llm_history_file, "a", encoding=self.encoding) as log_file: log_file.write(f"{role.upper()} {timestamp}\n") log_file.write(content + "\n") From 4b0192ffd9301e5eb09de9411e9e70483324783b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 20:08:51 -0300 Subject: [PATCH 099/121] cache the completions in AutoCompleter --- aider/commands.py | 30 ++++++++++++++++-------------- aider/io.py | 20 ++++++++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 3012e2b56..ca6875986 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -41,7 +41,7 @@ class Commands: raise SwitchModel(model) def completions_model(self): - models = [] # litellm.model_cost.keys() + models = litellm.model_cost.keys() return models def cmd_models(self, args): @@ -79,20 +79,22 @@ class Commands: def is_command(self, inp): return inp[0] in "/!" - def get_commands(self, with_completions=False): - commands = dict() + def get_completions(self, cmd): + assert cmd.startswith("/") + cmd = cmd[1:] + + fun = getattr(self, f"completions_{cmd}", None) + if not fun: + return [] + return sorted(fun()) + + def get_commands(self): + commands = [] for attr in dir(self): if not attr.startswith("cmd_"): continue - cmd = attr[4:] - completions = getattr(self, f"completions_{cmd}", None) - if completions and with_completions: - completions = sorted(completions()) - else: - completions = [] - - commands["/" + cmd] = completions + commands.append("/" + cmd) return commands @@ -112,7 +114,7 @@ class Commands: first_word = words[0] rest_inp = inp[len(words[0]) :] - all_commands = self.get_commands().keys() + all_commands = self.get_commands() matching_commands = [cmd for cmd in all_commands if cmd.startswith(first_word)] return matching_commands, first_word, rest_inp @@ -618,7 +620,7 @@ class Commands: def cmd_help(self, args): "Show help about all commands" - commands = sorted(self.get_commands().keys()) + commands = sorted(self.get_commands()) for cmd in commands: cmd_method_name = f"cmd_{cmd[1:]}" cmd_method = getattr(self, cmd_method_name, None) @@ -632,7 +634,7 @@ class Commands: "Show help about all commands in markdown" res = "" - commands = sorted(self.get_commands().keys()) + commands = sorted(self.get_commands()) for cmd in commands: cmd_method_name = f"cmd_{cmd[1:]}" cmd_method = getattr(self, cmd_method_name, None) diff --git a/aider/io.py b/aider/io.py index b08033df1..160c855a2 100644 --- a/aider/io.py +++ b/aider/io.py @@ -36,12 +36,9 @@ class AutoCompleter(Completer): self.words = set() - import time - - start = time.time() - self.commands = commands.get_commands(with_completions=True) - dur = time.time() - start - dump(dur) + self.commands = commands + self.command_names = self.commands.get_commands() + self.command_completions = dict() for rel_fname in addable_rel_fnames: self.words.add(rel_fname) @@ -71,7 +68,7 @@ class AutoCompleter(Completer): if text[0] == "/": if len(words) == 1 and not text[-1].isspace(): partial = words[0] - candidates = self.commands.keys() + candidates = self.command_names for cmd in candidates: if cmd.startswith(partial): yield Completion(cmd, start_position=-len(partial)) @@ -79,7 +76,14 @@ class AutoCompleter(Completer): cmd = words[0] partial = words[-1] - candidates = self.commands.get(cmd, []) + if cmd not in self.command_names: + return + if cmd not in self.command_completions: + candidates = self.commands.get_completions(cmd) + self.command_completions[cmd] = candidates + else: + candidates = self.command_completions[cmd] + for word in candidates: if partial in word: yield Completion(word, start_position=-len(partial)) From 3c1a6acc0dd5a247e66cd23856a156ab083887b4 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 20:12:04 -0300 Subject: [PATCH 100/121] handle files with spaces --- aider/commands.py | 2 ++ aider/io.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/aider/commands.py b/aider/commands.py index ca6875986..ac1f07bf4 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -381,6 +381,7 @@ class Commands: def completions_add(self): files = set(self.coder.get_all_relative_files()) files = files - set(self.coder.get_inchat_relative_files()) + files = [self.quote_fname(fn) for fn in files] return files def glob_filtered_to_repo(self, pattern): @@ -484,6 +485,7 @@ class Commands: def completions_drop(self): files = self.coder.get_inchat_relative_files() + files = [self.quote_fname(fn) for fn in files] return files def cmd_drop(self, args=""): diff --git a/aider/io.py b/aider/io.py index 160c855a2..aaa34a07b 100644 --- a/aider/io.py +++ b/aider/io.py @@ -72,7 +72,7 @@ class AutoCompleter(Completer): for cmd in candidates: if cmd.startswith(partial): yield Completion(cmd, start_position=-len(partial)) - elif len(words) > 1: + elif len(words) > 1 and not text[-1].isspace(): cmd = words[0] partial = words[-1] From aaceec1e1ff8ff77f231b0094131f20fac16528a Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 21:02:26 -0300 Subject: [PATCH 101/121] fix for tests --- aider/io.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aider/io.py b/aider/io.py index aaa34a07b..fc7e56f99 100644 --- a/aider/io.py +++ b/aider/io.py @@ -37,8 +37,9 @@ class AutoCompleter(Completer): self.words = set() self.commands = commands - self.command_names = self.commands.get_commands() self.command_completions = dict() + if commands: + self.command_names = self.commands.get_commands() for rel_fname in addable_rel_fnames: self.words.add(rel_fname) From 9d02628cf87c8d52e0ab5616fa7d6cefc725da35 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 21:32:50 -0300 Subject: [PATCH 102/121] streamlit borks sys.path, causes import("litellm") to load our litellm.py; fix --- aider/coders/base_coder.py | 2 +- aider/commands.py | 2 +- aider/{litellm.py => llm.py} | 3 ++- aider/main.py | 4 ++-- aider/models.py | 2 +- aider/sendchat.py | 4 ++-- aider/tests/test_sendchat.py | 2 +- aider/voice.py | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) rename aider/{litellm.py => llm.py} (87%) diff --git a/aider/coders/base_coder.py b/aider/coders/base_coder.py index 67df194f9..662bd7912 100755 --- a/aider/coders/base_coder.py +++ b/aider/coders/base_coder.py @@ -22,7 +22,7 @@ from aider.commands import Commands from aider.history import ChatSummary from aider.io import InputOutput from aider.linter import Linter -from aider.litellm import litellm +from aider.llm import litellm from aider.mdstream import MarkdownStream from aider.repo import GitRepo from aider.repomap import RepoMap diff --git a/aider/commands.py b/aider/commands.py index ac1f07bf4..fd129dc3c 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -7,7 +7,7 @@ from pathlib import Path import git from aider import models, prompts, voice -from aider.litellm import litellm +from aider.llm import litellm from aider.scrape import Scraper from aider.utils import is_image_file diff --git a/aider/litellm.py b/aider/llm.py similarity index 87% rename from aider/litellm.py rename to aider/llm.py index 4c97c0a94..73f7a25a5 100644 --- a/aider/litellm.py +++ b/aider/llm.py @@ -1,3 +1,4 @@ +import importlib import os import warnings @@ -15,7 +16,7 @@ class LazyLiteLLM: def __getattr__(self, name): if self._lazy_module is None: - self._lazy_module = __import__("litellm") + self._lazy_module = importlib.import_module("litellm") self._lazy_module.suppress_debug_info = True self._lazy_module.set_verbose = False diff --git a/aider/main.py b/aider/main.py index 88ea47f98..078fa0540 100644 --- a/aider/main.py +++ b/aider/main.py @@ -14,7 +14,7 @@ from aider.args import get_parser from aider.coders import Coder from aider.commands import SwitchModel from aider.io import InputOutput -from aider.litellm import litellm # noqa: F401; properly init litellm on launch +from aider.llm import litellm # noqa: F401; properly init litellm on launch from aider.repo import GitRepo from aider.versioncheck import check_version @@ -249,7 +249,7 @@ def register_models(git_root, model_settings_fname, io): 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 + ".aider.llm.models.json", git_root, model_metadata_fname ) try: diff --git a/aider/models.py b/aider/models.py index 44acdb95f..0667f8183 100644 --- a/aider/models.py +++ b/aider/models.py @@ -13,7 +13,7 @@ from PIL import Image from aider import urls from aider.dump import dump # noqa: F401 -from aider.litellm import litellm +from aider.llm import litellm DEFAULT_MODEL_NAME = "gpt-4o" diff --git a/aider/sendchat.py b/aider/sendchat.py index bb0030c3a..1123fe78c 100644 --- a/aider/sendchat.py +++ b/aider/sendchat.py @@ -4,7 +4,7 @@ import json import backoff from aider.dump import dump # noqa: F401 -from aider.litellm import litellm +from aider.llm import litellm # from diskcache import Cache @@ -57,7 +57,7 @@ def lazy_litellm_retry_decorator(func): @lazy_litellm_retry_decorator def send_with_retries(model_name, messages, functions, stream, temperature=0): - from aider.litellm import litellm + from aider.llm import litellm kwargs = dict( model=model_name, diff --git a/aider/tests/test_sendchat.py b/aider/tests/test_sendchat.py index 7f9ce274d..6ac02a47b 100644 --- a/aider/tests/test_sendchat.py +++ b/aider/tests/test_sendchat.py @@ -3,7 +3,7 @@ from unittest.mock import MagicMock, patch import httpx -from aider.litellm import litellm +from aider.llm import litellm from aider.sendchat import send_with_retries diff --git a/aider/voice.py b/aider/voice.py index 416917dee..46568e8e8 100644 --- a/aider/voice.py +++ b/aider/voice.py @@ -4,7 +4,7 @@ import queue import tempfile import time -from aider.litellm import litellm +from aider.llm import litellm try: import soundfile as sf From 644ec6f964e903e5706f8a4af4d0d888f97feedd Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 21:37:05 -0300 Subject: [PATCH 103/121] make test for playwright more robust #791 --- aider/scrape.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aider/scrape.py b/aider/scrape.py index 0ed64fc4c..0d5081726 100755 --- a/aider/scrape.py +++ b/aider/scrape.py @@ -91,12 +91,12 @@ class Scraper: if self.playwright_available is not None: return - with sync_playwright() as p: - try: + try: + with sync_playwright() as p: p.chromium.launch() self.playwright_available = True - except Exception: - self.playwright_available = False + except Exception: + self.playwright_available = False def get_playwright_instructions(self): if self.playwright_available in (True, None): From 499057c34586a73f79ed465be4a818645856043b Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 21:42:12 -0300 Subject: [PATCH 104/121] copy --- scripts/jekyll_run.sh | 3 ++- website/docs/unified-diffs.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/jekyll_run.sh b/scripts/jekyll_run.sh index d50ade007..a2ea847d5 100755 --- a/scripts/jekyll_run.sh +++ b/scripts/jekyll_run.sh @@ -6,7 +6,8 @@ docker run \ -v "$PWD/website:/site" \ -p 4000:4000 \ -e HISTFILE=/site/.bash_history \ - --entrypoint /bin/bash \ -it \ my-jekyll-site +# --entrypoint /bin/bash \ + diff --git a/website/docs/unified-diffs.md b/website/docs/unified-diffs.md index 9f745969d..67dd58e63 100644 --- a/website/docs/unified-diffs.md +++ b/website/docs/unified-diffs.md @@ -5,7 +5,8 @@ highlight_image: /assets/benchmarks-udiff.jpg nav_exclude: true --- {% if page.date %} - + {% endif %} # Unified diffs make GPT-4 Turbo 3X less lazy From 2d6af68a07c51be35a0b17a7f31db61baf833c24 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Wed, 3 Jul 2024 21:53:04 -0300 Subject: [PATCH 105/121] lowercase h4 in chat transcripts --- website/_sass/custom/custom.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/website/_sass/custom/custom.scss b/website/_sass/custom/custom.scss index fd9175385..015917fb7 100644 --- a/website/_sass/custom/custom.scss +++ b/website/_sass/custom/custom.scss @@ -77,6 +77,7 @@ color: #32FF32; border-top: 1px solid #32FF32; padding-top: 10px; + text-transform: none; } .chat-transcript h4::before { From f74eab694a27d49da2bbd89678273d81e0318613 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 09:16:56 -0300 Subject: [PATCH 106/121] copy --- website/_posts/2024-07-01-sonnet-not-lazy.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/_posts/2024-07-01-sonnet-not-lazy.md b/website/_posts/2024-07-01-sonnet-not-lazy.md index 27a93e441..d80870d5c 100644 --- a/website/_posts/2024-07-01-sonnet-not-lazy.md +++ b/website/_posts/2024-07-01-sonnet-not-lazy.md @@ -116,11 +116,11 @@ for more details, but you can get started quickly with aider and Sonnet like this: ``` -pip install aider-chat +$ pip install aider-chat -export ANTHROPIC_API_KEY= # Mac/Linux -setx ANTHROPIC_API_KEY # Windows +$ export ANTHROPIC_API_KEY= # Mac/Linux +$ setx ANTHROPIC_API_KEY # Windows -aider +$ aider ``` From 4da8541c1be93627c8a9ebb54f7ce22d29c3cfd8 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:20:40 -0300 Subject: [PATCH 107/121] Updated HISTORY --- HISTORY.md | 6 ++++++ website/HISTORY.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 55ff8cb57..3f5fffda1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,12 @@ # Release history +### Aider v0.42.0 + +- Performance release: + - 5X faster launch! + - Faster auto-complete in large git repos (users report ~100X speedup)! + ### Aider v0.41.0 - [Allow Claude 3.5 Sonnet to stream back >4k tokens!](https://aider.chat/2024/07/01/sonnet-not-lazy.html) diff --git a/website/HISTORY.md b/website/HISTORY.md index c8da5ea82..03ad6a5bb 100644 --- a/website/HISTORY.md +++ b/website/HISTORY.md @@ -12,6 +12,12 @@ cog.out(text) # Release history +### Aider v0.42.0 + +- Performance release: + - 5X faster launch! + - Faster auto-complete in large git repos (users report ~100X speedup)! + ### Aider v0.41.0 - [Allow Claude 3.5 Sonnet to stream back >4k tokens!](https://aider.chat/2024/07/01/sonnet-not-lazy.html) From 5f6ed30e11c6f4cd66415d6f362a1f06526003da Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:21:57 -0300 Subject: [PATCH 108/121] swallow exceptions in load_slow_imports --- aider/main.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aider/main.py b/aider/main.py index 078fa0540..5d62903e5 100644 --- a/aider/main.py +++ b/aider/main.py @@ -556,10 +556,13 @@ def load_slow_imports(): # improve startup time. # This func is called in a thread to load them in the background # while we wait for the user to type their first message. - import httpx # noqa: F401 - import litellm # noqa: F401 - import networkx # noqa: F401 - import numpy # noqa: F401 + try: + import httpx # noqa: F401 + import litellm # noqa: F401 + import networkx # noqa: F401 + import numpy # noqa: F401 + except Exception: + pass if __name__ == "__main__": From 13b03ce57b6a9fa85a49be969d1cac452cff5ca7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:22:28 -0300 Subject: [PATCH 109/121] version bump to 0.42.0 --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index aef8c9771..92717f71e 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.41.1-dev" +__version__ = "0.42.0" From 35f21b5f4d356752ccbf3e7dea5fc36c2fb4aa15 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:23:04 -0300 Subject: [PATCH 110/121] set version to 0.42.1-dev --- aider/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aider/__init__.py b/aider/__init__.py index 92717f71e..8c9ed5283 100644 --- a/aider/__init__.py +++ b/aider/__init__.py @@ -1 +1 @@ -__version__ = "0.42.0" +__version__ = "0.42.1-dev" From b64d6c187bca21c080370083615a381128f3cb71 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:44:35 -0300 Subject: [PATCH 111/121] updated sonnet leaderboard --- website/_data/edit_leaderboard.yml | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index b2c1fc469..2c8f33a3f 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -607,35 +607,35 @@ indentation_errors: 0 exhausted_context_windows: 0 test_timeouts: 0 - command: aider --model openrouter/anthropic/claude-3.5-sonnet --edit-format whole + command: aider --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 +- dirname: 2024-07-04-14-32-08--claude-3.5-sonnet-diff-continue test_cases: 133 - model: claude-3.5-sonnet (diff) + model: openrouter/anthropic/claude-3.5-sonnet 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 + commit_hash: 35f21b5 + pass_rate_1: 57.1 + pass_rate_2: 77.4 + percent_cases_well_formed: 99.2 + error_outputs: 23 + num_malformed_responses: 4 + num_with_malformed_responses: 1 + user_asks: 2 lazy_comments: 0 - syntax_errors: 0 + syntax_errors: 1 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 - + command: aider --sonnet + date: 2024-07-04 + versions: 0.42.1-dev + seconds_per_case: 17.6 + total_cost: 3.6346 + - dirname: 2024-06-17-14-45-54--deepseek-coder2-whole test_cases: 133 model: DeepSeek Coder V2 (whole) From 9d66cdecda0a9ff270ffadc3bd1378fdebd6d0ff Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:44:50 -0300 Subject: [PATCH 112/121] Renamed .aider.model... files --- aider/args.py | 4 ++-- aider/main.py | 4 ++-- website/docs/config/adv-model-settings.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aider/args.py b/aider/args.py index a2c28a099..eadc2b75c 100644 --- a/aider/args.py +++ b/aider/args.py @@ -142,13 +142,13 @@ def get_parser(default_config_files, git_root): group.add_argument( "--model-settings-file", metavar="MODEL_SETTINGS_FILE", - default=None, + default=".aider.model.settings.yml", help="Specify a file with aider model settings for unknown models", ) group.add_argument( "--model-metadata-file", metavar="MODEL_METADATA_FILE", - default=None, + default=".aider.model.metadata.json", help="Specify a file with context window and costs for unknown models", ) group.add_argument( diff --git a/aider/main.py b/aider/main.py index 5d62903e5..a8ad43326 100644 --- a/aider/main.py +++ b/aider/main.py @@ -231,7 +231,7 @@ def generate_search_path_list(default_fname, git_root, command_line_file): def register_models(git_root, model_settings_fname, io): model_settings_files = generate_search_path_list( - ".aider.models.yml", git_root, model_settings_fname + ".aider.model.settings.yml", git_root, model_settings_fname ) try: @@ -249,7 +249,7 @@ def register_models(git_root, model_settings_fname, io): def register_litellm_models(git_root, model_metadata_fname, io): model_metatdata_files = generate_search_path_list( - ".aider.llm.models.json", git_root, model_metadata_fname + ".aider.model.metadata.json", git_root, model_metadata_fname ) try: diff --git a/website/docs/config/adv-model-settings.md b/website/docs/config/adv-model-settings.md index 41f1be9da..6cf7b9909 100644 --- a/website/docs/config/adv-model-settings.md +++ b/website/docs/config/adv-model-settings.md @@ -12,7 +12,7 @@ In most cases, you can safely ignore aider's warning about unknown context window size and model costs. But, you can register context window limits and costs for models that aren't known -to aider. Create a `.aider.litellm.models.json` file in one of these locations: +to aider. Create a `.aider.model.metadata.json` file in one of these locations: - Your home directory. - The root if your git repo. @@ -51,7 +51,7 @@ But it can sometimes be helpful to override them or add settings for a model that aider doesn't know about. To do that, -create a `.aider.models.yml` file in one of these locations: +create a `.aider.model.settings.yml` file in one of these locations: - Your home directory. - The root if your git repo. From 4642596395ab901351b1f5e7c19caa1519ae21f7 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:45:33 -0300 Subject: [PATCH 113/121] copy --- website/assets/sample.aider.conf.yml | 4 ++-- website/assets/sample.env | 4 ++-- website/docs/config/aider_conf.md | 4 ++-- website/docs/config/dotenv.md | 4 ++-- website/docs/config/options.md | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/website/assets/sample.aider.conf.yml b/website/assets/sample.aider.conf.yml index 8aaac6dc0..afadcbecb 100644 --- a/website/assets/sample.aider.conf.yml +++ b/website/assets/sample.aider.conf.yml @@ -62,10 +62,10 @@ #openai-organization-id: ## Specify a file with aider model settings for unknown models -#model-settings-file: +#model-settings-file: .aider.model.settings.yml ## Specify a file with context window and costs for unknown models -#model-metadata-file: +#model-metadata-file: .aider.model.metadata.json ## Verify the SSL cert when connecting to models (default: True) #verify-ssl: true diff --git a/website/assets/sample.env b/website/assets/sample.env index e14e2c1d0..f18c917e1 100644 --- a/website/assets/sample.env +++ b/website/assets/sample.env @@ -70,10 +70,10 @@ #OPENAI_ORGANIZATION_ID= ## Specify a file with aider model settings for unknown models -#AIDER_MODEL_SETTINGS_FILE= +#AIDER_MODEL_SETTINGS_FILE=.aider.model.settings.yml ## Specify a file with context window and costs for unknown models -#AIDER_MODEL_METADATA_FILE= +#AIDER_MODEL_METADATA_FILE=.aider.model.metadata.json ## Verify the SSL cert when connecting to models (default: True) #AIDER_VERIFY_SSL=true diff --git a/website/docs/config/aider_conf.md b/website/docs/config/aider_conf.md index 977d5c26b..a3c2bd48b 100644 --- a/website/docs/config/aider_conf.md +++ b/website/docs/config/aider_conf.md @@ -90,10 +90,10 @@ cog.outl("```") #openai-organization-id: ## Specify a file with aider model settings for unknown models -#model-settings-file: +#model-settings-file: .aider.model.settings.yml ## Specify a file with context window and costs for unknown models -#model-metadata-file: +#model-metadata-file: .aider.model.metadata.json ## Verify the SSL cert when connecting to models (default: True) #verify-ssl: true diff --git a/website/docs/config/dotenv.md b/website/docs/config/dotenv.md index e0d2b00c6..b452506c1 100644 --- a/website/docs/config/dotenv.md +++ b/website/docs/config/dotenv.md @@ -103,10 +103,10 @@ cog.outl("```") #OPENAI_ORGANIZATION_ID= ## Specify a file with aider model settings for unknown models -#AIDER_MODEL_SETTINGS_FILE= +#AIDER_MODEL_SETTINGS_FILE=.aider.model.settings.yml ## Specify a file with context window and costs for unknown models -#AIDER_MODEL_METADATA_FILE= +#AIDER_MODEL_METADATA_FILE=.aider.model.metadata.json ## Verify the SSL cert when connecting to models (default: True) #AIDER_VERIFY_SSL=true diff --git a/website/docs/config/options.md b/website/docs/config/options.md index 58558a5b8..4d17386f5 100644 --- a/website/docs/config/options.md +++ b/website/docs/config/options.md @@ -135,10 +135,12 @@ Environment variable: `OPENAI_ORGANIZATION_ID` ### `--model-settings-file MODEL_SETTINGS_FILE` Specify a file with aider model settings for unknown models +Default: .aider.model.settings.yml Environment variable: `AIDER_MODEL_SETTINGS_FILE` ### `--model-metadata-file MODEL_METADATA_FILE` Specify a file with context window and costs for unknown models +Default: .aider.model.metadata.json Environment variable: `AIDER_MODEL_METADATA_FILE` ### `--verify-ssl` From 08868fd4375e381eb4424b0a327610341ae22f29 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 11:57:57 -0300 Subject: [PATCH 114/121] copy --- website/_data/edit_leaderboard.yml | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index 2c8f33a3f..bc0260b13 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -590,32 +590,9 @@ seconds_per_case: 280.6 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 --sonnet --edit-format whole - date: 2024-06-20 - versions: 0.38.1-dev - seconds_per_case: 15.4 - total_cost: 0.0000 - - dirname: 2024-07-04-14-32-08--claude-3.5-sonnet-diff-continue test_cases: 133 - model: openrouter/anthropic/claude-3.5-sonnet + model: claude-3.5-sonnet edit_format: diff commit_hash: 35f21b5 pass_rate_1: 57.1 From ddad0d932528e6480bc0588c4149e1f58a4699eb Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:10:56 -0300 Subject: [PATCH 115/121] added DeepSeek Coder V2 (128k context) to refac leaderboard --- website/_data/edit_leaderboard.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index bc0260b13..e2af02d72 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -681,4 +681,25 @@ versions: 0.41.1-dev seconds_per_case: 7.1 total_cost: 0.1946 - \ No newline at end of file + +- dirname: 2024-07-04-15-06-43--refac-deepseek-coder2-128k + test_cases: 89 + model: DeepSeek Coder V2 (128k context) + edit_format: diff + commit_hash: 08868fd + pass_rate_1: 38.2 + percent_cases_well_formed: 73.0 + error_outputs: 393 + num_malformed_responses: 89 + num_with_malformed_responses: 24 + user_asks: 4 + lazy_comments: 2 + syntax_errors: 1 + indentation_errors: 5 + exhausted_context_windows: 3 + test_timeouts: 0 + command: aider --model openrouter/deepseek/deepseek-coder + date: 2024-07-04 + versions: 0.42.1-dev + seconds_per_case: 82.9 + total_cost: 0.2601 \ No newline at end of file From 781050b3b9debf236bccd8a07d1df95ece696038 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:14:41 -0300 Subject: [PATCH 116/121] copy --- website/_data/edit_leaderboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index e2af02d72..c0287368d 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -698,7 +698,7 @@ indentation_errors: 5 exhausted_context_windows: 3 test_timeouts: 0 - command: aider --model openrouter/deepseek/deepseek-coder + command: aider --model deepseek/deepseek-coder date: 2024-07-04 versions: 0.42.1-dev seconds_per_case: 82.9 From 773241db0685f7b44a3b948e024e0e769c79f4c3 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:16:01 -0300 Subject: [PATCH 117/121] copy --- website/_data/edit_leaderboard.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index c0287368d..00811ba89 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -661,7 +661,7 @@ - dirname: 2024-07-01-21-41-48--haiku-whole test_cases: 133 - model: openrouter/anthropic/claude-3-haiku + model: claude-3-haiku-20240307 edit_format: whole commit_hash: 75f506d pass_rate_1: 40.6 @@ -676,7 +676,7 @@ indentation_errors: 0 exhausted_context_windows: 0 test_timeouts: 2 - command: aider --model openrouter/anthropic/claude-3-haiku + command: aider --model claude-3-haiku-20240307 date: 2024-07-01 versions: 0.41.1-dev seconds_per_case: 7.1 From 94c297578e9c3b78fe676c42cc955b2f55d54857 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:16:34 -0300 Subject: [PATCH 118/121] copy --- website/_data/edit_leaderboard.yml | 23 +---------------------- website/_data/refactor_leaderboard.yml | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/website/_data/edit_leaderboard.yml b/website/_data/edit_leaderboard.yml index 00811ba89..dacc0aa40 100644 --- a/website/_data/edit_leaderboard.yml +++ b/website/_data/edit_leaderboard.yml @@ -681,25 +681,4 @@ versions: 0.41.1-dev seconds_per_case: 7.1 total_cost: 0.1946 - -- dirname: 2024-07-04-15-06-43--refac-deepseek-coder2-128k - test_cases: 89 - model: DeepSeek Coder V2 (128k context) - edit_format: diff - commit_hash: 08868fd - pass_rate_1: 38.2 - percent_cases_well_formed: 73.0 - error_outputs: 393 - num_malformed_responses: 89 - num_with_malformed_responses: 24 - user_asks: 4 - lazy_comments: 2 - syntax_errors: 1 - indentation_errors: 5 - exhausted_context_windows: 3 - test_timeouts: 0 - command: aider --model deepseek/deepseek-coder - date: 2024-07-04 - versions: 0.42.1-dev - seconds_per_case: 82.9 - total_cost: 0.2601 \ No newline at end of file + \ No newline at end of file diff --git a/website/_data/refactor_leaderboard.yml b/website/_data/refactor_leaderboard.yml index 8a4aacfda..55205de35 100644 --- a/website/_data/refactor_leaderboard.yml +++ b/website/_data/refactor_leaderboard.yml @@ -159,9 +159,32 @@ indentation_errors: 0 exhausted_context_windows: 0 test_timeouts: 0 - command: aider --model openrouter/anthropic/claude-3.5-sonnet + command: aider --sonnet date: 2024-07-01 versions: 0.40.7-dev seconds_per_case: 42.8 total_cost: 11.5242 + + +- dirname: 2024-07-04-15-06-43--refac-deepseek-coder2-128k + test_cases: 89 + model: DeepSeek Coder V2 (128k context) + edit_format: diff + commit_hash: 08868fd + pass_rate_1: 38.2 + percent_cases_well_formed: 73.0 + error_outputs: 393 + num_malformed_responses: 89 + num_with_malformed_responses: 24 + user_asks: 4 + lazy_comments: 2 + syntax_errors: 1 + indentation_errors: 5 + exhausted_context_windows: 3 + test_timeouts: 0 + command: aider --model deepseek/deepseek-coder + date: 2024-07-04 + versions: 0.42.1-dev + seconds_per_case: 82.9 + total_cost: 0.2601 \ No newline at end of file From 3516bc0a450f0569989f0f34546543c3d97780d5 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:17:51 -0300 Subject: [PATCH 119/121] copy --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9a5d29dac..a682c458a 100644 --- a/README.md +++ b/README.md @@ -121,4 +121,5 @@ projects like django, scikitlearn, matplotlib, etc. - *I am an aider addict. I'm getting so much more work done, but in less time.* -- [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470) - *After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever.* -- [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548) - *Hands down, this is the best AI coding assistant tool so far.* -- [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs) +- *[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life.* -- [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264) - *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20) From dd0aec8aa65cb6dde27b80eb436d4c684709f098 Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:21:36 -0300 Subject: [PATCH 120/121] uniqify model files before loading --- aider/main.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/aider/main.py b/aider/main.py index a8ad43326..a7220e6c9 100644 --- a/aider/main.py +++ b/aider/main.py @@ -223,6 +223,14 @@ def generate_search_path_list(default_fname, git_root, command_line_file): if command_line_file: files.append(command_line_file) files.append(default_file.resolve()) + files = [Path(fn).resolve() for fn in files] + files.reverse() + uniq = [] + for fn in files: + if fn not in uniq: + uniq.append(fn) + uniq.reverse() + files = uniq files = list(map(str, files)) files = list(dict.fromkeys(files)) @@ -255,11 +263,11 @@ def register_litellm_models(git_root, model_metadata_fname, io): 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)") + io.tool_output(f"Loaded {len(model_metadata_files_loaded)} model metadata 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}") + io.tool_error(f"Error loading model metadata models: {e}") return 1 From b61b394700756d40b0ccb686c7d8e4211b1677bf Mon Sep 17 00:00:00 2001 From: Paul Gauthier Date: Thu, 4 Jul 2024 13:21:53 -0300 Subject: [PATCH 121/121] copy --- website/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/index.md b/website/index.md index 35060ea1a..059d924e9 100644 --- a/website/index.md +++ b/website/index.md @@ -135,5 +135,6 @@ projects like django, scikitlearn, matplotlib, etc. - *I am an aider addict. I'm getting so much more work done, but in less time.* -- [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470) - *After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever.* -- [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548) - *Hands down, this is the best AI coding assistant tool so far.* -- [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs) +- *[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life.* -- [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264) - *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20)