mirror of
https://github.com/Aider-AI/aider
synced 2026-05-05 06:32:04 +02:00
Compare commits
382 Commits
v0.62.1.de
...
v0.64.1.de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51b181d5fb | ||
|
|
2785b0a857 | ||
|
|
45a113ff9e | ||
|
|
cee59c2ca3 | ||
|
|
5cb9b242e2 | ||
|
|
1ed48de928 | ||
|
|
c4e4bc71ed | ||
|
|
ed0763706b | ||
|
|
1c79d517fc | ||
|
|
1a7a2f3088 | ||
|
|
4199789786 | ||
|
|
c9b0941d1f | ||
|
|
3d544136d2 | ||
|
|
8448eff1eb | ||
|
|
a3dde4599a | ||
|
|
3d7440c673 | ||
|
|
8f41006eba | ||
|
|
7d1379fe9f | ||
|
|
ced4f9f0d9 | ||
|
|
7fba332f58 | ||
|
|
8eda09533d | ||
|
|
6c42ee4edf | ||
|
|
3053595bfe | ||
|
|
6cf78d1a3f | ||
|
|
6c0ee9d917 | ||
|
|
50051dbfd5 | ||
|
|
30a8c5e12e | ||
|
|
4498549783 | ||
|
|
2ce3fedbfc | ||
|
|
8e10be665c | ||
|
|
80636cb8aa | ||
|
|
1b893da07d | ||
|
|
2f8aa3471b | ||
|
|
92a6957f0b | ||
|
|
d7248b11e5 | ||
|
|
6f4ed00105 | ||
|
|
347b299bbc | ||
|
|
614d297f22 | ||
|
|
59d0fe00c3 | ||
|
|
d6ebdff28f | ||
|
|
0ec16d0eab | ||
|
|
3906894fde | ||
|
|
a43772b21d | ||
|
|
cd81c2619b | ||
|
|
5abadc31a3 | ||
|
|
87654b698b | ||
|
|
2a1d2ef294 | ||
|
|
2a387707ef | ||
|
|
ce68062290 | ||
|
|
1b9073b085 | ||
|
|
e94961a14f | ||
|
|
d4e77b3be4 | ||
|
|
b96b36d5ea | ||
|
|
72d559a92c | ||
|
|
9a7bdcb6db | ||
|
|
2e4cf48e7e | ||
|
|
b78f8abeb4 | ||
|
|
7aa4e44317 | ||
|
|
2619b92131 | ||
|
|
28be59582f | ||
|
|
f96cc03587 | ||
|
|
cbd35b0e87 | ||
|
|
443acbe4b5 | ||
|
|
1f0d26e8c7 | ||
|
|
8302e9d0dd | ||
|
|
c797af020a | ||
|
|
1c85afa320 | ||
|
|
eb5317f8e5 | ||
|
|
8b860615b8 | ||
|
|
c15ac341e2 | ||
|
|
c2c7ee1047 | ||
|
|
72c46ccec6 | ||
|
|
dd3bfaee01 | ||
|
|
03206ad90e | ||
|
|
2e00307190 | ||
|
|
b3e29ab20e | ||
|
|
5504ac535b | ||
|
|
4b3dd7f4ea | ||
|
|
8edf9540d5 | ||
|
|
1c62ecd1b5 | ||
|
|
7cf3d9f3ce | ||
|
|
9b5a703307 | ||
|
|
370993cbed | ||
|
|
ddc538cdfa | ||
|
|
062dc43c87 | ||
|
|
7d9b986c04 | ||
|
|
bd2b9a12ed | ||
|
|
2b55707738 | ||
|
|
093540507e | ||
|
|
8f1dcfda07 | ||
|
|
16b319174b | ||
|
|
35115f5707 | ||
|
|
e9e51db9c7 | ||
|
|
ec39f018e2 | ||
|
|
59f4a3bcc7 | ||
|
|
1a8949eea3 | ||
|
|
fa85bdceed | ||
|
|
6b703244ec | ||
|
|
1a1cb0d3f1 | ||
|
|
95c9863d0a | ||
|
|
82187f6a71 | ||
|
|
75f52a1324 | ||
|
|
6e076a40a9 | ||
|
|
ded60036cb | ||
|
|
539a6cde63 | ||
|
|
3a28e74d89 | ||
|
|
c93f3faed9 | ||
|
|
1aaa3d9279 | ||
|
|
a06a9ed7d8 | ||
|
|
18a88596a6 | ||
|
|
2ac077603b | ||
|
|
3bb8b163b8 | ||
|
|
aac45097ca | ||
|
|
788956e86d | ||
|
|
e917424f5d | ||
|
|
1f6a5d04d9 | ||
|
|
dba844c7f9 | ||
|
|
09c11ef8ad | ||
|
|
ff21669bb5 | ||
|
|
a059ca2537 | ||
|
|
f4531b9434 | ||
|
|
05b350ac4b | ||
|
|
cfba315a85 | ||
|
|
12188b6684 | ||
|
|
725b2fc16f | ||
|
|
90a28e31e8 | ||
|
|
c057b00e08 | ||
|
|
b0c0104ba3 | ||
|
|
855b184e91 | ||
|
|
bc88242dc0 | ||
|
|
6357cd623f | ||
|
|
d3e7d20e30 | ||
|
|
7b568c2df3 | ||
|
|
2bff0522e8 | ||
|
|
77e180171c | ||
|
|
1e8fc97ba4 | ||
|
|
a799851832 | ||
|
|
2c81105bfc | ||
|
|
bf99ac56a0 | ||
|
|
8bfb47948a | ||
|
|
b8c41198a3 | ||
|
|
fe138ac05b | ||
|
|
bed62fdcb5 | ||
|
|
f068df0297 | ||
|
|
6a05e34d20 | ||
|
|
8b69b9c6a4 | ||
|
|
10192296fe | ||
|
|
7b98db2f7d | ||
|
|
81696c2e7a | ||
|
|
16a4f823bd | ||
|
|
39bbfc56a4 | ||
|
|
308c49b2d8 | ||
|
|
53e46a9251 | ||
|
|
6075b3dc33 | ||
|
|
860a828973 | ||
|
|
033eaa36c6 | ||
|
|
b3566f5449 | ||
|
|
f1306d3301 | ||
|
|
cc18441435 | ||
|
|
7e787d93e6 | ||
|
|
b08f444e74 | ||
|
|
09c1acdbe2 | ||
|
|
7a4a956eff | ||
|
|
aabbf5a72f | ||
|
|
5ed44bb174 | ||
|
|
92cf2cbd47 | ||
|
|
34aff6c786 | ||
|
|
11f10e586a | ||
|
|
a31feae2bd | ||
|
|
3517d58f01 | ||
|
|
9d13fadd41 | ||
|
|
c1febce528 | ||
|
|
e0aadbd961 | ||
|
|
7b9a76c5ea | ||
|
|
6133fa8384 | ||
|
|
67cc215ed3 | ||
|
|
ea4ad2ea4d | ||
|
|
847454a4f7 | ||
|
|
8cba1fdd71 | ||
|
|
e56112739c | ||
|
|
76988b746f | ||
|
|
32eb365a0b | ||
|
|
2c5feddad5 | ||
|
|
210500ff3e | ||
|
|
7c8f10e832 | ||
|
|
644da2f9e4 | ||
|
|
2a80d9c555 | ||
|
|
76f8791f3c | ||
|
|
c51a013109 | ||
|
|
88c2f95ea5 | ||
|
|
062fa7de78 | ||
|
|
d6c1a41e8d | ||
|
|
20ab5f9326 | ||
|
|
ab5a8b24a5 | ||
|
|
21a28623ca | ||
|
|
06a5fec612 | ||
|
|
46ecb8a663 | ||
|
|
3c9c6eef6e | ||
|
|
72734de376 | ||
|
|
a20ea09a6e | ||
|
|
4bd7bce232 | ||
|
|
8adf42216d | ||
|
|
5f40aaabb5 | ||
|
|
5295abbb83 | ||
|
|
201abe1524 | ||
|
|
6d26051d11 | ||
|
|
4613bf78d5 | ||
|
|
1f8fdc6f5c | ||
|
|
abf804cf10 | ||
|
|
d7a195706f | ||
|
|
569370109a | ||
|
|
eae3f04c83 | ||
|
|
ee0987f331 | ||
|
|
bdafa842bd | ||
|
|
76c7c2562c | ||
|
|
f7de2234f2 | ||
|
|
c725c45726 | ||
|
|
383bef456d | ||
|
|
7f48f3d01d | ||
|
|
0bf17a48f7 | ||
|
|
c127f8f2f0 | ||
|
|
d4d5d15e18 | ||
|
|
b254afa498 | ||
|
|
0ce5a94c15 | ||
|
|
38a5405c65 | ||
|
|
ecef784686 | ||
|
|
e82b2c12b4 | ||
|
|
3c26ced8db | ||
|
|
2681a41abb | ||
|
|
805deb1002 | ||
|
|
6b792de802 | ||
|
|
26f8e34dcb | ||
|
|
e0c1b2458c | ||
|
|
66f94d2141 | ||
|
|
503a9a0038 | ||
|
|
d8a5bc3ae9 | ||
|
|
94c3957d92 | ||
|
|
266350b8ce | ||
|
|
721d852cc7 | ||
|
|
ffbf205aba | ||
|
|
e1a1e43c3a | ||
|
|
c538817b61 | ||
|
|
71d85d2771 | ||
|
|
69f4d5fca7 | ||
|
|
bb31fc5301 | ||
|
|
87ce51e314 | ||
|
|
33555ca2ea | ||
|
|
6fc3776c0c | ||
|
|
8a05f05bd4 | ||
|
|
0dff51920e | ||
|
|
262f217d04 | ||
|
|
8b9154bab0 | ||
|
|
533613d92b | ||
|
|
20d87e1136 | ||
|
|
1450c4194e | ||
|
|
bc82baa968 | ||
|
|
218623be28 | ||
|
|
ea1a4ecdc6 | ||
|
|
6acbff3c11 | ||
|
|
153021efcf | ||
|
|
fef1b59b42 | ||
|
|
733b11b7d4 | ||
|
|
400514ff10 | ||
|
|
bbfac316fc | ||
|
|
7d0b67f70e | ||
|
|
d0f1b38848 | ||
|
|
4d4b5bc366 | ||
|
|
1f39c7ef0d | ||
|
|
2e97fcc47f | ||
|
|
550c8322c4 | ||
|
|
2467e23033 | ||
|
|
0e53198c59 | ||
|
|
44063590e2 | ||
|
|
557f25bf80 | ||
|
|
190531543f | ||
|
|
acfb7c3a89 | ||
|
|
94a6d3bc7e | ||
|
|
5751bcd382 | ||
|
|
21b88c0e65 | ||
|
|
f66b916d4b | ||
|
|
bd9c43a48d | ||
|
|
352b91f342 | ||
|
|
be6e3254ea | ||
|
|
dd1ee209ab | ||
|
|
c0b1101a52 | ||
|
|
52c49fc8fd | ||
|
|
77cb64958e | ||
|
|
b3d13e44b2 | ||
|
|
9dd7b795ca | ||
|
|
7a8399571a | ||
|
|
d0e85d9c2c | ||
|
|
14d02bc843 | ||
|
|
b79c09cf58 | ||
|
|
c9dfe5daff | ||
|
|
092e7f6b3c | ||
|
|
203634314c | ||
|
|
c472e6e160 | ||
|
|
86d9275375 | ||
|
|
9db6780af6 | ||
|
|
e10205ff69 | ||
|
|
9f539436b8 | ||
|
|
acd75e1083 | ||
|
|
f30225db90 | ||
|
|
007305962a | ||
|
|
8065e38797 | ||
|
|
34dc684867 | ||
|
|
7edc9603d0 | ||
|
|
479d476878 | ||
|
|
92bbec1852 | ||
|
|
d406636878 | ||
|
|
6362199363 | ||
|
|
22dbcb7590 | ||
|
|
cefea38ee5 | ||
|
|
d44cd01493 | ||
|
|
e578655653 | ||
|
|
434dc27557 | ||
|
|
79af39bd2c | ||
|
|
20d5a9fd4b | ||
|
|
80e57ca074 | ||
|
|
8a3c95d8dd | ||
|
|
4941a360cb | ||
|
|
9e7219c4d6 | ||
|
|
4d96728709 | ||
|
|
816fd5e65c | ||
|
|
8d4175536f | ||
|
|
bba9ca3d5a | ||
|
|
8bc9ebf2aa | ||
|
|
dad335b8b6 | ||
|
|
62e93d4002 | ||
|
|
728f4a0f81 | ||
|
|
0cafd6ee4b | ||
|
|
2962e51dac | ||
|
|
cf5733b237 | ||
|
|
c96e3326bc | ||
|
|
1cd373c0a5 | ||
|
|
ae970cf2da | ||
|
|
d43a01f182 | ||
|
|
42aac55b82 | ||
|
|
a66f31dc87 | ||
|
|
64c48f2151 | ||
|
|
9eead8a904 | ||
|
|
7761bccffe | ||
|
|
a848933875 | ||
|
|
e475f3d752 | ||
|
|
4d24dbc661 | ||
|
|
0368c3fae9 | ||
|
|
2cf93ccb54 | ||
|
|
3d72cafea4 | ||
|
|
af0466ea83 | ||
|
|
c84f2996ec | ||
|
|
add9b83d3b | ||
|
|
5411fb6fd4 | ||
|
|
49fc1b40e5 | ||
|
|
daef2eecdd | ||
|
|
1520422cc3 | ||
|
|
c7530085a6 | ||
|
|
bf43c567d8 | ||
|
|
b81f3e4f8d | ||
|
|
97051b9d40 | ||
|
|
90730845de | ||
|
|
f7c0c433c3 | ||
|
|
538752d0cf | ||
|
|
c71a92ac84 | ||
|
|
85f23b3408 | ||
|
|
44cab0a4d7 | ||
|
|
33db8ee0c3 | ||
|
|
da4b3770c0 | ||
|
|
12698998b9 | ||
|
|
6177856baf | ||
|
|
54b9c46b96 | ||
|
|
2dd83e7dbe | ||
|
|
17351e8f91 | ||
|
|
e8c153f72f | ||
|
|
ddfd1276c5 | ||
|
|
46251c6a1c | ||
|
|
e699968be5 | ||
|
|
389b58b75b | ||
|
|
a7cf34dea4 | ||
|
|
e601682706 | ||
|
|
55f16dc0b5 | ||
|
|
8801fda972 | ||
|
|
d8e9da35d6 |
24
.github/workflows/close-stale.yml
vendored
24
.github/workflows/close-stale.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: 'This issue has been labelled stale because it has been open for 2 weeks with no activity. Remove stale label or add a comment to keep this issue open. Otherwise, it will be closed in 7 days.'
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 3 weeks with no activity. Feel free to add a comment here and we can re-open it. Or feel free to file a new issue any time.'
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
only-labels: 'question'
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
@@ -187,8 +187,8 @@ pytest
|
||||
You can also run specific test files or test cases by providing the file path or test name:
|
||||
|
||||
```
|
||||
pytest aider/tests/test_coder.py
|
||||
pytest aider/tests/test_coder.py::TestCoder::test_specific_case
|
||||
pytest tests/basic/test_coder.py
|
||||
pytest tests/basic/test_coder.py::TestCoder::test_specific_case
|
||||
```
|
||||
|
||||
#### Continuous Integration
|
||||
|
||||
44
HISTORY.md
44
HISTORY.md
@@ -1,6 +1,48 @@
|
||||
|
||||
# Release history
|
||||
|
||||
### main branch
|
||||
|
||||
- Added [`/editor` command](https://aider.chat/docs/usage/commands.html) to open system editor for writing prompts, by @thehunmonkgroup.
|
||||
- Full support for `gpt-4o-2024-11-20`.
|
||||
- Stream o1 models by default.
|
||||
- `/run` and suggested shell commands are less mysterious and now confirm that they "Added XX lines of output to the chat."
|
||||
- Ask 1% of users if they want to opt-in to [analytics](https://aider.chat/docs/more/analytics.html).
|
||||
- Added support for [optional multiline input tags](https://aider.chat/docs/usage/commands.html#entering-multi-line-chat-messages) with matching closing tags.
|
||||
- Improved [model settings configuration](https://aider.chat/docs/config/adv-model-settings.html#global-extra-params) with support for global `extra_params` for `litellm.completion()`.
|
||||
- Architect mode now asks to add files suggested by the LLM.
|
||||
- Fixed bug in fuzzy model name matching.
|
||||
- Added Timeout exception to handle API provider timeouts.
|
||||
- Added `--show-release-notes` to control release notes display on first run of new version.
|
||||
- Save empty dict to cache file on model metadata download failure, to delay retry.
|
||||
- Improved error handling and code formatting.
|
||||
- Aider wrote 74% of the code in this release.
|
||||
|
||||
### Aider v0.63.2
|
||||
|
||||
- Fixed bug in fuzzy model name matching when litellm provider info is missing.
|
||||
- Modified model metadata file loading to allow override of resource file.
|
||||
- Allow recursive loading of dirs using `--read`.
|
||||
- Updated dependency versions to pick up litellm fix for ollama models.
|
||||
- Added exponential backoff retry when writing files to handle editor file locks.
|
||||
- Updated Qwen 2.5 Coder 32B model configuration.
|
||||
|
||||
### Aider v0.63.1
|
||||
|
||||
- Fixed bug in git ignored file handling.
|
||||
- Improved error handling for git operations.
|
||||
|
||||
### Aider v0.63.0
|
||||
|
||||
- Support for Qwen 2.5 Coder 32B.
|
||||
- `/web` command just adds the page to the chat, without triggering an LLM response.
|
||||
- Improved prompting for the user's preferred chat language.
|
||||
- Improved handling of LiteLLM exceptions.
|
||||
- Bugfix for double-counting tokens when reporting cache stats.
|
||||
- Bugfix for the LLM creating new files.
|
||||
- Other small bug fixes.
|
||||
- Aider wrote 55% of the code in this release.
|
||||
|
||||
### Aider v0.62.0
|
||||
|
||||
- Full support for Claude 3.5 Haiku
|
||||
@@ -8,6 +50,8 @@
|
||||
- Almost as good as Sonnet at much lower cost.
|
||||
- Launch with `--haiku` to use it.
|
||||
- Easily apply file edits from ChatGPT, Claude or other web apps
|
||||
- Chat with ChatGPT or Claude via their web app.
|
||||
- Give it your source files and ask for the changes you want.
|
||||
- Use the web app's "copy response" button to copy the entire reply from the LLM.
|
||||
- Run `aider --apply-clipboard-edits file-to-edit.js`.
|
||||
- Aider will edit your file with the LLM's changes.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
try:
|
||||
from aider.__version__ import __version__
|
||||
except Exception:
|
||||
__version__ = "0.62.1.dev"
|
||||
__version__ = "0.64.1.dev"
|
||||
|
||||
__all__ = [__version__]
|
||||
|
||||
@@ -62,8 +62,47 @@ class Analytics:
|
||||
self.permanently_disable = True
|
||||
self.save_data()
|
||||
|
||||
def need_to_ask(self):
|
||||
return not self.asked_opt_in and not self.permanently_disable
|
||||
def need_to_ask(self, args_analytics):
|
||||
if args_analytics is False:
|
||||
return False
|
||||
|
||||
could_ask = not self.asked_opt_in and not self.permanently_disable
|
||||
if not could_ask:
|
||||
return False
|
||||
|
||||
if args_analytics is True:
|
||||
return True
|
||||
|
||||
assert args_analytics is None, args_analytics
|
||||
|
||||
if not self.user_id:
|
||||
return False
|
||||
|
||||
PERCENT = 1
|
||||
return self.is_uuid_in_percentage(self.user_id, PERCENT)
|
||||
|
||||
def is_uuid_in_percentage(self, uuid_str, percent):
|
||||
"""Check if a UUID string falls within the first X percent of the UUID space.
|
||||
|
||||
Args:
|
||||
uuid_str: UUID string to test
|
||||
percent: Percentage threshold (0-100)
|
||||
|
||||
Returns:
|
||||
bool: True if UUID falls within the first X percent
|
||||
"""
|
||||
if not (0 <= percent <= 100):
|
||||
raise ValueError("Percentage must be between 0 and 100")
|
||||
|
||||
if not uuid_str:
|
||||
return False
|
||||
|
||||
# Convert percentage to hex threshold (1% = "04...", 10% = "1a...", etc)
|
||||
# Using first 6 hex digits
|
||||
if percent == 0:
|
||||
return False
|
||||
threshold = format(int(0xFFFFFF * percent / 100), "06x")
|
||||
return uuid_str[:6] <= threshold
|
||||
|
||||
def get_data_file_path(self):
|
||||
data_file = Path.home() / ".aider" / "analytics.json"
|
||||
|
||||
@@ -562,8 +562,8 @@ def get_parser(default_config_files, git_root):
|
||||
group.add_argument(
|
||||
"--analytics",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=False,
|
||||
help="Enable/disable analytics for one session (default: False)",
|
||||
default=None,
|
||||
help="Enable/disable analytics for current session (default: random)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--analytics-log",
|
||||
@@ -620,6 +620,12 @@ def get_parser(default_config_files, git_root):
|
||||
help="Check for new aider versions on launch",
|
||||
default=True,
|
||||
)
|
||||
group.add_argument(
|
||||
"--show-release-notes",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Show release notes on first run of new version (default: None, ask user)",
|
||||
default=None,
|
||||
)
|
||||
group.add_argument(
|
||||
"--install-main-branch",
|
||||
action="store_true",
|
||||
@@ -732,6 +738,10 @@ def get_parser(default_config_files, git_root):
|
||||
default=True,
|
||||
help="Enable/disable fancy input with history and completion (default: True)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--editor",
|
||||
help="Specify which editor to use for the /editor command",
|
||||
)
|
||||
|
||||
##########
|
||||
group = parser.add_argument_group("Voice Settings")
|
||||
|
||||
@@ -10,6 +10,9 @@ class ArchitectCoder(AskCoder):
|
||||
def reply_completed(self):
|
||||
content = self.partial_response_content
|
||||
|
||||
if not content or not content.strip():
|
||||
return
|
||||
|
||||
if not self.io.confirm_ask("Edit the files?"):
|
||||
return
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Just show the changes needed.
|
||||
|
||||
DO NOT show the entire updated function/file/etc!
|
||||
|
||||
Always reply in the same language as the change request.
|
||||
Always reply to the user in {language}.
|
||||
"""
|
||||
|
||||
example_messages = []
|
||||
|
||||
@@ -6,7 +6,7 @@ from .base_prompts import CoderPrompts
|
||||
class AskPrompts(CoderPrompts):
|
||||
main_system = """Act as an expert code analyst.
|
||||
Answer questions about the supplied code.
|
||||
Always reply to the user in the same language they are using.
|
||||
Always reply to the user in {language}.
|
||||
"""
|
||||
|
||||
example_messages = []
|
||||
|
||||
@@ -13,7 +13,6 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import webbrowser
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from json.decoder import JSONDecodeError
|
||||
@@ -23,6 +22,7 @@ from typing import List
|
||||
from aider import __version__, models, prompts, urls, utils
|
||||
from aider.analytics import Analytics
|
||||
from aider.commands import Commands
|
||||
from aider.exceptions import LiteLLMExceptions
|
||||
from aider.history import ChatSummary
|
||||
from aider.io import ConfirmGroup, InputOutput
|
||||
from aider.linter import Linter
|
||||
@@ -30,7 +30,7 @@ from aider.llm import litellm
|
||||
from aider.repo import ANY_GIT_ERROR, GitRepo
|
||||
from aider.repomap import RepoMap
|
||||
from aider.run_cmd import run_cmd
|
||||
from aider.sendchat import RETRY_TIMEOUT, retry_exceptions, send_completion
|
||||
from aider.sendchat import RETRY_TIMEOUT, send_completion
|
||||
from aider.utils import format_content, format_messages, format_tokens, is_image_file
|
||||
|
||||
from ..dump import dump # noqa: F401
|
||||
@@ -355,6 +355,9 @@ class Coder:
|
||||
|
||||
for fname in fnames:
|
||||
fname = Path(fname)
|
||||
if self.repo and self.repo.git_ignored_file(fname):
|
||||
self.io.tool_warning(f"Skipping {fname} that matches gitignore spec.")
|
||||
|
||||
if self.repo and self.repo.ignored_file(fname):
|
||||
self.io.tool_warning(f"Skipping {fname} that matches aiderignore spec.")
|
||||
continue
|
||||
@@ -790,34 +793,9 @@ class Coder:
|
||||
self.num_reflections += 1
|
||||
message = self.reflected_message
|
||||
|
||||
def check_and_open_urls(self, exc: Exception) -> List[str]:
|
||||
import openai
|
||||
|
||||
def check_and_open_urls(self, exc, friendly_msg=None):
|
||||
"""Check exception for URLs, offer to open in a browser, with user-friendly error msgs."""
|
||||
text = str(exc)
|
||||
friendly_msg = None
|
||||
|
||||
if isinstance(exc, (openai.APITimeoutError, openai.APIConnectionError)):
|
||||
friendly_msg = (
|
||||
"There is a problem connecting to the API provider. Please try again later or check"
|
||||
" your model settings."
|
||||
)
|
||||
elif isinstance(exc, openai.RateLimitError):
|
||||
friendly_msg = (
|
||||
"The API provider's rate limits have been exceeded. Check with your provider or"
|
||||
" wait awhile and retry."
|
||||
)
|
||||
elif isinstance(exc, openai.InternalServerError):
|
||||
friendly_msg = (
|
||||
"The API provider seems to be down or overloaded. Please try again later."
|
||||
)
|
||||
elif isinstance(exc, openai.BadRequestError):
|
||||
friendly_msg = "The API provider refused the request as invalid?"
|
||||
elif isinstance(exc, openai.AuthenticationError):
|
||||
friendly_msg = (
|
||||
"The API provider refused your authentication. Please check that you are using a"
|
||||
" valid API key."
|
||||
)
|
||||
|
||||
if friendly_msg:
|
||||
self.io.tool_warning(text)
|
||||
@@ -829,8 +807,7 @@ class Coder:
|
||||
urls = list(set(url_pattern.findall(text))) # Use set to remove duplicates
|
||||
for url in urls:
|
||||
url = url.rstrip(".',\"")
|
||||
if self.io.confirm_ask("Open URL for more info about this error?", subject=url):
|
||||
webbrowser.open(url)
|
||||
self.io.offer_url(url)
|
||||
return urls
|
||||
|
||||
def check_for_urls(self, inp: str) -> List[str]:
|
||||
@@ -846,7 +823,7 @@ class Coder:
|
||||
"Add URL to the chat?", subject=url, group=group, allow_never=True
|
||||
):
|
||||
inp += "\n\n"
|
||||
inp += self.commands.cmd_web(url)
|
||||
inp += self.commands.cmd_web(url, return_content=True)
|
||||
added_urls.append(url)
|
||||
else:
|
||||
self.rejected_urls.add(url)
|
||||
@@ -982,12 +959,18 @@ class Coder:
|
||||
platform=platform_text
|
||||
)
|
||||
|
||||
if self.chat_language:
|
||||
language = self.chat_language
|
||||
else:
|
||||
language = "the same language they are using"
|
||||
|
||||
prompt = prompt.format(
|
||||
fence=self.fence,
|
||||
lazy_prompt=lazy_prompt,
|
||||
platform=platform_text,
|
||||
shell_cmd_prompt=shell_cmd_prompt,
|
||||
shell_cmd_reminder=shell_cmd_reminder,
|
||||
language=language,
|
||||
)
|
||||
return prompt
|
||||
|
||||
@@ -1154,8 +1137,6 @@ class Coder:
|
||||
return chunks
|
||||
|
||||
def send_message(self, inp):
|
||||
import openai # for error codes below
|
||||
|
||||
self.cur_messages += [
|
||||
dict(role="user", content=inp),
|
||||
]
|
||||
@@ -1175,6 +1156,8 @@ class Coder:
|
||||
|
||||
retry_delay = 0.125
|
||||
|
||||
litellm_ex = LiteLLMExceptions()
|
||||
|
||||
self.usage_report = None
|
||||
exhausted = False
|
||||
interrupted = False
|
||||
@@ -1183,30 +1166,37 @@ class Coder:
|
||||
try:
|
||||
yield from self.send(messages, functions=self.functions)
|
||||
break
|
||||
except retry_exceptions() as err:
|
||||
# Print the error and its base classes
|
||||
# for cls in err.__class__.__mro__: dump(cls.__name__)
|
||||
except litellm_ex.exceptions_tuple() as err:
|
||||
ex_info = litellm_ex.get_ex_info(err)
|
||||
|
||||
retry_delay *= 2
|
||||
if retry_delay > RETRY_TIMEOUT:
|
||||
self.mdstream = None
|
||||
self.check_and_open_urls(err)
|
||||
if ex_info.name == "ContextWindowExceededError":
|
||||
exhausted = True
|
||||
break
|
||||
|
||||
should_retry = ex_info.retry
|
||||
if should_retry:
|
||||
retry_delay *= 2
|
||||
if retry_delay > RETRY_TIMEOUT:
|
||||
should_retry = False
|
||||
|
||||
if not should_retry:
|
||||
self.mdstream = None
|
||||
self.check_and_open_urls(err, ex_info.description)
|
||||
break
|
||||
|
||||
err_msg = str(err)
|
||||
self.io.tool_error(err_msg)
|
||||
if ex_info.description:
|
||||
self.io.tool_warning(err_msg)
|
||||
self.io.tool_error(ex_info.description)
|
||||
else:
|
||||
self.io.tool_error(err_msg)
|
||||
|
||||
self.io.tool_output(f"Retrying in {retry_delay:.1f} seconds...")
|
||||
time.sleep(retry_delay)
|
||||
continue
|
||||
except KeyboardInterrupt:
|
||||
interrupted = True
|
||||
break
|
||||
except litellm.ContextWindowExceededError:
|
||||
# The input is overflowing the context window!
|
||||
exhausted = True
|
||||
break
|
||||
except litellm.exceptions.BadRequestError as br_err:
|
||||
self.io.tool_error(f"BadRequestError: {br_err}")
|
||||
return
|
||||
except FinishReasonLength:
|
||||
# We hit the output limit!
|
||||
if not self.main_model.info.get("supports_assistant_prefill"):
|
||||
@@ -1221,12 +1211,8 @@ class Coder:
|
||||
messages.append(
|
||||
dict(role="assistant", content=self.multi_response_content, prefix=True)
|
||||
)
|
||||
except (openai.APIError, openai.APIStatusError) as err:
|
||||
# for cls in err.__class__.__mro__: dump(cls.__name__)
|
||||
self.mdstream = None
|
||||
self.check_and_open_urls(err)
|
||||
break
|
||||
except Exception as err:
|
||||
self.mdstream = None
|
||||
lines = traceback.format_exception(type(err), err, err.__traceback__)
|
||||
self.io.tool_warning("".join(lines))
|
||||
self.io.tool_error(str(err))
|
||||
@@ -1259,10 +1245,19 @@ class Coder:
|
||||
else:
|
||||
content = ""
|
||||
|
||||
try:
|
||||
self.reply_completed()
|
||||
except KeyboardInterrupt:
|
||||
interrupted = True
|
||||
if not interrupted:
|
||||
add_rel_files_message = self.check_for_file_mentions(content)
|
||||
if add_rel_files_message:
|
||||
if self.reflected_message:
|
||||
self.reflected_message += "\n\n" + add_rel_files_message
|
||||
else:
|
||||
self.reflected_message = add_rel_files_message
|
||||
return
|
||||
|
||||
try:
|
||||
self.reply_completed()
|
||||
except KeyboardInterrupt:
|
||||
interrupted = True
|
||||
|
||||
if interrupted:
|
||||
content += "\n^C KeyboardInterrupt"
|
||||
@@ -1313,13 +1308,6 @@ class Coder:
|
||||
self.update_cur_messages()
|
||||
return
|
||||
|
||||
add_rel_files_message = self.check_for_file_mentions(content)
|
||||
if add_rel_files_message:
|
||||
if self.reflected_message:
|
||||
self.reflected_message += "\n\n" + add_rel_files_message
|
||||
else:
|
||||
self.reflected_message = add_rel_files_message
|
||||
|
||||
def reply_completed(self):
|
||||
pass
|
||||
|
||||
@@ -1374,11 +1362,9 @@ class Coder:
|
||||
res.append("- Use /clear to clear the chat history.")
|
||||
res.append("- Break your code into smaller source files.")
|
||||
|
||||
res.append("")
|
||||
res.append(f"For more info: {urls.token_limits}")
|
||||
|
||||
res = "".join([line + "\n" for line in res])
|
||||
self.io.tool_error(res)
|
||||
self.io.offer_url(urls.token_limits)
|
||||
|
||||
def lint_edited(self, fnames):
|
||||
res = ""
|
||||
@@ -1622,7 +1608,6 @@ class Coder:
|
||||
completion.usage, "cache_creation_input_tokens"
|
||||
):
|
||||
self.message_tokens_sent += prompt_tokens
|
||||
self.message_tokens_sent += cache_hit_tokens
|
||||
self.message_tokens_sent += cache_write_tokens
|
||||
else:
|
||||
self.message_tokens_sent += prompt_tokens
|
||||
@@ -1799,6 +1784,10 @@ class Coder:
|
||||
self.check_for_dirty_commit(path)
|
||||
return True
|
||||
|
||||
if self.repo and self.repo.git_ignored_file(path):
|
||||
self.io.tool_warning(f"Skipping edits to {path} that matches gitignore spec.")
|
||||
return
|
||||
|
||||
if not Path(full_path).exists():
|
||||
if not self.io.confirm_ask("Create new file?", subject=path):
|
||||
self.io.tool_output(f"Skipping edits to {path}")
|
||||
@@ -2072,9 +2061,10 @@ class Coder:
|
||||
if output:
|
||||
accumulated_output += f"Output from {command}\n{output}\n"
|
||||
|
||||
if accumulated_output.strip() and not self.io.confirm_ask(
|
||||
if accumulated_output.strip() and self.io.confirm_ask(
|
||||
"Add command output to the chat?", allow_never=True
|
||||
):
|
||||
accumulated_output = ""
|
||||
|
||||
return accumulated_output
|
||||
num_lines = len(accumulated_output.strip().splitlines())
|
||||
line_plural = "line" if num_lines == 1 else "lines"
|
||||
self.io.tool_output(f"Added {num_lines} {line_plural} of output to the chat.")
|
||||
return accumulated_output
|
||||
|
||||
@@ -52,7 +52,10 @@ class EditBlockCoder(Coder):
|
||||
content = self.io.read_text(full_path)
|
||||
new_content = do_replace(full_path, content, original, updated, self.fence)
|
||||
|
||||
if not new_content:
|
||||
# If the edit failed, and
|
||||
# this is not a "create a new file" with an empty original...
|
||||
# https://github.com/Aider-AI/aider/issues/2258
|
||||
if not new_content and original.strip():
|
||||
# try patching any of the other files in the chat
|
||||
for full_path in self.abs_fnames:
|
||||
content = self.io.read_text(full_path)
|
||||
|
||||
@@ -11,7 +11,7 @@ Respect and use existing conventions, libraries, etc that are already present in
|
||||
Take requests for changes to the supplied code.
|
||||
If the request is ambiguous, ask questions.
|
||||
|
||||
Always reply to the user in the same language they are using.
|
||||
Always reply to the user in {language}.
|
||||
|
||||
Once you understand the request you MUST:
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Respect and use existing conventions, libraries, etc that are already present in
|
||||
Take requests for changes to the supplied code.
|
||||
If the request is ambiguous, ask questions.
|
||||
|
||||
Always reply to the user in the same language they are using.
|
||||
Always reply to the user in {language}.
|
||||
|
||||
For each file that needs to be changed, write out the changes similar to a unified diff like `diff -U0` would produce.
|
||||
"""
|
||||
|
||||
@@ -8,7 +8,7 @@ class WholeFilePrompts(CoderPrompts):
|
||||
Take requests for changes to the supplied code.
|
||||
If the request is ambiguous, ask questions.
|
||||
|
||||
Always reply to the user in the same language they are using.
|
||||
Always reply to the user in {language}.
|
||||
|
||||
{lazy_prompt}
|
||||
Once you understand the request you MUST:
|
||||
|
||||
@@ -14,6 +14,7 @@ from prompt_toolkit.completion import Completion, PathCompleter
|
||||
from prompt_toolkit.document import Document
|
||||
|
||||
from aider import models, prompts, voice
|
||||
from aider.editor import pipe_editor
|
||||
from aider.format_settings import format_settings
|
||||
from aider.help import Help, install_help_extra
|
||||
from aider.llm import litellm
|
||||
@@ -45,7 +46,15 @@ class Commands:
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, io, coder, voice_language=None, verify_ssl=True, args=None, parser=None, verbose=False
|
||||
self,
|
||||
io,
|
||||
coder,
|
||||
voice_language=None,
|
||||
verify_ssl=True,
|
||||
args=None,
|
||||
parser=None,
|
||||
verbose=False,
|
||||
editor=None,
|
||||
):
|
||||
self.io = io
|
||||
self.coder = coder
|
||||
@@ -60,6 +69,7 @@ class Commands:
|
||||
self.voice_language = voice_language
|
||||
|
||||
self.help = None
|
||||
self.editor = editor
|
||||
|
||||
def cmd_model(self, args):
|
||||
"Switch to a new LLM"
|
||||
@@ -139,7 +149,7 @@ class Commands:
|
||||
else:
|
||||
self.io.tool_output("Please provide a partial model name to search for.")
|
||||
|
||||
def cmd_web(self, args):
|
||||
def cmd_web(self, args, return_content=False):
|
||||
"Scrape a webpage, convert to markdown and send in a message"
|
||||
|
||||
url = args.strip()
|
||||
@@ -158,11 +168,16 @@ class Commands:
|
||||
)
|
||||
|
||||
content = self.scraper.scrape(url) or ""
|
||||
content = f"{url}:\n\n" + content
|
||||
content = f"Here is the content of {url}:\n\n" + content
|
||||
if return_content:
|
||||
return content
|
||||
|
||||
self.io.tool_output("... done.")
|
||||
self.io.tool_output("... added to chat.")
|
||||
|
||||
return content
|
||||
self.coder.cur_messages += [
|
||||
dict(role="user", content=content),
|
||||
dict(role="assistant", content="Ok."),
|
||||
]
|
||||
|
||||
def is_command(self, inp):
|
||||
return inp[0] in "/!"
|
||||
@@ -738,6 +753,10 @@ class Commands:
|
||||
)
|
||||
continue
|
||||
|
||||
if self.coder.repo and self.coder.repo.git_ignored_file(matched_file):
|
||||
self.io.tool_error(f"Can't add {matched_file} which is in gitignore")
|
||||
continue
|
||||
|
||||
if abs_file_path in self.coder.abs_fnames:
|
||||
self.io.tool_error(f"{matched_file} is already in the chat as an editable file")
|
||||
continue
|
||||
@@ -859,7 +878,6 @@ class Commands:
|
||||
exit_status, combined_output = run_cmd(
|
||||
args, verbose=self.verbose, error_print=self.io.tool_error
|
||||
)
|
||||
instructions = None
|
||||
|
||||
if combined_output is None:
|
||||
return
|
||||
@@ -867,36 +885,22 @@ class Commands:
|
||||
if add_on_nonzero_exit:
|
||||
add = exit_status != 0
|
||||
else:
|
||||
self.io.tool_output()
|
||||
response = self.io.prompt_ask(
|
||||
"Add the output to the chat?\n(Y)es/(n)o/message with instructions:",
|
||||
).strip()
|
||||
self.io.tool_output()
|
||||
|
||||
if response.lower() in ["yes", "y"]:
|
||||
add = True
|
||||
elif response.lower() in ["no", "n"]:
|
||||
add = False
|
||||
else:
|
||||
add = True
|
||||
instructions = response
|
||||
if response.strip():
|
||||
self.io.user_input(response, log_only=True)
|
||||
self.io.add_to_input_history(response)
|
||||
add = self.io.confirm_ask("Add command output to the chat?")
|
||||
|
||||
if add:
|
||||
for line in combined_output.splitlines():
|
||||
self.io.tool_output(line, log_only=True)
|
||||
num_lines = len(combined_output.strip().splitlines())
|
||||
line_plural = "line" if num_lines == 1 else "lines"
|
||||
self.io.tool_output(f"Added {num_lines} {line_plural} of output to the chat.")
|
||||
|
||||
msg = prompts.run_output.format(
|
||||
command=args,
|
||||
output=combined_output,
|
||||
)
|
||||
|
||||
if instructions:
|
||||
msg = instructions + "\n\n" + msg
|
||||
|
||||
return msg
|
||||
self.coder.cur_messages += [
|
||||
dict(role="user", content=msg),
|
||||
dict(role="assistant", content="Ok."),
|
||||
]
|
||||
|
||||
def cmd_exit(self, args):
|
||||
"Exit the application"
|
||||
@@ -1357,6 +1361,14 @@ class Commands:
|
||||
|
||||
report_github_issue(issue_text, title=title, confirm=False)
|
||||
|
||||
def cmd_editor(self, initial_content=""):
|
||||
"Open an editor to write a prompt"
|
||||
|
||||
user_input = pipe_editor(initial_content, suffix="md", editor=self.editor)
|
||||
self.io.user_input(user_input, log_only=False)
|
||||
self.io.add_to_input_history(user_input)
|
||||
return user_input
|
||||
|
||||
|
||||
def expand_subdir(file_path):
|
||||
if file_path.is_file():
|
||||
|
||||
@@ -50,7 +50,6 @@ def diff_partial_update(lines_orig, lines_updated, final=False, fname=None):
|
||||
# dump(lines_orig)
|
||||
# dump(lines_updated)
|
||||
|
||||
assert_newlines(lines_orig)
|
||||
assert_newlines(lines_orig)
|
||||
|
||||
num_orig_lines = len(lines_orig)
|
||||
|
||||
146
aider/editor.py
Normal file
146
aider/editor.py
Normal file
@@ -0,0 +1,146 @@
|
||||
"""
|
||||
Editor module for handling system text editor interactions.
|
||||
|
||||
This module provides functionality to:
|
||||
- Discover and launch the system's configured text editor
|
||||
- Create and manage temporary files for editing
|
||||
- Handle editor preferences from environment variables
|
||||
- Support cross-platform editor operations
|
||||
"""
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
DEFAULT_EDITOR_NIX = "vi"
|
||||
DEFAULT_EDITOR_OS_X = "vim"
|
||||
DEFAULT_EDITOR_WINDOWS = "notepad"
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def print_status_message(success, message, style=None):
|
||||
"""
|
||||
Print a status message with appropriate styling.
|
||||
|
||||
:param success: Whether the operation was successful
|
||||
:param message: The message to display
|
||||
:param style: Optional style override. If None, uses green for success and red for failure
|
||||
"""
|
||||
if style is None:
|
||||
style = "bold green" if success else "bold red"
|
||||
console.print(message, style=style)
|
||||
print("")
|
||||
|
||||
|
||||
def write_temp_file(
|
||||
input_data="",
|
||||
suffix=None,
|
||||
prefix=None,
|
||||
dir=None,
|
||||
):
|
||||
"""
|
||||
Create a temporary file with the given input data.
|
||||
|
||||
:param input_data: Content to write to the temporary file
|
||||
:param suffix: Optional file extension (without the dot)
|
||||
:param prefix: Optional prefix for the temporary filename
|
||||
:param dir: Optional directory to create the file in
|
||||
:return: Path to the created temporary file
|
||||
:raises: OSError if file creation or writing fails
|
||||
"""
|
||||
kwargs = {"prefix": prefix, "dir": dir}
|
||||
if suffix:
|
||||
kwargs["suffix"] = f".{suffix}"
|
||||
fd, filepath = tempfile.mkstemp(**kwargs)
|
||||
try:
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(input_data)
|
||||
except Exception:
|
||||
os.close(fd)
|
||||
raise
|
||||
return filepath
|
||||
|
||||
|
||||
def get_environment_editor(default=None):
|
||||
"""
|
||||
Fetches the preferred editor from the environment variables.
|
||||
|
||||
This function checks the following environment variables in order to
|
||||
determine the user's preferred editor:
|
||||
|
||||
- VISUAL
|
||||
- EDITOR
|
||||
|
||||
:param default: The default editor to return if no environment variable is set.
|
||||
:type default: str or None
|
||||
:return: The preferred editor as specified by environment variables or the default value.
|
||||
:rtype: str or None
|
||||
"""
|
||||
editor = os.environ.get("VISUAL", os.environ.get("EDITOR", default))
|
||||
return editor
|
||||
|
||||
|
||||
def discover_editor(editor_override=None):
|
||||
"""
|
||||
Discovers and returns the appropriate editor command as a list of arguments.
|
||||
|
||||
Handles cases where the editor command includes arguments, including quoted arguments
|
||||
with spaces (e.g. 'vim -c "set noswapfile"').
|
||||
|
||||
:return: A list of command parts ready for subprocess execution
|
||||
:rtype: list[str]
|
||||
"""
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
default_editor = DEFAULT_EDITOR_WINDOWS
|
||||
elif system == "Darwin":
|
||||
default_editor = DEFAULT_EDITOR_OS_X
|
||||
else:
|
||||
default_editor = DEFAULT_EDITOR_NIX
|
||||
if editor_override:
|
||||
editor = editor_override
|
||||
else:
|
||||
editor = get_environment_editor(default_editor)
|
||||
try:
|
||||
return shlex.split(editor)
|
||||
except ValueError as e:
|
||||
raise RuntimeError(f"Invalid editor command format '{editor}': {e}")
|
||||
|
||||
|
||||
def pipe_editor(input_data="", suffix=None, editor=None):
|
||||
"""
|
||||
Opens the system editor with optional input data and returns the edited content.
|
||||
|
||||
This function creates a temporary file with the provided input data, opens it in
|
||||
the system editor, waits for the user to make changes and close the editor, then
|
||||
reads and returns the modified content. The temporary file is deleted afterwards.
|
||||
|
||||
:param input_data: Initial content to populate the editor with
|
||||
:type input_data: str
|
||||
:param suffix: Optional file extension for the temporary file (e.g. '.txt', '.md')
|
||||
:type suffix: str or None
|
||||
:return: The edited content after the editor is closed
|
||||
:rtype: str
|
||||
"""
|
||||
filepath = write_temp_file(input_data, suffix)
|
||||
command_parts = discover_editor(editor)
|
||||
command_parts.append(filepath)
|
||||
subprocess.call(command_parts)
|
||||
with open(filepath, "r") as f:
|
||||
output_data = f.read()
|
||||
try:
|
||||
os.remove(filepath)
|
||||
except PermissionError:
|
||||
print_status_message(
|
||||
False,
|
||||
(
|
||||
f"WARNING: Unable to delete temporary file {filepath!r}. You may need to delete it"
|
||||
" manually."
|
||||
),
|
||||
)
|
||||
return output_data
|
||||
81
aider/exceptions.py
Normal file
81
aider/exceptions.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExInfo:
|
||||
name: str
|
||||
retry: bool
|
||||
description: str
|
||||
|
||||
|
||||
EXCEPTIONS = [
|
||||
ExInfo("APIConnectionError", True, None),
|
||||
ExInfo("APIError", True, None),
|
||||
ExInfo("APIResponseValidationError", True, None),
|
||||
ExInfo(
|
||||
"AuthenticationError",
|
||||
False,
|
||||
"The API provider is not able to authenticate you. Check your API key.",
|
||||
),
|
||||
ExInfo("AzureOpenAIError", True, None),
|
||||
ExInfo("BadRequestError", False, None),
|
||||
ExInfo("BudgetExceededError", True, None),
|
||||
ExInfo(
|
||||
"ContentPolicyViolationError",
|
||||
True,
|
||||
"The API provider has refused the request due to a safety policy about the content.",
|
||||
),
|
||||
ExInfo("ContextWindowExceededError", False, None), # special case handled in base_coder
|
||||
ExInfo("InternalServerError", True, "The API provider's servers are down or overloaded."),
|
||||
ExInfo("InvalidRequestError", True, None),
|
||||
ExInfo("JSONSchemaValidationError", True, None),
|
||||
ExInfo("NotFoundError", False, None),
|
||||
ExInfo("OpenAIError", True, None),
|
||||
ExInfo(
|
||||
"RateLimitError",
|
||||
True,
|
||||
"The API provider has rate limited you. Try again later or check your quotas.",
|
||||
),
|
||||
ExInfo("RouterRateLimitError", True, None),
|
||||
ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."),
|
||||
ExInfo("UnprocessableEntityError", True, None),
|
||||
ExInfo("UnsupportedParamsError", True, None),
|
||||
ExInfo(
|
||||
"Timeout",
|
||||
True,
|
||||
"The API provider timed out without returning a response. They may be down or overloaded.",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
class LiteLLMExceptions:
|
||||
exceptions = dict()
|
||||
|
||||
def __init__(self):
|
||||
self._load()
|
||||
|
||||
def _load(self, strict=False):
|
||||
import litellm
|
||||
|
||||
for var in dir(litellm):
|
||||
if not var.endswith("Error"):
|
||||
continue
|
||||
|
||||
ex_info = None
|
||||
for exi in EXCEPTIONS:
|
||||
if var == exi.name:
|
||||
ex_info = exi
|
||||
break
|
||||
|
||||
if strict and not ex_info:
|
||||
raise ValueError(f"{var} is in litellm but not in aider's exceptions list")
|
||||
|
||||
ex = getattr(litellm, var)
|
||||
self.exceptions[ex] = ex_info
|
||||
|
||||
def exceptions_tuple(self):
|
||||
return tuple(self.exceptions)
|
||||
|
||||
def get_ex_info(self, ex):
|
||||
"""Return the ExInfo for a given exception instance"""
|
||||
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
||||
@@ -7,4 +7,5 @@ exclude_website_pats = [
|
||||
"docs/unified-diffs.md",
|
||||
"docs/leaderboards/index.md",
|
||||
"assets/**",
|
||||
"**/.DS_Store",
|
||||
]
|
||||
|
||||
121
aider/io.py
121
aider/io.py
@@ -1,5 +1,7 @@
|
||||
import base64
|
||||
import os
|
||||
import time
|
||||
import webbrowser
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
@@ -332,14 +334,36 @@ class InputOutput:
|
||||
self.tool_error("Use --encoding to set the unicode encoding.")
|
||||
return
|
||||
|
||||
def write_text(self, filename, content):
|
||||
def write_text(self, filename, content, max_retries=5, initial_delay=0.1):
|
||||
"""
|
||||
Writes content to a file, retrying with progressive backoff if the file is locked.
|
||||
|
||||
:param filename: Path to the file to write.
|
||||
:param content: Content to write to the file.
|
||||
:param max_retries: Maximum number of retries if a file lock is encountered.
|
||||
:param initial_delay: Initial delay (in seconds) before the first retry.
|
||||
"""
|
||||
if self.dry_run:
|
||||
return
|
||||
try:
|
||||
with open(str(filename), "w", encoding=self.encoding) as f:
|
||||
f.write(content)
|
||||
except OSError as err:
|
||||
self.tool_error(f"Unable to write file {filename}: {err}")
|
||||
|
||||
delay = initial_delay
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
with open(str(filename), "w", encoding=self.encoding) as f:
|
||||
f.write(content)
|
||||
return # Successfully wrote the file
|
||||
except PermissionError as err:
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(delay)
|
||||
delay *= 2 # Exponential backoff
|
||||
else:
|
||||
self.tool_error(
|
||||
f"Unable to write file {filename} after {max_retries} attempts: {err}"
|
||||
)
|
||||
raise
|
||||
except OSError as err:
|
||||
self.tool_error(f"Unable to write file {filename}: {err}")
|
||||
raise
|
||||
|
||||
def rule(self):
|
||||
if self.pretty:
|
||||
@@ -417,13 +441,38 @@ class InputOutput:
|
||||
self.tool_error(str(err))
|
||||
return ""
|
||||
|
||||
if line and line[0] == "{" and not multiline_input:
|
||||
multiline_input = True
|
||||
inp += line[1:] + "\n"
|
||||
if line.strip("\r\n") and not multiline_input:
|
||||
stripped = line.strip("\r\n")
|
||||
if stripped == "{":
|
||||
multiline_input = True
|
||||
multiline_tag = None
|
||||
inp += ""
|
||||
elif stripped[0] == "{":
|
||||
# Extract tag if it exists (only alphanumeric chars)
|
||||
tag = "".join(c for c in stripped[1:] if c.isalnum())
|
||||
if stripped == "{" + tag:
|
||||
multiline_input = True
|
||||
multiline_tag = tag
|
||||
inp += ""
|
||||
else:
|
||||
inp = line
|
||||
break
|
||||
else:
|
||||
inp = line
|
||||
break
|
||||
continue
|
||||
elif line and line[-1] == "}" and multiline_input:
|
||||
inp += line[:-1] + "\n"
|
||||
break
|
||||
elif multiline_input and line.strip():
|
||||
if multiline_tag:
|
||||
# Check if line is exactly "tag}"
|
||||
if line.strip("\r\n") == f"{multiline_tag}}}":
|
||||
break
|
||||
else:
|
||||
inp += line + "\n"
|
||||
# Check if line is exactly "}"
|
||||
elif line.strip("\r\n") == "}":
|
||||
break
|
||||
else:
|
||||
inp += line + "\n"
|
||||
elif multiline_input:
|
||||
inp += line + "\n"
|
||||
else:
|
||||
@@ -439,8 +488,8 @@ class InputOutput:
|
||||
return
|
||||
FileHistory(self.input_history_file).append_string(inp)
|
||||
# Also add to the in-memory history if it exists
|
||||
if hasattr(self, "session") and hasattr(self.session, "history"):
|
||||
self.session.history.append_string(inp)
|
||||
if self.prompt_session and self.prompt_session.history:
|
||||
self.prompt_session.history.append_string(inp)
|
||||
|
||||
def get_input_history(self):
|
||||
if not self.input_history_file:
|
||||
@@ -457,14 +506,17 @@ class InputOutput:
|
||||
log_file.write(f"{role.upper()} {timestamp}\n")
|
||||
log_file.write(content + "\n")
|
||||
|
||||
def display_user_input(self, inp):
|
||||
if self.pretty and self.user_input_color:
|
||||
style = dict(style=self.user_input_color)
|
||||
else:
|
||||
style = dict()
|
||||
|
||||
self.console.print(Text(inp), **style)
|
||||
|
||||
def user_input(self, inp, log_only=True):
|
||||
if not log_only:
|
||||
if self.pretty and self.user_input_color:
|
||||
style = dict(style=self.user_input_color)
|
||||
else:
|
||||
style = dict()
|
||||
|
||||
self.console.print(Text(inp), **style)
|
||||
self.display_user_input(inp)
|
||||
|
||||
prefix = "####"
|
||||
if inp:
|
||||
@@ -484,6 +536,15 @@ class InputOutput:
|
||||
hist = "\n" + content.strip() + "\n\n"
|
||||
self.append_chat_history(hist)
|
||||
|
||||
def offer_url(self, url, prompt="Open URL for more info?"):
|
||||
"""Offer to open a URL in the browser, returns True if opened."""
|
||||
if url in self.never_prompts:
|
||||
return False
|
||||
if self.confirm_ask(prompt, subject=url, allow_never=True):
|
||||
webbrowser.open(url)
|
||||
return True
|
||||
return False
|
||||
|
||||
def confirm_ask(
|
||||
self,
|
||||
question,
|
||||
@@ -722,13 +783,23 @@ class InputOutput:
|
||||
editable_files = [f for f in sorted(rel_fnames) if f not in rel_read_only_fnames]
|
||||
|
||||
if read_only_files:
|
||||
console.print("Read only files:", style="bold")
|
||||
console.print(Columns(read_only_files))
|
||||
files_with_label = ["Readonly:"] + read_only_files
|
||||
read_only_output = StringIO()
|
||||
Console(file=read_only_output, force_terminal=False).print(Columns(files_with_label))
|
||||
read_only_lines = read_only_output.getvalue().splitlines()
|
||||
console.print(Columns(files_with_label))
|
||||
|
||||
if editable_files:
|
||||
files_with_label = editable_files
|
||||
if read_only_files:
|
||||
console.print()
|
||||
console.print("Editable files:", style="bold")
|
||||
console.print(Columns(editable_files))
|
||||
files_with_label = ["Editable:"] + editable_files
|
||||
editable_output = StringIO()
|
||||
Console(file=editable_output, force_terminal=False).print(Columns(files_with_label))
|
||||
editable_lines = editable_output.getvalue().splitlines()
|
||||
|
||||
if len(read_only_lines) > 1 or len(editable_lines) > 1:
|
||||
console.print()
|
||||
console.print(Columns(files_with_label))
|
||||
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
108
aider/main.py
108
aider/main.py
@@ -88,15 +88,25 @@ def make_new_repo(git_root, io):
|
||||
|
||||
|
||||
def setup_git(git_root, io):
|
||||
try:
|
||||
cwd = Path.cwd()
|
||||
except OSError:
|
||||
cwd = None
|
||||
|
||||
repo = None
|
||||
|
||||
if git_root:
|
||||
repo = git.Repo(git_root)
|
||||
elif Path.cwd() == Path.home():
|
||||
try:
|
||||
repo = git.Repo(git_root)
|
||||
except ANY_GIT_ERROR:
|
||||
pass
|
||||
elif cwd == Path.home():
|
||||
io.tool_warning("You should probably run aider in a directory, not your home dir.")
|
||||
return
|
||||
elif io.confirm_ask("No git repo found, create one to track aider's changes (recommended)?"):
|
||||
git_root = str(Path.cwd().resolve())
|
||||
elif cwd and io.confirm_ask(
|
||||
"No git repo found, create one to track aider's changes (recommended)?"
|
||||
):
|
||||
git_root = str(cwd.resolve())
|
||||
repo = make_new_repo(git_root, io)
|
||||
|
||||
if not repo:
|
||||
@@ -323,14 +333,16 @@ def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"):
|
||||
|
||||
|
||||
def register_litellm_models(git_root, model_metadata_fname, io, verbose=False):
|
||||
model_metatdata_files = generate_search_path_list(
|
||||
".aider.model.metadata.json", git_root, model_metadata_fname
|
||||
)
|
||||
model_metatdata_files = []
|
||||
|
||||
# Add the resource file path
|
||||
resource_metadata = importlib_resources.files("aider.resources").joinpath("model-metadata.json")
|
||||
model_metatdata_files.append(str(resource_metadata))
|
||||
|
||||
model_metatdata_files += generate_search_path_list(
|
||||
".aider.model.metadata.json", git_root, model_metadata_fname
|
||||
)
|
||||
|
||||
try:
|
||||
model_metadata_files_loaded = models.register_litellm_models(model_metatdata_files)
|
||||
if len(model_metadata_files_loaded) > 0 and verbose:
|
||||
@@ -367,8 +379,7 @@ def sanity_check_repo(repo, io):
|
||||
io.tool_error("Aider only works with git repos with version number 1 or 2.")
|
||||
io.tool_output("You may be able to convert your repo: git update-index --index-version=2")
|
||||
io.tool_output("Or run aider --no-git to proceed without using git.")
|
||||
if io.confirm_ask("Open documentation url for more info?", subject=urls.git_index_version):
|
||||
webbrowser.open(urls.git_index_version)
|
||||
io.offer_url(urls.git_index_version, "Open documentation url for more info?")
|
||||
return False
|
||||
|
||||
io.tool_error("Unable to read git repository, it may be corrupt?")
|
||||
@@ -432,7 +443,6 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
if args.analytics_disable:
|
||||
analytics = Analytics(permanently_disable=True)
|
||||
print("Analytics have been permanently disabled.")
|
||||
return
|
||||
|
||||
if not args.verify_ssl:
|
||||
import httpx
|
||||
@@ -496,8 +506,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
io.tool_warning("Terminal does not support pretty output (UnicodeDecodeError)")
|
||||
|
||||
analytics = Analytics(logfile=args.analytics_log, permanently_disable=args.analytics_disable)
|
||||
if args.analytics:
|
||||
if analytics.need_to_ask():
|
||||
if args.analytics is not False:
|
||||
if analytics.need_to_ask(args.analytics):
|
||||
io.tool_output(
|
||||
"Aider respects your privacy and never collects your code, chat messages, keys or"
|
||||
" personal info."
|
||||
@@ -533,7 +543,14 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
|
||||
all_files = args.files + (args.file or [])
|
||||
fnames = [str(Path(fn).resolve()) for fn in all_files]
|
||||
read_only_fnames = [str(Path(fn).resolve()) for fn in (args.read or [])]
|
||||
read_only_fnames = []
|
||||
for fn in args.read or []:
|
||||
path = Path(fn).resolve()
|
||||
if path.is_dir():
|
||||
read_only_fnames.extend(str(f) for f in path.rglob("*") if f.is_file())
|
||||
else:
|
||||
read_only_fnames.append(str(path))
|
||||
|
||||
if len(all_files) > 1:
|
||||
good = True
|
||||
for fname in all_files:
|
||||
@@ -596,7 +613,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
cmd_line = scrub_sensitive_info(args, cmd_line)
|
||||
io.tool_output(cmd_line, log_only=True)
|
||||
|
||||
check_and_load_imports(io, verbose=args.verbose)
|
||||
is_first_run = is_first_run_of_new_version(io, verbose=args.verbose)
|
||||
check_and_load_imports(io, is_first_run, verbose=args.verbose)
|
||||
|
||||
if args.anthropic_api_key:
|
||||
os.environ["ANTHROPIC_API_KEY"] = args.anthropic_api_key
|
||||
@@ -642,10 +660,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
io.tool_output("You can skip this check with --no-show-model-warnings")
|
||||
|
||||
try:
|
||||
if io.confirm_ask(
|
||||
"Open documentation url for more info?", subject=urls.model_warnings
|
||||
):
|
||||
webbrowser.open(urls.model_warnings)
|
||||
io.offer_url(urls.model_warnings, "Open documentation url for more info?")
|
||||
io.tool_output()
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
@@ -674,7 +689,13 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
return 1
|
||||
|
||||
commands = Commands(
|
||||
io, None, verify_ssl=args.verify_ssl, args=args, parser=parser, verbose=args.verbose
|
||||
io,
|
||||
None,
|
||||
verify_ssl=args.verify_ssl,
|
||||
args=args,
|
||||
parser=parser,
|
||||
verbose=args.verbose,
|
||||
editor=args.editor,
|
||||
)
|
||||
|
||||
summarizer = ChatSummary(
|
||||
@@ -784,6 +805,18 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
|
||||
io.tool_output('Use /help <question> for help, run "aider --help" to see cmd line args')
|
||||
|
||||
show = False
|
||||
if args.show_release_notes is True:
|
||||
show = True
|
||||
elif args.show_release_notes is None and is_first_run:
|
||||
io.tool_output()
|
||||
show = io.confirm_ask("Would you like to see what's new in this version?")
|
||||
|
||||
if show:
|
||||
io.tool_output(f"Opening release notes: {urls.release_notes}")
|
||||
io.tool_output()
|
||||
webbrowser.open(urls.release_notes)
|
||||
|
||||
if git_root and Path.cwd().resolve() != Path(git_root).resolve():
|
||||
io.tool_warning(
|
||||
"Note: in-chat filenames are always relative to the git working dir, not the current"
|
||||
@@ -839,7 +872,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
coder.show_announcements()
|
||||
|
||||
|
||||
def check_and_load_imports(io, verbose=False):
|
||||
def is_first_run_of_new_version(io, verbose=False):
|
||||
"""Check if this is the first run of a new version/executable combination"""
|
||||
installs_file = Path.home() / ".aider" / "installs.json"
|
||||
key = (__version__, sys.executable)
|
||||
|
||||
@@ -860,7 +894,26 @@ def check_and_load_imports(io, verbose=False):
|
||||
if verbose:
|
||||
io.tool_output("Installs file does not exist, creating new dictionary")
|
||||
|
||||
if str(key) not in installs:
|
||||
is_first_run = str(key) not in installs
|
||||
|
||||
if is_first_run:
|
||||
installs[str(key)] = True
|
||||
installs_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(installs_file, "w") as f:
|
||||
json.dump(installs, f, indent=4)
|
||||
|
||||
return is_first_run
|
||||
|
||||
except Exception as e:
|
||||
io.tool_warning(f"Error checking version: {e}")
|
||||
if verbose:
|
||||
io.tool_output(f"Full exception details: {traceback.format_exc()}")
|
||||
return True # Safer to assume it's a first run if we hit an error
|
||||
|
||||
|
||||
def check_and_load_imports(io, is_first_run, verbose=False):
|
||||
try:
|
||||
if is_first_run:
|
||||
if verbose:
|
||||
io.tool_output(
|
||||
"First run for this version and executable, loading imports synchronously"
|
||||
@@ -870,17 +923,9 @@ def check_and_load_imports(io, verbose=False):
|
||||
except Exception as err:
|
||||
io.tool_error(str(err))
|
||||
io.tool_output("Error loading required imports. Did you install aider properly?")
|
||||
if io.confirm_ask(
|
||||
"Open documentation url for more info?", subject=urls.install_properly
|
||||
):
|
||||
webbrowser.open(urls.install_properly)
|
||||
|
||||
io.offer_url(urls.install_properly, "Open documentation url for more info?")
|
||||
sys.exit(1)
|
||||
|
||||
installs[str(key)] = True
|
||||
installs_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(installs_file, "w") as f:
|
||||
json.dump(installs, f, indent=4)
|
||||
if verbose:
|
||||
io.tool_output("Imports loaded and installs file updated")
|
||||
else:
|
||||
@@ -889,8 +934,9 @@ def check_and_load_imports(io, verbose=False):
|
||||
thread = threading.Thread(target=load_slow_imports)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
except Exception as e:
|
||||
io.tool_warning(f"Error in checking imports: {e}")
|
||||
io.tool_warning(f"Error in loading imports: {e}")
|
||||
if verbose:
|
||||
io.tool_output(f"Full exception details: {traceback.format_exc()}")
|
||||
|
||||
|
||||
214
aider/models.py
214
aider/models.py
@@ -161,6 +161,22 @@ MODEL_SETTINGS = [
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
ModelSettings(
|
||||
"gpt-4o-2024-11-20",
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
ModelSettings(
|
||||
"openai/gpt-4o-2024-11-20",
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
ModelSettings(
|
||||
"gpt-4o",
|
||||
"diff",
|
||||
@@ -234,24 +250,24 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"claude-3-opus-20240229",
|
||||
"diff",
|
||||
weak_model_name="claude-3-haiku-20240307",
|
||||
weak_model_name="claude-3-5-haiku-20241022",
|
||||
use_repo_map=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3-opus",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3-haiku",
|
||||
weak_model_name="openrouter/anthropic/claude-3-5-haiku",
|
||||
use_repo_map=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"claude-3-sonnet-20240229",
|
||||
"whole",
|
||||
weak_model_name="claude-3-haiku-20240307",
|
||||
weak_model_name="claude-3-5-haiku-20241022",
|
||||
),
|
||||
ModelSettings(
|
||||
"claude-3-5-sonnet-20240620",
|
||||
"diff",
|
||||
weak_model_name="claude-3-haiku-20240307",
|
||||
weak_model_name="claude-3-5-haiku-20241022",
|
||||
editor_model_name="claude-3-5-sonnet-20240620",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -268,7 +284,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"anthropic/claude-3-5-sonnet-20240620",
|
||||
"diff",
|
||||
weak_model_name="anthropic/claude-3-haiku-20240307",
|
||||
weak_model_name="anthropic/claude-3-5-haiku-20241022",
|
||||
editor_model_name="anthropic/claude-3-5-sonnet-20240620",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -285,7 +301,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"anthropic/claude-3-5-sonnet-20241022",
|
||||
"diff",
|
||||
weak_model_name="anthropic/claude-3-haiku-20240307",
|
||||
weak_model_name="anthropic/claude-3-5-haiku-20241022",
|
||||
editor_model_name="anthropic/claude-3-5-sonnet-20241022",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -299,10 +315,27 @@ MODEL_SETTINGS = [
|
||||
cache_control=True,
|
||||
reminder="user",
|
||||
),
|
||||
ModelSettings(
|
||||
"bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"diff",
|
||||
weak_model_name="bedrock/anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
editor_model_name="bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
examples_as_sys_msg=True,
|
||||
extra_params={
|
||||
"extra_headers": {
|
||||
"anthropic-beta": ANTHROPIC_BETA_HEADER,
|
||||
},
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
cache_control=True,
|
||||
reminder="user",
|
||||
),
|
||||
ModelSettings(
|
||||
"anthropic/claude-3-5-sonnet-latest",
|
||||
"diff",
|
||||
weak_model_name="anthropic/claude-3-haiku-20240307",
|
||||
weak_model_name="anthropic/claude-3-5-haiku-20241022",
|
||||
editor_model_name="anthropic/claude-3-5-sonnet-20241022",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -319,7 +352,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"claude-3-5-sonnet-20241022",
|
||||
"diff",
|
||||
weak_model_name="claude-3-haiku-20240307",
|
||||
weak_model_name="claude-3-5-haiku-20241022",
|
||||
editor_model_name="claude-3-5-sonnet-20241022",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -349,6 +382,19 @@ MODEL_SETTINGS = [
|
||||
"anthropic/claude-3-5-haiku-20241022",
|
||||
"diff",
|
||||
weak_model_name="anthropic/claude-3-5-haiku-20241022",
|
||||
use_repo_map=True,
|
||||
extra_params={
|
||||
"extra_headers": {
|
||||
"anthropic-beta": ANTHROPIC_BETA_HEADER,
|
||||
},
|
||||
},
|
||||
cache_control=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"bedrock/anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
"diff",
|
||||
weak_model_name="bedrock/anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
use_repo_map=True,
|
||||
extra_params={
|
||||
"extra_headers": {
|
||||
"anthropic-beta": ANTHROPIC_BETA_HEADER,
|
||||
@@ -360,6 +406,7 @@ MODEL_SETTINGS = [
|
||||
"claude-3-5-haiku-20241022",
|
||||
"diff",
|
||||
weak_model_name="claude-3-5-haiku-20241022",
|
||||
use_repo_map=True,
|
||||
examples_as_sys_msg=True,
|
||||
extra_params={
|
||||
"extra_headers": {
|
||||
@@ -372,28 +419,11 @@ MODEL_SETTINGS = [
|
||||
"vertex_ai/claude-3-5-haiku@20241022",
|
||||
"diff",
|
||||
weak_model_name="vertex_ai/claude-3-5-haiku@20241022",
|
||||
use_repo_map=True,
|
||||
extra_params={
|
||||
"max_tokens": 4096,
|
||||
},
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3.5-haiku",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3.5-haiku",
|
||||
extra_params={
|
||||
"max_tokens": 4096,
|
||||
},
|
||||
cache_control=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3.5-haiku:beta",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3.5-haiku:beta",
|
||||
extra_params={
|
||||
"max_tokens": 4096,
|
||||
},
|
||||
cache_control=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"claude-3-haiku-20240307",
|
||||
"whole",
|
||||
@@ -409,7 +439,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3.5-sonnet",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3-haiku",
|
||||
weak_model_name="openrouter/anthropic/claude-3-5-haiku",
|
||||
editor_model_name="openrouter/anthropic/claude-3.5-sonnet",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -423,7 +453,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3.5-sonnet:beta",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3-haiku:beta",
|
||||
weak_model_name="openrouter/anthropic/claude-3-5-haiku:beta",
|
||||
editor_model_name="openrouter/anthropic/claude-3.5-sonnet:beta",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -439,7 +469,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"vertex_ai/claude-3-5-sonnet@20240620",
|
||||
"diff",
|
||||
weak_model_name="vertex_ai/claude-3-haiku@20240307",
|
||||
weak_model_name="vertex_ai/claude-3-5-haiku@20241022",
|
||||
editor_model_name="vertex_ai/claude-3-5-sonnet@20240620",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -452,7 +482,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"vertex_ai/claude-3-5-sonnet-v2@20241022",
|
||||
"diff",
|
||||
weak_model_name="vertex_ai/claude-3-haiku@20240307",
|
||||
weak_model_name="vertex_ai/claude-3-5-haiku@20241022",
|
||||
editor_model_name="vertex_ai/claude-3-5-sonnet-v2@20241022",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
@@ -465,13 +495,13 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"vertex_ai/claude-3-opus@20240229",
|
||||
"diff",
|
||||
weak_model_name="vertex_ai/claude-3-haiku@20240307",
|
||||
weak_model_name="vertex_ai/claude-3-5-haiku@20241022",
|
||||
use_repo_map=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"vertex_ai/claude-3-sonnet@20240229",
|
||||
"whole",
|
||||
weak_model_name="vertex_ai/claude-3-haiku@20240307",
|
||||
weak_model_name="vertex_ai/claude-3-5-haiku@20241022",
|
||||
),
|
||||
# Cohere
|
||||
ModelSettings(
|
||||
@@ -536,6 +566,11 @@ MODEL_SETTINGS = [
|
||||
"diff-fenced",
|
||||
use_repo_map=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"vertex_ai/gemini-pro-experimental",
|
||||
"diff-fenced",
|
||||
use_repo_map=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"gemini/gemini-1.5-flash-exp-0827",
|
||||
"whole",
|
||||
@@ -610,7 +645,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"azure/o1-mini",
|
||||
@@ -622,7 +656,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"o1-mini",
|
||||
@@ -634,7 +667,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openai/o1-preview",
|
||||
@@ -646,7 +678,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"azure/o1-preview",
|
||||
@@ -658,7 +689,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"o1-preview",
|
||||
@@ -670,7 +700,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/openai/o1-mini",
|
||||
@@ -682,7 +711,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/openai/o1-preview",
|
||||
@@ -694,7 +722,14 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/qwen/qwen-2.5-coder-32b-instruct",
|
||||
"diff",
|
||||
weak_model_name="openrouter/qwen/qwen-2.5-coder-32b-instruct",
|
||||
editor_model_name="openrouter/qwen/qwen-2.5-coder-32b-instruct",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -735,6 +770,11 @@ class ModelInfoManager:
|
||||
pass
|
||||
except Exception as ex:
|
||||
print(str(ex))
|
||||
try:
|
||||
# Save empty dict to cache file on failure
|
||||
self.cache_file.write_text("{}")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def get_model_from_cached_json_db(self, model):
|
||||
if not self.content:
|
||||
@@ -756,16 +796,20 @@ class ModelInfoManager:
|
||||
return dict()
|
||||
|
||||
def get_model_info(self, model):
|
||||
if not litellm._lazy_module:
|
||||
info = self.get_model_from_cached_json_db(model)
|
||||
if info:
|
||||
return info
|
||||
cached_info = self.get_model_from_cached_json_db(model)
|
||||
|
||||
# If all else fails, do it the slow way...
|
||||
try:
|
||||
return litellm.get_model_info(model)
|
||||
except Exception:
|
||||
return dict()
|
||||
litellm_info = None
|
||||
if litellm._lazy_module or not cached_info:
|
||||
try:
|
||||
litellm_info = litellm.get_model_info(model)
|
||||
except Exception as ex:
|
||||
if "model_prices_and_context_window.json" not in str(ex):
|
||||
print(str(ex))
|
||||
|
||||
if litellm_info:
|
||||
return litellm_info
|
||||
|
||||
return cached_info
|
||||
|
||||
|
||||
model_info_manager = ModelInfoManager()
|
||||
@@ -778,6 +822,11 @@ class Model(ModelSettings):
|
||||
self.weak_model = None
|
||||
self.editor_model = None
|
||||
|
||||
# Find the extra settings
|
||||
self.extra_model_settings = next(
|
||||
(ms for ms in MODEL_SETTINGS if ms.name == "aider/extra_params"), None
|
||||
)
|
||||
|
||||
self.info = self.get_model_info(model)
|
||||
|
||||
# Are all needed keys/params available?
|
||||
@@ -805,17 +854,44 @@ class Model(ModelSettings):
|
||||
def get_model_info(self, model):
|
||||
return model_info_manager.get_model_info(model)
|
||||
|
||||
def _copy_fields(self, source):
|
||||
"""Helper to copy fields from a ModelSettings instance to self"""
|
||||
for field in fields(ModelSettings):
|
||||
val = getattr(source, field.name)
|
||||
setattr(self, field.name, val)
|
||||
|
||||
def configure_model_settings(self, model):
|
||||
# Look for exact model match
|
||||
exact_match = False
|
||||
for ms in MODEL_SETTINGS:
|
||||
# direct match, or match "provider/<model>"
|
||||
if model == ms.name:
|
||||
for field in fields(ModelSettings):
|
||||
val = getattr(ms, field.name)
|
||||
setattr(self, field.name, val)
|
||||
return # <--
|
||||
self._copy_fields(ms)
|
||||
exact_match = True
|
||||
break # Continue to apply overrides
|
||||
|
||||
model = model.lower()
|
||||
|
||||
# If no exact match, try generic settings
|
||||
if not exact_match:
|
||||
self.apply_generic_model_settings(model)
|
||||
|
||||
# Apply override settings last if they exist
|
||||
if self.extra_model_settings and self.extra_model_settings.extra_params:
|
||||
# Initialize extra_params if it doesn't exist
|
||||
if not self.extra_params:
|
||||
self.extra_params = {}
|
||||
|
||||
# Deep merge the extra_params dicts
|
||||
for key, value in self.extra_model_settings.extra_params.items():
|
||||
if isinstance(value, dict) and isinstance(self.extra_params.get(key), dict):
|
||||
# For nested dicts, merge recursively
|
||||
self.extra_params[key] = {**self.extra_params[key], **value}
|
||||
else:
|
||||
# For non-dict values, simply update
|
||||
self.extra_params[key] = value
|
||||
|
||||
def apply_generic_model_settings(self, model):
|
||||
if ("llama3" in model or "llama-3" in model) and "70b" in model:
|
||||
self.edit_format = "diff"
|
||||
self.use_repo_map = True
|
||||
@@ -837,21 +913,36 @@ class Model(ModelSettings):
|
||||
|
||||
if "gpt-3.5" in model or "gpt-4" in model:
|
||||
self.reminder = "sys"
|
||||
return # <--
|
||||
|
||||
if "3.5-sonnet" in model or "3-5-sonnet" in model:
|
||||
self.edit_format = "diff"
|
||||
self.use_repo_map = True
|
||||
self.examples_as_sys_msg = True
|
||||
self.reminder = "user"
|
||||
return # <--
|
||||
|
||||
if model.startswith("o1-") or "/o1-" in model:
|
||||
self.use_system_prompt = False
|
||||
self.use_temperature = False
|
||||
self.streaming = False
|
||||
return # <--
|
||||
|
||||
if (
|
||||
"qwen" in model
|
||||
and "coder" in model
|
||||
and ("2.5" in model or "2-5" in model)
|
||||
and "32b" in model
|
||||
):
|
||||
"openrouter/qwen/qwen-2.5-coder-32b-instruct",
|
||||
self.edit_format = "diff"
|
||||
self.editor_edit_format = "editor-diff"
|
||||
self.use_repo_map = True
|
||||
return # <--
|
||||
|
||||
# use the defaults
|
||||
if self.edit_format == "diff":
|
||||
self.use_repo_map = True
|
||||
return # <--
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -1035,8 +1126,14 @@ def register_litellm_models(model_fnames):
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(model_fname, "r") as model_def_file:
|
||||
model_def = json5.load(model_def_file)
|
||||
data = Path(model_fname).read_text()
|
||||
if not data.strip():
|
||||
continue
|
||||
model_def = json5.loads(data)
|
||||
if not model_def:
|
||||
continue
|
||||
|
||||
# only load litellm if we have actual data
|
||||
litellm._load_litellm()
|
||||
litellm.register_model(model_def)
|
||||
except Exception as e:
|
||||
@@ -1119,7 +1216,10 @@ def fuzzy_match_models(name):
|
||||
model = model.lower()
|
||||
if attrs.get("mode") != "chat":
|
||||
continue
|
||||
provider = (attrs["litellm_provider"] + "/").lower()
|
||||
provider = attrs.get("litellm_provider", "").lower()
|
||||
if not provider:
|
||||
continue
|
||||
provider += "/"
|
||||
|
||||
if model.startswith(provider):
|
||||
fq_model = model
|
||||
|
||||
@@ -169,7 +169,7 @@ class GitRepo:
|
||||
def get_rel_repo_dir(self):
|
||||
try:
|
||||
return os.path.relpath(self.repo.git_dir, os.getcwd())
|
||||
except ValueError:
|
||||
except (ValueError, OSError):
|
||||
return self.repo.git_dir
|
||||
|
||||
def get_commit_message(self, diffs, context):
|
||||
@@ -331,6 +331,15 @@ class GitRepo:
|
||||
lines,
|
||||
)
|
||||
|
||||
def git_ignored_file(self, path):
|
||||
if not self.repo:
|
||||
return
|
||||
try:
|
||||
if self.repo.ignored(path):
|
||||
return True
|
||||
except ANY_GIT_ERROR:
|
||||
return False
|
||||
|
||||
def ignored_file(self, fname):
|
||||
self.refresh_aider_ignore()
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from tree_sitter_languages import get_language, get_parser # noqa: E402
|
||||
Tag = namedtuple("Tag", "rel_fname fname line name kind".split())
|
||||
|
||||
|
||||
SQLITE_ERRORS = (sqlite3.OperationalError, sqlite3.DatabaseError)
|
||||
SQLITE_ERRORS = (sqlite3.OperationalError, sqlite3.DatabaseError, OSError)
|
||||
|
||||
|
||||
class RepoMap:
|
||||
@@ -197,7 +197,7 @@ class RepoMap:
|
||||
self.TAGS_CACHE = new_cache
|
||||
return
|
||||
|
||||
except (SQLITE_ERRORS, OSError) as e:
|
||||
except SQLITE_ERRORS as e:
|
||||
# If anything goes wrong, warn and fall back to dict
|
||||
self.io.tool_warning(
|
||||
f"Unable to use tags cache at {path}, falling back to memory cache"
|
||||
@@ -368,6 +368,8 @@ class RepoMap:
|
||||
showing_bar = False
|
||||
|
||||
for fname in fnames:
|
||||
if self.verbose:
|
||||
self.io.tool_output(f"Processing {fname}")
|
||||
if progress and not showing_bar:
|
||||
progress()
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"claude-3-5-haiku-20241022": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "anthropic",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"tool_use_system_prompt_tokens": 264,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_prompt_caching": true
|
||||
},
|
||||
"vertex_ai/claude-3-5-haiku@20241022": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "vertex_ai-anthropic_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_assistant_prefill": true
|
||||
},
|
||||
"openrouter/anthropic/claude-3-5-haiku": {
|
||||
"max_tokens": 200000,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "openrouter",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
},
|
||||
"openrouter/anthropic/claude-3-5-haiku-20241022": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "openrouter",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"tool_use_system_prompt_tokens": 264
|
||||
},
|
||||
"anthropic.claude-3-5-haiku-20241022-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
},
|
||||
"us.anthropic.claude-3-5-haiku-20241022-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
},
|
||||
"eu.anthropic.claude-3-5-haiku-20241022-v1:0": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000005,
|
||||
"litellm_provider": "bedrock",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ import hashlib
|
||||
import json
|
||||
import time
|
||||
|
||||
import backoff
|
||||
|
||||
from aider.dump import dump # noqa: F401
|
||||
from aider.exceptions import LiteLLMExceptions
|
||||
from aider.llm import litellm
|
||||
|
||||
# from diskcache import Cache
|
||||
@@ -17,52 +16,6 @@ CACHE = None
|
||||
RETRY_TIMEOUT = 60
|
||||
|
||||
|
||||
def retry_exceptions():
|
||||
import httpx
|
||||
import openai
|
||||
|
||||
return (
|
||||
# httpx
|
||||
httpx.ConnectError,
|
||||
httpx.RemoteProtocolError,
|
||||
httpx.ReadTimeout,
|
||||
#
|
||||
# litellm exceptions inherit from openai exceptions
|
||||
# https://docs.litellm.ai/docs/exception_mapping
|
||||
#
|
||||
# openai.BadRequestError,
|
||||
# litellm.ContextWindowExceededError,
|
||||
# litellm.ContentPolicyViolationError,
|
||||
#
|
||||
# openai.AuthenticationError,
|
||||
# openai.PermissionDeniedError,
|
||||
# openai.NotFoundError,
|
||||
#
|
||||
openai.APITimeoutError,
|
||||
openai.UnprocessableEntityError,
|
||||
openai.RateLimitError,
|
||||
openai.APIConnectionError,
|
||||
# openai.APIError,
|
||||
# openai.APIStatusError,
|
||||
openai.InternalServerError,
|
||||
)
|
||||
|
||||
|
||||
def lazy_litellm_retry_decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
decorated_func = backoff.on_exception(
|
||||
backoff.expo,
|
||||
retry_exceptions(),
|
||||
max_time=RETRY_TIMEOUT,
|
||||
on_backoff=lambda details: print(
|
||||
f"{details.get('exception', 'Exception')}\nRetry in {details['wait']:.1f} seconds."
|
||||
),
|
||||
)(func)
|
||||
return decorated_func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def send_completion(
|
||||
model_name,
|
||||
messages,
|
||||
@@ -104,6 +57,8 @@ def send_completion(
|
||||
|
||||
|
||||
def simple_send_with_retries(model_name, messages, extra_params=None):
|
||||
litellm_ex = LiteLLMExceptions()
|
||||
|
||||
retry_delay = 0.125
|
||||
while True:
|
||||
try:
|
||||
@@ -116,14 +71,27 @@ def simple_send_with_retries(model_name, messages, extra_params=None):
|
||||
}
|
||||
|
||||
_hash, response = send_completion(**kwargs)
|
||||
if not response or not hasattr(response, "choices") or not response.choices:
|
||||
return None
|
||||
return response.choices[0].message.content
|
||||
except retry_exceptions() as err:
|
||||
except litellm_ex.exceptions_tuple() as err:
|
||||
ex_info = litellm_ex.get_ex_info(err)
|
||||
|
||||
print(str(err))
|
||||
retry_delay *= 2
|
||||
if retry_delay > RETRY_TIMEOUT:
|
||||
break
|
||||
if ex_info.description:
|
||||
print(ex_info.description)
|
||||
|
||||
should_retry = ex_info.retry
|
||||
if should_retry:
|
||||
retry_delay *= 2
|
||||
if retry_delay > RETRY_TIMEOUT:
|
||||
should_retry = False
|
||||
|
||||
if not should_retry:
|
||||
return None
|
||||
|
||||
print(f"Retrying in {retry_delay:.1f} seconds...")
|
||||
time.sleep(retry_delay)
|
||||
continue
|
||||
except AttributeError:
|
||||
return
|
||||
return None
|
||||
|
||||
@@ -12,3 +12,4 @@ github_issues = "https://github.com/Aider-AI/aider/issues/new"
|
||||
git_index_version = "https://github.com/Aider-AI/aider/issues/211"
|
||||
install_properly = "https://aider.chat/docs/troubleshooting/imports.html"
|
||||
analytics = "https://aider.chat/docs/more/analytics.html"
|
||||
release_notes = "https://aider.chat/HISTORY.html#release-notes"
|
||||
|
||||
@@ -10,6 +10,12 @@ description: Release notes and stats on aider writing its own code.
|
||||
|
||||
{% include blame.md %}
|
||||
|
||||
The above
|
||||
[stats are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed)
|
||||
of the aider repo.
|
||||
|
||||
## Release notes
|
||||
|
||||
<!--[[[cog
|
||||
# This page is a copy of HISTORY.md, adding the front matter above.
|
||||
text = open("HISTORY.md").read()
|
||||
@@ -19,6 +25,48 @@ cog.out(text)
|
||||
|
||||
|
||||
|
||||
### main branch
|
||||
|
||||
- Added [`/editor` command](https://aider.chat/docs/usage/commands.html) to open system editor for writing prompts, by @thehunmonkgroup.
|
||||
- Full support for `gpt-4o-2024-11-20`.
|
||||
- Stream o1 models by default.
|
||||
- `/run` and suggested shell commands are less mysterious and now confirm that they "Added XX lines of output to the chat."
|
||||
- Ask 1% of users if they want to opt-in to [analytics](https://aider.chat/docs/more/analytics.html).
|
||||
- Added support for [optional multiline input tags](https://aider.chat/docs/usage/commands.html#entering-multi-line-chat-messages) with matching closing tags.
|
||||
- Improved [model settings configuration](https://aider.chat/docs/config/adv-model-settings.html#global-extra-params) with support for global `extra_params` for `litellm.completion()`.
|
||||
- Architect mode now asks to add files suggested by the LLM.
|
||||
- Fixed bug in fuzzy model name matching.
|
||||
- Added Timeout exception to handle API provider timeouts.
|
||||
- Added `--show-release-notes` to control release notes display on first run of new version.
|
||||
- Save empty dict to cache file on model metadata download failure, to delay retry.
|
||||
- Improved error handling and code formatting.
|
||||
- Aider wrote 74% of the code in this release.
|
||||
|
||||
### Aider v0.63.2
|
||||
|
||||
- Fixed bug in fuzzy model name matching when litellm provider info is missing.
|
||||
- Modified model metadata file loading to allow override of resource file.
|
||||
- Allow recursive loading of dirs using `--read`.
|
||||
- Updated dependency versions to pick up litellm fix for ollama models.
|
||||
- Added exponential backoff retry when writing files to handle editor file locks.
|
||||
- Updated Qwen 2.5 Coder 32B model configuration.
|
||||
|
||||
### Aider v0.63.1
|
||||
|
||||
- Fixed bug in git ignored file handling.
|
||||
- Improved error handling for git operations.
|
||||
|
||||
### Aider v0.63.0
|
||||
|
||||
- Support for Qwen 2.5 Coder 32B.
|
||||
- `/web` command just adds the page to the chat, without triggering an LLM response.
|
||||
- Improved prompting for the user's preferred chat language.
|
||||
- Improved handling of LiteLLM exceptions.
|
||||
- Bugfix for double-counting tokens when reporting cache stats.
|
||||
- Bugfix for the LLM creating new files.
|
||||
- Other small bug fixes.
|
||||
- Aider wrote 55% of the code in this release.
|
||||
|
||||
### Aider v0.62.0
|
||||
|
||||
- Full support for Claude 3.5 Haiku
|
||||
@@ -26,6 +74,8 @@ cog.out(text)
|
||||
- Almost as good as Sonnet at much lower cost.
|
||||
- Launch with `--haiku` to use it.
|
||||
- Easily apply file edits from ChatGPT, Claude or other web apps
|
||||
- Chat with ChatGPT or Claude via their web app.
|
||||
- Give it your source files and ask for the changes you want.
|
||||
- Use the web app's "copy response" button to copy the entire reply from the LLM.
|
||||
- Run `aider --apply-clipboard-edits file-to-edit.js`.
|
||||
- Aider will edit your file with the LLM's changes.
|
||||
|
||||
@@ -1801,8 +1801,8 @@
|
||||
Paul Gauthier (aider): 113
|
||||
start_tag: v0.44.0
|
||||
total_lines: 266
|
||||
- aider_percentage: 47.04
|
||||
aider_total: 254
|
||||
- aider_percentage: 53.3
|
||||
aider_total: 339
|
||||
end_date: '2024-07-29'
|
||||
end_tag: v0.46.0
|
||||
file_counts:
|
||||
@@ -1853,6 +1853,9 @@
|
||||
aider/scrape.py:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 32
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 11
|
||||
Paul Gauthier (aider): 85
|
||||
benchmark/Dockerfile:
|
||||
Your Name: 1
|
||||
tests/basic/test_coder.py:
|
||||
@@ -1868,13 +1871,13 @@
|
||||
Paul Gauthier (aider): 73
|
||||
grand_total:
|
||||
Charles Joachim: 4
|
||||
Paul Gauthier: 209
|
||||
Paul Gauthier (aider): 204
|
||||
Paul Gauthier: 220
|
||||
Paul Gauthier (aider): 289
|
||||
Your Name: 73
|
||||
Your Name (aider): 50
|
||||
start_tag: v0.45.0
|
||||
total_lines: 540
|
||||
- aider_percentage: 59.12
|
||||
total_lines: 636
|
||||
- aider_percentage: 57.16
|
||||
aider_total: 415
|
||||
end_date: '2024-07-31'
|
||||
end_tag: v0.47.0
|
||||
@@ -1920,6 +1923,8 @@
|
||||
Paul Gauthier (aider): 2
|
||||
aider/utils.py:
|
||||
Paul Gauthier: 7
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 24
|
||||
docker/Dockerfile:
|
||||
Paul Gauthier: 19
|
||||
Paul Gauthier (aider): 21
|
||||
@@ -1944,10 +1949,10 @@
|
||||
tests/basic/test_repomap.py:
|
||||
Paul Gauthier: 1
|
||||
grand_total:
|
||||
Paul Gauthier: 287
|
||||
Paul Gauthier: 311
|
||||
Paul Gauthier (aider): 415
|
||||
start_tag: v0.46.0
|
||||
total_lines: 702
|
||||
total_lines: 726
|
||||
- aider_percentage: 44.44
|
||||
aider_total: 276
|
||||
end_date: '2024-08-06'
|
||||
@@ -2011,8 +2016,8 @@
|
||||
paul-gauthier: 1
|
||||
start_tag: v0.47.0
|
||||
total_lines: 621
|
||||
- aider_percentage: 61.52
|
||||
aider_total: 478
|
||||
- aider_percentage: 61.2
|
||||
aider_total: 489
|
||||
end_date: '2024-08-10'
|
||||
end_tag: v0.49.0
|
||||
file_counts:
|
||||
@@ -2055,6 +2060,9 @@
|
||||
aider/versioncheck.py:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 11
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 11
|
||||
Paul Gauthier (aider): 11
|
||||
docker/Dockerfile:
|
||||
Paul Gauthier: 5
|
||||
Paul Gauthier (aider): 2
|
||||
@@ -2075,10 +2083,10 @@
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 49
|
||||
grand_total:
|
||||
Paul Gauthier: 299
|
||||
Paul Gauthier (aider): 478
|
||||
Paul Gauthier: 310
|
||||
Paul Gauthier (aider): 489
|
||||
start_tag: v0.48.0
|
||||
total_lines: 777
|
||||
total_lines: 799
|
||||
- aider_percentage: 66.05
|
||||
aider_total: 214
|
||||
end_date: '2024-08-13'
|
||||
@@ -2135,7 +2143,7 @@
|
||||
Paul Gauthier (aider): 201
|
||||
start_tag: v0.49.0
|
||||
total_lines: 324
|
||||
- aider_percentage: 56.25
|
||||
- aider_percentage: 56.18
|
||||
aider_total: 450
|
||||
end_date: '2024-08-20'
|
||||
end_tag: v0.51.0
|
||||
@@ -2170,6 +2178,8 @@
|
||||
Paul Gauthier: 3
|
||||
aider/utils.py:
|
||||
Paul Gauthier (aider): 6
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 7
|
||||
benchmark/over_time.py:
|
||||
@@ -2188,11 +2198,11 @@
|
||||
Paul Gauthier: 15
|
||||
Paul Gauthier (aider): 104
|
||||
grand_total:
|
||||
Paul Gauthier: 350
|
||||
Paul Gauthier: 351
|
||||
Paul Gauthier (aider): 450
|
||||
start_tag: v0.50.0
|
||||
total_lines: 800
|
||||
- aider_percentage: 68.19
|
||||
total_lines: 801
|
||||
- aider_percentage: 68.1
|
||||
aider_total: 521
|
||||
end_date: '2024-08-23'
|
||||
end_tag: v0.52.0
|
||||
@@ -2231,6 +2241,8 @@
|
||||
Paul Gauthier (aider): 9
|
||||
aider/versioncheck.py:
|
||||
Paul Gauthier: 2
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 1
|
||||
scripts/blame.py:
|
||||
@@ -2250,11 +2262,11 @@
|
||||
tests/basic/test_wholefile.py:
|
||||
Paul Gauthier: 8
|
||||
grand_total:
|
||||
Paul Gauthier: 242
|
||||
Paul Gauthier: 243
|
||||
Paul Gauthier (aider): 521
|
||||
pcamp: 1
|
||||
start_tag: v0.51.0
|
||||
total_lines: 764
|
||||
total_lines: 765
|
||||
- aider_percentage: 58.61
|
||||
aider_total: 405
|
||||
end_date: '2024-08-27'
|
||||
@@ -2328,7 +2340,7 @@
|
||||
Paul Gauthier (aider): 405
|
||||
start_tag: v0.52.0
|
||||
total_lines: 691
|
||||
- aider_percentage: 63.95
|
||||
- aider_percentage: 63.75
|
||||
aider_total: 204
|
||||
end_date: '2024-08-28'
|
||||
end_tag: v0.54.0
|
||||
@@ -2364,6 +2376,8 @@
|
||||
aider/versioncheck.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 13
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
tests/basic/test_coder.py:
|
||||
Paul Gauthier: 36
|
||||
Paul Gauthier (aider): 27
|
||||
@@ -2376,11 +2390,11 @@
|
||||
Paul Gauthier: 1
|
||||
grand_total:
|
||||
Antti Kaihola: 4
|
||||
Paul Gauthier: 111
|
||||
Paul Gauthier: 112
|
||||
Paul Gauthier (aider): 204
|
||||
start_tag: v0.53.0
|
||||
total_lines: 319
|
||||
- aider_percentage: 52.9
|
||||
total_lines: 320
|
||||
- aider_percentage: 52.87
|
||||
aider_total: 811
|
||||
end_date: '2024-09-04'
|
||||
end_tag: v0.55.0
|
||||
@@ -2450,6 +2464,8 @@
|
||||
aider/voice.py:
|
||||
Paul Gauthier: 7
|
||||
Paul Gauthier (aider): 9
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
scripts/versionbump.py:
|
||||
Paul Gauthier: 9
|
||||
tests/basic/test_coder.py:
|
||||
@@ -2478,11 +2494,11 @@
|
||||
grand_total:
|
||||
Antti Kaihola: 12
|
||||
Nikolay Sedelnikov: 45
|
||||
Paul Gauthier: 665
|
||||
Paul Gauthier: 666
|
||||
Paul Gauthier (aider): 811
|
||||
start_tag: v0.54.0
|
||||
total_lines: 1533
|
||||
- aider_percentage: 55.6
|
||||
total_lines: 1534
|
||||
- aider_percentage: 55.4
|
||||
aider_total: 154
|
||||
end_date: '2024-09-09'
|
||||
end_tag: v0.56.0
|
||||
@@ -2517,6 +2533,8 @@
|
||||
aider/report.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 20
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 1
|
||||
tests/basic/test_linter.py:
|
||||
@@ -2526,13 +2544,13 @@
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 9
|
||||
grand_total:
|
||||
Paul Gauthier: 108
|
||||
Paul Gauthier: 109
|
||||
Paul Gauthier (aider): 154
|
||||
fry69: 15
|
||||
start_tag: v0.55.0
|
||||
total_lines: 277
|
||||
- aider_percentage: 69.98
|
||||
aider_total: 394
|
||||
total_lines: 278
|
||||
- aider_percentage: 70.36
|
||||
aider_total: 406
|
||||
end_date: '2024-09-21'
|
||||
end_tag: v0.57.0
|
||||
file_counts:
|
||||
@@ -2575,6 +2593,10 @@
|
||||
Paul Gauthier: 3
|
||||
aider/utils.py:
|
||||
Paul Gauthier: 2
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Anjor Kanekar: 1
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 12
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 4
|
||||
scripts/issues.py:
|
||||
@@ -2592,15 +2614,16 @@
|
||||
Paul Gauthier: 18
|
||||
Paul Gauthier (aider): 20
|
||||
grand_total:
|
||||
Anjor Kanekar: 1
|
||||
Christian Clauss: 2
|
||||
Jay Alammar: 1
|
||||
Jay Alammar (aider): 13
|
||||
Krazer: 33
|
||||
Paul Gauthier: 133
|
||||
Paul Gauthier (aider): 381
|
||||
Paul Gauthier: 134
|
||||
Paul Gauthier (aider): 393
|
||||
start_tag: v0.56.0
|
||||
total_lines: 563
|
||||
- aider_percentage: 53.45
|
||||
total_lines: 577
|
||||
- aider_percentage: 47.95
|
||||
aider_total: 712
|
||||
end_date: '2024-09-29'
|
||||
end_tag: v0.58.0
|
||||
@@ -2677,6 +2700,8 @@
|
||||
Mike Bailey: 17
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 10
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 153
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 25
|
||||
Paul Gauthier (aider): 29
|
||||
@@ -2705,14 +2730,14 @@
|
||||
grand_total:
|
||||
Jonathan Ellis: 2
|
||||
Mike Bailey: 18
|
||||
Paul Gauthier: 376
|
||||
Paul Gauthier: 529
|
||||
Paul Gauthier (aider): 712
|
||||
Stein Martin Hustad: 26
|
||||
fry69: 197
|
||||
rti: 1
|
||||
start_tag: v0.57.0
|
||||
total_lines: 1332
|
||||
- aider_percentage: 76.79
|
||||
total_lines: 1485
|
||||
- aider_percentage: 75.44
|
||||
aider_total: 172
|
||||
end_date: '2024-10-04'
|
||||
end_tag: v0.59.0
|
||||
@@ -2747,6 +2772,8 @@
|
||||
Paul Gauthier: 2
|
||||
aider/versioncheck.py:
|
||||
Paul Gauthier: 1
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 4
|
||||
scripts/issues.py:
|
||||
Paul Gauthier: 1
|
||||
scripts/update-docs.sh:
|
||||
@@ -2762,11 +2789,11 @@
|
||||
tests/help/test_help.py:
|
||||
Paul Gauthier: 1
|
||||
grand_total:
|
||||
Paul Gauthier: 52
|
||||
Paul Gauthier: 56
|
||||
Paul Gauthier (aider): 172
|
||||
start_tag: v0.58.0
|
||||
total_lines: 224
|
||||
- aider_percentage: 49.12
|
||||
total_lines: 228
|
||||
- aider_percentage: 48.95
|
||||
aider_total: 140
|
||||
end_date: '2024-10-22'
|
||||
end_tag: v0.60.0
|
||||
@@ -2804,6 +2831,8 @@
|
||||
Paul Gauthier: 3
|
||||
aider/sendchat.py:
|
||||
Paul Gauthier: 3
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
tests/basic/test_editblock.py:
|
||||
Paul Gauthier: 23
|
||||
tests/basic/test_main.py:
|
||||
@@ -2813,12 +2842,12 @@
|
||||
Paul Gauthier (aider): 46
|
||||
grand_total:
|
||||
Jonathan Ellis: 10
|
||||
Paul Gauthier: 93
|
||||
Paul Gauthier: 94
|
||||
Paul Gauthier (aider): 140
|
||||
Sven Grunewaldt: 24
|
||||
fry69: 18
|
||||
start_tag: v0.59.0
|
||||
total_lines: 285
|
||||
total_lines: 286
|
||||
- aider_percentage: 67.61
|
||||
aider_total: 860
|
||||
end_date: '2024-11-01'
|
||||
@@ -2895,3 +2924,92 @@
|
||||
kAIto47802: 4
|
||||
start_tag: v0.60.0
|
||||
total_lines: 1272
|
||||
- aider_percentage: 82.42
|
||||
aider_total: 75
|
||||
end_date: '2024-11-04'
|
||||
end_tag: v0.62.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/args.py:
|
||||
Paul Gauthier (aider): 14
|
||||
aider/coders/editblock_coder.py:
|
||||
Paul Gauthier: 6
|
||||
aider/main.py:
|
||||
Paul Gauthier (aider): 4
|
||||
aider/models.py:
|
||||
Paul Gauthier: 5
|
||||
Paul Gauthier (aider): 45
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 12
|
||||
grand_total:
|
||||
Paul Gauthier: 16
|
||||
Paul Gauthier (aider): 75
|
||||
start_tag: v0.61.0
|
||||
total_lines: 91
|
||||
- aider_percentage: 55.08
|
||||
aider_total: 385
|
||||
end_date: '2024-11-13'
|
||||
end_tag: v0.63.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/coders/architect_coder.py:
|
||||
Paul Gauthier: 3
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 42
|
||||
Paul Gauthier (aider): 1
|
||||
aider/coders/editblock_coder.py:
|
||||
Paul Gauthier: 4
|
||||
aider/commands.py:
|
||||
Paul Gauthier: 13
|
||||
aider/exceptions.py:
|
||||
Paul Gauthier: 72
|
||||
Paul Gauthier (aider): 4
|
||||
aider/io.py:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 23
|
||||
aider/main.py:
|
||||
Paul Gauthier: 9
|
||||
Paul Gauthier (aider): 9
|
||||
aider/models.py:
|
||||
Logan Attwood: 29
|
||||
Paul Gauthier: 50
|
||||
Paul Gauthier (aider): 7
|
||||
aider/repo.py:
|
||||
Paul Gauthier: 7
|
||||
aider/repomap.py:
|
||||
Paul Gauthier: 4
|
||||
aider/sendchat.py:
|
||||
Paul Gauthier: 17
|
||||
Paul Gauthier (aider): 4
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
scripts/issues.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 195
|
||||
tests/basic/test_coder.py:
|
||||
Paul Gauthier: 2
|
||||
tests/basic/test_commands.py:
|
||||
Paul Gauthier (aider): 20
|
||||
tests/basic/test_editblock.py:
|
||||
Paul Gauthier: 41
|
||||
tests/basic/test_exceptions.py:
|
||||
Paul Gauthier (aider): 65
|
||||
tests/basic/test_main.py:
|
||||
Paul Gauthier: 1
|
||||
tests/basic/test_sanity_check_repo.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 2
|
||||
tests/basic/test_sendchat.py:
|
||||
Paul Gauthier: 8
|
||||
Paul Gauthier (aider): 55
|
||||
tests/scrape/test_scrape.py:
|
||||
Paul Gauthier: 1
|
||||
grand_total:
|
||||
Logan Attwood: 29
|
||||
Paul Gauthier: 285
|
||||
Paul Gauthier (aider): 385
|
||||
start_tag: v0.62.0
|
||||
total_lines: 699
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
- dirname: 2024-05-01-20-05-59--direct-opus-filenames-outside-fence
|
||||
test_cases: 133
|
||||
model: claude-3-opus-20240229
|
||||
released: 2024-02-29
|
||||
_released: 2024-02-29
|
||||
edit_format: diff
|
||||
commit_hash: f4b1797-dirty, f4b1797
|
||||
pass_rate_1: 53.4
|
||||
@@ -20,7 +20,7 @@
|
||||
versions: 0.30.2-dev
|
||||
seconds_per_case: 32.4
|
||||
total_cost: 13.8395
|
||||
|
||||
|
||||
- dirname: 2024-03-06-16-42-00--claude3-sonnet-whole
|
||||
test_cases: 133
|
||||
model: claude-3-sonnet-20240229
|
||||
@@ -43,7 +43,7 @@
|
||||
versions: 0.25.1-dev
|
||||
seconds_per_case: 23.1
|
||||
total_cost: 0.0000
|
||||
|
||||
|
||||
- dirname: 2024-05-03-20-47-24--gemini-1.5-pro-diff-fenced
|
||||
test_cases: 133
|
||||
model: gemini-1.5-pro-latest
|
||||
@@ -88,7 +88,7 @@
|
||||
versions: 0.33.1-dev
|
||||
seconds_per_case: 6.5
|
||||
total_cost: 0.5032
|
||||
|
||||
|
||||
- dirname: 2023-11-06-21-23-59--gpt-3.5-turbo-0301
|
||||
test_cases: 133
|
||||
model: gpt-3.5-turbo-0301
|
||||
@@ -111,7 +111,7 @@
|
||||
versions: 0.16.4-dev
|
||||
seconds_per_case: 6.5
|
||||
total_cost: 0.4822
|
||||
|
||||
|
||||
- dirname: 2023-11-07-02-41-07--gpt-3.5-turbo-0613
|
||||
test_cases: 133
|
||||
model: gpt-3.5-turbo-0613
|
||||
@@ -155,7 +155,7 @@
|
||||
versions: 0.30.2-dev
|
||||
seconds_per_case: 5.3
|
||||
total_cost: 0.3261
|
||||
|
||||
|
||||
- dirname: 2024-01-25-23-37-15--jan-exercism-gpt-4-0125-preview-udiff
|
||||
test_cases: 133
|
||||
model: gpt-4-0125-preview
|
||||
@@ -178,7 +178,7 @@
|
||||
versions: 0.22.1-dev
|
||||
seconds_per_case: 44.8
|
||||
total_cost: 14.6428
|
||||
|
||||
|
||||
- dirname: 2024-05-04-15-07-30--redo-gpt-4-0314-diff-reminder-rules
|
||||
test_cases: 133
|
||||
model: gpt-4-0314
|
||||
@@ -201,7 +201,7 @@
|
||||
versions: 0.31.2-dev
|
||||
seconds_per_case: 19.8
|
||||
total_cost: 16.2689
|
||||
|
||||
|
||||
- dirname: 2023-12-16-21-24-28--editblock-gpt-4-0613-actual-main
|
||||
test_cases: 133
|
||||
model: gpt-4-0613
|
||||
@@ -228,7 +228,7 @@
|
||||
- dirname: 2024-05-08-21-16-03--may-gpt-4-1106-preview-udiff
|
||||
test_cases: 133
|
||||
model: gpt-4-1106-preview
|
||||
released: 2023-11-06
|
||||
released: 2023-11-06
|
||||
edit_format: udiff
|
||||
commit_hash: 87664dc
|
||||
pass_rate_1: 51.9
|
||||
@@ -247,7 +247,7 @@
|
||||
versions: 0.33.1-dev
|
||||
seconds_per_case: 20.4
|
||||
total_cost: 6.6061
|
||||
|
||||
|
||||
- dirname: 2024-05-01-02-09-20--gpt-4-turbo-examples
|
||||
test_cases: 133
|
||||
model: gpt-4-turbo-2024-04-09 (udiff)
|
||||
@@ -270,11 +270,11 @@
|
||||
versions: 0.30.2-dev
|
||||
seconds_per_case: 22.8
|
||||
total_cost: 6.3337
|
||||
|
||||
|
||||
- dirname: 2024-05-03-22-24-48--openrouter--llama3-diff-examples-sys-msg
|
||||
test_cases: 132
|
||||
model: llama3-70b-8192
|
||||
released: 2024-04-18
|
||||
_released: 2024-04-18
|
||||
edit_format: diff
|
||||
commit_hash: b5bb453
|
||||
pass_rate_1: 38.6
|
||||
@@ -293,11 +293,11 @@
|
||||
versions: 0.31.2-dev
|
||||
seconds_per_case: 14.5
|
||||
total_cost: 0.4311
|
||||
|
||||
|
||||
- dirname: 2024-05-06-18-31-08--command-r-plus-whole-final
|
||||
test_cases: 133
|
||||
model: command-r-plus
|
||||
released: 2024-04-04
|
||||
_released: 2024-04-04
|
||||
edit_format: whole
|
||||
commit_hash: fc3a43e-dirty
|
||||
pass_rate_1: 21.8
|
||||
@@ -316,11 +316,11 @@
|
||||
versions: 0.31.2-dev
|
||||
seconds_per_case: 22.9
|
||||
total_cost: 2.7494
|
||||
|
||||
|
||||
- dirname: 2024-05-07-20-32-37--qwen1.5-110b-chat-whole
|
||||
test_cases: 133
|
||||
model: qwen1.5-110b-chat
|
||||
released: 2024-02-04
|
||||
released: 2024-02-04
|
||||
edit_format: whole
|
||||
commit_hash: 70b1c0c
|
||||
pass_rate_1: 30.8
|
||||
@@ -339,7 +339,7 @@
|
||||
versions: 0.31.2-dev
|
||||
seconds_per_case: 46.9
|
||||
total_cost: 0.0000
|
||||
|
||||
|
||||
- dirname: 2024-05-07-20-57-04--wizardlm-2-8x22b-whole
|
||||
test_cases: 133
|
||||
model: WizardLM-2 8x22B
|
||||
@@ -384,7 +384,7 @@
|
||||
versions: 0.34.1-dev
|
||||
seconds_per_case: 6.0
|
||||
total_cost: 0.0000
|
||||
|
||||
|
||||
- dirname: 2024-04-12-22-18-20--gpt-4-turbo-2024-04-09-plain-diff
|
||||
test_cases: 33
|
||||
model: gpt-4-turbo-2024-04-09 (diff)
|
||||
@@ -568,7 +568,7 @@
|
||||
versions: 0.42.1-dev
|
||||
seconds_per_case: 17.6
|
||||
total_cost: 3.6346
|
||||
|
||||
|
||||
- dirname: 2024-07-01-21-41-48--haiku-whole
|
||||
test_cases: 133
|
||||
model: claude-3-haiku-20240307
|
||||
@@ -623,7 +623,7 @@
|
||||
commit_hash: d31eef3-dirty
|
||||
pass_rate_1: 40.6
|
||||
pass_rate_2: 55.6
|
||||
released: 2024-07-18
|
||||
_released: 2024-07-18
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 1
|
||||
num_malformed_responses: 0
|
||||
@@ -671,7 +671,7 @@
|
||||
commit_hash: f7ce78b-dirty
|
||||
pass_rate_1: 46.6
|
||||
pass_rate_2: 63.9
|
||||
released: 2024-07-23
|
||||
_released: 2024-07-23
|
||||
percent_cases_well_formed: 92.5
|
||||
error_outputs: 84
|
||||
num_malformed_responses: 19
|
||||
@@ -691,6 +691,7 @@
|
||||
- dirname: 2024-07-24-06-30-29--llama-405b-whole
|
||||
test_cases: 133
|
||||
model: llama-3.1-405b-instruct (whole)
|
||||
_released: 2024-07-23
|
||||
edit_format: whole
|
||||
commit_hash: a362dea-dirty
|
||||
pass_rate_1: 48.9
|
||||
@@ -698,7 +699,6 @@
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 0
|
||||
num_malformed_responses: 0
|
||||
released: 2024-07-23
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 0
|
||||
lazy_comments: 0
|
||||
@@ -770,7 +770,7 @@
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 27
|
||||
num_malformed_responses: 0
|
||||
released: 2024-07-23
|
||||
_released: 2024-07-23
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 23
|
||||
lazy_comments: 8
|
||||
@@ -796,7 +796,7 @@
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 0
|
||||
released: 2024-07-23
|
||||
_released: 2024-07-23
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
@@ -946,7 +946,7 @@
|
||||
versions: 0.54.13.dev
|
||||
seconds_per_case: 8.3
|
||||
total_cost: 0.0000
|
||||
released: 2024-09-04
|
||||
_released: 2024-09-04
|
||||
|
||||
- dirname: 2024-09-04-16-17-33--yi-coder-9b-chat-q4_0-whole
|
||||
test_cases: 133
|
||||
@@ -973,6 +973,7 @@
|
||||
|
||||
- dirname: 2024-09-05-14-50-11--deepseek-sep5-no-shell
|
||||
test_cases: 133
|
||||
released: 2024-09-05
|
||||
model: DeepSeek V2.5
|
||||
edit_format: diff
|
||||
commit_hash: 1279c86
|
||||
@@ -1112,6 +1113,7 @@
|
||||
- dirname: 2024-09-21-16-45-11--o1-preview-flex-sr-markers
|
||||
test_cases: 133
|
||||
model: o1-preview
|
||||
_released: 2024-09-12
|
||||
edit_format: diff
|
||||
commit_hash: 5493654-dirty
|
||||
pass_rate_1: 57.9
|
||||
@@ -1131,7 +1133,7 @@
|
||||
versions: 0.56.1.dev
|
||||
seconds_per_case: 80.9
|
||||
total_cost: 63.9190
|
||||
|
||||
|
||||
- dirname: 2024-09-19-16-58-29--qwen2.5-coder:7b-instruct-q8_0
|
||||
test_cases: 133
|
||||
model: qwen2.5-coder:7b-instruct-q8_0
|
||||
@@ -1154,7 +1156,7 @@
|
||||
versions: 0.56.0
|
||||
seconds_per_case: 9.3
|
||||
total_cost: 0.0000
|
||||
|
||||
|
||||
- dirname: 2024-09-20-20-20-19--qwen-2.5-72b-instruct-diff
|
||||
test_cases: 133
|
||||
model: qwen-2.5-72b-instruct (bf16)
|
||||
@@ -1458,7 +1460,7 @@
|
||||
versions: 0.58.1.dev
|
||||
seconds_per_case: 63.7
|
||||
total_cost: 0.0000
|
||||
|
||||
|
||||
- dirname: 2024-10-01-16-50-09--hermes3-whole-4
|
||||
test_cases: 133
|
||||
model: ollama/hermes3
|
||||
@@ -1477,6 +1479,7 @@
|
||||
- dirname: 2024-10-04-16-30-08--chatgpt-4o-latest-diff-oct4
|
||||
test_cases: 133
|
||||
model: openai/chatgpt-4o-latest
|
||||
released: 2024-10-04
|
||||
edit_format: diff
|
||||
commit_hash: af10953
|
||||
pass_rate_1: 56.4
|
||||
@@ -1592,6 +1595,7 @@
|
||||
- dirname: 2024-10-22-17-45-28--sonnet-1022-diff-fixed-model-settings
|
||||
test_cases: 133
|
||||
model: claude-3-5-sonnet-20241022
|
||||
released: 2024-10-22
|
||||
edit_format: diff
|
||||
commit_hash: 3b14eb9
|
||||
pass_rate_1: 69.2
|
||||
@@ -1615,6 +1619,7 @@
|
||||
- dirname: 2024-11-04-19-19-32--haiku35-diff-ex-as-sys-false
|
||||
test_cases: 133
|
||||
model: claude-3-5-haiku-20241022
|
||||
released: 2024-10-22
|
||||
edit_format: diff
|
||||
commit_hash: 03bbdb0-dirty
|
||||
pass_rate_1: 61.7
|
||||
@@ -1633,4 +1638,260 @@
|
||||
date: 2024-11-04
|
||||
versions: 0.61.1.dev
|
||||
seconds_per_case: 18.4
|
||||
total_cost: 0.0000
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-07-06-15-36--Qwen2.5.1-Coder-7B-Instruct-GGUF:Q8_0-32k-whole
|
||||
test_cases: 133
|
||||
model: ollama/Qwen2.5.1-Coder-7B-Instruct-GGUF:Q8_0-32k
|
||||
edit_format: whole
|
||||
commit_hash: e76704e
|
||||
pass_rate_1: 52.6
|
||||
pass_rate_2: 63.9
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 0
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 4
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 1
|
||||
command: aider --model ollama/Qwen2.5.1-Coder-7B-Instruct-GGUF:Q8_0-32k
|
||||
date: 2024-11-07
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 18.2
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-10-29-00-29-09--Qwen2.5-Coder-0.5B-Instruct
|
||||
test_cases: 133
|
||||
model: Qwen2.5-Coder-0.5B-Instruct
|
||||
edit_format: whole
|
||||
commit_hash: 58bd375
|
||||
pass_rate_1: 14.3
|
||||
pass_rate_2: 14.3
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 20
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 45
|
||||
lazy_comments: 0
|
||||
syntax_errors: 2
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 20
|
||||
test_timeouts: 2
|
||||
command: aider --model openai/Qwen2.5-Coder-0.5B-Instruct
|
||||
date: 2024-10-29
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 16.0
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-11-19-37-01--Qwen2.5-Coder-1.5B-Instruct
|
||||
test_cases: 133
|
||||
model: Qwen2.5-Coder-1.5B-Instruct
|
||||
edit_format: whole
|
||||
commit_hash: bb5681c
|
||||
pass_rate_1: 28.6
|
||||
pass_rate_2: 31.6
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 5
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 13
|
||||
lazy_comments: 2
|
||||
syntax_errors: 1
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 5
|
||||
test_timeouts: 2
|
||||
command: aider --model openai/Qwen2.5-Coder-1.5B-Instruct
|
||||
date: 2024-11-11
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 27.4
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-04-02-25-32--Qwen2.5-Coder-3B-Instruct
|
||||
test_cases: 133
|
||||
model: Qwen2.5-Coder-3B-Instruct
|
||||
edit_format: whole
|
||||
commit_hash: 0ba3647
|
||||
pass_rate_1: 33.8
|
||||
pass_rate_2: 39.1
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 4
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 3
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 4
|
||||
test_timeouts: 6
|
||||
command: aider --model openai/Qwen2.5-Coder-3B-Instruct
|
||||
date: 2024-11-04
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 18.7
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-10-16-16-20-59--Qwen2.5-Coder-7B-Instruct
|
||||
test_cases: 133
|
||||
model: Qwen2.5-Coder-7B-Instruct
|
||||
edit_format: whole
|
||||
commit_hash: 92fe979-dirty
|
||||
pass_rate_1: 51.9
|
||||
pass_rate_2: 57.9
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 2
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 2
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 2
|
||||
test_timeouts: 5
|
||||
command: aider --model openai/Qwen2.5-Coder-7B-Instruct
|
||||
date: 2024-10-16
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 10.5
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-10-29-11-53-39--Qwen2.5-Coder-14B-Instruct
|
||||
test_cases: 133
|
||||
model: Qwen2.5-Coder-14B-Instruct
|
||||
edit_format: whole
|
||||
commit_hash: 58bd375
|
||||
pass_rate_1: 58.6
|
||||
pass_rate_2: 69.2
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 3
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 2
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 3
|
||||
test_timeouts: 0
|
||||
command: aider --model openai/Qwen2.5-Coder-14B-Instruct
|
||||
date: 2024-10-29
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 18.3
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-09-11-09-15--Qwen2.5-Coder-32B-Instruct
|
||||
test_cases: 133
|
||||
model: Qwen2.5-Coder-32B-Instruct
|
||||
released: 2024-11-12
|
||||
edit_format: diff
|
||||
commit_hash: ec9982a
|
||||
pass_rate_1: 59.4
|
||||
pass_rate_2: 71.4
|
||||
percent_cases_well_formed: 94.7
|
||||
error_outputs: 17
|
||||
num_malformed_responses: 17
|
||||
num_with_malformed_responses: 7
|
||||
user_asks: 1
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 3
|
||||
command: aider --model openai/Qwen2.5-Coder-32B-Instruct
|
||||
date: 2024-11-09
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 22.5
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-20-14-57-11--mistral-2411-direct-diff
|
||||
test_cases: 133
|
||||
model: Mistral Large (2411)
|
||||
released: 2024-11-18
|
||||
edit_format: diff
|
||||
commit_hash: dba844c
|
||||
pass_rate_1: 46.6
|
||||
pass_rate_2: 65.4
|
||||
percent_cases_well_formed: 96.2
|
||||
error_outputs: 8
|
||||
num_malformed_responses: 8
|
||||
num_with_malformed_responses: 5
|
||||
user_asks: 5
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 1
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 1
|
||||
command: aider --model mistral/mistral-large-latest
|
||||
date: 2024-11-20
|
||||
versions: 0.63.3.dev
|
||||
seconds_per_case: 24.9
|
||||
total_cost: 3.2334
|
||||
|
||||
- dirname: 2024-11-20-19-28-30--gpt-4o-2024-11-20
|
||||
test_cases: 133
|
||||
model: gpt-4o-2024-11-20
|
||||
released: 2024-11-20
|
||||
edit_format: diff
|
||||
commit_hash: 2ac0776-dirty
|
||||
pass_rate_1: 58.6
|
||||
pass_rate_2: 71.4
|
||||
percent_cases_well_formed: 99.2
|
||||
error_outputs: 1
|
||||
num_malformed_responses: 1
|
||||
num_with_malformed_responses: 1
|
||||
user_asks: 4
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 5
|
||||
command: aider --model openai/gpt-4o-2024-11-20
|
||||
date: 2024-11-20
|
||||
versions: 0.63.3.dev
|
||||
seconds_per_case: 6.0
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-09-20-21-47-17--qwen2.5-32b-instruct-q8_0-whole
|
||||
test_cases: 133
|
||||
model: ollama/qwen2.5:32b-instruct-q8_0
|
||||
edit_format: whole
|
||||
commit_hash: 2753ac6
|
||||
pass_rate_1: 46.6
|
||||
pass_rate_2: 58.6
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 0
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 1
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 2
|
||||
command: aider --model ollama/qwen2.5:32b-instruct-q8_0
|
||||
date: 2024-09-20
|
||||
versions: 0.56.1.dev
|
||||
seconds_per_case: 1763.7
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-20-15-17-37--qwen25-32b-or-diff
|
||||
test_cases: 133
|
||||
model: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
edit_format: diff
|
||||
commit_hash: e917424
|
||||
pass_rate_1: 49.6
|
||||
pass_rate_2: 65.4
|
||||
percent_cases_well_formed: 84.2
|
||||
error_outputs: 43
|
||||
num_malformed_responses: 31
|
||||
num_with_malformed_responses: 21
|
||||
user_asks: 43
|
||||
lazy_comments: 0
|
||||
syntax_errors: 2
|
||||
indentation_errors: 2
|
||||
exhausted_context_windows: 12
|
||||
test_timeouts: 2
|
||||
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
date: 2024-11-20
|
||||
versions: 0.63.3.dev
|
||||
seconds_per_case: 40.7
|
||||
total_cost: 0.1497
|
||||
93
aider/website/_data/quant.yml
Normal file
93
aider/website/_data/quant.yml
Normal file
@@ -0,0 +1,93 @@
|
||||
- dirname: 2024-11-09-11-09-15--Qwen2.5-Coder-32B-Instruct
|
||||
test_cases: 133
|
||||
model: HuggingFace weights via glhf.chat
|
||||
released: 2024-11-12
|
||||
edit_format: diff
|
||||
commit_hash: ec9982a
|
||||
pass_rate_1: 59.4
|
||||
pass_rate_2: 71.4
|
||||
percent_cases_well_formed: 94.7
|
||||
error_outputs: 17
|
||||
num_malformed_responses: 17
|
||||
num_with_malformed_responses: 7
|
||||
user_asks: 1
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 3
|
||||
command: aider --model openai/Qwen2.5-Coder-32B-Instruct
|
||||
date: 2024-11-09
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 22.5
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-11-20-15-17-37--qwen25-32b-or-diff
|
||||
test_cases: 133
|
||||
model: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
edit_format: diff
|
||||
commit_hash: e917424
|
||||
pass_rate_1: 49.6
|
||||
pass_rate_2: 65.4
|
||||
percent_cases_well_formed: 84.2
|
||||
error_outputs: 43
|
||||
num_malformed_responses: 31
|
||||
num_with_malformed_responses: 21
|
||||
user_asks: 43
|
||||
lazy_comments: 0
|
||||
syntax_errors: 2
|
||||
indentation_errors: 2
|
||||
exhausted_context_windows: 12
|
||||
test_timeouts: 2
|
||||
command: aider --model openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
date: 2024-11-20
|
||||
versions: 0.63.3.dev
|
||||
seconds_per_case: 40.7
|
||||
total_cost: 0.1497
|
||||
|
||||
- dirname: 2024-09-20-21-47-17--qwen2.5-32b-instruct-q8_0-whole
|
||||
test_cases: 133
|
||||
model: ollama/qwen2.5:32b-instruct-q8_0
|
||||
edit_format: whole
|
||||
commit_hash: 2753ac6
|
||||
pass_rate_1: 46.6
|
||||
pass_rate_2: 58.6
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 0
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 1
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 2
|
||||
command: aider --model ollama/qwen2.5:32b-instruct-q8_0
|
||||
date: 2024-09-20
|
||||
versions: 0.56.1.dev
|
||||
seconds_per_case: 1763.7
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-09-30-14-09-43--qwen2.5-32b-whole-2
|
||||
test_cases: 133
|
||||
model: ollama/qwen2.5:32b
|
||||
edit_format: whole
|
||||
commit_hash: 765c4cb
|
||||
pass_rate_1: 44.4
|
||||
pass_rate_2: 54.1
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 0
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 9
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 3
|
||||
command: aider --model ollama/qwen2.5:32b
|
||||
date: 2024-09-30
|
||||
versions: 0.58.1.dev
|
||||
seconds_per_case: 134.9
|
||||
total_cost: 0.0000
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<canvas id="linesChart" width="800" height="360" style="margin-top: 20px"></canvas>
|
||||
<canvas id="blameChart" width="800" height="360" style="margin-top: 20px"></canvas>
|
||||
<canvas id="linesChart" width="800" height="360" style="margin-top: 20px"></canvas>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/moment"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment"></script>
|
||||
|
||||
97
aider/website/_includes/edit-leaderboard.js
Normal file
97
aider/website/_includes/edit-leaderboard.js
Normal file
@@ -0,0 +1,97 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var ctx = document.getElementById('editChart').getContext('2d');
|
||||
const HIGHTLIGHT_MODEL = 'no no no no';
|
||||
var leaderboardData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Percent completed correctly',
|
||||
data: [],
|
||||
backgroundColor: function(context) {
|
||||
const label = context.chart.data.labels[context.dataIndex] || '';
|
||||
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 0.2)' : 'rgba(54, 162, 235, 0.2)';
|
||||
},
|
||||
borderColor: function(context) {
|
||||
const label = context.chart.data.labels[context.dataIndex] || '';
|
||||
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)';
|
||||
},
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
|
||||
var allData = [];
|
||||
{% for row in edit_sorted %}
|
||||
allData.push({
|
||||
model: '{{ row.model }}',
|
||||
pass_rate_2: {{ row.pass_rate_2 }},
|
||||
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
function updateChart() {
|
||||
var selectedRows = document.querySelectorAll('tr.selected');
|
||||
var showAll = selectedRows.length === 0;
|
||||
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
|
||||
allData.forEach(function(row, index) {
|
||||
var rowElement = document.getElementById('edit-row-' + index);
|
||||
if (showAll) {
|
||||
rowElement.classList.remove('selected');
|
||||
}
|
||||
if (showAll || rowElement.classList.contains('selected')) {
|
||||
leaderboardData.labels.push(row.model);
|
||||
leaderboardData.datasets[0].data.push(row.pass_rate_2);
|
||||
}
|
||||
});
|
||||
|
||||
leaderboardChart.update();
|
||||
}
|
||||
|
||||
var tableBody = document.querySelector('table tbody');
|
||||
allData.forEach(function(row, index) {
|
||||
var tr = tableBody.children[index];
|
||||
tr.id = 'edit-row-' + index;
|
||||
tr.style.cursor = 'pointer';
|
||||
tr.onclick = function() {
|
||||
this.classList.toggle('selected');
|
||||
updateChart();
|
||||
};
|
||||
});
|
||||
|
||||
var leaderboardChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: leaderboardData,
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateChart();
|
||||
|
||||
// Add search functionality for edit table
|
||||
document.getElementById('editSearchInput').addEventListener('keyup', function() {
|
||||
var searchWords = this.value.toLowerCase().split(' ').filter(word => word.length > 0);
|
||||
var tableBody = document.querySelector('table:first-of-type tbody');
|
||||
var rows = tableBody.getElementsByTagName('tr');
|
||||
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var rowText = rows[i].textContent;
|
||||
if (searchWords.every(word => rowText.toLowerCase().includes(word))) {
|
||||
rows[i].style.display = '';
|
||||
leaderboardData.labels.push(allData[i].model);
|
||||
leaderboardData.datasets[0].data.push(allData[i].pass_rate_2);
|
||||
} else {
|
||||
rows[i].style.display = 'none';
|
||||
}
|
||||
}
|
||||
leaderboardChart.update();
|
||||
});
|
||||
});
|
||||
0
aider/website/_includes/leaderboard.js
Normal file
0
aider/website/_includes/leaderboard.js
Normal file
@@ -1,5 +1,15 @@
|
||||
You can send long, multi-line messages in the chat in a few ways:
|
||||
- Paste a multi-line message directly into the chat.
|
||||
- Enter `{` alone on the first line to start a multiline message and `}` alone on the last line to end it.
|
||||
- Or, start with `{tag` (where "tag" is any sequence of letters/numbers) and end with `tag}`. This is useful when you need to include closing braces `}` in your message.
|
||||
- Use Meta-ENTER to start a new line without sending the message (Esc+ENTER in some environments).
|
||||
- Use `/paste` to paste text from the clipboard into the chat.
|
||||
- Use the `/editor` command to open your editor to create the next chat message. See [editor configuration docs](/docs/config/editor.html) for more info.
|
||||
|
||||
Example with a tag:
|
||||
```
|
||||
{python
|
||||
def hello():
|
||||
print("Hello}") # Note: contains a brace
|
||||
python}
|
||||
```
|
||||
|
||||
69
aider/website/_includes/quant-chart.js
Normal file
69
aider/website/_includes/quant-chart.js
Normal file
@@ -0,0 +1,69 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var ctx = document.getElementById('quantChart').getContext('2d');
|
||||
var chartData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Percent completed correctly',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
|
||||
var allData = [];
|
||||
{% for row in site.data.quant %}
|
||||
allData.push({
|
||||
model: '{{ row.model }}',
|
||||
pass_rate_2: {{ row.pass_rate_2 }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
allData.forEach(function(row) {
|
||||
chartData.labels.push(row.model);
|
||||
chartData.datasets[0].data.push(row.pass_rate_2);
|
||||
});
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: {
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Aider coder editing benchmark',
|
||||
font: {
|
||||
size: 16
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Percent completed correctly',
|
||||
font: {
|
||||
size: 14
|
||||
}
|
||||
},
|
||||
ticks: {
|
||||
font: {
|
||||
size: 16
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
ticks: {
|
||||
font: {
|
||||
size: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
90
aider/website/_includes/refactor-leaderboard.js
Normal file
90
aider/website/_includes/refactor-leaderboard.js
Normal file
@@ -0,0 +1,90 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var ctx = document.getElementById('refacChart').getContext('2d');
|
||||
var leaderboardData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Percent completed correctly',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
|
||||
var allData = [];
|
||||
{% for row in refac_sorted %}
|
||||
allData.push({
|
||||
model: '{{ row.model }}',
|
||||
pass_rate_1: {{ row.pass_rate_1 }},
|
||||
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
function updateChart() {
|
||||
var selectedRows = document.querySelectorAll('tr.selected');
|
||||
var showAll = selectedRows.length === 0;
|
||||
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
|
||||
allData.forEach(function(row, index) {
|
||||
var rowElement = document.getElementById('refac-row-' + index);
|
||||
if (showAll) {
|
||||
rowElement.classList.remove('selected');
|
||||
}
|
||||
if (showAll || rowElement.classList.contains('selected')) {
|
||||
leaderboardData.labels.push(row.model);
|
||||
leaderboardData.datasets[0].data.push(row.pass_rate_1);
|
||||
}
|
||||
});
|
||||
|
||||
leaderboardChart.update();
|
||||
}
|
||||
|
||||
var tableBody = document.querySelectorAll('table tbody')[1];
|
||||
allData.forEach(function(row, index) {
|
||||
var tr = tableBody.children[index];
|
||||
tr.id = 'refac-row-' + index;
|
||||
tr.style.cursor = 'pointer';
|
||||
tr.onclick = function() {
|
||||
this.classList.toggle('selected');
|
||||
updateChart();
|
||||
};
|
||||
});
|
||||
|
||||
var leaderboardChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: leaderboardData,
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateChart();
|
||||
|
||||
// Add search functionality for refactoring table
|
||||
document.getElementById('refacSearchInput').addEventListener('keyup', function() {
|
||||
var searchWords = this.value.toLowerCase().split(' ').filter(word => word.length > 0);
|
||||
var tableBody = document.querySelectorAll('table tbody')[1];
|
||||
var rows = tableBody.getElementsByTagName('tr');
|
||||
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var rowText = rows[i].textContent;
|
||||
if (searchWords.every(word => rowText.toLowerCase().includes(word))) {
|
||||
rows[i].style.display = '';
|
||||
leaderboardData.labels.push(allData[i].model);
|
||||
leaderboardData.datasets[0].data.push(allData[i].pass_rate_1);
|
||||
} else {
|
||||
rows[i].style.display = 'none';
|
||||
}
|
||||
}
|
||||
leaderboardChart.update();
|
||||
});
|
||||
});
|
||||
43
aider/website/_posts/2024-11-21-quantization.md
Normal file
43
aider/website/_posts/2024-11-21-quantization.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: Quantization matters
|
||||
excerpt: Open source LLMs are becoming very powerful, but pay attention to how you (or your provider) is quantizing the model. It can strongly affect code editing skill.
|
||||
highlight_image: /assets/quantization.jpg
|
||||
draft: false
|
||||
nav_exclude: true
|
||||
---
|
||||
{% if page.date %}
|
||||
<p class="post-date">{{ page.date | date: "%B %d, %Y" }}</p>
|
||||
{% endif %}
|
||||
|
||||
# Quantization matters
|
||||
|
||||
Open source models like Qwen 2.5 32B are performing very well on
|
||||
aider's code editing benchmark, rivaling closed source frontier models.
|
||||
But pay attention to how your model is being quantized, as it
|
||||
can strongly impact code editing skill.
|
||||
Heavily quantized models are often used by cloud API providers
|
||||
and local model servers like Ollama.
|
||||
|
||||
<canvas id="quantChart" width="800" height="450" style="margin: 20px 0"></canvas>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
{% include quant-chart.js %}
|
||||
</script>
|
||||
|
||||
The graph above compares 4 different versions of the Qwen 2.5 32B model,
|
||||
served both locally and from cloud providers.
|
||||
|
||||
- The [HuggingFace weights](https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct) served via [glhf.chat](https://glhf.chat).
|
||||
- The results from [OpenRouter's mix of providers](https://openrouter.ai/qwen/qwen-2.5-coder-32b-instruct/providers).
|
||||
- Two Ollama models run locally.
|
||||
|
||||
The best version of the model rivals GPT-4o, while the worst performer
|
||||
is more like GPT-3.5 Turbo.
|
||||
|
||||
|
||||
## Choosing providers with OpenRouter
|
||||
|
||||
OpenRouter allows you to ignore specific providers in your
|
||||
[preferences](https://openrouter.ai/settings/preferences).
|
||||
This can be effective to exclude highly quantized or otherwise
|
||||
undesirable providers.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 97 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 55 KiB |
BIN
aider/website/assets/quantization.jpg
Normal file
BIN
aider/website/assets/quantization.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
File diff suppressed because it is too large
Load Diff
@@ -271,8 +271,8 @@
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for one session (default: False)
|
||||
#analytics: false
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#analytics: xxx
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#analytics-log: xxx
|
||||
@@ -314,6 +314,9 @@
|
||||
## Check for new aider versions on launch
|
||||
#check-update: true
|
||||
|
||||
## Show release notes on first run of new version (default: None, ask user)
|
||||
#show-release-notes: xxx
|
||||
|
||||
## Install the latest version from the main branch
|
||||
#install-main-branch: false
|
||||
|
||||
@@ -365,6 +368,9 @@
|
||||
## Enable/disable fancy input with history and completion (default: True)
|
||||
#fancy-input: true
|
||||
|
||||
## Specify which editor to use for the /editor command
|
||||
#editor: xxx
|
||||
|
||||
#################
|
||||
# Voice Settings:
|
||||
|
||||
|
||||
@@ -270,8 +270,8 @@
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for one session (default: False)
|
||||
#AIDER_ANALYTICS=false
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#AIDER_ANALYTICS=
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#AIDER_ANALYTICS_LOG=
|
||||
@@ -300,6 +300,9 @@
|
||||
## Check for new aider versions on launch
|
||||
#AIDER_CHECK_UPDATE=true
|
||||
|
||||
## Show release notes on first run of new version (default: None, ask user)
|
||||
#AIDER_SHOW_RELEASE_NOTES=
|
||||
|
||||
## Install the latest version from the main branch
|
||||
#AIDER_INSTALL_MAIN_BRANCH=false
|
||||
|
||||
@@ -348,6 +351,9 @@
|
||||
## Enable/disable fancy input with history and completion (default: True)
|
||||
#AIDER_FANCY_INPUT=true
|
||||
|
||||
## Specify which editor to use for the /editor command
|
||||
#AIDER_EDITOR=
|
||||
|
||||
#################
|
||||
# Voice Settings:
|
||||
|
||||
|
||||
@@ -55,8 +55,10 @@ These model settings are pre-configured for most popular models.
|
||||
But it can sometimes be helpful to override them or add settings for
|
||||
a model that aider doesn't know about.
|
||||
|
||||
To do that,
|
||||
create a `.aider.model.settings.yml` file in one of these locations:
|
||||
|
||||
### Configuration file locations
|
||||
|
||||
You can override or add settings for any model by creating a `.aider.model.settings.yml` file in one of these locations:
|
||||
|
||||
- Your home directory.
|
||||
- The root if your git repo.
|
||||
@@ -66,9 +68,31 @@ create a `.aider.model.settings.yml` file in one of these locations:
|
||||
If the files above exist, they will be loaded in that order.
|
||||
Files loaded last will take priority.
|
||||
|
||||
The yaml file should be a a list of dictionary objects for each model.
|
||||
For example, below are all the pre-configured model settings
|
||||
to give a sense for the settings which are supported.
|
||||
The yaml file should be a list of dictionary objects for each model.
|
||||
|
||||
|
||||
### Global extra params
|
||||
|
||||
You can use the special model name `aider/extra_params` to define
|
||||
`extra_params` that will be passed to `litellm.completion()` for all models.
|
||||
Only the `extra_params` dict is used from this special model name.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
- name: aider/extra_params
|
||||
extra_params:
|
||||
extra_headers:
|
||||
Custom-Header: value
|
||||
max_tokens: 8192
|
||||
```
|
||||
|
||||
These settings will be merged with any model-specific settings, with the
|
||||
`aider/extra_params` settings taking precedence for any direct conflicts.
|
||||
|
||||
### Example model settings
|
||||
|
||||
Below are all the pre-configured model settings to give a sense for the settings which are supported.
|
||||
|
||||
You can also look at the `ModelSettings` class in
|
||||
[models.py](https://github.com/Aider-AI/aider/blob/main/aider/models.py)
|
||||
@@ -241,6 +265,38 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: false
|
||||
extra_params: null
|
||||
lazy: true
|
||||
name: gpt-4o-2024-11-20
|
||||
reminder: sys
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: false
|
||||
extra_params: null
|
||||
lazy: true
|
||||
name: openai/gpt-4o-2024-11-20
|
||||
reminder: sys
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -400,7 +456,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -416,7 +472,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-haiku
|
||||
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
@@ -432,7 +488,7 @@ cog.out("```\n")
|
||||
use_repo_map: false
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -451,7 +507,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -470,7 +526,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: anthropic/claude-3-haiku-20240307
|
||||
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -489,7 +545,26 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: anthropic/claude-3-haiku-20240307
|
||||
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
editor_model_name: bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
extra_headers:
|
||||
anthropic-beta: prompt-caching-2024-07-31
|
||||
max_tokens: 8192
|
||||
lazy: false
|
||||
name: bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -508,7 +583,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: anthropic/claude-3-haiku-20240307
|
||||
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -527,7 +602,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
@@ -560,10 +635,28 @@ cog.out("```\n")
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: false
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: false
|
||||
extra_params:
|
||||
extra_headers:
|
||||
anthropic-beta: prompt-caching-2024-07-31
|
||||
lazy: false
|
||||
name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -578,7 +671,7 @@ cog.out("```\n")
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: false
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
@@ -595,44 +688,10 @@ cog.out("```\n")
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: false
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: false
|
||||
extra_params:
|
||||
max_tokens: 4096
|
||||
lazy: false
|
||||
name: openrouter/anthropic/claude-3.5-haiku
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: false
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3.5-haiku
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: false
|
||||
extra_params:
|
||||
max_tokens: 4096
|
||||
lazy: false
|
||||
name: openrouter/anthropic/claude-3.5-haiku:beta
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: false
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3.5-haiku:beta
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
@@ -667,7 +726,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-haiku
|
||||
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -684,7 +743,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-haiku:beta
|
||||
weak_model_name: openrouter/anthropic/claude-3-5-haiku:beta
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -701,7 +760,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -718,7 +777,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
@@ -734,7 +793,7 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
@@ -750,7 +809,7 @@ cog.out("```\n")
|
||||
use_repo_map: false
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
@@ -911,6 +970,22 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff-fenced
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: false
|
||||
extra_params: null
|
||||
lazy: false
|
||||
name: vertex_ai/gemini-pro-experimental
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
@@ -1038,7 +1113,7 @@ cog.out("```\n")
|
||||
name: openai/o1-mini
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1054,7 +1129,7 @@ cog.out("```\n")
|
||||
name: azure/o1-mini
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1070,7 +1145,7 @@ cog.out("```\n")
|
||||
name: o1-mini
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1086,7 +1161,7 @@ cog.out("```\n")
|
||||
name: openai/o1-preview
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1102,7 +1177,7 @@ cog.out("```\n")
|
||||
name: azure/o1-preview
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1118,7 +1193,7 @@ cog.out("```\n")
|
||||
name: o1-preview
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1134,7 +1209,7 @@ cog.out("```\n")
|
||||
name: openrouter/openai/o1-mini
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
@@ -1150,11 +1225,27 @@ cog.out("```\n")
|
||||
name: openrouter/openai/o1-preview
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
editor_model_name: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
examples_as_sys_msg: false
|
||||
extra_params: null
|
||||
lazy: false
|
||||
name: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||
```
|
||||
<!--[[[end]]]-->
|
||||
|
||||
|
||||
@@ -327,8 +327,8 @@ cog.outl("```")
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for one session (default: False)
|
||||
#analytics: false
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#analytics: xxx
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#analytics-log: xxx
|
||||
@@ -370,6 +370,9 @@ cog.outl("```")
|
||||
## Check for new aider versions on launch
|
||||
#check-update: true
|
||||
|
||||
## Show release notes on first run of new version (default: None, ask user)
|
||||
#show-release-notes: xxx
|
||||
|
||||
## Install the latest version from the main branch
|
||||
#install-main-branch: false
|
||||
|
||||
@@ -421,6 +424,9 @@ cog.outl("```")
|
||||
## Enable/disable fancy input with history and completion (default: True)
|
||||
#fancy-input: true
|
||||
|
||||
## Specify which editor to use for the /editor command
|
||||
#editor: xxx
|
||||
|
||||
#################
|
||||
# Voice Settings:
|
||||
|
||||
|
||||
@@ -312,8 +312,8 @@ cog.outl("```")
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for one session (default: False)
|
||||
#AIDER_ANALYTICS=false
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#AIDER_ANALYTICS=
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#AIDER_ANALYTICS_LOG=
|
||||
@@ -342,6 +342,9 @@ cog.outl("```")
|
||||
## Check for new aider versions on launch
|
||||
#AIDER_CHECK_UPDATE=true
|
||||
|
||||
## Show release notes on first run of new version (default: None, ask user)
|
||||
#AIDER_SHOW_RELEASE_NOTES=
|
||||
|
||||
## Install the latest version from the main branch
|
||||
#AIDER_INSTALL_MAIN_BRANCH=false
|
||||
|
||||
@@ -390,6 +393,9 @@ cog.outl("```")
|
||||
## Enable/disable fancy input with history and completion (default: True)
|
||||
#AIDER_FANCY_INPUT=true
|
||||
|
||||
## Specify which editor to use for the /editor command
|
||||
#AIDER_EDITOR=
|
||||
|
||||
#################
|
||||
# Voice Settings:
|
||||
|
||||
|
||||
127
aider/website/docs/config/editor.md
Normal file
127
aider/website/docs/config/editor.md
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
parent: Configuration
|
||||
nav_order: 15
|
||||
description: How to configure a custom editor for aider's /editor command
|
||||
---
|
||||
|
||||
# Editor configuration
|
||||
|
||||
Aider allows you to configure your preferred text editor for use with the `/editor` command. The editor must be capable of running in "blocking mode", meaning the command line will wait until you close the editor before proceeding.
|
||||
|
||||
## Using `--editor`
|
||||
|
||||
You can specify the text editor with the `--editor` switch or using
|
||||
`editor:` in aider's
|
||||
[yaml config file](https://aider.chat/docs/config/aider_conf.html).
|
||||
|
||||
## Environment variables
|
||||
|
||||
Aider checks the following environment variables in order to determine which editor to use:
|
||||
|
||||
1. `AIDER_EDITOR`
|
||||
2. `VISUAL`
|
||||
3. `EDITOR`
|
||||
|
||||
## Default behavior
|
||||
|
||||
If no editor is configured, aider will use these platform-specific defaults:
|
||||
|
||||
- Windows: `notepad`
|
||||
- macOS: `vim`
|
||||
- Linux/Unix: `vi`
|
||||
|
||||
## Using a custom editor
|
||||
|
||||
You can set your preferred editor in your shell's configuration file (e.g., `.bashrc`, `.zshrc`):
|
||||
|
||||
```bash
|
||||
export AIDER_EDITOR=vim
|
||||
```
|
||||
|
||||
## Popular Editors by Platform
|
||||
|
||||
### macOS
|
||||
|
||||
1. **vim**
|
||||
```bash
|
||||
export AIDER_EDITOR=vim
|
||||
```
|
||||
|
||||
2. **Emacs**
|
||||
```bash
|
||||
export AIDER_EDITOR=emacs
|
||||
```
|
||||
|
||||
3. **VSCode**
|
||||
```bash
|
||||
export AIDER_EDITOR="code --wait"
|
||||
```
|
||||
|
||||
4. **Sublime Text**
|
||||
```bash
|
||||
export AIDER_EDITOR="subl --wait"
|
||||
```
|
||||
|
||||
5. **BBEdit**
|
||||
```bash
|
||||
export AIDER_EDITOR="bbedit --wait"
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
1. **vim**
|
||||
```bash
|
||||
export AIDER_EDITOR=vim
|
||||
```
|
||||
|
||||
2. **Emacs**
|
||||
```bash
|
||||
export AIDER_EDITOR=emacs
|
||||
```
|
||||
|
||||
3. **nano**
|
||||
```bash
|
||||
export AIDER_EDITOR=nano
|
||||
```
|
||||
|
||||
4. **VSCode**
|
||||
```bash
|
||||
export AIDER_EDITOR="code --wait"
|
||||
```
|
||||
|
||||
5. **Sublime Text**
|
||||
```bash
|
||||
export AIDER_EDITOR="subl --wait"
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
1. **Notepad**
|
||||
```bat
|
||||
set AIDER_EDITOR=notepad
|
||||
```
|
||||
|
||||
2. **VSCode**
|
||||
```bat
|
||||
set AIDER_EDITOR="code --wait"
|
||||
```
|
||||
|
||||
3. **Notepad++**
|
||||
```bat
|
||||
set AIDER_EDITOR="notepad++ -multiInst -notabbar -nosession -noPlugin -waitForClose"
|
||||
```
|
||||
|
||||
## Editor command arguments
|
||||
|
||||
Some editors require specific command-line arguments to operate in blocking mode. The `--wait` flag (or equivalent) is commonly used to make the editor block until the file is closed.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter issues with your editor not blocking (returning to the prompt immediately), verify that:
|
||||
|
||||
1. Your editor supports blocking mode
|
||||
2. You've included the necessary command-line arguments for blocking mode
|
||||
3. The editor command is properly quoted if it contains spaces or special characters, e.g.:
|
||||
```bash
|
||||
export AIDER_EDITOR="code --wait"
|
||||
```
|
||||
@@ -66,14 +66,15 @@ usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model]
|
||||
[--analytics-disable] [--file] [--read] [--vim]
|
||||
[--chat-language] [--version] [--just-check-update]
|
||||
[--check-update | --no-check-update]
|
||||
[--show-release-notes | --no-show-release-notes]
|
||||
[--install-main-branch] [--upgrade] [--apply]
|
||||
[--apply-clipboard-edits] [--yes-always] [-v]
|
||||
[--show-repo-map] [--show-prompts] [--exit] [--message]
|
||||
[--message-file] [--load] [--encoding] [-c]
|
||||
[--gui | --no-gui | --browser | --no-browser]
|
||||
[--suggest-shell-commands | --no-suggest-shell-commands]
|
||||
[--fancy-input | --no-fancy-input] [--voice-format]
|
||||
[--voice-language]
|
||||
[--fancy-input | --no-fancy-input] [--editor]
|
||||
[--voice-format] [--voice-language]
|
||||
|
||||
```
|
||||
|
||||
@@ -510,8 +511,7 @@ Environment variable: `AIDER_TEST`
|
||||
## Analytics:
|
||||
|
||||
### `--analytics`
|
||||
Enable/disable analytics for one session (default: False)
|
||||
Default: False
|
||||
Enable/disable analytics for current session (default: random)
|
||||
Environment variable: `AIDER_ANALYTICS`
|
||||
Aliases:
|
||||
- `--analytics`
|
||||
@@ -561,6 +561,13 @@ Aliases:
|
||||
- `--check-update`
|
||||
- `--no-check-update`
|
||||
|
||||
### `--show-release-notes`
|
||||
Show release notes on first run of new version (default: None, ask user)
|
||||
Environment variable: `AIDER_SHOW_RELEASE_NOTES`
|
||||
Aliases:
|
||||
- `--show-release-notes`
|
||||
- `--no-show-release-notes`
|
||||
|
||||
### `--install-main-branch`
|
||||
Install the latest version from the main branch
|
||||
Default: False
|
||||
@@ -666,6 +673,10 @@ Aliases:
|
||||
- `--fancy-input`
|
||||
- `--no-fancy-input`
|
||||
|
||||
### `--editor VALUE`
|
||||
Specify which editor to use for the /editor command
|
||||
Environment variable: `AIDER_EDITOR`
|
||||
|
||||
## Voice Settings:
|
||||
|
||||
### `--voice-format VOICE_FORMAT`
|
||||
|
||||
@@ -30,7 +30,7 @@ current chat to build a compact
|
||||
Adding a bunch of files that are mostly irrelevant to the
|
||||
task at hand will often distract or confuse the LLM.
|
||||
The LLM will give worse coding results, and sometimese even fail to correctly edit files.
|
||||
Addings extra files will also increase the token costs on your OpenAI invoice.
|
||||
Addings extra files will also increase your token costs.
|
||||
|
||||
Again, it's usually best to just add the files to the chat that will need to be modified.
|
||||
If you still wish to add lots of files to the chat, you can:
|
||||
@@ -60,6 +60,23 @@ directory you start in.
|
||||
You can also create a `.aiderignore` file to tell aider
|
||||
to ignore parts of the repo that aren't relevant to your task.
|
||||
This file conforms to `.gitignore` syntax and conventions.
|
||||
For example, to focus only on specific directories in a monorepo,
|
||||
you could create a `.aiderignore` file with:
|
||||
|
||||
```
|
||||
# Ignore everything
|
||||
/*
|
||||
|
||||
# Allow specific directories and their contents
|
||||
!foo/
|
||||
!bar/
|
||||
!baz/
|
||||
|
||||
# Allow nested files under these directories
|
||||
!foo/**
|
||||
!bar/**
|
||||
!baz/**
|
||||
```
|
||||
|
||||
You can use `--aiderignore <filename>` to name a specific file
|
||||
to use for ignore patterns.
|
||||
@@ -150,7 +167,6 @@ python -m aider
|
||||
|
||||
|
||||
|
||||
|
||||
## Can I change the system prompts that aider uses?
|
||||
|
||||
Aider is set up to support different system prompts and edit formats
|
||||
@@ -191,6 +207,16 @@ You can also refer to the
|
||||
[instructions for installing a development version of aider](https://aider.chat/docs/install/optional.html#install-the-development-version-of-aider).
|
||||
|
||||
|
||||
## How are the "aider wrote xx% of code" stats computed?
|
||||
|
||||
[Aider is tightly integrated with git](/docs/git.html) so all
|
||||
one of aider's code changes are committed to the repo with proper attribution.
|
||||
The
|
||||
[stats are computed](https://github.com/Aider-AI/aider/blob/main/scripts/blame.py)
|
||||
by doing something like `git blame` on the repo,
|
||||
and counting up who wrote all the new lines of code in each release.
|
||||
Only lines in source code files are counted, not documentation or prompt files.
|
||||
|
||||
## Can I share my aider chat transcript?
|
||||
|
||||
Yes, you can now share aider chat logs in a pretty way.
|
||||
@@ -213,6 +239,15 @@ This will give you a URL like this, which shows the chat history like you'd see
|
||||
https://aider.chat/share/?mdurl=https://gist.github.com/Aider-AI/2087ab8b64034a078c0a209440ac8be0
|
||||
```
|
||||
|
||||
## Can I edit files myself while aider is running?
|
||||
|
||||
Yes. Aider always reads the latest copy of files from the file
|
||||
system when you send each message.
|
||||
|
||||
While you're waiting for aider's reply to complete, it's probably unwise to
|
||||
edit files that you've added to the chat.
|
||||
Your edits and aider's edits might conflict.
|
||||
|
||||
## What is Aider AI LLC?
|
||||
|
||||
Aider AI LLC is the company behind the aider AI coding tool.
|
||||
@@ -222,11 +257,5 @@ under an
|
||||
[Apache 2.0 license](https://github.com/Aider-AI/aider/blob/main/LICENSE.txt).
|
||||
|
||||
|
||||
## Can I edit files myself while aider is running?
|
||||
<div style="height:80vh"></div>
|
||||
|
||||
Yes. Aider always reads the latest copy of files from the file
|
||||
system when you send each message.
|
||||
|
||||
While you're waiting for aider's reply to complete, it's probably unwise to
|
||||
edit files that you've added to the chat.
|
||||
Your edits and aider's edits might conflict.
|
||||
|
||||
@@ -31,6 +31,8 @@ This measures the LLM's coding ability, and whether it can
|
||||
write new code that integrates into existing code.
|
||||
The model also has to successfully apply all its changes to the source file without human intervention.
|
||||
|
||||
<input type="text" id="editSearchInput" placeholder="Search..." style="width: 100%; max-width: 800px; margin: 10px auto; padding: 8px; display: block; border: 1px solid #ddd; border-radius: 4px;">
|
||||
|
||||
<table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;">
|
||||
<thead style="background-color: #f2f2f2;">
|
||||
<tr>
|
||||
@@ -58,81 +60,7 @@ The model also has to successfully apply all its changes to the source file with
|
||||
<canvas id="editChart" width="800" height="450" style="margin-top: 20px"></canvas>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var ctx = document.getElementById('editChart').getContext('2d');
|
||||
const HIGHTLIGHT_MODEL = 'no no no no';
|
||||
var leaderboardData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Percent completed correctly',
|
||||
data: [],
|
||||
backgroundColor: function(context) {
|
||||
const label = context.chart.data.labels[context.dataIndex] || '';
|
||||
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 0.2)' : 'rgba(54, 162, 235, 0.2)';
|
||||
},
|
||||
borderColor: function(context) {
|
||||
const label = context.chart.data.labels[context.dataIndex] || '';
|
||||
return (label && label.includes(HIGHTLIGHT_MODEL)) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)';
|
||||
},
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
|
||||
var allData = [];
|
||||
{% for row in edit_sorted %}
|
||||
allData.push({
|
||||
model: '{{ row.model }}',
|
||||
pass_rate_2: {{ row.pass_rate_2 }},
|
||||
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
function updateChart() {
|
||||
var selectedRows = document.querySelectorAll('tr.selected');
|
||||
var showAll = selectedRows.length === 0;
|
||||
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
|
||||
allData.forEach(function(row, index) {
|
||||
var rowElement = document.getElementById('edit-row-' + index);
|
||||
if (showAll) {
|
||||
rowElement.classList.remove('selected');
|
||||
}
|
||||
if (showAll || rowElement.classList.contains('selected')) {
|
||||
leaderboardData.labels.push(row.model);
|
||||
leaderboardData.datasets[0].data.push(row.pass_rate_2);
|
||||
}
|
||||
});
|
||||
|
||||
leaderboardChart.update();
|
||||
}
|
||||
|
||||
var tableBody = document.querySelector('table tbody');
|
||||
allData.forEach(function(row, index) {
|
||||
var tr = tableBody.children[index];
|
||||
tr.id = 'edit-row-' + index;
|
||||
tr.style.cursor = 'pointer';
|
||||
tr.onclick = function() {
|
||||
this.classList.toggle('selected');
|
||||
updateChart();
|
||||
};
|
||||
});
|
||||
|
||||
var leaderboardChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: leaderboardData,
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateChart();
|
||||
});
|
||||
{% include edit-leaderboard.js %}
|
||||
</script>
|
||||
<style>
|
||||
tr.selected {
|
||||
@@ -158,6 +86,8 @@ The refactoring benchmark requires a large context window to
|
||||
work with large source files.
|
||||
Therefore, results are available for fewer models.
|
||||
|
||||
<input type="text" id="refacSearchInput" placeholder="Search..." style="width: 100%; max-width: 800px; margin: 10px auto; padding: 8px; display: block; border: 1px solid #ddd; border-radius: 4px;">
|
||||
|
||||
<table style="width: 100%; max-width: 800px; margin: auto; border-collapse: collapse; box-shadow: 0 2px 4px rgba(0,0,0,0.1); font-size: 14px;">
|
||||
<thead style="background-color: #f2f2f2;">
|
||||
<tr>
|
||||
@@ -185,74 +115,7 @@ Therefore, results are available for fewer models.
|
||||
<canvas id="refacChart" width="800" height="450" style="margin-top: 20px"></canvas>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var ctx = document.getElementById('refacChart').getContext('2d');
|
||||
var leaderboardData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Percent completed correctly',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
|
||||
var allData = [];
|
||||
{% for row in refac_sorted %}
|
||||
allData.push({
|
||||
model: '{{ row.model }}',
|
||||
pass_rate_1: {{ row.pass_rate_1 }},
|
||||
percent_cases_well_formed: {{ row.percent_cases_well_formed }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
function updateChart() {
|
||||
var selectedRows = document.querySelectorAll('tr.selected');
|
||||
var showAll = selectedRows.length === 0;
|
||||
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
|
||||
allData.forEach(function(row, index) {
|
||||
var rowElement = document.getElementById('refac-row-' + index);
|
||||
if (showAll) {
|
||||
rowElement.classList.remove('selected');
|
||||
}
|
||||
if (showAll || rowElement.classList.contains('selected')) {
|
||||
leaderboardData.labels.push(row.model);
|
||||
leaderboardData.datasets[0].data.push(row.pass_rate_1);
|
||||
}
|
||||
});
|
||||
|
||||
leaderboardChart.update();
|
||||
}
|
||||
|
||||
var tableBody = document.querySelectorAll('table tbody')[1];
|
||||
allData.forEach(function(row, index) {
|
||||
var tr = tableBody.children[index];
|
||||
tr.id = 'refac-row-' + index;
|
||||
tr.style.cursor = 'pointer';
|
||||
tr.onclick = function() {
|
||||
this.classList.toggle('selected');
|
||||
updateChart();
|
||||
};
|
||||
});
|
||||
|
||||
var leaderboardChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: leaderboardData,
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateChart();
|
||||
});
|
||||
{% include refactor-leaderboard.js %}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -318,6 +181,6 @@ mod_dates = [get_last_modified_date(file) for file in files]
|
||||
latest_mod_date = max(mod_dates)
|
||||
cog.out(f"{latest_mod_date.strftime('%B %d, %Y.')}")
|
||||
]]]-->
|
||||
November 04, 2024.
|
||||
November 21, 2024.
|
||||
<!--[[[end]]]-->
|
||||
</p>
|
||||
|
||||
24
aider/website/docs/llms/xai.md
Normal file
24
aider/website/docs/llms/xai.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
parent: Connecting to LLMs
|
||||
nav_order: 400
|
||||
---
|
||||
|
||||
# xAI
|
||||
|
||||
You'll need a [xAI API key](https://console.x.ai.).
|
||||
|
||||
To use xAI:
|
||||
|
||||
```
|
||||
python -m pip install -U aider-chat
|
||||
|
||||
export XAI_API_KEY=<key> # Mac/Linux
|
||||
setx XAI_API_KEY <key> # Windows, restart shell after setx
|
||||
|
||||
aider --model xai/groq-beta
|
||||
|
||||
# List models available from xAI
|
||||
aider --list-models groq/
|
||||
```
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ features and commands are most used.
|
||||
It also helps uncover bugs that users are experiencing, so that they can be fixed
|
||||
in upcoming releases.
|
||||
|
||||
## Enabling & disabling analytics
|
||||
## Disabling analytics
|
||||
|
||||
You can opt out of analytics forever by running this command one time:
|
||||
|
||||
@@ -39,10 +39,27 @@ You can opt out of analytics forever by running this command one time:
|
||||
aider --analytics-disable
|
||||
```
|
||||
|
||||
To enable analytics for a single session, you can run aider with `--analytics`.
|
||||
This will *not* have any effect if you have permanently disabled analytics with the previous command.
|
||||
## Enabling analytics
|
||||
|
||||
The first time, you will need to agree to opt-in.
|
||||
The `--[no-]analytics` switch controls whether analytics are enabled for the
|
||||
current session:
|
||||
|
||||
- `--analytics` will turn on analytics for the current session.
|
||||
This will *not* have any effect if you have permanently disabled analytics
|
||||
with `--analytics-disable`.
|
||||
If this is the first time you have enabled analytics, aider
|
||||
will confirm you wish to opt-in to analytics.
|
||||
- `--no-analytics` will turn off analytics for the current session.
|
||||
- By default, if you don't provide `--analytics` or `--no-analytics`,
|
||||
aider will enable analytics for a random subset of users.
|
||||
This will never happen if you have permanently disabled analytics
|
||||
with `--analytics-disable`.
|
||||
Randomly selected users will be asked if they wish to opt-in to analytics.
|
||||
|
||||
|
||||
## Opting in
|
||||
|
||||
The first time analytics are enabled, you will need to agree to opt-in.
|
||||
|
||||
```
|
||||
aider --analytics
|
||||
@@ -53,13 +70,8 @@ For more info: https://aider.chat/docs/more/analytics.html
|
||||
Allow collection of anonymous analytics to help improve aider? (Y)es/(N)o [Yes]:
|
||||
```
|
||||
|
||||
If you've added `analytics: true` to your
|
||||
[yaml config file](/docs/config/aider_conf.html),
|
||||
you can disable analytics for a single session, you can run:
|
||||
If you say "no", analytics will be permanently disabled.
|
||||
|
||||
```
|
||||
aider --no-analytics
|
||||
```
|
||||
|
||||
## Details about data being collected
|
||||
|
||||
@@ -100,7 +112,7 @@ aider --analytics-log filename.jsonl --no-analytics
|
||||
If you have concerns about any of the analytics that aider is collecting
|
||||
or our data practices
|
||||
please contact us by opening a
|
||||
[GitHub Issue](https://github.com/paul-gauthier/aider/issues).
|
||||
[GitHub Issue](https://github.com/aider-ai/aider/issues).
|
||||
|
||||
## Privacy policy
|
||||
|
||||
|
||||
@@ -55,8 +55,9 @@ model_list = "\n".join(f"- {model}" for model in sorted(prefill_models))
|
||||
|
||||
cog.out(model_list)
|
||||
]]]-->
|
||||
- anthropic.claude-3-5-haiku-20241022-v1:0
|
||||
- anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||
- anthropic/claude-3-5-sonnet-20241022
|
||||
- claude-3-5-haiku-20241022
|
||||
- claude-3-5-sonnet-20240620
|
||||
- claude-3-5-sonnet-20241022
|
||||
- claude-3-haiku-20240307
|
||||
@@ -87,11 +88,19 @@ cog.out(model_list)
|
||||
- mistral/open-mixtral-8x7b
|
||||
- mistral/pixtral-12b-2409
|
||||
- openrouter/anthropic/claude-3.5-sonnet
|
||||
- us.anthropic.claude-3-5-haiku-20241022-v1:0
|
||||
- us.anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||
- vertex_ai/claude-3-5-haiku
|
||||
- vertex_ai/claude-3-5-haiku@20241022
|
||||
- vertex_ai/claude-3-5-sonnet
|
||||
- vertex_ai/claude-3-5-sonnet-v2
|
||||
- vertex_ai/claude-3-5-sonnet-v2@20241022
|
||||
- vertex_ai/claude-3-5-sonnet@20240620
|
||||
- vertex_ai/claude-3-haiku
|
||||
- vertex_ai/claude-3-haiku@20240307
|
||||
- vertex_ai/claude-3-opus
|
||||
- vertex_ai/claude-3-opus@20240229
|
||||
- vertex_ai/claude-3-sonnet
|
||||
- vertex_ai/claude-3-sonnet@20240229
|
||||
<!--[[[end]]]-->
|
||||
|
||||
|
||||
@@ -95,3 +95,6 @@ io = InputOutput(yes=True)
|
||||
coder = Coder.create(model=model, fnames=fnames, io=io)
|
||||
```
|
||||
|
||||
{: .note }
|
||||
The scripting API is not officially supported or documented and may
|
||||
change without warning.
|
||||
|
||||
@@ -19,7 +19,19 @@ LLM edits that are "almost" correctly formatted.
|
||||
But sometimes the LLM just won't cooperate.
|
||||
In these cases, here are some things you might try.
|
||||
|
||||
## Use a capable model
|
||||
## Don't add too many files
|
||||
|
||||
Many LLMs now have very large context windows,
|
||||
but filling them with irrelevant code or conversation
|
||||
can confuse the model.
|
||||
|
||||
- Don't add too many files to the chat, *just* add the files you think need to be edited.
|
||||
Aider also sends the LLM a [map of your entire git repo](https://aider.chat/docs/repomap.html), so other relevant code will be included automatically.
|
||||
- Use `/drop` to remove files from the chat session which aren't needed for the task at hand. This will reduce distractions and may help the LLM produce properly formatted edits.
|
||||
- Use `/clear` to remove the conversation history, again to help the LLM focus.
|
||||
- Use `/tokens` to see how many tokens you are using for each message.
|
||||
|
||||
## Use a more capable model
|
||||
|
||||
If possible try using GPT-4o, Claude 3.5 Sonnet or Claude 3 Opus,
|
||||
as they are the strongest and most capable models.
|
||||
@@ -33,9 +45,9 @@ so editing errors are probably unavoidable.
|
||||
Local models which have been quantized are even more likely to have problems
|
||||
because they are not capable enough to follow aider's system prompts.
|
||||
|
||||
## Try the whole format
|
||||
## Try the whole edit format
|
||||
|
||||
Run aider with `--edit-format whole` if the model is using a different edit format.
|
||||
Run aider with `--edit-format whole` if were using a different edit format.
|
||||
You can see which edit format it is using in the announce lines:
|
||||
|
||||
```
|
||||
@@ -43,17 +55,6 @@ Aider v0.50.2-dev
|
||||
Models: claude-3-5-sonnet-20240620 with ♾️ diff edit format
|
||||
```
|
||||
|
||||
## Reduce distractions
|
||||
|
||||
Many LLMs now have very large context windows,
|
||||
but filling them with irrelevant code or conversation
|
||||
can confuse the model.
|
||||
|
||||
- Don't add too many files to the chat, *just* add the files you think need to be edited.
|
||||
Aider also sends the LLM a [map of your entire git repo](https://aider.chat/docs/repomap.html), so other relevant code will be included automatically.
|
||||
- Use `/drop` to remove files from the chat session which aren't needed for the task at hand. This will reduce distractions and may help the LLM produce properly formatted edits.
|
||||
- Use `/clear` to remove the conversation history, again to help the LLM focus.
|
||||
- Use `/tokens` to see how many tokens you are using for each message.
|
||||
|
||||
## More help
|
||||
|
||||
|
||||
@@ -3,7 +3,14 @@ parent: Usage
|
||||
nav_order: 50
|
||||
description: Control aider with in-chat commands like /add, /model, etc.
|
||||
---
|
||||
|
||||
# In-chat commands
|
||||
{: .no_toc }
|
||||
|
||||
- TOC
|
||||
{:toc}
|
||||
|
||||
## Slash commands
|
||||
|
||||
Aider supports commands from within the chat, which all start with `/`.
|
||||
|
||||
@@ -24,6 +31,7 @@ cog.out(get_help_md())
|
||||
| **/copy** | Copy the last assistant message to the clipboard |
|
||||
| **/diff** | Display the diff of changes since the last message |
|
||||
| **/drop** | Remove files from the chat session to free up context space |
|
||||
| **/editor** | Open an editor to write a prompt |
|
||||
| **/exit** | Exit the application |
|
||||
| **/git** | Run a git command (output excluded from chat) |
|
||||
| **/help** | Ask questions about aider |
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
nav_exclude: true
|
||||
---
|
||||
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self';
|
||||
script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com;
|
||||
connect-src http: https:;
|
||||
style-src 'self' 'unsafe-inline';">
|
||||
|
||||
# Shared aider chat transcript
|
||||
|
||||
A user has shared the following transcript of a pair programming chat session
|
||||
@@ -37,11 +43,29 @@ print("goodbye")
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
|
||||
<script>
|
||||
function isValidUrl(url) {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure marked with secure defaults
|
||||
marked.setOptions({
|
||||
headerIds: false,
|
||||
mangle: false
|
||||
});
|
||||
|
||||
window.onload = function() {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var conv = urlParams.get('mdurl');
|
||||
if (!conv) {
|
||||
if (!conv || !isValidUrl(conv)) {
|
||||
document.querySelector('#shared-transcript').innerHTML =
|
||||
'<div style="color: red; padding: 1em;">Error: Invalid or missing URL provided</div>';
|
||||
return;
|
||||
}
|
||||
document.getElementById('mdurl').href = conv;
|
||||
@@ -63,11 +87,14 @@ window.onload = function() {
|
||||
return line;
|
||||
}).join('\n');
|
||||
var html = marked.parse(markdown);
|
||||
var sanitizedHtml = DOMPurify.sanitize(html);
|
||||
var divElement = document.querySelector('#shared-transcript');
|
||||
divElement.innerHTML = html;
|
||||
divElement.innerHTML = sanitizedHtml;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching markdown:', error);
|
||||
document.querySelector('#shared-transcript').innerHTML =
|
||||
'<div style="color: red; padding: 1em;">Error: Failed to load chat transcript</div>';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -5,6 +5,33 @@ from matplotlib import rc
|
||||
|
||||
from aider.dump import dump # noqa: 401
|
||||
|
||||
LABEL_FONT_SIZE = 16 # Font size for scatter plot dot labels
|
||||
|
||||
|
||||
def get_legend_label(model):
|
||||
model = model.lower()
|
||||
if "claude-3-sonnet" in model:
|
||||
return "Sonnet"
|
||||
if "o1-preview" in model:
|
||||
return "O1 Preview"
|
||||
if "gpt-3.5" in model:
|
||||
return "GPT-3.5 Turbo"
|
||||
if "gpt-4-" in model and "-4o" not in model:
|
||||
return "GPT-4"
|
||||
if "qwen" in model:
|
||||
return "Qwen"
|
||||
if "-4o" in model:
|
||||
return "GPT-4o"
|
||||
if "haiku" in model:
|
||||
return "Haiku"
|
||||
if "deepseek" in model:
|
||||
return "DeepSeek"
|
||||
if "mistral" in model:
|
||||
return "Mistral"
|
||||
if "o1-preview" in model:
|
||||
return "o1-preview"
|
||||
return model
|
||||
|
||||
|
||||
def get_model_color(model):
|
||||
default = "lightblue"
|
||||
@@ -12,6 +39,21 @@ def get_model_color(model):
|
||||
if model == "gpt-4o-mini":
|
||||
return default
|
||||
|
||||
if "qwen" in model.lower():
|
||||
return "darkblue"
|
||||
|
||||
if "mistral" in model.lower():
|
||||
return "cyan"
|
||||
|
||||
if "haiku" in model.lower():
|
||||
return "pink"
|
||||
|
||||
if "deepseek" in model.lower():
|
||||
return "brown"
|
||||
|
||||
if "sonnet" in model.lower():
|
||||
return "orange"
|
||||
|
||||
if "-4o" in model:
|
||||
return "purple"
|
||||
|
||||
@@ -59,7 +101,7 @@ def plot_over_time(yaml_file):
|
||||
rc("font", **{"family": "sans-serif", "sans-serif": ["Helvetica"], "size": 10})
|
||||
plt.rcParams["text.color"] = "#444444"
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 6)) # Increase figure size for better visibility
|
||||
fig, ax = plt.subplots(figsize=(12, 8)) # Make figure square
|
||||
|
||||
print("Debug: Figure created. Plotting data...")
|
||||
ax.grid(axis="y", zorder=0, lw=0.2)
|
||||
@@ -73,39 +115,154 @@ def plot_over_time(yaml_file):
|
||||
purple_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "purple"]
|
||||
red_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "red"]
|
||||
green_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "green"]
|
||||
orange_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "orange"]
|
||||
brown_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "brown"]
|
||||
pink_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "pink"]
|
||||
qwen_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "darkblue"]
|
||||
mistral_points = [(d, r) for d, r, c in zip(dates, pass_rates, colors) if c == "cyan"]
|
||||
|
||||
# Plot lines for purple, red, and green points
|
||||
# Create a mapping of colors to first points and labels
|
||||
color_to_first_point = {}
|
||||
color_to_label = {}
|
||||
|
||||
for date, rate, color, model in sorted(zip(dates, pass_rates, colors, models)):
|
||||
if color not in color_to_first_point:
|
||||
color_to_first_point[color] = (date, rate)
|
||||
color_to_label[color] = get_legend_label(model)
|
||||
|
||||
# Plot lines and add labels at first points
|
||||
if purple_points:
|
||||
purple_dates, purple_rates = zip(*sorted(purple_points))
|
||||
ax.plot(purple_dates, purple_rates, c="purple", alpha=0.5, linewidth=1)
|
||||
if "purple" in color_to_first_point:
|
||||
date, rate = color_to_first_point["purple"]
|
||||
ax.annotate(
|
||||
color_to_label["purple"],
|
||||
(date, rate),
|
||||
xytext=(10, 5),
|
||||
textcoords="offset points",
|
||||
color="purple",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
if red_points:
|
||||
red_dates, red_rates = zip(*sorted(red_points))
|
||||
ax.plot(red_dates, red_rates, c="red", alpha=0.5, linewidth=1)
|
||||
if "red" in color_to_first_point:
|
||||
date, rate = color_to_first_point["red"]
|
||||
ax.annotate(
|
||||
color_to_label["red"],
|
||||
(date, rate),
|
||||
xytext=(10, 5),
|
||||
textcoords="offset points",
|
||||
color="red",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
if green_points:
|
||||
green_dates, green_rates = zip(*sorted(green_points))
|
||||
ax.plot(green_dates, green_rates, c="green", alpha=0.5, linewidth=1)
|
||||
if "green" in color_to_first_point:
|
||||
date, rate = color_to_first_point["green"]
|
||||
ax.annotate(
|
||||
color_to_label["green"],
|
||||
(date, rate),
|
||||
xytext=(10, 5),
|
||||
textcoords="offset points",
|
||||
color="green",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
# Plot all points
|
||||
ax.scatter(dates, pass_rates, c=colors, alpha=0.5, s=120)
|
||||
if orange_points:
|
||||
orange_dates, orange_rates = zip(*sorted(orange_points))
|
||||
ax.plot(orange_dates, orange_rates, c="orange", alpha=0.5, linewidth=1)
|
||||
if "orange" in color_to_first_point:
|
||||
date, rate = color_to_first_point["orange"]
|
||||
ax.annotate(
|
||||
color_to_label["orange"],
|
||||
(date, rate),
|
||||
xytext=(10, 5),
|
||||
textcoords="offset points",
|
||||
color="orange",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
for i, model in enumerate(models):
|
||||
ax.annotate(
|
||||
model,
|
||||
(dates[i], pass_rates[i]),
|
||||
fontsize=8,
|
||||
alpha=0.75,
|
||||
xytext=(5, 5),
|
||||
textcoords="offset points",
|
||||
)
|
||||
if brown_points:
|
||||
brown_dates, brown_rates = zip(*sorted(brown_points))
|
||||
ax.plot(brown_dates, brown_rates, c="brown", alpha=0.5, linewidth=1)
|
||||
if "brown" in color_to_first_point:
|
||||
date, rate = color_to_first_point["brown"]
|
||||
ax.annotate(
|
||||
color_to_label["brown"],
|
||||
(date, rate),
|
||||
xytext=(10, -10),
|
||||
textcoords="offset points",
|
||||
color="brown",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
if pink_points:
|
||||
pink_dates, pink_rates = zip(*sorted(pink_points))
|
||||
ax.plot(pink_dates, pink_rates, c="pink", alpha=0.5, linewidth=1)
|
||||
if "pink" in color_to_first_point:
|
||||
date, rate = color_to_first_point["pink"]
|
||||
ax.annotate(
|
||||
color_to_label["pink"],
|
||||
(date, rate),
|
||||
xytext=(10, 5),
|
||||
textcoords="offset points",
|
||||
color="pink",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
if qwen_points:
|
||||
qwen_dates, qwen_rates = zip(*sorted(qwen_points))
|
||||
ax.plot(qwen_dates, qwen_rates, c="darkblue", alpha=0.5, linewidth=1)
|
||||
if "darkblue" in color_to_first_point:
|
||||
date, rate = color_to_first_point["darkblue"]
|
||||
ax.annotate(
|
||||
color_to_label["darkblue"],
|
||||
(date, rate),
|
||||
xytext=(10, 5),
|
||||
textcoords="offset points",
|
||||
color="darkblue",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
if mistral_points:
|
||||
mistral_dates, mistral_rates = zip(*sorted(mistral_points))
|
||||
ax.plot(mistral_dates, mistral_rates, c="cyan", alpha=0.5, linewidth=1)
|
||||
if "cyan" in color_to_first_point:
|
||||
date, rate = color_to_first_point["cyan"]
|
||||
ax.annotate(
|
||||
color_to_label["cyan"],
|
||||
(date, rate),
|
||||
xytext=(10, -10),
|
||||
textcoords="offset points",
|
||||
color="cyan",
|
||||
alpha=0.8,
|
||||
fontsize=LABEL_FONT_SIZE,
|
||||
)
|
||||
|
||||
# Plot points without legend
|
||||
for date, rate, color in zip(dates, pass_rates, colors):
|
||||
ax.scatter([date], [rate], c=[color], alpha=0.5, s=120)
|
||||
|
||||
ax.set_xlabel("Model release date", fontsize=18, color="#555")
|
||||
ax.set_ylabel(
|
||||
"Aider code editing benchmark,\npercent completed correctly", fontsize=18, color="#555"
|
||||
)
|
||||
ax.set_title("LLM code editing skill by model release date", fontsize=20)
|
||||
ax.set_ylim(0, 100) # Adjust y-axis limit to accommodate higher values
|
||||
ax.set_ylim(30, 90) # Adjust y-axis limit to accommodate higher values
|
||||
plt.xticks(fontsize=14, rotation=45, ha="right") # Rotate x-axis labels for better readability
|
||||
plt.tight_layout(pad=3.0)
|
||||
plt.tight_layout(pad=1.0) # Adjust layout since we don't need room for legend anymore
|
||||
|
||||
print("Debug: Saving figures...")
|
||||
plt.savefig("tmp_over_time.png")
|
||||
|
||||
@@ -58,6 +58,7 @@ include = ["aider*", "aider.website"]
|
||||
"docs/unified-diffs.md",
|
||||
"docs/leaderboards/index.md",
|
||||
"assets/**",
|
||||
"**/.DS_Store",
|
||||
# [[[end]]]
|
||||
]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#
|
||||
aiohappyeyeballs==2.4.3
|
||||
# via aiohttp
|
||||
aiohttp==3.10.10
|
||||
aiohttp==3.11.2
|
||||
# via litellm
|
||||
aiosignal==1.3.1
|
||||
# via aiohttp
|
||||
@@ -86,9 +86,9 @@ importlib-resources==6.4.5
|
||||
# via -r requirements/requirements.in
|
||||
jinja2==3.1.4
|
||||
# via litellm
|
||||
jiter==0.7.0
|
||||
jiter==0.7.1
|
||||
# via openai
|
||||
json5==0.9.25
|
||||
json5==0.9.28
|
||||
# via -r requirements/requirements.in
|
||||
jsonschema==4.23.0
|
||||
# via
|
||||
@@ -96,7 +96,7 @@ jsonschema==4.23.0
|
||||
# litellm
|
||||
jsonschema-specifications==2024.10.1
|
||||
# via jsonschema
|
||||
litellm==1.51.2
|
||||
litellm==1.52.8
|
||||
# via -r requirements/requirements.in
|
||||
markdown-it-py==3.0.0
|
||||
# via rich
|
||||
@@ -120,9 +120,9 @@ numpy==1.26.4
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# scipy
|
||||
openai==1.53.0
|
||||
openai==1.54.4
|
||||
# via litellm
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# huggingface-hub
|
||||
@@ -139,7 +139,9 @@ posthog==3.7.0
|
||||
prompt-toolkit==3.0.48
|
||||
# via -r requirements/requirements.in
|
||||
propcache==0.2.0
|
||||
# via yarl
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
psutil==6.1.0
|
||||
# via -r requirements/requirements.in
|
||||
ptyprocess==0.7.0
|
||||
@@ -176,7 +178,7 @@ referencing==0.35.1
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
regex==2024.9.11
|
||||
regex==2024.11.6
|
||||
# via tiktoken
|
||||
requests==2.32.3
|
||||
# via
|
||||
@@ -185,9 +187,9 @@ requests==2.32.3
|
||||
# mixpanel
|
||||
# posthog
|
||||
# tiktoken
|
||||
rich==13.9.3
|
||||
rich==13.9.4
|
||||
# via -r requirements/requirements.in
|
||||
rpds-py==0.20.1
|
||||
rpds-py==0.21.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
@@ -217,7 +219,7 @@ tokenizers==0.19.1
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# litellm
|
||||
tqdm==4.66.6
|
||||
tqdm==4.67.0
|
||||
# via
|
||||
# huggingface-hub
|
||||
# openai
|
||||
@@ -241,5 +243,5 @@ wcwidth==0.2.13
|
||||
# via prompt-toolkit
|
||||
yarl==1.17.1
|
||||
# via aiohttp
|
||||
zipp==3.20.2
|
||||
zipp==3.21.0
|
||||
# via importlib-metadata
|
||||
|
||||
@@ -13,7 +13,7 @@ attrs==24.2.0
|
||||
# -c requirements/requirements-help.txt
|
||||
# jsonschema
|
||||
# referencing
|
||||
blinker==1.8.2
|
||||
blinker==1.9.0
|
||||
# via streamlit
|
||||
cachetools==5.5.0
|
||||
# via streamlit
|
||||
@@ -92,7 +92,7 @@ mdurl==0.1.2
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# markdown-it-py
|
||||
narwhals==1.12.1
|
||||
narwhals==1.13.5
|
||||
# via altair
|
||||
numpy==1.26.4
|
||||
# via
|
||||
@@ -103,7 +103,7 @@ numpy==1.26.4
|
||||
# pandas
|
||||
# pydeck
|
||||
# streamlit
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -157,13 +157,13 @@ requests==2.32.3
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# streamlit
|
||||
rich==13.9.3
|
||||
rich==13.9.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# streamlit
|
||||
rpds-py==0.20.1
|
||||
rpds-py==0.21.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -180,7 +180,7 @@ smmap==5.0.1
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# gitdb
|
||||
streamlit==1.39.0
|
||||
streamlit==1.40.1
|
||||
# via -r requirements/requirements-browser.in
|
||||
tenacity==8.5.0
|
||||
# via
|
||||
|
||||
@@ -32,7 +32,7 @@ codespell==2.3.0
|
||||
# via -r requirements/requirements-dev.in
|
||||
cogapp==3.4.1
|
||||
# via -r requirements/requirements-dev.in
|
||||
contourpy==1.3.0
|
||||
contourpy==1.3.1
|
||||
# via matplotlib
|
||||
cycler==0.12.1
|
||||
# via matplotlib
|
||||
@@ -51,9 +51,9 @@ filelock==3.16.1
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# virtualenv
|
||||
fonttools==4.54.1
|
||||
fonttools==4.55.0
|
||||
# via matplotlib
|
||||
identify==2.6.1
|
||||
identify==2.6.2
|
||||
# via pre-commit
|
||||
idna==3.10
|
||||
# via
|
||||
@@ -103,7 +103,7 @@ numpy==1.26.4
|
||||
# contourpy
|
||||
# matplotlib
|
||||
# pandas
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -164,7 +164,7 @@ requests==2.32.3
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# sphinx
|
||||
rich==13.9.3
|
||||
rich==13.9.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -184,7 +184,7 @@ sphinx==8.1.3
|
||||
# via
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
sphinx-rtd-theme==3.0.1
|
||||
sphinx-rtd-theme==3.0.2
|
||||
# via lox
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
@@ -200,7 +200,7 @@ sphinxcontrib-qthelp==2.0.0
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==2.0.0
|
||||
# via sphinx
|
||||
typer==0.12.5
|
||||
typer==0.13.0
|
||||
# via -r requirements/requirements-dev.in
|
||||
typing-extensions==4.12.2
|
||||
# via
|
||||
@@ -216,7 +216,7 @@ urllib3==2.2.3
|
||||
# requests
|
||||
virtualenv==20.27.1
|
||||
# via pre-commit
|
||||
wheel==0.44.0
|
||||
wheel==0.45.0
|
||||
# via pip-tools
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
|
||||
@@ -9,7 +9,7 @@ aiohappyeyeballs==2.4.3
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
aiohttp==3.10.10
|
||||
aiohttp==3.11.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -69,6 +69,8 @@ filelock==3.16.1
|
||||
# huggingface-hub
|
||||
# torch
|
||||
# transformers
|
||||
filetype==1.2.0
|
||||
# via llama-index-core
|
||||
frozenlist==1.5.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
@@ -128,7 +130,7 @@ joblib==1.4.2
|
||||
# via
|
||||
# nltk
|
||||
# scikit-learn
|
||||
llama-index-core==0.11.21
|
||||
llama-index-core==0.11.23
|
||||
# via
|
||||
# -r requirements/requirements-help.in
|
||||
# llama-index-embeddings-huggingface
|
||||
@@ -140,7 +142,7 @@ markupsafe==3.0.2
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# jinja2
|
||||
marshmallow==3.23.0
|
||||
marshmallow==3.23.1
|
||||
# via dataclasses-json
|
||||
mpmath==1.3.0
|
||||
# via sympy
|
||||
@@ -171,7 +173,7 @@ numpy==1.26.4
|
||||
# scikit-learn
|
||||
# scipy
|
||||
# transformers
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -190,6 +192,7 @@ propcache==0.2.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
# yarl
|
||||
pydantic==2.9.2
|
||||
# via
|
||||
@@ -209,7 +212,7 @@ pyyaml==6.0.2
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
# transformers
|
||||
regex==2024.9.11
|
||||
regex==2024.11.6
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
@@ -235,7 +238,7 @@ scipy==1.13.1
|
||||
# -c requirements/../requirements.txt
|
||||
# scikit-learn
|
||||
# sentence-transformers
|
||||
sentence-transformers==3.2.1
|
||||
sentence-transformers==3.3.0
|
||||
# via llama-index-embeddings-huggingface
|
||||
sniffio==1.3.1
|
||||
# via
|
||||
@@ -265,7 +268,7 @@ tokenizers==0.19.1
|
||||
# transformers
|
||||
torch==2.2.2
|
||||
# via sentence-transformers
|
||||
tqdm==4.66.6
|
||||
tqdm==4.67.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
|
||||
@@ -23,8 +23,10 @@ def blame(start_tag, end_tag=None):
|
||||
files = [
|
||||
f
|
||||
for f in files
|
||||
if f.endswith((".py", ".scm", ".sh", "Dockerfile", "Gemfile"))
|
||||
if f.endswith((".js", ".py", ".scm", ".sh", "Dockerfile", "Gemfile"))
|
||||
or (f.startswith(".github/workflows/") and f.endswith(".yml"))
|
||||
or f == "aider/website/share/index.md"
|
||||
or f == "aider/website/docs/leaderboards/index.md"
|
||||
]
|
||||
files = [f for f in files if not f.endswith("prompts.py")]
|
||||
|
||||
|
||||
@@ -22,11 +22,27 @@ def has_been_reopened(issue_number):
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
DUPLICATE_COMMENT = """Thanks for trying aider and filing this issue.
|
||||
BOT_SUFFIX = """Note: A [bot script](https://github.com/Aider-AI/aider/blob/main/scripts/issues.py) made these updates to the issue.
|
||||
""" # noqa
|
||||
|
||||
DUPLICATE_COMMENT = (
|
||||
"""Thanks for trying aider and filing this issue.
|
||||
|
||||
This looks like a duplicate of #{oldest_issue_number}. Please see the comments there for more information, and feel free to continue the discussion within that issue.
|
||||
|
||||
I'm going to close this issue for now. But please let me know if you think this is actually a distinct issue and I will reopen this issue.""" # noqa
|
||||
+ BOT_SUFFIX
|
||||
)
|
||||
|
||||
STALE_COMMENT = (
|
||||
"""I'm labeling this issue as stale because it has been open for 2 weeks with no activity. If there are no additional comments, I will close it in 7 days.""" # noqa
|
||||
+ BOT_SUFFIX
|
||||
)
|
||||
|
||||
CLOSE_STALE_COMMENT = (
|
||||
"""I'm closing this issue because it has been stalled for 3 weeks with no activity. Feel free to add a comment here and we can re-open it. Or feel free to file a new issue at any time.""" # noqa
|
||||
+ BOT_SUFFIX
|
||||
)
|
||||
|
||||
# GitHub API configuration
|
||||
GITHUB_API_URL = "https://api.github.com"
|
||||
@@ -112,22 +128,187 @@ def comment_and_close_duplicate(issue, oldest_issue):
|
||||
print(f" - Commented and closed issue #{issue['number']}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Handle duplicate GitHub issues")
|
||||
parser.add_argument(
|
||||
"--yes", action="store_true", help="Automatically close duplicates without prompting"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
def find_unlabeled_with_paul_comments(issues):
|
||||
unlabeled_issues = []
|
||||
for issue in issues:
|
||||
# Skip pull requests
|
||||
if "pull_request" in issue:
|
||||
continue
|
||||
|
||||
if not TOKEN:
|
||||
print("Error: Missing GITHUB_TOKEN environment variable. Please check your .env file.")
|
||||
if not issue["labels"] and issue["state"] == "open":
|
||||
# Get comments for this issue
|
||||
comments_url = (
|
||||
f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/comments"
|
||||
)
|
||||
response = requests.get(comments_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
comments = response.json()
|
||||
|
||||
# Check if paul-gauthier has commented
|
||||
if any(comment["user"]["login"] == "paul-gauthier" for comment in comments):
|
||||
unlabeled_issues.append(issue)
|
||||
return unlabeled_issues
|
||||
|
||||
|
||||
def handle_unlabeled_issues(all_issues, auto_yes):
|
||||
print("\nFinding unlabeled issues with paul-gauthier comments...")
|
||||
unlabeled_issues = find_unlabeled_with_paul_comments(all_issues)
|
||||
|
||||
if not unlabeled_issues:
|
||||
print("No unlabeled issues with paul-gauthier comments found.")
|
||||
return
|
||||
|
||||
all_issues = get_issues("all")
|
||||
print(f"\nFound {len(unlabeled_issues)} unlabeled issues with paul-gauthier comments:")
|
||||
for issue in unlabeled_issues:
|
||||
print(f" - #{issue['number']}: {issue['title']} {issue['html_url']}")
|
||||
|
||||
if not auto_yes:
|
||||
confirm = input("\nDo you want to add the 'question' label to these issues? (y/n): ")
|
||||
if confirm.lower() != "y":
|
||||
print("Skipping labeling.")
|
||||
return
|
||||
|
||||
print("\nAdding 'question' label to issues...")
|
||||
for issue in unlabeled_issues:
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}"
|
||||
response = requests.patch(url, headers=headers, json={"labels": ["question"]})
|
||||
response.raise_for_status()
|
||||
print(f" - Added 'question' label to #{issue['number']}")
|
||||
|
||||
|
||||
def handle_stale_issues(all_issues, auto_yes):
|
||||
print("\nChecking for stale question issues...")
|
||||
|
||||
for issue in all_issues:
|
||||
# Skip if not open, not a question, already stale, or has been reopened
|
||||
if (
|
||||
issue["state"] != "open"
|
||||
or "question" not in [label["name"] for label in issue["labels"]]
|
||||
or "stale" in [label["name"] for label in issue["labels"]]
|
||||
or has_been_reopened(issue["number"])
|
||||
):
|
||||
continue
|
||||
|
||||
# Get latest activity timestamp from issue or its comments
|
||||
latest_activity = datetime.strptime(issue["updated_at"], "%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Check if issue is stale (no activity for 14 days)
|
||||
days_inactive = (datetime.now() - latest_activity).days
|
||||
if days_inactive >= 14:
|
||||
print(f"\nStale issue found: #{issue['number']}: {issue['title']}\n{issue['html_url']}")
|
||||
print(f" No activity for {days_inactive} days")
|
||||
|
||||
if not auto_yes:
|
||||
confirm = input("Add stale label and comment? (y/n): ")
|
||||
if confirm.lower() != "y":
|
||||
print("Skipping this issue.")
|
||||
continue
|
||||
|
||||
# Add comment
|
||||
comment_url = (
|
||||
f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/comments"
|
||||
)
|
||||
response = requests.post(comment_url, headers=headers, json={"body": STALE_COMMENT})
|
||||
response.raise_for_status()
|
||||
|
||||
# Add stale label
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}"
|
||||
response = requests.patch(url, headers=headers, json={"labels": ["question", "stale"]})
|
||||
response.raise_for_status()
|
||||
|
||||
print(f" Added stale label and comment to #{issue['number']}")
|
||||
|
||||
|
||||
def handle_stale_closing(all_issues, auto_yes):
|
||||
print("\nChecking for issues to close or unstale...")
|
||||
|
||||
for issue in all_issues:
|
||||
# Skip if not open or not stale
|
||||
if issue["state"] != "open" or "stale" not in [label["name"] for label in issue["labels"]]:
|
||||
continue
|
||||
|
||||
# Get the timeline to find when the stale label was last added
|
||||
timeline_url = (
|
||||
f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/timeline"
|
||||
)
|
||||
response = requests.get(timeline_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
events = response.json()
|
||||
|
||||
# Find the most recent stale label addition
|
||||
stale_events = [
|
||||
event
|
||||
for event in events
|
||||
if event.get("event") == "labeled" and event.get("label", {}).get("name") == "stale"
|
||||
]
|
||||
|
||||
if not stale_events:
|
||||
continue
|
||||
|
||||
latest_stale = datetime.strptime(stale_events[-1]["created_at"], "%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
# Get comments since the stale label
|
||||
comments_url = (
|
||||
f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/comments"
|
||||
)
|
||||
response = requests.get(comments_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
comments = response.json()
|
||||
|
||||
# Check for comments newer than the stale label
|
||||
new_comments = [
|
||||
comment
|
||||
for comment in comments
|
||||
if datetime.strptime(comment["created_at"], "%Y-%m-%dT%H:%M:%SZ") > latest_stale
|
||||
]
|
||||
|
||||
if new_comments:
|
||||
print(f"\nFound new activity on stale issue #{issue['number']}: {issue['title']}")
|
||||
print(f" {len(new_comments)} new comments since stale label")
|
||||
|
||||
if not auto_yes:
|
||||
confirm = input("Remove stale label? (y/n): ")
|
||||
if confirm.lower() != "y":
|
||||
print("Skipping this issue.")
|
||||
continue
|
||||
|
||||
# Remove stale label but keep question label
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}"
|
||||
response = requests.patch(url, headers=headers, json={"labels": ["question"]})
|
||||
response.raise_for_status()
|
||||
print(f" Removed stale label from #{issue['number']}")
|
||||
else:
|
||||
# Check if it's been 7 days since stale label
|
||||
days_stale = (datetime.now() - latest_stale).days
|
||||
if days_stale >= 7:
|
||||
print(f"\nStale issue ready for closing #{issue['number']}: {issue['title']}")
|
||||
print(f" No activity for {days_stale} days since stale label")
|
||||
|
||||
if not auto_yes:
|
||||
confirm = input("Close this issue? (y/n): ")
|
||||
if confirm.lower() != "y":
|
||||
print("Skipping this issue.")
|
||||
continue
|
||||
|
||||
# Add closing comment
|
||||
comment_url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}/comments" # noqa
|
||||
response = requests.post(
|
||||
comment_url, headers=headers, json={"body": CLOSE_STALE_COMMENT}
|
||||
)
|
||||
response.raise_for_status()
|
||||
|
||||
# Close the issue
|
||||
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue['number']}"
|
||||
response = requests.patch(url, headers=headers, json={"state": "closed"})
|
||||
response.raise_for_status()
|
||||
print(f" Closed issue #{issue['number']}")
|
||||
|
||||
|
||||
def handle_duplicate_issues(all_issues, auto_yes):
|
||||
open_issues = [issue for issue in all_issues if issue["state"] == "open"]
|
||||
grouped_open_issues = group_issues_by_subject(open_issues)
|
||||
|
||||
print("Analyzing issues (skipping reopened issues)...")
|
||||
print("Looking for duplicate issues (skipping reopened issues)...")
|
||||
for subject, issues in grouped_open_issues.items():
|
||||
oldest_issue = find_oldest_issue(subject, all_issues)
|
||||
if not oldest_issue:
|
||||
@@ -149,14 +330,12 @@ def main():
|
||||
f" {oldest_issue['html_url']} ({oldest_issue['state']})"
|
||||
)
|
||||
|
||||
if not args.yes:
|
||||
# Confirmation prompt
|
||||
if not auto_yes:
|
||||
confirm = input("Do you want to comment and close duplicate issues? (y/n): ")
|
||||
if confirm.lower() != "y":
|
||||
print("Skipping this group of issues.")
|
||||
continue
|
||||
|
||||
# Comment and close duplicate issues
|
||||
for issue in issues:
|
||||
if issue["number"] != oldest_issue["number"]:
|
||||
comment_and_close_duplicate(issue, oldest_issue)
|
||||
@@ -165,5 +344,24 @@ def main():
|
||||
print(f"Oldest issue #{oldest_issue['number']} left open")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Handle duplicate GitHub issues")
|
||||
parser.add_argument(
|
||||
"--yes", action="store_true", help="Automatically close duplicates without prompting"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not TOKEN:
|
||||
print("Error: Missing GITHUB_TOKEN environment variable. Please check your .env file.")
|
||||
return
|
||||
|
||||
all_issues = get_issues("all")
|
||||
|
||||
handle_unlabeled_issues(all_issues, args.yes)
|
||||
handle_stale_issues(all_issues, args.yes)
|
||||
handle_stale_closing(all_issues, args.yes)
|
||||
handle_duplicate_issues(all_issues, args.yes)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -124,7 +124,10 @@ def main():
|
||||
for cmd in git_commands:
|
||||
print(f"Running: {' '.join(cmd)}")
|
||||
if not dry_run:
|
||||
subprocess.run(cmd, check=True)
|
||||
subprocess.run(
|
||||
cmd,
|
||||
check=True,
|
||||
)
|
||||
|
||||
new_dev_version = f"{incremented_version}.dev"
|
||||
updated_dev_content = re.sub(
|
||||
|
||||
@@ -91,10 +91,49 @@ def test_system_info(temp_data_dir):
|
||||
|
||||
def test_need_to_ask(temp_data_dir):
|
||||
analytics = Analytics()
|
||||
assert analytics.need_to_ask() is True
|
||||
assert analytics.need_to_ask(True) is True
|
||||
assert analytics.need_to_ask(False) is False
|
||||
|
||||
analytics.user_id = "111"
|
||||
assert analytics.need_to_ask(None) is False
|
||||
|
||||
analytics.user_id = "000"
|
||||
assert analytics.need_to_ask(None) is True
|
||||
|
||||
analytics.asked_opt_in = True
|
||||
assert analytics.need_to_ask() is False
|
||||
assert analytics.need_to_ask(True) is False
|
||||
|
||||
analytics.permanently_disable = True
|
||||
assert analytics.need_to_ask() is False
|
||||
assert analytics.need_to_ask(True) is False
|
||||
|
||||
|
||||
def test_is_uuid_in_percentage():
|
||||
analytics = Analytics()
|
||||
|
||||
# Test basic percentage thresholds
|
||||
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 1) is True
|
||||
assert analytics.is_uuid_in_percentage("01999000000000000000000000000000", 1) is True
|
||||
assert analytics.is_uuid_in_percentage("02000000000000000000000000000000", 1) is True
|
||||
assert analytics.is_uuid_in_percentage("02910000000000000000000000000001", 1) is False
|
||||
assert analytics.is_uuid_in_percentage("03000000000000000000000000000000", 1) is False
|
||||
assert analytics.is_uuid_in_percentage("ff000000000000000000000000000000", 1) is False
|
||||
|
||||
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 10) is True
|
||||
assert analytics.is_uuid_in_percentage("19000000000000000000000000000000", 10) is True
|
||||
assert analytics.is_uuid_in_percentage("1a000000000000000000000000000000", 10) is False
|
||||
assert analytics.is_uuid_in_percentage("ff000000000000000000000000000000", 10) is False
|
||||
|
||||
# Test edge cases
|
||||
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 0) is False
|
||||
assert analytics.is_uuid_in_percentage("00000000000000000000000000000000", 100) is True
|
||||
assert analytics.is_uuid_in_percentage("ffffffffffffffffffffffffffffffff", 100) is True
|
||||
|
||||
# Test invalid inputs
|
||||
with pytest.raises(ValueError):
|
||||
analytics.is_uuid_in_percentage("00000000000000000000000000000000", -1)
|
||||
with pytest.raises(ValueError):
|
||||
analytics.is_uuid_in_percentage("00000000000000000000000000000000", 101)
|
||||
|
||||
# Test empty/None UUID
|
||||
assert analytics.is_uuid_in_percentage("", 50) is False
|
||||
assert analytics.is_uuid_in_percentage(None, 50) is False
|
||||
|
||||
@@ -17,6 +17,8 @@ from aider.utils import GitTemporaryDirectory
|
||||
class TestCoder(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.GPT35 = Model("gpt-3.5-turbo")
|
||||
self.webbrowser_patcher = patch("aider.io.webbrowser.open")
|
||||
self.mock_webbrowser = self.webbrowser_patcher.start()
|
||||
|
||||
def test_allowed_to_edit(self):
|
||||
with GitTemporaryDirectory():
|
||||
|
||||
@@ -1068,8 +1068,10 @@ class TestCommands(TestCase):
|
||||
io.prompt_ask = lambda *args, **kwargs: "y"
|
||||
|
||||
# Test the cmd_run method with a command that should not raise an error
|
||||
result = commands.cmd_run("exit 1", add_on_nonzero_exit=True)
|
||||
self.assertIn("I ran this command", result)
|
||||
commands.cmd_run("exit 1", add_on_nonzero_exit=True)
|
||||
|
||||
# Check that the output was added to cur_messages
|
||||
self.assertTrue(any("exit 1" in msg["content"] for msg in coder.cur_messages))
|
||||
|
||||
def test_cmd_add_drop_untracked_files(self):
|
||||
with GitTemporaryDirectory():
|
||||
@@ -1210,6 +1212,26 @@ class TestCommands(TestCase):
|
||||
del commands
|
||||
del repo
|
||||
|
||||
def test_cmd_add_gitignored_file(self):
|
||||
with GitTemporaryDirectory():
|
||||
# Create a .gitignore file
|
||||
gitignore = Path(".gitignore")
|
||||
gitignore.write_text("*.ignored\n")
|
||||
|
||||
# Create a file that matches the gitignore pattern
|
||||
ignored_file = Path("test.ignored")
|
||||
ignored_file.write_text("This should be ignored")
|
||||
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
# Try to add the ignored file
|
||||
commands.cmd_add(str(ignored_file))
|
||||
|
||||
# Verify the file was not added
|
||||
self.assertEqual(len(coder.abs_fnames), 0)
|
||||
|
||||
def test_cmd_add_aiderignored_file(self):
|
||||
with GitTemporaryDirectory():
|
||||
repo = git.Repo()
|
||||
|
||||
@@ -10,6 +10,7 @@ from aider.coders import editblock_coder as eb
|
||||
from aider.dump import dump # noqa: F401
|
||||
from aider.io import InputOutput
|
||||
from aider.models import Model
|
||||
from aider.utils import ChdirTemporaryDirectory
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
@@ -341,6 +342,46 @@ These changes replace the `subprocess.run` patches with `subprocess.check_output
|
||||
result = eb.replace_most_similar_chunk(whole, part, replace)
|
||||
self.assertEqual(result, expected_output)
|
||||
|
||||
def test_create_new_file_with_other_file_in_chat(self):
|
||||
# https://github.com/Aider-AI/aider/issues/2258
|
||||
with ChdirTemporaryDirectory():
|
||||
# Create a few temporary files
|
||||
file1 = "file.txt"
|
||||
|
||||
with open(file1, "w", encoding="utf-8") as f:
|
||||
f.write("one\ntwo\nthree\n")
|
||||
|
||||
files = [file1]
|
||||
|
||||
# Initialize the Coder object with the mocked IO and mocked repo
|
||||
coder = Coder.create(
|
||||
self.GPT35, "diff", use_git=False, io=InputOutput(yes=True), fnames=files
|
||||
)
|
||||
|
||||
def mock_send(*args, **kwargs):
|
||||
coder.partial_response_content = f"""
|
||||
Do this:
|
||||
|
||||
newfile.txt
|
||||
<<<<<<< SEARCH
|
||||
=======
|
||||
creating a new file
|
||||
>>>>>>> REPLACE
|
||||
|
||||
"""
|
||||
coder.partial_response_function_call = dict()
|
||||
return []
|
||||
|
||||
coder.send = mock_send
|
||||
|
||||
coder.run(with_message="hi")
|
||||
|
||||
content = Path(file1).read_text(encoding="utf-8")
|
||||
self.assertEqual(content, "one\ntwo\nthree\n")
|
||||
|
||||
content = Path("newfile.txt").read_text(encoding="utf-8")
|
||||
self.assertEqual(content, "creating a new file\n")
|
||||
|
||||
def test_full_edit(self):
|
||||
# Create a few temporary files
|
||||
_, file1 = tempfile.mkstemp()
|
||||
|
||||
129
tests/basic/test_editor.py
Normal file
129
tests/basic/test_editor.py
Normal file
@@ -0,0 +1,129 @@
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from aider.editor import (
|
||||
DEFAULT_EDITOR_NIX,
|
||||
DEFAULT_EDITOR_OS_X,
|
||||
DEFAULT_EDITOR_WINDOWS,
|
||||
discover_editor,
|
||||
get_environment_editor,
|
||||
pipe_editor,
|
||||
print_status_message,
|
||||
write_temp_file,
|
||||
)
|
||||
|
||||
|
||||
def test_get_environment_editor():
|
||||
# Test with no environment variables set
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
assert get_environment_editor("default") == "default"
|
||||
|
||||
# Test EDITOR precedence
|
||||
with patch.dict(os.environ, {"EDITOR": "vim"}):
|
||||
assert get_environment_editor() == "vim"
|
||||
|
||||
# Test VISUAL overrides EDITOR
|
||||
with patch.dict(os.environ, {"EDITOR": "vim", "VISUAL": "code"}):
|
||||
assert get_environment_editor() == "code"
|
||||
|
||||
|
||||
def test_discover_editor_defaults():
|
||||
with patch("platform.system") as mock_system:
|
||||
# Test Windows default
|
||||
mock_system.return_value = "Windows"
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
assert discover_editor() == [DEFAULT_EDITOR_WINDOWS]
|
||||
|
||||
# Test macOS default
|
||||
mock_system.return_value = "Darwin"
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
assert discover_editor() == [DEFAULT_EDITOR_OS_X]
|
||||
|
||||
# Test Linux default
|
||||
mock_system.return_value = "Linux"
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
assert discover_editor() == [DEFAULT_EDITOR_NIX]
|
||||
|
||||
|
||||
def test_write_temp_file():
|
||||
# Test basic file creation
|
||||
content = "test content"
|
||||
filepath = write_temp_file(content)
|
||||
assert os.path.exists(filepath)
|
||||
with open(filepath, "r") as f:
|
||||
assert f.read() == content
|
||||
os.remove(filepath)
|
||||
|
||||
# Test with suffix
|
||||
filepath = write_temp_file("content", suffix="txt")
|
||||
assert filepath.endswith(".txt")
|
||||
os.remove(filepath)
|
||||
|
||||
# Test with prefix
|
||||
filepath = write_temp_file("content", prefix="test_")
|
||||
assert os.path.basename(filepath).startswith("test_")
|
||||
os.remove(filepath)
|
||||
|
||||
|
||||
def test_print_status_message(capsys):
|
||||
# Test success message
|
||||
print_status_message(True, "Success!")
|
||||
captured = capsys.readouterr()
|
||||
assert "Success!" in captured.out
|
||||
|
||||
# Test failure message
|
||||
print_status_message(False, "Failed!")
|
||||
captured = capsys.readouterr()
|
||||
assert "Failed!" in captured.out
|
||||
|
||||
|
||||
def test_discover_editor_override():
|
||||
# Test editor override
|
||||
assert discover_editor("code") == ["code"]
|
||||
assert discover_editor('vim -c "set noswapfile"') == ["vim", "-c", "set noswapfile"]
|
||||
|
||||
# Test invalid editor command
|
||||
with pytest.raises(RuntimeError):
|
||||
discover_editor('vim "unclosed quote')
|
||||
|
||||
|
||||
def test_pipe_editor():
|
||||
# Test with default editor
|
||||
test_content = "Initial content"
|
||||
modified_content = "Modified content"
|
||||
|
||||
# Mock the file operations and editor call
|
||||
with (
|
||||
patch("aider.editor.write_temp_file") as mock_write,
|
||||
patch("builtins.open") as mock_open,
|
||||
patch("os.remove") as mock_remove,
|
||||
):
|
||||
# Setup mocks
|
||||
mock_write.return_value = "temp.txt"
|
||||
mock_file = MagicMock()
|
||||
mock_file.__enter__.return_value.read.return_value = modified_content
|
||||
mock_open.return_value = mock_file
|
||||
|
||||
with patch("subprocess.call") as mock_subprocess:
|
||||
# Test with default editor
|
||||
result = pipe_editor(test_content)
|
||||
assert result == modified_content
|
||||
mock_write.assert_called_with(test_content, None)
|
||||
mock_subprocess.assert_called()
|
||||
|
||||
# Test with custom editor
|
||||
result = pipe_editor(test_content, editor="code")
|
||||
assert result == modified_content
|
||||
mock_subprocess.assert_called()
|
||||
|
||||
# Test with suffix
|
||||
result = pipe_editor(test_content, suffix="md")
|
||||
assert result == modified_content
|
||||
mock_write.assert_called_with(test_content, "md")
|
||||
|
||||
# Test cleanup on permission error
|
||||
mock_remove.side_effect = PermissionError
|
||||
result = pipe_editor(test_content)
|
||||
assert result == modified_content
|
||||
65
tests/basic/test_exceptions.py
Normal file
65
tests/basic/test_exceptions.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from aider.exceptions import ExInfo, LiteLLMExceptions
|
||||
|
||||
|
||||
def test_litellm_exceptions_load():
|
||||
"""Test that LiteLLMExceptions loads without errors"""
|
||||
ex = LiteLLMExceptions()
|
||||
assert len(ex.exceptions) > 0
|
||||
|
||||
|
||||
def test_exceptions_tuple():
|
||||
"""Test that exceptions_tuple returns a non-empty tuple"""
|
||||
ex = LiteLLMExceptions()
|
||||
assert isinstance(ex.exceptions_tuple(), tuple)
|
||||
assert len(ex.exceptions_tuple()) > 0
|
||||
|
||||
|
||||
def test_get_ex_info():
|
||||
"""Test get_ex_info returns correct ExInfo"""
|
||||
ex = LiteLLMExceptions()
|
||||
|
||||
# Test with a known exception type
|
||||
from litellm import AuthenticationError
|
||||
|
||||
auth_error = AuthenticationError(
|
||||
message="Invalid API key", llm_provider="openai", model="gpt-4"
|
||||
)
|
||||
ex_info = ex.get_ex_info(auth_error)
|
||||
assert isinstance(ex_info, ExInfo)
|
||||
assert ex_info.name == "AuthenticationError"
|
||||
assert ex_info.retry is False
|
||||
assert "API key" in ex_info.description
|
||||
|
||||
# Test with unknown exception type
|
||||
class UnknownError(Exception):
|
||||
pass
|
||||
|
||||
unknown = UnknownError()
|
||||
ex_info = ex.get_ex_info(unknown)
|
||||
assert isinstance(ex_info, ExInfo)
|
||||
assert ex_info.name is None
|
||||
assert ex_info.retry is None
|
||||
assert ex_info.description is None
|
||||
|
||||
|
||||
def test_rate_limit_error():
|
||||
"""Test specific handling of RateLimitError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import RateLimitError
|
||||
|
||||
rate_error = RateLimitError(message="Rate limit exceeded", llm_provider="openai", model="gpt-4")
|
||||
ex_info = ex.get_ex_info(rate_error)
|
||||
assert ex_info.retry is True
|
||||
assert "rate limited" in ex_info.description.lower()
|
||||
|
||||
|
||||
def test_context_window_error():
|
||||
"""Test specific handling of ContextWindowExceededError"""
|
||||
ex = LiteLLMExceptions()
|
||||
from litellm import ContextWindowExceededError
|
||||
|
||||
ctx_error = ContextWindowExceededError(
|
||||
message="Context length exceeded", model="gpt-4", llm_provider="openai"
|
||||
)
|
||||
ex_info = ex.get_ex_info(ctx_error)
|
||||
assert ex_info.retry is False
|
||||
@@ -32,7 +32,7 @@ class TestMain(TestCase):
|
||||
os.environ["HOME"] = self.homedir_obj.name
|
||||
self.input_patcher = patch("builtins.input", return_value=None)
|
||||
self.mock_input = self.input_patcher.start()
|
||||
self.webbrowser_patcher = patch("webbrowser.open")
|
||||
self.webbrowser_patcher = patch("aider.io.webbrowser.open")
|
||||
self.mock_webbrowser = self.webbrowser_patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
@@ -45,7 +45,7 @@ class TestMain(TestCase):
|
||||
self.webbrowser_patcher.stop()
|
||||
|
||||
def test_main_with_empty_dir_no_files_on_command(self):
|
||||
main(["--no-git", "--exit"], input=DummyInput(), output=DummyOutput())
|
||||
main(["--no-git", "--exit", "--yes"], input=DummyInput(), output=DummyOutput())
|
||||
|
||||
def test_main_with_emptqy_dir_new_file(self):
|
||||
main(["foo.txt", "--yes", "--no-git", "--exit"], input=DummyInput(), output=DummyOutput())
|
||||
@@ -332,7 +332,7 @@ class TestMain(TestCase):
|
||||
def test_false_vals_in_env_file(self):
|
||||
self.create_env_file(".env", "AIDER_SHOW_DIFFS=off")
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
main(["--no-git", "--yes"], input=DummyInput(), output=DummyOutput())
|
||||
MockCoder.assert_called_once()
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["show_diffs"], False)
|
||||
@@ -340,7 +340,7 @@ class TestMain(TestCase):
|
||||
def test_true_vals_in_env_file(self):
|
||||
self.create_env_file(".env", "AIDER_SHOW_DIFFS=on")
|
||||
with patch("aider.coders.Coder.create") as MockCoder:
|
||||
main(["--no-git"], input=DummyInput(), output=DummyOutput())
|
||||
main(["--no-git", "--yes"], input=DummyInput(), output=DummyOutput())
|
||||
MockCoder.assert_called_once()
|
||||
_, kwargs = MockCoder.call_args
|
||||
self.assertEqual(kwargs["show_diffs"], True)
|
||||
@@ -381,7 +381,11 @@ class TestMain(TestCase):
|
||||
def test_verbose_mode_lists_env_vars(self):
|
||||
self.create_env_file(".env", "AIDER_DARK_MODE=on")
|
||||
with patch("sys.stdout", new_callable=StringIO) as mock_stdout:
|
||||
main(["--no-git", "--verbose", "--exit"], input=DummyInput(), output=DummyOutput())
|
||||
main(
|
||||
["--no-git", "--verbose", "--exit", "--yes"],
|
||||
input=DummyInput(),
|
||||
output=DummyOutput(),
|
||||
)
|
||||
output = mock_stdout.getvalue()
|
||||
relevant_output = "\n".join(
|
||||
line
|
||||
|
||||
@@ -2,8 +2,10 @@ import unittest
|
||||
from unittest.mock import ANY, MagicMock, patch
|
||||
|
||||
from aider.models import (
|
||||
ANTHROPIC_BETA_HEADER,
|
||||
Model,
|
||||
ModelInfoManager,
|
||||
register_models,
|
||||
sanity_check_model,
|
||||
sanity_check_models,
|
||||
)
|
||||
@@ -80,7 +82,9 @@ class TestModels(unittest.TestCase):
|
||||
) # Should return True because there's a problem with the editor model
|
||||
mock_io.tool_warning.assert_called_with(ANY) # Ensure a warning was issued
|
||||
|
||||
warning_messages = [call.args[0] for call in mock_io.tool_warning.call_args_list]
|
||||
warning_messages = [
|
||||
warning_call.args[0] for warning_call in mock_io.tool_warning.call_args_list
|
||||
]
|
||||
print("Warning messages:", warning_messages) # Add this line
|
||||
|
||||
self.assertGreaterEqual(mock_io.tool_warning.call_count, 1) # Expect two warnings
|
||||
@@ -88,6 +92,57 @@ class TestModels(unittest.TestCase):
|
||||
any("bogus-model" in msg for msg in warning_messages)
|
||||
) # Check that one of the warnings mentions the bogus model
|
||||
|
||||
def test_aider_extra_model_settings(self):
|
||||
import tempfile
|
||||
|
||||
import yaml
|
||||
|
||||
# Create temporary YAML file with test settings
|
||||
test_settings = [
|
||||
{
|
||||
"name": "aider/extra_params",
|
||||
"extra_params": {
|
||||
"extra_headers": {"Foo": "bar"},
|
||||
"some_param": "some value",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# Write to a regular file instead of NamedTemporaryFile
|
||||
# for better cross-platform compatibility
|
||||
tmp = tempfile.mktemp(suffix=".yml")
|
||||
try:
|
||||
with open(tmp, "w") as f:
|
||||
yaml.dump(test_settings, f)
|
||||
|
||||
# Register the test settings
|
||||
register_models([tmp])
|
||||
|
||||
# Test that defaults are applied when no exact match
|
||||
model = Model("claude-3-5-sonnet-20240620")
|
||||
# Test that both the override and existing headers are present
|
||||
model = Model("claude-3-5-sonnet-20240620")
|
||||
self.assertEqual(model.extra_params["extra_headers"]["Foo"], "bar")
|
||||
self.assertEqual(
|
||||
model.extra_params["extra_headers"]["anthropic-beta"],
|
||||
ANTHROPIC_BETA_HEADER,
|
||||
)
|
||||
self.assertEqual(model.extra_params["some_param"], "some value")
|
||||
self.assertEqual(model.extra_params["max_tokens"], 8192)
|
||||
|
||||
# Test that exact match overrides defaults but not overrides
|
||||
model = Model("gpt-4")
|
||||
self.assertEqual(model.extra_params["extra_headers"]["Foo"], "bar")
|
||||
self.assertEqual(model.extra_params["some_param"], "some value")
|
||||
finally:
|
||||
# Clean up the temporary file
|
||||
import os
|
||||
|
||||
try:
|
||||
os.unlink(tmp)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -127,8 +127,9 @@ def test_git_index_version_greater_than_2(mock_browser, create_repo, mock_io):
|
||||
"You may be able to convert your repo: git update-index --index-version=2"
|
||||
)
|
||||
mock_io.tool_output.assert_any_call("Or run aider --no-git to proceed without using git.")
|
||||
mock_io.confirm_ask.assert_any_call(
|
||||
"Open documentation url for more info?", subject=urls.git_index_version
|
||||
mock_io.offer_url.assert_any_call(
|
||||
urls.git_index_version,
|
||||
"Open documentation url for more info?",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import httpx
|
||||
|
||||
from aider.exceptions import LiteLLMExceptions
|
||||
from aider.llm import litellm
|
||||
from aider.sendchat import retry_exceptions, simple_send_with_retries
|
||||
from aider.sendchat import send_completion, simple_send_with_retries
|
||||
|
||||
|
||||
class PrintCalled(Exception):
|
||||
@@ -12,9 +11,13 @@ class PrintCalled(Exception):
|
||||
|
||||
|
||||
class TestSendChat(unittest.TestCase):
|
||||
def test_retry_exceptions(self):
|
||||
"""Test that retry_exceptions() can be called without raising errors"""
|
||||
retry_exceptions() # Should not raise any exceptions
|
||||
def setUp(self):
|
||||
self.mock_messages = [{"role": "user", "content": "Hello"}]
|
||||
self.mock_model = "gpt-4"
|
||||
|
||||
def test_litellm_exceptions(self):
|
||||
litellm_ex = LiteLLMExceptions()
|
||||
litellm_ex._load(strict=True)
|
||||
|
||||
@patch("litellm.completion")
|
||||
@patch("builtins.print")
|
||||
@@ -24,7 +27,7 @@ class TestSendChat(unittest.TestCase):
|
||||
|
||||
# Set up the mock to raise
|
||||
mock_completion.side_effect = [
|
||||
litellm.exceptions.RateLimitError(
|
||||
litellm.RateLimitError(
|
||||
"rate limit exceeded",
|
||||
response=mock,
|
||||
llm_provider="llm_provider",
|
||||
@@ -35,17 +38,56 @@ class TestSendChat(unittest.TestCase):
|
||||
|
||||
# Call the simple_send_with_retries method
|
||||
simple_send_with_retries("model", ["message"])
|
||||
assert mock_print.call_count == 2
|
||||
assert mock_print.call_count == 3
|
||||
|
||||
@patch("litellm.completion")
|
||||
def test_send_completion_basic(self, mock_completion):
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_completion.return_value = mock_response
|
||||
|
||||
# Test basic send_completion
|
||||
hash_obj, response = send_completion(
|
||||
self.mock_model, self.mock_messages, functions=None, stream=False
|
||||
)
|
||||
|
||||
assert response == mock_response
|
||||
mock_completion.assert_called_once()
|
||||
|
||||
@patch("litellm.completion")
|
||||
def test_send_completion_with_functions(self, mock_completion):
|
||||
mock_function = {"name": "test_function", "parameters": {"type": "object"}}
|
||||
|
||||
hash_obj, response = send_completion(
|
||||
self.mock_model, self.mock_messages, functions=[mock_function], stream=False
|
||||
)
|
||||
|
||||
# Verify function was properly included in tools
|
||||
called_kwargs = mock_completion.call_args.kwargs
|
||||
assert "tools" in called_kwargs
|
||||
assert called_kwargs["tools"][0]["function"] == mock_function
|
||||
|
||||
@patch("litellm.completion")
|
||||
def test_simple_send_attribute_error(self, mock_completion):
|
||||
# Setup mock to raise AttributeError
|
||||
mock_completion.return_value = MagicMock()
|
||||
mock_completion.return_value.choices = None
|
||||
|
||||
# Should return None on AttributeError
|
||||
result = simple_send_with_retries(self.mock_model, self.mock_messages)
|
||||
assert result is None
|
||||
|
||||
@patch("litellm.completion")
|
||||
@patch("builtins.print")
|
||||
def test_simple_send_with_retries_connection_error(self, mock_print, mock_completion):
|
||||
# Set up the mock to raise
|
||||
mock_completion.side_effect = [
|
||||
httpx.ConnectError("Connection error"),
|
||||
None,
|
||||
]
|
||||
def test_simple_send_non_retryable_error(self, mock_print, mock_completion):
|
||||
# Test with an error that shouldn't trigger retries
|
||||
mock = MagicMock()
|
||||
mock.status_code = 400
|
||||
|
||||
# Call the simple_send_with_retries method
|
||||
simple_send_with_retries("model", ["message"])
|
||||
assert mock_print.call_count == 2
|
||||
mock_completion.side_effect = litellm.NotFoundError(
|
||||
message="Invalid request", llm_provider="test_provider", model="test_model"
|
||||
)
|
||||
|
||||
result = simple_send_with_retries(self.mock_model, self.mock_messages)
|
||||
assert result is None
|
||||
assert mock_print.call_count == 1
|
||||
|
||||
@@ -44,7 +44,7 @@ class TestScrape(unittest.TestCase):
|
||||
self.commands.io.tool_error = mock_print_error
|
||||
|
||||
# Run the cmd_web command
|
||||
result = self.commands.cmd_web("https://example.com")
|
||||
result = self.commands.cmd_web("https://example.com", return_content=True)
|
||||
|
||||
# Assert that the result contains some content
|
||||
self.assertIsNotNone(result)
|
||||
|
||||
Reference in New Issue
Block a user