Compare commits

..

134 commits

Author SHA1 Message Date
paul-gauthier
295122fc97
Merge pull request #4057 from emmanuel-ferdman/main
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
2025-06-02 14:03:17 -07:00
paul-gauthier
fa0aa9459b
Merge pull request #4156 from stackbuilders/always_pass_extra_headers_to_copilot_models
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
2025-06-02 07:06:10 -07:00
Sebastian Estrella
c67f6905a5 fix: Always pass extra_headers to Copilot models 2025-06-02 08:39:48 -05:00
paul-gauthier
3266eaca91
Merge pull request #4150 from muravvv/fix_encoding
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Fix issues on repositories with non-Unicode encodings
2025-06-01 12:46:17 -07:00
muravvv
bfaad12cac add missing encoding conversion for diff contents 2025-06-01 22:31:43 +03:00
muravvv
395188043b set fixed utf-8 encoding for llm history log 2025-06-01 20:15:51 +03:00
paul-gauthier
484b8a3603
Merge pull request #4146 from ktakayama/issues/4049-commit-language
Some checks failed
pre-commit / pre-commit (push) Waiting to run
Deploy Jekyll site to Pages / build (push) Has been cancelled
Deploy Jekyll site to Pages / deploy (push) Has been cancelled
2025-06-01 05:20:22 -07:00
Kyosuke Takayama
6eaf75f760
test: add test for commit-language option 2025-06-01 19:29:28 +09:00
Kyosuke Takayama
91f34e37f7
docs: add commit-language option to config and documentation files 2025-06-01 19:09:39 +09:00
Kyosuke Takayama
7ffd9c1859
feat: add commit language option for commit message localization 2025-06-01 18:52:38 +09:00
Paul Gauthier (aider)
0bb0f169d2 docs: Add link to release notes
Some checks failed
Deploy Jekyll site to Pages / build (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Deploy Jekyll site to Pages / deploy (push) Has been cancelled
2025-05-30 17:11:21 -07:00
Paul Gauthier
45ad3cdf47 copy 2025-05-30 16:32:21 -07:00
Paul Gauthier
fc30409f74 blame 2025-05-30 16:31:26 -07:00
Paul Gauthier
6d872b6dc0 copy 2025-05-30 16:30:10 -07:00
Paul Gauthier
6fdc956b9e set version to 0.84.1.dev 2025-05-30 16:27:25 -07:00
Paul Gauthier
196721d27d version bump to 0.84.0 2025-05-30 16:27:24 -07:00
Paul Gauthier (aider)
e331a967a6 fix: Update expected OpenRouter costs in tests 2025-05-30 15:57:45 -07:00
Paul Gauthier (aider)
48376e59c2 style: Apply formatting 2025-05-30 15:07:32 -07:00
Paul Gauthier (aider)
52510c7da5 test: Update OpenRouter default model expectations 2025-05-30 15:07:29 -07:00
Paul Gauthier
c24798c44f Merge branch 'main' of github.com:Aider-AI/aider 2025-05-30 14:46:36 -07:00
Paul Gauthier
6085be5883 copy 2025-05-30 14:46:28 -07:00
Paul Gauthier (aider)
05c56fe904 fix: Fix OpenRouter token cost calculation 2025-05-30 14:33:34 -07:00
Paul Gauthier
a7afbd0708 feat: Add claude-opus-4 and update default OpenRouter models 2025-05-30 14:30:10 -07:00
Paul Gauthier (aider)
3f2c403cf0 feat: Add openrouter/anthropic/claude-opus-4 model config 2025-05-30 14:26:50 -07:00
Paul Gauthier
d7504bed21 copy 2025-05-30 14:25:35 -07:00
paul-gauthier
119a44debe
Merge pull request #4114 from therealmarv/increase-deepseek-v3-openrouter-context
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
increase context window of Deepseek V3 to new OpenRouter limits
2025-05-27 13:33:42 -07:00
therealmarv
87dee0a5f2 reduce output tokens again 2025-05-27 23:18:45 +03:00
therealmarv
1d0e463d83 increase context window of Deepseek V3 to new OpenRouter limits 2025-05-27 23:02:11 +03:00
Paul Gauthier
8304029b92 lint
Some checks failed
pre-commit / pre-commit (push) Waiting to run
Deploy Jekyll site to Pages / build (push) Has been cancelled
Deploy Jekyll site to Pages / deploy (push) Has been cancelled
2025-05-26 16:29:28 -07:00
Paul Gauthier
ef2986a231 Merge branch 'main' of github.com:Aider-AI/aider 2025-05-26 16:29:14 -07:00
Paul Gauthier
b79a777936 copy 2025-05-26 16:29:10 -07:00
Paul Gauthier
9c9eedd9c5 chore: Update polyglot leaderboard data for gemini-2.5-flash 2025-05-26 12:18:22 -07:00
paul-gauthier
ebaad9d865
Merge pull request #4092 from noitcudni/main 2025-05-26 10:43:53 -07:00
paul-gauthier
d922023815
Merge pull request #4096 from KennyDizi/main 2025-05-26 10:41:33 -07:00
Paul Gauthier
acebc11237 chore: Update model names in polyglot leaderboard 2025-05-26 08:56:35 -07:00
Paul Gauthier
214b811ef9 chore: Add new polyglot benchmark results 2025-05-26 08:56:01 -07:00
Trung Dinh
de9df51b47 Change max_tokens to 32000 for claude-sonnet-4-20250514 family model 2025-05-26 11:17:50 +07:00
Paul Gauthier
3194a35230 feat: Add Gemini 2.5 Flash model and leaderboard data 2025-05-25 16:00:07 -07:00
Paul Gauthier
a8568c3c4f build: Update website source path in Dockerfile
Some checks are pending
Deploy Jekyll site to Pages / build (push) Waiting to run
Deploy Jekyll site to Pages / deploy (push) Blocked by required conditions
pre-commit / pre-commit (push) Waiting to run
2025-05-25 15:30:30 -07:00
Paul Gauthier (aider)
114ec42563 feat: Add Claude Opus 4 provider variants to model-settings.yml 2025-05-25 15:03:41 -07:00
Paul Gauthier (aider)
f7df96d224 feat: Add missing Sonnet 4 models to settings 2025-05-25 15:01:43 -07:00
Paul Gauthier
79edb0e1e0 added opus polyglot 2025-05-25 14:57:49 -07:00
Lih Chen
5a0951caaf Refresh Github's open api key automatically 2025-05-25 14:54:03 -07:00
Paul Gauthier (aider)
6b2bcf651e fix: Update expected model name for 'opus' alias test 2025-05-25 13:15:15 -07:00
Paul Gauthier (aider)
fea0ff189f test: Update sonnet model alias test 2025-05-25 13:14:47 -07:00
Paul Gauthier
803a8db60c noop 2025-05-25 12:52:47 -07:00
Paul Gauthier
414b4e3882 feat: Add new Claude models and update aliases 2025-05-25 12:51:45 -07:00
Paul Gauthier (aider)
a17599152f feat: Add Claude Sonnet 4 settings for multiple providers 2025-05-25 12:49:51 -07:00
Paul Gauthier
7b9d8e6ba7 feat: add claude-opus-4-20250514 model settings 2025-05-25 12:49:47 -07:00
Paul Gauthier
9ef3211365 proper pin for configargparse
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
2025-05-24 15:44:31 -07:00
Paul Gauthier
d9bf69041c Revert "bump deps"
This reverts commit ef3f8bb301.
2025-05-24 15:43:50 -07:00
Paul Gauthier
e3cb907767 claude-sonnet-4-20250514 ex-user 2025-05-24 15:06:57 -07:00
Paul Gauthier
ef3f8bb301 bump deps
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
2025-05-24 13:33:19 -07:00
Paul Gauthier
03a489ea35 set version to 0.83.3.dev
Some checks failed
pre-commit / pre-commit (push) Waiting to run
Deploy Jekyll site to Pages / deploy (push) Has been cancelled
Deploy Jekyll site to Pages / build (push) Has been cancelled
2025-05-23 16:02:30 -07:00
Paul Gauthier
81389b87d7 version bump to 0.83.2 2025-05-23 16:02:26 -07:00
Paul Gauthier
0d8ff295d6 copy 2025-05-23 15:48:36 -07:00
Paul Gauthier
6176a8dee3 Patch yanked configargparse 1.7 #4072 2025-05-23 15:24:03 -07:00
Paul Gauthier
299e6ae7a2 Revert "bump deps"
This reverts commit cb88b7e62a.
2025-05-23 15:20:40 -07:00
Paul Gauthier
0b1d49d630 Revert "drop pin of torch"
This reverts commit 2b9e669930.
2025-05-23 15:20:27 -07:00
Paul Gauthier
037a36edba Revert "unpin llama-index-core"
This reverts commit 66bc9cf292.
2025-05-23 15:20:13 -07:00
Paul Gauthier
66bc9cf292 unpin llama-index-core
Some checks are pending
Deploy Jekyll site to Pages / build (push) Waiting to run
Deploy Jekyll site to Pages / deploy (push) Blocked by required conditions
pre-commit / pre-commit (push) Waiting to run
2025-05-23 14:59:09 -07:00
Paul Gauthier
2b9e669930 drop pin of torch 2025-05-23 14:42:09 -07:00
Paul Gauthier
cb88b7e62a bump deps 2025-05-23 14:29:57 -07:00
Paul Gauthier
4e9943f2aa copy 2025-05-23 14:29:42 -07:00
Emmanuel Ferdman
999c292482
fix: display marker on error
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2025-05-21 17:15:45 -07:00
Paul Gauthier
9f5018e89e YAML -> yaml
Some checks failed
Deploy Jekyll site to Pages / build (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Deploy Jekyll site to Pages / deploy (push) Has been cancelled
2025-05-21 09:58:54 -07:00
Paul Gauthier (aider)
3caab85931 fix: Mark unused variable in test 2025-05-13 13:44:59 -07:00
Paul Gauthier
756372809e test: Update tests for git integration 2025-05-13 13:44:23 -07:00
Paul Gauthier (aider)
6aa05ab11c style: Format test file 2025-05-13 13:42:04 -07:00
Paul Gauthier (aider)
9cf373039e test: Add test for skipping gitignored files on init 2025-05-13 13:41:58 -07:00
Paul Gauthier (aider)
bc1272f029 fix: base coder not ignoring gitignore if --file is used. 2025-05-13 13:38:18 -07:00
Paul Gauthier
0049e78250 Merge branch 'main' of github.com:Aider-AI/aider 2025-05-12 13:20:59 -07:00
paul-gauthier
56b45ce1d3
Merge pull request #4008 from facelezzzz/main
fix: Fix #3987 Pass the coder object to repo.commit
2025-05-12 09:44:39 -07:00
wangboxue
bdd67eb229 fix: Fix #3987 Pass the coder object to repo.commit 2025-05-12 11:56:39 +08:00
Paul Gauthier (aider)
57020a2d5e test: Assert specific stderr messages for invalid edit format 2025-05-11 08:16:08 -07:00
Paul Gauthier (aider)
6b9045a2a2 fix: Fix F841 unused variable in test 2025-05-11 08:15:19 -07:00
Paul Gauthier (aider)
5f24a0013a test: Fix invalid edit format test assertion 2025-05-11 08:15:03 -07:00
Paul Gauthier (aider)
b79052501d style: Shorten comment to fix E501 2025-05-11 08:13:27 -07:00
Paul Gauthier (aider)
9e0d7d9c46 style: Fix code style in test 2025-05-11 08:13:18 -07:00
Paul Gauthier (aider)
a53ab7d937 fix: Correct test for invalid --edit-format argument 2025-05-11 08:13:11 -07:00
Paul Gauthier
c055602c6f Merge branch 'main' into completions 2025-05-11 08:01:15 -07:00
Paul Gauthier
170e8fc9a1 Merge branch 'main' of github.com:Aider-AI/aider 2025-05-11 08:01:03 -07:00
paul-gauthier
ee177054b8
Merge pull request #3993 from savioursho/file-path-completion
feat: Enable file completion for all file-related arguments
2025-05-11 08:00:49 -07:00
Paul Gauthier
f018b5fab5 include pip in uv installs 2025-05-11 07:56:55 -07:00
savioursho
5a29ba03dc
Merge branch 'Aider-AI:main' into file-path-completion 2025-05-11 20:06:30 +09:00
Paul Gauthier (aider)
035d99d3d3 fix: Remove unused import Coder 2025-05-10 19:45:04 -07:00
Paul Gauthier (aider)
702eff1033 refactor: Get edit format choices from coder classes 2025-05-10 19:44:44 -07:00
Paul Gauthier (aider)
97f3885357 chore: Apply linter rules 2025-05-10 19:42:28 -07:00
Paul Gauthier (aider)
f8653613bc feat: Add shell completion for edit format options 2025-05-10 19:42:22 -07:00
Paul Gauthier (aider)
b1d47c47d9 chore: Add shtab file completions to args 2025-05-10 17:56:29 -07:00
Paul Gauthier
2c4a126093 copy 2025-05-10 17:13:36 -07:00
Paul Gauthier (aider)
cdd1546243 docs: Improve GitHub Copilot connection docs tone and structure 2025-05-10 17:12:55 -07:00
Paul Gauthier
6a3bb0f4ec docs: Remove closing line and clarify Copilot billing 2025-05-10 17:12:52 -07:00
Paul Gauthier (aider)
24c0fbd326 docs: Add doc page for GitHub Copilot model access 2025-05-10 17:10:25 -07:00
Paul Gauthier
7b9eae117f docs: Add GitHub LLM docs 2025-05-10 17:10:23 -07:00
Paul Gauthier (aider)
512b4d891b chore: Remove unused imports in test_openrouter.py 2025-05-10 12:57:14 -07:00
Paul Gauthier (aider)
a6b0f43dce chore: Run linter 2025-05-10 12:56:13 -07:00
Paul Gauthier (aider)
e8d9ae9a1f test: add tests for OpenRouter model info handling 2025-05-10 12:56:08 -07:00
Paul Gauthier
2ab0074915 test: Add OpenRouter tests 2025-05-10 12:56:06 -07:00
Paul Gauthier (aider)
225e01717c feat: Show active model metadata in /settings command 2025-05-10 12:54:11 -07:00
Paul Gauthier (aider)
4d39b88110 style: Reorder imports in models.py 2025-05-10 12:50:41 -07:00
Paul Gauthier (aider)
5052150e2e feat: Add local cache for OpenRouter models 2025-05-10 12:50:34 -07:00
Paul Gauthier
d8fbd9cbd3 feat: Add OpenRouter API support 2025-05-10 12:50:32 -07:00
Paul Gauthier
53cda2cc10 set version to 0.83.2.dev 2025-05-10 12:36:47 -07:00
Paul Gauthier
543e5570ae version bump to 0.83.1 2025-05-10 12:36:45 -07:00
Paul Gauthier
62c7e15a36 copy 2025-05-10 12:33:40 -07:00
Paul Gauthier
17a2773a22 refactor: Validate locale language result 2025-05-10 12:28:51 -07:00
Paul Gauthier (aider)
b8758ca791 test: Fix mock for babel.Locale in test_normalize_language 2025-05-10 11:55:03 -07:00
Paul Gauthier (aider)
bf9522a2fb style: Format test file 2025-05-10 11:53:08 -07:00
Paul Gauthier (aider)
ddc8621d6e fix: Correctly normalize hyphenated language codes 2025-05-10 11:53:00 -07:00
Paul Gauthier (aider)
7875de078a fix: Remove unused import/var and fix line length in test 2025-05-10 11:46:49 -07:00
Paul Gauthier (aider)
ea1189b8ec style: Format test_coder.py 2025-05-10 11:46:27 -07:00
Paul Gauthier (aider)
1127b8b559 test: Add tests for user language detection and normalization 2025-05-10 11:46:17 -07:00
Paul Gauthier
64f218a06e ask prompt 2025-05-10 11:43:37 -07:00
Paul Gauthier (aider)
efde8e867e fix: Prevent "Reply in C." instruction for C/POSIX locales 2025-05-10 11:43:35 -07:00
Paul Gauthier
f815f0377e copy 2025-05-10 07:52:39 -07:00
Paul Gauthier (aider)
883aa9e03d docs: Include platform in testimonial link text 2025-05-10 07:45:43 -07:00
Paul Gauthier (aider)
2a410fab81 docs: Add platform/community to kind words in README 2025-05-10 07:24:33 -07:00
Paul Gauthier
34409311a3 chore: Adjust spinner text and spinner timing 2025-05-10 07:21:21 -07:00
Paul Gauthier (aider)
97379aa02f fix: Update Spinner import path 2025-05-10 07:11:08 -07:00
Paul Gauthier (aider)
ee4e9c9711 refactor: Remove unused time import 2025-05-10 07:10:28 -07:00
Paul Gauthier (aider)
7d3c817664 style: apply code formatting 2025-05-10 07:10:19 -07:00
Paul Gauthier (aider)
8c755bf032 refactor: move Spinner class to waiting module 2025-05-10 07:10:12 -07:00
saviour
0b112e948f feat: Enable file completion for all file-related arguments 2025-05-10 21:38:08 +09:00
Paul Gauthier (aider)
c11d21a230 style: apply linter fixes 2025-05-09 18:08:04 -07:00
Paul Gauthier (aider)
a9cb1a9d61 chore: Move commit message spinner into model loop and show model name 2025-05-09 18:07:58 -07:00
Paul Gauthier (aider)
43cd0164e0 style: Apply linter formatting 2025-05-09 18:07:05 -07:00
Paul Gauthier (aider)
49b3f85cc5 feat: Add spinner when generating commit message 2025-05-09 18:07:00 -07:00
Paul Gauthier
3daf7d4df3 copy 2025-05-09 18:04:04 -07:00
Paul Gauthier
3dcb23c193 qwen3 whole official api 2025-05-09 18:03:17 -07:00
Paul Gauthier
cad31b638b copy 2025-05-09 15:57:04 -07:00
Paul Gauthier
7fbe0d25f5 Merge branch 'main' into qwen3 2025-05-09 15:51:55 -07:00
Paul Gauthier
637a31e083 blame 2025-05-09 15:51:13 -07:00
Paul Gauthier
09880ee8f4 qwen3 official 2025-05-09 15:15:41 -07:00
54 changed files with 3215 additions and 1375 deletions

View file

@ -1,6 +1,33 @@
# Release history # Release history
### main branch ### Aider v0.84.0
- Added support for new Claude models including the Sonnet 4 and Opus 4 series (e.g., `claude-sonnet-4-20250514`,
`claude-opus-4-20250514`) across various providers. The default `sonnet` and `opus` aliases were updated to these newer
versions.
- Added support for the `vertex_ai/gemini-2.5-flash-preview-05-20` model.
- Fixed OpenRouter token cost calculation for improved accuracy.
- Updated default OpenRouter models during onboarding to `deepseek/deepseek-r1:free` for the free tier and
`anthropic/claude-sonnet-4` for paid tiers.
- Automatically refresh GitHub Copilot tokens when used as OpenAI API keys, by Lih Chen.
- Aider wrote 79% of the code in this release.
### Aider v0.83.2
- Bumped configargparse to 1.7.1 as 1.7 was pulled.
- Added shell tab completion for file path arguments (by saviour) and for `--edit-format`/`--editor-edit-format` options.
- Improved OpenRouter model metadata handling by introducing a local cache, increasing reliability and performance.
- The `/settings` command now displays detailed metadata for active main, editor, and weak models.
- Fixed an issue where files explicitly added via the command line were not correctly ignored if listed in `.gitignore`.
- Improved automatic commit messages by providing more context during their generation, by wangboxue.
### Aider v0.83.1
- Improved user language detection by correctly normalizing hyphenated language codes (e.g., `en-US` to `en`) and enhancing the validation of locale results.
- Prevented Aider from instructing the LLM to reply in 'C' or 'POSIX' when these are detected as the system locale.
- Displayed a spinner with the model name when generating commit messages.
### Aider v0.83.0
- Added support for `gemini-2.5-pro-preview-05-06` models. - Added support for `gemini-2.5-pro-preview-05-06` models.
- Added support for `qwen3-235b` models. - Added support for `qwen3-235b` models.
@ -404,7 +431,7 @@
- [Aider works with LLM web chat UIs](https://aider.chat/docs/usage/copypaste.html). - [Aider works with LLM web chat UIs](https://aider.chat/docs/usage/copypaste.html).
- New `--copy-paste` mode. - New `--copy-paste` mode.
- New `/copy-context` command. - New `/copy-context` command.
- [Set API keys and other environment variables for all providers from command line or yaml conf file](https://aider.chat/docs/config/aider_conf.html#storing-llm-keys). - [Set API keys and other environment variables for all providers from command line or YAML conf file](https://aider.chat/docs/config/aider_conf.html#storing-llm-keys).
- New `--api-key provider=key` setting. - New `--api-key provider=key` setting.
- New `--set-env VAR=value` setting. - New `--set-env VAR=value` setting.
- Added bash and zsh support to `--watch-files`. - Added bash and zsh support to `--watch-files`.
@ -572,7 +599,7 @@
### Aider v0.59.1 ### Aider v0.59.1
- Check for obsolete `yes: true` in yaml config, show helpful error. - Check for obsolete `yes: true` in YAML config, show helpful error.
- Model settings for openrouter/anthropic/claude-3.5-sonnet:beta - Model settings for openrouter/anthropic/claude-3.5-sonnet:beta
### Aider v0.59.0 ### Aider v0.59.0
@ -582,7 +609,7 @@
- Still auto-completes the full paths of the repo files like `/add`. - Still auto-completes the full paths of the repo files like `/add`.
- Now supports globs like `src/**/*.py` - Now supports globs like `src/**/*.py`
- Renamed `--yes` to `--yes-always`. - Renamed `--yes` to `--yes-always`.
- Now uses `AIDER_YES_ALWAYS` env var and `yes-always:` yaml key. - Now uses `AIDER_YES_ALWAYS` env var and `yes-always:` YAML key.
- Existing YAML and .env files will need to be updated. - Existing YAML and .env files will need to be updated.
- Can still abbreviate to `--yes` on the command line. - Can still abbreviate to `--yes` on the command line.
- Config file now uses standard YAML list syntax with ` - list entries`, one per line. - Config file now uses standard YAML list syntax with ` - list entries`, one per line.
@ -789,7 +816,7 @@
- Use `--map-refresh <always|files|manual|auto>` to configure. - Use `--map-refresh <always|files|manual|auto>` to configure.
- Improved cost estimate logic for caching. - Improved cost estimate logic for caching.
- Improved editing performance on Jupyter Notebook `.ipynb` files. - Improved editing performance on Jupyter Notebook `.ipynb` files.
- Show which config yaml file is loaded with `--verbose`. - Show which config YAML file is loaded with `--verbose`.
- Bumped dependency versions. - Bumped dependency versions.
- Bugfix: properly load `.aider.models.metadata.json` data. - Bugfix: properly load `.aider.models.metadata.json` data.
- Bugfix: Using `--msg /ask ...` caused an exception. - Bugfix: Using `--msg /ask ...` caused an exception.

View file

@ -27,13 +27,13 @@ cog.out(text)
<a href="https://github.com/Aider-AI/aider/stargazers"><img alt="GitHub Stars" title="Total number of GitHub stars the Aider project has received" <a href="https://github.com/Aider-AI/aider/stargazers"><img alt="GitHub Stars" title="Total number of GitHub stars the Aider project has received"
src="https://img.shields.io/github/stars/Aider-AI/aider?style=flat-square&logo=github&color=f1c40f&labelColor=555555"/></a> src="https://img.shields.io/github/stars/Aider-AI/aider?style=flat-square&logo=github&color=f1c40f&labelColor=555555"/></a>
<a href="https://pypi.org/project/aider-chat/"><img alt="PyPI Downloads" title="Total number of installations via pip from PyPI" <a href="https://pypi.org/project/aider-chat/"><img alt="PyPI Downloads" title="Total number of installations via pip from PyPI"
src="https://img.shields.io/badge/📦%20Installs-2.2M-2ecc71?style=flat-square&labelColor=555555"/></a> src="https://img.shields.io/badge/📦%20Installs-2.4M-2ecc71?style=flat-square&labelColor=555555"/></a>
<img alt="Tokens per week" title="Number of tokens processed weekly by Aider users" <img alt="Tokens per week" title="Number of tokens processed weekly by Aider users"
src="https://img.shields.io/badge/📈%20Tokens%2Fweek-15B-3498db?style=flat-square&labelColor=555555"/> src="https://img.shields.io/badge/📈%20Tokens%2Fweek-15B-3498db?style=flat-square&labelColor=555555"/>
<a href="https://openrouter.ai/#options-menu"><img alt="OpenRouter Ranking" title="Aider's ranking among applications on the OpenRouter platform" <a href="https://openrouter.ai/#options-menu"><img alt="OpenRouter Ranking" title="Aider's ranking among applications on the OpenRouter platform"
src="https://img.shields.io/badge/🏆%20OpenRouter-Top%2020-9b59b6?style=flat-square&labelColor=555555"/></a> src="https://img.shields.io/badge/🏆%20OpenRouter-Top%2020-9b59b6?style=flat-square&labelColor=555555"/></a>
<a href="https://aider.chat/HISTORY.html"><img alt="Singularity" title="Percentage of the new code in Aider's last release written by Aider itself" <a href="https://aider.chat/HISTORY.html"><img alt="Singularity" title="Percentage of the new code in Aider's last release written by Aider itself"
src="https://img.shields.io/badge/🔄%20Singularity-92%25-e74c3c?style=flat-square&labelColor=555555"/></a> src="https://img.shields.io/badge/🔄%20Singularity-79%25-e74c3c?style=flat-square&labelColor=555555"/></a>
<!--[[[end]]]--> <!--[[[end]]]-->
</p> </p>
@ -136,43 +136,44 @@ See the [installation instructions](https://aider.chat/docs/install.html) and [u
- [LLM Leaderboards](https://aider.chat/docs/leaderboards/) - [LLM Leaderboards](https://aider.chat/docs/leaderboards/)
- [GitHub Repository](https://github.com/Aider-AI/aider) - [GitHub Repository](https://github.com/Aider-AI/aider)
- [Discord Community](https://discord.gg/Y7X7bhMQFV) - [Discord Community](https://discord.gg/Y7X7bhMQFV)
- [Release notes](https://aider.chat/HISTORY.html)
- [Blog](https://aider.chat/blog/) - [Blog](https://aider.chat/blog/)
## Kind Words From Users ## Kind Words From Users
- *"My life has changed... There's finally an AI coding tool that's good enough to keep up with me... Aider... It's going to rock your world."* — [Eric S. Raymond](https://x.com/esrtweet/status/1910809356381413593) - *"My life has changed... Aider... It's going to rock your world."* — [Eric S. Raymond on X](https://x.com/esrtweet/status/1910809356381413593)
- *"The best free open source AI coding assistant."* — [IndyDevDan](https://youtu.be/YALpX8oOn78) - *"The best free open source AI coding assistant."* — [IndyDevDan on YouTube](https://youtu.be/YALpX8oOn78)
- *"The best AI coding assistant so far."* — [Matthew Berman](https://www.youtube.com/watch?v=df8afeb1FY8) - *"The best AI coding assistant so far."* — [Matthew Berman on YouTube](https://www.youtube.com/watch?v=df8afeb1FY8)
- *"Aider ... has easily quadrupled my coding productivity."* — [SOLAR_FIELDS](https://news.ycombinator.com/item?id=36212100) - *"Aider ... has easily quadrupled my coding productivity."* — [SOLAR_FIELDS on Hacker News](https://news.ycombinator.com/item?id=36212100)
- *"It's a cool workflow... Aider's ergonomics are perfect for me."* — [qup](https://news.ycombinator.com/item?id=38185326) - *"It's a cool workflow... Aider's ergonomics are perfect for me."* — [qup on Hacker News](https://news.ycombinator.com/item?id=38185326)
- *"It's really like having your senior developer live right in your Git repo - truly amazing!"* — [rappster](https://github.com/Aider-AI/aider/issues/124) - *"It's really like having your senior developer live right in your Git repo - truly amazing!"* — [rappster on GitHub](https://github.com/Aider-AI/aider/issues/124)
- *"What an amazing tool. It's incredible."* — [valyagolev](https://github.com/Aider-AI/aider/issues/6#issue-1722897858) - *"What an amazing tool. It's incredible."* — [valyagolev on GitHub](https://github.com/Aider-AI/aider/issues/6#issue-1722897858)
- *"Aider is such an astounding thing!"* — [cgrothaus](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700) - *"Aider is such an astounding thing!"* — [cgrothaus on GitHub](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700)
- *"It was WAY faster than I would be getting off the ground and making the first few working versions."* — [Daniel Feldman](https://twitter.com/d_feldman/status/1662295077387923456) - *"It was WAY faster than I would be getting off the ground and making the first few working versions."* — [Daniel Feldman on X](https://twitter.com/d_feldman/status/1662295077387923456)
- *"THANK YOU for Aider! It really feels like a glimpse into the future of coding."* — [derwiki](https://news.ycombinator.com/item?id=38205643) - *"THANK YOU for Aider! It really feels like a glimpse into the future of coding."* — [derwiki on Hacker News](https://news.ycombinator.com/item?id=38205643)
- *"It's just amazing. It is freeing me to do things I felt were out my comfort zone before."* — [Dougie](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656) - *"It's just amazing. It is freeing me to do things I felt were out my comfort zone before."* — [Dougie on Discord](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656)
- *"This project is stellar."* — [funkytaco](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008) - *"This project is stellar."* — [funkytaco on GitHub](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008)
- *"Amazing project, definitely the best AI coding assistant I've used."* — [joshuavial](https://github.com/Aider-AI/aider/issues/84) - *"Amazing project, definitely the best AI coding assistant I've used."* — [joshuavial on GitHub](https://github.com/Aider-AI/aider/issues/84)
- *"I absolutely love using Aider ... It makes software development feel so much lighter as an experience."* — [principalideal0](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468) - *"I absolutely love using Aider ... It makes software development feel so much lighter as an experience."* — [principalideal0 on Discord](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468)
- *"I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity."* — [codeninja](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG) - *"I have been recovering from ... surgeries ... aider ... has allowed me to continue productivity."* — [codeninja on Reddit](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG)
- *"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) - *"I am an aider addict. I'm getting so much more work done, but in less time."* — [dandandan on Discord](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) - *"Aider... blows everything else out of the water hands down, there's no competition whatsoever."* — [SystemSculpt on Discord](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548)
- *"Aider is amazing, coupled with Sonnet 3.5 it's quite mind blowing."* — [Josh Dingus](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548) - *"Aider is amazing, coupled with Sonnet 3.5 it's quite mind blowing."* — [Josh Dingus on Discord](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548)
- *"Hands down, this is the best AI coding assistant tool so far."* — [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs) - *"Hands down, this is the best AI coding assistant tool so far."* — [IndyDevDan on YouTube](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) - *"[Aider] changed my daily coding workflows. It's mind-blowing how ...(it)... can change your life."* — [maledorak on Discord](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) - *"Best agent for actual dev work in existing codebases."* — [Nick Dobos on X](https://twitter.com/NickADobos/status/1690408967963652097?s=20)
- *"One of my favorite pieces of software. Blazing trails on new paradigms!"* — [Chris Wall](https://x.com/chris65536/status/1905053299251798432) - *"One of my favorite pieces of software. Blazing trails on new paradigms!"* — [Chris Wall on X](https://x.com/chris65536/status/1905053299251798432)
- *"Aider has been revolutionary for me and my work."* — [Starry Hope](https://x.com/starryhopeblog/status/1904985812137132056) - *"Aider has been revolutionary for me and my work."* — [Starry Hope on X](https://x.com/starryhopeblog/status/1904985812137132056)
- *"Try aider! One of the best ways to vibe code."* — [Chris Wall](https://x.com/Chris65536/status/1905053418961391929) - *"Try aider! One of the best ways to vibe code."* — [Chris Wall on X](https://x.com/Chris65536/status/1905053418961391929)
- *"Aider is hands down the best. And it's free and opensource."* — [AriyaSavakaLurker](https://www.reddit.com/r/ChatGPTCoding/comments/1ik16y6/whats_your_take_on_aider/mbip39n/) - *"Aider is hands down the best. And it's free and opensource."* — [AriyaSavakaLurker on Reddit](https://www.reddit.com/r/ChatGPTCoding/comments/1ik16y6/whats_your_take_on_aider/mbip39n/)
- *"Aider is also my best friend."* — [jzn21](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27dcnb/) - *"Aider is also my best friend."* — [jzn21 on Reddit](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27dcnb/)
- *"Try Aider, it's worth it."* — [jorgejhms](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27cp99/) - *"Try Aider, it's worth it."* — [jorgejhms on Reddit](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27cp99/)
- *"I like aider :)"* — [Chenwei Cui](https://x.com/ccui42/status/1904965344999145698) - *"I like aider :)"* — [Chenwei Cui on X](https://x.com/ccui42/status/1904965344999145698)
- *"Aider is the precision tool of LLM code gen... Minimal, thoughtful and capable of surgical changes to your codebase all while keeping the developer in control."* — [Reilly Sweetland](https://x.com/rsweetland/status/1904963807237259586) - *"Aider is the precision tool of LLM code gen... Minimal, thoughtful and capable of surgical changes ... while keeping the developer in control."* — [Reilly Sweetland on X](https://x.com/rsweetland/status/1904963807237259586)
- *"Cannot believe aider vibe coded a 650 LOC feature across service and cli today in 1 shot."* - [autopoietist](https://discord.com/channels/1131200896827654144/1131200896827654149/1355675042259796101) - *"Cannot believe aider vibe coded a 650 LOC feature across service and cli today in 1 shot."* - [autopoietist on Discord](https://discord.com/channels/1131200896827654144/1131200896827654149/1355675042259796101)
- *"Oh no the secret is out! Yes, Aider is the best coding tool around. I highly, highly recommend it to anyone."* — [Joshua D Vander Hook](https://x.com/jodavaho/status/1911154899057795218) - *"Oh no the secret is out! Yes, Aider is the best coding tool around. I highly, highly recommend it to anyone."* — [Joshua D Vander Hook on X](https://x.com/jodavaho/status/1911154899057795218)
- *"thanks to aider, i have started and finished three personal projects within the last two days"* — [joseph stalzyn](https://x.com/anitaheeder/status/1908338609645904160) - *"thanks to aider, i have started and finished three personal projects within the last two days"* — [joseph stalzyn on X](https://x.com/anitaheeder/status/1908338609645904160)
- *"Been using aider as my daily driver for over a year ... I absolutely love the tool, like beyond words."* — [koleok](https://discord.com/channels/1131200896827654144/1273248471394291754/1356727448372252783) - *"Been using aider as my daily driver for over a year ... I absolutely love the tool, like beyond words."* — [koleok on Discord](https://discord.com/channels/1131200896827654144/1273248471394291754/1356727448372252783)
- *"Aider ... is the tool to benchmark against."* — [BeetleB](https://news.ycombinator.com/item?id=43930201) - *"Aider ... is the tool to benchmark against."* — [BeetleB on Hacker News](https://news.ycombinator.com/item?id=43930201)
- *"aider is really cool"* — [kache (@yacineMTB)](https://x.com/yacineMTB/status/1911224442430124387) - *"aider is really cool"* — [kache on X](https://x.com/yacineMTB/status/1911224442430124387)

View file

@ -1,6 +1,6 @@
from packaging import version from packaging import version
__version__ = "0.83.1.dev" __version__ = "0.84.1.dev"
safe_version = __version__ safe_version = __version__
try: try:

View file

@ -40,10 +40,22 @@ def get_parser(default_config_files, git_root):
config_file_parser_class=configargparse.YAMLConfigFileParser, config_file_parser_class=configargparse.YAMLConfigFileParser,
auto_env_var_prefix="AIDER_", auto_env_var_prefix="AIDER_",
) )
# List of valid edit formats for argparse validation & shtab completion.
# Dynamically gather them from the registered coder classes so the list
# stays in sync if new formats are added.
from aider import coders as _aider_coders
edit_format_choices = sorted(
{
c.edit_format
for c in _aider_coders.__all__
if hasattr(c, "edit_format") and c.edit_format is not None
}
)
group = parser.add_argument_group("Main model") group = parser.add_argument_group("Main model")
group.add_argument( group.add_argument(
"files", metavar="FILE", nargs="*", help="files to edit with an LLM (optional)" "files", metavar="FILE", nargs="*", help="files to edit with an LLM (optional)"
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--model", "--model",
metavar="MODEL", metavar="MODEL",
@ -110,13 +122,13 @@ def get_parser(default_config_files, git_root):
metavar="MODEL_SETTINGS_FILE", metavar="MODEL_SETTINGS_FILE",
default=".aider.model.settings.yml", default=".aider.model.settings.yml",
help="Specify a file with aider model settings for unknown models", help="Specify a file with aider model settings for unknown models",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--model-metadata-file", "--model-metadata-file",
metavar="MODEL_METADATA_FILE", metavar="MODEL_METADATA_FILE",
default=".aider.model.metadata.json", default=".aider.model.metadata.json",
help="Specify a file with context window and costs for unknown models", help="Specify a file with context window and costs for unknown models",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--alias", "--alias",
action="append", action="append",
@ -149,6 +161,7 @@ def get_parser(default_config_files, git_root):
"--edit-format", "--edit-format",
"--chat-mode", "--chat-mode",
metavar="EDIT_FORMAT", metavar="EDIT_FORMAT",
choices=edit_format_choices,
default=None, default=None,
help="Specify what edit format the LLM should use (default depends on model)", help="Specify what edit format the LLM should use (default depends on model)",
) )
@ -183,6 +196,7 @@ def get_parser(default_config_files, git_root):
group.add_argument( group.add_argument(
"--editor-edit-format", "--editor-edit-format",
metavar="EDITOR_EDIT_FORMAT", metavar="EDITOR_EDIT_FORMAT",
choices=edit_format_choices,
default=None, default=None,
help="Specify the edit format for the editor model (default: depends on editor model)", help="Specify the edit format for the editor model (default: depends on editor model)",
) )
@ -262,13 +276,13 @@ def get_parser(default_config_files, git_root):
metavar="INPUT_HISTORY_FILE", metavar="INPUT_HISTORY_FILE",
default=default_input_history_file, default=default_input_history_file,
help=f"Specify the chat input history file (default: {default_input_history_file})", help=f"Specify the chat input history file (default: {default_input_history_file})",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--chat-history-file", "--chat-history-file",
metavar="CHAT_HISTORY_FILE", metavar="CHAT_HISTORY_FILE",
default=default_chat_history_file, default=default_chat_history_file,
help=f"Specify the chat history file (default: {default_chat_history_file})", help=f"Specify the chat history file (default: {default_chat_history_file})",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--restore-chat-history", "--restore-chat-history",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
@ -280,7 +294,7 @@ def get_parser(default_config_files, git_root):
metavar="LLM_HISTORY_FILE", metavar="LLM_HISTORY_FILE",
default=None, default=None,
help="Log the conversation with the LLM to this file (for example, .aider.llm.history)", help="Log the conversation with the LLM to this file (for example, .aider.llm.history)",
) ).complete = shtab.FILE
########## ##########
group = parser.add_argument_group("Output settings") group = parser.add_argument_group("Output settings")
@ -406,7 +420,7 @@ def get_parser(default_config_files, git_root):
type=lambda path_str: resolve_aiderignore_path(path_str, git_root), type=lambda path_str: resolve_aiderignore_path(path_str, git_root),
default=default_aiderignore_file, default=default_aiderignore_file,
help="Specify the aider ignore file (default: .aiderignore in git root)", help="Specify the aider ignore file (default: .aiderignore in git root)",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--subtree-only", "--subtree-only",
action="store_true", action="store_true",
@ -552,7 +566,7 @@ def get_parser(default_config_files, git_root):
"--analytics-log", "--analytics-log",
metavar="ANALYTICS_LOG_FILE", metavar="ANALYTICS_LOG_FILE",
help="Specify a file to log analytics events", help="Specify a file to log analytics events",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--analytics-disable", "--analytics-disable",
action="store_true", action="store_true",
@ -619,7 +633,7 @@ def get_parser(default_config_files, git_root):
"Specify a file containing the message to send the LLM, process reply, then exit" "Specify a file containing the message to send the LLM, process reply, then exit"
" (disables chat mode)" " (disables chat mode)"
), ),
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--gui", "--gui",
"--browser", "--browser",
@ -637,7 +651,7 @@ def get_parser(default_config_files, git_root):
"--apply", "--apply",
metavar="FILE", metavar="FILE",
help="Apply the changes from the given file instead of running the chat (debug)", help="Apply the changes from the given file instead of running the chat (debug)",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--apply-clipboard-edits", "--apply-clipboard-edits",
action="store_true", action="store_true",
@ -698,13 +712,13 @@ def get_parser(default_config_files, git_root):
action="append", action="append",
metavar="FILE", metavar="FILE",
help="specify a file to edit (can be used multiple times)", help="specify a file to edit (can be used multiple times)",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--read", "--read",
action="append", action="append",
metavar="FILE", metavar="FILE",
help="specify a read-only file (can be used multiple times)", help="specify a read-only file (can be used multiple times)",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--vim", "--vim",
action="store_true", action="store_true",
@ -717,6 +731,12 @@ def get_parser(default_config_files, git_root):
default=None, default=None,
help="Specify the language to use in the chat (default: None, uses system settings)", help="Specify the language to use in the chat (default: None, uses system settings)",
) )
group.add_argument(
"--commit-language",
metavar="COMMIT_LANGUAGE",
default=None,
help="Specify the language to use in the commit message (default: None, user language)",
)
group.add_argument( group.add_argument(
"--yes-always", "--yes-always",
action="store_true", action="store_true",
@ -734,7 +754,7 @@ def get_parser(default_config_files, git_root):
"--load", "--load",
metavar="LOAD_FILE", metavar="LOAD_FILE",
help="Load and execute /commands from a file on launch", help="Load and execute /commands from a file on launch",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--encoding", "--encoding",
default="utf-8", default="utf-8",
@ -755,7 +775,7 @@ def get_parser(default_config_files, git_root):
"Specify the config file (default: search for .aider.conf.yml in git root, cwd" "Specify the config file (default: search for .aider.conf.yml in git root, cwd"
" or home directory)" " or home directory)"
), ),
) ).complete = shtab.FILE
# This is a duplicate of the argument in the preparser and is a no-op by this time of # This is a duplicate of the argument in the preparser and is a no-op by this time of
# argument parsing, but it's here so that the help is displayed as expected. # argument parsing, but it's here so that the help is displayed as expected.
group.add_argument( group.add_argument(
@ -763,7 +783,7 @@ def get_parser(default_config_files, git_root):
metavar="ENV_FILE", metavar="ENV_FILE",
default=default_env_file(git_root), default=default_env_file(git_root),
help="Specify the .env file to load (default: .env in git root)", help="Specify the .env file to load (default: .env in git root)",
) ).complete = shtab.FILE
group.add_argument( group.add_argument(
"--suggest-shell-commands", "--suggest-shell-commands",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,

View file

@ -96,7 +96,7 @@ class YamlHelpFormatter(argparse.HelpFormatter):
# Place in your home dir, or at the root of your git repo. # Place in your home dir, or at the root of your git repo.
########################################################## ##########################################################
# Note: You can only put OpenAI and Anthropic API keys in the yaml # Note: You can only put OpenAI and Anthropic API keys in the YAML
# config file. Keys for all APIs can be stored in a .env file # config file. Keys for all APIs can be stored in a .env file
# https://aider.chat/docs/config/dotenv.html # https://aider.chat/docs/config/dotenv.html

View file

@ -8,8 +8,7 @@ class AskPrompts(CoderPrompts):
Answer questions about the supplied code. Answer questions about the supplied code.
Always reply to the user in {language}. Always reply to the user in {language}.
Describe code changes however you like, but elide unchanging code. If you need to describe code changes, do so *briefly*.
Don't use SEARCH/REPLACE blocks or return huge swaths of unchanging code.
""" """
example_messages = [] example_messages = []

View file

@ -118,6 +118,7 @@ class Coder:
detect_urls = True detect_urls = True
ignore_mentions = None ignore_mentions = None
chat_language = None chat_language = None
commit_language = None
file_watcher = None file_watcher = None
@classmethod @classmethod
@ -328,6 +329,7 @@ class Coder:
num_cache_warming_pings=0, num_cache_warming_pings=0,
suggest_shell_commands=True, suggest_shell_commands=True,
chat_language=None, chat_language=None,
commit_language=None,
detect_urls=True, detect_urls=True,
ignore_mentions=None, ignore_mentions=None,
total_tokens_sent=0, total_tokens_sent=0,
@ -341,6 +343,7 @@ class Coder:
self.event = self.analytics.event self.event = self.analytics.event
self.chat_language = chat_language self.chat_language = chat_language
self.commit_language = commit_language
self.commit_before_message = [] self.commit_before_message = []
self.aider_commit_hashes = set() self.aider_commit_hashes = set()
self.rejected_urls = set() self.rejected_urls = set()
@ -445,6 +448,7 @@ class Coder:
fname = Path(fname) fname = Path(fname)
if self.repo and self.repo.git_ignored_file(fname): if self.repo and self.repo.git_ignored_file(fname):
self.io.tool_warning(f"Skipping {fname} that matches gitignore spec.") self.io.tool_warning(f"Skipping {fname} that matches gitignore spec.")
continue
if self.repo and self.repo.ignored_file(fname): if self.repo and self.repo.ignored_file(fname):
self.io.tool_warning(f"Skipping {fname} that matches aiderignore spec.") self.io.tool_warning(f"Skipping {fname} that matches aiderignore spec.")
@ -1049,6 +1053,9 @@ class Coder:
if not lang_code: if not lang_code:
return None return None
if lang_code.upper() in ("C", "POSIX"):
return None
# Probably already a language name # Probably already a language name
if ( if (
len(lang_code) > 3 len(lang_code) > 3
@ -1079,7 +1086,8 @@ class Coder:
"ko": "Korean", "ko": "Korean",
"ru": "Russian", "ru": "Russian",
} }
return fallback.get(lang_code.split("_")[0].lower(), lang_code) primary_lang_code = lang_code.replace("-", "_").split("_")[0].lower()
return fallback.get(primary_lang_code, lang_code)
def get_user_language(self): def get_user_language(self):
""" """
@ -1090,6 +1098,7 @@ class Coder:
2. ``locale.getlocale()`` 2. ``locale.getlocale()``
3. ``LANG`` / ``LANGUAGE`` / ``LC_ALL`` / ``LC_MESSAGES`` environment variables 3. ``LANG`` / ``LANGUAGE`` / ``LC_ALL`` / ``LC_MESSAGES`` environment variables
""" """
# Explicit override # Explicit override
if self.chat_language: if self.chat_language:
return self.normalize_language(self.chat_language) return self.normalize_language(self.chat_language)
@ -1098,9 +1107,11 @@ class Coder:
try: try:
lang = locale.getlocale()[0] lang = locale.getlocale()[0]
if lang: if lang:
return self.normalize_language(lang) lang = self.normalize_language(lang)
if lang:
return lang
except Exception: except Exception:
pass # pragma: no cover pass
# Environment variables # Environment variables
for env_var in ("LANG", "LANGUAGE", "LC_ALL", "LC_MESSAGES"): for env_var in ("LANG", "LANGUAGE", "LC_ALL", "LC_MESSAGES"):
@ -1182,10 +1193,10 @@ class Coder:
) )
rename_with_shell = "" rename_with_shell = ""
if self.chat_language: if user_lang: # user_lang is the result of self.get_user_language()
language = self.chat_language language = user_lang
else: else:
language = "the same language they are using" language = "the same language they are using" # Default if no specific lang detected
if self.fence[0] == "`" * 4: if self.fence[0] == "`" * 4:
quad_backtick_reminder = ( quad_backtick_reminder = (

View file

@ -109,7 +109,7 @@ class RelativeIndenter:
""" """
if self.marker in text: if self.marker in text:
raise ValueError("Text already contains the outdent marker: {self.marker}") raise ValueError(f"Text already contains the outdent marker: {self.marker}")
lines = text.splitlines(keepends=True) lines = text.splitlines(keepends=True)

View file

@ -346,7 +346,7 @@ class Commands:
return return
commit_message = args.strip() if args else None commit_message = args.strip() if args else None
self.coder.repo.commit(message=commit_message) self.coder.repo.commit(message=commit_message, coder=self.coder)
def cmd_lint(self, args="", fnames=None): def cmd_lint(self, args="", fnames=None):
"Lint and fix in-chat files or all dirty files if none in chat" "Lint and fix in-chat files or all dirty files if none in chat"
@ -1392,7 +1392,30 @@ class Commands:
"Print out the current settings" "Print out the current settings"
settings = format_settings(self.parser, self.args) settings = format_settings(self.parser, self.args)
announcements = "\n".join(self.coder.get_announcements()) announcements = "\n".join(self.coder.get_announcements())
# Build metadata for the active models (main, editor, weak)
model_sections = []
active_models = [
("Main model", self.coder.main_model),
("Editor model", getattr(self.coder.main_model, "editor_model", None)),
("Weak model", getattr(self.coder.main_model, "weak_model", None)),
]
for label, model in active_models:
if not model:
continue
info = getattr(model, "info", {}) or {}
if not info:
continue
model_sections.append(f"{label} ({model.name}):")
for k, v in sorted(info.items()):
model_sections.append(f" {k}: {v}")
model_sections.append("") # blank line between models
model_metadata = "\n".join(model_sections)
output = f"{announcements}\n{settings}" output = f"{announcements}\n{settings}"
if model_metadata:
output += "\n" + model_metadata
self.io.tool_output(output) self.io.tool_output(output)
def completions_raw_load(self, document, complete_event): def completions_raw_load(self, document, complete_event):

View file

@ -749,7 +749,7 @@ class InputOutput:
if not self.llm_history_file: if not self.llm_history_file:
return return
timestamp = datetime.now().isoformat(timespec="seconds") timestamp = datetime.now().isoformat(timespec="seconds")
with open(self.llm_history_file, "a", encoding=self.encoding) as log_file: with open(self.llm_history_file, "a", encoding="utf-8") as log_file:
log_file.write(f"{role.upper()} {timestamp}\n") log_file.write(f"{role.upper()} {timestamp}\n")
log_file.write(content + "\n") log_file.write(content + "\n")

View file

@ -993,6 +993,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
num_cache_warming_pings=args.cache_keepalive_pings, num_cache_warming_pings=args.cache_keepalive_pings,
suggest_shell_commands=args.suggest_shell_commands, suggest_shell_commands=args.suggest_shell_commands,
chat_language=args.chat_language, chat_language=args.chat_language,
commit_language=args.commit_language,
detect_urls=args.detect_urls, detect_urls=args.detect_urls,
auto_copy_context=args.copy_paste, auto_copy_context=args.copy_paste,
auto_accept_architect=args.auto_accept_architect, auto_accept_architect=args.auto_accept_architect,

View file

@ -8,6 +8,7 @@ import platform
import sys import sys
import time import time
from dataclasses import dataclass, fields from dataclasses import dataclass, fields
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Optional, Union from typing import Optional, Union
@ -15,8 +16,10 @@ import json5
import yaml import yaml
from PIL import Image from PIL import Image
from aider import __version__
from aider.dump import dump # noqa: F401 from aider.dump import dump # noqa: F401
from aider.llm import litellm from aider.llm import litellm
from aider.openrouter import OpenRouterModelManager
from aider.sendchat import ensure_alternating_roles, sanity_check_messages from aider.sendchat import ensure_alternating_roles, sanity_check_messages
from aider.utils import check_pip_install_extra from aider.utils import check_pip_install_extra
@ -69,6 +72,8 @@ claude-3-opus-20240229
claude-3-sonnet-20240229 claude-3-sonnet-20240229
claude-3-5-sonnet-20240620 claude-3-5-sonnet-20240620
claude-3-5-sonnet-20241022 claude-3-5-sonnet-20241022
claude-sonnet-4-20250514
claude-opus-4-20250514
""" """
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()]
@ -76,9 +81,9 @@ ANTHROPIC_MODELS = [ln.strip() for ln in ANTHROPIC_MODELS.splitlines() if ln.str
# Mapping of model aliases to their canonical names # Mapping of model aliases to their canonical names
MODEL_ALIASES = { MODEL_ALIASES = {
# Claude models # Claude models
"sonnet": "anthropic/claude-3-7-sonnet-20250219", "sonnet": "anthropic/claude-sonnet-4-20250514",
"haiku": "claude-3-5-haiku-20241022", "haiku": "claude-3-5-haiku-20241022",
"opus": "claude-3-opus-20240229", "opus": "claude-opus-4-20250514",
# GPT models # GPT models
"4": "gpt-4-0613", "4": "gpt-4-0613",
"4o": "gpt-4o", "4o": "gpt-4o",
@ -149,8 +154,13 @@ class ModelInfoManager:
self.verify_ssl = True self.verify_ssl = True
self._cache_loaded = False self._cache_loaded = False
# Manager for the cached OpenRouter model database
self.openrouter_manager = OpenRouterModelManager()
def set_verify_ssl(self, verify_ssl): def set_verify_ssl(self, verify_ssl):
self.verify_ssl = verify_ssl self.verify_ssl = verify_ssl
if hasattr(self, "openrouter_manager"):
self.openrouter_manager.set_verify_ssl(verify_ssl)
def _load_cache(self): def _load_cache(self):
if self._cache_loaded: if self._cache_loaded:
@ -232,6 +242,12 @@ class ModelInfoManager:
return litellm_info return litellm_info
if not cached_info and model.startswith("openrouter/"): if not cached_info and model.startswith("openrouter/"):
# First try using the locally cached OpenRouter model database
openrouter_info = self.openrouter_manager.get_model_info(model)
if openrouter_info:
return openrouter_info
# Fallback to legacy web-scraping if the API cache does not contain the model
openrouter_info = self.fetch_openrouter_model_info(model) openrouter_info = self.fetch_openrouter_model_info(model)
if openrouter_info: if openrouter_info:
return openrouter_info return openrouter_info
@ -861,6 +877,26 @@ class Model(ModelSettings):
def is_ollama(self): def is_ollama(self):
return self.name.startswith("ollama/") or self.name.startswith("ollama_chat/") return self.name.startswith("ollama/") or self.name.startswith("ollama_chat/")
def github_copilot_token_to_open_ai_key(self, extra_headers):
# check to see if there's an openai api key
# If so, check to see if it's expire
openai_api_key = "OPENAI_API_KEY"
if openai_api_key not in os.environ or (
int(dict(x.split("=") for x in os.environ[openai_api_key].split(";"))["exp"])
< int(datetime.now().timestamp())
):
import requests
headers = {
"Authorization": f"Bearer {os.environ['GITHUB_COPILOT_TOKEN']}",
"Editor-Version": extra_headers["Editor-Version"],
"Copilot-Integration-Id": extra_headers["Copilot-Integration-Id"],
"Content-Type": "application/json",
}
res = requests.get("https://api.github.com/copilot_internal/v2/token", headers=headers)
os.environ[openai_api_key] = res.json()["token"]
def send_completion(self, messages, functions, stream, temperature=None): def send_completion(self, messages, functions, stream, temperature=None):
if os.environ.get("AIDER_SANITY_CHECK_TURNS"): if os.environ.get("AIDER_SANITY_CHECK_TURNS"):
sanity_check_messages(messages) sanity_check_messages(messages)
@ -902,6 +938,16 @@ class Model(ModelSettings):
dump(kwargs) dump(kwargs)
kwargs["messages"] = messages kwargs["messages"] = messages
# Are we using github copilot?
if "GITHUB_COPILOT_TOKEN" in os.environ:
if "extra_headers" not in kwargs:
kwargs["extra_headers"] = {
"Editor-Version": f"aider/{__version__}",
"Copilot-Integration-Id": "vscode-chat",
}
self.github_copilot_token_to_open_ai_key(kwargs["extra_headers"])
res = litellm.completion(**kwargs) res = litellm.completion(**kwargs)
return hash_object, res return hash_object, res

View file

@ -55,9 +55,9 @@ def try_to_select_default_model():
# Check if the user is on a free tier # Check if the user is on a free tier
is_free_tier = check_openrouter_tier(openrouter_key) is_free_tier = check_openrouter_tier(openrouter_key)
if is_free_tier: if is_free_tier:
return "openrouter/google/gemini-2.5-pro-exp-03-25:free" return "openrouter/deepseek/deepseek-r1:free"
else: else:
return "openrouter/anthropic/claude-3.7-sonnet" return "openrouter/anthropic/claude-sonnet-4"
# Select model based on other available API keys # Select model based on other available API keys
model_key_pairs = [ model_key_pairs = [

128
aider/openrouter.py Normal file
View file

@ -0,0 +1,128 @@
"""
OpenRouter model metadata caching and lookup.
This module keeps a local cached copy of the OpenRouter model list
(downloaded from ``https://openrouter.ai/api/v1/models``) and exposes a
helper class that returns metadata for a given model in a format compatible
with litellms ``get_model_info``.
"""
from __future__ import annotations
import json
import time
from pathlib import Path
from typing import Dict
import requests
def _cost_per_token(val: str | None) -> float | None:
"""Convert a price string (USD per token) to a float."""
if val in (None, "", "0"):
return 0.0 if val == "0" else None
try:
return float(val)
except Exception: # noqa: BLE001
return None
class OpenRouterModelManager:
MODELS_URL = "https://openrouter.ai/api/v1/models"
CACHE_TTL = 60 * 60 * 24 # 24 h
def __init__(self) -> None:
self.cache_dir = Path.home() / ".aider" / "caches"
self.cache_file = self.cache_dir / "openrouter_models.json"
self.content: Dict | None = None
self.verify_ssl: bool = True
self._cache_loaded = False
# ------------------------------------------------------------------ #
# Public API #
# ------------------------------------------------------------------ #
def set_verify_ssl(self, verify_ssl: bool) -> None:
"""Enable/disable SSL verification for API requests."""
self.verify_ssl = verify_ssl
def get_model_info(self, model: str) -> Dict:
"""
Return metadata for *model* or an empty ``dict`` when unknown.
``model`` should use the aider naming convention, e.g.
``openrouter/nousresearch/deephermes-3-mistral-24b-preview:free``.
"""
self._ensure_content()
if not self.content or "data" not in self.content:
return {}
route = self._strip_prefix(model)
# Consider both the exact id and id without any “:suffix”.
candidates = {route}
if ":" in route:
candidates.add(route.split(":", 1)[0])
record = next((item for item in self.content["data"] if item.get("id") in candidates), None)
if not record:
return {}
context_len = (
record.get("top_provider", {}).get("context_length")
or record.get("context_length")
or None
)
pricing = record.get("pricing", {})
return {
"max_input_tokens": context_len,
"max_tokens": context_len,
"max_output_tokens": context_len,
"input_cost_per_token": _cost_per_token(pricing.get("prompt")),
"output_cost_per_token": _cost_per_token(pricing.get("completion")),
"litellm_provider": "openrouter",
}
# ------------------------------------------------------------------ #
# Internal helpers #
# ------------------------------------------------------------------ #
def _strip_prefix(self, model: str) -> str:
return model[len("openrouter/") :] if model.startswith("openrouter/") else model
def _ensure_content(self) -> None:
self._load_cache()
if not self.content:
self._update_cache()
def _load_cache(self) -> None:
if self._cache_loaded:
return
try:
self.cache_dir.mkdir(parents=True, exist_ok=True)
if self.cache_file.exists():
cache_age = time.time() - self.cache_file.stat().st_mtime
if cache_age < self.CACHE_TTL:
try:
self.content = json.loads(self.cache_file.read_text())
except json.JSONDecodeError:
self.content = None
except OSError:
# Cache directory might be unwritable; ignore.
pass
self._cache_loaded = True
def _update_cache(self) -> None:
try:
response = requests.get(self.MODELS_URL, timeout=10, verify=self.verify_ssl)
if response.status_code == 200:
self.content = response.json()
try:
self.cache_file.write_text(json.dumps(self.content, indent=2))
except OSError:
pass # Non-fatal if we cant write the cache
except Exception as ex: # noqa: BLE001
print(f"Failed to fetch OpenRouter model list: {ex}")
try:
self.cache_file.write_text("{}")
except OSError:
pass

View file

@ -21,6 +21,7 @@ import pathspec
from aider import prompts, utils from aider import prompts, utils
from .dump import dump # noqa: F401 from .dump import dump # noqa: F401
from .waiting import WaitingSpinner
ANY_GIT_ERROR += [ ANY_GIT_ERROR += [
OSError, OSError,
@ -209,7 +210,9 @@ class GitRepo:
else: else:
user_language = None user_language = None
if coder: if coder:
user_language = coder.get_user_language() user_language = coder.commit_language
if not user_language:
user_language = coder.get_user_language()
commit_message = self.get_commit_message(diffs, context, user_language) commit_message = self.get_commit_message(diffs, context, user_language)
# Retrieve attribute settings, prioritizing coder.args if available # Retrieve attribute settings, prioritizing coder.args if available
@ -343,13 +346,15 @@ class GitRepo:
commit_message = None commit_message = None
for model in self.models: for model in self.models:
num_tokens = model.token_count(messages) spinner_text = f"Generating commit message with {model.name}"
max_tokens = model.info.get("max_input_tokens") or 0 with WaitingSpinner(spinner_text):
if max_tokens and num_tokens > max_tokens: num_tokens = model.token_count(messages)
continue max_tokens = model.info.get("max_input_tokens") or 0
commit_message = model.simple_send_with_retries(messages) if max_tokens and num_tokens > max_tokens:
if commit_message: continue
break commit_message = model.simple_send_with_retries(messages)
if commit_message:
break # Found a model that could generate the message
if not commit_message: if not commit_message:
self.io.tool_error("Failed to generate commit message!") self.io.tool_error("Failed to generate commit message!")
@ -386,14 +391,20 @@ class GitRepo:
try: try:
if current_branch_has_commits: if current_branch_has_commits:
args = ["HEAD", "--"] + list(fnames) args = ["HEAD", "--"] + list(fnames)
diffs += self.repo.git.diff(*args) diffs += self.repo.git.diff(*args, stdout_as_string=False).decode(
self.io.encoding, "replace"
)
return diffs return diffs
wd_args = ["--"] + list(fnames) wd_args = ["--"] + list(fnames)
index_args = ["--cached"] + wd_args index_args = ["--cached"] + wd_args
diffs += self.repo.git.diff(*index_args) diffs += self.repo.git.diff(*index_args, stdout_as_string=False).decode(
diffs += self.repo.git.diff(*wd_args) self.io.encoding, "replace"
)
diffs += self.repo.git.diff(*wd_args, stdout_as_string=False).decode(
self.io.encoding, "replace"
)
return diffs return diffs
except ANY_GIT_ERROR as err: except ANY_GIT_ERROR as err:
@ -407,7 +418,9 @@ class GitRepo:
args += ["--color=never"] args += ["--color=never"]
args += [from_commit, to_commit] args += [from_commit, to_commit]
diffs = self.repo.git.diff(*args) diffs = self.repo.git.diff(*args, stdout_as_string=False).decode(
self.io.encoding, "replace"
)
return diffs return diffs

View file

@ -19,7 +19,7 @@ from tqdm import tqdm
from aider.dump import dump from aider.dump import dump
from aider.special import filter_important_files from aider.special import filter_important_files
from aider.utils import Spinner from aider.waiting import Spinner
# tree_sitter is throwing a FutureWarning # tree_sitter is throwing a FutureWarning
warnings.simplefilter("ignore", category=FutureWarning) warnings.simplefilter("ignore", category=FutureWarning)

View file

@ -49,7 +49,7 @@
}, },
"openrouter/deepseek/deepseek-chat-v3-0324": { "openrouter/deepseek/deepseek-chat-v3-0324": {
"max_tokens": 8192, "max_tokens": 8192,
"max_input_tokens": 64000, "max_input_tokens": 131072,
"max_output_tokens": 8192, "max_output_tokens": 8192,
"input_cost_per_token": 0.00000055, "input_cost_per_token": 0.00000055,
"input_cost_per_token_cache_hit": 0.00000014, "input_cost_per_token_cache_hit": 0.00000014,

View file

@ -1435,4 +1435,333 @@
# top_k: 20 # top_k: 20
# min_p: 0.0 # min_p: 0.0
# temperature: 0.7 # temperature: 0.7
- name: claude-sonnet-4-20250514
edit_format: diff
weak_model_name: claude-3-5-haiku-20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: anthropic/claude-sonnet-4-20250514
edit_format: diff
weak_model_name: anthropic/claude-3-5-haiku-20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: anthropic/claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock/anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock/anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock_converse/anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock_converse/anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock_converse/us.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock_converse/us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: vertex_ai/claude-sonnet-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
max_tokens: 64000
editor_model_name: vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: vertex_ai-anthropic_models/vertex_ai/claude-sonnet-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
max_tokens: 64000
editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: openrouter/anthropic/claude-sonnet-4
edit_format: diff
weak_model_name: openrouter/anthropic/claude-3-5-haiku
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: openrouter/anthropic/claude-sonnet-4
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock_converse/eu.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock_converse/eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: eu.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: us.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: claude-opus-4-20250514
edit_format: diff
weak_model_name: claude-3-5-haiku-20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: anthropic/claude-opus-4-20250514
edit_format: diff
weak_model_name: anthropic/claude-3-5-haiku-20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: anthropic/claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock_converse/anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: bedrock_converse/anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock_converse/us.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: bedrock_converse/us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: bedrock_converse/eu.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: bedrock_converse/eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: eu.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: us.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: vertex_ai/claude-opus-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
max_tokens: 32000
editor_model_name: vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: vertex_ai-anthropic_models/vertex_ai/claude-opus-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
examples_as_sys_msg: false
extra_params:
max_tokens: 32000
editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]
- name: vertex_ai/gemini-2.5-flash-preview-05-20
edit_format: diff
use_repo_map: true
accepts_settings: ["reasoning_effort", "thinking_tokens"]
- name: openrouter/anthropic/claude-opus-4
edit_format: diff
weak_model_name: openrouter/anthropic/claude-3-5-haiku
use_repo_map: true
examples_as_sys_msg: false
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: openrouter/anthropic/claude-sonnet-4
editor_edit_format: editor-diff
accepts_settings: ["thinking_tokens"]

View file

@ -3,13 +3,12 @@ import platform
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import time
from pathlib import Path from pathlib import Path
import oslex import oslex
from rich.console import Console
from aider.dump import dump # noqa: F401 from aider.dump import dump # noqa: F401
from aider.waiting import Spinner
IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".webp", ".pdf"} IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".webp", ".pdf"}
@ -251,154 +250,6 @@ def run_install(cmd):
return False, output return False, output
class Spinner:
"""
Minimal spinner that scans a single marker back and forth across a line.
The animation is pre-rendered into a list of frames. If the terminal
cannot display unicode the frames are converted to plain ASCII.
"""
last_frame_idx = 0 # Class variable to store the last frame index
def __init__(self, text: str, width: int = 7):
self.text = text
self.start_time = time.time()
self.last_update = 0.0
self.visible = False
self.is_tty = sys.stdout.isatty()
self.console = Console()
# Pre-render the animation frames using pure ASCII so they will
# always display, even on very limited terminals.
ascii_frames = [
"#= ", # C1 C2 space(8)
"=# ", # C2 C1 space(8)
" =# ", # space(1) C2 C1 space(7)
" =# ", # space(2) C2 C1 space(6)
" =# ", # space(3) C2 C1 space(5)
" =# ", # space(4) C2 C1 space(4)
" =# ", # space(5) C2 C1 space(3)
" =# ", # space(6) C2 C1 space(2)
" =# ", # space(7) C2 C1 space(1)
" =#", # space(8) C2 C1
" #=", # space(8) C1 C2
" #= ", # space(7) C1 C2 space(1)
" #= ", # space(6) C1 C2 space(2)
" #= ", # space(5) C1 C2 space(3)
" #= ", # space(4) C1 C2 space(4)
" #= ", # space(3) C1 C2 space(5)
" #= ", # space(2) C1 C2 space(6)
" #= ", # space(1) C1 C2 space(7)
]
self.unicode_palette = "░█"
xlate_from, xlate_to = ("=#", self.unicode_palette)
# If unicode is supported, swap the ASCII chars for nicer glyphs.
if self._supports_unicode():
translation_table = str.maketrans(xlate_from, xlate_to)
frames = [f.translate(translation_table) for f in ascii_frames]
self.scan_char = xlate_to[xlate_from.find("#")]
else:
frames = ascii_frames
self.scan_char = "#"
# Bounce the scanner back and forth.
self.frames = frames
self.frame_idx = Spinner.last_frame_idx # Initialize from class variable
self.width = len(frames[0]) - 2 # number of chars between the brackets
self.animation_len = len(frames[0])
self.last_display_len = 0 # Length of the last spinner line (frame + text)
def _supports_unicode(self) -> bool:
if not self.is_tty:
return False
try:
out = self.unicode_palette
out += "\b" * len(self.unicode_palette)
out += " " * len(self.unicode_palette)
out += "\b" * len(self.unicode_palette)
sys.stdout.write(out)
sys.stdout.flush()
return True
except UnicodeEncodeError:
return False
except Exception:
return False
def _next_frame(self) -> str:
frame = self.frames[self.frame_idx]
self.frame_idx = (self.frame_idx + 1) % len(self.frames)
Spinner.last_frame_idx = self.frame_idx # Update class variable
return frame
def step(self, text: str = None) -> None:
if text is not None:
self.text = text
if not self.is_tty:
return
now = time.time()
if not self.visible and now - self.start_time >= 0.5:
self.visible = True
self.last_update = 0.0
if self.is_tty:
self.console.show_cursor(False)
if not self.visible or now - self.last_update < 0.1:
return
self.last_update = now
frame_str = self._next_frame()
# Determine the maximum width for the spinner line
# Subtract 2 as requested, to leave a margin or prevent cursor wrapping issues
max_spinner_width = self.console.width - 2
if max_spinner_width < 0: # Handle extremely narrow terminals
max_spinner_width = 0
current_text_payload = f" {self.text}"
line_to_display = f"{frame_str}{current_text_payload}"
# Truncate the line if it's too long for the console width
if len(line_to_display) > max_spinner_width:
line_to_display = line_to_display[:max_spinner_width]
len_line_to_display = len(line_to_display)
# Calculate padding to clear any remnants from a longer previous line
padding_to_clear = " " * max(0, self.last_display_len - len_line_to_display)
# Write the spinner frame, text, and any necessary clearing spaces
sys.stdout.write(f"\r{line_to_display}{padding_to_clear}")
self.last_display_len = len_line_to_display
# Calculate number of backspaces to position cursor at the scanner character
scan_char_abs_pos = frame_str.find(self.scan_char)
# Total characters written to the line (frame + text + padding)
total_chars_written_on_line = len_line_to_display + len(padding_to_clear)
# num_backspaces will be non-positive if scan_char_abs_pos is beyond
# total_chars_written_on_line (e.g., if the scan char itself was truncated).
# (e.g., if the scan char itself was truncated).
# In such cases, (effectively) 0 backspaces are written,
# and the cursor stays at the end of the line.
num_backspaces = total_chars_written_on_line - scan_char_abs_pos
sys.stdout.write("\b" * num_backspaces)
sys.stdout.flush()
def end(self) -> None:
if self.visible and self.is_tty:
clear_len = self.last_display_len # Use the length of the last displayed content
sys.stdout.write("\r" + " " * clear_len + "\r")
sys.stdout.flush()
self.console.show_cursor(True)
self.visible = False
def find_common_root(abs_fnames): def find_common_root(abs_fnames):
try: try:
if len(abs_fnames) == 1: if len(abs_fnames) == 1:
@ -485,20 +336,3 @@ def printable_shell_command(cmd_list):
str: Shell-escaped command string. str: Shell-escaped command string.
""" """
return oslex.join(cmd_list) return oslex.join(cmd_list)
def main():
spinner = Spinner("Running spinner...")
try:
for _ in range(100):
time.sleep(0.15)
spinner.step()
print("Success!")
except KeyboardInterrupt:
print("\nInterrupted by user.")
finally:
spinner.end()
if __name__ == "__main__":
main()

View file

@ -13,10 +13,159 @@ Use it like:
spinner.stop() spinner.stop()
""" """
import sys
import threading import threading
import time import time
from aider.utils import Spinner from rich.console import Console
class Spinner:
"""
Minimal spinner that scans a single marker back and forth across a line.
The animation is pre-rendered into a list of frames. If the terminal
cannot display unicode the frames are converted to plain ASCII.
"""
last_frame_idx = 0 # Class variable to store the last frame index
def __init__(self, text: str, width: int = 7):
self.text = text
self.start_time = time.time()
self.last_update = 0.0
self.visible = False
self.is_tty = sys.stdout.isatty()
self.console = Console()
# Pre-render the animation frames using pure ASCII so they will
# always display, even on very limited terminals.
ascii_frames = [
"#= ", # C1 C2 space(8)
"=# ", # C2 C1 space(8)
" =# ", # space(1) C2 C1 space(7)
" =# ", # space(2) C2 C1 space(6)
" =# ", # space(3) C2 C1 space(5)
" =# ", # space(4) C2 C1 space(4)
" =# ", # space(5) C2 C1 space(3)
" =# ", # space(6) C2 C1 space(2)
" =# ", # space(7) C2 C1 space(1)
" =#", # space(8) C2 C1
" #=", # space(8) C1 C2
" #= ", # space(7) C1 C2 space(1)
" #= ", # space(6) C1 C2 space(2)
" #= ", # space(5) C1 C2 space(3)
" #= ", # space(4) C1 C2 space(4)
" #= ", # space(3) C1 C2 space(5)
" #= ", # space(2) C1 C2 space(6)
" #= ", # space(1) C1 C2 space(7)
]
self.unicode_palette = "░█"
xlate_from, xlate_to = ("=#", self.unicode_palette)
# If unicode is supported, swap the ASCII chars for nicer glyphs.
if self._supports_unicode():
translation_table = str.maketrans(xlate_from, xlate_to)
frames = [f.translate(translation_table) for f in ascii_frames]
self.scan_char = xlate_to[xlate_from.find("#")]
else:
frames = ascii_frames
self.scan_char = "#"
# Bounce the scanner back and forth.
self.frames = frames
self.frame_idx = Spinner.last_frame_idx # Initialize from class variable
self.width = len(frames[0]) - 2 # number of chars between the brackets
self.animation_len = len(frames[0])
self.last_display_len = 0 # Length of the last spinner line (frame + text)
def _supports_unicode(self) -> bool:
if not self.is_tty:
return False
try:
out = self.unicode_palette
out += "\b" * len(self.unicode_palette)
out += " " * len(self.unicode_palette)
out += "\b" * len(self.unicode_palette)
sys.stdout.write(out)
sys.stdout.flush()
return True
except UnicodeEncodeError:
return False
except Exception:
return False
def _next_frame(self) -> str:
frame = self.frames[self.frame_idx]
self.frame_idx = (self.frame_idx + 1) % len(self.frames)
Spinner.last_frame_idx = self.frame_idx # Update class variable
return frame
def step(self, text: str = None) -> None:
if text is not None:
self.text = text
if not self.is_tty:
return
now = time.time()
if not self.visible and now - self.start_time >= 0.5:
self.visible = True
self.last_update = 0.0
if self.is_tty:
self.console.show_cursor(False)
if not self.visible or now - self.last_update < 0.1:
return
self.last_update = now
frame_str = self._next_frame()
# Determine the maximum width for the spinner line
# Subtract 2 as requested, to leave a margin or prevent cursor wrapping issues
max_spinner_width = self.console.width - 2
if max_spinner_width < 0: # Handle extremely narrow terminals
max_spinner_width = 0
current_text_payload = f" {self.text}"
line_to_display = f"{frame_str}{current_text_payload}"
# Truncate the line if it's too long for the console width
if len(line_to_display) > max_spinner_width:
line_to_display = line_to_display[:max_spinner_width]
len_line_to_display = len(line_to_display)
# Calculate padding to clear any remnants from a longer previous line
padding_to_clear = " " * max(0, self.last_display_len - len_line_to_display)
# Write the spinner frame, text, and any necessary clearing spaces
sys.stdout.write(f"\r{line_to_display}{padding_to_clear}")
self.last_display_len = len_line_to_display
# Calculate number of backspaces to position cursor at the scanner character
scan_char_abs_pos = frame_str.find(self.scan_char)
# Total characters written to the line (frame + text + padding)
total_chars_written_on_line = len_line_to_display + len(padding_to_clear)
# num_backspaces will be non-positive if scan_char_abs_pos is beyond
# total_chars_written_on_line (e.g., if the scan char itself was truncated).
# (e.g., if the scan char itself was truncated).
# In such cases, (effectively) 0 backspaces are written,
# and the cursor stays at the end of the line.
num_backspaces = total_chars_written_on_line - scan_char_abs_pos
sys.stdout.write("\b" * num_backspaces)
sys.stdout.flush()
def end(self) -> None:
if self.visible and self.is_tty:
clear_len = self.last_display_len # Use the length of the last displayed content
sys.stdout.write("\r" + " " * clear_len + "\r")
sys.stdout.flush()
self.console.show_cursor(True)
self.visible = False
class WaitingSpinner: class WaitingSpinner:
@ -30,8 +179,8 @@ class WaitingSpinner:
def _spin(self): def _spin(self):
while not self._stop_event.is_set(): while not self._stop_event.is_set():
time.sleep(self.delay)
self.spinner.step() self.spinner.step()
time.sleep(self.delay)
self.spinner.end() self.spinner.end()
def start(self): def start(self):
@ -43,7 +192,7 @@ class WaitingSpinner:
"""Request the spinner to stop and wait briefly for the thread to exit.""" """Request the spinner to stop and wait briefly for the thread to exit."""
self._stop_event.set() self._stop_event.set()
if self._thread.is_alive(): if self._thread.is_alive():
self._thread.join(timeout=0.1) self._thread.join(timeout=self.delay)
self.spinner.end() self.spinner.end()
# Allow use as a context-manager # Allow use as a context-manager
@ -53,3 +202,20 @@ class WaitingSpinner:
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
self.stop() self.stop()
def main():
spinner = Spinner("Running spinner...")
try:
for _ in range(100):
time.sleep(0.15)
spinner.step()
print("Success!")
except KeyboardInterrupt:
print("\nInterrupted by user.")
finally:
spinner.end()
if __name__ == "__main__":
main()

View file

@ -24,7 +24,34 @@ cog.out(text)
]]]--> ]]]-->
### main branch ### Aider v0.84.0
- Added support for new Claude models including the Sonnet 4 and Opus 4 series (e.g., `claude-sonnet-4-20250514`,
`claude-opus-4-20250514`) across various providers. The default `sonnet` and `opus` aliases were updated to these newer
versions.
- Added support for the `vertex_ai/gemini-2.5-flash-preview-05-20` model.
- Fixed OpenRouter token cost calculation for improved accuracy.
- Updated default OpenRouter models during onboarding to `deepseek/deepseek-r1:free` for the free tier and
`anthropic/claude-sonnet-4` for paid tiers.
- Automatically refresh GitHub Copilot tokens when used as OpenAI API keys, by Lih Chen.
- Aider wrote 79% of the code in this release.
### Aider v0.83.2
- Bumped configargparse to 1.7.1 as 1.7 was pulled.
- Added shell tab completion for file path arguments (by saviour) and for `--edit-format`/`--editor-edit-format` options.
- Improved OpenRouter model metadata handling by introducing a local cache, increasing reliability and performance.
- The `/settings` command now displays detailed metadata for active main, editor, and weak models.
- Fixed an issue where files explicitly added via the command line were not correctly ignored if listed in `.gitignore`.
- Improved automatic commit messages by providing more context during their generation, by wangboxue.
### Aider v0.83.1
- Improved user language detection by correctly normalizing hyphenated language codes (e.g., `en-US` to `en`) and enhancing the validation of locale results.
- Prevented Aider from instructing the LLM to reply in 'C' or 'POSIX' when these are detected as the system locale.
- Displayed a spinner with the model name when generating commit messages.
### Aider v0.83.0
- Added support for `gemini-2.5-pro-preview-05-06` models. - Added support for `gemini-2.5-pro-preview-05-06` models.
- Added support for `qwen3-235b` models. - Added support for `qwen3-235b` models.
@ -428,7 +455,7 @@ cog.out(text)
- [Aider works with LLM web chat UIs](https://aider.chat/docs/usage/copypaste.html). - [Aider works with LLM web chat UIs](https://aider.chat/docs/usage/copypaste.html).
- New `--copy-paste` mode. - New `--copy-paste` mode.
- New `/copy-context` command. - New `/copy-context` command.
- [Set API keys and other environment variables for all providers from command line or yaml conf file](https://aider.chat/docs/config/aider_conf.html#storing-llm-keys). - [Set API keys and other environment variables for all providers from command line or YAML conf file](https://aider.chat/docs/config/aider_conf.html#storing-llm-keys).
- New `--api-key provider=key` setting. - New `--api-key provider=key` setting.
- New `--set-env VAR=value` setting. - New `--set-env VAR=value` setting.
- Added bash and zsh support to `--watch-files`. - Added bash and zsh support to `--watch-files`.
@ -596,7 +623,7 @@ cog.out(text)
### Aider v0.59.1 ### Aider v0.59.1
- Check for obsolete `yes: true` in yaml config, show helpful error. - Check for obsolete `yes: true` in YAML config, show helpful error.
- Model settings for openrouter/anthropic/claude-3.5-sonnet:beta - Model settings for openrouter/anthropic/claude-3.5-sonnet:beta
### Aider v0.59.0 ### Aider v0.59.0
@ -606,7 +633,7 @@ cog.out(text)
- Still auto-completes the full paths of the repo files like `/add`. - Still auto-completes the full paths of the repo files like `/add`.
- Now supports globs like `src/**/*.py` - Now supports globs like `src/**/*.py`
- Renamed `--yes` to `--yes-always`. - Renamed `--yes` to `--yes-always`.
- Now uses `AIDER_YES_ALWAYS` env var and `yes-always:` yaml key. - Now uses `AIDER_YES_ALWAYS` env var and `yes-always:` YAML key.
- Existing YAML and .env files will need to be updated. - Existing YAML and .env files will need to be updated.
- Can still abbreviate to `--yes` on the command line. - Can still abbreviate to `--yes` on the command line.
- Config file now uses standard YAML list syntax with ` - list entries`, one per line. - Config file now uses standard YAML list syntax with ` - list entries`, one per line.
@ -813,7 +840,7 @@ cog.out(text)
- Use `--map-refresh <always|files|manual|auto>` to configure. - Use `--map-refresh <always|files|manual|auto>` to configure.
- Improved cost estimate logic for caching. - Improved cost estimate logic for caching.
- Improved editing performance on Jupyter Notebook `.ipynb` files. - Improved editing performance on Jupyter Notebook `.ipynb` files.
- Show which config yaml file is loaded with `--verbose`. - Show which config YAML file is loaded with `--verbose`.
- Bumped dependency versions. - Bumped dependency versions.
- Bugfix: properly load `.aider.models.metadata.json` data. - Bugfix: properly load `.aider.models.metadata.json` data.
- Bugfix: Using `--msg /ask ...` caused an exception. - Bugfix: Using `--msg /ask ...` caused an exception.

View file

@ -4500,3 +4500,228 @@
Paul Gauthier (aider): 1567 Paul Gauthier (aider): 1567
start_tag: v0.81.0 start_tag: v0.81.0
total_lines: 1706 total_lines: 1706
- aider_percentage: 54.32
aider_total: 1409
end_date: '2025-05-09'
end_tag: v0.83.0
file_counts:
.github/workflows/check_pypi_version.yml:
Paul Gauthier (aider): 1
.github/workflows/pre-commit.yml:
MDW: 48
.github/workflows/ubuntu-tests.yml:
Paul Gauthier (aider): 1
.github/workflows/windows-tests.yml:
Paul Gauthier (aider): 1
.github/workflows/windows_check_pypi_version.yml:
Paul Gauthier (aider): 1
aider/__init__.py:
Paul Gauthier: 1
aider/args.py:
Andrew Grigorev: 21
Andrew Grigorev (aider): 5
Paul Gauthier (aider): 38
aider/coders/__init__.py:
Paul Gauthier (aider): 2
aider/coders/base_coder.py:
Andrew Grigorev (aider): 2
Paul Gauthier: 60
Paul Gauthier (aider): 104
aider/coders/editblock_coder.py:
Paul Gauthier: 10
Paul Gauthier (aider): 7
zjy1412: 2
aider/coders/editblock_fenced_coder.py:
MDW: 1
aider/coders/help_coder.py:
MDW: 1
aider/coders/patch_coder.py:
Paul Gauthier (aider): 38
aider/coders/shell.py:
Paul Gauthier: 37
aider/coders/udiff_coder.py:
Paul Gauthier: 2
Paul Gauthier (aider): 9
aider/coders/udiff_simple.py:
Paul Gauthier (aider): 14
aider/commands.py:
Andrew Grigorev: 10
Paul Gauthier: 7
Paul Gauthier (aider): 1
aider/gui.py:
Jon Keys: 2
aider/io.py:
Kay Gosho: 1
Paul Gauthier (aider): 5
aider/linter.py:
Paul Gauthier: 1
Titusz Pan: 1
aider/main.py:
Paul Gauthier (aider): 9
aider/mdstream.py:
Paul Gauthier (aider): 11
aider/models.py:
Paul Gauthier: 4
Paul Gauthier (aider): 66
Stefan Hladnik: 4
Stefan Hladnik (aider): 41
aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm:
Andrey Popp: 98
aider/queries/tree-sitter-languages/ocaml_interface-tags.scm:
Andrey Popp: 98
aider/repo.py:
Andrew Grigorev: 115
Andrew Grigorev (aider): 21
Paul Gauthier: 6
Paul Gauthier (aider): 33
aider/repomap.py:
Paul Gauthier: 5
Paul Gauthier (aider): 6
aider/resources/model-settings.yml:
Paul Gauthier: 183
Paul Gauthier (aider): 175
cantalupo555: 1
aider/scrape.py:
Jon Keys: 12
aider/utils.py:
Paul Gauthier: 13
Paul Gauthier (aider): 131
Titusz Pan: 1
aider/waiting.py:
Paul Gauthier: 1
Paul Gauthier (aider): 54
aider/watch.py:
Paul Gauthier: 6
Paul Gauthier (aider): 7
aider/website/_includes/leaderboard_table.js:
Paul Gauthier: 2
Paul Gauthier (aider): 18
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
Paul Gauthier (aider): 2
aider/website/index.html:
Paul Gauthier: 13
benchmark/benchmark.py:
Paul Gauthier: 3
Paul Gauthier (aider): 42
benchmark/docker.sh:
Paul Gauthier: 2
benchmark/refactor_tools.py:
MDW: 1
scripts/30k-image.py:
MDW: 1
scripts/clean_metadata.py:
Paul Gauthier (aider): 258
scripts/update-history.py:
Paul Gauthier: 2
Paul Gauthier (aider): 7
tests/basic/test_coder.py:
Paul Gauthier (aider): 3
tests/basic/test_commands.py:
Paul Gauthier: 2
Paul Gauthier (aider): 90
tests/basic/test_editblock.py:
Paul Gauthier: 10
zjy1412: 52
tests/basic/test_io.py:
Paul Gauthier (aider): 132
tests/basic/test_linter.py:
Paul Gauthier: 22
Titusz Pan: 10
tests/basic/test_repo.py:
Andrew Grigorev: 75
Andrew Grigorev (aider): 65
Paul Gauthier: 79
Paul Gauthier (aider): 6
tests/basic/test_repomap.py:
Andrey Popp: 7
tests/basic/test_watch.py:
MDW: 1
tests/fixtures/languages/ocaml_interface/test.mli:
Andrey Popp: 14
tests/scrape/test_playwright_disable.py:
Andrew Grigorev: 111
Paul Gauthier: 25
Paul Gauthier (aider): 3
grand_total:
Andrew Grigorev: 332
Andrew Grigorev (aider): 93
Andrey Popp: 217
Jon Keys: 14
Kay Gosho: 1
MDW: 53
Paul Gauthier: 497
Paul Gauthier (aider): 1275
Stefan Hladnik: 4
Stefan Hladnik (aider): 41
Titusz Pan: 12
cantalupo555: 1
zjy1412: 54
start_tag: v0.82.0
total_lines: 2594
- aider_percentage: 78.92
aider_total: 655
end_date: '2025-05-30'
end_tag: v0.84.0
file_counts:
aider/__init__.py:
Paul Gauthier: 1
aider/args.py:
Paul Gauthier (aider): 27
saviour: 2
aider/args_formatter.py:
Paul Gauthier: 1
aider/coders/base_coder.py:
Paul Gauthier: 4
Paul Gauthier (aider): 10
aider/commands.py:
Paul Gauthier (aider): 23
wangboxue: 1
aider/models.py:
Lih Chen: 15
Paul Gauthier: 16
Paul Gauthier (aider): 12
aider/onboarding.py:
Paul Gauthier: 2
aider/openrouter.py:
Paul Gauthier (aider): 120
aider/repo.py:
Paul Gauthier: 1
Paul Gauthier (aider): 10
aider/repomap.py:
Paul Gauthier (aider): 1
aider/resources/model-settings.yml:
Paul Gauthier: 71
Paul Gauthier (aider): 193
Trung Dinh: 11
aider/utils.py:
Paul Gauthier (aider): 1
aider/waiting.py:
Paul Gauthier: 2
Paul Gauthier (aider): 6
aider/website/docs/leaderboards/index.md:
Paul Gauthier: 1
aider/website/index.html:
Paul Gauthier: 43
scripts/update-history.py:
Paul Gauthier: 2
tests/basic/test_coder.py:
Paul Gauthier: 2
Paul Gauthier (aider): 144
tests/basic/test_main.py:
Paul Gauthier (aider): 28
tests/basic/test_models.py:
Paul Gauthier (aider): 2
tests/basic/test_onboarding.py:
Paul Gauthier (aider): 5
tests/basic/test_openrouter.py:
Paul Gauthier (aider): 73
grand_total:
Lih Chen: 15
Paul Gauthier: 146
Paul Gauthier (aider): 655
Trung Dinh: 11
saviour: 2
wangboxue: 1
start_tag: v0.83.0
total_lines: 830

View file

@ -1279,30 +1279,202 @@
seconds_per_case: 372.2 seconds_per_case: 372.2
total_cost: 0.7603 total_cost: 0.7603
- dirname: 2025-05-08-03-22-37--qwen3-235b-defaults - dirname: 2025-05-09-17-02-02--qwen3-235b-a22b.unthink_16k_diff
test_cases: 225 test_cases: 225
model: Qwen3 235B A22B model: Qwen3 235B A22B diff, no think, Alibaba API
edit_format: diff edit_format: diff
commit_hash: aaacee5-dirty commit_hash: 91d7fbd-dirty
pass_rate_1: 17.3 pass_rate_1: 28.9
pass_rate_2: 49.8 pass_rate_2: 59.6
pass_num_1: 39 pass_num_1: 65
pass_num_2: 112 pass_num_2: 134
percent_cases_well_formed: 91.6 percent_cases_well_formed: 92.9
error_outputs: 58 error_outputs: 22
num_malformed_responses: 29 num_malformed_responses: 22
num_with_malformed_responses: 19 num_with_malformed_responses: 16
user_asks: 102 user_asks: 111
lazy_comments: 0 lazy_comments: 0
syntax_errors: 0 syntax_errors: 0
indentation_errors: 0 indentation_errors: 0
exhausted_context_windows: 0 exhausted_context_windows: 0
prompt_tokens: 0 prompt_tokens: 2816192
completion_tokens: 0 completion_tokens: 342062
test_timeouts: 1 test_timeouts: 1
total_tests: 225 total_tests: 225
command: aider --model openrouter/qwen/qwen3-235b-a22b command: aider --model openai/qwen3-235b-a22b
date: 2025-05-08 date: 2025-05-09
versions: 0.82.4.dev versions: 0.82.4.dev
seconds_per_case: 428.1 seconds_per_case: 45.4
total_cost: 1.8037 total_cost: 0.0000
- dirname: 2025-05-24-21-17-54--sonnet4-diff-exuser
test_cases: 225
model: claude-sonnet-4-20250514 (no thinking)
edit_format: diff
commit_hash: ef3f8bb-dirty
pass_rate_1: 20.4
pass_rate_2: 56.4
pass_num_1: 46
pass_num_2: 127
percent_cases_well_formed: 98.2
error_outputs: 6
num_malformed_responses: 4
num_with_malformed_responses: 4
user_asks: 129
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 1
prompt_tokens: 3460663
completion_tokens: 433373
test_timeouts: 7
total_tests: 225
command: aider --model claude-sonnet-4-20250514
date: 2025-05-24
versions: 0.83.3.dev
seconds_per_case: 29.8
total_cost: 15.8155
- dirname: 2025-05-24-22-10-36--sonnet4-diff-exuser-think32k
test_cases: 225
model: claude-sonnet-4-20250514 (32k thinking)
edit_format: diff
commit_hash: e3cb907
thinking_tokens: 32000
pass_rate_1: 25.8
pass_rate_2: 61.3
pass_num_1: 58
pass_num_2: 138
percent_cases_well_formed: 97.3
error_outputs: 10
num_malformed_responses: 10
num_with_malformed_responses: 6
user_asks: 111
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 2863068
completion_tokens: 1271074
test_timeouts: 6
total_tests: 225
command: aider --model claude-sonnet-4-20250514
date: 2025-05-24
versions: 0.83.3.dev
seconds_per_case: 79.9
total_cost: 26.5755
- dirname: 2025-05-25-19-57-20--opus4-diff-exuser
test_cases: 225
model: claude-opus-4-20250514 (no think)
edit_format: diff
commit_hash: 9ef3211
pass_rate_1: 32.9
pass_rate_2: 70.7
pass_num_1: 74
pass_num_2: 159
percent_cases_well_formed: 98.7
error_outputs: 3
num_malformed_responses: 3
num_with_malformed_responses: 3
user_asks: 105
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 2671437
completion_tokens: 380717
test_timeouts: 3
total_tests: 225
command: aider --model claude-opus-4-20250514
date: 2025-05-25
versions: 0.83.3.dev
seconds_per_case: 42.5
total_cost: 68.6253
- dirname: 2025-05-25-20-40-51--opus4-diff-exuser
test_cases: 225
model: claude-opus-4-20250514 (32k thinking)
edit_format: diff
commit_hash: 9ef3211
thinking_tokens: 32000
pass_rate_1: 37.3
pass_rate_2: 72.0
pass_num_1: 84
pass_num_2: 162
percent_cases_well_formed: 97.3
error_outputs: 10
num_malformed_responses: 6
num_with_malformed_responses: 6
user_asks: 97
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 2567514
completion_tokens: 363142
test_timeouts: 4
total_tests: 225
command: aider --model claude-opus-4-20250514
date: 2025-05-25
versions: 0.83.3.dev
seconds_per_case: 44.1
total_cost: 65.7484
- dirname: 2025-05-26-15-56-31--flash25-05-20-24k-think # dirname is misleading
test_cases: 225
model: gemini-2.5-flash-preview-05-20 (no think)
edit_format: diff
commit_hash: 214b811-dirty
thinking_tokens: 0 # <-- no thinking
pass_rate_1: 20.9
pass_rate_2: 44.0
pass_num_1: 47
pass_num_2: 99
percent_cases_well_formed: 93.8
error_outputs: 16
num_malformed_responses: 16
num_with_malformed_responses: 14
user_asks: 79
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 5512458
completion_tokens: 514145
test_timeouts: 4
total_tests: 225
command: aider --model gemini/gemini-2.5-flash-preview-05-20
date: 2025-05-26
versions: 0.83.3.dev
seconds_per_case: 12.2
total_cost: 1.1354
- dirname: 2025-05-25-22-58-44--flash25-05-20-24k-think
test_cases: 225
model: gemini-2.5-flash-preview-05-20 (24k think)
edit_format: diff
commit_hash: a8568c3-dirty
thinking_tokens: 24576
pass_rate_1: 26.2
pass_rate_2: 55.1
pass_num_1: 59
pass_num_2: 124
percent_cases_well_formed: 95.6
error_outputs: 15
num_malformed_responses: 15
num_with_malformed_responses: 10
user_asks: 101
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 3666792
completion_tokens: 2703162
test_timeouts: 4
total_tests: 225
command: aider --model gemini/gemini-2.5-flash-preview-05-20
date: 2025-05-25
versions: 0.83.3.dev
seconds_per_case: 53.9
total_cost: 8.5625

View file

@ -213,3 +213,60 @@
versions: 0.82.4.dev versions: 0.82.4.dev
seconds_per_case: 635.2 seconds_per_case: 635.2
total_cost: 0.0000 total_cost: 0.0000
- dirname: 2025-05-09-17-02-02--qwen3-235b-a22b.unthink_16k_diff
test_cases: 225
model: Qwen3 235B A22B diff, no think, via official Alibaba API
edit_format: diff
commit_hash: 91d7fbd-dirty
pass_rate_1: 28.9
pass_rate_2: 59.6
pass_num_1: 65
pass_num_2: 134
percent_cases_well_formed: 92.9
error_outputs: 22
num_malformed_responses: 22
num_with_malformed_responses: 16
user_asks: 111
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 2816192
completion_tokens: 342062
test_timeouts: 1
total_tests: 225
command: aider --model openai/qwen3-235b-a22b
date: 2025-05-09
versions: 0.82.4.dev
seconds_per_case: 45.4
total_cost: 0.0000
- dirname: 2025-05-09-23-01-22--qwen3-235b-a22b.unthink_16k_whole
test_cases: 225
model: Qwen3 235B A22B whole, no think, via official Alibaba API
edit_format: whole
commit_hash: 425fb6d
pass_rate_1: 26.7
pass_rate_2: 61.8
pass_num_1: 60
pass_num_2: 139
percent_cases_well_formed: 100.0
error_outputs: 0
num_malformed_responses: 0
num_with_malformed_responses: 0
user_asks: 175
lazy_comments: 0
syntax_errors: 0
indentation_errors: 0
exhausted_context_windows: 0
prompt_tokens: 2768173
completion_tokens: 384000
test_timeouts: 1
total_tests: 225
command: aider --model openai/qwen3-235b-a22b
date: 2025-05-09
versions: 0.82.4.dev
seconds_per_case: 50.8
total_cost: 0.0000

View file

@ -15,12 +15,12 @@ nav_exclude: true
I recently wanted to draw a graph showing how LLM code editing skill has been I recently wanted to draw a graph showing how LLM code editing skill has been
changing over time as new models have been released by OpenAI, Anthropic and others. changing over time as new models have been released by OpenAI, Anthropic and others.
I have all the I have all the
[data in a yaml file](https://github.com/Aider-AI/aider/blob/main/website/_data/edit_leaderboard.yml) that is used to render [data in a YAML file](https://github.com/Aider-AI/aider/blob/main/website/_data/edit_leaderboard.yml) that is used to render
[aider's LLM leaderboards](https://aider.chat/docs/leaderboards/). [aider's LLM leaderboards](https://aider.chat/docs/leaderboards/).
Below is the aider chat transcript, which shows: Below is the aider chat transcript, which shows:
- I launch aider with the yaml file, a file with other plots I've done recently (so GPT can crib the style) and an empty file called `over_time.py`. - I launch aider with the YAML file, a file with other plots I've done recently (so GPT can crib the style) and an empty file called `over_time.py`.
- Then I ask GPT to draw the scatterplot I want. - Then I ask GPT to draw the scatterplot I want.
- I run the resulting script and share the error output with GPT so it can fix a small bug. - I run the resulting script and share the error output with GPT so it can fix a small bug.
- I ask it to color the points for GPT-4 and GPT-3.5 family models differently, to better see trends within those model families. - I ask it to color the points for GPT-4 and GPT-3.5 family models differently, to better see trends within those model families.
@ -28,7 +28,7 @@ Below is the aider chat transcript, which shows:
- I work through a series of other small style changes, like changing fonts and the graph border. - I work through a series of other small style changes, like changing fonts and the graph border.
In the end I have the graph, but I also have the python code in my repo. In the end I have the graph, but I also have the python code in my repo.
So I can update this graph easily whenever I add new entries to the yaml data file. So I can update this graph easily whenever I add new entries to the YAML data file.
## Aider chat transcript ## Aider chat transcript

View file

@ -277,6 +277,31 @@ const LEADERBOARD_CUSTOM_TITLE = "Qwen3 results on the aider polyglot benchmark"
</script> </script>
## No think, via official Alibaba API
These results were obtained running against `https://dashscope.aliyuncs.com/compatible-mode/v1`
with no thinking.
```bash
export OPENAI_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1
export OPENAI_API_KEY=<key>
```
```yaml
- name: openai/qwen3-235b-a22b
use_temperature: 0.7
streaming: false
extra_params:
stream: false
max_tokens: 16384
top_p: 0.8
top_k: 20
temperature: 0.7
enable_thinking: false
extra_body:
enable_thinking: false
```
## OpenRouter only TogetherAI, recommended /no_think settings ## OpenRouter only TogetherAI, recommended /no_think settings
These results were obtained with the These results were obtained with the

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
# Place in your home dir, or at the root of your git repo. # Place in your home dir, or at the root of your git repo.
########################################################## ##########################################################
# Note: You can only put OpenAI and Anthropic API keys in the yaml # Note: You can only put OpenAI and Anthropic API keys in the YAML
# config file. Keys for all APIs can be stored in a .env file # config file. Keys for all APIs can be stored in a .env file
# https://aider.chat/docs/config/dotenv.html # https://aider.chat/docs/config/dotenv.html
@ -386,6 +386,9 @@
## Specify the language to use in the chat (default: None, uses system settings) ## Specify the language to use in the chat (default: None, uses system settings)
#chat-language: xxx #chat-language: xxx
## Specify the language to use in the commit message (default: None, user language)
#commit-language: xxx
## Always say yes to every confirmation ## Always say yes to every confirmation
#yes-always: false #yes-always: false

View file

@ -81,7 +81,7 @@ You can override or add settings for any model by creating a `.aider.model.setti
If the files above exist, they will be loaded in that order. If the files above exist, they will be loaded in that order.
Files loaded last will take priority. Files loaded last will take priority.
The yaml file should be a list of dictionary objects for each model. The YAML file should be a list of dictionary objects for each model.
### Passing extra params to litellm.completion ### Passing extra params to litellm.completion
@ -158,6 +158,34 @@ cog.out("```\n")
system_prompt_prefix: null system_prompt_prefix: null
accepts_settings: null accepts_settings: null
- name: anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: anthropic/claude-3-5-haiku-20241022 - name: anthropic/claude-3-5-haiku-20241022
edit_format: diff edit_format: diff
weak_model_name: anthropic/claude-3-5-haiku-20241022 weak_model_name: anthropic/claude-3-5-haiku-20241022
@ -246,6 +274,34 @@ cog.out("```\n")
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25 anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
cache_control: true cache_control: true
- name: anthropic/claude-opus-4-20250514
edit_format: diff
weak_model_name: anthropic/claude-3-5-haiku-20241022
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: anthropic/claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: anthropic/claude-sonnet-4-20250514
edit_format: diff
weak_model_name: anthropic/claude-3-5-haiku-20241022
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: anthropic/claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: azure/gpt-4.1 - name: azure/gpt-4.1
edit_format: diff edit_format: diff
weak_model_name: azure/gpt-4.1-mini weak_model_name: azure/gpt-4.1-mini
@ -407,6 +463,20 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- thinking_tokens - thinking_tokens
- name: bedrock/anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock/anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0 - name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0
edit_format: diff edit_format: diff
weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0 weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0
@ -423,6 +493,20 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- thinking_tokens - thinking_tokens
- name: bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0 - name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0
edit_format: diff edit_format: diff
weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0 weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0
@ -439,6 +523,62 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- thinking_tokens - thinking_tokens
- name: bedrock_converse/anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: bedrock_converse/anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock_converse/anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock_converse/anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock_converse/eu.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: bedrock_converse/eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock_converse/eu.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock_converse/eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0 - name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0
edit_format: diff edit_format: diff
weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0 weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0
@ -455,6 +595,34 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- thinking_tokens - thinking_tokens
- name: bedrock_converse/us.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: bedrock_converse/us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: bedrock_converse/us.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: bedrock_converse/us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: claude-3-5-haiku-20241022 - name: claude-3-5-haiku-20241022
edit_format: diff edit_format: diff
weak_model_name: claude-3-5-haiku-20241022 weak_model_name: claude-3-5-haiku-20241022
@ -538,6 +706,34 @@ cog.out("```\n")
- name: claude-3-sonnet-20240229 - name: claude-3-sonnet-20240229
weak_model_name: claude-3-5-haiku-20241022 weak_model_name: claude-3-5-haiku-20241022
- name: claude-opus-4-20250514
edit_format: diff
weak_model_name: claude-3-5-haiku-20241022
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: claude-sonnet-4-20250514
edit_format: diff
weak_model_name: claude-3-5-haiku-20241022
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: claude-sonnet-4-20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: cohere_chat/command-a-03-2025 - name: cohere_chat/command-a-03-2025
examples_as_sys_msg: true examples_as_sys_msg: true
@ -600,6 +796,34 @@ cog.out("```\n")
editor_model_name: deepseek/deepseek-chat editor_model_name: deepseek/deepseek-chat
editor_edit_format: editor-diff editor_edit_format: editor-diff
- name: eu.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: eu.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: eu.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: eu.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: fireworks_ai/accounts/fireworks/models/deepseek-r1 - name: fireworks_ai/accounts/fireworks/models/deepseek-r1
edit_format: diff edit_format: diff
weak_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3 weak_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3
@ -1145,6 +1369,34 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- thinking_tokens - thinking_tokens
- name: openrouter/anthropic/claude-opus-4
edit_format: diff
weak_model_name: openrouter/anthropic/claude-3-5-haiku
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: openrouter/anthropic/claude-sonnet-4
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: openrouter/anthropic/claude-sonnet-4
edit_format: diff
weak_model_name: openrouter/anthropic/claude-3-5-haiku
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: openrouter/anthropic/claude-sonnet-4
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: openrouter/cohere/command-a-03-2025 - name: openrouter/cohere/command-a-03-2025
examples_as_sys_msg: true examples_as_sys_msg: true
@ -1434,6 +1686,34 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- reasoning_effort - reasoning_effort
- name: us.anthropic.claude-opus-4-20250514-v1:0
edit_format: diff
weak_model_name: us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 32000
cache_control: true
editor_model_name: us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: us.anthropic.claude-sonnet-4-20250514-v1:0
edit_format: diff
weak_model_name: us.anthropic.claude-3-5-haiku-20241022-v1:0
use_repo_map: true
extra_params:
extra_headers:
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
max_tokens: 64000
cache_control: true
editor_model_name: us.anthropic.claude-sonnet-4-20250514-v1:0
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219 - name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219
edit_format: diff edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022 weak_model_name: vertex_ai/claude-3-5-haiku@20241022
@ -1447,6 +1727,28 @@ cog.out("```\n")
accepts_settings: accepts_settings:
- thinking_tokens - thinking_tokens
- name: vertex_ai-anthropic_models/vertex_ai/claude-opus-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
extra_params:
max_tokens: 32000
editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: vertex_ai-anthropic_models/vertex_ai/claude-sonnet-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
extra_params:
max_tokens: 64000
editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 - name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17
edit_format: diff edit_format: diff
use_repo_map: true use_repo_map: true
@ -1502,6 +1804,35 @@ cog.out("```\n")
- name: vertex_ai/claude-3-sonnet@20240229 - name: vertex_ai/claude-3-sonnet@20240229
weak_model_name: vertex_ai/claude-3-5-haiku@20241022 weak_model_name: vertex_ai/claude-3-5-haiku@20241022
- name: vertex_ai/claude-opus-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
extra_params:
max_tokens: 32000
editor_model_name: vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: vertex_ai/claude-sonnet-4@20250514
edit_format: diff
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
use_repo_map: true
extra_params:
max_tokens: 64000
editor_model_name: vertex_ai/claude-sonnet-4@20250514
editor_edit_format: editor-diff
accepts_settings:
- thinking_tokens
- name: vertex_ai/gemini-2.5-flash-preview-05-20
edit_format: diff
use_repo_map: true
accepts_settings:
- reasoning_effort
- thinking_tokens
- name: vertex_ai/gemini-2.5-pro-exp-03-25 - name: vertex_ai/gemini-2.5-pro-exp-03-25
edit_format: diff-fenced edit_format: diff-fenced
weak_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17 weak_model_name: vertex_ai-language-models/gemini-2.5-flash-preview-04-17

View file

@ -1,7 +1,7 @@
--- ---
parent: Configuration parent: Configuration
nav_order: 15 nav_order: 15
description: How to configure aider with a yaml config file. description: How to configure aider with a YAML config file.
--- ---
# YAML config file # YAML config file
@ -58,7 +58,7 @@ cog.outl("```")
# Place in your home dir, or at the root of your git repo. # Place in your home dir, or at the root of your git repo.
########################################################## ##########################################################
# Note: You can only put OpenAI and Anthropic API keys in the yaml # Note: You can only put OpenAI and Anthropic API keys in the YAML
# config file. Keys for all APIs can be stored in a .env file # config file. Keys for all APIs can be stored in a .env file
# https://aider.chat/docs/config/dotenv.html # https://aider.chat/docs/config/dotenv.html
@ -440,6 +440,9 @@ cog.outl("```")
## Specify the language to use in the chat (default: None, uses system settings) ## Specify the language to use in the chat (default: None, uses system settings)
#chat-language: xxx #chat-language: xxx
## Specify the language to use in the commit message (default: None, user language)
#commit-language: xxx
## Always say yes to every confirmation ## Always say yes to every confirmation
#yes-always: false #yes-always: false

View file

@ -40,9 +40,9 @@ OPENAI_API_KEY=<key>
ANTHROPIC_API_KEY=<key> ANTHROPIC_API_KEY=<key>
``` ```
#### Yaml config file #### YAML config file
You can also set those API keys via special entries in the You can also set those API keys via special entries in the
[yaml config file](/docs/config/aider_conf.html), like this: [YAML config file](/docs/config/aider_conf.html), like this:
```yaml ```yaml
openai-api-key: <key> openai-api-key: <key>
@ -74,7 +74,7 @@ OPENROUTER_API_KEY=bar
DEEPSEEK_API_KEY=baz DEEPSEEK_API_KEY=baz
``` ```
#### Yaml config file #### YAML config file
You can also set API keys in the You can also set API keys in the

View file

@ -12,7 +12,7 @@ Aider allows you to configure your preferred text editor for use with the `/edit
You can specify the text editor with the `--editor` switch or using You can specify the text editor with the `--editor` switch or using
`editor:` in aider's `editor:` in aider's
[yaml config file](https://aider.chat/docs/config/aider_conf.html). [YAML config file](https://aider.chat/docs/config/aider_conf.html).
## Environment variables ## Environment variables

View file

@ -86,10 +86,10 @@ for alias, model in sorted(MODEL_ALIASES.items()):
- `grok3`: xai/grok-3-beta - `grok3`: xai/grok-3-beta
- `haiku`: claude-3-5-haiku-20241022 - `haiku`: claude-3-5-haiku-20241022
- `optimus`: openrouter/openrouter/optimus-alpha - `optimus`: openrouter/openrouter/optimus-alpha
- `opus`: claude-3-opus-20240229 - `opus`: claude-opus-4-20250514
- `quasar`: openrouter/openrouter/quasar-alpha - `quasar`: openrouter/openrouter/quasar-alpha
- `r1`: deepseek/deepseek-reasoner - `r1`: deepseek/deepseek-reasoner
- `sonnet`: anthropic/claude-3-7-sonnet-20250219 - `sonnet`: anthropic/claude-sonnet-4-20250514
<!--[[[end]]]--> <!--[[[end]]]-->
## Priority ## Priority

View file

@ -74,7 +74,7 @@ usage: aider [-h] [--model] [--openai-api-key] [--anthropic-api-key]
[--apply-clipboard-edits] [--exit] [--show-repo-map] [--apply-clipboard-edits] [--exit] [--show-repo-map]
[--show-prompts] [--voice-format] [--voice-language] [--show-prompts] [--voice-format] [--voice-language]
[--voice-input-device] [--disable-playwright] [--file] [--voice-input-device] [--disable-playwright] [--file]
[--read] [--vim] [--chat-language] [--yes-always] [-v] [--read] [--vim] [--chat-language] [--commit-language] [--yes-always] [-v]
[--load] [--encoding] [--line-endings] [-c] [--load] [--encoding] [--line-endings] [-c]
[--env-file] [--env-file]
[--suggest-shell-commands | --no-suggest-shell-commands] [--suggest-shell-commands | --no-suggest-shell-commands]
@ -683,6 +683,10 @@ Environment variable: `AIDER_VIM`
Specify the language to use in the chat (default: None, uses system settings) Specify the language to use in the chat (default: None, uses system settings)
Environment variable: `AIDER_CHAT_LANGUAGE` Environment variable: `AIDER_CHAT_LANGUAGE`
### `--commit-language COMMIT_LANGUAGE`
Specify the language to use in the commit message (default: None, user language)
Environment variable: `AIDER_COMMIT_LANGUAGE`
### `--yes-always` ### `--yes-always`
Always say yes to every confirmation Always say yes to every confirmation
Environment variable: `AIDER_YES_ALWAYS` Environment variable: `AIDER_YES_ALWAYS`

View file

@ -264,9 +264,12 @@ tr:hover { background-color: #f5f5f5; }
</style> </style>
<table> <table>
<tr><th>Model Name</th><th class='right'>Total Tokens</th><th class='right'>Percent</th></tr> <tr><th>Model Name</th><th class='right'>Total Tokens</th><th class='right'>Percent</th></tr>
<tr><td>gemini/gemini-2.5-pro-exp-03-25</td><td class='right'>890,057</td><td class='right'>69.9%</td></tr> <tr><td>o3</td><td class='right'>542,669</td><td class='right'>45.1%</td></tr>
<tr><td>o3</td><td class='right'>373,753</td><td class='right'>29.4%</td></tr> <tr><td>gemini/gemini-2.5-pro-exp-03-25</td><td class='right'>479,518</td><td class='right'>39.9%</td></tr>
<tr><td>openrouter/REDACTED</td><td class='right'>8,745</td><td class='right'>0.7%</td></tr> <tr><td>anthropic/claude-sonnet-4-20250514</td><td class='right'>131,972</td><td class='right'>11.0%</td></tr>
<tr><td>gemini/gemini-2.5-pro-preview-05-06</td><td class='right'>40,256</td><td class='right'>3.3%</td></tr>
<tr><td>gemini/gemini-2.5-flash-preview-05-20</td><td class='right'>7,638</td><td class='right'>0.6%</td></tr>
<tr><td>gemini/REDACTED</td><td class='right'>643</td><td class='right'>0.1%</td></tr>
</table> </table>
{: .note :} {: .note :}

View file

@ -28,12 +28,6 @@ These one-liners will install aider, along with python 3.12 if needed.
They are based on the They are based on the
[uv installers](https://docs.astral.sh/uv/getting-started/installation/). [uv installers](https://docs.astral.sh/uv/getting-started/installation/).
#### Windows
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://aider.chat/install.ps1 | iex"
```
#### Mac & Linux #### Mac & Linux
Use curl to download the script and execute it with sh: Use curl to download the script and execute it with sh:
@ -48,6 +42,12 @@ If your system doesn't have curl, you can use wget:
wget -qO- https://aider.chat/install.sh | sh wget -qO- https://aider.chat/install.sh | sh
``` ```
#### Windows
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://aider.chat/install.ps1 | iex"
```
## Install with uv ## Install with uv
@ -55,7 +55,7 @@ You can install aider with uv:
```bash ```bash
python -m pip install uv # If you need to install uv python -m pip install uv # If you need to install uv
uv tool install --force --python python3.12 aider-chat@latest uv tool install --force --python python3.12 --with pip aider-chat@latest
``` ```
This will install uv using your existing python version 3.8-3.13, This will install uv using your existing python version 3.8-3.13,

View file

@ -285,6 +285,6 @@ mod_dates = [get_last_modified_date(file) for file in files]
latest_mod_date = max(mod_dates) latest_mod_date = max(mod_dates)
cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}") cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}")
]]]--> ]]]-->
May 08, 2025. May 26, 2025.
<!--[[[end]]]--> <!--[[[end]]]-->
</p> </p>

View file

@ -0,0 +1,105 @@
---
parent: Connecting to LLMs
nav_order: 510
---
# GitHub Copilot
Aider can connect to GitHub Copilots LLMs because Copilot exposes a standard **OpenAI-style**
endpoint at:
```
https://api.githubcopilot.com
```
First, install aider:
{% include install.md %}
---
## Configure your environment
```bash
# macOS/Linux
export OPENAI_API_BASE=https://api.githubcopilot.com
export OPENAI_API_KEY=<oauth_token>
# Windows (PowerShell)
setx OPENAI_API_BASE https://api.githubcopilot.com
setx OPENAI_API_KEY <oauth_token>
# …restart the shell after setx commands
```
---
### Where do I get the token?
The easiest path is to sign in to Copilot from any JetBrains IDE (PyCharm, GoLand, etc).
After you authenticate a file appears:
```
~/.config/github-copilot/apps.json
```
Copy the `oauth_token` value that string is your `OPENAI_API_KEY`.
*Note:* tokens created by the Neovim **copilot.lua** plugin (old `hosts.json`) sometimes lack the
needed scopes. If you see “access to this endpoint is forbidden”, regenerate the token with a
JetBrains IDE or the VS Code Copilot extension.
---
## Discover available models
Copilot hosts many models (OpenAI, Anthropic, Google, etc).
List the models your subscription allows with:
```bash
curl -s https://api.githubcopilot.com/models \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Copilot-Integration-Id: vscode-chat" | jq -r '.data[].id'
```
Each returned ID can be used with aider by **prefixing it with `openai/`**:
```bash
aider --model openai/gpt-4o
# or
aider --model openai/claude-3.7-sonnet-thought
```
---
## Quick start
```bash
# change into your project
cd /to/your/project
# talk to Copilot
aider --model openai/gpt-4o
```
---
## Optional config file (`~/.aider.conf.yml`)
```yaml
openai-api-base: https://api.githubcopilot.com
openai-api-key: "<oauth_token>"
model: openai/gpt-4o
weak-model: openai/gpt-4o-mini
show-model-warnings: false
```
---
## FAQ
* Calls made through aider are billed through your Copilot subscription
(aider will still print *estimated* costs).
* The Copilot docs explicitly allow third-party “agents” that hit this API aider is playing by
the rules.
* Aider talks directly to the REST endpoint—no web-UI scraping or browser automation.

View file

@ -40,7 +40,7 @@ cd /to/your/project
aider --model vertex_ai/claude-3-5-sonnet@20240620 aider --model vertex_ai/claude-3-5-sonnet@20240620
``` ```
Or you can use the [yaml config](/docs/config/aider_conf.html) to set the model to any of the Or you can use the [YAML config](/docs/config/aider_conf.html) to set the model to any of the
models supported by Vertex AI. models supported by Vertex AI.
Example `.aider.conf.yml` file: Example `.aider.conf.yml` file:

View file

@ -58,6 +58,9 @@ cog.out(model_list)
- anthropic.claude-3-5-haiku-20241022-v1:0 - anthropic.claude-3-5-haiku-20241022-v1:0
- anthropic.claude-3-5-sonnet-20241022-v2:0 - anthropic.claude-3-5-sonnet-20241022-v2:0
- anthropic.claude-3-7-sonnet-20250219-v1:0 - anthropic.claude-3-7-sonnet-20250219-v1:0
- anthropic.claude-opus-4-20250514-v1:0
- anthropic.claude-sonnet-4-20250514-v1:0
- azure_ai/mistral-medium-2505
- claude-3-5-haiku-20241022 - claude-3-5-haiku-20241022
- claude-3-5-haiku-latest - claude-3-5-haiku-latest
- claude-3-5-sonnet-20240620 - claude-3-5-sonnet-20240620
@ -69,6 +72,8 @@ cog.out(model_list)
- claude-3-opus-20240229 - claude-3-opus-20240229
- claude-3-opus-latest - claude-3-opus-latest
- claude-3-sonnet-20240229 - claude-3-sonnet-20240229
- claude-opus-4-20250514
- claude-sonnet-4-20250514
- codestral/codestral-2405 - codestral/codestral-2405
- codestral/codestral-latest - codestral/codestral-latest
- databricks/databricks-claude-3-7-sonnet - databricks/databricks-claude-3-7-sonnet
@ -77,15 +82,20 @@ cog.out(model_list)
- deepseek/deepseek-reasoner - deepseek/deepseek-reasoner
- eu.anthropic.claude-3-5-haiku-20241022-v1:0 - eu.anthropic.claude-3-5-haiku-20241022-v1:0
- eu.anthropic.claude-3-5-sonnet-20241022-v2:0 - eu.anthropic.claude-3-5-sonnet-20241022-v2:0
- eu.anthropic.claude-3-7-sonnet-20250219-v1:0
- eu.anthropic.claude-opus-4-20250514-v1:0
- eu.anthropic.claude-sonnet-4-20250514-v1:0
- mistral/codestral-2405 - mistral/codestral-2405
- mistral/codestral-latest - mistral/codestral-latest
- mistral/codestral-mamba-latest - mistral/codestral-mamba-latest
- mistral/devstral-small-2505
- mistral/mistral-large-2402 - mistral/mistral-large-2402
- mistral/mistral-large-2407 - mistral/mistral-large-2407
- mistral/mistral-large-2411 - mistral/mistral-large-2411
- mistral/mistral-large-latest - mistral/mistral-large-latest
- mistral/mistral-medium - mistral/mistral-medium
- mistral/mistral-medium-2312 - mistral/mistral-medium-2312
- mistral/mistral-medium-2505
- mistral/mistral-medium-latest - mistral/mistral-medium-latest
- mistral/mistral-small - mistral/mistral-small
- mistral/mistral-small-latest - mistral/mistral-small-latest
@ -105,6 +115,8 @@ cog.out(model_list)
- us.anthropic.claude-3-5-haiku-20241022-v1:0 - us.anthropic.claude-3-5-haiku-20241022-v1:0
- us.anthropic.claude-3-5-sonnet-20241022-v2:0 - us.anthropic.claude-3-5-sonnet-20241022-v2:0
- us.anthropic.claude-3-7-sonnet-20250219-v1:0 - us.anthropic.claude-3-7-sonnet-20250219-v1:0
- us.anthropic.claude-opus-4-20250514-v1:0
- us.anthropic.claude-sonnet-4-20250514-v1:0
- vertex_ai/claude-3-5-haiku - vertex_ai/claude-3-5-haiku
- vertex_ai/claude-3-5-haiku@20241022 - vertex_ai/claude-3-5-haiku@20241022
- vertex_ai/claude-3-5-sonnet - vertex_ai/claude-3-5-sonnet
@ -118,6 +130,8 @@ cog.out(model_list)
- vertex_ai/claude-3-opus@20240229 - vertex_ai/claude-3-opus@20240229
- vertex_ai/claude-3-sonnet - vertex_ai/claude-3-sonnet
- vertex_ai/claude-3-sonnet@20240229 - vertex_ai/claude-3-sonnet@20240229
- vertex_ai/claude-opus-4@20250514
- vertex_ai/claude-sonnet-4@20250514
<!--[[[end]]]--> <!--[[[end]]]-->

View file

@ -69,11 +69,11 @@ cog.out(text)
]]]--> ]]]-->
<a href="https://github.com/Aider-AI/aider" class="github-badge badge-stars" title="Total number of GitHub stars the Aider project has received"> <a href="https://github.com/Aider-AI/aider" class="github-badge badge-stars" title="Total number of GitHub stars the Aider project has received">
<span class="badge-label">⭐ GitHub Stars</span> <span class="badge-label">⭐ GitHub Stars</span>
<span class="badge-value">33K</span> <span class="badge-value">34K</span>
</a> </a>
<a href="https://pypi.org/project/aider-chat/" class="github-badge badge-installs" title="Total number of installations via pip from PyPI"> <a href="https://pypi.org/project/aider-chat/" class="github-badge badge-installs" title="Total number of installations via pip from PyPI">
<span class="badge-label">📦 Installs</span> <span class="badge-label">📦 Installs</span>
<span class="badge-value">2.2M</span> <span class="badge-value">2.4M</span>
</a> </a>
<div class="github-badge badge-tokens" title="Number of tokens processed weekly by Aider users"> <div class="github-badge badge-tokens" title="Number of tokens processed weekly by Aider users">
<span class="badge-label">📈 Tokens/week</span> <span class="badge-label">📈 Tokens/week</span>
@ -85,7 +85,7 @@ cog.out(text)
</a> </a>
<a href="/HISTORY.html" class="github-badge badge-coded" title="Percentage of the new code in Aider's last release written by Aider itself"> <a href="/HISTORY.html" class="github-badge badge-coded" title="Percentage of the new code in Aider's last release written by Aider itself">
<span class="badge-label">🔄 Singularity</span> <span class="badge-label">🔄 Singularity</span>
<span class="badge-value">92%</span> <span class="badge-value">79%</span>
</a> </a>
<!--[[[end]]]--> <!--[[[end]]]-->
</div> </div>
@ -269,178 +269,178 @@ cog.out(text)
<script> <script>
const testimonials = [ const testimonials = [
{ {
text: "My life has changed... There's finally an AI coding tool that's good enough to keep up with me... Aider... It's going to rock your world.", text: "My life has changed... Aider... It's going to rock your world.",
author: "Eric S. Raymond", author: "Eric S. Raymond on X",
link: "https://x.com/esrtweet/status/1910809356381413593" link: "https://x.com/esrtweet/status/1910809356381413593"
}, },
{ {
text: "The best free open source AI coding assistant.", text: "The best free open source AI coding assistant.",
author: "IndyDevDan", author: "IndyDevDan on YouTube",
link: "https://youtu.be/YALpX8oOn78" link: "https://youtu.be/YALpX8oOn78"
}, },
{ {
text: "The best AI coding assistant so far.", text: "The best AI coding assistant so far.",
author: "Matthew Berman", author: "Matthew Berman on YouTube",
link: "https://www.youtube.com/watch?v=df8afeb1FY8" link: "https://www.youtube.com/watch?v=df8afeb1FY8"
}, },
{ {
text: "Aider ... has easily quadrupled my coding productivity.", text: "Aider ... has easily quadrupled my coding productivity.",
author: "SOLAR_FIELDS", author: "SOLAR_FIELDS on Hacker News",
link: "https://news.ycombinator.com/item?id=36212100" link: "https://news.ycombinator.com/item?id=36212100"
}, },
{ {
text: "It's a cool workflow... Aider's ergonomics are perfect for me.", text: "It's a cool workflow... Aider's ergonomics are perfect for me.",
author: "qup", author: "qup on Hacker News",
link: "https://news.ycombinator.com/item?id=38185326" link: "https://news.ycombinator.com/item?id=38185326"
}, },
{ {
text: "It's really like having your senior developer live right in your Git repo - truly amazing!", text: "It's really like having your senior developer live right in your Git repo - truly amazing!",
author: "rappster", author: "rappster on GitHub",
link: "https://github.com/Aider-AI/aider/issues/124" link: "https://github.com/Aider-AI/aider/issues/124"
}, },
{ {
text: "What an amazing tool. It's incredible.", text: "What an amazing tool. It's incredible.",
author: "valyagolev", author: "valyagolev on GitHub",
link: "https://github.com/Aider-AI/aider/issues/6#issue-1722897858" link: "https://github.com/Aider-AI/aider/issues/6#issue-1722897858"
}, },
{ {
text: "Aider is such an astounding thing!", text: "Aider is such an astounding thing!",
author: "cgrothaus", author: "cgrothaus on GitHub",
link: "https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700" link: "https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700"
}, },
{ {
text: "It was WAY faster than I would be getting off the ground and making the first few working versions.", text: "It was WAY faster than I would be getting off the ground and making the first few working versions.",
author: "Daniel Feldman", author: "Daniel Feldman on X",
link: "https://twitter.com/d_feldman/status/1662295077387923456" link: "https://twitter.com/d_feldman/status/1662295077387923456"
}, },
{ {
text: "THANK YOU for Aider! It really feels like a glimpse into the future of coding.", text: "THANK YOU for Aider! It really feels like a glimpse into the future of coding.",
author: "derwiki", author: "derwiki on Hacker News",
link: "https://news.ycombinator.com/item?id=38205643" link: "https://news.ycombinator.com/item?id=38205643"
}, },
{ {
text: "It's just amazing. It is freeing me to do things I felt were out my comfort zone before.", text: "It's just amazing. It is freeing me to do things I felt were out my comfort zone before.",
author: "Dougie", author: "Dougie on Discord",
link: "https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656" link: "https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656"
}, },
{ {
text: "This project is stellar.", text: "This project is stellar.",
author: "funkytaco", author: "funkytaco on GitHub",
link: "https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008" link: "https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008"
}, },
{ {
text: "Amazing project, definitely the best AI coding assistant I've used.", text: "Amazing project, definitely the best AI coding assistant I've used.",
author: "joshuavial", author: "joshuavial on GitHub",
link: "https://github.com/Aider-AI/aider/issues/84" link: "https://github.com/Aider-AI/aider/issues/84"
}, },
{ {
text: "I absolutely love using Aider ... It makes software development feel so much lighter as an experience.", text: "I absolutely love using Aider ... It makes software development feel so much lighter as an experience.",
author: "principalideal0", author: "principalideal0 on Discord",
link: "https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468" link: "https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468"
}, },
{ {
text: "I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity.", text: "I have been recovering from ... surgeries ... aider ... has allowed me to continue productivity.",
author: "codeninja", author: "codeninja on Reddit",
link: "https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG" link: "https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG"
}, },
{ {
text: "I am an aider addict. I'm getting so much more work done, but in less time.", text: "I am an aider addict. I'm getting so much more work done, but in less time.",
author: "dandandan", author: "dandandan on Discord",
link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470" link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470"
}, },
{ {
text: "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.", text: "Aider... blows everything else out of the water hands down, there's no competition whatsoever.",
author: "SystemSculpt", author: "SystemSculpt on Discord",
link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548" link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548"
}, },
{ {
text: "Aider is amazing, coupled with Sonnet 3.5 it's quite mind blowing.", text: "Aider is amazing, coupled with Sonnet 3.5 it's quite mind blowing.",
author: "Josh Dingus", author: "Josh Dingus on Discord",
link: "https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548" link: "https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548"
}, },
{ {
text: "Hands down, this is the best AI coding assistant tool so far.", text: "Hands down, this is the best AI coding assistant tool so far.",
author: "IndyDevDan", author: "IndyDevDan on YouTube",
link: "https://www.youtube.com/watch?v=MPYFPvxfGZs" link: "https://www.youtube.com/watch?v=MPYFPvxfGZs"
}, },
{ {
text: "[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life.", text: "[Aider] changed my daily coding workflows. It's mind-blowing how ...(it)... can change your life.",
author: "maledorak", author: "maledorak on Discord",
link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264" link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264"
}, },
{ {
text: "Best agent for actual dev work in existing codebases.", text: "Best agent for actual dev work in existing codebases.",
author: "Nick Dobos", author: "Nick Dobos on X",
link: "https://twitter.com/NickADobos/status/1690408967963652097?s=20" link: "https://twitter.com/NickADobos/status/1690408967963652097?s=20"
}, },
{ {
text: "One of my favorite pieces of software. Blazing trails on new paradigms!", text: "One of my favorite pieces of software. Blazing trails on new paradigms!",
author: "Chris Wall", author: "Chris Wall on X",
link: "https://x.com/chris65536/status/1905053299251798432" link: "https://x.com/chris65536/status/1905053299251798432"
}, },
{ {
text: "Aider has been revolutionary for me and my work.", text: "Aider has been revolutionary for me and my work.",
author: "Starry Hope", author: "Starry Hope on X",
link: "https://x.com/starryhopeblog/status/1904985812137132056" link: "https://x.com/starryhopeblog/status/1904985812137132056"
}, },
{ {
text: "Try aider! One of the best ways to vibe code.", text: "Try aider! One of the best ways to vibe code.",
author: "Chris Wall", author: "Chris Wall on X",
link: "https://x.com/Chris65536/status/1905053418961391929" link: "https://x.com/Chris65536/status/1905053418961391929"
}, },
{ {
text: "Aider is hands down the best. And it's free and opensource.", text: "Aider is hands down the best. And it's free and opensource.",
author: "AriyaSavakaLurker", author: "AriyaSavakaLurker on Reddit",
link: "https://www.reddit.com/r/ChatGPTCoding/comments/1ik16y6/whats_your_take_on_aider/mbip39n/" link: "https://www.reddit.com/r/ChatGPTCoding/comments/1ik16y6/whats_your_take_on_aider/mbip39n/"
}, },
{ {
text: "Aider is also my best friend.", text: "Aider is also my best friend.",
author: "jzn21", author: "jzn21 on Reddit",
link: "https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27dcnb/" link: "https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27dcnb/"
}, },
{ {
text: "Try Aider, it's worth it.", text: "Try Aider, it's worth it.",
author: "jorgejhms", author: "jorgejhms on Reddit",
link: "https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27cp99/" link: "https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27cp99/"
}, },
{ {
text: "I like aider :)", text: "I like aider :)",
author: "Chenwei Cui", author: "Chenwei Cui on X",
link: "https://x.com/ccui42/status/1904965344999145698" link: "https://x.com/ccui42/status/1904965344999145698"
}, },
{ {
text: "Aider is the precision tool of LLM code gen... Minimal, thoughtful and capable of surgical changes to your codebase all while keeping the developer in control.", text: "Aider is the precision tool of LLM code gen... Minimal, thoughtful and capable of surgical changes ... while keeping the developer in control.",
author: "Reilly Sweetland", author: "Reilly Sweetland on X",
link: "https://x.com/rsweetland/status/1904963807237259586" link: "https://x.com/rsweetland/status/1904963807237259586"
}, },
{ {
text: "Cannot believe aider vibe coded a 650 LOC feature across service and cli today in 1 shot.", text: "Cannot believe aider vibe coded a 650 LOC feature across service and cli today in 1 shot.",
author: "autopoietist", author: "autopoietist on Discord",
link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1355675042259796101" link: "https://discord.com/channels/1131200896827654144/1131200896827654149/1355675042259796101"
}, },
{ {
text: "Oh no the secret is out! Yes, Aider is the best coding tool around. I highly, highly recommend it to anyone.", text: "Oh no the secret is out! Yes, Aider is the best coding tool around. I highly, highly recommend it to anyone.",
author: "Joshua D Vander Hook", author: "Joshua D Vander Hook on X",
link: "https://x.com/jodavaho/status/1911154899057795218" link: "https://x.com/jodavaho/status/1911154899057795218"
}, },
{ {
text: "thanks to aider, i have started and finished three personal projects within the last two days", text: "thanks to aider, i have started and finished three personal projects within the last two days",
author: "joseph stalzyn", author: "joseph stalzyn on X",
link: "https://x.com/anitaheeder/status/1908338609645904160" link: "https://x.com/anitaheeder/status/1908338609645904160"
}, },
{ {
text: "Been using aider as my daily driver for over a year ... I absolutely love the tool, like beyond words.", text: "Been using aider as my daily driver for over a year ... I absolutely love the tool, like beyond words.",
author: "koleok", author: "koleok on Discord",
link: "https://discord.com/channels/1131200896827654144/1273248471394291754/1356727448372252783" link: "https://discord.com/channels/1131200896827654144/1273248471394291754/1356727448372252783"
}, },
{ {
text: "Aider ... is the tool to benchmark against.", text: "Aider ... is the tool to benchmark against.",
author: "BeetleB", author: "BeetleB on Hacker News",
link: "https://news.ycombinator.com/item?id=43930201" link: "https://news.ycombinator.com/item?id=43930201"
}, },
{ {
text: "aider is really cool", text: "aider is really cool",
author: "kache (@yacineMTB)", author: "kache on X",
link: "https://x.com/yacineMTB/status/1911224442430124387" link: "https://x.com/yacineMTB/status/1911224442430124387"
} }
]; ];
@ -642,6 +642,7 @@ const testimonials = [
<li><a href="/docs/leaderboards/">LLM Leaderboards</a></li> <li><a href="/docs/leaderboards/">LLM Leaderboards</a></li>
<li><a href="https://github.com/Aider-AI/aider">GitHub Repository</a></li> <li><a href="https://github.com/Aider-AI/aider">GitHub Repository</a></li>
<li><a href="https://discord.gg/Y7X7bhMQFV">Discord Community</a></li> <li><a href="https://discord.gg/Y7X7bhMQFV">Discord Community</a></li>
<li><a href="https://aider.chat/HISTORY.html">Release notes</a></li>
<li><a href="/blog/">Blog</a></li> <li><a href="/blog/">Blog</a></li>
</ul> </ul>
</div> </div>

View file

@ -425,7 +425,7 @@ function Invoke-Installer($artifacts, $platforms) {
Write-Information "" Write-Information ""
Write-Information "Installing aider-chat..." Write-Information "Installing aider-chat..."
& "$dest_dir\uv.exe" tool install --force --python python3.12 aider-chat@latest & "$dest_dir\uv.exe" tool install --force --python python3.12 --with pip aider-chat@latest
if (-not $NoModifyPath) { if (-not $NoModifyPath) {
Add-Ci-Path $dest_dir Add-Ci-Path $dest_dir

View file

@ -1178,7 +1178,7 @@ install() {
say "Installing aider..." say "Installing aider..."
say "" say ""
# Install aider-chat using the newly installed uv # Install aider-chat using the newly installed uv
ensure "${_install_dir}/uv" tool install --force --python python3.12 aider-chat@latest ensure "${_install_dir}/uv" tool install --force --python python3.12 --with pip aider-chat@latest
# Avoid modifying the users PATH if they are managing their PATH manually # Avoid modifying the users PATH if they are managing their PATH manually
case :$PATH: case :$PATH:

View file

@ -60,7 +60,7 @@ click==8.1.8
# via # via
# -c requirements/common-constraints.txt # -c requirements/common-constraints.txt
# litellm # litellm
configargparse==1.7 configargparse==1.7.1
# via # via
# -c requirements/common-constraints.txt # -c requirements/common-constraints.txt
# -r requirements/requirements.in # -r requirements/requirements.in

View file

@ -65,7 +65,7 @@ cogapp==3.4.1
# via -r requirements/requirements-dev.in # via -r requirements/requirements-dev.in
colorama==0.4.6 colorama==0.4.6
# via griffe # via griffe
configargparse==1.7 configargparse==1.7.1
# via -r requirements/requirements.in # via -r requirements/requirements.in
contourpy==1.3.2 contourpy==1.3.2
# via matplotlib # via matplotlib

View file

@ -46,3 +46,6 @@ scipy
# https://github.com/pypa/twine/blob/6fbf880ee60915cf1666348c4bdd78a10415f2ac/twine/__init__.py#L40 # https://github.com/pypa/twine/blob/6fbf880ee60915cf1666348c4bdd78a10415f2ac/twine/__init__.py#L40
# Uses importlib-metadata # Uses importlib-metadata
importlib-metadata<8.0.0 importlib-metadata<8.0.0
# configargparse was 1.7 pulled
configargparse>1.7

View file

@ -5,7 +5,7 @@ FROM bretfisher/jekyll-serve
WORKDIR /site WORKDIR /site
# Copy the current directory contents into the container at /srv/jekyll # Copy the current directory contents into the container at /srv/jekyll
COPY website /site COPY aider/website /site
RUN apt-get update && apt-get install libcurl4 RUN apt-get update && apt-get install libcurl4

View file

@ -112,6 +112,8 @@ def main():
cmd = [ cmd = [
"aider", "aider",
"--model",
"sonnet",
hist_path, hist_path,
"--read", "--read",
log_path, log_path,

View file

@ -834,6 +834,36 @@ two
self.assertNotIn(fname2, str(coder.abs_fnames)) self.assertNotIn(fname2, str(coder.abs_fnames))
self.assertNotIn(fname3, str(coder.abs_fnames)) self.assertNotIn(fname3, str(coder.abs_fnames))
def test_skip_gitignored_files_on_init(self):
with GitTemporaryDirectory() as _:
repo_path = Path(".")
repo = git.Repo.init(repo_path)
ignored_file = repo_path / "ignored_by_git.txt"
ignored_file.write_text("This file should be ignored by git.")
regular_file = repo_path / "regular_file.txt"
regular_file.write_text("This is a regular file.")
gitignore_content = "ignored_by_git.txt\n"
(repo_path / ".gitignore").write_text(gitignore_content)
repo.index.add([str(regular_file), ".gitignore"])
repo.index.commit("Initial commit with gitignore and regular file")
mock_io = MagicMock()
mock_io.tool_warning = MagicMock()
fnames_to_add = [str(ignored_file), str(regular_file)]
coder = Coder.create(self.GPT35, None, mock_io, fnames=fnames_to_add)
self.assertNotIn(str(ignored_file.resolve()), coder.abs_fnames)
self.assertIn(str(regular_file.resolve()), coder.abs_fnames)
mock_io.tool_warning.assert_any_call(
f"Skipping {ignored_file.name} that matches gitignore spec."
)
def test_check_for_urls(self): def test_check_for_urls(self):
io = InputOutput(yes=True) io = InputOutput(yes=True)
coder = Coder.create(self.GPT35, None, io=io) coder = Coder.create(self.GPT35, None, io=io)
@ -1181,6 +1211,122 @@ This command will print 'Hello, World!' to the console."""
sanity_check_messages(coder.cur_messages) sanity_check_messages(coder.cur_messages)
self.assertEqual(coder.cur_messages[-1]["role"], "assistant") self.assertEqual(coder.cur_messages[-1]["role"], "assistant")
def test_normalize_language(self):
coder = Coder.create(self.GPT35, None, io=InputOutput())
# Test None and empty
self.assertIsNone(coder.normalize_language(None))
self.assertIsNone(coder.normalize_language(""))
# Test "C" and "POSIX"
self.assertIsNone(coder.normalize_language("C"))
self.assertIsNone(coder.normalize_language("POSIX"))
# Test already formatted names
self.assertEqual(coder.normalize_language("English"), "English")
self.assertEqual(coder.normalize_language("French"), "French")
# Test common locale codes (fallback map, assuming babel is not installed or fails)
with patch("aider.coders.base_coder.Locale", None):
self.assertEqual(coder.normalize_language("en_US"), "English")
self.assertEqual(coder.normalize_language("fr_FR"), "French")
self.assertEqual(coder.normalize_language("es"), "Spanish")
self.assertEqual(coder.normalize_language("de_DE.UTF-8"), "German")
self.assertEqual(
coder.normalize_language("zh-CN"), "Chinese"
) # Test hyphen in fallback
self.assertEqual(coder.normalize_language("ja"), "Japanese")
self.assertEqual(
coder.normalize_language("unknown_code"), "unknown_code"
) # Fallback to original
# Test with babel.Locale mocked (available)
mock_babel_locale = MagicMock()
mock_locale_instance = MagicMock()
mock_babel_locale.parse.return_value = mock_locale_instance
with patch("aider.coders.base_coder.Locale", mock_babel_locale):
mock_locale_instance.get_display_name.return_value = "english" # For en_US
self.assertEqual(coder.normalize_language("en_US"), "English")
mock_babel_locale.parse.assert_called_with("en_US")
mock_locale_instance.get_display_name.assert_called_with("en")
mock_locale_instance.get_display_name.return_value = "french" # For fr-FR
self.assertEqual(coder.normalize_language("fr-FR"), "French") # Test with hyphen
mock_babel_locale.parse.assert_called_with("fr_FR") # Hyphen replaced
mock_locale_instance.get_display_name.assert_called_with("en")
# Test with babel.Locale raising an exception (simulating parse failure)
mock_babel_locale_error = MagicMock()
mock_babel_locale_error.parse.side_effect = Exception("Babel parse error")
with patch("aider.coders.base_coder.Locale", mock_babel_locale_error):
self.assertEqual(coder.normalize_language("en_US"), "English") # Falls back to map
def test_get_user_language(self):
io = InputOutput()
coder = Coder.create(self.GPT35, None, io=io)
# 1. Test with self.chat_language set
coder.chat_language = "fr_CA"
with patch.object(coder, "normalize_language", return_value="French Canadian") as mock_norm:
self.assertEqual(coder.get_user_language(), "French Canadian")
mock_norm.assert_called_once_with("fr_CA")
coder.chat_language = None # Reset
# 2. Test with locale.getlocale()
with patch("locale.getlocale", return_value=("en_GB", "UTF-8")) as mock_getlocale:
with patch.object(
coder, "normalize_language", return_value="British English"
) as mock_norm:
self.assertEqual(coder.get_user_language(), "British English")
mock_getlocale.assert_called_once()
mock_norm.assert_called_once_with("en_GB")
# Test with locale.getlocale() returning None or empty
with patch("locale.getlocale", return_value=(None, None)) as mock_getlocale:
with patch("os.environ.get") as mock_env_get: # Ensure env vars are not used yet
mock_env_get.return_value = None
self.assertIsNone(coder.get_user_language()) # Should be None if nothing found
# 3. Test with environment variables: LANG
with patch(
"locale.getlocale", side_effect=Exception("locale error")
): # Mock locale to fail
with patch("os.environ.get") as mock_env_get:
mock_env_get.side_effect = lambda key: "de_DE.UTF-8" if key == "LANG" else None
with patch.object(coder, "normalize_language", return_value="German") as mock_norm:
self.assertEqual(coder.get_user_language(), "German")
mock_env_get.assert_any_call("LANG")
mock_norm.assert_called_once_with("de_DE")
# Test LANGUAGE (takes precedence over LANG if both were hypothetically checked
# by os.environ.get, but our code checks in order, so we mock the first one it finds)
with patch("locale.getlocale", side_effect=Exception("locale error")):
with patch("os.environ.get") as mock_env_get:
mock_env_get.side_effect = lambda key: "es_ES" if key == "LANGUAGE" else None
with patch.object(coder, "normalize_language", return_value="Spanish") as mock_norm:
self.assertEqual(coder.get_user_language(), "Spanish")
mock_env_get.assert_any_call("LANGUAGE") # LANG would be called first
mock_norm.assert_called_once_with("es_ES")
# 4. Test priority: chat_language > locale > env
coder.chat_language = "it_IT"
with patch("locale.getlocale", return_value=("en_US", "UTF-8")) as mock_getlocale:
with patch("os.environ.get", return_value="de_DE") as mock_env_get:
with patch.object(
coder, "normalize_language", side_effect=lambda x: x.upper()
) as mock_norm:
self.assertEqual(coder.get_user_language(), "IT_IT") # From chat_language
mock_norm.assert_called_once_with("it_IT")
mock_getlocale.assert_not_called()
mock_env_get.assert_not_called()
coder.chat_language = None
# 5. Test when no language is found
with patch("locale.getlocale", side_effect=Exception("locale error")):
with patch("os.environ.get", return_value=None) as mock_env_get:
self.assertIsNone(coder.get_user_language())
def test_architect_coder_auto_accept_true(self): def test_architect_coder_auto_accept_true(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
io = InputOutput(yes=True) io = InputOutput(yes=True)

View file

@ -949,16 +949,19 @@ class TestMain(TestCase):
def test_invalid_edit_format(self): def test_invalid_edit_format(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
with patch("aider.io.InputOutput.offer_url") as mock_offer_url: # Suppress stderr for this test as argparse prints an error message
result = main( with patch("sys.stderr", new_callable=StringIO) as mock_stderr:
["--edit-format", "not-a-real-format", "--exit", "--yes"], with self.assertRaises(SystemExit) as cm:
input=DummyInput(), _ = main(
output=DummyOutput(), ["--edit-format", "not-a-real-format", "--exit", "--yes"],
) input=DummyInput(),
self.assertEqual(result, 1) # main() should return 1 on error output=DummyOutput(),
mock_offer_url.assert_called_once() )
args, _ = mock_offer_url.call_args # argparse.ArgumentParser.exit() is called with status 2 for invalid choice
self.assertEqual(args[0], "https://aider.chat/docs/more/edit-formats.html") self.assertEqual(cm.exception.code, 2)
stderr_output = mock_stderr.getvalue()
self.assertIn("invalid choice", stderr_output)
self.assertIn("not-a-real-format", stderr_output)
def test_default_model_selection(self): def test_default_model_selection(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
@ -1032,6 +1035,16 @@ class TestMain(TestCase):
system_info = coder.get_platform_info() system_info = coder.get_platform_info()
self.assertIn("Spanish", system_info) self.assertIn("Spanish", system_info)
def test_commit_language_japanese(self):
with GitTemporaryDirectory():
coder = main(
["--commit-language", "japanese", "--exit", "--yes"],
input=DummyInput(),
output=DummyOutput(),
return_coder=True,
)
self.assertIn("japanese", coder.commit_language)
@patch("git.Repo.init") @patch("git.Repo.init")
def test_main_exit_with_git_command_not_found(self, mock_git_init): def test_main_exit_with_git_command_not_found(self, mock_git_init):
mock_git_init.side_effect = git.exc.GitCommandNotFound("git", "Command 'git' not found") mock_git_init.side_effect = git.exc.GitCommandNotFound("git", "Command 'git' not found")
@ -1275,6 +1288,21 @@ class TestMain(TestCase):
for call in mock_io_instance.tool_warning.call_args_list: for call in mock_io_instance.tool_warning.call_args_list:
self.assertNotIn("Cost estimates may be inaccurate", call[0][0]) self.assertNotIn("Cost estimates may be inaccurate", call[0][0])
def test_argv_file_respects_git(self):
with GitTemporaryDirectory():
fname = Path("not_in_git.txt")
fname.touch()
with open(".gitignore", "w+") as f:
f.write("not_in_git.txt")
coder = main(
argv=["--file", "not_in_git.txt"],
input=DummyInput(),
output=DummyOutput(),
return_coder=True,
)
self.assertNotIn("not_in_git.txt", str(coder.abs_fnames))
self.assertFalse(coder.allowed_to_edit("not_in_git.txt"))
def test_load_dotenv_files_override(self): def test_load_dotenv_files_override(self):
with GitTemporaryDirectory() as git_dir: with GitTemporaryDirectory() as git_dir:
git_dir = Path(git_dir) git_dir = Path(git_dir)

View file

@ -138,13 +138,13 @@ class TestModels(unittest.TestCase):
self.assertEqual(model.name, "gpt-3.5-turbo") self.assertEqual(model.name, "gpt-3.5-turbo")
model = Model("sonnet") model = Model("sonnet")
self.assertEqual(model.name, "anthropic/claude-3-7-sonnet-20250219") self.assertEqual(model.name, "anthropic/claude-sonnet-4-20250514")
model = Model("haiku") model = Model("haiku")
self.assertEqual(model.name, "claude-3-5-haiku-20241022") self.assertEqual(model.name, "claude-3-5-haiku-20241022")
model = Model("opus") model = Model("opus")
self.assertEqual(model.name, "claude-3-opus-20240229") self.assertEqual(model.name, "claude-opus-4-20250514")
# Test non-alias passes through unchanged # Test non-alias passes through unchanged
model = Model("gpt-4") model = Model("gpt-4")

View file

@ -93,16 +93,14 @@ class TestOnboarding(unittest.TestCase):
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "or_key"}, clear=True) @patch.dict(os.environ, {"OPENROUTER_API_KEY": "or_key"}, clear=True)
def test_try_select_default_model_openrouter_free(self, mock_check_tier): def test_try_select_default_model_openrouter_free(self, mock_check_tier):
"""Test OpenRouter free model selection.""" """Test OpenRouter free model selection."""
self.assertEqual( self.assertEqual(try_to_select_default_model(), "openrouter/deepseek/deepseek-r1:free")
try_to_select_default_model(), "openrouter/google/gemini-2.5-pro-exp-03-25:free"
)
mock_check_tier.assert_called_once_with("or_key") mock_check_tier.assert_called_once_with("or_key")
@patch("aider.onboarding.check_openrouter_tier", return_value=False) # Assume paid tier @patch("aider.onboarding.check_openrouter_tier", return_value=False) # Assume paid tier
@patch.dict(os.environ, {"OPENROUTER_API_KEY": "or_key"}, clear=True) @patch.dict(os.environ, {"OPENROUTER_API_KEY": "or_key"}, clear=True)
def test_try_select_default_model_openrouter_paid(self, mock_check_tier): def test_try_select_default_model_openrouter_paid(self, mock_check_tier):
"""Test OpenRouter paid model selection.""" """Test OpenRouter paid model selection."""
self.assertEqual(try_to_select_default_model(), "openrouter/anthropic/claude-3.7-sonnet") self.assertEqual(try_to_select_default_model(), "openrouter/anthropic/claude-sonnet-4")
mock_check_tier.assert_called_once_with("or_key") mock_check_tier.assert_called_once_with("or_key")
@patch("aider.onboarding.check_openrouter_tier") @patch("aider.onboarding.check_openrouter_tier")
@ -146,7 +144,7 @@ class TestOnboarding(unittest.TestCase):
) )
def test_try_select_default_model_priority_openrouter(self, mock_check_tier): def test_try_select_default_model_priority_openrouter(self, mock_check_tier):
"""Test OpenRouter key takes priority.""" """Test OpenRouter key takes priority."""
self.assertEqual(try_to_select_default_model(), "openrouter/anthropic/claude-3.7-sonnet") self.assertEqual(try_to_select_default_model(), "openrouter/anthropic/claude-sonnet-4")
mock_check_tier.assert_called_once_with("or_key") mock_check_tier.assert_called_once_with("or_key")
@patch("aider.onboarding.check_openrouter_tier") @patch("aider.onboarding.check_openrouter_tier")
@ -346,7 +344,7 @@ class TestOnboarding(unittest.TestCase):
@patch( @patch(
"aider.onboarding.try_to_select_default_model", "aider.onboarding.try_to_select_default_model",
side_effect=[None, "openrouter/google/gemini-2.5-pro-exp-03-25:free"], side_effect=[None, "openrouter/deepseek/deepseek-r1:free"],
) # Fails first, succeeds after oauth ) # Fails first, succeeds after oauth
@patch( @patch(
"aider.onboarding.offer_openrouter_oauth", return_value=True "aider.onboarding.offer_openrouter_oauth", return_value=True
@ -360,7 +358,7 @@ class TestOnboarding(unittest.TestCase):
selected_model = select_default_model(args, io_mock, analytics_mock) selected_model = select_default_model(args, io_mock, analytics_mock)
self.assertEqual(selected_model, "openrouter/google/gemini-2.5-pro-exp-03-25:free") self.assertEqual(selected_model, "openrouter/deepseek/deepseek-r1:free")
self.assertEqual(mock_try_select.call_count, 2) # Called before and after oauth self.assertEqual(mock_try_select.call_count, 2) # Called before and after oauth
mock_offer_oauth.assert_called_once_with(io_mock, analytics_mock) mock_offer_oauth.assert_called_once_with(io_mock, analytics_mock)
# Only one warning is expected: "No LLM model..." # Only one warning is expected: "No LLM model..."

View file

@ -0,0 +1,73 @@
from pathlib import Path
from aider.models import ModelInfoManager
from aider.openrouter import OpenRouterModelManager
class DummyResponse:
"""Minimal stand-in for requests.Response used in tests."""
def __init__(self, json_data):
self.status_code = 200
self._json_data = json_data
def json(self):
return self._json_data
def test_openrouter_get_model_info_from_cache(monkeypatch, tmp_path):
"""
OpenRouterModelManager should return correct metadata taken from the
downloaded (and locally cached) models JSON payload.
"""
payload = {
"data": [
{
"id": "mistralai/mistral-medium-3",
"context_length": 32768,
"pricing": {"prompt": "100", "completion": "200"},
"top_provider": {"context_length": 32768},
}
]
}
# Fake out the network call and the HOME directory used for the cache file
monkeypatch.setattr("requests.get", lambda *a, **k: DummyResponse(payload))
monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
manager = OpenRouterModelManager()
info = manager.get_model_info("openrouter/mistralai/mistral-medium-3")
assert info["max_input_tokens"] == 32768
assert info["input_cost_per_token"] == 100.0
assert info["output_cost_per_token"] == 200.0
assert info["litellm_provider"] == "openrouter"
def test_model_info_manager_uses_openrouter_manager(monkeypatch):
"""
ModelInfoManager should delegate to OpenRouterModelManager when litellm
provides no data for an OpenRouter-prefixed model.
"""
# Ensure litellm path returns no info so that fallback logic triggers
monkeypatch.setattr("aider.models.litellm.get_model_info", lambda *a, **k: {})
stub_info = {
"max_input_tokens": 512,
"max_tokens": 512,
"max_output_tokens": 512,
"input_cost_per_token": 100.0,
"output_cost_per_token": 200.0,
"litellm_provider": "openrouter",
}
# Force OpenRouterModelManager to return our stub info
monkeypatch.setattr(
"aider.models.OpenRouterModelManager.get_model_info",
lambda self, model: stub_info,
)
mim = ModelInfoManager()
info = mim.get_model_info("openrouter/fake/model")
assert info == stub_info

View file

@ -59,6 +59,28 @@ class TestRepo(unittest.TestCase):
self.assertIn("index", diffs) self.assertIn("index", diffs)
self.assertIn("workingdir", diffs) self.assertIn("workingdir", diffs)
def test_diffs_with_single_byte_encoding(self):
with GitTemporaryDirectory():
encoding = "cp1251"
repo = git.Repo()
fname = Path("foo.txt")
fname.write_text("index\n", encoding=encoding)
repo.git.add(str(fname))
# Make a change with non-ASCII symbols in the working dir
fname.write_text("АБВ\n", encoding=encoding)
git_repo = GitRepo(InputOutput(encoding=encoding), None, ".")
diffs = git_repo.get_diffs()
# check that all diff output can be converted to utf-8 for sending to model
diffs.encode("utf-8")
self.assertIn("index", diffs)
self.assertIn("АБВ", diffs)
def test_diffs_detached_head(self): def test_diffs_detached_head(self):
with GitTemporaryDirectory(): with GitTemporaryDirectory():
repo = git.Repo() repo = git.Repo()