mirror of
https://github.com/Aider-AI/aider
synced 2026-04-26 01:25:17 +02:00
Compare commits
837 Commits
v0.59.2.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 | ||
|
|
775011033f | ||
|
|
8ffe21a2dd | ||
|
|
73d63ef5ce | ||
|
|
e12b1a9184 | ||
|
|
d099a95b92 | ||
|
|
496ed90439 | ||
|
|
7883db1834 | ||
|
|
0cfc23b1a8 | ||
|
|
d33104aec1 | ||
|
|
711b2a431c | ||
|
|
09d21b5951 | ||
|
|
d9193387cc | ||
|
|
d5330ae2f3 | ||
|
|
571c1b47b5 | ||
|
|
0c9d4dd123 | ||
|
|
7d79408683 | ||
|
|
e6d4c3558b | ||
|
|
3be2109964 | ||
|
|
ce37ff26b5 | ||
|
|
03bbdb010f | ||
|
|
0bde1da42c | ||
|
|
987cb3bca9 | ||
|
|
0b11024967 | ||
|
|
dfaaedb466 | ||
|
|
8bc0d5544d | ||
|
|
c67e63bc09 | ||
|
|
129f5fae76 | ||
|
|
966a613ffe | ||
|
|
96ad107c19 | ||
|
|
ebdc126b00 | ||
|
|
5e1be966ed | ||
|
|
8801fda972 | ||
|
|
d8e9da35d6 | ||
|
|
0022c1a67e | ||
|
|
6a0380b8c0 | ||
|
|
305b4fee87 | ||
|
|
9e18fc55d3 | ||
|
|
ebc9a30cc6 | ||
|
|
f53f5927df | ||
|
|
78a8d19ac5 | ||
|
|
bfa9dce1e0 | ||
|
|
3b2f1f1e66 | ||
|
|
b2bcca967f | ||
|
|
929aeb22bf | ||
|
|
66ad186edd | ||
|
|
c098354a67 | ||
|
|
d67d319b31 | ||
|
|
93639039d4 | ||
|
|
789535cb85 | ||
|
|
d8e1e823e7 | ||
|
|
5b9fe6fee0 | ||
|
|
91bc8cddf3 | ||
|
|
392162ae84 | ||
|
|
0c4e4a123a | ||
|
|
27711010a0 | ||
|
|
401967c27f | ||
|
|
d1b25b9a72 | ||
|
|
df478e1f8f | ||
|
|
a459f0e130 | ||
|
|
66b6788990 | ||
|
|
624f9122ab | ||
|
|
704350286c | ||
|
|
ec40ff5987 | ||
|
|
af7aaad903 | ||
|
|
3794cf941f | ||
|
|
72c1f25005 | ||
|
|
c13536e641 | ||
|
|
7a9988c060 | ||
|
|
b9939d4bd9 | ||
|
|
b71a3148cf | ||
|
|
42abdb0a41 | ||
|
|
4c39e92773 | ||
|
|
b7f7204c4e | ||
|
|
8f73c15f48 | ||
|
|
17330e53c3 | ||
|
|
3fcd79e165 | ||
|
|
750b12282f | ||
|
|
94c5ff2fd2 | ||
|
|
85ad2826da | ||
|
|
15c62e3e43 | ||
|
|
fea7134064 | ||
|
|
da706d9eb8 | ||
|
|
9a37c0491d | ||
|
|
1d3d6a589f | ||
|
|
c5a439e4e8 | ||
|
|
2b7584b651 | ||
|
|
1ef60b5a40 | ||
|
|
b79321da51 | ||
|
|
6829b29ef4 | ||
|
|
a9c0d20347 | ||
|
|
25a906950c | ||
|
|
740534dcff | ||
|
|
de65d86122 | ||
|
|
98bf9bd26d | ||
|
|
ade615c445 | ||
|
|
d959e1c60d | ||
|
|
8d2f66fe34 | ||
|
|
66dc8dbccd | ||
|
|
b228bcab8c | ||
|
|
ccbf1482c1 | ||
|
|
f9a0946472 | ||
|
|
faa80b7699 | ||
|
|
1eb2c724a5 | ||
|
|
c43e7f998e | ||
|
|
3e2454b84b | ||
|
|
2817766cf5 | ||
|
|
ba7656bc1c | ||
|
|
c14392a35a | ||
|
|
6bfb258473 | ||
|
|
1e872599fd | ||
|
|
c08b7f9c22 | ||
|
|
6e4ccf8715 | ||
|
|
e4913669d4 | ||
|
|
8e098752bd | ||
|
|
6867dab89c | ||
|
|
09a9fac91e | ||
|
|
2fd1681fab | ||
|
|
ece91dc724 | ||
|
|
01bf154151 | ||
|
|
b5916981b3 | ||
|
|
a68b87272b | ||
|
|
88b55df1c6 | ||
|
|
676c017eb1 | ||
|
|
139b8a2d4a | ||
|
|
24c68928d6 | ||
|
|
f957111141 | ||
|
|
e94e60b1d2 | ||
|
|
a565a63436 | ||
|
|
a045bda171 | ||
|
|
a3d78e0944 | ||
|
|
8082cbed98 | ||
|
|
aba8b5d00a | ||
|
|
3d5a4d9303 | ||
|
|
a899b0e27e | ||
|
|
267872b7e4 | ||
|
|
bd59a8debf | ||
|
|
c0e137889c | ||
|
|
5b84b901b2 | ||
|
|
059883abf7 | ||
|
|
0b622a6fd7 | ||
|
|
97989dd51a | ||
|
|
d81c421bfe | ||
|
|
bce586f510 | ||
|
|
a70b364842 | ||
|
|
920e8da57c | ||
|
|
71a8b286dc | ||
|
|
ee4decc50b | ||
|
|
63330aa833 | ||
|
|
15a0eb976f | ||
|
|
4f52ad385a | ||
|
|
cd1496f91b | ||
|
|
31babf39cd | ||
|
|
fe3dbba1d9 | ||
|
|
763724ed4e | ||
|
|
9e0e68caf9 | ||
|
|
52f697e513 | ||
|
|
aeca62bcf6 | ||
|
|
7e574bc214 | ||
|
|
e9beb1336c | ||
|
|
4ac8386313 | ||
|
|
f0233455d2 | ||
|
|
5c28dd039c | ||
|
|
26d1ab7a5f | ||
|
|
068fb38a5d | ||
|
|
53e7eba00b | ||
|
|
854d908fe0 | ||
|
|
ea3359fb4b | ||
|
|
55a2ba4bd6 | ||
|
|
20ca9c84c7 | ||
|
|
0d86124b15 | ||
|
|
554fa98c48 | ||
|
|
eb92fa4f88 | ||
|
|
0424e4b00a | ||
|
|
adca062081 | ||
|
|
ef0fcb8f38 | ||
|
|
0feed0047c | ||
|
|
7f027ff6e5 | ||
|
|
e25a46c892 | ||
|
|
68916b1186 | ||
|
|
e9771588e4 | ||
|
|
e1d55c82b1 | ||
|
|
5b6be29c1c | ||
|
|
28d9f6f8da | ||
|
|
3ccae4eff7 | ||
|
|
3ad240a10e | ||
|
|
87a31a583a | ||
|
|
7553104433 | ||
|
|
f9604633e6 | ||
|
|
eb9a73bdb0 | ||
|
|
d75e75190d | ||
|
|
94a49e601c | ||
|
|
3df3d86295 | ||
|
|
63f8979f2b | ||
|
|
ad94e49ef5 | ||
|
|
b40ff2a601 | ||
|
|
a4be01b474 | ||
|
|
c5dc44a73f | ||
|
|
358cbc9388 | ||
|
|
2e5fa9dea4 | ||
|
|
717592463e | ||
|
|
347a9f2a6d | ||
|
|
d288122fab | ||
|
|
d1cf3d4600 | ||
|
|
5188872791 | ||
|
|
a57a06e8c7 | ||
|
|
347ad34038 | ||
|
|
ccdd333ba3 | ||
|
|
56f3220d4c | ||
|
|
26a85c2047 | ||
|
|
f800ce1e5a | ||
|
|
d4103cc271 | ||
|
|
c3ba3b6f48 | ||
|
|
be54df4084 | ||
|
|
d06c6f8557 | ||
|
|
a876561ea0 | ||
|
|
6dc0b8d853 | ||
|
|
460311d49e | ||
|
|
94396070e8 | ||
|
|
4a3cb8dc95 | ||
|
|
f9005451fa | ||
|
|
16e292b1fd | ||
|
|
7d37793765 | ||
|
|
9e9c162a16 | ||
|
|
8d6db81a40 | ||
|
|
425bd8932b | ||
|
|
38820701be | ||
|
|
1bc74676ff | ||
|
|
0c37f002c9 | ||
|
|
18c41b6128 | ||
|
|
5876af4e94 | ||
|
|
e1d145013a | ||
|
|
143eeff4da | ||
|
|
3a7e4bac34 | ||
|
|
f5ca162576 | ||
|
|
dbefa6b010 | ||
|
|
79cdff6163 | ||
|
|
b111fc357c | ||
|
|
2af48e159c | ||
|
|
6cec44e402 | ||
|
|
fc6c01a9a5 | ||
|
|
01439875af | ||
|
|
fcdf998fac | ||
|
|
ed4ad45e3d | ||
|
|
907c1dbe2b | ||
|
|
3baad86afd | ||
|
|
0351924628 | ||
|
|
29250f82ed | ||
|
|
a819bf1d64 | ||
|
|
bc515cf74a | ||
|
|
f9c45432e6 | ||
|
|
54d55c857b | ||
|
|
8e2a4b47d6 | ||
|
|
513f06be46 | ||
|
|
0fb79917ff | ||
|
|
cd133f95ee | ||
|
|
3d66b53791 | ||
|
|
bf63e7045b | ||
|
|
e2dff0a74b | ||
|
|
bf60e58d5b | ||
|
|
e3a3a55dd5 | ||
|
|
1ed2eefff4 | ||
|
|
df65e87ef3 | ||
|
|
ddb876d2fe | ||
|
|
a080581329 | ||
|
|
f70a82791b | ||
|
|
5d3a60228c | ||
|
|
fd820dbfe2 | ||
|
|
b911ceaa61 | ||
|
|
9edf9b4186 | ||
|
|
be74259df6 | ||
|
|
29293cc8ac | ||
|
|
9e7995b730 | ||
|
|
ba9fdaa755 | ||
|
|
dde2dee304 | ||
|
|
55a99143c2 | ||
|
|
ba2bee61de | ||
|
|
5fd8fb15b9 | ||
|
|
8908a48b68 | ||
|
|
43fbda0faf | ||
|
|
97a9c65125 | ||
|
|
8a63b1b3ce | ||
|
|
3c658ac66f | ||
|
|
055d529947 | ||
|
|
9507f3c008 | ||
|
|
e0a1eefe0a | ||
|
|
ea37ba8346 | ||
|
|
8ea789c29d | ||
|
|
901e4397cb | ||
|
|
bcf781da73 | ||
|
|
a68178fd1b | ||
|
|
e18b8b0a29 | ||
|
|
62ff822ee8 | ||
|
|
c9993dccc8 | ||
|
|
6181fe5efa | ||
|
|
dd6124fa64 | ||
|
|
239302a219 | ||
|
|
e0949bff69 | ||
|
|
e0c603c764 | ||
|
|
8f62f99db7 | ||
|
|
b46de7127d | ||
|
|
e9627cb3c6 | ||
|
|
eb8fdf483e | ||
|
|
a6d5fa9cda | ||
|
|
1bef71f57a | ||
|
|
63c393e269 | ||
|
|
1efb0ba53e | ||
|
|
4a3e6ef1e5 | ||
|
|
a800d219c7 | ||
|
|
7f962b5bb0 | ||
|
|
57642cf96c | ||
|
|
c2e49be096 | ||
|
|
c26b2e4859 | ||
|
|
9ce0757835 | ||
|
|
c80a032297 | ||
|
|
bd28d8f3fb | ||
|
|
748fd0cf12 | ||
|
|
ac6f495377 | ||
|
|
3b14eb98a5 | ||
|
|
914bf3d82b | ||
|
|
949b50bd69 | ||
|
|
d234d5e3ec | ||
|
|
1b530f9200 | ||
|
|
3c4dad7eb4 | ||
|
|
cfcb6656cb | ||
|
|
ae54ef57ae | ||
|
|
cb306b61cd | ||
|
|
46269f2e9b | ||
|
|
7c9f9be7d8 | ||
|
|
ef45fe1015 | ||
|
|
eaaf05d964 | ||
|
|
3ce1cfb908 | ||
|
|
9e0a9e919b | ||
|
|
671f3078c1 | ||
|
|
67d538a499 | ||
|
|
b69d131810 | ||
|
|
949b7ece36 | ||
|
|
d1e35bcdd1 | ||
|
|
34ec197199 | ||
|
|
5a1cf67ea3 | ||
|
|
e427e2da05 | ||
|
|
2cfd4d719a | ||
|
|
709633de72 | ||
|
|
bf46f6dc8b | ||
|
|
259a18f3dd | ||
|
|
4c3aea4bfc | ||
|
|
6c595c261c | ||
|
|
ecaf1e674d | ||
|
|
e76704e261 | ||
|
|
e092081c56 | ||
|
|
4d6db0cb09 | ||
|
|
0729a725ad | ||
|
|
b7a884d81e | ||
|
|
90e6941de5 | ||
|
|
c7a05d75fd | ||
|
|
ce253fec8f | ||
|
|
6bb9b2567f | ||
|
|
26058c89fe | ||
|
|
692f6a7b85 | ||
|
|
163a29b026 | ||
|
|
06534fa91e | ||
|
|
bf45a14b30 | ||
|
|
0a497b7fd7 | ||
|
|
e1f85aa19e | ||
|
|
2f307e46e2 | ||
|
|
22203124e9 | ||
|
|
7e881927d6 | ||
|
|
a4e359daf4 | ||
|
|
ef2c165399 | ||
|
|
deb27f8f27 | ||
|
|
e51d8a403f | ||
|
|
27ddf8aeab | ||
|
|
b915b1834d | ||
|
|
cd3e0ae914 | ||
|
|
e820fea289 | ||
|
|
fc63faa737 | ||
|
|
65b50cdaee | ||
|
|
464c3e29e1 | ||
|
|
f65bf9aee7 | ||
|
|
80f7e56785 | ||
|
|
0cb05dd001 | ||
|
|
0fe5247d4c | ||
|
|
91a013afb3 | ||
|
|
3bc056a107 | ||
|
|
456c163cb9 | ||
|
|
f3ff24e35a | ||
|
|
1d2a3f573c | ||
|
|
cbdabd3ae9 | ||
|
|
c24ec7f230 | ||
|
|
3146d285bf | ||
|
|
bde93903de | ||
|
|
e487351c8d | ||
|
|
10ecb4b97f | ||
|
|
04a2cbb494 | ||
|
|
30238dbbd5 | ||
|
|
0aad7b46f6 | ||
|
|
62d8190733 | ||
|
|
4759297b67 | ||
|
|
607a9a8c86 | ||
|
|
96b67ab26c | ||
|
|
93b8cb9cec | ||
|
|
7b6ad16fdb | ||
|
|
b722572a28 | ||
|
|
46489f1a46 | ||
|
|
d7a29c42b7 | ||
|
|
1ebe5f8bd5 | ||
|
|
26fe63b7ac | ||
|
|
d2435c2e00 | ||
|
|
4d04a35e73 | ||
|
|
cabad84521 | ||
|
|
aeadf2f139 | ||
|
|
4ebbfa01b7 | ||
|
|
960b456e70 | ||
|
|
329514289d | ||
|
|
03f14dfe47 | ||
|
|
1c321df457 | ||
|
|
8a21eb7804 | ||
|
|
c1d4adbebf | ||
|
|
ef933c207b | ||
|
|
08aaae4d36 | ||
|
|
f52265362f | ||
|
|
00aa43d964 | ||
|
|
84489f16b5 | ||
|
|
e07194bbeb | ||
|
|
1567d3e3d1 | ||
|
|
eca7a57138 | ||
|
|
64df0ad590 | ||
|
|
5a28d499a8 | ||
|
|
4bad876f66 | ||
|
|
1a8763d989 | ||
|
|
13eaf5e5ce | ||
|
|
82250db8af | ||
|
|
85fa78f5a6 | ||
|
|
9ed732959e | ||
|
|
860a36fe6c | ||
|
|
01a9a8ffc4 | ||
|
|
aa840f0e28 | ||
|
|
a7a626423c | ||
|
|
c2c9b60ea6 | ||
|
|
a6282818db | ||
|
|
f563544761 | ||
|
|
48a344bc6d | ||
|
|
f110e8c8db | ||
|
|
65c0608d5c | ||
|
|
57ce0dca67 | ||
|
|
4129065d6c | ||
|
|
d59fd508c2 | ||
|
|
62a5cf8dee | ||
|
|
6ec4e60058 | ||
|
|
087b3d4ffb | ||
|
|
7d3585bafe | ||
|
|
1a49974f98 | ||
|
|
b11c17dbd4 | ||
|
|
474ac62391 | ||
|
|
6ee8a74d47 | ||
|
|
121dd908a6 | ||
|
|
2e1ac25ce2 | ||
|
|
b49ee06f23 |
6
.github/workflows/pages.yml
vendored
6
.github/workflows/pages.yml
vendored
@@ -70,15 +70,15 @@ jobs:
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v2
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install linkchecker
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install linkchecker
|
||||
python -m pip install linkchecker
|
||||
|
||||
- name: Run linkchecker
|
||||
run: |
|
||||
|
||||
@@ -33,12 +33,9 @@ ensure that your contributions can be integrated smoothly.
|
||||
|
||||
## Licensing
|
||||
|
||||
By contributing to this project, you agree that your contributions
|
||||
will be licensed under the Apache License 2.0. Additionally, you
|
||||
understand and agree that contributions may be subject to a different
|
||||
license, should the project maintainers decide to change the licensing
|
||||
terms.
|
||||
|
||||
Before contributing a PR, please review our
|
||||
[Individual Contributor License Agreement](https://aider.chat/docs/legal/contributor-agreement.html).
|
||||
All contributors will be asked to complete the agreement as part of the PR process.
|
||||
|
||||
## Setting up a Development Environment
|
||||
|
||||
@@ -190,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
|
||||
|
||||
103
HISTORY.md
103
HISTORY.md
@@ -1,7 +1,108 @@
|
||||
|
||||
# Release history
|
||||
|
||||
### v0.59.0
|
||||
### 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
|
||||
- Scored 75% on [aider's code editing leaderboard](https://aider.chat/docs/leaderboards/).
|
||||
- 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.
|
||||
- Bugfix for creating new files.
|
||||
- Aider wrote 84% of the code in this release.
|
||||
|
||||
### Aider v0.61.0
|
||||
|
||||
- Load and save aider slash-commands to files:
|
||||
- `/save <fname>` command will make a file of `/add` and `/read-only` commands that recreate the current file context in the chat.
|
||||
- `/load <fname>` will replay the commands in the file.
|
||||
- You can use `/load` to run any arbitrary set of slash-commands, not just `/add` and `/read-only`.
|
||||
- Use `--load <fname>` to run a list of commands on launch, before the interactive chat begins.
|
||||
- Anonymous, opt-in [analytics](https://aider.chat/docs/more/analytics.html) with no personal data sharing.
|
||||
- Aider follows litellm's `supports_vision` attribute to enable image support for models.
|
||||
- Bugfix for when diff mode flexibly handles the model using the wrong filename.
|
||||
- Displays filenames in sorted order for `/add` and `/read-only`.
|
||||
- New `--no-fancy-input` switch disables prompt toolkit input, now still available with `--no-pretty`.
|
||||
- Override browser config with `--no-browser` or `--no-gui`.
|
||||
- Offer to open documentation URLs when errors occur.
|
||||
- Properly support all o1 models, regardless of provider.
|
||||
- Improved layout of filenames above input prompt.
|
||||
- Better handle corrupted repomap tags cache.
|
||||
- Improved handling of API errors, especially when accessing the weak model.
|
||||
- Aider wrote 68% of the code in this release.
|
||||
|
||||
### Aider v0.60.1
|
||||
|
||||
- Enable image support for Sonnet 10/22.
|
||||
- Display filenames in sorted order.
|
||||
|
||||
### Aider v0.60.0
|
||||
|
||||
- Full support for Sonnet 10/22, the new SOTA model on aider's code editing benchmark.
|
||||
- Aider uses Sonnet 10/22 by default.
|
||||
- Improved formatting of added and read-only files above chat prompt, by @jbellis.
|
||||
- Improved support for o1 models by more flexibly parsing their nonconforming code edit replies.
|
||||
- Corrected diff edit format prompt that only the first match is replaced.
|
||||
- Stronger whole edit format prompt asking for clean file names.
|
||||
- Now offers to add `.env` to the `.gitignore` file.
|
||||
- Ships with a small model metadata json file to handle models not yet updated in litellm.
|
||||
- Model settings for o1 models on azure.
|
||||
- Bugfix to properly include URLs in `/help` RAG results.
|
||||
- Aider wrote 49% of the code in this release.
|
||||
|
||||
### Aider v0.59.1
|
||||
|
||||
- Check for obsolete `yes: true` in yaml config, show helpful error.
|
||||
- Model settings for openrouter/anthropic/claude-3.5-sonnet:beta
|
||||
|
||||
### Aider v0.59.0
|
||||
|
||||
- Improvements to `/read-only`:
|
||||
- Now supports shell-style auto-complete of the full file system.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
try:
|
||||
from aider.__version__ import __version__
|
||||
except Exception:
|
||||
__version__ = "0.59.2.dev"
|
||||
__version__ = "0.64.1.dev"
|
||||
|
||||
__all__ = [__version__]
|
||||
|
||||
203
aider/analytics.py
Normal file
203
aider/analytics.py
Normal file
@@ -0,0 +1,203 @@
|
||||
import json
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
from mixpanel import Mixpanel
|
||||
from posthog import Posthog
|
||||
|
||||
from aider import __version__
|
||||
from aider.dump import dump # noqa: F401
|
||||
from aider.models import model_info_manager
|
||||
|
||||
mixpanel_project_token = "6da9a43058a5d1b9f3353153921fb04d"
|
||||
posthog_project_api_key = "phc_99T7muzafUMMZX15H8XePbMSreEUzahHbtWjy3l5Qbv"
|
||||
posthog_host = "https://us.i.posthog.com"
|
||||
|
||||
|
||||
class Analytics:
|
||||
# providers
|
||||
mp = None
|
||||
ph = None
|
||||
|
||||
# saved
|
||||
user_id = None
|
||||
permanently_disable = None
|
||||
asked_opt_in = None
|
||||
|
||||
# ephemeral
|
||||
logfile = None
|
||||
|
||||
def __init__(self, logfile=None, permanently_disable=False):
|
||||
self.logfile = logfile
|
||||
self.get_or_create_uuid()
|
||||
|
||||
if self.permanently_disable or permanently_disable or not self.asked_opt_in:
|
||||
self.disable(permanently_disable)
|
||||
|
||||
def enable(self):
|
||||
if not self.user_id:
|
||||
self.disable(False)
|
||||
return
|
||||
|
||||
if self.permanently_disable:
|
||||
self.disable(True)
|
||||
return
|
||||
|
||||
if not self.asked_opt_in:
|
||||
self.disable(False)
|
||||
return
|
||||
|
||||
self.mp = Mixpanel(mixpanel_project_token)
|
||||
self.ph = Posthog(project_api_key=posthog_project_api_key, host=posthog_host)
|
||||
|
||||
def disable(self, permanently):
|
||||
self.mp = None
|
||||
self.ph = None
|
||||
|
||||
if permanently:
|
||||
self.asked_opt_in = True
|
||||
self.permanently_disable = True
|
||||
self.save_data()
|
||||
|
||||
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"
|
||||
data_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
return data_file
|
||||
|
||||
def get_or_create_uuid(self):
|
||||
self.load_data()
|
||||
if self.user_id:
|
||||
return
|
||||
|
||||
self.user_id = str(uuid.uuid4())
|
||||
self.save_data()
|
||||
|
||||
def load_data(self):
|
||||
data_file = self.get_data_file_path()
|
||||
if data_file.exists():
|
||||
try:
|
||||
data = json.loads(data_file.read_text())
|
||||
self.permanently_disable = data.get("permanently_disable")
|
||||
self.user_id = data.get("uuid")
|
||||
self.asked_opt_in = data.get("asked_opt_in", False)
|
||||
except (json.decoder.JSONDecodeError, OSError):
|
||||
self.disable(permanently=False)
|
||||
|
||||
def save_data(self):
|
||||
data_file = self.get_data_file_path()
|
||||
data = dict(
|
||||
uuid=self.user_id,
|
||||
permanently_disable=self.permanently_disable,
|
||||
asked_opt_in=self.asked_opt_in,
|
||||
)
|
||||
|
||||
# Allow exceptions; crash if we can't record permanently_disabled=True, etc
|
||||
data_file.write_text(json.dumps(data, indent=4))
|
||||
|
||||
def get_system_info(self):
|
||||
return {
|
||||
"python_version": sys.version.split()[0],
|
||||
"os_platform": platform.system(),
|
||||
"os_release": platform.release(),
|
||||
"machine": platform.machine(),
|
||||
}
|
||||
|
||||
def _redact_model_name(self, model):
|
||||
if not model:
|
||||
return None
|
||||
|
||||
info = model_info_manager.get_model_from_cached_json_db(model.name)
|
||||
if info:
|
||||
return model.name
|
||||
elif "/" in model.name:
|
||||
return model.name.split("/")[0] + "/REDACTED"
|
||||
return None
|
||||
|
||||
def event(self, event_name, main_model=None, **kwargs):
|
||||
if not (self.mp or self.ph) and not self.logfile:
|
||||
return
|
||||
|
||||
properties = {}
|
||||
|
||||
if main_model:
|
||||
properties["main_model"] = self._redact_model_name(main_model)
|
||||
properties["weak_model"] = self._redact_model_name(main_model.weak_model)
|
||||
properties["editor_model"] = self._redact_model_name(main_model.editor_model)
|
||||
|
||||
properties.update(kwargs)
|
||||
properties.update(self.get_system_info()) # Add system info to all events
|
||||
|
||||
# Handle numeric values
|
||||
for key, value in properties.items():
|
||||
if isinstance(value, (int, float)):
|
||||
properties[key] = value
|
||||
else:
|
||||
properties[key] = str(value)
|
||||
|
||||
properties["aider_version"] = __version__
|
||||
|
||||
if self.mp:
|
||||
self.mp.track(self.user_id, event_name, dict(properties))
|
||||
|
||||
if self.ph:
|
||||
self.ph.capture(self.user_id, event_name, dict(properties))
|
||||
|
||||
if self.logfile:
|
||||
log_entry = {
|
||||
"event": event_name,
|
||||
"properties": properties,
|
||||
"user_id": self.user_id,
|
||||
"time": int(time.time()),
|
||||
}
|
||||
with open(self.logfile, "a") as f:
|
||||
json.dump(log_entry, f)
|
||||
f.write("\n")
|
||||
|
||||
def __del__(self):
|
||||
if self.ph:
|
||||
self.ph.shutdown()
|
||||
@@ -58,7 +58,7 @@ def get_parser(default_config_files, git_root):
|
||||
const=opus_model,
|
||||
help=f"Use {opus_model} model for the main chat",
|
||||
)
|
||||
sonnet_model = "claude-3-5-sonnet-20240620"
|
||||
sonnet_model = "claude-3-5-sonnet-20241022"
|
||||
group.add_argument(
|
||||
"--sonnet",
|
||||
action="store_const",
|
||||
@@ -66,6 +66,14 @@ def get_parser(default_config_files, git_root):
|
||||
const=sonnet_model,
|
||||
help=f"Use {sonnet_model} model for the main chat",
|
||||
)
|
||||
haiku_model = "claude-3-5-haiku-20241022"
|
||||
group.add_argument(
|
||||
"--haiku",
|
||||
action="store_const",
|
||||
dest="model",
|
||||
const=haiku_model,
|
||||
help=f"Use {haiku_model} model for the main chat",
|
||||
)
|
||||
gpt_4_model = "gpt-4-0613"
|
||||
group.add_argument(
|
||||
"--4",
|
||||
@@ -237,8 +245,8 @@ def get_parser(default_config_files, git_root):
|
||||
type=int,
|
||||
default=None,
|
||||
help=(
|
||||
"Maximum number of tokens to use for chat history. If not specified, uses the model's"
|
||||
" max_chat_history_tokens."
|
||||
"Soft limit on tokens for chat history, after which summarization begins."
|
||||
" If unspecified, defaults to the model's max_chat_history_tokens."
|
||||
),
|
||||
)
|
||||
# This is a duplicate of the argument in the preparser and is a no-op by this time of
|
||||
@@ -550,6 +558,25 @@ def get_parser(default_config_files, git_root):
|
||||
)
|
||||
|
||||
##########
|
||||
group = parser.add_argument_group("Analytics")
|
||||
group.add_argument(
|
||||
"--analytics",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=None,
|
||||
help="Enable/disable analytics for current session (default: random)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--analytics-log",
|
||||
metavar="ANALYTICS_LOG_FILE",
|
||||
help="Specify a file to log analytics events",
|
||||
)
|
||||
group.add_argument(
|
||||
"--analytics-disable",
|
||||
action="store_true",
|
||||
help="Permanently disable analytics",
|
||||
default=False,
|
||||
)
|
||||
|
||||
group = parser.add_argument_group("Other Settings")
|
||||
group.add_argument(
|
||||
"--file",
|
||||
@@ -593,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",
|
||||
@@ -611,6 +644,12 @@ def get_parser(default_config_files, git_root):
|
||||
metavar="FILE",
|
||||
help="Apply the changes from the given file instead of running the chat (debug)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--apply-clipboard-edits",
|
||||
action="store_true",
|
||||
help="Apply clipboard contents as edits using the main model's editor format",
|
||||
default=False,
|
||||
)
|
||||
group.add_argument(
|
||||
"--yes-always",
|
||||
action="store_true",
|
||||
@@ -660,6 +699,11 @@ def get_parser(default_config_files, git_root):
|
||||
" (disables chat mode)"
|
||||
),
|
||||
)
|
||||
group.add_argument(
|
||||
"--load",
|
||||
metavar="LOAD_FILE",
|
||||
help="Load and execute /commands from a file on launch",
|
||||
)
|
||||
group.add_argument(
|
||||
"--encoding",
|
||||
default="utf-8",
|
||||
@@ -678,8 +722,8 @@ def get_parser(default_config_files, git_root):
|
||||
group.add_argument(
|
||||
"--gui",
|
||||
"--browser",
|
||||
action="store_true",
|
||||
help="Run aider in your browser",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Run aider in your browser (default: False)",
|
||||
default=False,
|
||||
)
|
||||
group.add_argument(
|
||||
@@ -688,6 +732,16 @@ def get_parser(default_config_files, git_root):
|
||||
default=True,
|
||||
help="Enable/disable suggesting shell commands (default: True)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--fancy-input",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
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")
|
||||
@@ -719,7 +773,6 @@ def get_md_help():
|
||||
parser.formatter_class = MarkdownHelpFormatter
|
||||
|
||||
return argparse.ArgumentParser.format_help(parser)
|
||||
return parser.format_help()
|
||||
|
||||
|
||||
def get_sample_yaml():
|
||||
@@ -733,7 +786,6 @@ def get_sample_yaml():
|
||||
parser.formatter_class = YamlHelpFormatter
|
||||
|
||||
return argparse.ArgumentParser.format_help(parser)
|
||||
return parser.format_help()
|
||||
|
||||
|
||||
def get_sample_dotenv():
|
||||
@@ -747,7 +799,6 @@ def get_sample_dotenv():
|
||||
parser.formatter_class = DotEnvFormatter
|
||||
|
||||
return argparse.ArgumentParser.format_help(parser)
|
||||
return parser.format_help()
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -17,9 +17,12 @@ from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from json.decoder import JSONDecodeError
|
||||
from pathlib import Path
|
||||
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
|
||||
@@ -27,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_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
|
||||
@@ -258,12 +261,17 @@ class Coder:
|
||||
commands=None,
|
||||
summarizer=None,
|
||||
total_cost=0.0,
|
||||
analytics=None,
|
||||
map_refresh="auto",
|
||||
cache_prompts=False,
|
||||
num_cache_warming_pings=0,
|
||||
suggest_shell_commands=True,
|
||||
chat_language=None,
|
||||
):
|
||||
# Fill in a dummy Analytics if needed, but it is never .enable()'d
|
||||
self.analytics = analytics if analytics is not None else Analytics()
|
||||
|
||||
self.event = self.analytics.event
|
||||
self.chat_language = chat_language
|
||||
self.commit_before_message = []
|
||||
self.aider_commit_hashes = set()
|
||||
@@ -347,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
|
||||
@@ -679,7 +690,7 @@ class Coder:
|
||||
return chat_files_messages
|
||||
|
||||
def get_images_message(self):
|
||||
if not self.main_model.accepts_images:
|
||||
if not self.main_model.info.get("supports_vision"):
|
||||
return None
|
||||
|
||||
image_messages = []
|
||||
@@ -782,18 +793,37 @@ class Coder:
|
||||
self.num_reflections += 1
|
||||
message = self.reflected_message
|
||||
|
||||
def check_for_urls(self, inp):
|
||||
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)
|
||||
|
||||
if friendly_msg:
|
||||
self.io.tool_warning(text)
|
||||
self.io.tool_error(f"{friendly_msg}")
|
||||
else:
|
||||
self.io.tool_error(text)
|
||||
|
||||
url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*)")
|
||||
urls = list(set(url_pattern.findall(text))) # Use set to remove duplicates
|
||||
for url in urls:
|
||||
url = url.rstrip(".',\"")
|
||||
self.io.offer_url(url)
|
||||
return urls
|
||||
|
||||
def check_for_urls(self, inp: str) -> List[str]:
|
||||
"""Check input for URLs and offer to add them to the chat."""
|
||||
url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*[^\s,.])")
|
||||
urls = list(set(url_pattern.findall(inp))) # Use set to remove duplicates
|
||||
added_urls = []
|
||||
group = ConfirmGroup(urls)
|
||||
for url in urls:
|
||||
if url not in self.rejected_urls:
|
||||
url = url.rstrip(".',\"")
|
||||
if self.io.confirm_ask(
|
||||
"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)
|
||||
@@ -929,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
|
||||
|
||||
@@ -1120,6 +1156,8 @@ class Coder:
|
||||
|
||||
retry_delay = 0.125
|
||||
|
||||
litellm_ex = LiteLLMExceptions()
|
||||
|
||||
self.usage_report = None
|
||||
exhausted = False
|
||||
interrupted = False
|
||||
@@ -1128,24 +1166,37 @@ class Coder:
|
||||
try:
|
||||
yield from self.send(messages, functions=self.functions)
|
||||
break
|
||||
except retry_exceptions() as err:
|
||||
self.io.tool_warning(str(err))
|
||||
retry_delay *= 2
|
||||
if retry_delay > 60:
|
||||
except litellm_ex.exceptions_tuple() as err:
|
||||
ex_info = litellm_ex.get_ex_info(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)
|
||||
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"):
|
||||
@@ -1161,9 +1212,10 @@ class Coder:
|
||||
dict(role="assistant", content=self.multi_response_content, prefix=True)
|
||||
)
|
||||
except Exception as err:
|
||||
self.io.tool_error(f"Unexpected error: {err}")
|
||||
self.mdstream = None
|
||||
lines = traceback.format_exception(type(err), err, err.__traceback__)
|
||||
self.io.tool_error("".join(lines))
|
||||
self.io.tool_warning("".join(lines))
|
||||
self.io.tool_error(str(err))
|
||||
return
|
||||
finally:
|
||||
if self.mdstream:
|
||||
@@ -1193,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"
|
||||
@@ -1247,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
|
||||
|
||||
@@ -1308,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 = ""
|
||||
@@ -1556,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
|
||||
@@ -1638,11 +1689,27 @@ class Coder:
|
||||
self.usage_report = tokens_report + sep + cost_report
|
||||
|
||||
def show_usage_report(self):
|
||||
if self.usage_report:
|
||||
self.io.tool_output(self.usage_report)
|
||||
self.message_cost = 0.0
|
||||
self.message_tokens_sent = 0
|
||||
self.message_tokens_received = 0
|
||||
if not self.usage_report:
|
||||
return
|
||||
|
||||
self.io.tool_output(self.usage_report)
|
||||
|
||||
prompt_tokens = self.message_tokens_sent
|
||||
completion_tokens = self.message_tokens_received
|
||||
self.event(
|
||||
"message_send",
|
||||
main_model=self.main_model,
|
||||
edit_format=self.edit_format,
|
||||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=completion_tokens,
|
||||
total_tokens=prompt_tokens + completion_tokens,
|
||||
cost=self.message_cost,
|
||||
total_cost=self.total_cost,
|
||||
)
|
||||
|
||||
self.message_cost = 0.0
|
||||
self.message_tokens_sent = 0
|
||||
self.message_tokens_received = 0
|
||||
|
||||
def get_multi_response_content(self, final=False):
|
||||
cur = self.multi_response_content or ""
|
||||
@@ -1717,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}")
|
||||
@@ -1811,8 +1882,10 @@ class Coder:
|
||||
edited = set()
|
||||
try:
|
||||
edits = self.get_edits()
|
||||
edits = self.apply_edits_dry_run(edits)
|
||||
edits = self.prepare_to_edit(edits)
|
||||
edited = set(edit[0] for edit in edits)
|
||||
|
||||
self.apply_edits(edits)
|
||||
except ValueError as err:
|
||||
self.num_malformed_responses += 1
|
||||
@@ -1940,6 +2013,9 @@ class Coder:
|
||||
def apply_edits(self, edits):
|
||||
return
|
||||
|
||||
def apply_edits_dry_run(self, edits):
|
||||
return edits
|
||||
|
||||
def run_shell_commands(self):
|
||||
if not self.suggest_shell_commands:
|
||||
return ""
|
||||
@@ -1985,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
|
||||
|
||||
@@ -35,29 +35,47 @@ class EditBlockCoder(Coder):
|
||||
|
||||
return edits
|
||||
|
||||
def apply_edits(self, edits):
|
||||
def apply_edits_dry_run(self, edits):
|
||||
return self.apply_edits(edits, dry_run=True)
|
||||
|
||||
def apply_edits(self, edits, dry_run=False):
|
||||
failed = []
|
||||
passed = []
|
||||
updated_edits = []
|
||||
|
||||
for edit in edits:
|
||||
path, original, updated = edit
|
||||
full_path = self.abs_root_path(path)
|
||||
content = self.io.read_text(full_path)
|
||||
new_content = do_replace(full_path, content, original, updated, self.fence)
|
||||
if not new_content:
|
||||
new_content = None
|
||||
|
||||
if Path(full_path).exists():
|
||||
content = self.io.read_text(full_path)
|
||||
new_content = do_replace(full_path, content, original, updated, self.fence)
|
||||
|
||||
# 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)
|
||||
new_content = do_replace(full_path, content, original, updated, self.fence)
|
||||
if new_content:
|
||||
path = self.get_rel_fname(full_path)
|
||||
break
|
||||
|
||||
updated_edits.append((path, original, updated))
|
||||
|
||||
if new_content:
|
||||
self.io.write_text(full_path, new_content)
|
||||
if not dry_run:
|
||||
self.io.write_text(full_path, new_content)
|
||||
passed.append(edit)
|
||||
else:
|
||||
failed.append(edit)
|
||||
|
||||
if dry_run:
|
||||
return updated_edits
|
||||
|
||||
if not failed:
|
||||
return
|
||||
|
||||
@@ -365,9 +383,9 @@ def do_replace(fname, content, before_text, after_text, fence=None):
|
||||
return new_content
|
||||
|
||||
|
||||
HEAD = r"<{5,9} SEARCH"
|
||||
DIVIDER = r"={5,9}"
|
||||
UPDATED = r">{5,9} REPLACE"
|
||||
HEAD = r"^<{5,9} SEARCH\s*$"
|
||||
DIVIDER = r"^={5,9}\s*$"
|
||||
UPDATED = r"^>{5,9} REPLACE\s*$"
|
||||
|
||||
HEAD_ERR = "<<<<<<< SEARCH"
|
||||
DIVIDER_ERR = "======="
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -159,8 +159,9 @@ Use the *FULL* file path, as shown to you by the user.
|
||||
Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
|
||||
If the file contains code or other data wrapped/escaped in json/xml/quotes or other containers, you need to propose edits to the literal contents of the file, including the container markup.
|
||||
|
||||
*SEARCH/REPLACE* blocks will replace *all* matching occurrences.
|
||||
Include enough lines to make the SEARCH blocks uniquely match the lines to change.
|
||||
*SEARCH/REPLACE* blocks will *only* replace the first match occurrence.
|
||||
Including multiple unique *SEARCH/REPLACE* blocks if needed.
|
||||
Include enough lines in each SEARCH section to uniquely match each set of lines that need to change.
|
||||
|
||||
Keep *SEARCH/REPLACE* blocks concise.
|
||||
Break large *SEARCH/REPLACE* blocks into a series of smaller blocks that each change a small portion of the file.
|
||||
|
||||
@@ -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:
|
||||
@@ -52,7 +52,7 @@ path/to/filename.js
|
||||
{fence[1]}
|
||||
|
||||
Every *file listing* MUST use this format:
|
||||
- First line: the filename with any originally provided path
|
||||
- First line: the filename with any originally provided path; no extra markup, punctuation, comments, etc. **JUST** the filename with path.
|
||||
- Second line: opening {fence[0]}
|
||||
- ... entire content of the file ...
|
||||
- Final line: closing {fence[1]}
|
||||
|
||||
@@ -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 "/!"
|
||||
@@ -223,6 +238,7 @@ class Commands:
|
||||
|
||||
def run(self, inp):
|
||||
if inp.startswith("!"):
|
||||
self.coder.event("command_run")
|
||||
return self.do_run("run", inp[1:])
|
||||
|
||||
res = self.matching_commands(inp)
|
||||
@@ -230,9 +246,13 @@ class Commands:
|
||||
return
|
||||
matching_commands, first_word, rest_inp = res
|
||||
if len(matching_commands) == 1:
|
||||
return self.do_run(matching_commands[0][1:], rest_inp)
|
||||
command = matching_commands[0][1:]
|
||||
self.coder.event(f"command_{command}")
|
||||
return self.do_run(command, rest_inp)
|
||||
elif first_word in matching_commands:
|
||||
return self.do_run(first_word[1:], rest_inp)
|
||||
command = first_word[1:]
|
||||
self.coder.event(f"command_{command}")
|
||||
return self.do_run(command, rest_inp)
|
||||
elif len(matching_commands) > 1:
|
||||
self.io.tool_error(f"Ambiguous command: {', '.join(matching_commands)}")
|
||||
else:
|
||||
@@ -654,7 +674,7 @@ class Commands:
|
||||
else:
|
||||
try:
|
||||
raw_matched_files = list(Path(self.coder.root).glob(pattern))
|
||||
except IndexError:
|
||||
except (IndexError, AttributeError):
|
||||
raw_matched_files = []
|
||||
except ValueError as err:
|
||||
self.io.tool_error(f"Error matching {pattern}: {err}")
|
||||
@@ -724,7 +744,7 @@ class Commands:
|
||||
except OSError as e:
|
||||
self.io.tool_error(f"Error creating file {fname}: {e}")
|
||||
|
||||
for matched_file in all_matched_files:
|
||||
for matched_file in sorted(all_matched_files):
|
||||
abs_file_path = self.coder.abs_root_path(matched_file)
|
||||
|
||||
if not abs_file_path.startswith(self.coder.root) and not is_image_file(matched_file):
|
||||
@@ -733,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
|
||||
@@ -748,7 +772,9 @@ class Commands:
|
||||
f"Cannot add {matched_file} as it's not part of the repository"
|
||||
)
|
||||
else:
|
||||
if is_image_file(matched_file) and not self.coder.main_model.accepts_images:
|
||||
if is_image_file(matched_file) and not self.coder.main_model.info.get(
|
||||
"supports_vision"
|
||||
):
|
||||
self.io.tool_error(
|
||||
f"Cannot add image file {matched_file} as the"
|
||||
f" {self.coder.main_model.name} does not support images."
|
||||
@@ -852,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
|
||||
@@ -860,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"
|
||||
@@ -961,6 +972,7 @@ class Commands:
|
||||
self.basic_help()
|
||||
return
|
||||
|
||||
self.coder.event("interactive help")
|
||||
from aider.coders import Coder
|
||||
|
||||
if not self.help:
|
||||
@@ -1163,25 +1175,41 @@ class Commands:
|
||||
return
|
||||
|
||||
filenames = parse_quoted_filenames(args)
|
||||
all_paths = []
|
||||
|
||||
# First collect all expanded paths
|
||||
for pattern in filenames:
|
||||
# Expand tilde for home directory
|
||||
expanded_pattern = expanduser(pattern)
|
||||
if os.path.isabs(expanded_pattern):
|
||||
# For absolute paths, glob it
|
||||
matches = list(glob.glob(expanded_pattern))
|
||||
else:
|
||||
# For relative paths and globs, use glob from the root directory
|
||||
matches = list(Path(self.coder.root).glob(expanded_pattern))
|
||||
|
||||
expanded_paths = glob.glob(expanded_pattern, recursive=True)
|
||||
if not expanded_paths:
|
||||
if not matches:
|
||||
self.io.tool_error(f"No matches found for: {pattern}")
|
||||
continue
|
||||
else:
|
||||
all_paths.extend(matches)
|
||||
|
||||
for path in expanded_paths:
|
||||
abs_path = self.coder.abs_root_path(path)
|
||||
if os.path.isfile(abs_path):
|
||||
self._add_read_only_file(abs_path, path)
|
||||
elif os.path.isdir(abs_path):
|
||||
self._add_read_only_directory(abs_path, path)
|
||||
else:
|
||||
self.io.tool_error(f"Not a file or directory: {abs_path}")
|
||||
# Then process them in sorted order
|
||||
for path in sorted(all_paths):
|
||||
abs_path = self.coder.abs_root_path(path)
|
||||
if os.path.isfile(abs_path):
|
||||
self._add_read_only_file(abs_path, path)
|
||||
elif os.path.isdir(abs_path):
|
||||
self._add_read_only_directory(abs_path, path)
|
||||
else:
|
||||
self.io.tool_error(f"Not a file or directory: {abs_path}")
|
||||
|
||||
def _add_read_only_file(self, abs_path, original_name):
|
||||
if is_image_file(original_name) and not self.coder.main_model.info.get("supports_vision"):
|
||||
self.io.tool_error(
|
||||
f"Cannot add image file {original_name} as the"
|
||||
f" {self.coder.main_model.name} does not support images."
|
||||
)
|
||||
return
|
||||
|
||||
if abs_path in self.coder.abs_read_only_fnames:
|
||||
self.io.tool_error(f"{original_name} is already in the chat as a read-only file")
|
||||
return
|
||||
@@ -1235,6 +1263,63 @@ class Commands:
|
||||
output = f"{announcements}\n{settings}"
|
||||
self.io.tool_output(output)
|
||||
|
||||
def completions_raw_load(self, document, complete_event):
|
||||
return self.completions_raw_read_only(document, complete_event)
|
||||
|
||||
def cmd_load(self, args):
|
||||
"Load and execute commands from a file"
|
||||
if not args.strip():
|
||||
self.io.tool_error("Please provide a filename containing commands to load.")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(args.strip(), "r", encoding=self.io.encoding, errors="replace") as f:
|
||||
commands = f.readlines()
|
||||
except FileNotFoundError:
|
||||
self.io.tool_error(f"File not found: {args}")
|
||||
return
|
||||
except Exception as e:
|
||||
self.io.tool_error(f"Error reading file: {e}")
|
||||
return
|
||||
|
||||
for cmd in commands:
|
||||
cmd = cmd.strip()
|
||||
if not cmd or cmd.startswith("#"):
|
||||
continue
|
||||
|
||||
self.io.tool_output(f"\nExecuting: {cmd}")
|
||||
self.run(cmd)
|
||||
|
||||
def completions_raw_save(self, document, complete_event):
|
||||
return self.completions_raw_read_only(document, complete_event)
|
||||
|
||||
def cmd_save(self, args):
|
||||
"Save commands to a file that can reconstruct the current chat session's files"
|
||||
if not args.strip():
|
||||
self.io.tool_error("Please provide a filename to save the commands to.")
|
||||
return
|
||||
|
||||
try:
|
||||
with open(args.strip(), "w", encoding=self.io.encoding) as f:
|
||||
f.write("/drop\n")
|
||||
# Write commands to add editable files
|
||||
for fname in sorted(self.coder.abs_fnames):
|
||||
rel_fname = self.coder.get_rel_fname(fname)
|
||||
f.write(f"/add {rel_fname}\n")
|
||||
|
||||
# Write commands to add read-only files
|
||||
for fname in sorted(self.coder.abs_read_only_fnames):
|
||||
# Use absolute path for files outside repo root, relative path for files inside
|
||||
if Path(fname).is_relative_to(self.coder.root):
|
||||
rel_fname = self.coder.get_rel_fname(fname)
|
||||
f.write(f"/read-only {rel_fname}\n")
|
||||
else:
|
||||
f.write(f"/read-only {fname}\n")
|
||||
|
||||
self.io.tool_output(f"Saved commands to {args.strip()}")
|
||||
except Exception as e:
|
||||
self.io.tool_error(f"Error saving commands to file: {e}")
|
||||
|
||||
def cmd_copy(self, args):
|
||||
"Copy the last assistant message to the clipboard"
|
||||
all_messages = self.coder.done_messages + self.coder.cur_messages
|
||||
@@ -1276,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))
|
||||
@@ -40,24 +40,45 @@ def get_package_files():
|
||||
|
||||
|
||||
def fname_to_url(filepath):
|
||||
website = "website/"
|
||||
index = "/index.md"
|
||||
website = "website"
|
||||
index = "index.md"
|
||||
md = ".md"
|
||||
|
||||
docid = ""
|
||||
if filepath.startswith("website/_includes/"):
|
||||
pass
|
||||
elif filepath.startswith(website):
|
||||
docid = filepath[len(website) :]
|
||||
# Convert backslashes to forward slashes for consistency
|
||||
filepath = filepath.replace("\\", "/")
|
||||
|
||||
if filepath.endswith(index):
|
||||
filepath = filepath[: -len(index)] + "/"
|
||||
elif filepath.endswith(md):
|
||||
filepath = filepath[: -len(md)] + ".html"
|
||||
# Convert to Path object for easier manipulation
|
||||
path = Path(filepath)
|
||||
|
||||
docid = "https://aider.chat/" + filepath
|
||||
# Split the path into parts
|
||||
parts = path.parts
|
||||
|
||||
return docid
|
||||
# Find the 'website' part in the path
|
||||
try:
|
||||
website_index = [p.lower() for p in parts].index(website.lower())
|
||||
except ValueError:
|
||||
return "" # 'website' not found in the path
|
||||
|
||||
# Extract the part of the path starting from 'website'
|
||||
relevant_parts = parts[website_index + 1 :]
|
||||
|
||||
# Handle _includes directory
|
||||
if relevant_parts and relevant_parts[0].lower() == "_includes":
|
||||
return ""
|
||||
|
||||
# Join the remaining parts
|
||||
url_path = "/".join(relevant_parts)
|
||||
|
||||
# Handle index.md and other .md files
|
||||
if url_path.lower().endswith(index.lower()):
|
||||
url_path = url_path[: -len(index)]
|
||||
elif url_path.lower().endswith(md.lower()):
|
||||
url_path = url_path[: -len(md)] + ".html"
|
||||
|
||||
# Ensure the URL starts and ends with '/'
|
||||
url_path = url_path.strip("/")
|
||||
|
||||
return f"https://aider.chat/{url_path}"
|
||||
|
||||
|
||||
def get_index():
|
||||
|
||||
@@ -7,4 +7,5 @@ exclude_website_pats = [
|
||||
"docs/unified-diffs.md",
|
||||
"docs/leaderboards/index.md",
|
||||
"assets/**",
|
||||
"**/.DS_Store",
|
||||
]
|
||||
|
||||
170
aider/io.py
170
aider/io.py
@@ -1,8 +1,11 @@
|
||||
import base64
|
||||
import os
|
||||
import time
|
||||
import webbrowser
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
|
||||
from prompt_toolkit.completion import Completer, Completion, ThreadedCompleter
|
||||
@@ -15,6 +18,7 @@ from prompt_toolkit.shortcuts import CompleteStyle, PromptSession
|
||||
from prompt_toolkit.styles import Style
|
||||
from pygments.lexers import MarkdownLexer, guess_lexer_for_filename
|
||||
from pygments.token import Token
|
||||
from rich.columns import Columns
|
||||
from rich.console import Console
|
||||
from rich.markdown import Markdown
|
||||
from rich.style import Style as RichStyle
|
||||
@@ -192,6 +196,7 @@ class InputOutput:
|
||||
dry_run=False,
|
||||
llm_history_file=None,
|
||||
editingmode=EditingMode.EMACS,
|
||||
fancy_input=True,
|
||||
):
|
||||
self.never_prompts = set()
|
||||
self.editingmode = editingmode
|
||||
@@ -234,15 +239,16 @@ class InputOutput:
|
||||
self.append_chat_history(f"\n# aider chat started at {current_time}\n\n")
|
||||
|
||||
self.prompt_session = None
|
||||
if self.pretty:
|
||||
if fancy_input:
|
||||
# Initialize PromptSession
|
||||
session_kwargs = {
|
||||
"input": self.input,
|
||||
"output": self.output,
|
||||
"lexer": PygmentsLexer(MarkdownLexer),
|
||||
"editing_mode": self.editingmode,
|
||||
"cursor": ModalCursorShapeConfig(),
|
||||
}
|
||||
if self.editingmode == EditingMode.VI:
|
||||
session_kwargs["cursor"] = ModalCursorShapeConfig()
|
||||
if self.input_history_file is not None:
|
||||
session_kwargs["history"] = FileHistory(self.input_history_file)
|
||||
try:
|
||||
@@ -328,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:
|
||||
@@ -358,7 +386,10 @@ class InputOutput:
|
||||
rel_fnames = list(rel_fnames)
|
||||
show = ""
|
||||
if rel_fnames:
|
||||
show = " ".join(rel_fnames) + "\n"
|
||||
rel_read_only_fnames = [
|
||||
get_rel_fname(fname, root) for fname in (abs_read_only_fnames or [])
|
||||
]
|
||||
show = self.format_files_for_input(rel_fnames, rel_read_only_fnames)
|
||||
if edit_format:
|
||||
show += edit_format
|
||||
show += "> "
|
||||
@@ -410,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:
|
||||
@@ -432,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:
|
||||
@@ -450,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:
|
||||
@@ -477,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,
|
||||
@@ -689,9 +757,55 @@ class InputOutput:
|
||||
try:
|
||||
with self.chat_history_file.open("a", encoding=self.encoding, errors="ignore") as f:
|
||||
f.write(text)
|
||||
except (PermissionError, OSError):
|
||||
self.tool_error(
|
||||
f"Warning: Unable to write to chat history file {self.chat_history_file}."
|
||||
" Permission denied."
|
||||
)
|
||||
except (PermissionError, OSError) as err:
|
||||
print(f"Warning: Unable to write to chat history file {self.chat_history_file}.")
|
||||
print(err)
|
||||
self.chat_history_file = None # Disable further attempts to write
|
||||
|
||||
def format_files_for_input(self, rel_fnames, rel_read_only_fnames):
|
||||
if not self.pretty:
|
||||
read_only_files = []
|
||||
for full_path in sorted(rel_read_only_fnames or []):
|
||||
read_only_files.append(f"{full_path} (read only)")
|
||||
|
||||
editable_files = []
|
||||
for full_path in sorted(rel_fnames):
|
||||
if full_path in rel_read_only_fnames:
|
||||
continue
|
||||
editable_files.append(f"{full_path}")
|
||||
|
||||
return "\n".join(read_only_files + editable_files) + "\n"
|
||||
|
||||
output = StringIO()
|
||||
console = Console(file=output, force_terminal=False)
|
||||
|
||||
read_only_files = sorted(rel_read_only_fnames or [])
|
||||
editable_files = [f for f in sorted(rel_fnames) if f not in rel_read_only_fnames]
|
||||
|
||||
if 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:
|
||||
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()
|
||||
|
||||
|
||||
def get_rel_fname(fname, root):
|
||||
try:
|
||||
return os.path.relpath(fname, root)
|
||||
except ValueError:
|
||||
return fname
|
||||
|
||||
@@ -221,7 +221,12 @@ def basic_lint(fname, code):
|
||||
|
||||
tree = parser.parse(bytes(code, "utf-8"))
|
||||
|
||||
errors = traverse_tree(tree.root_node)
|
||||
try:
|
||||
errors = traverse_tree(tree.root_node)
|
||||
except RecursionError:
|
||||
print(f"Unable to lint {fname} due to RecursionError")
|
||||
return
|
||||
|
||||
if not errors:
|
||||
return
|
||||
|
||||
|
||||
189
aider/main.py
189
aider/main.py
@@ -5,13 +5,16 @@ import re
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
|
||||
import git
|
||||
import importlib_resources
|
||||
from dotenv import load_dotenv
|
||||
from prompt_toolkit.enums import EditingMode
|
||||
|
||||
from aider import __version__, models, urls, utils
|
||||
from aider.analytics import Analytics
|
||||
from aider.args import get_parser
|
||||
from aider.coders import Coder
|
||||
from aider.commands import Commands, SwitchCoder
|
||||
@@ -57,7 +60,7 @@ def guessed_wrong_repo(io, git_root, fnames, git_dname):
|
||||
|
||||
try:
|
||||
check_repo = Path(GitRepo(io, fnames, git_dname).root).resolve()
|
||||
except FileNotFoundError:
|
||||
except (OSError,) + ANY_GIT_ERROR:
|
||||
return
|
||||
|
||||
# we had no guess, rely on the "true" repo result
|
||||
@@ -85,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:
|
||||
@@ -131,32 +144,39 @@ def check_gitignore(git_root, io, ask=True):
|
||||
|
||||
try:
|
||||
repo = git.Repo(git_root)
|
||||
if repo.ignored(".aider"):
|
||||
if repo.ignored(".aider") and repo.ignored(".env"):
|
||||
return
|
||||
except ANY_GIT_ERROR:
|
||||
pass
|
||||
|
||||
pat = ".aider*"
|
||||
patterns = [".aider*", ".env"]
|
||||
patterns_to_add = []
|
||||
|
||||
gitignore_file = Path(git_root) / ".gitignore"
|
||||
if gitignore_file.exists():
|
||||
content = io.read_text(gitignore_file)
|
||||
if content is None:
|
||||
return
|
||||
if pat in content.splitlines():
|
||||
return
|
||||
existing_lines = content.splitlines()
|
||||
for pat in patterns:
|
||||
if pat not in existing_lines:
|
||||
patterns_to_add.append(pat)
|
||||
else:
|
||||
content = ""
|
||||
patterns_to_add = patterns
|
||||
|
||||
if ask and not io.confirm_ask(f"Add {pat} to .gitignore (recommended)?"):
|
||||
if not patterns_to_add:
|
||||
return
|
||||
|
||||
if ask and not io.confirm_ask(f"Add {', '.join(patterns_to_add)} to .gitignore (recommended)?"):
|
||||
return
|
||||
|
||||
if content and not content.endswith("\n"):
|
||||
content += "\n"
|
||||
content += pat + "\n"
|
||||
content += "\n".join(patterns_to_add) + "\n"
|
||||
io.write_text(gitignore_file, content)
|
||||
|
||||
io.tool_output(f"Added {pat} to .gitignore")
|
||||
io.tool_output(f"Added {', '.join(patterns_to_add)} to .gitignore")
|
||||
|
||||
|
||||
def check_streamlit_install(io):
|
||||
@@ -186,7 +206,10 @@ def launch_gui(args):
|
||||
"--server.runOnSave=false",
|
||||
]
|
||||
|
||||
if "-dev" in __version__:
|
||||
# https://github.com/Aider-AI/aider/issues/2193
|
||||
is_dev = "-dev" in str(__version__)
|
||||
|
||||
if is_dev:
|
||||
print("Watching for file changes.")
|
||||
else:
|
||||
st_args += [
|
||||
@@ -234,16 +257,23 @@ def parse_lint_cmds(lint_cmds, io):
|
||||
return res
|
||||
|
||||
|
||||
def generate_search_path_list(default_fname, git_root, command_line_file):
|
||||
def generate_search_path_list(default_file, git_root, command_line_file):
|
||||
files = []
|
||||
default_file = Path(default_fname)
|
||||
files.append(Path.home() / default_file) # homedir
|
||||
if git_root:
|
||||
files.append(Path(git_root) / default_file) # git root
|
||||
files.append(default_file.resolve())
|
||||
files.append(default_file)
|
||||
if command_line_file:
|
||||
files.append(command_line_file)
|
||||
files = [Path(fn).resolve() for fn in files]
|
||||
|
||||
resolved_files = []
|
||||
for fn in files:
|
||||
try:
|
||||
resolved_files.append(Path(fn).resolve())
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
files = resolved_files
|
||||
files.reverse()
|
||||
uniq = []
|
||||
for fn in files:
|
||||
@@ -303,7 +333,13 @@ 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(
|
||||
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
|
||||
)
|
||||
|
||||
@@ -343,7 +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.")
|
||||
io.tool_output(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?")
|
||||
@@ -404,6 +440,10 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
# Parse again to include any arguments that might have been defined in .env
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.analytics_disable:
|
||||
analytics = Analytics(permanently_disable=True)
|
||||
print("Analytics have been permanently disabled.")
|
||||
|
||||
if not args.verify_ssl:
|
||||
import httpx
|
||||
|
||||
@@ -453,6 +493,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
encoding=args.encoding,
|
||||
llm_history_file=args.llm_history_file,
|
||||
editingmode=editing_mode,
|
||||
fancy_input=args.fancy_input,
|
||||
)
|
||||
|
||||
io = get_io(args.pretty)
|
||||
@@ -464,9 +505,35 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
io = get_io(False)
|
||||
io.tool_warning("Terminal does not support pretty output (UnicodeDecodeError)")
|
||||
|
||||
analytics = Analytics(logfile=args.analytics_log, permanently_disable=args.analytics_disable)
|
||||
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."
|
||||
)
|
||||
io.tool_output(f"For more info: {urls.analytics}")
|
||||
disable = not io.confirm_ask(
|
||||
"Allow collection of anonymous analytics to help improve aider?"
|
||||
)
|
||||
|
||||
analytics.asked_opt_in = True
|
||||
if disable:
|
||||
analytics.disable(permanently=True)
|
||||
io.tool_output("Analytics have been permanently disabled.")
|
||||
|
||||
analytics.save_data()
|
||||
io.tool_output()
|
||||
|
||||
# This is a no-op if the user has opted out
|
||||
analytics.enable()
|
||||
|
||||
analytics.event("launched")
|
||||
|
||||
if args.gui and not return_coder:
|
||||
if not check_streamlit_install(io):
|
||||
return
|
||||
analytics.event("gui session")
|
||||
launch_gui(argv)
|
||||
return
|
||||
|
||||
@@ -476,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:
|
||||
@@ -539,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
|
||||
@@ -561,7 +636,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
if not args.model:
|
||||
args.model = "gpt-4o-2024-08-06"
|
||||
if os.environ.get("ANTHROPIC_API_KEY"):
|
||||
args.model = "claude-3-5-sonnet-20240620"
|
||||
args.model = "claude-3-5-sonnet-20241022"
|
||||
|
||||
main_model = models.Model(
|
||||
args.model,
|
||||
@@ -581,11 +656,12 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
if args.show_model_warnings:
|
||||
problem = models.sanity_check_models(io, main_model)
|
||||
if problem:
|
||||
analytics.event("model warning", main_model=main_model)
|
||||
io.tool_output("You can skip this check with --no-show-model-warnings")
|
||||
io.tool_output()
|
||||
|
||||
try:
|
||||
if not io.confirm_ask("Proceed anyway?"):
|
||||
return 1
|
||||
io.offer_url(urls.model_warnings, "Open documentation url for more info?")
|
||||
io.tool_output()
|
||||
except KeyboardInterrupt:
|
||||
return 1
|
||||
|
||||
@@ -613,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(
|
||||
@@ -654,6 +736,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
test_cmd=args.test_cmd,
|
||||
commands=commands,
|
||||
summarizer=summarizer,
|
||||
analytics=analytics,
|
||||
map_refresh=args.map_refresh,
|
||||
cache_prompts=args.cache_prompts,
|
||||
map_mul_no_files=args.map_multiplier_no_files,
|
||||
@@ -712,12 +795,28 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
coder.apply_updates()
|
||||
return
|
||||
|
||||
if args.apply_clipboard_edits:
|
||||
args.edit_format = main_model.editor_edit_format
|
||||
args.message = "/paste"
|
||||
|
||||
if "VSCODE_GIT_IPC_HANDLE" in os.environ:
|
||||
args.pretty = False
|
||||
io.tool_output("VSCode terminal detected, pretty output has been disabled.")
|
||||
|
||||
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"
|
||||
@@ -727,6 +826,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
io.tool_output(f"Cur working dir: {Path.cwd()}")
|
||||
io.tool_output(f"Git working dir: {git_root}")
|
||||
|
||||
if args.load:
|
||||
commands.cmd_load(args.load)
|
||||
|
||||
if args.message:
|
||||
io.add_to_input_history(args.message)
|
||||
io.tool_output()
|
||||
@@ -752,6 +854,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
if args.exit:
|
||||
return
|
||||
|
||||
analytics.event("cli session", main_model=main_model, edit_format=main_model.edit_format)
|
||||
|
||||
while True:
|
||||
try:
|
||||
coder.run()
|
||||
@@ -768,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)
|
||||
|
||||
@@ -789,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"
|
||||
@@ -799,13 +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?")
|
||||
io.tool_output("https://aider.chat/docs/install/install.html")
|
||||
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:
|
||||
@@ -814,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()}")
|
||||
|
||||
|
||||
431
aider/models.py
431
aider/models.py
@@ -13,7 +13,6 @@ import json5
|
||||
import yaml
|
||||
from PIL import Image
|
||||
|
||||
from aider import urls
|
||||
from aider.dump import dump # noqa: F401
|
||||
from aider.llm import litellm
|
||||
|
||||
@@ -53,9 +52,11 @@ ANTHROPIC_MODELS = """
|
||||
claude-2
|
||||
claude-2.1
|
||||
claude-3-haiku-20240307
|
||||
claude-3-5-haiku-20241022
|
||||
claude-3-opus-20240229
|
||||
claude-3-sonnet-20240229
|
||||
claude-3-5-sonnet-20240620
|
||||
claude-3-5-sonnet-20241022
|
||||
"""
|
||||
|
||||
ANTHROPIC_MODELS = [ln.strip() for ln in ANTHROPIC_MODELS.splitlines() if ln.strip()]
|
||||
@@ -69,7 +70,6 @@ class ModelSettings:
|
||||
weak_model_name: Optional[str] = None
|
||||
use_repo_map: bool = False
|
||||
send_undo_reply: bool = False
|
||||
accepts_images: bool = False
|
||||
lazy: bool = False
|
||||
reminder: str = "user"
|
||||
examples_as_sys_msg: bool = False
|
||||
@@ -125,7 +125,6 @@ MODEL_SETTINGS = [
|
||||
"udiff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
@@ -134,7 +133,6 @@ MODEL_SETTINGS = [
|
||||
"udiff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
@@ -143,7 +141,6 @@ MODEL_SETTINGS = [
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
editor_edit_format="editor-diff",
|
||||
@@ -153,7 +150,6 @@ MODEL_SETTINGS = [
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
@@ -162,7 +158,22 @@ MODEL_SETTINGS = [
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
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",
|
||||
),
|
||||
@@ -171,7 +182,6 @@ MODEL_SETTINGS = [
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
editor_edit_format="editor-diff",
|
||||
@@ -180,7 +190,6 @@ MODEL_SETTINGS = [
|
||||
"gpt-4o-mini",
|
||||
"whole",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
@@ -188,7 +197,6 @@ MODEL_SETTINGS = [
|
||||
"openai/gpt-4o-mini",
|
||||
"whole",
|
||||
weak_model_name="openai/gpt-4o-mini",
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
),
|
||||
@@ -214,7 +222,6 @@ MODEL_SETTINGS = [
|
||||
"diff",
|
||||
weak_model_name="gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
reminder="sys",
|
||||
),
|
||||
ModelSettings(
|
||||
@@ -243,29 +250,28 @@ 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,
|
||||
examples_as_sys_msg=True,
|
||||
accepts_images=True,
|
||||
extra_params={
|
||||
"extra_headers": {
|
||||
"anthropic-beta": ANTHROPIC_BETA_HEADER,
|
||||
@@ -278,7 +284,7 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"anthropic/claude-3-5-sonnet-20240620",
|
||||
"diff",
|
||||
weak_model_name="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,
|
||||
@@ -292,6 +298,74 @@ MODEL_SETTINGS = [
|
||||
cache_control=True,
|
||||
reminder="user",
|
||||
),
|
||||
ModelSettings(
|
||||
"anthropic/claude-3-5-sonnet-20241022",
|
||||
"diff",
|
||||
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,
|
||||
examples_as_sys_msg=True,
|
||||
extra_params={
|
||||
"extra_headers": {
|
||||
"anthropic-beta": ANTHROPIC_BETA_HEADER,
|
||||
},
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
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-5-haiku-20241022",
|
||||
editor_model_name="anthropic/claude-3-5-sonnet-20241022",
|
||||
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(
|
||||
"claude-3-5-sonnet-20241022",
|
||||
"diff",
|
||||
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,
|
||||
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-haiku-20240307",
|
||||
"whole",
|
||||
@@ -304,6 +378,52 @@ MODEL_SETTINGS = [
|
||||
},
|
||||
cache_control=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"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,
|
||||
},
|
||||
},
|
||||
cache_control=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"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": {
|
||||
"anthropic-beta": ANTHROPIC_BETA_HEADER,
|
||||
},
|
||||
},
|
||||
cache_control=True,
|
||||
),
|
||||
ModelSettings(
|
||||
"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(
|
||||
"claude-3-haiku-20240307",
|
||||
"whole",
|
||||
@@ -319,12 +439,11 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3.5-sonnet",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3-haiku-20240307",
|
||||
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,
|
||||
examples_as_sys_msg=True,
|
||||
accepts_images=True,
|
||||
extra_params={
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
@@ -334,12 +453,11 @@ MODEL_SETTINGS = [
|
||||
ModelSettings(
|
||||
"openrouter/anthropic/claude-3.5-sonnet:beta",
|
||||
"diff",
|
||||
weak_model_name="openrouter/anthropic/claude-3-haiku-20240307",
|
||||
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,
|
||||
examples_as_sys_msg=True,
|
||||
accepts_images=True,
|
||||
extra_params={
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
@@ -351,12 +469,24 @@ 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,
|
||||
examples_as_sys_msg=True,
|
||||
accepts_images=True,
|
||||
extra_params={
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
reminder="user",
|
||||
),
|
||||
ModelSettings(
|
||||
"vertex_ai/claude-3-5-sonnet-v2@20241022",
|
||||
"diff",
|
||||
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,
|
||||
examples_as_sys_msg=True,
|
||||
extra_params={
|
||||
"max_tokens": 8192,
|
||||
},
|
||||
@@ -365,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(
|
||||
@@ -436,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",
|
||||
@@ -496,7 +631,6 @@ MODEL_SETTINGS = [
|
||||
"diff",
|
||||
weak_model_name="openrouter/openai/gpt-4o-mini",
|
||||
use_repo_map=True,
|
||||
accepts_images=True,
|
||||
lazy=True,
|
||||
reminder="sys",
|
||||
editor_edit_format="editor-diff",
|
||||
@@ -511,7 +645,17 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"azure/o1-mini",
|
||||
"whole",
|
||||
weak_model_name="azure/gpt-4o-mini",
|
||||
editor_model_name="azure/gpt-4o",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"o1-mini",
|
||||
@@ -523,7 +667,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openai/o1-preview",
|
||||
@@ -535,7 +678,17 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"azure/o1-preview",
|
||||
"diff",
|
||||
weak_model_name="azure/gpt-4o-mini",
|
||||
editor_model_name="azure/gpt-4o",
|
||||
editor_edit_format="editor-diff",
|
||||
use_repo_map=True,
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"o1-preview",
|
||||
@@ -547,7 +700,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/openai/o1-mini",
|
||||
@@ -559,7 +711,6 @@ MODEL_SETTINGS = [
|
||||
reminder="user",
|
||||
use_system_prompt=False,
|
||||
use_temperature=False,
|
||||
streaming=False,
|
||||
),
|
||||
ModelSettings(
|
||||
"openrouter/openai/o1-preview",
|
||||
@@ -571,82 +722,98 @@ 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,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
model_info_url = (
|
||||
"https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
|
||||
)
|
||||
class ModelInfoManager:
|
||||
MODEL_INFO_URL = (
|
||||
"https://raw.githubusercontent.com/BerriAI/litellm/main/"
|
||||
"model_prices_and_context_window.json"
|
||||
)
|
||||
CACHE_TTL = 60 * 60 * 24 # 24 hours
|
||||
|
||||
def __init__(self):
|
||||
self.cache_dir = Path.home() / ".aider" / "caches"
|
||||
self.cache_file = self.cache_dir / "model_prices_and_context_window.json"
|
||||
self.content = None
|
||||
self._load_cache()
|
||||
|
||||
def get_model_flexible(model, content):
|
||||
info = content.get(model, dict())
|
||||
if info:
|
||||
return info
|
||||
|
||||
pieces = model.split("/")
|
||||
if len(pieces) == 2:
|
||||
info = content.get(pieces[1])
|
||||
if info and info.get("litellm_provider") == pieces[0]:
|
||||
return info
|
||||
|
||||
return dict()
|
||||
|
||||
|
||||
def get_model_info(model):
|
||||
if not litellm._lazy_module:
|
||||
cache_dir = Path.home() / ".aider" / "caches"
|
||||
cache_file = cache_dir / "model_prices_and_context_window.json"
|
||||
|
||||
def _load_cache(self):
|
||||
try:
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
use_cache = True
|
||||
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
if self.cache_file.exists():
|
||||
cache_age = time.time() - self.cache_file.stat().st_mtime
|
||||
if cache_age < self.CACHE_TTL:
|
||||
self.content = json.loads(self.cache_file.read_text())
|
||||
except OSError:
|
||||
# If we can't create the cache directory, we'll skip using the cache
|
||||
use_cache = False
|
||||
|
||||
if use_cache:
|
||||
current_time = time.time()
|
||||
cache_age = (
|
||||
current_time - cache_file.stat().st_mtime if cache_file.exists() else float("inf")
|
||||
)
|
||||
|
||||
if cache_age < 60 * 60 * 24:
|
||||
try:
|
||||
content = json.loads(cache_file.read_text())
|
||||
res = get_model_flexible(model, content)
|
||||
if res:
|
||||
return res
|
||||
except Exception as ex:
|
||||
print(str(ex))
|
||||
|
||||
import requests
|
||||
pass
|
||||
|
||||
def _update_cache(self):
|
||||
try:
|
||||
response = requests.get(model_info_url, timeout=5)
|
||||
import requests
|
||||
|
||||
response = requests.get(self.MODEL_INFO_URL, timeout=5)
|
||||
if response.status_code == 200:
|
||||
content = response.json()
|
||||
if use_cache:
|
||||
try:
|
||||
cache_file.write_text(json.dumps(content, indent=4))
|
||||
except OSError:
|
||||
# If we can't write to the cache file, we'll just skip caching
|
||||
pass
|
||||
res = get_model_flexible(model, content)
|
||||
if res:
|
||||
return res
|
||||
self.content = response.json()
|
||||
try:
|
||||
self.cache_file.write_text(json.dumps(self.content, indent=4))
|
||||
except OSError:
|
||||
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:
|
||||
self._update_cache()
|
||||
|
||||
if not self.content:
|
||||
return dict()
|
||||
|
||||
info = self.content.get(model, dict())
|
||||
if info:
|
||||
return info
|
||||
|
||||
pieces = model.split("/")
|
||||
if len(pieces) == 2:
|
||||
info = self.content.get(pieces[1])
|
||||
if info and info.get("litellm_provider") == pieces[0]:
|
||||
return info
|
||||
|
||||
# If all else fails, do it the slow way...
|
||||
try:
|
||||
info = litellm.get_model_info(model)
|
||||
return info
|
||||
except Exception:
|
||||
return dict()
|
||||
|
||||
def get_model_info(self, model):
|
||||
cached_info = self.get_model_from_cached_json_db(model)
|
||||
|
||||
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()
|
||||
|
||||
|
||||
class Model(ModelSettings):
|
||||
def __init__(self, model, weak_model=None, editor_model=None, editor_edit_format=None):
|
||||
@@ -655,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?
|
||||
@@ -680,19 +852,46 @@ class Model(ModelSettings):
|
||||
self.get_editor_model(editor_model, editor_edit_format)
|
||||
|
||||
def get_model_info(self, model):
|
||||
return get_model_info(model)
|
||||
return model_info_manager.get_model_info(model)
|
||||
|
||||
def _copy_fields(self, source):
|
||||
"""Helper to copy fields from a ModelSettings instance to self"""
|
||||
for field in fields(ModelSettings):
|
||||
val = getattr(source, field.name)
|
||||
setattr(self, field.name, val)
|
||||
|
||||
def configure_model_settings(self, model):
|
||||
# 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
|
||||
@@ -714,16 +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 = None
|
||||
self.reminder = "user"
|
||||
return # <--
|
||||
|
||||
if model.startswith("o1-") or "/o1-" in model:
|
||||
self.use_system_prompt = False
|
||||
self.use_temperature = 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
|
||||
@@ -907,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:
|
||||
@@ -980,9 +1205,6 @@ def sanity_check_model(io, model):
|
||||
for match in possible_matches:
|
||||
io.tool_output(f"- {match}")
|
||||
|
||||
if show:
|
||||
io.tool_output(f"For more info, see: {urls.model_warnings}")
|
||||
|
||||
return show
|
||||
|
||||
|
||||
@@ -994,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()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import colorsys
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import sqlite3
|
||||
import sys
|
||||
import time
|
||||
@@ -27,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:
|
||||
@@ -166,13 +167,52 @@ class RepoMap:
|
||||
# Just return the full fname.
|
||||
return fname
|
||||
|
||||
def tags_cache_error(self, original_error=None):
|
||||
"""Handle SQLite errors by trying to recreate cache, falling back to dict if needed"""
|
||||
|
||||
if self.verbose and original_error:
|
||||
self.io.tool_warning(f"Tags cache error: {str(original_error)}")
|
||||
|
||||
if isinstance(getattr(self, "TAGS_CACHE", None), dict):
|
||||
return
|
||||
|
||||
path = Path(self.root) / self.TAGS_CACHE_DIR
|
||||
|
||||
# Try to recreate the cache
|
||||
try:
|
||||
# Delete existing cache dir
|
||||
if path.exists():
|
||||
shutil.rmtree(path)
|
||||
|
||||
# Try to create new cache
|
||||
new_cache = Cache(path)
|
||||
|
||||
# Test that it works
|
||||
test_key = "test"
|
||||
new_cache[test_key] = "test"
|
||||
_ = new_cache[test_key]
|
||||
del new_cache[test_key]
|
||||
|
||||
# If we got here, the new cache works
|
||||
self.TAGS_CACHE = new_cache
|
||||
return
|
||||
|
||||
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"
|
||||
)
|
||||
if self.verbose:
|
||||
self.io.tool_warning(f"Cache recreation error: {str(e)}")
|
||||
|
||||
self.TAGS_CACHE = dict()
|
||||
|
||||
def load_tags_cache(self):
|
||||
path = Path(self.root) / self.TAGS_CACHE_DIR
|
||||
try:
|
||||
self.TAGS_CACHE = Cache(path)
|
||||
except SQLITE_ERRORS:
|
||||
self.io.tool_warning(f"Unable to use tags cache, delete {path} to resolve.")
|
||||
self.TAGS_CACHE = dict()
|
||||
except SQLITE_ERRORS as e:
|
||||
self.tags_cache_error(e)
|
||||
|
||||
def save_tags_cache(self):
|
||||
pass
|
||||
@@ -190,9 +230,18 @@ class RepoMap:
|
||||
return []
|
||||
|
||||
cache_key = fname
|
||||
val = self.TAGS_CACHE.get(cache_key) # Issue #1308
|
||||
try:
|
||||
val = self.TAGS_CACHE.get(cache_key) # Issue #1308
|
||||
except SQLITE_ERRORS as e:
|
||||
self.tags_cache_error(e)
|
||||
val = self.TAGS_CACHE.get(cache_key)
|
||||
|
||||
if val is not None and val.get("mtime") == file_mtime:
|
||||
return self.TAGS_CACHE[cache_key]["data"]
|
||||
try:
|
||||
return self.TAGS_CACHE[cache_key]["data"]
|
||||
except SQLITE_ERRORS as e:
|
||||
self.tags_cache_error(e)
|
||||
return self.TAGS_CACHE[cache_key]["data"]
|
||||
|
||||
# miss!
|
||||
data = list(self.get_tags_raw(fname, rel_fname))
|
||||
@@ -201,8 +250,9 @@ class RepoMap:
|
||||
try:
|
||||
self.TAGS_CACHE[cache_key] = {"mtime": file_mtime, "data": data}
|
||||
self.save_tags_cache()
|
||||
except SQLITE_ERRORS:
|
||||
pass
|
||||
except SQLITE_ERRORS as e:
|
||||
self.tags_cache_error(e)
|
||||
self.TAGS_CACHE[cache_key] = {"mtime": file_mtime, "data": data}
|
||||
|
||||
return data
|
||||
|
||||
@@ -302,7 +352,13 @@ class RepoMap:
|
||||
# https://networkx.org/documentation/stable/_modules/networkx/algorithms/link_analysis/pagerank_alg.html#pagerank
|
||||
personalize = 100 / len(fnames)
|
||||
|
||||
if len(fnames) - len(self.TAGS_CACHE) > 100:
|
||||
try:
|
||||
cache_size = len(self.TAGS_CACHE)
|
||||
except SQLITE_ERRORS as e:
|
||||
self.tags_cache_error(e)
|
||||
cache_size = len(self.TAGS_CACHE)
|
||||
|
||||
if len(fnames) - cache_size > 100:
|
||||
self.io.tool_output(
|
||||
"Initial repo scan can be slow in larger repos, but only happens once."
|
||||
)
|
||||
@@ -312,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()
|
||||
|
||||
|
||||
3
aider/resources/__init__.py
Normal file
3
aider/resources/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# This ensures that importlib_resources.files("aider.resources")
|
||||
# doesn't raise ImportError, even if there are no other files in this
|
||||
# dir.
|
||||
0
aider/resources/model-metadata.json
Normal file
0
aider/resources/model-metadata.json
Normal file
@@ -1,9 +1,9 @@
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
import backoff
|
||||
import time
|
||||
|
||||
from aider.dump import dump # noqa: F401
|
||||
from aider.exceptions import LiteLLMExceptions
|
||||
from aider.llm import litellm
|
||||
|
||||
# from diskcache import Cache
|
||||
@@ -13,37 +13,7 @@ CACHE_PATH = "~/.aider.send.cache.v1"
|
||||
CACHE = None
|
||||
# CACHE = Cache(CACHE_PATH)
|
||||
|
||||
|
||||
def retry_exceptions():
|
||||
import httpx
|
||||
|
||||
return (
|
||||
httpx.ConnectError,
|
||||
httpx.RemoteProtocolError,
|
||||
httpx.ReadTimeout,
|
||||
litellm.exceptions.APIConnectionError,
|
||||
litellm.exceptions.APIError,
|
||||
litellm.exceptions.RateLimitError,
|
||||
litellm.exceptions.ServiceUnavailableError,
|
||||
litellm.exceptions.Timeout,
|
||||
litellm.exceptions.InternalServerError,
|
||||
litellm.llms.anthropic.chat.AnthropicError,
|
||||
)
|
||||
|
||||
|
||||
def lazy_litellm_retry_decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
decorated_func = backoff.on_exception(
|
||||
backoff.expo,
|
||||
retry_exceptions(),
|
||||
max_time=60,
|
||||
on_backoff=lambda details: print(
|
||||
f"{details.get('exception', 'Exception')}\nRetry in {details['wait']:.1f} seconds."
|
||||
),
|
||||
)(func)
|
||||
return decorated_func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
RETRY_TIMEOUT = 60
|
||||
|
||||
|
||||
def send_completion(
|
||||
@@ -54,8 +24,6 @@ def send_completion(
|
||||
temperature=0,
|
||||
extra_params=None,
|
||||
):
|
||||
from aider.llm import litellm
|
||||
|
||||
kwargs = dict(
|
||||
model=model_name,
|
||||
messages=messages,
|
||||
@@ -88,18 +56,42 @@ def send_completion(
|
||||
return hash_object, res
|
||||
|
||||
|
||||
@lazy_litellm_retry_decorator
|
||||
def simple_send_with_retries(model_name, messages, extra_params=None):
|
||||
try:
|
||||
kwargs = {
|
||||
"model_name": model_name,
|
||||
"messages": messages,
|
||||
"functions": None,
|
||||
"stream": False,
|
||||
"extra_params": extra_params,
|
||||
}
|
||||
litellm_ex = LiteLLMExceptions()
|
||||
|
||||
_hash, response = send_completion(**kwargs)
|
||||
return response.choices[0].message.content
|
||||
except (AttributeError, litellm.exceptions.BadRequestError):
|
||||
return
|
||||
retry_delay = 0.125
|
||||
while True:
|
||||
try:
|
||||
kwargs = {
|
||||
"model_name": model_name,
|
||||
"messages": messages,
|
||||
"functions": None,
|
||||
"stream": False,
|
||||
"extra_params": extra_params,
|
||||
}
|
||||
|
||||
_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 litellm_ex.exceptions_tuple() as err:
|
||||
ex_info = litellm_ex.get_ex_info(err)
|
||||
|
||||
print(str(err))
|
||||
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 None
|
||||
|
||||
@@ -10,3 +10,6 @@ llms = "https://aider.chat/docs/llms.html"
|
||||
large_repos = "https://aider.chat/docs/faq.html#can-i-use-aider-in-a-large-mono-repo"
|
||||
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"
|
||||
|
||||
@@ -275,8 +275,12 @@ class Spinner:
|
||||
self.start_time = time.time()
|
||||
self.last_update = 0
|
||||
self.visible = False
|
||||
self.is_tty = sys.stdout.isatty()
|
||||
|
||||
def step(self):
|
||||
if not self.is_tty:
|
||||
return
|
||||
|
||||
current_time = time.time()
|
||||
if not self.visible and current_time - self.start_time >= 0.5:
|
||||
self.visible = True
|
||||
@@ -292,7 +296,7 @@ class Spinner:
|
||||
print(f"\r{self.text} {next(self.spinner_chars)}\r{self.text} ", end="", flush=True)
|
||||
|
||||
def end(self):
|
||||
if self.visible:
|
||||
if self.visible and self.is_tty:
|
||||
print("\r" + " " * (len(self.text) + 3))
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Release history
|
||||
parent: More info
|
||||
nav_order: 999
|
||||
nav_order: 900
|
||||
highlight_image: /assets/blame.jpg
|
||||
description: Release notes and stats on aider writing its own code.
|
||||
---
|
||||
@@ -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,7 +25,108 @@ cog.out(text)
|
||||
|
||||
|
||||
|
||||
### v0.59.0
|
||||
### 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
|
||||
- Scored 75% on [aider's code editing leaderboard](https://aider.chat/docs/leaderboards/).
|
||||
- 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.
|
||||
- Bugfix for creating new files.
|
||||
- Aider wrote 84% of the code in this release.
|
||||
|
||||
### Aider v0.61.0
|
||||
|
||||
- Load and save aider slash-commands to files:
|
||||
- `/save <fname>` command will make a file of `/add` and `/read-only` commands that recreate the current file context in the chat.
|
||||
- `/load <fname>` will replay the commands in the file.
|
||||
- You can use `/load` to run any arbitrary set of slash-commands, not just `/add` and `/read-only`.
|
||||
- Use `--load <fname>` to run a list of commands on launch, before the interactive chat begins.
|
||||
- Anonymous, opt-in [analytics](https://aider.chat/docs/more/analytics.html) with no personal data sharing.
|
||||
- Aider follows litellm's `supports_vision` attribute to enable image support for models.
|
||||
- Bugfix for when diff mode flexibly handles the model using the wrong filename.
|
||||
- Displays filenames in sorted order for `/add` and `/read-only`.
|
||||
- New `--no-fancy-input` switch disables prompt toolkit input, now still available with `--no-pretty`.
|
||||
- Override browser config with `--no-browser` or `--no-gui`.
|
||||
- Offer to open documentation URLs when errors occur.
|
||||
- Properly support all o1 models, regardless of provider.
|
||||
- Improved layout of filenames above input prompt.
|
||||
- Better handle corrupted repomap tags cache.
|
||||
- Improved handling of API errors, especially when accessing the weak model.
|
||||
- Aider wrote 68% of the code in this release.
|
||||
|
||||
### Aider v0.60.1
|
||||
|
||||
- Enable image support for Sonnet 10/22.
|
||||
- Display filenames in sorted order.
|
||||
|
||||
### Aider v0.60.0
|
||||
|
||||
- Full support for Sonnet 10/22, the new SOTA model on aider's code editing benchmark.
|
||||
- Aider uses Sonnet 10/22 by default.
|
||||
- Improved formatting of added and read-only files above chat prompt, by @jbellis.
|
||||
- Improved support for o1 models by more flexibly parsing their nonconforming code edit replies.
|
||||
- Corrected diff edit format prompt that only the first match is replaced.
|
||||
- Stronger whole edit format prompt asking for clean file names.
|
||||
- Now offers to add `.env` to the `.gitignore` file.
|
||||
- Ships with a small model metadata json file to handle models not yet updated in litellm.
|
||||
- Model settings for o1 models on azure.
|
||||
- Bugfix to properly include URLs in `/help` RAG results.
|
||||
- Aider wrote 49% of the code in this release.
|
||||
|
||||
### Aider v0.59.1
|
||||
|
||||
- Check for obsolete `yes: true` in yaml config, show helpful error.
|
||||
- Model settings for openrouter/anthropic/claude-3.5-sonnet:beta
|
||||
|
||||
### Aider v0.59.0
|
||||
|
||||
- Improvements to `/read-only`:
|
||||
- Now supports shell-style auto-complete of the full file system.
|
||||
|
||||
@@ -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,7 +2789,227 @@
|
||||
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
|
||||
total_lines: 228
|
||||
- aider_percentage: 48.95
|
||||
aider_total: 140
|
||||
end_date: '2024-10-22'
|
||||
end_tag: v0.60.0
|
||||
file_counts:
|
||||
.github/workflows/close-stale.yml:
|
||||
Paul Gauthier: 5
|
||||
Paul Gauthier (aider): 19
|
||||
.github/workflows/pages.yml:
|
||||
Paul Gauthier: 3
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/args.py:
|
||||
Paul Gauthier: 1
|
||||
fry69: 2
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 2
|
||||
aider/coders/editblock_coder.py:
|
||||
Paul Gauthier (aider): 3
|
||||
aider/commands.py:
|
||||
Paul Gauthier: 1
|
||||
aider/help.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 33
|
||||
aider/io.py:
|
||||
Jonathan Ellis: 10
|
||||
Paul Gauthier: 7
|
||||
aider/main.py:
|
||||
Paul Gauthier: 20
|
||||
Paul Gauthier (aider): 39
|
||||
aider/models.py:
|
||||
Paul Gauthier: 18
|
||||
Sven Grunewaldt: 24
|
||||
fry69: 16
|
||||
aider/resources/__init__.py:
|
||||
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:
|
||||
Paul Gauthier: 1
|
||||
tests/help/test_help.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 46
|
||||
grand_total:
|
||||
Jonathan Ellis: 10
|
||||
Paul Gauthier: 94
|
||||
Paul Gauthier (aider): 140
|
||||
Sven Grunewaldt: 24
|
||||
fry69: 18
|
||||
start_tag: v0.59.0
|
||||
total_lines: 286
|
||||
- aider_percentage: 67.61
|
||||
aider_total: 860
|
||||
end_date: '2024-11-01'
|
||||
end_tag: v0.61.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/analytics.py:
|
||||
Paul Gauthier: 75
|
||||
Paul Gauthier (aider): 89
|
||||
aider/args.py:
|
||||
Paul Gauthier: 5
|
||||
Paul Gauthier (aider): 29
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 56
|
||||
Paul Gauthier (aider): 43
|
||||
aider/coders/editblock_coder.py:
|
||||
Paul Gauthier: 14
|
||||
aider/commands.py:
|
||||
Paul Gauthier: 14
|
||||
Paul Gauthier (aider): 86
|
||||
aider/io.py:
|
||||
Paul Gauthier: 12
|
||||
Paul Gauthier (aider): 32
|
||||
aider/linter.py:
|
||||
Paul Gauthier: 6
|
||||
aider/main.py:
|
||||
Paul Gauthier: 48
|
||||
Paul Gauthier (aider): 10
|
||||
aider/models.py:
|
||||
Paul Gauthier: 54
|
||||
Paul Gauthier (aider): 63
|
||||
kAIto47802: 4
|
||||
aider/repomap.py:
|
||||
Paul Gauthier: 12
|
||||
Paul Gauthier (aider): 52
|
||||
aider/sendchat.py:
|
||||
Paul Gauthier: 23
|
||||
Paul Gauthier (aider): 23
|
||||
aider/urls.py:
|
||||
Paul Gauthier: 2
|
||||
aider/utils.py:
|
||||
Paul Gauthier (aider): 6
|
||||
scripts/issues.py:
|
||||
Paul Gauthier (aider): 13
|
||||
scripts/pip-compile.sh:
|
||||
Paul Gauthier (aider): 13
|
||||
scripts/update-docs.sh:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 5
|
||||
tests/basic/test_analytics.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 99
|
||||
tests/basic/test_commands.py:
|
||||
Konstantin L: 34
|
||||
Paul Gauthier: 45
|
||||
Paul Gauthier (aider): 267
|
||||
tests/basic/test_io.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 4
|
||||
tests/basic/test_main.py:
|
||||
Paul Gauthier (aider): 3
|
||||
tests/basic/test_models.py:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 9
|
||||
tests/basic/test_sanity_check_repo.py:
|
||||
Paul Gauthier (aider): 6
|
||||
tests/basic/test_sendchat.py:
|
||||
Paul Gauthier (aider): 8
|
||||
grand_total:
|
||||
Konstantin L: 34
|
||||
Paul Gauthier: 374
|
||||
Paul Gauthier (aider): 860
|
||||
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)
|
||||
@@ -547,7 +547,7 @@
|
||||
|
||||
- dirname: 2024-07-04-14-32-08--claude-3.5-sonnet-diff-continue
|
||||
test_cases: 133
|
||||
model: claude-3.5-sonnet
|
||||
model: claude-3.5-sonnet-20240620
|
||||
edit_format: diff
|
||||
commit_hash: 35f21b5
|
||||
pass_rate_1: 57.1
|
||||
@@ -563,12 +563,12 @@
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 1
|
||||
command: aider --sonnet
|
||||
command: aider --model claude-3.5-sonnet-20240620
|
||||
date: 2024-07-04
|
||||
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
|
||||
@@ -1495,4 +1498,400 @@
|
||||
date: 2024-10-04
|
||||
versions: 0.58.2.dev
|
||||
seconds_per_case: 23.7
|
||||
total_cost: 4.0641
|
||||
total_cost: 4.0641
|
||||
|
||||
- dirname: 2024-10-05-20-03-10--dracarys-glhf-whole
|
||||
test_cases: 133
|
||||
model: Dracarys2-72B-Instruct
|
||||
edit_format: whole
|
||||
commit_hash: 04a2cbb
|
||||
pass_rate_1: 55.6
|
||||
pass_rate_2: 66.9
|
||||
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: 0
|
||||
command: (via glhf.chat)
|
||||
date: 2024-10-05
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 46.7
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-10-13-21-33-42--grok2-whole
|
||||
test_cases: 133
|
||||
model: Grok-2
|
||||
edit_format: whole
|
||||
commit_hash: 0a497b7
|
||||
pass_rate_1: 45.9
|
||||
pass_rate_2: 58.6
|
||||
percent_cases_well_formed: 98.5
|
||||
error_outputs: 7
|
||||
num_malformed_responses: 7
|
||||
num_with_malformed_responses: 2
|
||||
user_asks: 24
|
||||
lazy_comments: 4
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 1
|
||||
command: aider --model openrouter/x-ai/grok-2
|
||||
date: 2024-10-13
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 34.6
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-10-13-23-58-44--grok2mini-whole
|
||||
test_cases: 133
|
||||
model: Grok-2-mini
|
||||
edit_format: whole
|
||||
commit_hash: 0a497b7-dirty, 0a497b7
|
||||
pass_rate_1: 40.6
|
||||
pass_rate_2: 54.9
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 0
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 8
|
||||
lazy_comments: 2
|
||||
syntax_errors: 2
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 0
|
||||
command: aider --model openrouter/x-ai/grok-2-mini
|
||||
date: 2024-10-13
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 32.1
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-10-16-15-55-37--nemotron-glhf-whole3
|
||||
test_cases: 133
|
||||
model: Llama-3.1-Nemotron-70B-Instruct-HF
|
||||
edit_format: whole
|
||||
commit_hash: 6bb9b25-dirty
|
||||
pass_rate_1: 36.8
|
||||
pass_rate_2: 54.9
|
||||
percent_cases_well_formed: 99.2
|
||||
error_outputs: 17
|
||||
num_malformed_responses: 1
|
||||
num_with_malformed_responses: 1
|
||||
user_asks: 53
|
||||
lazy_comments: 17
|
||||
syntax_errors: 1
|
||||
indentation_errors: 2
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 3
|
||||
command: (via glhf.chat)
|
||||
date: 2024-10-16
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 64.9
|
||||
total_cost: 0.0000
|
||||
|
||||
- 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
|
||||
pass_rate_2: 84.2
|
||||
percent_cases_well_formed: 99.2
|
||||
error_outputs: 1
|
||||
num_malformed_responses: 1
|
||||
num_with_malformed_responses: 1
|
||||
user_asks: 0
|
||||
lazy_comments: 1
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 0
|
||||
command: aider --model anthropic/claude-3-5-sonnet-20241022
|
||||
date: 2024-10-22
|
||||
versions: 0.59.2.dev
|
||||
seconds_per_case: 18.6
|
||||
total_cost: 0.0000
|
||||
|
||||
- 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
|
||||
pass_rate_2: 75.2
|
||||
percent_cases_well_formed: 95.5
|
||||
error_outputs: 11
|
||||
num_malformed_responses: 11
|
||||
num_with_malformed_responses: 6
|
||||
user_asks: 1
|
||||
lazy_comments: 1
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 2
|
||||
command: aider --model anthropic/claude-3-5-haiku-20241022
|
||||
date: 2024-11-04
|
||||
versions: 0.61.1.dev
|
||||
seconds_per_case: 18.4
|
||||
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
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
|
||||
- dirname: 2024-07-01-18-30-33--refac-claude-3.5-sonnet-diff-not-lazy
|
||||
test_cases: 89
|
||||
model: claude-3.5-sonnet (diff)
|
||||
model: claude-3.5-sonnet-20240620
|
||||
edit_format: diff
|
||||
commit_hash: 7396e38-dirty
|
||||
pass_rate_1: 64.0
|
||||
@@ -229,4 +229,70 @@
|
||||
date: 2024-09-05
|
||||
versions: 0.55.1.dev
|
||||
seconds_per_case: 225.4
|
||||
total_cost: 1.0338
|
||||
total_cost: 1.0338
|
||||
|
||||
- dirname: 2024-10-22-19-57-27--refac-openrouter-sonnet-1022
|
||||
test_cases: 89
|
||||
model: claude-3-5-sonnet-20241022
|
||||
edit_format: diff
|
||||
commit_hash: 4a3e6ef
|
||||
pass_rate_1: 92.1
|
||||
percent_cases_well_formed: 91.0
|
||||
error_outputs: 13
|
||||
num_malformed_responses: 12
|
||||
num_with_malformed_responses: 8
|
||||
user_asks: 14
|
||||
lazy_comments: 2
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 0
|
||||
command: aider --sonnet
|
||||
date: 2024-10-22
|
||||
versions: 0.60.1.dev
|
||||
seconds_per_case: 32.5
|
||||
total_cost: 8.4644
|
||||
|
||||
- dirname: 2024-10-22-20-03-10--refac-o1mini
|
||||
test_cases: 89
|
||||
model: o1-mini
|
||||
edit_format: diff
|
||||
commit_hash: 4a3e6ef-dirty
|
||||
pass_rate_1: 44.9
|
||||
percent_cases_well_formed: 29.2
|
||||
error_outputs: 151
|
||||
num_malformed_responses: 150
|
||||
num_with_malformed_responses: 63
|
||||
user_asks: 28
|
||||
lazy_comments: 2
|
||||
syntax_errors: 5
|
||||
indentation_errors: 4
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 0
|
||||
command: aider --model o1-mini
|
||||
date: 2024-10-22
|
||||
versions: 0.60.1.dev
|
||||
seconds_per_case: 115.3
|
||||
total_cost: 29.0492
|
||||
|
||||
- dirname: 2024-10-22-20-26-36--refac-o1preview
|
||||
test_cases: 89
|
||||
model: o1-preview
|
||||
edit_format: diff
|
||||
commit_hash: 4a3e6ef-dirty
|
||||
pass_rate_1: 75.3
|
||||
percent_cases_well_formed: 57.3
|
||||
error_outputs: 75
|
||||
num_malformed_responses: 74
|
||||
num_with_malformed_responses: 38
|
||||
user_asks: 19
|
||||
lazy_comments: 2
|
||||
syntax_errors: 2
|
||||
indentation_errors: 3
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 0
|
||||
command: aider --model o1-preview
|
||||
date: 2024-10-22
|
||||
versions: 0.60.1.dev
|
||||
seconds_per_case: 231.7
|
||||
total_cost: 120.9850
|
||||
@@ -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 `/clipboard` to paste text from the clipboard into the chat.
|
||||
- Use `/paste` to paste text from the clipboard into the chat.
|
||||
- Use the `/editor` command to open your editor to create the next chat message. See [editor configuration docs](/docs/config/editor.html) for more info.
|
||||
|
||||
Example with a tag:
|
||||
```
|
||||
{python
|
||||
def hello():
|
||||
print("Hello}") # Note: contains a brace
|
||||
python}
|
||||
```
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,10 @@ nav_exclude: true
|
||||
|
||||
[](https://aider.chat/assets/self-assembly.jpg)
|
||||
|
||||
{: .note }
|
||||
This article is quite out dated. For current statistics, see
|
||||
[aider's release history](/HISTORY.html).
|
||||
|
||||
The
|
||||
[aider git repo](https://github.com/Aider-AI/aider)
|
||||
currently contains about 4K commits and 14K lines of code.
|
||||
|
||||
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 |
1000
aider/website/assets/sample-analytics.jsonl
Normal file
1000
aider/website/assets/sample-analytics.jsonl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,9 +29,12 @@
|
||||
## Use claude-3-opus-20240229 model for the main chat
|
||||
#opus: false
|
||||
|
||||
## Use claude-3-5-sonnet-20240620 model for the main chat
|
||||
## Use claude-3-5-sonnet-20241022 model for the main chat
|
||||
#sonnet: false
|
||||
|
||||
## Use claude-3-5-haiku-20241022 model for the main chat
|
||||
#haiku: false
|
||||
|
||||
## Use gpt-4-0613 model for the main chat
|
||||
#4: false
|
||||
|
||||
@@ -104,7 +107,7 @@
|
||||
## Only work with models that have meta-data available (default: True)
|
||||
#show-model-warnings: true
|
||||
|
||||
## Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.
|
||||
## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens.
|
||||
#max-chat-history-tokens: xxx
|
||||
|
||||
## Specify the .env file to load (default: .env in git root)
|
||||
@@ -265,6 +268,18 @@
|
||||
## Run tests and fix problems found
|
||||
#test: false
|
||||
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#analytics: xxx
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#analytics-log: xxx
|
||||
|
||||
## Permanently disable analytics
|
||||
#analytics-disable: false
|
||||
|
||||
#################
|
||||
# Other Settings:
|
||||
|
||||
@@ -299,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
|
||||
|
||||
@@ -308,6 +326,9 @@
|
||||
## Apply the changes from the given file instead of running the chat (debug)
|
||||
#apply: xxx
|
||||
|
||||
## Apply clipboard contents as edits using the main model's editor format
|
||||
#apply-clipboard-edits: false
|
||||
|
||||
## Always say yes to every confirmation
|
||||
#yes-always: false
|
||||
|
||||
@@ -329,18 +350,27 @@
|
||||
## Specify a file containing the message to send the LLM, process reply, then exit (disables chat mode)
|
||||
#message-file: xxx
|
||||
|
||||
## Load and execute /commands from a file on launch
|
||||
#load: xxx
|
||||
|
||||
## Specify the encoding for input and output (default: utf-8)
|
||||
#encoding: utf-8
|
||||
|
||||
## Specify the config file (default: search for .aider.conf.yml in git root, cwd or home directory)
|
||||
#config: xxx
|
||||
|
||||
## Run aider in your browser
|
||||
## Run aider in your browser (default: False)
|
||||
#gui: false
|
||||
|
||||
## Enable/disable suggesting shell commands (default: True)
|
||||
#suggest-shell-commands: true
|
||||
|
||||
## 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:
|
||||
|
||||
|
||||
@@ -33,9 +33,12 @@
|
||||
## Use claude-3-opus-20240229 model for the main chat
|
||||
#AIDER_OPUS=
|
||||
|
||||
## Use claude-3-5-sonnet-20240620 model for the main chat
|
||||
## Use claude-3-5-sonnet-20241022 model for the main chat
|
||||
#AIDER_SONNET=
|
||||
|
||||
## Use claude-3-5-haiku-20241022 model for the main chat
|
||||
#AIDER_HAIKU=
|
||||
|
||||
## Use gpt-4-0613 model for the main chat
|
||||
#AIDER_4=
|
||||
|
||||
@@ -108,7 +111,7 @@
|
||||
## Only work with models that have meta-data available (default: True)
|
||||
#AIDER_SHOW_MODEL_WARNINGS=true
|
||||
|
||||
## Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.
|
||||
## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens.
|
||||
#AIDER_MAX_CHAT_HISTORY_TOKENS=
|
||||
|
||||
## Specify the .env file to load (default: .env in git root)
|
||||
@@ -264,6 +267,18 @@
|
||||
## Run tests and fix problems found
|
||||
#AIDER_TEST=false
|
||||
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#AIDER_ANALYTICS=
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#AIDER_ANALYTICS_LOG=
|
||||
|
||||
## Permanently disable analytics
|
||||
#AIDER_ANALYTICS_DISABLE=false
|
||||
|
||||
#################
|
||||
# Other Settings:
|
||||
|
||||
@@ -285,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
|
||||
|
||||
@@ -294,6 +312,9 @@
|
||||
## Apply the changes from the given file instead of running the chat (debug)
|
||||
#AIDER_APPLY=
|
||||
|
||||
## Apply clipboard contents as edits using the main model's editor format
|
||||
#AIDER_APPLY_CLIPBOARD_EDITS=false
|
||||
|
||||
## Always say yes to every confirmation
|
||||
#AIDER_YES_ALWAYS=
|
||||
|
||||
@@ -315,15 +336,24 @@
|
||||
## Specify a file containing the message to send the LLM, process reply, then exit (disables chat mode)
|
||||
#AIDER_MESSAGE_FILE=
|
||||
|
||||
## Load and execute /commands from a file on launch
|
||||
#AIDER_LOAD=
|
||||
|
||||
## Specify the encoding for input and output (default: utf-8)
|
||||
#AIDER_ENCODING=utf-8
|
||||
|
||||
## Run aider in your browser
|
||||
## Run aider in your browser (default: False)
|
||||
#AIDER_GUI=false
|
||||
|
||||
## Enable/disable suggesting shell commands (default: True)
|
||||
#AIDER_SUGGEST_SHELL_COMMANDS=true
|
||||
|
||||
## 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)
|
||||
@@ -81,8 +105,7 @@ cog.out(get_model_settings_as_yaml())
|
||||
cog.out("```\n")
|
||||
]]]-->
|
||||
```yaml
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -98,8 +121,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -115,8 +137,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -132,8 +153,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -149,8 +169,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -166,8 +185,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: udiff
|
||||
editor_edit_format: null
|
||||
@@ -183,8 +201,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: udiff
|
||||
editor_edit_format: null
|
||||
@@ -200,8 +217,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -217,8 +233,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -234,8 +249,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -251,8 +265,39 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- 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
|
||||
editor_edit_format: editor-diff
|
||||
@@ -268,8 +313,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -285,8 +329,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -302,8 +345,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openai/gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: udiff
|
||||
editor_edit_format: null
|
||||
@@ -319,8 +361,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: udiff
|
||||
editor_edit_format: null
|
||||
@@ -336,8 +377,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -353,8 +393,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -370,8 +409,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -387,8 +425,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -404,8 +441,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -420,9 +456,8 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -437,9 +472,8 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-haiku
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -454,9 +488,8 @@ cog.out("```\n")
|
||||
use_repo_map: false
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
- accepts_images: true
|
||||
cache_control: true
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -474,9 +507,8 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
- accepts_images: false
|
||||
cache_control: true
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -494,9 +526,84 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
- accepts_images: false
|
||||
cache_control: true
|
||||
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: anthropic/claude-3-5-sonnet-20241022
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
extra_headers:
|
||||
anthropic-beta: prompt-caching-2024-07-31
|
||||
max_tokens: 8192
|
||||
lazy: false
|
||||
name: anthropic/claude-3-5-sonnet-20241022
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
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: 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
|
||||
editor_edit_format: editor-diff
|
||||
editor_model_name: anthropic/claude-3-5-sonnet-20241022
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
extra_headers:
|
||||
anthropic-beta: prompt-caching-2024-07-31
|
||||
max_tokens: 8192
|
||||
lazy: false
|
||||
name: anthropic/claude-3-5-sonnet-latest
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
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: editor-diff
|
||||
editor_model_name: claude-3-5-sonnet-20241022
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
extra_headers:
|
||||
anthropic-beta: prompt-caching-2024-07-31
|
||||
max_tokens: 8192
|
||||
lazy: false
|
||||
name: claude-3-5-sonnet-20241022
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -514,8 +621,78 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: anthropic/claude-3-haiku-20240307
|
||||
- accepts_images: false
|
||||
cache_control: true
|
||||
- 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: anthropic/claude-3-5-haiku-20241022
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
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
|
||||
editor_edit_format: null
|
||||
editor_model_name: null
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
extra_headers:
|
||||
anthropic-beta: prompt-caching-2024-07-31
|
||||
lazy: false
|
||||
name: claude-3-5-haiku-20241022
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-5-haiku-20241022
|
||||
- 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:
|
||||
max_tokens: 4096
|
||||
lazy: false
|
||||
name: vertex_ai/claude-3-5-haiku@20241022
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
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: whole
|
||||
editor_edit_format: null
|
||||
@@ -533,8 +710,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: claude-3-haiku-20240307
|
||||
- accepts_images: true
|
||||
cache_control: true
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -550,9 +726,8 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-haiku-20240307
|
||||
- accepts_images: true
|
||||
cache_control: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||
- cache_control: true
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -568,9 +743,8 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/anthropic/claude-3-haiku-20240307
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
weak_model_name: openrouter/anthropic/claude-3-5-haiku:beta
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -586,9 +760,25 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
editor_model_name: vertex_ai/claude-3-5-sonnet-v2@20241022
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
max_tokens: 8192
|
||||
lazy: false
|
||||
name: vertex_ai/claude-3-5-sonnet-v2@20241022
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -603,9 +793,8 @@ cog.out("```\n")
|
||||
use_repo_map: true
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -620,9 +809,8 @@ cog.out("```\n")
|
||||
use_repo_map: false
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: vertex_ai/claude-3-haiku@20240307
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -638,8 +826,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: command-r-plus
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -655,8 +842,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: command-r-08-2024
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -672,8 +858,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: command-r-plus-08-2024
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -689,8 +874,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: groq/llama3-8b-8192
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -706,8 +890,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/meta-llama/llama-3-70b-instruct
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -723,8 +906,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: null
|
||||
@@ -740,8 +922,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff-fenced
|
||||
editor_edit_format: null
|
||||
@@ -757,8 +938,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff-fenced
|
||||
editor_edit_format: null
|
||||
@@ -774,8 +954,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff-fenced
|
||||
editor_edit_format: null
|
||||
@@ -791,8 +970,23 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- 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
|
||||
editor_edit_format: null
|
||||
@@ -808,8 +1002,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -826,8 +1019,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: true
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -844,8 +1036,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -862,8 +1053,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: true
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -880,8 +1070,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: null
|
||||
@@ -897,8 +1086,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: null
|
||||
- accepts_images: true
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -914,8 +1102,7 @@ cog.out("```\n")
|
||||
use_system_prompt: true
|
||||
use_temperature: true
|
||||
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: editor-diff
|
||||
@@ -926,13 +1113,28 @@ 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
|
||||
weak_model_name: openai/gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: editor-diff
|
||||
editor_model_name: azure/gpt-4o
|
||||
examples_as_sys_msg: false
|
||||
extra_params: null
|
||||
lazy: false
|
||||
name: azure/o1-mini
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
weak_model_name: azure/gpt-4o-mini
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: editor-diff
|
||||
@@ -943,13 +1145,12 @@ 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
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -960,13 +1161,28 @@ 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
|
||||
weak_model_name: openai/gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
editor_model_name: azure/gpt-4o
|
||||
examples_as_sys_msg: false
|
||||
extra_params: null
|
||||
lazy: false
|
||||
name: azure/o1-preview
|
||||
reminder: user
|
||||
send_undo_reply: false
|
||||
streaming: true
|
||||
use_repo_map: true
|
||||
use_system_prompt: false
|
||||
use_temperature: false
|
||||
weak_model_name: azure/gpt-4o-mini
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: architect
|
||||
editor_edit_format: editor-diff
|
||||
@@ -977,13 +1193,12 @@ 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
|
||||
weak_model_name: gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: whole
|
||||
editor_edit_format: editor-diff
|
||||
@@ -994,13 +1209,12 @@ 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
|
||||
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||
- accepts_images: false
|
||||
cache_control: false
|
||||
- cache_control: false
|
||||
caches_by_default: false
|
||||
edit_format: diff
|
||||
editor_edit_format: editor-diff
|
||||
@@ -1011,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]]]-->
|
||||
|
||||
|
||||
@@ -85,9 +85,12 @@ cog.outl("```")
|
||||
## Use claude-3-opus-20240229 model for the main chat
|
||||
#opus: false
|
||||
|
||||
## Use claude-3-5-sonnet-20240620 model for the main chat
|
||||
## Use claude-3-5-sonnet-20241022 model for the main chat
|
||||
#sonnet: false
|
||||
|
||||
## Use claude-3-5-haiku-20241022 model for the main chat
|
||||
#haiku: false
|
||||
|
||||
## Use gpt-4-0613 model for the main chat
|
||||
#4: false
|
||||
|
||||
@@ -160,7 +163,7 @@ cog.outl("```")
|
||||
## Only work with models that have meta-data available (default: True)
|
||||
#show-model-warnings: true
|
||||
|
||||
## Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.
|
||||
## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens.
|
||||
#max-chat-history-tokens: xxx
|
||||
|
||||
## Specify the .env file to load (default: .env in git root)
|
||||
@@ -321,6 +324,18 @@ cog.outl("```")
|
||||
## Run tests and fix problems found
|
||||
#test: false
|
||||
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#analytics: xxx
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#analytics-log: xxx
|
||||
|
||||
## Permanently disable analytics
|
||||
#analytics-disable: false
|
||||
|
||||
#################
|
||||
# Other Settings:
|
||||
|
||||
@@ -355,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
|
||||
|
||||
@@ -364,6 +382,9 @@ cog.outl("```")
|
||||
## Apply the changes from the given file instead of running the chat (debug)
|
||||
#apply: xxx
|
||||
|
||||
## Apply clipboard contents as edits using the main model's editor format
|
||||
#apply-clipboard-edits: false
|
||||
|
||||
## Always say yes to every confirmation
|
||||
#yes-always: false
|
||||
|
||||
@@ -385,18 +406,27 @@ cog.outl("```")
|
||||
## Specify a file containing the message to send the LLM, process reply, then exit (disables chat mode)
|
||||
#message-file: xxx
|
||||
|
||||
## Load and execute /commands from a file on launch
|
||||
#load: xxx
|
||||
|
||||
## Specify the encoding for input and output (default: utf-8)
|
||||
#encoding: utf-8
|
||||
|
||||
## Specify the config file (default: search for .aider.conf.yml in git root, cwd or home directory)
|
||||
#config: xxx
|
||||
|
||||
## Run aider in your browser
|
||||
## Run aider in your browser (default: False)
|
||||
#gui: false
|
||||
|
||||
## Enable/disable suggesting shell commands (default: True)
|
||||
#suggest-shell-commands: true
|
||||
|
||||
## 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:
|
||||
|
||||
|
||||
@@ -75,9 +75,12 @@ cog.outl("```")
|
||||
## Use claude-3-opus-20240229 model for the main chat
|
||||
#AIDER_OPUS=
|
||||
|
||||
## Use claude-3-5-sonnet-20240620 model for the main chat
|
||||
## Use claude-3-5-sonnet-20241022 model for the main chat
|
||||
#AIDER_SONNET=
|
||||
|
||||
## Use claude-3-5-haiku-20241022 model for the main chat
|
||||
#AIDER_HAIKU=
|
||||
|
||||
## Use gpt-4-0613 model for the main chat
|
||||
#AIDER_4=
|
||||
|
||||
@@ -150,7 +153,7 @@ cog.outl("```")
|
||||
## Only work with models that have meta-data available (default: True)
|
||||
#AIDER_SHOW_MODEL_WARNINGS=true
|
||||
|
||||
## Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.
|
||||
## Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens.
|
||||
#AIDER_MAX_CHAT_HISTORY_TOKENS=
|
||||
|
||||
## Specify the .env file to load (default: .env in git root)
|
||||
@@ -306,6 +309,18 @@ cog.outl("```")
|
||||
## Run tests and fix problems found
|
||||
#AIDER_TEST=false
|
||||
|
||||
############
|
||||
# Analytics:
|
||||
|
||||
## Enable/disable analytics for current session (default: random)
|
||||
#AIDER_ANALYTICS=
|
||||
|
||||
## Specify a file to log analytics events
|
||||
#AIDER_ANALYTICS_LOG=
|
||||
|
||||
## Permanently disable analytics
|
||||
#AIDER_ANALYTICS_DISABLE=false
|
||||
|
||||
#################
|
||||
# Other Settings:
|
||||
|
||||
@@ -327,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
|
||||
|
||||
@@ -336,6 +354,9 @@ cog.outl("```")
|
||||
## Apply the changes from the given file instead of running the chat (debug)
|
||||
#AIDER_APPLY=
|
||||
|
||||
## Apply clipboard contents as edits using the main model's editor format
|
||||
#AIDER_APPLY_CLIPBOARD_EDITS=false
|
||||
|
||||
## Always say yes to every confirmation
|
||||
#AIDER_YES_ALWAYS=
|
||||
|
||||
@@ -357,15 +378,24 @@ cog.outl("```")
|
||||
## Specify a file containing the message to send the LLM, process reply, then exit (disables chat mode)
|
||||
#AIDER_MESSAGE_FILE=
|
||||
|
||||
## Load and execute /commands from a file on launch
|
||||
#AIDER_LOAD=
|
||||
|
||||
## Specify the encoding for input and output (default: utf-8)
|
||||
#AIDER_ENCODING=utf-8
|
||||
|
||||
## Run aider in your browser
|
||||
## Run aider in your browser (default: False)
|
||||
#AIDER_GUI=false
|
||||
|
||||
## Enable/disable suggesting shell commands (default: True)
|
||||
#AIDER_SUGGEST_SHELL_COMMANDS=true
|
||||
|
||||
## 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"
|
||||
```
|
||||
@@ -26,12 +26,12 @@ cog.out(get_md_help())
|
||||
]]]-->
|
||||
```
|
||||
usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model]
|
||||
[--opus] [--sonnet] [--4] [--4o] [--mini] [--4-turbo]
|
||||
[--35turbo] [--deepseek] [--o1-mini] [--o1-preview]
|
||||
[--list-models] [--openai-api-base] [--openai-api-type]
|
||||
[--openai-api-version] [--openai-api-deployment-id]
|
||||
[--openai-organization-id] [--model-settings-file]
|
||||
[--model-metadata-file]
|
||||
[--opus] [--sonnet] [--haiku] [--4] [--4o] [--mini]
|
||||
[--4-turbo] [--35turbo] [--deepseek] [--o1-mini]
|
||||
[--o1-preview] [--list-models] [--openai-api-base]
|
||||
[--openai-api-type] [--openai-api-version]
|
||||
[--openai-api-deployment-id] [--openai-organization-id]
|
||||
[--model-settings-file] [--model-metadata-file]
|
||||
[--verify-ssl | --no-verify-ssl] [--edit-format]
|
||||
[--architect] [--weak-model] [--editor-model]
|
||||
[--editor-edit-format]
|
||||
@@ -61,15 +61,19 @@ usage: aider [-h] [--openai-api-key] [--anthropic-api-key] [--model]
|
||||
[--commit] [--commit-prompt] [--dry-run | --no-dry-run]
|
||||
[--skip-sanity-check-repo] [--lint] [--lint-cmd]
|
||||
[--auto-lint | --no-auto-lint] [--test-cmd]
|
||||
[--auto-test | --no-auto-test] [--test] [--file]
|
||||
[--read] [--vim] [--chat-language] [--version]
|
||||
[--just-check-update]
|
||||
[--auto-test | --no-auto-test] [--test]
|
||||
[--analytics | --no-analytics] [--analytics-log]
|
||||
[--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]
|
||||
[--yes-always] [-v] [--show-repo-map] [--show-prompts]
|
||||
[--exit] [--message] [--message-file] [--encoding] [-c]
|
||||
[--gui]
|
||||
[--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] [--editor]
|
||||
[--voice-format] [--voice-language]
|
||||
|
||||
```
|
||||
@@ -101,9 +105,13 @@ Use claude-3-opus-20240229 model for the main chat
|
||||
Environment variable: `AIDER_OPUS`
|
||||
|
||||
### `--sonnet`
|
||||
Use claude-3-5-sonnet-20240620 model for the main chat
|
||||
Use claude-3-5-sonnet-20241022 model for the main chat
|
||||
Environment variable: `AIDER_SONNET`
|
||||
|
||||
### `--haiku`
|
||||
Use claude-3-5-haiku-20241022 model for the main chat
|
||||
Environment variable: `AIDER_HAIKU`
|
||||
|
||||
### `--4`
|
||||
Use gpt-4-0613 model for the main chat
|
||||
Environment variable: `AIDER_4`
|
||||
@@ -223,7 +231,7 @@ Aliases:
|
||||
- `--no-show-model-warnings`
|
||||
|
||||
### `--max-chat-history-tokens VALUE`
|
||||
Maximum number of tokens to use for chat history. If not specified, uses the model's max_chat_history_tokens.
|
||||
Soft limit on tokens for chat history, after which summarization begins. If unspecified, defaults to the model's max_chat_history_tokens.
|
||||
Environment variable: `AIDER_MAX_CHAT_HISTORY_TOKENS`
|
||||
|
||||
### `--env-file ENV_FILE`
|
||||
@@ -500,6 +508,24 @@ Run tests and fix problems found
|
||||
Default: False
|
||||
Environment variable: `AIDER_TEST`
|
||||
|
||||
## Analytics:
|
||||
|
||||
### `--analytics`
|
||||
Enable/disable analytics for current session (default: random)
|
||||
Environment variable: `AIDER_ANALYTICS`
|
||||
Aliases:
|
||||
- `--analytics`
|
||||
- `--no-analytics`
|
||||
|
||||
### `--analytics-log ANALYTICS_LOG_FILE`
|
||||
Specify a file to log analytics events
|
||||
Environment variable: `AIDER_ANALYTICS_LOG`
|
||||
|
||||
### `--analytics-disable`
|
||||
Permanently disable analytics
|
||||
Default: False
|
||||
Environment variable: `AIDER_ANALYTICS_DISABLE`
|
||||
|
||||
## Other Settings:
|
||||
|
||||
### `--file FILE`
|
||||
@@ -535,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
|
||||
@@ -552,6 +585,11 @@ Aliases:
|
||||
Apply the changes from the given file instead of running the chat (debug)
|
||||
Environment variable: `AIDER_APPLY`
|
||||
|
||||
### `--apply-clipboard-edits`
|
||||
Apply clipboard contents as edits using the main model's editor format
|
||||
Default: False
|
||||
Environment variable: `AIDER_APPLY_CLIPBOARD_EDITS`
|
||||
|
||||
### `--yes-always`
|
||||
Always say yes to every confirmation
|
||||
Environment variable: `AIDER_YES_ALWAYS`
|
||||
@@ -594,6 +632,10 @@ Aliases:
|
||||
- `--message-file MESSAGE_FILE`
|
||||
- `-f MESSAGE_FILE`
|
||||
|
||||
### `--load LOAD_FILE`
|
||||
Load and execute /commands from a file on launch
|
||||
Environment variable: `AIDER_LOAD`
|
||||
|
||||
### `--encoding VALUE`
|
||||
Specify the encoding for input and output (default: utf-8)
|
||||
Default: utf-8
|
||||
@@ -606,12 +648,14 @@ Aliases:
|
||||
- `--config CONFIG_FILE`
|
||||
|
||||
### `--gui`
|
||||
Run aider in your browser
|
||||
Run aider in your browser (default: False)
|
||||
Default: False
|
||||
Environment variable: `AIDER_GUI`
|
||||
Aliases:
|
||||
- `--gui`
|
||||
- `--no-gui`
|
||||
- `--browser`
|
||||
- `--no-browser`
|
||||
|
||||
### `--suggest-shell-commands`
|
||||
Enable/disable suggesting shell commands (default: True)
|
||||
@@ -621,6 +665,18 @@ Aliases:
|
||||
- `--suggest-shell-commands`
|
||||
- `--no-suggest-shell-commands`
|
||||
|
||||
### `--fancy-input`
|
||||
Enable/disable fancy input with history and completion (default: True)
|
||||
Default: True
|
||||
Environment variable: `AIDER_FANCY_INPUT`
|
||||
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.
|
||||
@@ -212,3 +238,24 @@ 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.
|
||||
Aider is
|
||||
[open source and available on GitHub](https://github.com/Aider-AI/aider)
|
||||
under an
|
||||
[Apache 2.0 license](https://github.com/Aider-AI/aider/blob/main/LICENSE.txt).
|
||||
|
||||
|
||||
<div style="height:80vh"></div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 800
|
||||
nav_order: 100
|
||||
description: Aider is tightly integrated with git.
|
||||
---
|
||||
|
||||
@@ -22,9 +22,16 @@ This keeps your edits separate from aider's edits, and makes sure you never lose
|
||||
|
||||
## In-chat commands
|
||||
|
||||
Aider also allows you to use in-chat commands to `/diff` or `/undo` the last change.
|
||||
To do more complex management of your git history, you cat use raw `git` commands,
|
||||
either by using `/git` within the chat, or with standard git tools outside of aider.
|
||||
Aider also allows you to use
|
||||
[in-chat commands](/docs/usage/commands.html)
|
||||
to perform git operations:
|
||||
|
||||
- `/diff` will show all the file changes since the last message you sent.
|
||||
- `/undo` will undo and discard the last change.
|
||||
- `/commit` to commit all dirty changes with a sensible commit message.
|
||||
- `/git` will let you run raw git commands to do more complex management of your git history.
|
||||
|
||||
You can also manage your git history outside of aider with your preferred git tools.
|
||||
|
||||
## Disabling git integration
|
||||
|
||||
@@ -36,6 +43,8 @@ While it is not recommended, you can disable aider's use of git in a few ways:
|
||||
|
||||
## Commit messages
|
||||
|
||||
Aider sends the `--weak-model` a copy of the diffs and the chat history
|
||||
and asks it to produce a commit message.
|
||||
By default, aider creates commit messages which follow
|
||||
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
||||
|
||||
@@ -45,6 +54,7 @@ with the `--commit-prompt` option.
|
||||
You can place that on the command line, or
|
||||
[configure it via a config file or environment variables](https://aider.chat/docs/config.html).
|
||||
|
||||
|
||||
## Commit attribution
|
||||
|
||||
Aider marks commits that it either authored or committed.
|
||||
|
||||
@@ -9,6 +9,10 @@ nav_order: 10
|
||||
- TOC
|
||||
{:toc}
|
||||
|
||||
## Python version
|
||||
|
||||
Aider currently works with python 3.9-3.12.
|
||||
|
||||
## Install git
|
||||
|
||||
Make sure you have git installed.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 900
|
||||
nav_order: 200
|
||||
description: Aider supports pretty much all popular coding languages.
|
||||
---
|
||||
# Supported languages
|
||||
|
||||
@@ -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,79 +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');
|
||||
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 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: {
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateChart();
|
||||
});
|
||||
{% include edit-leaderboard.js %}
|
||||
</script>
|
||||
<style>
|
||||
tr.selected {
|
||||
@@ -156,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>
|
||||
@@ -183,79 +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: {
|
||||
yAxes: [{
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateChart();
|
||||
});
|
||||
{% include refactor-leaderboard.js %}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -321,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.')}")
|
||||
]]]-->
|
||||
October 04, 2024.
|
||||
November 21, 2024.
|
||||
<!--[[[end]]]-->
|
||||
</p>
|
||||
|
||||
111
aider/website/docs/legal/contributor-agreement.md
Normal file
111
aider/website/docs/legal/contributor-agreement.md
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
Individual Contributor License Agreement
|
||||
|
||||
Thank you for your interest in Aider AI LLC ("Aider AI").
|
||||
To clarify the intellectual property license
|
||||
granted with Contributions from any person or entity, Aider AI
|
||||
must have on file a signed Contributor License Agreement ("CLA")
|
||||
from each Contributor, indicating agreement with the license
|
||||
terms below. This agreement is for your protection as a Contributor
|
||||
as well as the protection of Aider AI and its users. It does not
|
||||
change your rights to use your own Contributions for any other purpose.
|
||||
|
||||
Please complete and sign this Agreement. Read this document carefully
|
||||
before signing and keep a copy for your records.
|
||||
|
||||
You accept and agree to the following terms and conditions for Your
|
||||
Contributions (present and future) that you submit to Aider AI.
|
||||
Except for the license granted herein to Aider AI and recipients
|
||||
of software distributed by Aider AI, You reserve all right, title,
|
||||
and interest in and to Your Contributions.
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"You" (or "Your") shall mean the copyright owner or legal entity
|
||||
authorized by the copyright owner that is making this Agreement
|
||||
with Aider AI. For legal entities, the entity making a
|
||||
Contribution and all other entities that control, are controlled
|
||||
by, or are under common control with that entity are considered to
|
||||
be a single Contributor. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"Contribution" shall mean any original work of authorship,
|
||||
including any modifications or additions to an existing work, that
|
||||
is intentionally submitted by You to Aider AI for inclusion
|
||||
in, or documentation of, any of the products owned or managed by
|
||||
Aider AI (the "Work"). For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written
|
||||
communication sent to Aider AI or its representatives,
|
||||
including but not limited to communication on electronic mailing
|
||||
lists, source code control systems, and issue tracking systems that
|
||||
are managed by, or on behalf of, Aider AI for the purpose of
|
||||
discussing and improving the Work, but excluding communication that
|
||||
is conspicuously marked or otherwise designated in writing by You
|
||||
as "Not a Contribution."
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this Agreement, You hereby grant to Aider AI and to
|
||||
recipients of software distributed by Aider AI a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare derivative works of,
|
||||
publicly display, publicly perform, sublicense, and distribute Your
|
||||
Contributions and such derivative works.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this Agreement, You hereby grant to Aider AI and to
|
||||
recipients of software distributed by Aider AI a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the
|
||||
Work, where such license applies only to those patent claims
|
||||
licensable by You that are necessarily infringed by Your
|
||||
Contribution(s) alone or by combination of Your Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If any
|
||||
entity institutes patent litigation against You or any other entity
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that your Contribution, or the Work to which you have contributed,
|
||||
constitutes direct or contributory patent infringement, then any
|
||||
patent licenses granted to that entity under this Agreement for
|
||||
that Contribution or Work shall terminate as of the date such
|
||||
litigation is filed.
|
||||
|
||||
4. You represent that you are legally entitled to grant the above
|
||||
license. If your employer(s) has rights to intellectual property
|
||||
that you create that includes your Contributions, you represent
|
||||
that you have received permission to make Contributions on behalf
|
||||
of that employer, that your employer has waived such rights for
|
||||
your Contributions to Aider AI, or that your employer has
|
||||
executed a separate Corporate CLA with Aider AI.
|
||||
|
||||
5. You represent that each of Your Contributions is Your original
|
||||
creation (see section 7 for submissions on behalf of others). You
|
||||
represent that Your Contribution submissions include complete
|
||||
details of any third-party license or other restriction (including,
|
||||
but not limited to, related patents and trademarks) of which you
|
||||
are personally aware and which are associated with any part of Your
|
||||
Contributions.
|
||||
|
||||
6. You are not expected to provide support for Your Contributions,
|
||||
except to the extent You desire to provide support. You may provide
|
||||
support for free, for a fee, or not at all. Unless required by
|
||||
applicable law or agreed to in writing, You provide Your
|
||||
Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
|
||||
OF ANY KIND, either express or implied, including, without
|
||||
limitation, any warranties or conditions of TITLE, NON-
|
||||
INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
7. Should You wish to submit work that is not Your original creation,
|
||||
You may submit it to Aider AI separately from any
|
||||
Contribution, identifying the complete details of its source and of
|
||||
any license or other restriction (including, but not limited to,
|
||||
related patents, trademarks, and license agreements) of which you
|
||||
are personally aware, and conspicuously marking the work as
|
||||
"Submitted on behalf of a third-party: [named here]".
|
||||
|
||||
8. You agree to notify Aider AI of any facts or circumstances of
|
||||
which you become aware that would make these representations
|
||||
inaccurate in any respect.
|
||||
|
||||
104
aider/website/docs/legal/privacy.md
Normal file
104
aider/website/docs/legal/privacy.md
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 500
|
||||
---
|
||||
|
||||
# Privacy policy
|
||||
|
||||
[Aider AI LLC](/docs/faq.html#what-is-aider-ai-llc)
|
||||
(“Aider,” “we,” “our,” and/or “us”) values the privacy of individuals who use our website, programming tools, and related services (collectively, our “Services”). This privacy policy (the “Privacy Policy”) explains how we collect, use, and disclose information from users of our Services. By using our Services, you agree to the collection, use, disclosure, and procedures this Privacy Policy describes.
|
||||
|
||||
### Information We Collect
|
||||
|
||||
We may collect a variety of information from or about you or your devices from various sources, as described below.
|
||||
|
||||
### A. Information You Provide to Us.
|
||||
|
||||
**Communications.** If you contact us directly, we may receive additional information about you, such as your name, email address, the contents of a message or attachments that you may send to us, and other information you choose to provide.
|
||||
|
||||
### B. Information We Collect When You Use Our Services.
|
||||
|
||||
**Device Information.** We may receive information about the device and software you use to access our Services, including IP address, device type, device identifiers, web browser type and version, and operating system version.
|
||||
|
||||
**Usage Information.** We may automatically receive information about your interactions with our Services, like the pages or other content you view, referrer information (the website you visited before coming to our Services), and the dates and times of your visits.
|
||||
|
||||
**Analytics Information.** If you use our programming tools, we may receive information about your interactions with the tools, such as how often certain features or commands are used, information about exceptions and errors, and which large language models are used. This information is associated with a randomly generated identifier, not any directly identifiable user information such as your name or email address. Please see the “Your Choices” section below for information on how to disable the collection of this information.
|
||||
|
||||
**Information from Cookies and Other Tracking Technologies.** We and our third-party partners may collect information about your activities on our Services using cookies, pixel tags, SDKs, or other tracking technologies. Our third-party partners, such as analytics and security partners, may also use these technologies to collect information about your online activities over time and across different services.
|
||||
|
||||
|
||||
### How We Use the Information We Collect
|
||||
|
||||
We use the information we collect:
|
||||
|
||||
- To provide, maintain, improve, and enhance our Services;
|
||||
- To understand and analyze how you use our Services and develop new products, services, features, and functionality;
|
||||
- To communicate with you, provide you with updates and other information relating to our Services, provide information that you request, respond to comments and questions, and otherwise provide customer support;
|
||||
- To generate anonymized or aggregate data containing only de-identified, non-personal information that we may use for any lawful purposes such as to publish reports;
|
||||
- To find and prevent fraud and abuse, and respond to trust and safety issues that may arise;
|
||||
- For compliance purposes, including enforcing our legal rights, or as may be required by applicable laws and regulations or requested by any judicial process or governmental agency; and
|
||||
- For other purposes for which we provide specific notice at the time the information is collected.
|
||||
|
||||
### How We Disclose the Information We Collect
|
||||
|
||||
**Affiliates.** We may disclose any information we receive to our current or future affiliates for any of the purposes described in this Privacy Policy.
|
||||
|
||||
**Vendors and Service Providers.** We may disclose any information we receive to vendors and service providers retained in connection with the provision of our Services.
|
||||
|
||||
**Analytics Partners.** We may use analytics services to collect and process certain analytics data to improve our Services, such as by improving the ability of our programming tools to work with LLMs, edit code, and complete user requests.
|
||||
|
||||
**As Required By Law and Similar Disclosures.** We may access, preserve, and disclose your information if we believe doing so is required or appropriate to: (a) comply with law enforcement requests and legal process, such as a court order or subpoena; (b) respond to your requests; or (c) protect your, our, or others’ rights, property, or safety. For the avoidance of doubt, the disclosure of your information may occur if you post any objectionable content on or through the Services.
|
||||
|
||||
**Merger, Sale, or Other Asset Transfers.** We may transfer your information to service providers, advisors, potential transactional partners, or other third parties in connection with the consideration, negotiation, or completion of a corporate transaction in which we are acquired by or merged with another company or we sell, liquidate, or transfer all or a portion of our assets. The use of your information following any of these events will be governed by the provisions of this Privacy Policy in effect at the time the applicable information was collected.
|
||||
|
||||
**Consent.** We may also disclose your information with your permission.
|
||||
|
||||
### Your Choices
|
||||
|
||||
**Analytics Information.** You can turn off analytics collection when using our programming tools. Please visit this
|
||||
[documentation page](/docs/more/analytics.html)
|
||||
for more information about the data collected and your options.
|
||||
|
||||
### Third Parties
|
||||
|
||||
Our Services may contain links to other websites, products, or services that we do not own or operate. We are not responsible for the privacy practices of these third parties. Please be aware that this Privacy Policy does not apply to your activities on these third-party services or any information you disclose to these third parties. We encourage you to read their privacy policies before providing any information to them.
|
||||
|
||||
### Security
|
||||
|
||||
We make reasonable efforts to protect your information by using physical and electronic safeguards designed to improve the security of the information we maintain. However, because no electronic transmission or storage of information can be entirely secure, we can make no guarantees as to the security or privacy of your information.
|
||||
|
||||
### Children’s Privacy
|
||||
|
||||
We do not knowingly collect, maintain, or use personal information from children under 18 years of age, and no part of our Service(s) is directed to children. If you learn that a child has provided us with personal information in violation of this Privacy Policy, then you may alert us at [INSERT EMAIL ADDRESS].
|
||||
|
||||
### International Visitors
|
||||
|
||||
Our Services are hosted in the United States and intended for visitors located within the United States. If you choose to use the Services from the European Union or other regions of the world with laws governing data collection and use that may differ from U.S. law, then please note that you are transferring your personal information outside of those regions to the U.S. for storage and processing. We may also transfer your data from the U.S. to other countries or regions in connection with storage and processing of data, fulfilling your requests, and operating the Services. By providing any information, including personal information, on or to the Services, you consent to such transfer, storage, and processing.
|
||||
|
||||
|
||||
### Changes to this Privacy Policy
|
||||
|
||||
We will post any adjustments to the Privacy Policy on this page, and the revised version will be effective when it is posted. If we materially change the ways in which we use or disclose personal information previously collected from you through the Services, we will notify you through the Services, by email, or other communication.
|
||||
|
||||
### Contact Information
|
||||
|
||||
If you have any questions, comments, or concerns about our processing activities, please email us at privacy@aider.chat.
|
||||
|
||||
----
|
||||
|
||||
<p class="post-date">
|
||||
Last updated
|
||||
<!--[[[cog
|
||||
import subprocess
|
||||
import datetime
|
||||
|
||||
result = subprocess.run(['git', 'log', '-1', '--format=%ct', 'aider/website/docs/legal/privacy.md'], capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
timestamp = int(result.stdout.strip())
|
||||
date = datetime.datetime.fromtimestamp(timestamp)
|
||||
cog.out(f"{date.strftime('%B %d, %Y.')}")
|
||||
]]]-->
|
||||
October 31, 2024.
|
||||
<!--[[[end]]]-->
|
||||
|
||||
</p>
|
||||
@@ -39,6 +39,14 @@ export AWS_PROFILE=your-profile
|
||||
You can add these to your
|
||||
[.env file](/docs/config/dotenv.html).
|
||||
|
||||
## Bedrock with `pipx` installation
|
||||
|
||||
The AWS Bedrock provider requires the `boto3` package in order to function correctly. To use aider installed via `pipx` with AWS Bedrock, you must add the `boto3` dependency to aider's virtual environment by running
|
||||
|
||||
```
|
||||
pipx inject aider boto3
|
||||
```
|
||||
|
||||
|
||||
## Running Aider with Bedrock
|
||||
|
||||
|
||||
@@ -22,12 +22,18 @@ setx OPENAI_API_KEY <key> # Windows, restart shell after setx
|
||||
# Aider uses gpt-4o by default (or use --4o)
|
||||
aider
|
||||
|
||||
# GPT-4 Turbo (1106)
|
||||
aider --4-turbo
|
||||
# GPT-4o
|
||||
aider --4o
|
||||
|
||||
# GPT-3.5 Turbo
|
||||
aider --35-turbo
|
||||
|
||||
# o1-mini
|
||||
aider --model o1-mini
|
||||
|
||||
# o1-preview
|
||||
aider --model o1-preview
|
||||
|
||||
# List models available from OpenAI
|
||||
aider --list-models openai/
|
||||
```
|
||||
|
||||
@@ -84,16 +84,17 @@ cog.out(''.join(lines))
|
||||
- NVIDIA_NIM_API_KEY
|
||||
- OLLAMA_API_KEY
|
||||
- OPENAI_API_KEY
|
||||
- OPENAI_LIKE_API_KEY
|
||||
- OPENROUTER_API_KEY
|
||||
- OR_API_KEY
|
||||
- PALM_API_KEY
|
||||
- PERPLEXITYAI_API_KEY
|
||||
- PREDIBASE_API_KEY
|
||||
- PROVIDER_API_KEY
|
||||
- QDRANT_API_KEY
|
||||
- REPLICATE_API_KEY
|
||||
- TOGETHERAI_API_KEY
|
||||
- VOLCENGINE_API_KEY
|
||||
- VOYAGE_API_KEY
|
||||
- XAI_API_KEY
|
||||
- XINFERENCE_API_KEY
|
||||
<!--[[[end]]]-->
|
||||
|
||||
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/
|
||||
```
|
||||
|
||||
|
||||
122
aider/website/docs/more/analytics.md
Normal file
122
aider/website/docs/more/analytics.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 500
|
||||
description: Opt-in, anonymous, no personal info.
|
||||
---
|
||||
|
||||
# Analytics
|
||||
|
||||
Aider can collect anonymous analytics to help
|
||||
improve aider's ability to work with LLMs, edit code and complete user requests.
|
||||
|
||||
## Opt-in, anonymous, no personal info
|
||||
|
||||
Analytics are only collected if you agree and opt-in.
|
||||
Aider respects your privacy and never collects your code, chat messages, keys or
|
||||
personal info.
|
||||
|
||||
Aider collects information on:
|
||||
|
||||
- which LLMs are used and with how many tokens,
|
||||
- which of aider's edit formats are used,
|
||||
- how often features and commands are used,
|
||||
- information about exceptions and errors,
|
||||
- etc
|
||||
|
||||
These analytics are associated with an anonymous,
|
||||
randomly generated UUID4 user identifier.
|
||||
|
||||
This information helps improve aider by identifying which models, edit formats,
|
||||
features and commands are most used.
|
||||
It also helps uncover bugs that users are experiencing, so that they can be fixed
|
||||
in upcoming releases.
|
||||
|
||||
## Disabling analytics
|
||||
|
||||
You can opt out of analytics forever by running this command one time:
|
||||
|
||||
```
|
||||
aider --analytics-disable
|
||||
```
|
||||
|
||||
## Enabling analytics
|
||||
|
||||
The `--[no-]analytics` switch controls whether analytics are enabled for the
|
||||
current session:
|
||||
|
||||
- `--analytics` will turn on analytics for the current session.
|
||||
This will *not* have any effect if you have permanently disabled analytics
|
||||
with `--analytics-disable`.
|
||||
If this is the first time you have enabled analytics, aider
|
||||
will confirm you wish to opt-in to analytics.
|
||||
- `--no-analytics` will turn off analytics for the current session.
|
||||
- By default, if you don't provide `--analytics` or `--no-analytics`,
|
||||
aider will enable analytics for a random subset of users.
|
||||
This will never happen if you have permanently disabled analytics
|
||||
with `--analytics-disable`.
|
||||
Randomly selected users will be asked if they wish to opt-in to analytics.
|
||||
|
||||
|
||||
## Opting in
|
||||
|
||||
The first time analytics are enabled, you will need to agree to opt-in.
|
||||
|
||||
```
|
||||
aider --analytics
|
||||
|
||||
Aider respects your privacy and never collects your code, prompts, chats, keys or any personal
|
||||
info.
|
||||
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 say "no", analytics will be permanently disabled.
|
||||
|
||||
|
||||
## Details about data being collected
|
||||
|
||||
### Sample analytics data
|
||||
|
||||
To get a better sense of what type of data is collected, you can review some
|
||||
[sample analytics logs](https://github.com/aider-ai/aider/blob/main/aider/website/assets/sample-analytics.jsonl).
|
||||
These are the last 1,000 analytics events from the author's
|
||||
personal use of aider, updated regularly.
|
||||
|
||||
|
||||
### Analytics code
|
||||
|
||||
Since aider is open source, all the places where aider collects analytics
|
||||
are visible in the source code.
|
||||
They can be viewed using
|
||||
[GitHub search](https://github.com/search?q=repo%3Aaider-ai%2Faider+%22.event%28%22&type=code).
|
||||
|
||||
|
||||
### Logging and inspecting analytics
|
||||
|
||||
You can get a full log of the analytics that aider is collecting,
|
||||
in case you would like to audit or inspect this data.
|
||||
|
||||
```
|
||||
aider --analytics-log filename.jsonl
|
||||
```
|
||||
|
||||
If you want to just log analytics without reporting them, you can do:
|
||||
|
||||
```
|
||||
aider --analytics-log filename.jsonl --no-analytics
|
||||
```
|
||||
|
||||
|
||||
## Reporting issues
|
||||
|
||||
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/aider-ai/aider/issues).
|
||||
|
||||
## Privacy policy
|
||||
|
||||
Please see aider's
|
||||
[privacy policy](/docs/legal/privacy.html)
|
||||
for more details.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 960
|
||||
nav_order: 490
|
||||
description: Aider uses various "edit formats" to let LLMs edit source files.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 920
|
||||
nav_order: 480
|
||||
description: Aider can handle "infinite output" from models that support prefill.
|
||||
---
|
||||
|
||||
@@ -55,7 +55,11 @@ 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
|
||||
- claude-3-5-haiku-20241022
|
||||
- claude-3-5-sonnet-20240620
|
||||
- claude-3-5-sonnet-20241022
|
||||
- claude-3-haiku-20240307
|
||||
- claude-3-opus-20240229
|
||||
- claude-3-sonnet-20240229
|
||||
@@ -63,6 +67,7 @@ cog.out(model_list)
|
||||
- codestral/codestral-latest
|
||||
- deepseek-chat
|
||||
- deepseek-coder
|
||||
- eu.anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||
- mistral/codestral-2405
|
||||
- mistral/codestral-latest
|
||||
- mistral/codestral-mamba-latest
|
||||
@@ -83,9 +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]]]-->
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
parent: More info
|
||||
highlight_image: /assets/robot-ast.png
|
||||
nav_order: 900
|
||||
nav_order: 300
|
||||
description: Aider uses a map of your git repository to provide code context to LLMs.
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
parent: More info
|
||||
nav_order: 900
|
||||
nav_order: 400
|
||||
description: You can script aider via the command line or python.
|
||||
---
|
||||
|
||||
@@ -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,10 +31,12 @@ 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 |
|
||||
| **/lint** | Lint and fix in-chat files or all dirty files if none in chat |
|
||||
| **/load** | Load and execute commands from a file |
|
||||
| **/ls** | List all known files and indicate which are included in the chat session |
|
||||
| **/map** | Print out the current repository map |
|
||||
| **/map-refresh** | Force a refresh of the repository map |
|
||||
@@ -39,6 +48,7 @@ cog.out(get_help_md())
|
||||
| **/report** | Report a problem by opening a GitHub Issue |
|
||||
| **/reset** | Drop all files and clear the chat history |
|
||||
| **/run** | Run a shell command and optionally add the output to the chat (alias: !) |
|
||||
| **/save** | Save commands to a file that can reconstruct the current chat session's files |
|
||||
| **/settings** | Print out the current settings |
|
||||
| **/test** | Run a shell command and add the output to the chat on non-zero exit code |
|
||||
| **/tokens** | Report on the number of tokens used by the current chat context |
|
||||
@@ -67,6 +77,8 @@ The interactive prompt is built with [prompt-toolkit](https://github.com/prompt-
|
||||
|
||||
### Emacs
|
||||
|
||||
- `Up Arrow` : Scroll back through previously sent messages.
|
||||
- `Down Arrow` : Scroll forward through previously sent messages.
|
||||
- `Ctrl-A` : Move cursor to the start of the line.
|
||||
- `Ctrl-B` : Move cursor back one character.
|
||||
- `Ctrl-D` : Delete the character under the cursor.
|
||||
@@ -83,6 +95,8 @@ The interactive prompt is built with [prompt-toolkit](https://github.com/prompt-
|
||||
|
||||
To use vi/vim keybindings, run aider with the `--vim` switch.
|
||||
|
||||
- `Up Arrow` : Scroll back through previously sent messages.
|
||||
- `Down Arrow` : Scroll forward through previously sent messages.
|
||||
- `Esc` : Switch to command mode.
|
||||
- `i` : Switch to insert mode.
|
||||
- `a` : Move cursor one character to the right and switch to insert mode.
|
||||
|
||||
@@ -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]]]
|
||||
]
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
#
|
||||
# pip-compile --output-file=requirements.txt requirements/requirements.in
|
||||
#
|
||||
aiohappyeyeballs==2.4.0
|
||||
aiohappyeyeballs==2.4.3
|
||||
# via aiohttp
|
||||
aiohttp==3.10.5
|
||||
aiohttp==3.11.2
|
||||
# via litellm
|
||||
aiosignal==1.3.1
|
||||
# via aiohttp
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
anyio==4.6.0
|
||||
anyio==4.6.2.post1
|
||||
# via
|
||||
# httpx
|
||||
# openai
|
||||
@@ -22,7 +22,9 @@ attrs==24.2.0
|
||||
# jsonschema
|
||||
# referencing
|
||||
backoff==2.2.1
|
||||
# via -r requirements/requirements.in
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# posthog
|
||||
beautifulsoup4==4.12.3
|
||||
# via -r requirements/requirements.in
|
||||
certifi==2024.8.30
|
||||
@@ -34,13 +36,13 @@ cffi==1.17.1
|
||||
# via
|
||||
# sounddevice
|
||||
# soundfile
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==3.4.0
|
||||
# via requests
|
||||
click==8.1.7
|
||||
# via litellm
|
||||
configargparse==1.7
|
||||
# via -r requirements/requirements.in
|
||||
diff-match-patch==20230430
|
||||
diff-match-patch==20241021
|
||||
# via -r requirements/requirements.in
|
||||
diskcache==5.6.3
|
||||
# via -r requirements/requirements.in
|
||||
@@ -50,11 +52,11 @@ filelock==3.16.1
|
||||
# via huggingface-hub
|
||||
flake8==7.1.1
|
||||
# via -r requirements/requirements.in
|
||||
frozenlist==1.4.1
|
||||
frozenlist==1.5.0
|
||||
# via
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
fsspec==2024.9.0
|
||||
fsspec==2024.10.0
|
||||
# via huggingface-hub
|
||||
gitdb==4.0.11
|
||||
# via gitpython
|
||||
@@ -64,11 +66,11 @@ grep-ast==0.3.3
|
||||
# via -r requirements/requirements.in
|
||||
h11==0.14.0
|
||||
# via httpcore
|
||||
httpcore==1.0.5
|
||||
httpcore==1.0.6
|
||||
# via httpx
|
||||
httpx==0.27.2
|
||||
# via openai
|
||||
huggingface-hub==0.25.0
|
||||
huggingface-hub==0.26.2
|
||||
# via tokenizers
|
||||
idna==3.10
|
||||
# via
|
||||
@@ -84,26 +86,30 @@ importlib-resources==6.4.5
|
||||
# via -r requirements/requirements.in
|
||||
jinja2==3.1.4
|
||||
# via litellm
|
||||
jiter==0.5.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
|
||||
# -r requirements/requirements.in
|
||||
# litellm
|
||||
jsonschema-specifications==2023.12.1
|
||||
jsonschema-specifications==2024.10.1
|
||||
# via jsonschema
|
||||
litellm==1.47.0
|
||||
litellm==1.52.8
|
||||
# via -r requirements/requirements.in
|
||||
markdown-it-py==3.0.0
|
||||
# via rich
|
||||
markupsafe==2.1.5
|
||||
markupsafe==3.0.2
|
||||
# via jinja2
|
||||
mccabe==0.7.0
|
||||
# via flake8
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
mixpanel==4.10.1
|
||||
# via -r requirements/requirements.in
|
||||
monotonic==1.6
|
||||
# via posthog
|
||||
multidict==6.1.0
|
||||
# via
|
||||
# aiohttp
|
||||
@@ -114,9 +120,9 @@ numpy==1.26.4
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# scipy
|
||||
openai==1.47.0
|
||||
openai==1.54.4
|
||||
# via litellm
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# huggingface-hub
|
||||
@@ -128,9 +134,15 @@ pexpect==4.9.0
|
||||
# via -r requirements/requirements.in
|
||||
pillow==10.4.0
|
||||
# via -r requirements/requirements.in
|
||||
prompt-toolkit==3.0.47
|
||||
posthog==3.7.0
|
||||
# via -r requirements/requirements.in
|
||||
psutil==6.0.0
|
||||
prompt-toolkit==3.0.48
|
||||
# via -r requirements/requirements.in
|
||||
propcache==0.2.0
|
||||
# via
|
||||
# aiohttp
|
||||
# yarl
|
||||
psutil==6.1.0
|
||||
# via -r requirements/requirements.in
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
@@ -150,10 +162,12 @@ pyflakes==3.2.0
|
||||
# via flake8
|
||||
pygments==2.18.0
|
||||
# via rich
|
||||
pypandoc==1.13
|
||||
pypandoc==1.14
|
||||
# via -r requirements/requirements.in
|
||||
pyperclip==1.9.0
|
||||
# via -r requirements/requirements.in
|
||||
python-dateutil==2.9.0.post0
|
||||
# via posthog
|
||||
python-dotenv==1.0.1
|
||||
# via litellm
|
||||
pyyaml==6.0.2
|
||||
@@ -164,21 +178,28 @@ referencing==0.35.1
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
regex==2024.9.11
|
||||
regex==2024.11.6
|
||||
# via tiktoken
|
||||
requests==2.32.3
|
||||
# via
|
||||
# huggingface-hub
|
||||
# litellm
|
||||
# mixpanel
|
||||
# posthog
|
||||
# tiktoken
|
||||
rich==13.8.1
|
||||
rich==13.9.4
|
||||
# via -r requirements/requirements.in
|
||||
rpds-py==0.20.0
|
||||
rpds-py==0.21.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
scipy==1.13.1
|
||||
# via -r requirements/requirements.in
|
||||
six==1.16.0
|
||||
# via
|
||||
# mixpanel
|
||||
# posthog
|
||||
# python-dateutil
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sniffio==1.3.1
|
||||
@@ -186,19 +207,19 @@ sniffio==1.3.1
|
||||
# anyio
|
||||
# httpx
|
||||
# openai
|
||||
sounddevice==0.5.0
|
||||
sounddevice==0.5.1
|
||||
# via -r requirements/requirements.in
|
||||
soundfile==0.12.1
|
||||
# via -r requirements/requirements.in
|
||||
soupsieve==2.6
|
||||
# via beautifulsoup4
|
||||
tiktoken==0.7.0
|
||||
tiktoken==0.8.0
|
||||
# via litellm
|
||||
tokenizers==0.19.1
|
||||
# via
|
||||
# -r requirements/requirements.in
|
||||
# litellm
|
||||
tqdm==4.66.5
|
||||
tqdm==4.67.0
|
||||
# via
|
||||
# huggingface-hub
|
||||
# openai
|
||||
@@ -215,10 +236,12 @@ typing-extensions==4.12.2
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
urllib3==2.2.3
|
||||
# via requests
|
||||
# via
|
||||
# mixpanel
|
||||
# requests
|
||||
wcwidth==0.2.13
|
||||
# via prompt-toolkit
|
||||
yarl==1.11.1
|
||||
yarl==1.17.1
|
||||
# via aiohttp
|
||||
zipp==3.20.2
|
||||
zipp==3.21.0
|
||||
# via importlib-metadata
|
||||
|
||||
@@ -2,144 +2,212 @@
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in
|
||||
# pip-compile --constraint=requirements.txt --constraint=requirements/requirements-dev.txt --constraint=requirements/requirements-help.txt --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in
|
||||
#
|
||||
altair==5.4.1
|
||||
# via streamlit
|
||||
attrs==24.2.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# jsonschema
|
||||
# referencing
|
||||
blinker==1.8.2
|
||||
blinker==1.9.0
|
||||
# via streamlit
|
||||
cachetools==5.5.0
|
||||
# via streamlit
|
||||
certifi==2024.8.30
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# requests
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==3.4.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# streamlit
|
||||
gitdb==4.0.11
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# gitpython
|
||||
gitpython==3.1.43
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# streamlit
|
||||
idna==3.10
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# requests
|
||||
jinja2==3.1.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# altair
|
||||
# pydeck
|
||||
jsonschema==4.23.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# altair
|
||||
jsonschema-specifications==2023.12.1
|
||||
jsonschema-specifications==2024.10.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# jsonschema
|
||||
markdown-it-py==3.0.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# rich
|
||||
markupsafe==2.1.5
|
||||
markupsafe==3.0.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# jinja2
|
||||
mdurl==0.1.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# markdown-it-py
|
||||
narwhals==1.8.2
|
||||
narwhals==1.13.5
|
||||
# via altair
|
||||
numpy==1.26.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# pandas
|
||||
# pyarrow
|
||||
# pydeck
|
||||
# streamlit
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# altair
|
||||
# streamlit
|
||||
pandas==2.2.3
|
||||
# via streamlit
|
||||
# via
|
||||
# -c requirements/requirements-dev.txt
|
||||
# streamlit
|
||||
pillow==10.4.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# streamlit
|
||||
protobuf==5.28.2
|
||||
protobuf==5.28.3
|
||||
# via streamlit
|
||||
pyarrow==17.0.0
|
||||
pyarrow==18.0.0
|
||||
# via streamlit
|
||||
pydeck==0.9.1
|
||||
# via streamlit
|
||||
pygments==2.18.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# rich
|
||||
python-dateutil==2.9.0.post0
|
||||
# via pandas
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# pandas
|
||||
pytz==2024.2
|
||||
# via pandas
|
||||
# via
|
||||
# -c requirements/requirements-dev.txt
|
||||
# pandas
|
||||
referencing==0.35.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
requests==2.32.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# streamlit
|
||||
rich==13.8.1
|
||||
rich==13.9.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# streamlit
|
||||
rpds-py==0.20.0
|
||||
rpds-py==0.21.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# jsonschema
|
||||
# referencing
|
||||
six==1.16.0
|
||||
# via python-dateutil
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# python-dateutil
|
||||
smmap==5.0.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# gitdb
|
||||
streamlit==1.38.0
|
||||
streamlit==1.40.1
|
||||
# via -r requirements/requirements-browser.in
|
||||
tenacity==8.5.0
|
||||
# via streamlit
|
||||
# via
|
||||
# -c requirements/requirements-help.txt
|
||||
# streamlit
|
||||
toml==0.10.2
|
||||
# via streamlit
|
||||
tornado==6.4.1
|
||||
# via streamlit
|
||||
typing-extensions==4.12.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# altair
|
||||
# streamlit
|
||||
tzdata==2024.1
|
||||
# via pandas
|
||||
tzdata==2024.2
|
||||
# via
|
||||
# -c requirements/requirements-dev.txt
|
||||
# pandas
|
||||
urllib3==2.2.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# requests
|
||||
watchdog==4.0.2
|
||||
# via -r requirements/requirements-browser.in
|
||||
|
||||
@@ -2,26 +2,29 @@
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/requirements-dev.txt requirements/requirements-dev.in
|
||||
# pip-compile --constraint=requirements.txt --output-file=requirements/requirements-dev.txt requirements/requirements-dev.in
|
||||
#
|
||||
alabaster==0.7.16
|
||||
alabaster==1.0.0
|
||||
# via sphinx
|
||||
babel==2.16.0
|
||||
# via sphinx
|
||||
build==1.2.2
|
||||
build==1.2.2.post1
|
||||
# via pip-tools
|
||||
certifi==2024.8.30
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# requests
|
||||
cfgv==3.4.0
|
||||
# via pre-commit
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==3.4.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# pip-tools
|
||||
# typer
|
||||
@@ -29,30 +32,32 @@ 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
|
||||
dill==0.3.8
|
||||
dill==0.3.9
|
||||
# via
|
||||
# multiprocess
|
||||
# pathos
|
||||
distlib==0.3.8
|
||||
distlib==0.3.9
|
||||
# via virtualenv
|
||||
docutils==0.20.1
|
||||
docutils==0.21.2
|
||||
# via
|
||||
# sphinx
|
||||
# sphinx-rtd-theme
|
||||
filelock==3.16.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# virtualenv
|
||||
fonttools==4.53.1
|
||||
fonttools==4.55.0
|
||||
# via matplotlib
|
||||
identify==2.6.1
|
||||
identify==2.6.2
|
||||
# via pre-commit
|
||||
idna==3.10
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# requests
|
||||
imagesize==1.4.1
|
||||
@@ -63,6 +68,7 @@ iniconfig==2.0.0
|
||||
# via pytest
|
||||
jinja2==3.1.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# sphinx
|
||||
kiwisolver==1.4.7
|
||||
@@ -71,30 +77,35 @@ lox==0.12.0
|
||||
# via -r requirements/requirements-dev.in
|
||||
markdown-it-py==3.0.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# rich
|
||||
markupsafe==2.1.5
|
||||
markupsafe==3.0.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# jinja2
|
||||
matplotlib==3.9.2
|
||||
# via -r requirements/requirements-dev.in
|
||||
mdurl==0.1.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# markdown-it-py
|
||||
multiprocess==0.70.16
|
||||
multiprocess==0.70.17
|
||||
# via pathos
|
||||
nodeenv==1.9.1
|
||||
# via pre-commit
|
||||
numpy==1.26.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# contourpy
|
||||
# matplotlib
|
||||
# pandas
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# build
|
||||
# matplotlib
|
||||
@@ -102,10 +113,11 @@ packaging==24.1
|
||||
# sphinx
|
||||
pandas==2.2.3
|
||||
# via -r requirements/requirements-dev.in
|
||||
pathos==0.3.2
|
||||
pathos==0.3.3
|
||||
# via lox
|
||||
pillow==10.4.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# matplotlib
|
||||
pip-tools==7.4.1
|
||||
@@ -114,20 +126,21 @@ platformdirs==4.3.6
|
||||
# via virtualenv
|
||||
pluggy==1.5.0
|
||||
# via pytest
|
||||
pox==0.3.4
|
||||
pox==0.3.5
|
||||
# via pathos
|
||||
ppft==1.7.6.8
|
||||
ppft==1.7.6.9
|
||||
# via pathos
|
||||
pre-commit==3.8.0
|
||||
pre-commit==4.0.1
|
||||
# via -r requirements/requirements-dev.in
|
||||
pygments==2.18.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# rich
|
||||
# sphinx
|
||||
pyparsing==3.1.4
|
||||
pyparsing==3.2.0
|
||||
# via matplotlib
|
||||
pyproject-hooks==1.1.0
|
||||
pyproject-hooks==1.2.0
|
||||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
@@ -135,20 +148,25 @@ pytest==8.3.3
|
||||
# via -r requirements/requirements-dev.in
|
||||
python-dateutil==2.9.0.post0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# matplotlib
|
||||
# pandas
|
||||
pytz==2024.2
|
||||
# via pandas
|
||||
pyyaml==6.0.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# pre-commit
|
||||
requests==2.32.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# sphinx
|
||||
rich==13.8.1
|
||||
rich==13.9.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# typer
|
||||
semver==3.0.2
|
||||
@@ -156,14 +174,17 @@ semver==3.0.2
|
||||
shellingham==1.5.4
|
||||
# via typer
|
||||
six==1.16.0
|
||||
# via python-dateutil
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# python-dateutil
|
||||
snowballstemmer==2.2.0
|
||||
# via sphinx
|
||||
sphinx==7.4.7
|
||||
sphinx==8.1.3
|
||||
# via
|
||||
# sphinx-rtd-theme
|
||||
# sphinxcontrib-jquery
|
||||
sphinx-rtd-theme==2.0.0
|
||||
sphinx-rtd-theme==3.0.2
|
||||
# via lox
|
||||
sphinxcontrib-applehelp==2.0.0
|
||||
# via sphinx
|
||||
@@ -179,21 +200,23 @@ 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
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# typer
|
||||
tzdata==2024.1
|
||||
tzdata==2024.2
|
||||
# via pandas
|
||||
urllib3==2.2.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# requests
|
||||
virtualenv==20.26.5
|
||||
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:
|
||||
|
||||
@@ -2,46 +2,58 @@
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/requirements-help.txt requirements/requirements-help.in
|
||||
# pip-compile --constraint=requirements.txt --constraint=requirements/requirements-dev.txt --output-file=requirements/requirements-help.txt requirements/requirements-help.in
|
||||
#
|
||||
aiohappyeyeballs==2.4.0
|
||||
aiohappyeyeballs==2.4.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
aiohttp==3.10.5
|
||||
aiohttp==3.11.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
aiosignal==1.3.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
annotated-types==0.7.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# pydantic
|
||||
anyio==4.6.0
|
||||
anyio==4.6.2.post1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# httpx
|
||||
attrs==24.2.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
certifi==2024.8.30
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# httpcore
|
||||
# httpx
|
||||
# requests
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==3.4.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# nltk
|
||||
dataclasses-json==0.6.7
|
||||
# via llama-index-core
|
||||
@@ -51,17 +63,23 @@ dirtyjson==1.0.8
|
||||
# via llama-index-core
|
||||
filelock==3.16.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# huggingface-hub
|
||||
# torch
|
||||
# transformers
|
||||
frozenlist==1.4.1
|
||||
filetype==1.2.0
|
||||
# via llama-index-core
|
||||
frozenlist==1.5.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
fsspec==2024.9.0
|
||||
fsspec==2024.10.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
@@ -72,18 +90,22 @@ greenlet==3.0.3
|
||||
# sqlalchemy
|
||||
h11==0.14.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# httpcore
|
||||
httpcore==1.0.5
|
||||
httpcore==1.0.6
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# httpx
|
||||
httpx==0.27.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# llama-index-core
|
||||
huggingface-hub[inference]==0.25.0
|
||||
huggingface-hub[inference]==0.26.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# llama-index-embeddings-huggingface
|
||||
# sentence-transformers
|
||||
@@ -91,37 +113,42 @@ huggingface-hub[inference]==0.25.0
|
||||
# transformers
|
||||
idna==3.10
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# anyio
|
||||
# httpx
|
||||
# requests
|
||||
# yarl
|
||||
jinja2==3.1.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# torch
|
||||
joblib==1.4.2
|
||||
# via
|
||||
# nltk
|
||||
# scikit-learn
|
||||
llama-index-core==0.11.11
|
||||
llama-index-core==0.11.23
|
||||
# via
|
||||
# -r requirements/requirements-help.in
|
||||
# llama-index-embeddings-huggingface
|
||||
llama-index-embeddings-huggingface==0.3.1
|
||||
# via -r requirements/requirements-help.in
|
||||
markupsafe==2.1.5
|
||||
markupsafe==3.0.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# jinja2
|
||||
marshmallow==3.22.0
|
||||
marshmallow==3.23.1
|
||||
# via dataclasses-json
|
||||
minijinja==2.2.0
|
||||
# via huggingface-hub
|
||||
mpmath==1.3.0
|
||||
# via sympy
|
||||
multidict==6.1.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
# yarl
|
||||
@@ -131,6 +158,7 @@ nest-asyncio==1.6.0
|
||||
# via llama-index-core
|
||||
networkx==3.2.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# llama-index-core
|
||||
# torch
|
||||
@@ -138,45 +166,64 @@ nltk==3.9.1
|
||||
# via llama-index-core
|
||||
numpy==1.26.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# llama-index-core
|
||||
# scikit-learn
|
||||
# scipy
|
||||
# transformers
|
||||
packaging==24.1
|
||||
packaging==24.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# huggingface-hub
|
||||
# marshmallow
|
||||
# transformers
|
||||
pillow==10.4.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# llama-index-core
|
||||
# sentence-transformers
|
||||
propcache==0.2.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
# yarl
|
||||
pydantic==2.9.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# llama-index-core
|
||||
pydantic-core==2.23.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# pydantic
|
||||
pyyaml==6.0.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
# transformers
|
||||
regex==2024.9.11
|
||||
regex==2024.11.6
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# nltk
|
||||
# tiktoken
|
||||
# transformers
|
||||
requests==2.32.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
# tiktoken
|
||||
@@ -187,17 +234,19 @@ scikit-learn==1.5.2
|
||||
# via sentence-transformers
|
||||
scipy==1.13.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# scikit-learn
|
||||
# sentence-transformers
|
||||
sentence-transformers==3.1.1
|
||||
sentence-transformers==3.3.0
|
||||
# via llama-index-embeddings-huggingface
|
||||
sniffio==1.3.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# anyio
|
||||
# httpx
|
||||
sqlalchemy[asyncio]==2.0.35
|
||||
sqlalchemy[asyncio]==2.0.36
|
||||
# via
|
||||
# llama-index-core
|
||||
# sqlalchemy
|
||||
@@ -207,18 +256,21 @@ tenacity==8.5.0
|
||||
# via llama-index-core
|
||||
threadpoolctl==3.5.0
|
||||
# via scikit-learn
|
||||
tiktoken==0.7.0
|
||||
tiktoken==0.8.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# llama-index-core
|
||||
tokenizers==0.19.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# transformers
|
||||
torch==2.2.2
|
||||
# via sentence-transformers
|
||||
tqdm==4.66.5
|
||||
tqdm==4.67.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
@@ -229,7 +281,9 @@ transformers==4.44.2
|
||||
# via sentence-transformers
|
||||
typing-extensions==4.12.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# huggingface-hub
|
||||
# llama-index-core
|
||||
# pydantic
|
||||
@@ -243,13 +297,16 @@ typing-inspect==0.9.0
|
||||
# llama-index-core
|
||||
urllib3==2.2.3
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# requests
|
||||
wrapt==1.16.0
|
||||
# via
|
||||
# deprecated
|
||||
# llama-index-core
|
||||
yarl==1.11.1
|
||||
yarl==1.17.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# aiohttp
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
# This file is autogenerated by pip-compile with Python 3.12
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/requirements-playwright.txt requirements/requirements-playwright.in
|
||||
# pip-compile --constraint=requirements.txt --constraint=requirements/requirements-browser.txt --constraint=requirements/requirements-dev.txt --constraint=requirements/requirements-help.txt --output-file=requirements/requirements-playwright.txt requirements/requirements-playwright.in
|
||||
#
|
||||
greenlet==3.0.3
|
||||
# via
|
||||
# -c requirements/requirements-help.txt
|
||||
# -r requirements/requirements-playwright.in
|
||||
# playwright
|
||||
playwright==1.47.0
|
||||
@@ -14,5 +15,9 @@ pyee==12.0.0
|
||||
# via playwright
|
||||
typing-extensions==4.12.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# -c requirements/../requirements.txt
|
||||
# -c requirements/requirements-browser.txt
|
||||
# -c requirements/requirements-dev.txt
|
||||
# -c requirements/requirements-help.txt
|
||||
# pyee
|
||||
|
||||
@@ -17,13 +17,14 @@ sounddevice
|
||||
soundfile
|
||||
beautifulsoup4
|
||||
PyYAML
|
||||
Pillow
|
||||
diff-match-patch
|
||||
pypandoc
|
||||
litellm
|
||||
flake8
|
||||
importlib_resources
|
||||
pyperclip
|
||||
posthog
|
||||
mixpanel
|
||||
pexpect
|
||||
json5
|
||||
psutil
|
||||
@@ -55,3 +56,6 @@ numpy<2
|
||||
# Going past this makes dependencies unresolvable
|
||||
# Seems to be caused by sentence-transformers
|
||||
tokenizers==0.19.1
|
||||
|
||||
# streamlit 1.39.0 depends on this, as far back as 1.22 which is ancient and doesn't have chat ui
|
||||
Pillow<11
|
||||
|
||||
@@ -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")]
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
from collections import defaultdict
|
||||
@@ -21,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"
|
||||
@@ -111,16 +128,187 @@ def comment_and_close_duplicate(issue, oldest_issue):
|
||||
print(f" - Commented and closed issue #{issue['number']}")
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
print("Error: Missing GITHUB_TOKEN environment variable. Please check your .env file.")
|
||||
def find_unlabeled_with_paul_comments(issues):
|
||||
unlabeled_issues = []
|
||||
for issue in issues:
|
||||
# Skip pull requests
|
||||
if "pull_request" in issue:
|
||||
continue
|
||||
|
||||
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:
|
||||
@@ -142,13 +330,12 @@ def main():
|
||||
f" {oldest_issue['html_url']} ({oldest_issue['state']})"
|
||||
)
|
||||
|
||||
# Confirmation prompt
|
||||
confirm = input("Do you want to comment and close duplicate issues? (y/n): ")
|
||||
if confirm.lower() != "y":
|
||||
print("Skipping this group of issues.")
|
||||
continue
|
||||
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)
|
||||
@@ -157,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()
|
||||
|
||||
@@ -3,16 +3,23 @@
|
||||
# exit when any command fails
|
||||
set -e
|
||||
|
||||
# First compile the base requirements
|
||||
pip-compile \
|
||||
requirements/requirements.in \
|
||||
--output-file=requirements.txt \
|
||||
$1
|
||||
|
||||
for SUFFIX in dev help browser playwright; do
|
||||
# Then compile each additional requirements file in sequence
|
||||
SUFFIXES=(dev help browser playwright)
|
||||
CONSTRAINTS="--constraint=requirements.txt"
|
||||
|
||||
for SUFFIX in "${SUFFIXES[@]}"; do
|
||||
pip-compile \
|
||||
requirements/requirements-${SUFFIX}.in \
|
||||
--output-file=requirements/requirements-${SUFFIX}.txt \
|
||||
${CONSTRAINTS} \
|
||||
$1
|
||||
done
|
||||
|
||||
# Add this file as a constraint for the next iteration
|
||||
CONSTRAINTS+=" --constraint=requirements/requirements-${SUFFIX}.txt"
|
||||
done
|
||||
|
||||
@@ -9,6 +9,10 @@ else
|
||||
ARG=$1
|
||||
fi
|
||||
|
||||
if [ "$ARG" != "--check" ]; then
|
||||
tail -1000 ~/.aider/analytics.jsonl > aider/website/assets/sample-analytics.jsonl
|
||||
fi
|
||||
|
||||
# README.md before index.md, because index.md uses cog to include README.md
|
||||
cog $ARG \
|
||||
README.md \
|
||||
@@ -22,4 +26,5 @@ cog $ARG \
|
||||
aider/website/docs/config/adv-model-settings.md \
|
||||
aider/website/docs/leaderboards/index.md \
|
||||
aider/website/docs/llms/other.md \
|
||||
aider/website/docs/more/infinite-output.md
|
||||
aider/website/docs/more/infinite-output.md \
|
||||
aider/website/docs/legal/privacy.md
|
||||
|
||||
@@ -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(
|
||||
|
||||
139
tests/basic/test_analytics.py
Normal file
139
tests/basic/test_analytics.py
Normal file
@@ -0,0 +1,139 @@
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from aider.analytics import Analytics
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_analytics_file():
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
yield f.name
|
||||
os.unlink(f.name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_data_dir(monkeypatch):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
temp_dir = Path(tmpdir)
|
||||
monkeypatch.setattr(Path, "home", lambda: temp_dir)
|
||||
yield temp_dir
|
||||
|
||||
|
||||
def test_analytics_initialization(temp_data_dir):
|
||||
analytics = Analytics(permanently_disable=True)
|
||||
assert analytics.mp is None
|
||||
assert analytics.ph is None
|
||||
assert analytics.permanently_disable is True
|
||||
assert analytics.user_id is not None
|
||||
|
||||
|
||||
def test_analytics_enable_disable(temp_data_dir):
|
||||
analytics = Analytics()
|
||||
analytics.asked_opt_in = True
|
||||
|
||||
analytics.enable()
|
||||
assert analytics.mp is not None
|
||||
assert analytics.ph is not None
|
||||
|
||||
analytics.disable(permanently=False)
|
||||
assert analytics.mp is None
|
||||
assert analytics.ph is None
|
||||
assert analytics.permanently_disable is not True
|
||||
|
||||
analytics.disable(permanently=True)
|
||||
assert analytics.permanently_disable is True
|
||||
|
||||
|
||||
def test_analytics_data_persistence(temp_data_dir):
|
||||
analytics1 = Analytics()
|
||||
user_id = analytics1.user_id
|
||||
|
||||
analytics2 = Analytics()
|
||||
assert analytics2.user_id == user_id
|
||||
|
||||
|
||||
def test_analytics_event_logging(temp_analytics_file, temp_data_dir):
|
||||
analytics = Analytics(logfile=temp_analytics_file)
|
||||
analytics.asked_opt_in = True
|
||||
analytics.enable()
|
||||
|
||||
test_event = "test_event"
|
||||
test_properties = {"test_key": "test_value"}
|
||||
|
||||
with patch.object(analytics.mp, "track") as mock_mp_track:
|
||||
with patch.object(analytics.ph, "capture") as mock_ph_capture:
|
||||
analytics.event(test_event, **test_properties)
|
||||
|
||||
mock_mp_track.assert_called_once()
|
||||
mock_ph_capture.assert_called_once()
|
||||
|
||||
# Verify logfile
|
||||
with open(temp_analytics_file) as f:
|
||||
log_entry = json.loads(f.read().strip())
|
||||
assert log_entry["event"] == test_event
|
||||
assert "test_key" in log_entry["properties"]
|
||||
|
||||
|
||||
def test_system_info(temp_data_dir):
|
||||
analytics = Analytics()
|
||||
sys_info = analytics.get_system_info()
|
||||
|
||||
assert "python_version" in sys_info
|
||||
assert "os_platform" in sys_info
|
||||
assert "os_release" in sys_info
|
||||
assert "machine" in sys_info
|
||||
|
||||
|
||||
def test_need_to_ask(temp_data_dir):
|
||||
analytics = Analytics()
|
||||
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(True) is False
|
||||
|
||||
analytics.permanently_disable = True
|
||||
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():
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import codecs
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -33,7 +34,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -48,7 +49,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_copy(self):
|
||||
# Initialize InputOutput and Coder instances
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -78,7 +79,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_copy_with_cur_messages(self):
|
||||
# Initialize InputOutput and Coder instances
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -108,7 +109,7 @@ class TestCommands(TestCase):
|
||||
" cur_messages"
|
||||
)
|
||||
mock_tool_output.assert_any_call(expected_preview)
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -124,7 +125,7 @@ class TestCommands(TestCase):
|
||||
mock_tool_error.assert_called_once_with("No assistant messages found to copy.")
|
||||
|
||||
def test_cmd_copy_pyperclip_exception(self):
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -147,7 +148,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_add_bad_glob(self):
|
||||
# https://github.com/Aider-AI/aider/issues/293
|
||||
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -157,7 +158,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_with_glob_patterns(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -183,7 +184,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_no_match(self):
|
||||
# yes=False means we will *not* create the file when it is not found
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -197,7 +198,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_no_match_but_make_it(self):
|
||||
# yes=True means we *will* create the file when it is not found
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -214,7 +215,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_drop_directory(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -265,7 +266,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_drop_with_glob_patterns(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -292,7 +293,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_bad_encoding(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -308,7 +309,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_git(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
|
||||
with GitTemporaryDirectory() as tempdir:
|
||||
# Create a file in the temporary directory
|
||||
@@ -329,7 +330,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_tokens(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
@@ -371,7 +372,7 @@ class TestCommands(TestCase):
|
||||
|
||||
os.chdir("subdir")
|
||||
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -387,7 +388,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_from_subdir_again(self):
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -413,7 +414,7 @@ class TestCommands(TestCase):
|
||||
repo.git.add(fname)
|
||||
repo.git.commit("-m", "initial")
|
||||
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -432,7 +433,7 @@ class TestCommands(TestCase):
|
||||
root.mkdir()
|
||||
os.chdir(str(root))
|
||||
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -455,7 +456,7 @@ class TestCommands(TestCase):
|
||||
|
||||
make_repo()
|
||||
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -473,7 +474,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_filename_with_special_chars(self):
|
||||
with ChdirTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -498,7 +499,7 @@ class TestCommands(TestCase):
|
||||
repo.git.add(A=True)
|
||||
repo.git.commit("-m", "Initial commit")
|
||||
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(Model("claude-3-5-sonnet-20240620"), None, io)
|
||||
@@ -538,7 +539,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_dirname_with_special_chars(self):
|
||||
with ChdirTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -556,7 +557,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_dirname_with_special_chars_git(self):
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -578,7 +579,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_abs_filename(self):
|
||||
with ChdirTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -593,7 +594,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_quoted_filename(self):
|
||||
with ChdirTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -621,7 +622,7 @@ class TestCommands(TestCase):
|
||||
# leave a dirty `git rm`
|
||||
repo.git.rm("one.txt")
|
||||
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -642,9 +643,269 @@ class TestCommands(TestCase):
|
||||
del commands
|
||||
del repo
|
||||
|
||||
def test_cmd_save_and_load(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
# Create some test files
|
||||
test_files = {
|
||||
"file1.txt": "Content of file 1",
|
||||
"file2.py": "print('Content of file 2')",
|
||||
"subdir/file3.md": "# Content of file 3",
|
||||
}
|
||||
|
||||
for file_path, content in test_files.items():
|
||||
full_path = Path(repo_dir) / file_path
|
||||
full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
full_path.write_text(content)
|
||||
|
||||
# Add some files as editable and some as read-only
|
||||
commands.cmd_add("file1.txt file2.py")
|
||||
commands.cmd_read_only("subdir/file3.md")
|
||||
|
||||
# Save the session to a file
|
||||
session_file = "test_session.txt"
|
||||
commands.cmd_save(session_file)
|
||||
|
||||
# Verify the session file was created and contains the expected commands
|
||||
self.assertTrue(Path(session_file).exists())
|
||||
with open(session_file, encoding=io.encoding) as f:
|
||||
commands_text = f.read().splitlines()
|
||||
|
||||
# Convert paths to absolute for comparison
|
||||
abs_file1 = str(Path("file1.txt").resolve())
|
||||
abs_file2 = str(Path("file2.py").resolve())
|
||||
abs_file3 = str(Path("subdir/file3.md").resolve())
|
||||
|
||||
# Check each line for matching paths using os.path.samefile
|
||||
found_file1 = found_file2 = found_file3 = False
|
||||
for line in commands_text:
|
||||
if line.startswith("/add "):
|
||||
path = Path(line[5:].strip()).resolve()
|
||||
if os.path.samefile(str(path), abs_file1):
|
||||
found_file1 = True
|
||||
elif os.path.samefile(str(path), abs_file2):
|
||||
found_file2 = True
|
||||
elif line.startswith("/read-only "):
|
||||
path = Path(line[11:]).resolve()
|
||||
if os.path.samefile(str(path), abs_file3):
|
||||
found_file3 = True
|
||||
|
||||
self.assertTrue(found_file1, "file1.txt not found in commands")
|
||||
self.assertTrue(found_file2, "file2.py not found in commands")
|
||||
self.assertTrue(found_file3, "file3.md not found in commands")
|
||||
|
||||
# Clear the current session
|
||||
commands.cmd_reset("")
|
||||
self.assertEqual(len(coder.abs_fnames), 0)
|
||||
self.assertEqual(len(coder.abs_read_only_fnames), 0)
|
||||
|
||||
# Load the session back
|
||||
commands.cmd_load(session_file)
|
||||
|
||||
# Verify files were restored correctly
|
||||
added_files = {Path(coder.get_rel_fname(f)).as_posix() for f in coder.abs_fnames}
|
||||
read_only_files = {
|
||||
Path(coder.get_rel_fname(f)).as_posix() for f in coder.abs_read_only_fnames
|
||||
}
|
||||
|
||||
self.assertEqual(added_files, {"file1.txt", "file2.py"})
|
||||
self.assertEqual(read_only_files, {"subdir/file3.md"})
|
||||
|
||||
# Clean up
|
||||
Path(session_file).unlink()
|
||||
|
||||
def test_cmd_save_and_load_with_external_file(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as external_file:
|
||||
external_file.write("External file content")
|
||||
external_file_path = external_file.name
|
||||
|
||||
try:
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
# Create some test files in the repo
|
||||
test_files = {
|
||||
"file1.txt": "Content of file 1",
|
||||
"file2.py": "print('Content of file 2')",
|
||||
}
|
||||
|
||||
for file_path, content in test_files.items():
|
||||
full_path = Path(repo_dir) / file_path
|
||||
full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
full_path.write_text(content)
|
||||
|
||||
# Add some files as editable and some as read-only
|
||||
commands.cmd_add(str(Path("file1.txt")))
|
||||
commands.cmd_read_only(external_file_path)
|
||||
|
||||
# Save the session to a file
|
||||
session_file = str(Path("test_session.txt"))
|
||||
commands.cmd_save(session_file)
|
||||
|
||||
# Verify the session file was created and contains the expected commands
|
||||
self.assertTrue(Path(session_file).exists())
|
||||
with open(session_file, encoding=io.encoding) as f:
|
||||
commands_text = f.read()
|
||||
commands_text = re.sub(
|
||||
r"/add +", "/add ", commands_text
|
||||
) # Normalize add command spaces
|
||||
self.assertIn("/add file1.txt", commands_text)
|
||||
# Split commands and check each one
|
||||
for line in commands_text.splitlines():
|
||||
if line.startswith("/read-only "):
|
||||
saved_path = line.split(" ", 1)[1]
|
||||
if os.path.samefile(saved_path, external_file_path):
|
||||
break
|
||||
else:
|
||||
self.fail(f"No matching read-only command found for {external_file_path}")
|
||||
|
||||
# Clear the current session
|
||||
commands.cmd_reset("")
|
||||
self.assertEqual(len(coder.abs_fnames), 0)
|
||||
self.assertEqual(len(coder.abs_read_only_fnames), 0)
|
||||
|
||||
# Load the session back
|
||||
commands.cmd_load(session_file)
|
||||
|
||||
# Verify files were restored correctly
|
||||
added_files = {coder.get_rel_fname(f) for f in coder.abs_fnames}
|
||||
read_only_files = {coder.get_rel_fname(f) for f in coder.abs_read_only_fnames}
|
||||
|
||||
self.assertEqual(added_files, {str(Path("file1.txt"))})
|
||||
self.assertTrue(
|
||||
any(os.path.samefile(external_file_path, f) for f in read_only_files)
|
||||
)
|
||||
|
||||
# Clean up
|
||||
Path(session_file).unlink()
|
||||
|
||||
finally:
|
||||
os.unlink(external_file_path)
|
||||
|
||||
def test_cmd_save_and_load_with_multiple_external_files(self):
|
||||
with (
|
||||
tempfile.NamedTemporaryFile(mode="w", delete=False) as external_file1,
|
||||
tempfile.NamedTemporaryFile(mode="w", delete=False) as external_file2,
|
||||
):
|
||||
external_file1.write("External file 1 content")
|
||||
external_file2.write("External file 2 content")
|
||||
external_file1_path = external_file1.name
|
||||
external_file2_path = external_file2.name
|
||||
|
||||
try:
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
# Create some test files in the repo
|
||||
test_files = {
|
||||
"internal1.txt": "Content of internal file 1",
|
||||
"internal2.txt": "Content of internal file 2",
|
||||
}
|
||||
|
||||
for file_path, content in test_files.items():
|
||||
full_path = Path(repo_dir) / file_path
|
||||
full_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
full_path.write_text(content)
|
||||
|
||||
# Add files as editable and read-only
|
||||
commands.cmd_add(str(Path("internal1.txt")))
|
||||
commands.cmd_read_only(external_file1_path)
|
||||
commands.cmd_read_only(external_file2_path)
|
||||
|
||||
# Save the session to a file
|
||||
session_file = str(Path("test_session.txt"))
|
||||
commands.cmd_save(session_file)
|
||||
|
||||
# Verify the session file was created and contains the expected commands
|
||||
self.assertTrue(Path(session_file).exists())
|
||||
with open(session_file, encoding=io.encoding) as f:
|
||||
commands_text = f.read()
|
||||
commands_text = re.sub(
|
||||
r"/add +", "/add ", commands_text
|
||||
) # Normalize add command spaces
|
||||
self.assertIn("/add internal1.txt", commands_text)
|
||||
# Split commands and check each one
|
||||
for line in commands_text.splitlines():
|
||||
if line.startswith("/read-only "):
|
||||
saved_path = line.split(" ", 1)[1]
|
||||
if os.path.samefile(saved_path, external_file1_path):
|
||||
break
|
||||
else:
|
||||
self.fail(f"No matching read-only command found for {external_file1_path}")
|
||||
# Split commands and check each one
|
||||
for line in commands_text.splitlines():
|
||||
if line.startswith("/read-only "):
|
||||
saved_path = line.split(" ", 1)[1]
|
||||
if os.path.samefile(saved_path, external_file2_path):
|
||||
break
|
||||
else:
|
||||
self.fail(f"No matching read-only command found for {external_file2_path}")
|
||||
|
||||
# Clear the current session
|
||||
commands.cmd_reset("")
|
||||
self.assertEqual(len(coder.abs_fnames), 0)
|
||||
self.assertEqual(len(coder.abs_read_only_fnames), 0)
|
||||
|
||||
# Load the session back
|
||||
commands.cmd_load(session_file)
|
||||
|
||||
# Verify files were restored correctly
|
||||
added_files = {coder.get_rel_fname(f) for f in coder.abs_fnames}
|
||||
read_only_files = {coder.get_rel_fname(f) for f in coder.abs_read_only_fnames}
|
||||
|
||||
self.assertEqual(added_files, {str(Path("internal1.txt"))})
|
||||
self.assertTrue(
|
||||
all(
|
||||
any(os.path.samefile(external_path, fname) for fname in read_only_files)
|
||||
for external_path in [external_file1_path, external_file2_path]
|
||||
)
|
||||
)
|
||||
|
||||
# Clean up
|
||||
Path(session_file).unlink()
|
||||
|
||||
finally:
|
||||
os.unlink(external_file1_path)
|
||||
os.unlink(external_file2_path)
|
||||
|
||||
def test_cmd_read_only_with_image_file(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
# Create a test image file
|
||||
test_file = Path(repo_dir) / "test_image.jpg"
|
||||
test_file.write_text("Mock image content")
|
||||
|
||||
# Test with non-vision model
|
||||
commands.cmd_read_only(str(test_file))
|
||||
self.assertEqual(len(coder.abs_read_only_fnames), 0)
|
||||
|
||||
# Test with vision model
|
||||
vision_model = Model("gpt-4-vision-preview")
|
||||
vision_coder = Coder.create(vision_model, None, io)
|
||||
vision_commands = Commands(io, vision_coder)
|
||||
|
||||
vision_commands.cmd_read_only(str(test_file))
|
||||
self.assertEqual(len(vision_coder.abs_read_only_fnames), 1)
|
||||
self.assertTrue(
|
||||
any(
|
||||
os.path.samefile(str(test_file), fname)
|
||||
for fname in vision_coder.abs_read_only_fnames
|
||||
)
|
||||
)
|
||||
|
||||
def test_cmd_read_only_with_glob_pattern(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -679,7 +940,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_read_only_with_recursive_glob(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -706,7 +967,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_read_only_with_nonexistent_glob(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -724,7 +985,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_add_unicode_error(self):
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -742,7 +1003,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_add_read_only_file(self):
|
||||
with GitTemporaryDirectory():
|
||||
# Initialize the Commands and InputOutput objects
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -797,7 +1058,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_test_unbound_local_error(self):
|
||||
with ChdirTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -807,14 +1068,16 @@ 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():
|
||||
repo = git.Repo()
|
||||
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
from aider.coders import Coder
|
||||
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
@@ -839,7 +1102,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_undo_with_dirty_files_not_in_last_commit(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
repo = git.Repo(repo_dir)
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -887,7 +1150,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_undo_with_newly_committed_file(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
repo = git.Repo(repo_dir)
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -923,7 +1186,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_undo_on_first_commit(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
repo = git.Repo(repo_dir)
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -949,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()
|
||||
@@ -991,7 +1274,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_read_only(self):
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1021,14 +1304,54 @@ class TestCommands(TestCase):
|
||||
)
|
||||
)
|
||||
|
||||
def test_cmd_read_only_from_working_dir(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
# Create a subdirectory and a test file within it
|
||||
subdir = Path(repo_dir) / "subdir"
|
||||
subdir.mkdir()
|
||||
test_file = subdir / "test_read_only_file.txt"
|
||||
test_file.write_text("Test content")
|
||||
|
||||
# Change the current working directory to the subdirectory
|
||||
os.chdir(subdir)
|
||||
|
||||
# Test the /read-only command using git_root referenced name
|
||||
commands.cmd_read_only(os.path.join("subdir", "test_read_only_file.txt"))
|
||||
|
||||
# Check if the file was added to abs_read_only_fnames
|
||||
self.assertTrue(
|
||||
any(
|
||||
os.path.samefile(str(test_file.resolve()), fname)
|
||||
for fname in coder.abs_read_only_fnames
|
||||
)
|
||||
)
|
||||
|
||||
# Test dropping the read-only file using git_root referenced name
|
||||
commands.cmd_drop(os.path.join("subdir", "test_read_only_file.txt"))
|
||||
|
||||
# Check if the file was removed from abs_read_only_fnames
|
||||
self.assertFalse(
|
||||
any(
|
||||
os.path.samefile(str(test_file.resolve()), fname)
|
||||
for fname in coder.abs_read_only_fnames
|
||||
)
|
||||
)
|
||||
|
||||
def test_cmd_read_only_with_external_file(self):
|
||||
with tempfile.NamedTemporaryFile(mode="w", delete=False) as external_file:
|
||||
external_file.write("External file content")
|
||||
external_file_path = external_file.name
|
||||
|
||||
try:
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
# Create a test file in the repo
|
||||
repo_file = Path(repo_dir) / "repo_file.txt"
|
||||
repo_file.write_text("Repo file content")
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1059,7 +1382,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_read_only_with_multiple_files(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1090,7 +1413,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_read_only_with_tilde_path(self):
|
||||
with GitTemporaryDirectory():
|
||||
io = InputOutput(pretty=False, yes=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=False)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1125,7 +1448,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_diff(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
repo = git.Repo(repo_dir)
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1189,7 +1512,7 @@ class TestCommands(TestCase):
|
||||
self.assertIn("+Final modified content", diff_output)
|
||||
|
||||
def test_cmd_ask(self):
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1208,7 +1531,7 @@ class TestCommands(TestCase):
|
||||
def test_cmd_lint_with_dirty_file(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
repo = git.Repo(repo_dir)
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
@@ -1245,7 +1568,7 @@ class TestCommands(TestCase):
|
||||
|
||||
def test_cmd_reset(self):
|
||||
with GitTemporaryDirectory() as repo_dir:
|
||||
io = InputOutput(pretty=False, yes=True)
|
||||
io = InputOutput(pretty=False, fancy_input=False, yes=True)
|
||||
coder = Coder.create(self.GPT35, None, io)
|
||||
commands = Commands(io, coder)
|
||||
|
||||
|
||||
@@ -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):
|
||||
@@ -296,6 +297,28 @@ 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_replace_multiple_matches(self):
|
||||
"only replace first occurrence"
|
||||
|
||||
whole = "line1\nline2\nline1\nline3\n"
|
||||
part = "line1\n"
|
||||
replace = "new_line\n"
|
||||
expected_output = "new_line\nline2\nline1\nline3\n"
|
||||
|
||||
result = eb.replace_most_similar_chunk(whole, part, replace)
|
||||
self.assertEqual(result, expected_output)
|
||||
|
||||
def test_replace_multiple_matches_missing_whitespace(self):
|
||||
"only replace first occurrence"
|
||||
|
||||
whole = " line1\n line2\n line1\n line3\n"
|
||||
part = "line1\n"
|
||||
replace = "new_line\n"
|
||||
expected_output = " new_line\n line2\n line1\n line3\n"
|
||||
|
||||
result = eb.replace_most_similar_chunk(whole, part, replace)
|
||||
self.assertEqual(result, expected_output)
|
||||
|
||||
def test_replace_part_with_just_some_missing_leading_whitespace(self):
|
||||
whole = " line1\n line2\n line3\n"
|
||||
part = " line1\n line2\n"
|
||||
@@ -319,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()
|
||||
@@ -482,9 +545,7 @@ two
|
||||
Hope you like it!
|
||||
"""
|
||||
|
||||
edits = list(
|
||||
eb.find_original_update_blocks(edit, valid_fnames=["path/to/a/file1.txt"])
|
||||
)
|
||||
edits = list(eb.find_original_update_blocks(edit, valid_fnames=["path/to/a/file1.txt"]))
|
||||
self.assertEqual(
|
||||
edits,
|
||||
[
|
||||
|
||||
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
|
||||
@@ -14,7 +14,7 @@ from aider.utils import ChdirTemporaryDirectory
|
||||
class TestInputOutput(unittest.TestCase):
|
||||
def test_no_color_environment_variable(self):
|
||||
with patch.dict(os.environ, {"NO_COLOR": "1"}):
|
||||
io = InputOutput()
|
||||
io = InputOutput(fancy_input=False)
|
||||
self.assertFalse(io.pretty)
|
||||
|
||||
def test_autocompleter_get_command_completions(self):
|
||||
@@ -107,7 +107,7 @@ class TestInputOutput(unittest.TestCase):
|
||||
|
||||
@patch("builtins.input", return_value="test input")
|
||||
def test_get_input_is_a_directory_error(self, mock_input):
|
||||
io = InputOutput(pretty=False) # Windows tests throw UnicodeDecodeError
|
||||
io = InputOutput(pretty=False, fancy_input=False) # Windows tests throw UnicodeDecodeError
|
||||
root = "/"
|
||||
rel_fnames = ["existing_file.txt"]
|
||||
addable_rel_fnames = ["new_file.txt"]
|
||||
@@ -121,7 +121,7 @@ class TestInputOutput(unittest.TestCase):
|
||||
|
||||
@patch("builtins.input")
|
||||
def test_confirm_ask_explicit_yes_required(self, mock_input):
|
||||
io = InputOutput(pretty=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False)
|
||||
|
||||
# Test case 1: explicit_yes_required=True, self.yes=True
|
||||
io.yes = True
|
||||
@@ -153,7 +153,7 @@ class TestInputOutput(unittest.TestCase):
|
||||
|
||||
@patch("builtins.input")
|
||||
def test_confirm_ask_with_group(self, mock_input):
|
||||
io = InputOutput(pretty=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False)
|
||||
group = ConfirmGroup()
|
||||
|
||||
# Test case 1: No group preference, user selects 'All'
|
||||
@@ -195,7 +195,7 @@ class TestInputOutput(unittest.TestCase):
|
||||
|
||||
@patch("builtins.input")
|
||||
def test_confirm_ask_yes_no(self, mock_input):
|
||||
io = InputOutput(pretty=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False)
|
||||
|
||||
# Test case 1: User selects 'Yes'
|
||||
mock_input.return_value = "y"
|
||||
@@ -220,7 +220,7 @@ class TestInputOutput(unittest.TestCase):
|
||||
|
||||
@patch("builtins.input", side_effect=["d"])
|
||||
def test_confirm_ask_allow_never(self, mock_input):
|
||||
io = InputOutput(pretty=False)
|
||||
io = InputOutput(pretty=False, fancy_input=False)
|
||||
|
||||
# First call: user selects "Don't ask again"
|
||||
result = io.confirm_ask("Are you sure?", allow_never=True)
|
||||
|
||||
@@ -32,6 +32,8 @@ 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("aider.io.webbrowser.open")
|
||||
self.mock_webbrowser = self.webbrowser_patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.original_cwd)
|
||||
@@ -40,9 +42,10 @@ class TestMain(TestCase):
|
||||
os.environ.clear()
|
||||
os.environ.update(self.original_env)
|
||||
self.input_patcher.stop()
|
||||
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())
|
||||
@@ -138,7 +141,7 @@ class TestMain(TestCase):
|
||||
|
||||
gitignore.write_text("one\ntwo\n")
|
||||
check_gitignore(cwd, io)
|
||||
self.assertEqual("one\ntwo\n.aider*\n", gitignore.read_text())
|
||||
self.assertEqual("one\ntwo\n.aider*\n.env\n", gitignore.read_text())
|
||||
del os.environ["GIT_CONFIG_GLOBAL"]
|
||||
|
||||
def test_main_args(self):
|
||||
@@ -329,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)
|
||||
@@ -337,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)
|
||||
@@ -378,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
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import unittest
|
||||
from unittest.mock import ANY, MagicMock, patch
|
||||
|
||||
from aider.models import Model, get_model_info, sanity_check_model, sanity_check_models
|
||||
from aider.models import (
|
||||
ANTHROPIC_BETA_HEADER,
|
||||
Model,
|
||||
ModelInfoManager,
|
||||
register_models,
|
||||
sanity_check_model,
|
||||
sanity_check_models,
|
||||
)
|
||||
|
||||
|
||||
class TestModels(unittest.TestCase):
|
||||
def test_get_model_info_nonexistent(self):
|
||||
info = get_model_info("non-existent-model")
|
||||
manager = ModelInfoManager()
|
||||
info = manager.get_model_info("non-existent-model")
|
||||
self.assertEqual(info, {})
|
||||
|
||||
def test_max_context_tokens(self):
|
||||
@@ -73,12 +81,68 @@ class TestModels(unittest.TestCase):
|
||||
result
|
||||
) # 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
|
||||
self.assertGreaterEqual(mock_io.tool_warning.call_count, 2) # Expect two warnings
|
||||
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
|
||||
self.assertTrue(
|
||||
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()
|
||||
|
||||
@@ -6,6 +6,7 @@ from unittest import mock
|
||||
import pytest
|
||||
from git import GitError, Repo
|
||||
|
||||
from aider import urls
|
||||
from aider.main import sanity_check_repo
|
||||
|
||||
|
||||
@@ -99,7 +100,8 @@ def test_detached_head_state(create_repo, mock_io):
|
||||
mock_io.tool_output.assert_not_called()
|
||||
|
||||
|
||||
def test_git_index_version_greater_than_2(create_repo, mock_io):
|
||||
@mock.patch("webbrowser.open")
|
||||
def test_git_index_version_greater_than_2(mock_browser, create_repo, mock_io):
|
||||
repo_path, repo = create_repo
|
||||
# Set the git index version to 3
|
||||
set_git_index_version(str(repo_path), 3)
|
||||
@@ -125,7 +127,10 @@ def test_git_index_version_greater_than_2(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.tool_output.assert_any_call("https://github.com/Aider-AI/aider/issues/211")
|
||||
mock_io.offer_url.assert_any_call(
|
||||
urls.git_index_version,
|
||||
"Open documentation url for more info?",
|
||||
)
|
||||
|
||||
|
||||
def test_bare_repository(create_repo, mock_io, tmp_path):
|
||||
|
||||
@@ -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 simple_send_with_retries
|
||||
from aider.sendchat import send_completion, simple_send_with_retries
|
||||
|
||||
|
||||
class PrintCalled(Exception):
|
||||
@@ -12,6 +11,14 @@ class PrintCalled(Exception):
|
||||
|
||||
|
||||
class TestSendChat(unittest.TestCase):
|
||||
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")
|
||||
def test_simple_send_with_retries_rate_limit_error(self, mock_print, mock_completion):
|
||||
@@ -20,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",
|
||||
@@ -31,17 +38,56 @@ class TestSendChat(unittest.TestCase):
|
||||
|
||||
# Call the simple_send_with_retries method
|
||||
simple_send_with_retries("model", ["message"])
|
||||
mock_print.assert_called_once()
|
||||
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"])
|
||||
mock_print.assert_called_once()
|
||||
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
|
||||
|
||||
@@ -4,7 +4,7 @@ from unittest.mock import MagicMock
|
||||
import aider
|
||||
from aider.coders import Coder
|
||||
from aider.commands import Commands
|
||||
from aider.help import Help
|
||||
from aider.help import Help, fname_to_url
|
||||
from aider.io import InputOutput
|
||||
from aider.models import Model
|
||||
|
||||
@@ -54,6 +54,53 @@ class TestHelp(unittest.TestCase):
|
||||
# Assert that there are more than 5 <doc> entries
|
||||
self.assertGreater(result.count("<doc"), 5)
|
||||
|
||||
def test_fname_to_url_unix(self):
|
||||
# Test relative Unix-style paths
|
||||
self.assertEqual(fname_to_url("website/docs/index.md"), "https://aider.chat/docs")
|
||||
self.assertEqual(
|
||||
fname_to_url("website/docs/usage.md"), "https://aider.chat/docs/usage.html"
|
||||
)
|
||||
self.assertEqual(fname_to_url("website/_includes/header.md"), "")
|
||||
|
||||
# Test absolute Unix-style paths
|
||||
self.assertEqual(
|
||||
fname_to_url("/home/user/project/website/docs/index.md"), "https://aider.chat/docs"
|
||||
)
|
||||
self.assertEqual(
|
||||
fname_to_url("/home/user/project/website/docs/usage.md"),
|
||||
"https://aider.chat/docs/usage.html",
|
||||
)
|
||||
self.assertEqual(fname_to_url("/home/user/project/website/_includes/header.md"), "")
|
||||
|
||||
def test_fname_to_url_windows(self):
|
||||
# Test relative Windows-style paths
|
||||
self.assertEqual(fname_to_url(r"website\docs\index.md"), "https://aider.chat/docs")
|
||||
self.assertEqual(
|
||||
fname_to_url(r"website\docs\usage.md"), "https://aider.chat/docs/usage.html"
|
||||
)
|
||||
self.assertEqual(fname_to_url(r"website\_includes\header.md"), "")
|
||||
|
||||
# Test absolute Windows-style paths
|
||||
self.assertEqual(
|
||||
fname_to_url(r"C:\Users\user\project\website\docs\index.md"), "https://aider.chat/docs"
|
||||
)
|
||||
self.assertEqual(
|
||||
fname_to_url(r"C:\Users\user\project\website\docs\usage.md"),
|
||||
"https://aider.chat/docs/usage.html",
|
||||
)
|
||||
self.assertEqual(fname_to_url(r"C:\Users\user\project\website\_includes\header.md"), "")
|
||||
|
||||
def test_fname_to_url_edge_cases(self):
|
||||
# Test paths that don't contain 'website'
|
||||
self.assertEqual(fname_to_url("/home/user/project/docs/index.md"), "")
|
||||
self.assertEqual(fname_to_url(r"C:\Users\user\project\docs\index.md"), "")
|
||||
|
||||
# Test empty path
|
||||
self.assertEqual(fname_to_url(""), "")
|
||||
|
||||
# Test path with 'website' in the wrong place
|
||||
self.assertEqual(fname_to_url("/home/user/website_project/docs/index.md"), "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -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