mirror of
https://github.com/Aider-AI/aider
synced 2026-05-05 06:32:04 +02:00
Compare commits
2534 Commits
v0.68.1.de
...
v0.80.3.de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8547c24dac | ||
|
|
0e1e1aae2e | ||
|
|
6f897fec59 | ||
|
|
8c3d77f4c7 | ||
|
|
f9b60d83ac | ||
|
|
3992681b84 | ||
|
|
340bd78259 | ||
|
|
12a46275a2 | ||
|
|
b56234f1c9 | ||
|
|
60859ec2b9 | ||
|
|
0a840860f1 | ||
|
|
cebae18dd6 | ||
|
|
9c9c6b6591 | ||
|
|
ca0ffc66d1 | ||
|
|
b0623f04fe | ||
|
|
2dec862ea6 | ||
|
|
f18fe53a9a | ||
|
|
73348de2b4 | ||
|
|
f4a418bfcd | ||
|
|
50588800f5 | ||
|
|
2762215d66 | ||
|
|
4e53797aac | ||
|
|
b24ac4b3a2 | ||
|
|
88ab6afd3e | ||
|
|
5c5db0a961 | ||
|
|
587186d96c | ||
|
|
d9ddf93f83 | ||
|
|
d3882d3513 | ||
|
|
a458215bbb | ||
|
|
7ae0fa3775 | ||
|
|
f1695f8b15 | ||
|
|
4c08bbb9e5 | ||
|
|
9b55ff8c4c | ||
|
|
2096d2b786 | ||
|
|
70196cd6fd | ||
|
|
c2cba97722 | ||
|
|
7534ebd145 | ||
|
|
6b2331340b | ||
|
|
da7b5005fe | ||
|
|
9210e12316 | ||
|
|
2c47a79c38 | ||
|
|
48cebef974 | ||
|
|
52952efd33 | ||
|
|
30dfd28ac4 | ||
|
|
b5a04f05f3 | ||
|
|
d5a34dcbc5 | ||
|
|
fc6a05ced6 | ||
|
|
2d3162a90b | ||
|
|
83c599e741 | ||
|
|
c7f1671d5a | ||
|
|
9f2d945691 | ||
|
|
36ff099145 | ||
|
|
120e010e48 | ||
|
|
2887816cf0 | ||
|
|
9848479306 | ||
|
|
b662e6b9eb | ||
|
|
258f1f0848 | ||
|
|
a07f312089 | ||
|
|
605d8fe59a | ||
|
|
1c7db4da0d | ||
|
|
b0acc95b01 | ||
|
|
5bcad73515 | ||
|
|
db05754d29 | ||
|
|
dfe3457906 | ||
|
|
7dbb1a2aa8 | ||
|
|
83dac4aae2 | ||
|
|
75b79fa002 | ||
|
|
27c1fd0262 | ||
|
|
8069e06f43 | ||
|
|
8cd106fc8a | ||
|
|
a9c9877580 | ||
|
|
19e1201c8a | ||
|
|
912f98e6eb | ||
|
|
b6808e3700 | ||
|
|
a4f78b60e0 | ||
|
|
6c9906c639 | ||
|
|
8a90af6779 | ||
|
|
9831a13284 | ||
|
|
d2386bc1f6 | ||
|
|
5b10af7b1a | ||
|
|
eacf3cc4ed | ||
|
|
87090139f6 | ||
|
|
650c4cf948 | ||
|
|
24c074eeaa | ||
|
|
b54629addb | ||
|
|
cd67d11ecf | ||
|
|
16bb0c93e7 | ||
|
|
7c40c3a61c | ||
|
|
b8e8b7496d | ||
|
|
d29d5e3a47 | ||
|
|
db261d0fa4 | ||
|
|
b7f6b847d6 | ||
|
|
5311a842a5 | ||
|
|
e288f59da7 | ||
|
|
c62ceb5db1 | ||
|
|
35decf122d | ||
|
|
2adfe1507b | ||
|
|
5516e6b279 | ||
|
|
8b811c610a | ||
|
|
23348f8e65 | ||
|
|
e1c3a2f8cf | ||
|
|
0b0493fa21 | ||
|
|
14d1742869 | ||
|
|
96aa77288b | ||
|
|
a4c9c10029 | ||
|
|
c9d561e7ad | ||
|
|
87ba63c14c | ||
|
|
0decbad7d0 | ||
|
|
0e647dbc0e | ||
|
|
2540d28b34 | ||
|
|
19a5e5bb00 | ||
|
|
f22afc6458 | ||
|
|
ab00415ca1 | ||
|
|
8df5406986 | ||
|
|
7f05159f0f | ||
|
|
61147dfecf | ||
|
|
06da133aac | ||
|
|
ff1d047048 | ||
|
|
4a8b17cb84 | ||
|
|
fbafc09e6a | ||
|
|
c3c960383e | ||
|
|
9e3adf0bf8 | ||
|
|
2bc0aa1777 | ||
|
|
3bc4064b61 | ||
|
|
b4f9258f3c | ||
|
|
ad844cce5c | ||
|
|
c73b064133 | ||
|
|
bd9b63a1aa | ||
|
|
2d87431aeb | ||
|
|
3f3b1fb657 | ||
|
|
477f9eb4ec | ||
|
|
91497dc2ee | ||
|
|
928b78d9f6 | ||
|
|
51825663b9 | ||
|
|
01fdbda728 | ||
|
|
fa3c68fccd | ||
|
|
189977e4c7 | ||
|
|
290fd99b6d | ||
|
|
15cec5bd50 | ||
|
|
f53db636e1 | ||
|
|
47d3802ffe | ||
|
|
e98ffb5ae0 | ||
|
|
5d77eb1314 | ||
|
|
f124cdbb6f | ||
|
|
1649d084d2 | ||
|
|
36ca790c3d | ||
|
|
a91a8216b7 | ||
|
|
8cae7b20e7 | ||
|
|
a537119f3d | ||
|
|
15fe0afe62 | ||
|
|
1b2a4db1ed | ||
|
|
88a02723fa | ||
|
|
7d013f35e2 | ||
|
|
e881d33bea | ||
|
|
38da91becd | ||
|
|
c99d96a700 | ||
|
|
fa89a6950b | ||
|
|
cde08da282 | ||
|
|
8619bd4e84 | ||
|
|
f49449b520 | ||
|
|
2fe79ac6a3 | ||
|
|
d8830c43c5 | ||
|
|
4ac945da70 | ||
|
|
ee0019e25f | ||
|
|
f37b814570 | ||
|
|
e559bc8694 | ||
|
|
f7618440e7 | ||
|
|
7bc62cb674 | ||
|
|
db77e2e9b9 | ||
|
|
083b49f3c4 | ||
|
|
f3f0416d31 | ||
|
|
775a9f86a1 | ||
|
|
e2bfdc444a | ||
|
|
3c7783585e | ||
|
|
761a297903 | ||
|
|
6d30094a93 | ||
|
|
d8e1816774 | ||
|
|
ae371cb362 | ||
|
|
73c46e8e24 | ||
|
|
ef4c40c692 | ||
|
|
04b3ada7f7 | ||
|
|
424b43b3d3 | ||
|
|
9a9255d6f9 | ||
|
|
d9e52e41ff | ||
|
|
a038bc002a | ||
|
|
fa256eb1a7 | ||
|
|
6689f001cf | ||
|
|
cc043bab9c | ||
|
|
5af73b1dcf | ||
|
|
85925a2dc6 | ||
|
|
fb23b6c26f | ||
|
|
d5cec5f71e | ||
|
|
13b62e3d06 | ||
|
|
779f07f072 | ||
|
|
b923d63700 | ||
|
|
7e2dd9bc04 | ||
|
|
ef1f869b73 | ||
|
|
959d6334db | ||
|
|
d7b00b93c7 | ||
|
|
eec084c842 | ||
|
|
87b504a58f | ||
|
|
243d4d0727 | ||
|
|
673acf4308 | ||
|
|
fd180ebff5 | ||
|
|
61705ce7fc | ||
|
|
6e1dd4474b | ||
|
|
7924657584 | ||
|
|
4f5ed8ace0 | ||
|
|
8737220fb6 | ||
|
|
bcb01e8c1b | ||
|
|
41f669bb89 | ||
|
|
983bc199b3 | ||
|
|
8f15269bd0 | ||
|
|
6ffe3e7067 | ||
|
|
51bf6035f7 | ||
|
|
249a6fc9b1 | ||
|
|
c6d4337855 | ||
|
|
9fa3636c57 | ||
|
|
a417e6e644 | ||
|
|
999eb86d7a | ||
|
|
7b97f93051 | ||
|
|
02bc926d75 | ||
|
|
48ee3cdf98 | ||
|
|
2556a912d3 | ||
|
|
487674b1c5 | ||
|
|
347fbf6471 | ||
|
|
3eff70a3bc | ||
|
|
ad7c708039 | ||
|
|
f993c1f22c | ||
|
|
75b714a1ad | ||
|
|
0636d40909 | ||
|
|
cbb3660a17 | ||
|
|
42363beb72 | ||
|
|
efa36a7196 | ||
|
|
fab713a6a8 | ||
|
|
7d5f1143af | ||
|
|
f05f8df44c | ||
|
|
c89a1a8021 | ||
|
|
db7c679e74 | ||
|
|
71cbbf545b | ||
|
|
6809a7ec3e | ||
|
|
70847a74c2 | ||
|
|
6933bc8add | ||
|
|
a6cbaad5a2 | ||
|
|
899972e22f | ||
|
|
820b925b78 | ||
|
|
5457b43a89 | ||
|
|
0adbc9678f | ||
|
|
8d6a2ecf0e | ||
|
|
6acbd80cee | ||
|
|
2331224157 | ||
|
|
5d2aea434c | ||
|
|
798f6983e4 | ||
|
|
a428fdc951 | ||
|
|
ccacc09ff0 | ||
|
|
40cc155aad | ||
|
|
645e0de971 | ||
|
|
325eb5968d | ||
|
|
65a7583731 | ||
|
|
6d8e4e8fb1 | ||
|
|
4716cce208 | ||
|
|
c67bbe6c00 | ||
|
|
1272e21603 | ||
|
|
7302280417 | ||
|
|
871c6d56dc | ||
|
|
eb0389938c | ||
|
|
e6c191bdc6 | ||
|
|
2727eb6dd7 | ||
|
|
821087bcce | ||
|
|
f1955577bc | ||
|
|
966686cd5d | ||
|
|
79d3d50de6 | ||
|
|
da59f0a0db | ||
|
|
bdc00e5dd4 | ||
|
|
9854a4f92e | ||
|
|
33413ecfe9 | ||
|
|
c7e8d297a4 | ||
|
|
3619953f83 | ||
|
|
b20753c3ac | ||
|
|
4fd6b7a608 | ||
|
|
1de8e477b9 | ||
|
|
6c50645213 | ||
|
|
5aeea0c228 | ||
|
|
86ceeb554d | ||
|
|
ca121e0e28 | ||
|
|
d1fef7fd17 | ||
|
|
25d157afdc | ||
|
|
97afe3cd0b | ||
|
|
2923304bdb | ||
|
|
10d741b6df | ||
|
|
12cd115ae4 | ||
|
|
d1def13cd1 | ||
|
|
127c305b1a | ||
|
|
fd06db18a0 | ||
|
|
387b392c18 | ||
|
|
964022f7b5 | ||
|
|
9edc346c2c | ||
|
|
2fe671744b | ||
|
|
fb8daa5607 | ||
|
|
65e0da72b8 | ||
|
|
3d5924e2f5 | ||
|
|
5b2e2d630b | ||
|
|
6a96cb6ba7 | ||
|
|
43ece9c644 | ||
|
|
46cef723b7 | ||
|
|
9b19dac569 | ||
|
|
54eb642726 | ||
|
|
5bb664657c | ||
|
|
dea4d16e87 | ||
|
|
059cd23543 | ||
|
|
502b8630a2 | ||
|
|
b2444b43a6 | ||
|
|
be87ff0193 | ||
|
|
c7fe86021c | ||
|
|
99b0209c89 | ||
|
|
4d61b596ff | ||
|
|
0549e7079d | ||
|
|
b591b64d3f | ||
|
|
a3377686fa | ||
|
|
d23bba5d9f | ||
|
|
5e8fc3e4c8 | ||
|
|
04e76ad6ff | ||
|
|
3a18a9296b | ||
|
|
3e9a6ffbca | ||
|
|
0b06e56182 | ||
|
|
fb5a32f429 | ||
|
|
75dfd4505b | ||
|
|
5661d1428e | ||
|
|
f543c1ee1c | ||
|
|
74254cdbd5 | ||
|
|
a5c8c534c1 | ||
|
|
24159dda58 | ||
|
|
ad0a2b3260 | ||
|
|
7e51c68fde | ||
|
|
3b376a15b7 | ||
|
|
8e5f311708 | ||
|
|
02c48fa8c3 | ||
|
|
51fa2eb103 | ||
|
|
0fd08fe667 | ||
|
|
0da65c6169 | ||
|
|
d2c6db0680 | ||
|
|
5b73938e29 | ||
|
|
2540933921 | ||
|
|
e75e2272f7 | ||
|
|
64b8fc80e8 | ||
|
|
033c149737 | ||
|
|
a6bce6d5f1 | ||
|
|
94016c87fd | ||
|
|
4b6424f631 | ||
|
|
9bf70d8641 | ||
|
|
0eb26fffe1 | ||
|
|
279e7e1456 | ||
|
|
099fc7b31e | ||
|
|
f1e070cc1e | ||
|
|
36f2458f2e | ||
|
|
29db4231ee | ||
|
|
74926578a2 | ||
|
|
5b8ae368bd | ||
|
|
b3e368237e | ||
|
|
3ee5dc131d | ||
|
|
2765d2fe66 | ||
|
|
f50fee407a | ||
|
|
f3295d7c1d | ||
|
|
dd02803f1f | ||
|
|
d4e44d7555 | ||
|
|
f2b8d36d9e | ||
|
|
8d43d4ee21 | ||
|
|
b54af5dbe3 | ||
|
|
4980e901a0 | ||
|
|
cd32311c1f | ||
|
|
b97997b009 | ||
|
|
4aad9fbdd4 | ||
|
|
ac2439e25b | ||
|
|
ddc1556ae0 | ||
|
|
0d75c4d0e3 | ||
|
|
f3a042dcdf | ||
|
|
f9e0a99064 | ||
|
|
d6906fb100 | ||
|
|
821662abcb | ||
|
|
cf496abec0 | ||
|
|
e245d39216 | ||
|
|
44f87c2c17 | ||
|
|
a6fd0de762 | ||
|
|
072ecba4c5 | ||
|
|
3ad5d75bee | ||
|
|
8679c425e0 | ||
|
|
1ec257278e | ||
|
|
ffe89362ab | ||
|
|
a82c0f9e49 | ||
|
|
413271e82a | ||
|
|
60b926b698 | ||
|
|
98be67442e | ||
|
|
8e9c75b6f8 | ||
|
|
8c65604b29 | ||
|
|
930923c211 | ||
|
|
710d540dcd | ||
|
|
ddef8c2499 | ||
|
|
c6289c2bea | ||
|
|
b624bb6fb8 | ||
|
|
4e03f4165d | ||
|
|
2f6ea2f499 | ||
|
|
8c47d1f9c0 | ||
|
|
e31be8d3c9 | ||
|
|
2069ad62d1 | ||
|
|
2993e285c8 | ||
|
|
4ee9cee52a | ||
|
|
0e555699bd | ||
|
|
aad6c63206 | ||
|
|
8c78e09f03 | ||
|
|
25d1eac0f5 | ||
|
|
26daf2cd31 | ||
|
|
280bd44ba3 | ||
|
|
c9d87fef71 | ||
|
|
649742f02a | ||
|
|
245ee84c2d | ||
|
|
76c09205d0 | ||
|
|
d365b5248c | ||
|
|
3dd91bc6f2 | ||
|
|
3dc5a48fcc | ||
|
|
bff077f855 | ||
|
|
6022b09437 | ||
|
|
62b52a78fe | ||
|
|
e33dc9355d | ||
|
|
834da07736 | ||
|
|
fe795cc2d5 | ||
|
|
593de47438 | ||
|
|
ac231e43ad | ||
|
|
aca64071c8 | ||
|
|
5c205cd753 | ||
|
|
57ce2c48b8 | ||
|
|
0937b34983 | ||
|
|
291c4eb258 | ||
|
|
a8744708a0 | ||
|
|
3010b94a64 | ||
|
|
9c4d173b1f | ||
|
|
cca155c5c6 | ||
|
|
a93e7108e6 | ||
|
|
d26df6c178 | ||
|
|
e915964d81 | ||
|
|
1304ca2425 | ||
|
|
894f6b6d1d | ||
|
|
6605a9b22a | ||
|
|
b5d963d151 | ||
|
|
8f840ae0b1 | ||
|
|
7942ca9206 | ||
|
|
a5614bfe40 | ||
|
|
eaac00ad3f | ||
|
|
6687cc9465 | ||
|
|
5b9464ed31 | ||
|
|
4c7d99b26c | ||
|
|
5bf248164a | ||
|
|
4eeae967db | ||
|
|
0ac2a85a3e | ||
|
|
71884e58d4 | ||
|
|
37b2f9617c | ||
|
|
01af14693c | ||
|
|
90e2929fc5 | ||
|
|
8f260aa544 | ||
|
|
ee72a09278 | ||
|
|
e6a422b6c2 | ||
|
|
d29aff919b | ||
|
|
5023bbe8c0 | ||
|
|
c08e3daf29 | ||
|
|
9cf194b52b | ||
|
|
1b0ba71ef0 | ||
|
|
de5ab102b7 | ||
|
|
4e2c7c3329 | ||
|
|
b62840c347 | ||
|
|
674379fe30 | ||
|
|
004ffb9f60 | ||
|
|
bed836b15a | ||
|
|
bbf219ecd2 | ||
|
|
022bf4bdcc | ||
|
|
d8ed90a2c0 | ||
|
|
e5d9d91b01 | ||
|
|
c8f6576cd7 | ||
|
|
14f140fdc5 | ||
|
|
fdbe169423 | ||
|
|
e4ec370e2a | ||
|
|
dfbdb43c31 | ||
|
|
9b8606535d | ||
|
|
0b79fd0a8e | ||
|
|
f032606f32 | ||
|
|
b652a8416d | ||
|
|
8f7b50f3df | ||
|
|
2adafad4e2 | ||
|
|
99a5862bc9 | ||
|
|
6dda54f431 | ||
|
|
8e187a913f | ||
|
|
8935c87d7a | ||
|
|
c6e544750b | ||
|
|
c14d406f59 | ||
|
|
4edfe39449 | ||
|
|
1239d77c88 | ||
|
|
7d4b0b20e8 | ||
|
|
1f42cdd762 | ||
|
|
f182b81e0f | ||
|
|
3f2256d4c4 | ||
|
|
e4b1b12b26 | ||
|
|
35477e0a5a | ||
|
|
f752c9c8a9 | ||
|
|
d5dd7b0a96 | ||
|
|
4e16eba6b0 | ||
|
|
4f129e1c9d | ||
|
|
ffbbb5539d | ||
|
|
30e5b48461 | ||
|
|
0dc31011f5 | ||
|
|
479df00a3b | ||
|
|
06f3e15ce9 | ||
|
|
2f7fde163a | ||
|
|
71d26c2ec5 | ||
|
|
acf8b40a28 | ||
|
|
318027d9e1 | ||
|
|
3116e833a7 | ||
|
|
1cb71b33fa | ||
|
|
7e76b8d0c3 | ||
|
|
e0e78cd879 | ||
|
|
82e4dcf40f | ||
|
|
22b77ad14f | ||
|
|
9b23975f09 | ||
|
|
584c3a65f5 | ||
|
|
db4d6c4419 | ||
|
|
92cb1c60d1 | ||
|
|
ca72d0ca5b | ||
|
|
ba7f245d88 | ||
|
|
ef67ba5cf4 | ||
|
|
1f630a03b9 | ||
|
|
5f71cdc497 | ||
|
|
b85226b8a5 | ||
|
|
af4acccf9f | ||
|
|
3a3bc9898d | ||
|
|
5349c99c74 | ||
|
|
68d175f1c5 | ||
|
|
352f1f31d2 | ||
|
|
c51ef714ad | ||
|
|
5ae68a3c48 | ||
|
|
d2c7e51fac | ||
|
|
d9aa3fb973 | ||
|
|
e1b79ddd69 | ||
|
|
3270a8737c | ||
|
|
cbe8d320fa | ||
|
|
78ce63e71e | ||
|
|
1bb396bea5 | ||
|
|
16304beeab | ||
|
|
d2cfd541ed | ||
|
|
8b1aaf690c | ||
|
|
f1ca8f594c | ||
|
|
78d2e0aa25 | ||
|
|
4a3d239082 | ||
|
|
94eaee8d39 | ||
|
|
471f03ab4f | ||
|
|
9df9af822c | ||
|
|
7b49623b31 | ||
|
|
12bda2b8d0 | ||
|
|
2022f214aa | ||
|
|
786738ba81 | ||
|
|
e4f1b59475 | ||
|
|
7cb302f571 | ||
|
|
8bae91cf6e | ||
|
|
44a145fc86 | ||
|
|
a1fa62adef | ||
|
|
fe247a8f6a | ||
|
|
54e70eff13 | ||
|
|
5453f4a85d | ||
|
|
f2654390e7 | ||
|
|
777903f7da | ||
|
|
2f7892561f | ||
|
|
6d79000d6c | ||
|
|
284911cd6b | ||
|
|
70b6da108c | ||
|
|
6de3b2b262 | ||
|
|
e7554c5413 | ||
|
|
2facd2ab16 | ||
|
|
20ba252c56 | ||
|
|
eba123e381 | ||
|
|
3bb57af331 | ||
|
|
aeb991fdf3 | ||
|
|
75a14380ef | ||
|
|
0aeecf4d9a | ||
|
|
447347c760 | ||
|
|
79619de045 | ||
|
|
1af423e2df | ||
|
|
92fcf9a159 | ||
|
|
d1b1cc5c14 | ||
|
|
b9514ad82a | ||
|
|
7b3411b236 | ||
|
|
915e87e88e | ||
|
|
face505f0d | ||
|
|
dada307e79 | ||
|
|
5124c512c9 | ||
|
|
89f579c1ba | ||
|
|
0035f28035 | ||
|
|
4d556d9235 | ||
|
|
dd5ef29355 | ||
|
|
ca9922ef0a | ||
|
|
b674a0a5e8 | ||
|
|
541d0e6f5c | ||
|
|
fc96e980fb | ||
|
|
1425e3e6c7 | ||
|
|
b9d3c11032 | ||
|
|
81b65e0de3 | ||
|
|
0f80f6af27 | ||
|
|
5eedf98aae | ||
|
|
8f45a2ef3d | ||
|
|
9d51d4a572 | ||
|
|
9978c8ea48 | ||
|
|
e779358429 | ||
|
|
8d663cc3d6 | ||
|
|
a98ca30438 | ||
|
|
589cb2ac79 | ||
|
|
d973be8fea | ||
|
|
41c3b59755 | ||
|
|
8bf168da47 | ||
|
|
0d85c06be2 | ||
|
|
fadede7305 | ||
|
|
a24d31fbba | ||
|
|
c9d463443a | ||
|
|
0e3d486658 | ||
|
|
574ab6bdda | ||
|
|
a172d15463 | ||
|
|
a1362b946a | ||
|
|
6ecb4c5ece | ||
|
|
63c1320f36 | ||
|
|
bf484ce4c4 | ||
|
|
1700212f30 | ||
|
|
751ca3bf75 | ||
|
|
01be8bc64e | ||
|
|
a9e8005b14 | ||
|
|
871cff6232 | ||
|
|
1de8f5ceda | ||
|
|
dd4d2420df | ||
|
|
e9b1a98314 | ||
|
|
f0f48e8e30 | ||
|
|
89b008e1eb | ||
|
|
fb3f8439e2 | ||
|
|
f0e66d2bfb | ||
|
|
65ec254c1b | ||
|
|
47c0d95bd4 | ||
|
|
55d7bed563 | ||
|
|
9a4f3b8d8e | ||
|
|
d9c5ce15f3 | ||
|
|
292908288b | ||
|
|
7fec2661fc | ||
|
|
0cee8bc6ac | ||
|
|
37d867e47e | ||
|
|
c1338fe92f | ||
|
|
1e90a679c0 | ||
|
|
ddb7b9cb8b | ||
|
|
31e1604d99 | ||
|
|
e11c32bca5 | ||
|
|
16309dc077 | ||
|
|
8a1b496cd5 | ||
|
|
99cf99e014 | ||
|
|
f2e9b06dbd | ||
|
|
6cce7c34c2 | ||
|
|
dd2efac3ae | ||
|
|
14c0e50721 | ||
|
|
68e218d002 | ||
|
|
0a026e71b7 | ||
|
|
5175a8c7ca | ||
|
|
661e0cfc71 | ||
|
|
9ff6f35330 | ||
|
|
ab5a066780 | ||
|
|
c2d649e655 | ||
|
|
f82bb65810 | ||
|
|
e76c54b18e | ||
|
|
43658055d3 | ||
|
|
0b71fded7f | ||
|
|
03d1bada2a | ||
|
|
ad76355299 | ||
|
|
c98d409f0a | ||
|
|
9084673fd7 | ||
|
|
8d7300a522 | ||
|
|
7c10600044 | ||
|
|
277da37047 | ||
|
|
f6d697ed2b | ||
|
|
f247927244 | ||
|
|
7097e62582 | ||
|
|
e577bab263 | ||
|
|
274c40793f | ||
|
|
afbe9266e7 | ||
|
|
195ed3e6b6 | ||
|
|
ed8ace7884 | ||
|
|
14761ebec2 | ||
|
|
50d8a19397 | ||
|
|
719324981d | ||
|
|
182c06107f | ||
|
|
d4d4852a0c | ||
|
|
f352fed313 | ||
|
|
5dfce2d199 | ||
|
|
6aafac544b | ||
|
|
2589c29b1c | ||
|
|
076c38526a | ||
|
|
9fd5cfa777 | ||
|
|
c01db8783d | ||
|
|
77101a96a1 | ||
|
|
46863b470c | ||
|
|
736c1f7e1e | ||
|
|
4cf9679334 | ||
|
|
89c9290602 | ||
|
|
3ba75c5a62 | ||
|
|
4ceaed7183 | ||
|
|
f310497d47 | ||
|
|
94bcaa71e6 | ||
|
|
67bdccbda6 | ||
|
|
f89dabbd0a | ||
|
|
76eee60ad5 | ||
|
|
ac4e4959eb | ||
|
|
7316635aba | ||
|
|
feb277049f | ||
|
|
35d5febd7d | ||
|
|
97a3bfca4e | ||
|
|
cf58133e06 | ||
|
|
0d1811a4ae | ||
|
|
2a8bb715f0 | ||
|
|
cf93869d3f | ||
|
|
77976a5253 | ||
|
|
5af46aaa2e | ||
|
|
679c634459 | ||
|
|
cc33fc2822 | ||
|
|
4f4b10fd86 | ||
|
|
26789115b6 | ||
|
|
87a28dcb9f | ||
|
|
1ad99f39e2 | ||
|
|
dfbeec199e | ||
|
|
41a99ec29d | ||
|
|
7cee3aa1f1 | ||
|
|
b86d8099f1 | ||
|
|
9c4a0043dd | ||
|
|
5cde755976 | ||
|
|
51aab7b656 | ||
|
|
791dc213fa | ||
|
|
8404165db3 | ||
|
|
aee52c01a3 | ||
|
|
58e16b0c48 | ||
|
|
3225ac88c0 | ||
|
|
38b8b85ec2 | ||
|
|
278f748c1c | ||
|
|
874df40303 | ||
|
|
0465d8ce80 | ||
|
|
e9b3f5fd43 | ||
|
|
c49bc2418b | ||
|
|
fd21f5195d | ||
|
|
a1aa63fa06 | ||
|
|
b0a619c714 | ||
|
|
ec648f2c6f | ||
|
|
7ac7c72e03 | ||
|
|
f1d00dbd7f | ||
|
|
f644aba7a8 | ||
|
|
ed75287c8c | ||
|
|
a035c73c41 | ||
|
|
781a619262 | ||
|
|
e08b63cc9f | ||
|
|
97ed57a252 | ||
|
|
71f1779c8c | ||
|
|
116f44cade | ||
|
|
2ebdd689ab | ||
|
|
beecc1a718 | ||
|
|
557ba2adc1 | ||
|
|
89780c1283 | ||
|
|
411e7f86c1 | ||
|
|
610fce67e1 | ||
|
|
d9350cd3ed | ||
|
|
54d6643a1f | ||
|
|
f664420628 | ||
|
|
d4d4c6de68 | ||
|
|
b4313599f8 | ||
|
|
6866f8f0a9 | ||
|
|
3cb478214b | ||
|
|
8d7b4d6446 | ||
|
|
cc6b0bcd72 | ||
|
|
831564cf48 | ||
|
|
4e59b62026 | ||
|
|
f345b9b0ff | ||
|
|
d4fb88a8c4 | ||
|
|
2cb1b6be46 | ||
|
|
feda315c2b | ||
|
|
41219a7d85 | ||
|
|
813a201b6a | ||
|
|
1bc40d48fe | ||
|
|
ddb4e51938 | ||
|
|
9ee67c343d | ||
|
|
bb816eae83 | ||
|
|
92bd446d09 | ||
|
|
a7526fa9c4 | ||
|
|
9b6ff487da | ||
|
|
9d61490743 | ||
|
|
9cce6e41fa | ||
|
|
a56dbdf502 | ||
|
|
7d5f27fa34 | ||
|
|
a61ba0db22 | ||
|
|
7f02a889e2 | ||
|
|
88d388a574 | ||
|
|
4d5a659e1e | ||
|
|
ecfcf1071d | ||
|
|
ec385d45e9 | ||
|
|
985107bb4b | ||
|
|
eb340c74ac | ||
|
|
9a16b33f00 | ||
|
|
0acebc5916 | ||
|
|
8442d9fe5f | ||
|
|
5318dd1a80 | ||
|
|
d916180ec8 | ||
|
|
6e5aa08ee0 | ||
|
|
83e115cde5 | ||
|
|
a3a92cd5dd | ||
|
|
16fc10fb0f | ||
|
|
6bba8b57d4 | ||
|
|
f45c75a137 | ||
|
|
3348df0652 | ||
|
|
3b6a01b63d | ||
|
|
a3554a95c5 | ||
|
|
6fa9af20c0 | ||
|
|
e23437dfa4 | ||
|
|
dcf9eaad77 | ||
|
|
768df05692 | ||
|
|
e0d5d35e32 | ||
|
|
a718a05414 | ||
|
|
953391d9d0 | ||
|
|
4ca229fd42 | ||
|
|
693a43efc8 | ||
|
|
f3d4c931f5 | ||
|
|
ece21315b1 | ||
|
|
318cc57ffe | ||
|
|
ba17924174 | ||
|
|
6bc9daa6ee | ||
|
|
2d8bc95bae | ||
|
|
520eb4a932 | ||
|
|
03733516cc | ||
|
|
e561130336 | ||
|
|
169fa2e7b7 | ||
|
|
6d6db996fb | ||
|
|
f8642bfd94 | ||
|
|
dfdd6bf533 | ||
|
|
91cef71048 | ||
|
|
3daf632384 | ||
|
|
dba5fb9dfa | ||
|
|
70d10a0bb2 | ||
|
|
97291b806a | ||
|
|
c583f008e9 | ||
|
|
b182eba56f | ||
|
|
de5da0e7ce | ||
|
|
f2a893c0d4 | ||
|
|
371a03c794 | ||
|
|
88e3d48be5 | ||
|
|
e8a8681a75 | ||
|
|
b541eec3b8 | ||
|
|
ce3f732645 | ||
|
|
1b3cae1ed5 | ||
|
|
024b9130a0 | ||
|
|
61a8b6020f | ||
|
|
d25877aace | ||
|
|
c3c2d4dc22 | ||
|
|
7454154599 | ||
|
|
04ecea614b | ||
|
|
084a14b640 | ||
|
|
a477759a49 | ||
|
|
d4df207612 | ||
|
|
57d492d4b8 | ||
|
|
fb7413436c | ||
|
|
2cc8105e68 | ||
|
|
a83d5ff123 | ||
|
|
a709d650df | ||
|
|
570e8eae31 | ||
|
|
22f1703bee | ||
|
|
14037eaeb8 | ||
|
|
189d64dc3d | ||
|
|
315f8093c6 | ||
|
|
a776d70e0d | ||
|
|
1ab6c70ac7 | ||
|
|
73eb8701dd | ||
|
|
a503f291e7 | ||
|
|
b54d800024 | ||
|
|
d74068464d | ||
|
|
86a5e8dbe1 | ||
|
|
24d2b683c8 | ||
|
|
9451f0abe4 | ||
|
|
544d58ddbd | ||
|
|
7c1d2d75e0 | ||
|
|
0b949f47d9 | ||
|
|
393b45dd21 | ||
|
|
6bb43555dc | ||
|
|
ba03b07602 | ||
|
|
0ac4c0b97d | ||
|
|
41e93a4d94 | ||
|
|
a0b5b19d38 | ||
|
|
65fad5ae30 | ||
|
|
a689f2116c | ||
|
|
68b5c90d95 | ||
|
|
44eb9af7bc | ||
|
|
9e824e6070 | ||
|
|
ba6bb527a7 | ||
|
|
3525eeae54 | ||
|
|
e535e01e83 | ||
|
|
3b1c81e50e | ||
|
|
89406e5b7d | ||
|
|
d5cc855c0f | ||
|
|
2ed61eaf92 | ||
|
|
865f71e2cc | ||
|
|
08a75808df | ||
|
|
c9dd37db8e | ||
|
|
7e86c8a90c | ||
|
|
b87a5496e9 | ||
|
|
9a88363437 | ||
|
|
c0b9665cfc | ||
|
|
70284ce1c2 | ||
|
|
48621dadaa | ||
|
|
e69bad57e4 | ||
|
|
f55099e969 | ||
|
|
76994facec | ||
|
|
2d843f6e79 | ||
|
|
e15518dd29 | ||
|
|
011e0fd109 | ||
|
|
5f125c1812 | ||
|
|
a6ebed8d16 | ||
|
|
849e02cbfb | ||
|
|
1ab5238405 | ||
|
|
ae6192111d | ||
|
|
59af4114dd | ||
|
|
4491b88763 | ||
|
|
92377fc390 | ||
|
|
79b8e50412 | ||
|
|
b5cd39cc50 | ||
|
|
63c2a98f3c | ||
|
|
c168f78a13 | ||
|
|
42d45b4037 | ||
|
|
c41df63629 | ||
|
|
330bb81206 | ||
|
|
d84c755ee8 | ||
|
|
a24ff28031 | ||
|
|
3a837c472e | ||
|
|
f4880e2ef3 | ||
|
|
a2e4022e31 | ||
|
|
5668b41daa | ||
|
|
c052270048 | ||
|
|
70547171ca | ||
|
|
881868bf17 | ||
|
|
e90eb39a9b | ||
|
|
9513d307a1 | ||
|
|
533e5ec03f | ||
|
|
55f63395c7 | ||
|
|
4253d98a73 | ||
|
|
0c4af58866 | ||
|
|
a5919bd27d | ||
|
|
5e40974fdd | ||
|
|
a27f4d0e04 | ||
|
|
1bb3041298 | ||
|
|
dc6040adda | ||
|
|
864725ff3d | ||
|
|
0fcbea03e5 | ||
|
|
2ce63e6ad3 | ||
|
|
d45af94cee | ||
|
|
1f874b654d | ||
|
|
a7a21757bc | ||
|
|
1bed4e8972 | ||
|
|
efcda12dda | ||
|
|
d7b4079ab5 | ||
|
|
2eb1513612 | ||
|
|
87bcbe0420 | ||
|
|
26b0c6e6da | ||
|
|
8cfbc9b827 | ||
|
|
46eee9e642 | ||
|
|
7d902d2f3e | ||
|
|
3d05007024 | ||
|
|
a2bf2e2910 | ||
|
|
9cf4286cee | ||
|
|
c9ddca3a16 | ||
|
|
06370cb096 | ||
|
|
342586519d | ||
|
|
89174bb524 | ||
|
|
1fa3bc4018 | ||
|
|
d68e2b33fb | ||
|
|
1313cd8216 | ||
|
|
7afc8c760c | ||
|
|
79f714ab16 | ||
|
|
e21bab2d17 | ||
|
|
4288cf2a39 | ||
|
|
9d570a9cb1 | ||
|
|
333ddfb37a | ||
|
|
10a5250527 | ||
|
|
fd57eccdca | ||
|
|
1432be9be6 | ||
|
|
1773bbf759 | ||
|
|
5c94624186 | ||
|
|
dc06c2fab3 | ||
|
|
f37799b39c | ||
|
|
cc84f590fe | ||
|
|
afcf3e77b5 | ||
|
|
0406dda2a6 | ||
|
|
935227f7e7 | ||
|
|
67ebb2566d | ||
|
|
1c736161c5 | ||
|
|
a348c2d013 | ||
|
|
444a95bc6c | ||
|
|
c38cdef220 | ||
|
|
5608db0892 | ||
|
|
98b3722a02 | ||
|
|
2d623ff196 | ||
|
|
703e124277 | ||
|
|
68c27f885f | ||
|
|
58cd190ca9 | ||
|
|
2c5c2b2f67 | ||
|
|
e10fe50c6f | ||
|
|
b8ad0b15e8 | ||
|
|
1b81fb0fdf | ||
|
|
703cb8849d | ||
|
|
55f856b23c | ||
|
|
93c284a67d | ||
|
|
38fd715247 | ||
|
|
987d024847 | ||
|
|
a3c0d628a1 | ||
|
|
3cb6ec9ddb | ||
|
|
c21619608e | ||
|
|
76a8789bc1 | ||
|
|
bbf538e06c | ||
|
|
d94ab3395b | ||
|
|
6d8457a61f | ||
|
|
303f8e1bc9 | ||
|
|
e638116a2f | ||
|
|
5bac9133e6 | ||
|
|
6fde4041ba | ||
|
|
d0c8b38ffc | ||
|
|
be7888ab18 | ||
|
|
11b71fa28c | ||
|
|
c838f9bfd5 | ||
|
|
8d073ce221 | ||
|
|
0be5d39453 | ||
|
|
10d599f26a | ||
|
|
6b76ed8098 | ||
|
|
74ecdf2d3f | ||
|
|
20eacfab0f | ||
|
|
0396e15a3b | ||
|
|
3432a936ea | ||
|
|
87cd2b5dfe | ||
|
|
313b91edbe | ||
|
|
a1f104cb4d | ||
|
|
eadb8d5d0a | ||
|
|
41ae947885 | ||
|
|
84f610c0e9 | ||
|
|
0df959cf68 | ||
|
|
a15d10ea1e | ||
|
|
a37d6e86df | ||
|
|
a405063385 | ||
|
|
f5a5b85e9d | ||
|
|
ba7d941e5b | ||
|
|
804a2d1af9 | ||
|
|
c1bc6e161e | ||
|
|
af1b728b90 | ||
|
|
14e37a82ab | ||
|
|
f8a7854efa | ||
|
|
072ce87051 | ||
|
|
cac9b4460e | ||
|
|
67bf90a149 | ||
|
|
af8558b19e | ||
|
|
1903542f11 | ||
|
|
3ed16fb796 | ||
|
|
6f99392eda | ||
|
|
680dbfbf77 | ||
|
|
51a72b497b | ||
|
|
d6e57dd194 | ||
|
|
e92ab55da6 | ||
|
|
c78b3e0204 | ||
|
|
ac1c05389a | ||
|
|
95583fe2cd | ||
|
|
ddedda9233 | ||
|
|
d30b9d1513 | ||
|
|
4c35f88ea0 | ||
|
|
e6623ae0a8 | ||
|
|
4755578822 | ||
|
|
319d543ac2 | ||
|
|
9e668cda7f | ||
|
|
5447483da2 | ||
|
|
8e22a8d107 | ||
|
|
18d27ab4e4 | ||
|
|
fe60832492 | ||
|
|
6bf683409f | ||
|
|
634bfb1eae | ||
|
|
c9d597d2b1 | ||
|
|
92c616f717 | ||
|
|
a1286d0d4d | ||
|
|
eef3a3afeb | ||
|
|
b1e8d29ae0 | ||
|
|
e0cef55fcd | ||
|
|
9aacf5c7db | ||
|
|
4858749a20 | ||
|
|
b53c0b982a | ||
|
|
2aac9ff9c5 | ||
|
|
bdaa70ada5 | ||
|
|
ca6abdfc61 | ||
|
|
e18593fe88 | ||
|
|
08401aff26 | ||
|
|
cddc67ad69 | ||
|
|
586af2a435 | ||
|
|
97091fab60 | ||
|
|
5f2cf75be8 | ||
|
|
37c7b81c95 | ||
|
|
779a266713 | ||
|
|
ebaedc6f05 | ||
|
|
08a392787a | ||
|
|
883bf74bad | ||
|
|
80de3335b7 | ||
|
|
794072bdf8 | ||
|
|
e28fdb9cb1 | ||
|
|
7873d1c6b3 | ||
|
|
f8c069132e | ||
|
|
c53833072f | ||
|
|
16d7cf7a52 | ||
|
|
3b9c2b9729 | ||
|
|
b230fea66f | ||
|
|
f9b6501af1 | ||
|
|
0cd8e3701d | ||
|
|
8545672839 | ||
|
|
4a6c4b95f1 | ||
|
|
c893bc21ab | ||
|
|
c8c5cbf8cc | ||
|
|
54ef8a1e19 | ||
|
|
82df218bcb | ||
|
|
f613ad6c05 | ||
|
|
4e732d0379 | ||
|
|
ad8b5c9d29 | ||
|
|
1ab4bf14dc | ||
|
|
068a0b4576 | ||
|
|
5f694f228f | ||
|
|
2ffe49130d | ||
|
|
f7d18ef976 | ||
|
|
8233eb6007 | ||
|
|
de4693cdf3 | ||
|
|
8fb235c3f5 | ||
|
|
6feb00dcd9 | ||
|
|
4fc1847a70 | ||
|
|
e7f16f07f7 | ||
|
|
d9551b3106 | ||
|
|
854428795b | ||
|
|
5c3b4bd987 | ||
|
|
9d686d3e52 | ||
|
|
b62e00b935 | ||
|
|
634745c818 | ||
|
|
490c6d9a28 | ||
|
|
e6dd9978cb | ||
|
|
240a5613a5 | ||
|
|
fb96cbcaaf | ||
|
|
b58f879db7 | ||
|
|
1585c6095e | ||
|
|
30a630412d | ||
|
|
e5ca79cd51 | ||
|
|
148353aca4 | ||
|
|
7a098ce740 | ||
|
|
f3b9831a0c | ||
|
|
01454674c8 | ||
|
|
5093b18ecc | ||
|
|
c16cfd0668 | ||
|
|
1674cd5db9 | ||
|
|
f111ab48fb | ||
|
|
65309854ac | ||
|
|
cf0aff8c40 | ||
|
|
16b768485a | ||
|
|
c2e7b533d3 | ||
|
|
539859f1ab | ||
|
|
d1d40a9a76 | ||
|
|
52162a5604 | ||
|
|
84e84207a5 | ||
|
|
a4e1745eca | ||
|
|
5931979b74 | ||
|
|
a412a65315 | ||
|
|
25da0674bb | ||
|
|
c823bf4fbb | ||
|
|
cfd0e67a6b | ||
|
|
94f3af57f1 | ||
|
|
0050a3fe6c | ||
|
|
0e65ddee37 | ||
|
|
101f7de889 | ||
|
|
a21c1ff92d | ||
|
|
f9bb2e498e | ||
|
|
f6bb803be5 | ||
|
|
204a88c171 | ||
|
|
012afc0708 | ||
|
|
cf089abb64 | ||
|
|
40e463cdc1 | ||
|
|
6a1284a5ca | ||
|
|
60522ee474 | ||
|
|
0045641db7 | ||
|
|
97b5b1b669 | ||
|
|
448de8519a | ||
|
|
95e1fe0446 | ||
|
|
47254be254 | ||
|
|
3da15bfd19 | ||
|
|
c79db2581b | ||
|
|
93b86a8800 | ||
|
|
56ba7ef411 | ||
|
|
e2117fd8a9 | ||
|
|
e817c76e38 | ||
|
|
65e059a7d2 | ||
|
|
f661025acc | ||
|
|
2fe1b1e16e | ||
|
|
665ffe3984 | ||
|
|
c3401047e0 | ||
|
|
996177ceaf | ||
|
|
09e998523f | ||
|
|
38e8d27416 | ||
|
|
813de04596 | ||
|
|
3c0eae4180 | ||
|
|
99424a9f53 | ||
|
|
51d118fdb5 | ||
|
|
a26509a1fd | ||
|
|
0db70379e8 | ||
|
|
c612b5d17b | ||
|
|
1b469cce49 | ||
|
|
c62cbd2d77 | ||
|
|
da1bc19052 | ||
|
|
8e2246ec5c | ||
|
|
5cf6945bcb | ||
|
|
7132ae47d7 | ||
|
|
96bde4ad03 | ||
|
|
85b9bdd8f4 | ||
|
|
e5a85108d7 | ||
|
|
aaa3a8ebda | ||
|
|
9ceb766a67 | ||
|
|
f894240fbb | ||
|
|
4bac8e2ebe | ||
|
|
d3ad1fd384 | ||
|
|
ed0e4189e4 | ||
|
|
5f147242be | ||
|
|
c7b4c22b94 | ||
|
|
667bacf81e | ||
|
|
e896b0ea96 | ||
|
|
3b0a5a8b41 | ||
|
|
81d39e9bde | ||
|
|
59eabf03a6 | ||
|
|
4fc4987c43 | ||
|
|
dbf5bb149a | ||
|
|
38acbf6970 | ||
|
|
ad4bd91751 | ||
|
|
1a6f290979 | ||
|
|
c6e02a620a | ||
|
|
90efaa41c2 | ||
|
|
51a73ad8b5 | ||
|
|
93f2387d1b | ||
|
|
207a631a65 | ||
|
|
74e60e98b7 | ||
|
|
6ca6bf7457 | ||
|
|
ea49cdeb17 | ||
|
|
bcc8b1917a | ||
|
|
67b12d4416 | ||
|
|
dc02daecee | ||
|
|
6212b38ea6 | ||
|
|
ce7e5726e7 | ||
|
|
1156b3f22e | ||
|
|
66097f3507 | ||
|
|
146f02d314 | ||
|
|
77e5882ce7 | ||
|
|
d44850a4f3 | ||
|
|
e6e692dc43 | ||
|
|
dc65770ae3 | ||
|
|
8c15802277 | ||
|
|
3d666d9929 | ||
|
|
c0c960ec2e | ||
|
|
2bb4db127c | ||
|
|
dd1a5d4f58 | ||
|
|
961fdf7029 | ||
|
|
ff3d2b006f | ||
|
|
d7efbad3df | ||
|
|
d70995bb1a | ||
|
|
1b58e95dce | ||
|
|
780f70d5c6 | ||
|
|
a01e1f96fa | ||
|
|
3adb443ca5 | ||
|
|
dc9ff3a004 | ||
|
|
f879f4f432 | ||
|
|
183f831a7e | ||
|
|
3c361be621 | ||
|
|
5764d44faf | ||
|
|
ce86677faa | ||
|
|
17d93b39d5 | ||
|
|
1357b85a3d | ||
|
|
c67cb5c604 | ||
|
|
6ffb0df6cb | ||
|
|
032b40c78d | ||
|
|
742aea115b | ||
|
|
0f16cd46f9 | ||
|
|
eea64cf272 | ||
|
|
3d5c5f8054 | ||
|
|
748099a324 | ||
|
|
9c1d050d8b | ||
|
|
4ef834e295 | ||
|
|
50bead172b | ||
|
|
ee4508af03 | ||
|
|
6638959d66 | ||
|
|
f266a9d25d | ||
|
|
6cb8e1a518 | ||
|
|
85375359ed | ||
|
|
17c9ba2c68 | ||
|
|
34334ad8b8 | ||
|
|
4527714094 | ||
|
|
b43d74dbb7 | ||
|
|
0c4140ff02 | ||
|
|
b074c02fa2 | ||
|
|
7636c97f9f | ||
|
|
4211ab28b0 | ||
|
|
cecfbc7e20 | ||
|
|
31a6aff932 | ||
|
|
c4a67c4356 | ||
|
|
9f5765134b | ||
|
|
0c5b51d2ac | ||
|
|
31c4198cee | ||
|
|
a94c4b4ce4 | ||
|
|
088dd99ec1 | ||
|
|
4f9b907b4d | ||
|
|
e7dc3e6062 | ||
|
|
53055e78eb | ||
|
|
9a9c34aa18 | ||
|
|
2f1384840c | ||
|
|
b462e55799 | ||
|
|
263ec60ba6 | ||
|
|
8d44a57200 | ||
|
|
976722c129 | ||
|
|
4a9447d344 | ||
|
|
ac2ed9aa87 | ||
|
|
51cf241dae | ||
|
|
f239b8e26d | ||
|
|
ab9f4161ea | ||
|
|
1d10e649b7 | ||
|
|
a95b40aac6 | ||
|
|
1b5777821f | ||
|
|
587d469193 | ||
|
|
6c3e30f3ea | ||
|
|
91dbcae9e2 | ||
|
|
fb5db4f6b7 | ||
|
|
a1e029a825 | ||
|
|
54dbf9b6f2 | ||
|
|
b6344951fe | ||
|
|
ea972118b5 | ||
|
|
2fccd4799d | ||
|
|
a3937e4d0d | ||
|
|
acb022d5d5 | ||
|
|
b6e46d6101 | ||
|
|
347f75f804 | ||
|
|
4005ced505 | ||
|
|
c748c35b37 | ||
|
|
a73836ca43 | ||
|
|
b357fab326 | ||
|
|
16a3000451 | ||
|
|
c4fac2d179 | ||
|
|
60d11a6eba | ||
|
|
93edbda984 | ||
|
|
75bd94d757 | ||
|
|
a5cf0b6ef5 | ||
|
|
506280d645 | ||
|
|
2f79b4fde7 | ||
|
|
846f98628d | ||
|
|
eed9be5a9e | ||
|
|
27c77afafc | ||
|
|
c156b2f817 | ||
|
|
96fcc5df6b | ||
|
|
3c775fd5de | ||
|
|
75e9ee6528 | ||
|
|
ea0ee96398 | ||
|
|
3fd4a2841a | ||
|
|
6ecf44c87a | ||
|
|
031e8cea6e | ||
|
|
757fbb0124 | ||
|
|
d65e3f73df | ||
|
|
5b13105d58 | ||
|
|
c8745afb37 | ||
|
|
85189c0bde | ||
|
|
a8635bade2 | ||
|
|
4560572ff2 | ||
|
|
c7fa57fd14 | ||
|
|
54965fdf2e | ||
|
|
30361aa685 | ||
|
|
8be93b72c4 | ||
|
|
c7e9d645e5 | ||
|
|
fe6a3c89f3 | ||
|
|
686a32cbc0 | ||
|
|
55d7397ff5 | ||
|
|
3714d554df | ||
|
|
0415de853b | ||
|
|
0ba1e8f904 | ||
|
|
58bfcb0953 | ||
|
|
fa281d89d2 | ||
|
|
908b10dae0 | ||
|
|
ea03f9def0 | ||
|
|
3510799fca | ||
|
|
1f4a63d6db | ||
|
|
dd94a444d2 | ||
|
|
50fafc9ff6 | ||
|
|
47fc6a689d | ||
|
|
86175a1827 | ||
|
|
6d6e25df4e | ||
|
|
5402ed112c | ||
|
|
235b83d02e | ||
|
|
6ffbec969a | ||
|
|
185ea71646 | ||
|
|
69fcc3acd7 | ||
|
|
da94cf4aab | ||
|
|
8799cf95b4 | ||
|
|
108ce18d51 | ||
|
|
f67ea5d010 | ||
|
|
dd857aeccf | ||
|
|
44b1acd385 | ||
|
|
b2f6018e05 | ||
|
|
bca6507f11 | ||
|
|
30332c2ba5 | ||
|
|
17919d7503 | ||
|
|
42237ced80 | ||
|
|
737021ccdf | ||
|
|
22ed9d8d7c | ||
|
|
49dcd11813 | ||
|
|
7c30086d78 | ||
|
|
e2dbfdc537 | ||
|
|
3e8f9aa31c | ||
|
|
674eb109c2 | ||
|
|
927b5bc8cc | ||
|
|
f7dd0fc582 | ||
|
|
35f30bde04 | ||
|
|
a682b50fd4 | ||
|
|
3b5024749f | ||
|
|
2a56d892d7 | ||
|
|
e3d5eaf388 | ||
|
|
5d1f50117b | ||
|
|
f6a2ec15d7 | ||
|
|
64a8d56725 | ||
|
|
71caea32e7 | ||
|
|
17993ef9ff | ||
|
|
b0aa4ef4c8 | ||
|
|
5c4aaa27d9 | ||
|
|
53586d95d0 | ||
|
|
3877ab1f00 | ||
|
|
2425322e8d | ||
|
|
3f80a113d1 | ||
|
|
9ad20849d3 | ||
|
|
c8c58280d8 | ||
|
|
d40505cd16 | ||
|
|
25c5f84090 | ||
|
|
a58293f04b | ||
|
|
1408fb41b8 | ||
|
|
cb7cb8e527 | ||
|
|
d750dbc703 | ||
|
|
91b417138a | ||
|
|
db5eabd927 | ||
|
|
cbcc0fde04 | ||
|
|
cca3b98a09 | ||
|
|
e63b8ff35d | ||
|
|
b6a37bf0e2 | ||
|
|
249ca4fd75 | ||
|
|
d382869b98 | ||
|
|
41a3c27aba | ||
|
|
af48c46c30 | ||
|
|
52bc51a197 | ||
|
|
57ca9cc840 | ||
|
|
56d6a47ad3 | ||
|
|
b806360a49 | ||
|
|
739a88ed00 | ||
|
|
38d4341e59 | ||
|
|
6118d91922 | ||
|
|
71ac7efafe | ||
|
|
cf0710225c | ||
|
|
21e96df85a | ||
|
|
79f32c2ebd | ||
|
|
492a1f69b3 | ||
|
|
32b962e186 | ||
|
|
37beb8e6b2 | ||
|
|
1ee9f3815d | ||
|
|
65a5e8721c | ||
|
|
036c7a2117 | ||
|
|
229e8e1ad1 | ||
|
|
390bb1bdc5 | ||
|
|
83b401b241 | ||
|
|
cfb2c1f62a | ||
|
|
4ad7df746e | ||
|
|
a218b1d3d0 | ||
|
|
6f61aff735 | ||
|
|
4893f78286 | ||
|
|
97296f3169 | ||
|
|
ebcf4364f5 | ||
|
|
6d0078d39b | ||
|
|
9b80b693c1 | ||
|
|
2e1e26fdb9 | ||
|
|
ddeb43783c | ||
|
|
b61e527baa | ||
|
|
53ce96b48f | ||
|
|
36ea166c20 | ||
|
|
f9fd4c71f1 | ||
|
|
44171417e3 | ||
|
|
b554a46a4c | ||
|
|
19a2c37678 | ||
|
|
23d74040ed | ||
|
|
685e63b9da | ||
|
|
39855f4d2b | ||
|
|
ae6fc41ca9 | ||
|
|
1bb41bec2a | ||
|
|
041d679a54 | ||
|
|
46058c275c | ||
|
|
b9e15a1340 | ||
|
|
f9eb4ffee2 | ||
|
|
419952f33b | ||
|
|
af8bdcd9e0 | ||
|
|
54122af9d7 | ||
|
|
5e4852bd32 | ||
|
|
3714f9fdbd | ||
|
|
a9dd6e0f3d | ||
|
|
3c9f4ee555 | ||
|
|
7ff0b4c6b9 | ||
|
|
648662469b | ||
|
|
c37ddd7872 | ||
|
|
17f35cde19 | ||
|
|
b5d17b99df | ||
|
|
5c9746e209 | ||
|
|
51938affc2 | ||
|
|
856006a68d | ||
|
|
a2622263ce | ||
|
|
7db1613b1a | ||
|
|
3add686e9b | ||
|
|
a9f0983f0f | ||
|
|
3b16d6c291 | ||
|
|
85399bd6e2 | ||
|
|
aef2b95d41 | ||
|
|
11a233da84 | ||
|
|
016aa87e34 | ||
|
|
9094af565f | ||
|
|
d7de908c66 | ||
|
|
a3985ac94c | ||
|
|
b48f26020a | ||
|
|
630d3679b5 | ||
|
|
78c89eb29b | ||
|
|
7fe4996bbe | ||
|
|
370deda5a7 | ||
|
|
d0d8ff8313 | ||
|
|
550b9ebf4d | ||
|
|
2265456bda | ||
|
|
b0f1cde33f | ||
|
|
cdd150be42 | ||
|
|
0d24d75d8f | ||
|
|
5c866c67b5 | ||
|
|
b49fea87ab | ||
|
|
1c262d22ce | ||
|
|
0dde77009e | ||
|
|
3e71c35fdd | ||
|
|
10f1fc5e92 | ||
|
|
8fbad757bf | ||
|
|
5755aa3eb8 | ||
|
|
f76d14f613 | ||
|
|
0c3470bab2 | ||
|
|
315ad06ecc | ||
|
|
c1627612cf | ||
|
|
b5cfceeed6 | ||
|
|
7fe7dd743c | ||
|
|
3e36f27987 | ||
|
|
b9f4f3f71c | ||
|
|
ff1230c3ae | ||
|
|
e71ec574e1 | ||
|
|
c2e716ec4a | ||
|
|
5ad8bb1830 | ||
|
|
751e78baa9 | ||
|
|
be620bd437 | ||
|
|
44365651a6 | ||
|
|
7b557c0586 | ||
|
|
495a27c0a7 | ||
|
|
e07fddb20b | ||
|
|
56eb1d106f | ||
|
|
c8b6d61ae2 | ||
|
|
47e91e943c | ||
|
|
4f8c52f09e | ||
|
|
f20b32b01b | ||
|
|
37cbb5ed01 | ||
|
|
289e13cb46 | ||
|
|
fb03c4c311 | ||
|
|
a65aecaf74 | ||
|
|
da9ba0a26a | ||
|
|
8440e881c0 | ||
|
|
85fa8a4761 | ||
|
|
5c8c78ca69 | ||
|
|
e9097c3b29 | ||
|
|
d53ee24741 | ||
|
|
6517cb15ef | ||
|
|
7b78f92feb | ||
|
|
0af6dc3838 | ||
|
|
e313a2ea45 | ||
|
|
f21ef30482 | ||
|
|
606fce65ab | ||
|
|
b4084484ff | ||
|
|
80062908d9 | ||
|
|
af8f7e95b0 | ||
|
|
9553478384 | ||
|
|
535b3ce286 | ||
|
|
cfe9c86edd | ||
|
|
ee66044425 | ||
|
|
30d56e1af0 | ||
|
|
354630770b | ||
|
|
74da63e3ca | ||
|
|
faa438bc51 | ||
|
|
6de6fb1932 | ||
|
|
6a8acefa30 | ||
|
|
ddec8325e7 | ||
|
|
b1852526f5 | ||
|
|
20aaf58ee9 | ||
|
|
b3db597c4b | ||
|
|
d302f228f9 | ||
|
|
74d5e2b0c1 | ||
|
|
dd42d24d8a | ||
|
|
5692fb32cd | ||
|
|
dbf80d564b | ||
|
|
72b82a8d19 | ||
|
|
c3beaedaa6 | ||
|
|
db694b20df | ||
|
|
34227ce738 | ||
|
|
24b1360eb8 | ||
|
|
60aff26d94 | ||
|
|
144bdf7dc7 | ||
|
|
8db4bb298e | ||
|
|
028477f34d | ||
|
|
6725c9e3cd | ||
|
|
a14dee5b8d | ||
|
|
2f8a1fc58f | ||
|
|
f250c4310e | ||
|
|
ad46e8a5e0 | ||
|
|
1e7031e5f4 | ||
|
|
8c736e979d | ||
|
|
335742a023 | ||
|
|
384ff3484c | ||
|
|
e17c29c258 | ||
|
|
e7d979ca74 | ||
|
|
bc2f38c790 | ||
|
|
88ee089d86 | ||
|
|
d9adaa5020 | ||
|
|
4a963adbcb | ||
|
|
56ac57b4cf | ||
|
|
cdbe2393c4 | ||
|
|
2f4490d059 | ||
|
|
447660504c | ||
|
|
5e44d18d54 | ||
|
|
7a9edae227 | ||
|
|
81b7bd35f4 | ||
|
|
4b946a23ca | ||
|
|
5ab92b1833 | ||
|
|
1a6a16e061 | ||
|
|
061b602334 | ||
|
|
f7deb02560 | ||
|
|
9dfe85eca3 | ||
|
|
cd5823d9f6 | ||
|
|
1af0a6cc8f | ||
|
|
9ed8ebab78 | ||
|
|
7f82a33bf5 | ||
|
|
41a7e5c915 | ||
|
|
8d22c0ba90 | ||
|
|
4636ae7237 | ||
|
|
ee9d0c4a99 | ||
|
|
476a0ad6ad | ||
|
|
14612fc116 | ||
|
|
5c87787351 | ||
|
|
3fa796382e | ||
|
|
f4f4761517 | ||
|
|
c78de41ccf | ||
|
|
b0d58d10bd | ||
|
|
f778741ee3 | ||
|
|
2fb517b293 | ||
|
|
aecc32fbfb | ||
|
|
64c8c0590c | ||
|
|
24dc436122 | ||
|
|
1e1fef52c4 | ||
|
|
c8b9e2ff37 | ||
|
|
34a6902986 | ||
|
|
0c47b0eb53 | ||
|
|
5d4ef7d009 | ||
|
|
31d23bc9a7 | ||
|
|
b4b54d1796 | ||
|
|
5023dfeb24 | ||
|
|
dad3092d8d | ||
|
|
ab77c032de | ||
|
|
778e54ef32 | ||
|
|
699db63615 | ||
|
|
01d0e13884 | ||
|
|
d743c196be | ||
|
|
c60ec18f34 | ||
|
|
1be1274d40 | ||
|
|
a65b49ea30 | ||
|
|
762d14c5a1 | ||
|
|
bd44c52cbb | ||
|
|
4cbdd27862 | ||
|
|
62cf42efb4 | ||
|
|
47dc30ea79 | ||
|
|
55abdff58c | ||
|
|
27cde532be | ||
|
|
9c2bd58488 | ||
|
|
a3c898fc4f | ||
|
|
da380119ef | ||
|
|
db631e3d57 | ||
|
|
fb63f9cc92 | ||
|
|
31e738a5a3 | ||
|
|
f3b1b351e8 | ||
|
|
149ecb380b | ||
|
|
67a43ff549 | ||
|
|
69f29d6fac | ||
|
|
51c12ef745 | ||
|
|
d0e89ec72a | ||
|
|
74c8b381e6 | ||
|
|
ddbd4236ab | ||
|
|
c0cbb5c75d | ||
|
|
717d00e64a | ||
|
|
19e9e52c4f | ||
|
|
1df2465222 | ||
|
|
70883d7fdc | ||
|
|
f3f5f0f896 | ||
|
|
a7828809e9 | ||
|
|
b80a2b0bc2 | ||
|
|
88d897eb14 | ||
|
|
2e9f562329 | ||
|
|
7aa6a30169 | ||
|
|
298f713e9b | ||
|
|
aa339d0851 | ||
|
|
ebb8596f03 | ||
|
|
fb57d3beef | ||
|
|
8488175ee8 | ||
|
|
e68191dcd9 | ||
|
|
0019a1f7dc | ||
|
|
5095a9e1c3 | ||
|
|
ddbaa8b32b | ||
|
|
9f7275eced | ||
|
|
3d8e6823f7 | ||
|
|
1368348cd9 | ||
|
|
b31cf90596 | ||
|
|
66025a06d5 | ||
|
|
65c8504141 | ||
|
|
cd16e001f6 | ||
|
|
77d2bc58fd | ||
|
|
bfc57459e1 | ||
|
|
3422718415 | ||
|
|
0b5e0a1113 | ||
|
|
b6b44e0f2d | ||
|
|
b642543600 | ||
|
|
095a05a8e1 | ||
|
|
4783204f31 | ||
|
|
82d819a6c7 | ||
|
|
10e7875680 | ||
|
|
2aad566857 | ||
|
|
a3e0a3ff1a | ||
|
|
8fe9ad80bb | ||
|
|
531262387d | ||
|
|
a73cd87b50 | ||
|
|
4601940f8d | ||
|
|
6e5b2c7368 | ||
|
|
8a3cc6041d | ||
|
|
25687c2db1 | ||
|
|
871229d0c5 | ||
|
|
74d179e479 | ||
|
|
910d384ed8 | ||
|
|
da89d6ab9c | ||
|
|
8d2159761f | ||
|
|
d434f8641d | ||
|
|
f49733d1d2 | ||
|
|
a3726d72f5 | ||
|
|
fe89ae13af | ||
|
|
6b90cd1277 | ||
|
|
ce64ec5397 | ||
|
|
bf6ca2dc78 | ||
|
|
204c68d475 | ||
|
|
5a7e59d833 | ||
|
|
0336a982ff | ||
|
|
aa18b63c16 | ||
|
|
3f890551e7 | ||
|
|
823127c87e | ||
|
|
cf2c9c6dc7 | ||
|
|
9b63b90ec4 | ||
|
|
a0ba140895 | ||
|
|
588f2502ec | ||
|
|
ae7d4592e1 | ||
|
|
24c7d145ea | ||
|
|
f1e7d68415 | ||
|
|
91f1528149 | ||
|
|
4f19f89d4c | ||
|
|
60b8bccd37 | ||
|
|
674dcba53c | ||
|
|
3dec9e531f | ||
|
|
980197cb05 | ||
|
|
5d30c71ccf | ||
|
|
1dcc5ca9f3 | ||
|
|
1eb24981c6 | ||
|
|
cb6b8ea5ac | ||
|
|
546a662a30 | ||
|
|
ef84c4dfad | ||
|
|
8ca81d0991 | ||
|
|
02e8158918 | ||
|
|
37cbe6c488 | ||
|
|
58d763f971 | ||
|
|
3d2700d29d | ||
|
|
e14ea94b0d | ||
|
|
17fde3df0c | ||
|
|
162204f28f | ||
|
|
491fb14eaa | ||
|
|
f4d7fe8850 | ||
|
|
4af583e5d5 | ||
|
|
282887368a | ||
|
|
94e372d8f2 | ||
|
|
3cb67939e4 | ||
|
|
f3197d2618 | ||
|
|
3785f7621c | ||
|
|
2b59badde7 | ||
|
|
54162b43c8 | ||
|
|
1933cdc28c | ||
|
|
f2512d1ff1 | ||
|
|
d54b13e80a | ||
|
|
8ed5e81bdb | ||
|
|
f0fc83372b | ||
|
|
081504edab | ||
|
|
b939123e84 | ||
|
|
f008d9dd19 | ||
|
|
d2386af523 | ||
|
|
41b9024e28 | ||
|
|
50c17bd5e4 | ||
|
|
1882c43389 | ||
|
|
d7027887cc | ||
|
|
7d8c9df252 | ||
|
|
e1e465dc51 | ||
|
|
b276d48ecf | ||
|
|
0c13734f7a | ||
|
|
de788266eb | ||
|
|
eb879a743e | ||
|
|
231bceeabb | ||
|
|
387b7602cf | ||
|
|
d8c14c04e3 | ||
|
|
33f981d8f1 | ||
|
|
6188b89ff0 | ||
|
|
2d424e078e | ||
|
|
ee5d72301a | ||
|
|
ddb02adbb4 | ||
|
|
b6b05f79a1 | ||
|
|
4a438e4799 | ||
|
|
31dc69da42 | ||
|
|
35dfd13ddd | ||
|
|
b4535bd29b | ||
|
|
5a30ec1806 | ||
|
|
d7bb80468b | ||
|
|
92f6d31f33 | ||
|
|
421bc93765 | ||
|
|
9d6a692054 | ||
|
|
278c7bfc53 | ||
|
|
ad23c0e03e | ||
|
|
ca8274dbe8 | ||
|
|
1234fbf5f4 | ||
|
|
afebfe5f4f | ||
|
|
e64ed4c27f | ||
|
|
a4b7236289 | ||
|
|
16c4374f7a | ||
|
|
05a77c7406 | ||
|
|
fceead7cbe | ||
|
|
3d81bdd281 | ||
|
|
56ab8de968 | ||
|
|
30b150dbfc | ||
|
|
40ee3b1b45 | ||
|
|
c79217dd75 | ||
|
|
075d4d4210 | ||
|
|
216b679e4b | ||
|
|
c5fe81f4e6 | ||
|
|
0c464d0220 | ||
|
|
02f28d12e3 | ||
|
|
13d24278f2 | ||
|
|
42ef4352f4 | ||
|
|
843720a671 | ||
|
|
0884dd88d6 | ||
|
|
4262fa8637 | ||
|
|
29a2db6552 | ||
|
|
06fa0c17a4 | ||
|
|
cfdca6a894 | ||
|
|
2873f6c193 | ||
|
|
8b963ed63c | ||
|
|
adb951426a | ||
|
|
9ced96a1c9 | ||
|
|
7e155dc87b | ||
|
|
c5e2d80fc0 | ||
|
|
b0fa646de9 | ||
|
|
83f08cffee | ||
|
|
03652a0030 | ||
|
|
1c3e0ba656 | ||
|
|
48f80b947b | ||
|
|
d7873de4e8 | ||
|
|
32d025bcf2 | ||
|
|
61ab5d1652 | ||
|
|
f5fd6833e2 | ||
|
|
163e6f56df | ||
|
|
5650697475 | ||
|
|
2968087d37 | ||
|
|
e7ec80f58a | ||
|
|
f0ba699463 | ||
|
|
06d5b14b86 | ||
|
|
dff544cd5d | ||
|
|
73bc0f6258 | ||
|
|
7e5e180000 | ||
|
|
fc431df2b4 | ||
|
|
bb61be630a | ||
|
|
cdc9ec2854 | ||
|
|
21d3703b69 | ||
|
|
c395be252e | ||
|
|
293c350fb7 | ||
|
|
a777f336e1 | ||
|
|
5b6c186125 | ||
|
|
6451d59deb | ||
|
|
c912b66a8f | ||
|
|
d62c43bc95 | ||
|
|
3b7b9b6ed1 | ||
|
|
9822a6ed5d | ||
|
|
a06f4dfad6 | ||
|
|
b92df87400 | ||
|
|
d7921c0111 | ||
|
|
af09c3e62a | ||
|
|
0ed42f657d | ||
|
|
ed7fbabd1c | ||
|
|
bd03563fcb | ||
|
|
05ffc7f8d6 | ||
|
|
ebc475d278 | ||
|
|
2813437515 | ||
|
|
ea2e885505 | ||
|
|
a7fadc3a45 | ||
|
|
8040a20f71 | ||
|
|
0e87854819 | ||
|
|
3bc6c641de | ||
|
|
4abb6e17ba | ||
|
|
1986f08cf9 | ||
|
|
620ae5cf1d | ||
|
|
590ee5a248 | ||
|
|
a08326ab60 | ||
|
|
63cf99361d | ||
|
|
1e54ca82b8 | ||
|
|
2ec576e110 | ||
|
|
4251e976b3 | ||
|
|
d831e2f3a4 | ||
|
|
21f20417d6 | ||
|
|
e1c914d9bb | ||
|
|
4b03b0a93a | ||
|
|
bbcde55a9e | ||
|
|
0cba898280 | ||
|
|
b9edec069a | ||
|
|
939cb7958a | ||
|
|
ebb38c6518 | ||
|
|
fa80d2f3cc | ||
|
|
869f37cd89 | ||
|
|
c22202585d | ||
|
|
f28c912d5a | ||
|
|
a0e56c5282 | ||
|
|
de7da1e806 | ||
|
|
add2f6f669 | ||
|
|
b06e765e68 | ||
|
|
c3952cb985 | ||
|
|
d5469a64d2 | ||
|
|
ac26fc6d5f | ||
|
|
122088712d | ||
|
|
9fb09ce14d | ||
|
|
392fb21946 | ||
|
|
e94b05851f | ||
|
|
571a5962b7 | ||
|
|
8b6863dc40 | ||
|
|
01af629399 | ||
|
|
4ece6d2a9b | ||
|
|
18d1d7af33 | ||
|
|
5ada250a66 | ||
|
|
308c7ab670 | ||
|
|
89d35e020a | ||
|
|
6729570799 | ||
|
|
f72f5f6438 | ||
|
|
a02e11e0bc | ||
|
|
9ff15e1506 | ||
|
|
fdddfc6b1f | ||
|
|
78ebb6295d | ||
|
|
73c89e8c00 | ||
|
|
fcc499e401 | ||
|
|
6e8efe22aa | ||
|
|
f9c5cb73a2 | ||
|
|
c939521f5f | ||
|
|
2640e05307 | ||
|
|
c1a371e3d3 | ||
|
|
5a4871155a | ||
|
|
d8e6dbf788 | ||
|
|
7ea69ae4b4 | ||
|
|
d238ead451 | ||
|
|
e6b449f24d | ||
|
|
41018d05a8 | ||
|
|
d48008e13d | ||
|
|
a9cf438100 | ||
|
|
6c7a0d21d2 | ||
|
|
d887db4c18 | ||
|
|
e6be69ec6d | ||
|
|
c1ba7db8a1 | ||
|
|
dc5b5896a9 | ||
|
|
38678fafc1 | ||
|
|
7611211d1c | ||
|
|
0d9c2cd902 | ||
|
|
1a84c109fc | ||
|
|
cbedf3f8cc | ||
|
|
50436e3106 | ||
|
|
62498ec867 | ||
|
|
91b94bb16c | ||
|
|
c2bbdc503c | ||
|
|
babae0fa6e | ||
|
|
f047882ac1 | ||
|
|
b818d6a921 | ||
|
|
ba631c8451 | ||
|
|
2c963b389c | ||
|
|
8437fbc314 | ||
|
|
76404004a4 | ||
|
|
1b64514c2c | ||
|
|
6efea7d365 | ||
|
|
e1a3b77d67 | ||
|
|
cfc7ad5627 | ||
|
|
b3cbf14ad6 | ||
|
|
51cfbe6b00 | ||
|
|
155f397d0b | ||
|
|
4b53b8b6a1 | ||
|
|
dbea4a1787 | ||
|
|
5b6da85e68 | ||
|
|
61671e9d3f | ||
|
|
f94e05e04e | ||
|
|
b5cad9a8cc | ||
|
|
3a97d8cc82 | ||
|
|
3c099465da | ||
|
|
fcdb2591b6 | ||
|
|
b67a16e9af | ||
|
|
1d672616be | ||
|
|
ed9d70903d | ||
|
|
8a9bab8a46 | ||
|
|
fdc6a08eb4 | ||
|
|
154309912d | ||
|
|
a84fea86b8 | ||
|
|
609998bc18 | ||
|
|
891868b061 | ||
|
|
3fc5cf8b9f | ||
|
|
6048ed5bc1 | ||
|
|
a1a007134c | ||
|
|
d82f9fa432 | ||
|
|
c0074301a3 | ||
|
|
9c2c05ad44 | ||
|
|
fad230c02e | ||
|
|
ad3c95f273 | ||
|
|
684fdb6095 | ||
|
|
8e64f171b8 | ||
|
|
d616c3fed7 | ||
|
|
e07e6cd2a3 | ||
|
|
f1bd5cdb52 | ||
|
|
be82b6bed9 | ||
|
|
37ad4758a1 | ||
|
|
e4a238a05c | ||
|
|
e5ca922ce8 | ||
|
|
6a1f4431d0 | ||
|
|
d67eda24d2 | ||
|
|
bba0cc8dc5 | ||
|
|
884b52b710 | ||
|
|
01ef2351b3 | ||
|
|
de1d566e9e | ||
|
|
19114a61ae | ||
|
|
36e5599ead | ||
|
|
f5a82e575c | ||
|
|
1851de323d | ||
|
|
f9408640a3 | ||
|
|
73837730fa | ||
|
|
9b46991721 | ||
|
|
606cd0368f | ||
|
|
d9ef23ad99 | ||
|
|
48b8f54c12 | ||
|
|
cec9f90c1c | ||
|
|
44d36f140a | ||
|
|
f88adcfa85 | ||
|
|
c5919f0c15 | ||
|
|
ac160cac12 | ||
|
|
867aaa5864 | ||
|
|
0b26505a20 | ||
|
|
98ad513621 | ||
|
|
591edbb003 | ||
|
|
a17b1c2ab7 | ||
|
|
648f14d95b | ||
|
|
2e4c2422b1 | ||
|
|
463fdb1ed9 | ||
|
|
d6b612a4a3 | ||
|
|
d24376608e | ||
|
|
ff41f9bd9a | ||
|
|
6d3dd5c484 | ||
|
|
f0bc8983b8 | ||
|
|
729354b038 | ||
|
|
c0be857f37 | ||
|
|
98b0e88ace | ||
|
|
3d501df21f | ||
|
|
1b4abb747d | ||
|
|
b1d418f7fb | ||
|
|
a702b4752b | ||
|
|
684ba7c0cb | ||
|
|
1bcc27d2be | ||
|
|
50d3b305d4 | ||
|
|
a341c98ec6 | ||
|
|
fa5c8a00e4 | ||
|
|
42f6c20ada | ||
|
|
787738094d | ||
|
|
a44ebfe99f | ||
|
|
c5959664e4 | ||
|
|
9b581268de | ||
|
|
acf654c984 | ||
|
|
94f83eb9e3 | ||
|
|
07c675ed06 | ||
|
|
f292e01980 | ||
|
|
61f9123147 | ||
|
|
60708a7fd7 | ||
|
|
761fb93aba | ||
|
|
20bc718bdc | ||
|
|
07337d2f41 | ||
|
|
3d2de00f49 | ||
|
|
a7242ca846 | ||
|
|
d3298ac5f2 | ||
|
|
e486243c06 | ||
|
|
8eaefb57d3 | ||
|
|
c21f7afdcb | ||
|
|
d734dee589 | ||
|
|
f035c4c01a | ||
|
|
8fcdcecf36 | ||
|
|
3f9ee1ac2e | ||
|
|
188e1f788d | ||
|
|
98a0f1cf5b | ||
|
|
1467a673b9 | ||
|
|
889eb86d89 | ||
|
|
125da0e2db | ||
|
|
14b274c707 | ||
|
|
dedbc20ac6 | ||
|
|
36cf2348d0 | ||
|
|
a19f8996b8 | ||
|
|
7293263773 | ||
|
|
ed5b07374d | ||
|
|
07b96bef95 | ||
|
|
5c3cca157d | ||
|
|
f1f66a9b9d | ||
|
|
f4007210c1 | ||
|
|
77962dbd4c | ||
|
|
419cbae55f | ||
|
|
5233789c40 | ||
|
|
70731719f7 | ||
|
|
cf4aa68f98 | ||
|
|
6bfd89074c | ||
|
|
acc4893a7f | ||
|
|
ab61ae2b36 | ||
|
|
514b9c1c7d | ||
|
|
44f2cf2b24 | ||
|
|
9befec5cd1 | ||
|
|
7efdfe5639 | ||
|
|
be6811b29a | ||
|
|
f160b8df04 | ||
|
|
158471b218 | ||
|
|
a383cece53 | ||
|
|
0901e6ab77 | ||
|
|
e1485971d8 | ||
|
|
91c9b1bfe7 | ||
|
|
befe6be86c | ||
|
|
79428cf4ed | ||
|
|
7bde5fe893 | ||
|
|
2f7e024387 | ||
|
|
9235cfa739 | ||
|
|
f1e623ec5a | ||
|
|
7c86dc9ac6 | ||
|
|
ec2da0a399 | ||
|
|
dd9b2a872c | ||
|
|
9767759033 | ||
|
|
7537d79311 | ||
|
|
b68f34eb9e | ||
|
|
0a23c4abd6 | ||
|
|
b51768b08e | ||
|
|
4561f0c79e | ||
|
|
5c92491bc0 | ||
|
|
083605e2d7 | ||
|
|
d28e2f0f56 | ||
|
|
87a964355b | ||
|
|
fbc3f0cef5 | ||
|
|
2b1625e3a8 | ||
|
|
6230df749e | ||
|
|
e62003c0ce | ||
|
|
d0f092f7ae | ||
|
|
9e2e07e8a7 | ||
|
|
4154d47c07 | ||
|
|
62e0cba7bd | ||
|
|
39d5c785d5 | ||
|
|
e5cb81c11f | ||
|
|
3abb8d38ec | ||
|
|
7f0860d5d0 | ||
|
|
8c74c8ab6f | ||
|
|
37df899ada | ||
|
|
f55181e447 | ||
|
|
b1bc2f8c5c | ||
|
|
350df7ca55 | ||
|
|
8768a8aca6 | ||
|
|
0f8bb016f4 | ||
|
|
eb30791ff4 | ||
|
|
7b37bf0f48 | ||
|
|
fff67a5917 | ||
|
|
3dbe91577c | ||
|
|
0e05b64ebc | ||
|
|
c895e99306 | ||
|
|
6d7e8beaaa | ||
|
|
8b62d8a6c5 | ||
|
|
ec44850646 | ||
|
|
2ea5a98ee0 | ||
|
|
80928b90a5 | ||
|
|
b28174aba1 | ||
|
|
da170bab3a | ||
|
|
49a2f998dd | ||
|
|
4efdc8b4f7 | ||
|
|
a75507980a | ||
|
|
8d0decc17a | ||
|
|
e334cbb5d4 | ||
|
|
e3ac8ab19d | ||
|
|
bddf6e9017 | ||
|
|
bcdc0217b3 | ||
|
|
521841b447 | ||
|
|
c53cd336f9 | ||
|
|
a8226989c8 | ||
|
|
114b156d74 | ||
|
|
def2d4bac9 | ||
|
|
250e2ab6aa | ||
|
|
6185ddf76a | ||
|
|
dddf192e5a | ||
|
|
2d32f77ed0 | ||
|
|
7eb7533d42 | ||
|
|
bb711fe255 | ||
|
|
14a8759b82 | ||
|
|
5a0d4eff71 | ||
|
|
805d6bbc8c | ||
|
|
a0004ab892 | ||
|
|
46a444dc21 | ||
|
|
aefb5c37fe | ||
|
|
2216978726 | ||
|
|
3a2d8edb53 | ||
|
|
e0d57b7713 | ||
|
|
3a9912c01e | ||
|
|
6cadee31bf | ||
|
|
678845dfda | ||
|
|
7f7e218504 | ||
|
|
256a9951f5 | ||
|
|
370b45bb35 | ||
|
|
616c4a9a53 | ||
|
|
821f7d6694 | ||
|
|
bc89be6187 | ||
|
|
86b6a4cefd | ||
|
|
1f9a53a454 | ||
|
|
f22d112da2 | ||
|
|
c36c06ab99 | ||
|
|
a915c60999 | ||
|
|
6ddb8a7d88 | ||
|
|
50bb2cb1e6 | ||
|
|
e1571dda9b | ||
|
|
7410c6216c | ||
|
|
8f84df44ab | ||
|
|
82f21b6734 | ||
|
|
892fd5a6ef | ||
|
|
3069db0cfd | ||
|
|
b71c9d539e | ||
|
|
78e643970d | ||
|
|
34da3dd3d7 | ||
|
|
817cb0d363 | ||
|
|
01088e214c | ||
|
|
3e4500f9fd | ||
|
|
d4b62608a9 | ||
|
|
e6bfc1c2fc | ||
|
|
051cabed69 | ||
|
|
04916a6e97 | ||
|
|
f3be2fa66b | ||
|
|
c36087cc0c | ||
|
|
e35909ac7d | ||
|
|
e5a693ab94 | ||
|
|
9e9cfb4600 | ||
|
|
5dddaac006 | ||
|
|
14af6f1fba | ||
|
|
e88064fdc9 | ||
|
|
6badf5ea1d | ||
|
|
20f5f3da24 | ||
|
|
8c1b147705 | ||
|
|
366155b828 | ||
|
|
2c7d1897eb | ||
|
|
26ccb23402 | ||
|
|
d9e2471fcd | ||
|
|
8302b351dd | ||
|
|
b8647c0481 | ||
|
|
a168403d68 | ||
|
|
42d8650058 | ||
|
|
7ad0d46c11 | ||
|
|
58812f7f1f | ||
|
|
65133b2aef | ||
|
|
291d8cd335 | ||
|
|
7a27e2b94b | ||
|
|
57a8eab1c3 | ||
|
|
236a7f68e9 | ||
|
|
81d424f475 | ||
|
|
687ba8c9a2 | ||
|
|
6d74a564e6 | ||
|
|
0a3e0665ab | ||
|
|
a19f1fbc67 | ||
|
|
2aa4615c78 | ||
|
|
7dd1346878 | ||
|
|
31f8c7d9cb | ||
|
|
914ce0b94d | ||
|
|
664f09111e | ||
|
|
6141f414fd | ||
|
|
8911f0f217 | ||
|
|
5af108ccee | ||
|
|
94e4169445 | ||
|
|
479b5b7064 | ||
|
|
674e935cf5 | ||
|
|
6b4982d75b | ||
|
|
4167743a34 | ||
|
|
ba289f6db4 | ||
|
|
422fd11f4d | ||
|
|
614d9c9b0d | ||
|
|
f91be86662 | ||
|
|
72f05544e8 | ||
|
|
81f55820be | ||
|
|
b9c14e1d65 | ||
|
|
5c55453a0e | ||
|
|
12491c4983 | ||
|
|
77d379c021 | ||
|
|
1a12a59e91 | ||
|
|
0b970dd9c7 | ||
|
|
93ac2bd53e | ||
|
|
f9646ac47a | ||
|
|
e8ed3b9e23 | ||
|
|
6238a07c8f | ||
|
|
1fb33f0c47 | ||
|
|
a842f41627 | ||
|
|
c4c135e678 | ||
|
|
f36f2fdea2 | ||
|
|
e3f0a67584 | ||
|
|
f6f05fa0c6 | ||
|
|
54ca7ceac8 | ||
|
|
cf5b38d4f5 | ||
|
|
b23669400f | ||
|
|
aaacd00ecf | ||
|
|
03aa22ba84 | ||
|
|
1493b8703f | ||
|
|
59308c20c6 | ||
|
|
cac5d8e716 | ||
|
|
7f16757bbe | ||
|
|
674e3846e2 | ||
|
|
3a0be0cca9 | ||
|
|
00d7c3a05a | ||
|
|
91f5fca5e9 | ||
|
|
1d7cb0c119 | ||
|
|
24599aa64f | ||
|
|
54c1553892 | ||
|
|
0ae53ce1a1 | ||
|
|
c69ffe02f8 | ||
|
|
7bfc2e0e74 | ||
|
|
9cc674c283 | ||
|
|
66e597a05c | ||
|
|
074c636e53 | ||
|
|
4ec44936f6 | ||
|
|
eb9c41f2a0 | ||
|
|
04afb99c54 | ||
|
|
2124e7b221 | ||
|
|
2416a8bf96 | ||
|
|
408a40f78b | ||
|
|
195ae5ce4b | ||
|
|
9bebb1e9a9 | ||
|
|
e7bec5be1d | ||
|
|
c708e8ba8e | ||
|
|
60f26cc067 | ||
|
|
64fa058bc7 | ||
|
|
9a770eeae9 | ||
|
|
ffc2c5a26e | ||
|
|
b6c5bd552e | ||
|
|
05147a3199 | ||
|
|
7f0d08ad77 | ||
|
|
64f95af833 | ||
|
|
7cd2662355 | ||
|
|
154d485c9e | ||
|
|
5f6821c7e2 | ||
|
|
59cf823d56 | ||
|
|
70312c58be | ||
|
|
4942366271 | ||
|
|
f237d0f212 | ||
|
|
9b424e0fe7 | ||
|
|
81b75d178b | ||
|
|
410e732eb3 | ||
|
|
2ca93cd93d | ||
|
|
50c806286e | ||
|
|
59de835b42 | ||
|
|
ecbac76cba | ||
|
|
9b16f2139d | ||
|
|
ef14df5ba2 | ||
|
|
b4be9875b2 | ||
|
|
8d0c962f42 | ||
|
|
5e0cb8d658 | ||
|
|
1869ab94fe | ||
|
|
2627c5baaf | ||
|
|
e4e16b8f77 | ||
|
|
85218d74d4 | ||
|
|
47d5b66986 | ||
|
|
ecfb133de2 | ||
|
|
23c95d24f1 | ||
|
|
b37d89bd08 | ||
|
|
5c848d59b2 | ||
|
|
b1c04dece9 | ||
|
|
b87c7987bb | ||
|
|
0bef52ae7d | ||
|
|
9ae04cf1ec | ||
|
|
0f7c4a8d4f | ||
|
|
07353207c0 | ||
|
|
d89be83414 | ||
|
|
01382527f5 | ||
|
|
dece2193fc | ||
|
|
d298f864fa | ||
|
|
ff37d8c691 | ||
|
|
f7f305a564 | ||
|
|
86f38e11cd | ||
|
|
0b60c48253 | ||
|
|
024b9840f0 | ||
|
|
2f4f59d82f | ||
|
|
830d5ee763 | ||
|
|
5bc63f7a33 | ||
|
|
b54f970e12 | ||
|
|
0cc8c54152 | ||
|
|
241e1e27d0 | ||
|
|
0145e86202 | ||
|
|
4d50e1e373 | ||
|
|
4b6ae34800 | ||
|
|
2feb85e831 | ||
|
|
a67ac81265 | ||
|
|
dab536c9e8 | ||
|
|
64cf298521 | ||
|
|
10b5aaa6a5 | ||
|
|
8c79e5ccfb | ||
|
|
2ef536a342 | ||
|
|
85b1303460 | ||
|
|
60aca3a241 | ||
|
|
db98381a86 | ||
|
|
f62ef34715 | ||
|
|
f95a6c1a5a | ||
|
|
65555b5dd0 | ||
|
|
9f6331a35e | ||
|
|
8c10cb6230 | ||
|
|
c8894bcead | ||
|
|
edc602c33a | ||
|
|
abfb2ca810 | ||
|
|
9967efe45a | ||
|
|
e77d80bda5 | ||
|
|
a691d1750a | ||
|
|
834e2f9304 | ||
|
|
66e5e9c1ce | ||
|
|
2d5f613984 | ||
|
|
868e7a278f | ||
|
|
f953d17889 | ||
|
|
3473969aae | ||
|
|
ec11ae7c40 | ||
|
|
8217ee1bbb | ||
|
|
1ad3ee0aec | ||
|
|
b3e9a2fede | ||
|
|
2f5d6bf909 | ||
|
|
cd79f479e9 | ||
|
|
12c0f675ce | ||
|
|
29471b8019 | ||
|
|
cad661f0c8 | ||
|
|
261d770482 | ||
|
|
a75c9c74fd | ||
|
|
8b5bdf9a16 | ||
|
|
e35eba2d51 | ||
|
|
df8c88cef4 | ||
|
|
844e12769c | ||
|
|
b982626ac4 | ||
|
|
517b2b42a6 | ||
|
|
6778c33628 | ||
|
|
bc0b11e1ef | ||
|
|
6b0d4b9c93 | ||
|
|
7d4e4f029e | ||
|
|
1fbdb629ff | ||
|
|
a437237947 | ||
|
|
910637c549 | ||
|
|
e8974a3e69 | ||
|
|
6c98310f7f | ||
|
|
72aed0d26d | ||
|
|
e10ef8b9e0 | ||
|
|
39fa8ba831 | ||
|
|
8ee8279044 | ||
|
|
c3f85c3bb2 | ||
|
|
ad59c4cbf3 | ||
|
|
26bc981981 | ||
|
|
514c34b242 | ||
|
|
6f266c0090 | ||
|
|
baa7352ca6 | ||
|
|
fdb1c8d99a | ||
|
|
79f5dba094 | ||
|
|
2134965f16 | ||
|
|
77b7a59a27 | ||
|
|
365e7126d0 | ||
|
|
d3daf9d159 | ||
|
|
8d59a519a6 | ||
|
|
239262f360 | ||
|
|
9eb938fd6f | ||
|
|
3fcbf1a43f | ||
|
|
2b7ee271df | ||
|
|
11512c6281 | ||
|
|
b16ba547ab | ||
|
|
e5b8899b4c | ||
|
|
fee3e9e63b | ||
|
|
57a24d30b5 | ||
|
|
5a83610fb1 | ||
|
|
96086f12c6 | ||
|
|
a6ee3ce07f | ||
|
|
5f36ddd425 | ||
|
|
bec67074e0 | ||
|
|
895c92cdae | ||
|
|
7406a5bc26 | ||
|
|
ef97714404 | ||
|
|
6d6daee511 | ||
|
|
32cd1c2a65 | ||
|
|
abad920a8a | ||
|
|
e1ab49e100 | ||
|
|
df300e89a2 | ||
|
|
9a278bed21 | ||
|
|
4a7fc084ce | ||
|
|
2649e736fb | ||
|
|
42ac891e28 | ||
|
|
4dc3b9072e | ||
|
|
ae3235b099 | ||
|
|
7cc5e0d577 | ||
|
|
f6b956dc8e | ||
|
|
fcb2bacd1e | ||
|
|
a9401e921e | ||
|
|
02e7e315b2 | ||
|
|
def72a64e0 | ||
|
|
46e7672197 | ||
|
|
2f8a5ce935 | ||
|
|
730e5bd831 | ||
|
|
b7984a05af | ||
|
|
5d4af67186 | ||
|
|
370e08cf4d | ||
|
|
aaf7e3f943 | ||
|
|
fbde0936e7 | ||
|
|
af48f7bab4 | ||
|
|
61def89878 | ||
|
|
88e86cee77 | ||
|
|
acc1625406 | ||
|
|
502f448053 | ||
|
|
55b081884c | ||
|
|
fd67171908 | ||
|
|
3f63a64254 | ||
|
|
ee837889db | ||
|
|
a5f4cba72f |
86
.github/workflows/check_pypi_version.yml
vendored
Normal file
86
.github/workflows/check_pypi_version.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
name: Check PyPI Version
|
||||||
|
|
||||||
|
# Check to be sure `pip install aider-chat` installs the most recently published version.
|
||||||
|
# If dependencies get yanked, it may render the latest version uninstallable.
|
||||||
|
# See https://github.com/Aider-AI/aider/issues/3699 for example.
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run once a day at midnight UTC
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
workflow_dispatch: # Allows manual triggering
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install aider-chat
|
||||||
|
run: pip install aider-chat
|
||||||
|
|
||||||
|
- name: Get installed aider version
|
||||||
|
id: installed_version
|
||||||
|
run: |
|
||||||
|
set -x # Enable debugging output
|
||||||
|
aider_version_output=$(aider --version)
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Error: 'aider --version' command failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Raw aider --version output: $aider_version_output"
|
||||||
|
|
||||||
|
# Extract version number (format X.Y.Z)
|
||||||
|
version_num=$(echo "$aider_version_output" | grep -oP '\d+\.\d+\.\d+')
|
||||||
|
|
||||||
|
# Check if grep found anything
|
||||||
|
if [ -z "$version_num" ]; then
|
||||||
|
echo "Error: Could not extract version number using grep -oP '\d+\.\d+\.\d+' from output: $aider_version_output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Extracted version number: $version_num"
|
||||||
|
echo "version=$version_num" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Fetch all history for all tags
|
||||||
|
|
||||||
|
- name: Get latest tag
|
||||||
|
id: latest_tag
|
||||||
|
run: |
|
||||||
|
set -x # Enable debugging output
|
||||||
|
# Fetch all tags from remote just in case
|
||||||
|
git fetch --tags origin main
|
||||||
|
# Get the latest tag that strictly matches vX.Y.Z (no suffixes like .dev)
|
||||||
|
# List all tags, sort by version descending, filter for exact pattern, take the first one
|
||||||
|
latest_tag=$(git tag --sort=-v:refname | grep -P '^v\d+\.\d+\.\d+$' | head -n 1)
|
||||||
|
|
||||||
|
if [ -z "$latest_tag" ]; then
|
||||||
|
echo "Error: Could not find any tags matching the pattern '^v\d+\.\d+\.\d+$'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Latest non-dev tag: $latest_tag"
|
||||||
|
# Remove 'v' prefix for comparison
|
||||||
|
tag_num=${latest_tag#v}
|
||||||
|
echo "Extracted tag number: $tag_num"
|
||||||
|
echo "tag=$tag_num" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Compare versions
|
||||||
|
run: |
|
||||||
|
echo "Installed version: ${{ steps.installed_version.outputs.version }}"
|
||||||
|
echo "Latest tag version: ${{ steps.latest_tag.outputs.tag }}"
|
||||||
|
if [ "${{ steps.installed_version.outputs.version }}" != "${{ steps.latest_tag.outputs.tag }}" ]; then
|
||||||
|
echo "Error: Installed aider version (${{ steps.installed_version.outputs.version }}) does not match the latest tag (${{ steps.latest_tag.outputs.tag }})."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Versions match."
|
||||||
51
.github/workflows/docker-build-test.yml
vendored
51
.github/workflows/docker-build-test.yml
vendored
@@ -4,23 +4,25 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'aider/website/**'
|
- 'aider/website/**'
|
||||||
- README.md
|
- 'README.md'
|
||||||
- HISTORY.md
|
- 'HISTORY.md'
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- '!.github/workflows/docker-build-test.yml'
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'aider/website/**'
|
- 'aider/website/**'
|
||||||
- README.md
|
- 'README.md'
|
||||||
|
- 'HISTORY.md'
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- '!.github/workflows/docker-build-test.yml'
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
# copy most of these steps from release.yml, but push: false and no tags:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
docker_build_and_push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -33,7 +35,15 @@ jobs:
|
|||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Build Docker standard image
|
- name: Login to DockerHub
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build Docker images (PR)
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@@ -42,7 +52,19 @@ jobs:
|
|||||||
push: false
|
push: false
|
||||||
target: aider
|
target: aider
|
||||||
|
|
||||||
- name: Build Docker full image
|
- name: Build Docker images (Push)
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider:dev
|
||||||
|
target: aider
|
||||||
|
|
||||||
|
- name: Build Docker full image (PR)
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@@ -50,3 +72,14 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: false
|
push: false
|
||||||
target: aider-full
|
target: aider-full
|
||||||
|
|
||||||
|
- name: Build Docker full image (Push)
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.DOCKERHUB_USERNAME }}/aider-full:dev
|
||||||
|
target: aider-full
|
||||||
|
|||||||
8
.github/workflows/pages.yml
vendored
8
.github/workflows/pages.yml
vendored
@@ -12,6 +12,7 @@ on:
|
|||||||
- "main"
|
- "main"
|
||||||
paths:
|
paths:
|
||||||
- "aider/website/**"
|
- "aider/website/**"
|
||||||
|
- ".github/workflows/pages.yml"
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -55,10 +56,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
JEKYLL_ENV: production
|
JEKYLL_ENV: production
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
# Automatically uploads an artifact from the './_site' directory by default
|
uses: actions/upload-pages-artifact@v3
|
||||||
uses: actions/upload-pages-artifact@v1
|
|
||||||
with:
|
with:
|
||||||
path: "aider/website/_site/"
|
path: "aider/website/_site"
|
||||||
|
|
||||||
# Deployment job
|
# Deployment job
|
||||||
deploy:
|
deploy:
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v2
|
uses: actions/deploy-pages@v4
|
||||||
|
|
||||||
- name: Set up Python 3.12
|
- name: Set up Python 3.12
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
|
|||||||
11
.github/workflows/ubuntu-tests.yml
vendored
11
.github/workflows/ubuntu-tests.yml
vendored
@@ -4,14 +4,19 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'aider/website/**'
|
- 'aider/website/**'
|
||||||
- README.md
|
- 'README.md'
|
||||||
- HISTORY.md
|
- 'HISTORY.md'
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- '!.github/workflows/ubuntu-tests.yml'
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'aider/website/**'
|
- 'aider/website/**'
|
||||||
- README.md
|
- 'README.md'
|
||||||
|
- 'HISTORY.md'
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- '!.github/workflows/ubuntu-tests.yml'
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
|
|||||||
11
.github/workflows/windows-tests.yml
vendored
11
.github/workflows/windows-tests.yml
vendored
@@ -4,14 +4,19 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'aider/website/**'
|
- 'aider/website/**'
|
||||||
- README.md
|
- 'README.md'
|
||||||
- HISTORY.md
|
- 'HISTORY.md'
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- '!.github/workflows/windows-tests.yml'
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'aider/website/**'
|
- 'aider/website/**'
|
||||||
- README.md
|
- 'README.md'
|
||||||
|
- 'HISTORY.md'
|
||||||
|
- '.github/workflows/*'
|
||||||
|
- '!.github/workflows/windows-tests.yml'
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
|
|||||||
90
.github/workflows/windows_check_pypi_version.yml
vendored
Normal file
90
.github/workflows/windows_check_pypi_version.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
name: Windows Check PyPI Version
|
||||||
|
|
||||||
|
# Check to be sure `pip install aider-chat` installs the most recently published version on Windows.
|
||||||
|
# If dependencies get yanked, it may render the latest version uninstallable.
|
||||||
|
# See https://github.com/Aider-AI/aider/issues/3699 for example.
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run once a day at 1 AM UTC (offset from Ubuntu check)
|
||||||
|
- cron: '0 1 * * *'
|
||||||
|
workflow_dispatch: # Allows manual triggering
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_version:
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: pwsh # Use PowerShell for all run steps
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install aider-chat
|
||||||
|
run: pip install aider-chat
|
||||||
|
|
||||||
|
- name: Get installed aider version
|
||||||
|
id: installed_version
|
||||||
|
run: |
|
||||||
|
Write-Host "Running 'aider --version'..."
|
||||||
|
$aider_version_output = aider --version
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Error: 'aider --version' command failed."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host "Raw aider --version output: $aider_version_output"
|
||||||
|
|
||||||
|
# Extract version number (format X.Y.Z) using PowerShell regex
|
||||||
|
$match = [regex]::Match($aider_version_output, '\d+\.\d+\.\d+')
|
||||||
|
|
||||||
|
if (-not $match.Success) {
|
||||||
|
Write-Error "Error: Could not extract version number using regex '\d+\.\d+\.\d+' from output: $aider_version_output"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$version_num = $match.Value
|
||||||
|
|
||||||
|
Write-Host "Extracted version number: $version_num"
|
||||||
|
echo "version=$version_num" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Fetch all history for all tags
|
||||||
|
|
||||||
|
- name: Get latest tag
|
||||||
|
id: latest_tag
|
||||||
|
run: |
|
||||||
|
Write-Host "Fetching tags..."
|
||||||
|
# Fetch all tags from remote just in case
|
||||||
|
git fetch --tags origin main
|
||||||
|
Write-Host "Getting latest non-dev tag..."
|
||||||
|
# Get the latest tag that strictly matches vX.Y.Z (no suffixes like .dev)
|
||||||
|
# List all tags, sort by version descending, filter for exact pattern, take the first one
|
||||||
|
$latest_tag = (git tag --sort=-v:refname | Select-String -Pattern '^v\d+\.\d+\.\d+$' | Select-Object -First 1).Line
|
||||||
|
|
||||||
|
if (-not $latest_tag) {
|
||||||
|
Write-Error "Error: Could not find any tags matching the pattern '^v\d+\.\d+\.\d+$'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Latest non-dev tag: $latest_tag"
|
||||||
|
# Remove 'v' prefix for comparison
|
||||||
|
$tag_num = $latest_tag.Substring(1)
|
||||||
|
Write-Host "Extracted tag number: $tag_num"
|
||||||
|
echo "tag=$tag_num" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Compare versions
|
||||||
|
run: |
|
||||||
|
Write-Host "Installed version: ${{ steps.installed_version.outputs.version }}"
|
||||||
|
Write-Host "Latest tag version: ${{ steps.latest_tag.outputs.tag }}"
|
||||||
|
if ("${{ steps.installed_version.outputs.version }}" -ne "${{ steps.latest_tag.outputs.tag }}") {
|
||||||
|
Write-Error "Error: Installed aider version (${{ steps.installed_version.outputs.version }}) does not match the latest tag (${{ steps.latest_tag.outputs.tag }})."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
Write-Host "Versions match."
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ aider/_version.py
|
|||||||
.venv/
|
.venv/
|
||||||
.#*
|
.#*
|
||||||
.gitattributes
|
.gitattributes
|
||||||
|
tmp.benchmarks/
|
||||||
@@ -18,5 +18,6 @@ repos:
|
|||||||
rev: v2.2.6
|
rev: v2.2.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
|
args: ["--skip", "aider/website/docs/languages.md"]
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- tomli
|
- tomli
|
||||||
|
|||||||
@@ -56,13 +56,6 @@ It is recommended to create a virtual environment outside of the repository to k
|
|||||||
python -m venv /path/to/venv
|
python -m venv /path/to/venv
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using `virtualenv` (for older Python versions)
|
|
||||||
|
|
||||||
```
|
|
||||||
pip install virtualenv
|
|
||||||
virtualenv /path/to/venv
|
|
||||||
```
|
|
||||||
|
|
||||||
### Activate the Virtual Environment
|
### Activate the Virtual Environment
|
||||||
|
|
||||||
#### On Windows
|
#### On Windows
|
||||||
|
|||||||
290
HISTORY.md
290
HISTORY.md
@@ -1,6 +1,294 @@
|
|||||||
# Release history
|
# Release history
|
||||||
|
|
||||||
### main branch
|
### Aider v0.80.2
|
||||||
|
|
||||||
|
- Bumped deps to pickup litellm change to properly display the root cause of OpenRouter "choices" errors.
|
||||||
|
- Aider wrote 89% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.80.1
|
||||||
|
|
||||||
|
- Updated deps for yanked fsspec and aiohttp packages #3699
|
||||||
|
- Removed redundant dependency check during OpenRouter OAuth flow, by Claudia Pellegrino.
|
||||||
|
- Aider wrote 0% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.80.0
|
||||||
|
|
||||||
|
- OpenRouter OAuth integration:
|
||||||
|
- Offer to OAuth against OpenRouter if no model and keys are provided.
|
||||||
|
- Select OpenRouter default model based on free/paid tier status if `OPENROUTER_API_KEY` is set and no model is specified.
|
||||||
|
- Prioritize `gemini/gemini-2.5-pro-exp-03-25` if `GEMINI_API_KEY` is set, and `vertex_ai/gemini-2.5-pro-exp-03-25` if `VERTEXAI_PROJECT` is set, when no model is specified.
|
||||||
|
- Validate user-configured color settings on startup and warn/disable invalid ones.
|
||||||
|
- Warn at startup if `--stream` and `--cache-prompts` are used together, as cost estimates may be inaccurate.
|
||||||
|
- Boost repomap ranking for files whose path components match identifiers mentioned in the chat.
|
||||||
|
- Change web scraping timeout from an error to a warning, allowing scraping to continue with potentially incomplete content.
|
||||||
|
- Left-align markdown headings in the terminal output, by Peter Schilling.
|
||||||
|
- Update edit format to the new model's default when switching models with `/model`, if the user was using the old model's default format.
|
||||||
|
- Add `Ctrl-X Ctrl-E` keybinding to edit the current input buffer in an external editor, by Matteo Landi.
|
||||||
|
- Fix linting errors for filepaths containing shell metacharacters, by Mir Adnan ALI.
|
||||||
|
- Add the `openrouter/deepseek-chat-v3-0324:free` model.
|
||||||
|
- Add repomap support for the Scala language, by Vasil Markoukin.
|
||||||
|
- Fixed bug in `/run` that was preventing auto-testing.
|
||||||
|
- Fix bug preventing `UnboundLocalError` during git tree traversal.
|
||||||
|
- Handle `GitCommandNotFound` error if git is not installed or not in PATH.
|
||||||
|
- Handle `FileNotFoundError` if the current working directory is deleted while aider is running.
|
||||||
|
- Fix completion menu current item color styling, by Andrey Ivanov.
|
||||||
|
- Aider wrote 87% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.79.2
|
||||||
|
|
||||||
|
- Added 'gemini' alias for gemini-2.5-pro model.
|
||||||
|
- Updated Gemini 2.5 Pro max output tokens to 64k.
|
||||||
|
- Added support for Lisp-style semicolon comments in file watcher, by Matteo Landi.
|
||||||
|
- Added OpenRouter API error detection and retries.
|
||||||
|
- Added openrouter/deepseek-chat-v3-0324 model.
|
||||||
|
- Aider wrote 93% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.79.1
|
||||||
|
|
||||||
|
- Improved model listing to include all models in fuzzy matching, including those provided by aider (not litellm).
|
||||||
|
|
||||||
|
### Aider v0.79.0
|
||||||
|
|
||||||
|
- Added support for Gemini 2.5 Pro models.
|
||||||
|
- Added support for DeepSeek V3 0324 model.
|
||||||
|
- Added a new `/context` command that automatically identifies which files need to be edited for a given request.
|
||||||
|
- Added `/edit` as an alias for the `/editor` command.
|
||||||
|
- Added "overeager" mode for Claude 3.7 Sonnet models to try and keep it working within the requested scope.
|
||||||
|
- Aider wrote 65% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.78.0
|
||||||
|
|
||||||
|
- Added support for thinking tokens for OpenRouter Sonnet 3.7.
|
||||||
|
- Added commands to switch between model types: `/editor-model` for Editor Model, and `/weak-model` for Weak Model, by csala.
|
||||||
|
- Added model setting validation to ignore `--reasoning-effort` and `--thinking-tokens` if the model doesn't support them.
|
||||||
|
- Added `--check-model-accepts-settings` flag (default: true) to force unsupported model settings.
|
||||||
|
- Annotated which models support reasoning_effort and thinking_tokens settings in the model settings data.
|
||||||
|
- Improved code block rendering in markdown output with better padding using NoInsetMarkdown.
|
||||||
|
- Added `--git-commit-verify` flag (default: False) to control whether git commit hooks are bypassed.
|
||||||
|
- Fixed autocompletion for `/ask`, `/code`, and `/architect` commands, by shladnik.
|
||||||
|
- Added vi-like behavior when pressing enter in multiline-mode while in vi normal/navigation-mode, by Marco Mayer.
|
||||||
|
- Added AWS_PROFILE support for Bedrock models, allowing use of AWS profiles instead of explicit credentials, by lentil32.
|
||||||
|
- Enhanced `--aiderignore` argument to resolve both absolute and relative paths, by mopemope.
|
||||||
|
- Improved platform information handling to gracefully handle retrieval errors.
|
||||||
|
- Aider wrote 92% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.77.1
|
||||||
|
|
||||||
|
- Bumped dependencies to pickup litellm fix for Ollama.
|
||||||
|
- Added support for `openrouter/google/gemma-3-27b-it` model.
|
||||||
|
- Updated exclude patterns for help documentation.
|
||||||
|
|
||||||
|
### Aider v0.77.0
|
||||||
|
|
||||||
|
- Big upgrade in [programming languages supported](https://aider.chat/docs/languages.html) by adopting [tree-sitter-language-pack](https://github.com/Goldziher/tree-sitter-language-pack/).
|
||||||
|
- 130 new languages with linter support.
|
||||||
|
- 20 new languages with repo-map support.
|
||||||
|
- Added `/think-tokens` command to set thinking token budget with support for human-readable formats (8k, 10.5k, 0.5M).
|
||||||
|
- Added `/reasoning-effort` command to control model reasoning level.
|
||||||
|
- The `/think-tokens` and `/reasoning-effort` commands display current settings when called without arguments.
|
||||||
|
- Display of thinking token budget and reasoning effort in model information.
|
||||||
|
- Changed `--thinking-tokens` argument to accept string values with human-readable formats.
|
||||||
|
- Added `--auto-accept-architect` flag (default: true) to automatically accept changes from architect coder format without confirmation.
|
||||||
|
- Added support for `cohere_chat/command-a-03-2025` and `gemini/gemma-3-27b-it`
|
||||||
|
- The bare `/drop` command now preserves original read-only files provided via args.read.
|
||||||
|
- Fixed a bug where default model would be set by deprecated `--shortcut` switches even when already specified in the command line.
|
||||||
|
- Improved AutoCompleter to require 3 characters for autocompletion to reduce noise.
|
||||||
|
- Aider wrote 72% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.76.2
|
||||||
|
|
||||||
|
- Fixed handling of JSONDecodeError when loading model cache file.
|
||||||
|
- Fixed handling of GitCommandError when retrieving git user configuration.
|
||||||
|
- Aider wrote 75% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.76.1
|
||||||
|
|
||||||
|
- Added ignore_permission_denied option to file watcher to prevent errors when accessing restricted files, by Yutaka Matsubara.
|
||||||
|
- Aider wrote 0% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.76.0
|
||||||
|
|
||||||
|
- Improved support for thinking/reasoningmodels:
|
||||||
|
- Added `--thinking-tokens` CLI option to control token budget for models that support thinking.
|
||||||
|
- Display thinking/reasoning content from LLMs which return it.
|
||||||
|
- Enhanced handling of reasoning tags to better clean up model responses.
|
||||||
|
- Added deprecation warning for `remove_reasoning` setting, now replaced by `reasoning_tag`.
|
||||||
|
- Aider will notify you when it's completed the last request and needs your input:
|
||||||
|
- Added [notifications when LLM responses are ready](https://aider.chat/docs/usage/notifications.html) with `--notifications` flag.
|
||||||
|
- Specify desktop notification command with `--notifications-command`.
|
||||||
|
- Added support for QWQ 32B.
|
||||||
|
- Switch to `tree-sitter-language-pack` for tree sitter support.
|
||||||
|
- Improved error handling for EOF (Ctrl+D) in user input prompts.
|
||||||
|
- Added helper function to ensure hex color values have a # prefix.
|
||||||
|
- Fixed handling of Git errors when reading staged files.
|
||||||
|
- Improved SSL verification control for model information requests.
|
||||||
|
- Improved empty LLM response handling with clearer warning messages.
|
||||||
|
- Fixed Git identity retrieval to respect global configuration, by Akira Komamura.
|
||||||
|
- Offer to install dependencies for Bedrock and Vertex AI models.
|
||||||
|
- Deprecated model shortcut args (like --4o, --opus) in favor of the --model flag.
|
||||||
|
- Aider wrote 85% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.75.3
|
||||||
|
|
||||||
|
- Support for V3 free on OpenRouter: `--model openrouter/deepseek/deepseek-chat:free`.
|
||||||
|
|
||||||
|
### Aider v0.75.2
|
||||||
|
|
||||||
|
- Added support for Claude 3.7 Sonnet models on OpenRouter, Bedrock and Vertex AI.
|
||||||
|
- Updated default model to Claude 3.7 Sonnet on OpenRouter.
|
||||||
|
- Added support for GPT-4.5-preview model.
|
||||||
|
- Added support for Claude 3.7 Sonnet:beta on OpenRouter.
|
||||||
|
- Fixed weak_model_name patterns to match main model name patterns for some models.
|
||||||
|
|
||||||
|
### Aider v0.75.1
|
||||||
|
|
||||||
|
- Added support for `openrouter/anthropic/claude-3.7-sonnet`
|
||||||
|
|
||||||
|
### Aider v0.75.0
|
||||||
|
|
||||||
|
- Basic support for Claude 3.7 Sonnet
|
||||||
|
- Use `--model sonnet` to use the new 3.7
|
||||||
|
- Thinking support coming soon.
|
||||||
|
- Bugfix to `/editor` command.
|
||||||
|
- Aider wrote 46% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.74.3
|
||||||
|
|
||||||
|
- Downgrade streamlit dependency to avoid threading bug.
|
||||||
|
- Added support for tree-sitter language pack.
|
||||||
|
- Added openrouter/o3-mini-high model configuration.
|
||||||
|
- Added build.gradle.kts to special files for Kotlin project support, by Lucas Shadler.
|
||||||
|
|
||||||
|
### Aider v0.74.2
|
||||||
|
|
||||||
|
- Prevent more than one cache warming thread from becoming active.
|
||||||
|
- Fixed continuation prompt ". " for multiline input.
|
||||||
|
- Added HCL (Terraform) syntax support, by Warren Krewenki.
|
||||||
|
|
||||||
|
### Aider v0.74.1
|
||||||
|
|
||||||
|
- Have o1 & o3-mini generate markdown by sending the magic "Formatting re-enabled." string.
|
||||||
|
- Bugfix for multi-line inputs, which should not include the ". " continuation prompt.
|
||||||
|
|
||||||
|
### Aider v0.74.0
|
||||||
|
|
||||||
|
- Dynamically changes the Ollama context window to hold the current chat.
|
||||||
|
- Better support for o3-mini, DeepSeek V3 & R1, o1-mini, o1 especially via third-party API providers.
|
||||||
|
- Remove `<think>` tags from R1 responses for commit messages (and other weak model uses).
|
||||||
|
- Can now specify `use_temperature: <float>` in model settings, not just true/false.
|
||||||
|
- The full docker container now includes `boto3` for Bedrock.
|
||||||
|
- Docker containers now set `HOME=/app` which is the normal project mount-point, to persist `~/.aider`.
|
||||||
|
- Bugfix to prevent creating incorrect filenames like `python`, `php`, etc.
|
||||||
|
- Bugfix for `--timeout`
|
||||||
|
- Bugfix so that `/model` now correctly reports that the weak model is not changed.
|
||||||
|
- Bugfix so that multi-line mode persists through ^C at confirmation prompts.
|
||||||
|
- Watch files now fully ignores top-level directories named in ignore files, to reduce the chance of hitting OS watch limits. Helpful to ignore giant subtrees like `node_modules`.
|
||||||
|
- Fast startup with more providers and when model metadata provided in local files.
|
||||||
|
- Improved .gitignore handling:
|
||||||
|
- Honor ignores already in effect regardless of how they've been configured.
|
||||||
|
- Check for .env only when the file exists.
|
||||||
|
- Yes/No prompts now accept All/Skip as alias for Y/N even when not processing a group of confirmations.
|
||||||
|
- Aider wrote 77% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.73.0
|
||||||
|
|
||||||
|
- Full support for o3-mini: `aider --model o3-mini`
|
||||||
|
- New `--reasoning-effort` argument: low, medium, high.
|
||||||
|
- Improved handling of context window size limits, with better messaging and Ollama-specific guidance.
|
||||||
|
- Added support for removing model-specific reasoning tags from responses with `remove_reasoning: tagname` model setting.
|
||||||
|
- Auto-create parent directories when creating new files, by xqyz.
|
||||||
|
- Support for R1 free on OpenRouter: `--model openrouter/deepseek/deepseek-r1:free`
|
||||||
|
- Aider wrote 69% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.72.3
|
||||||
|
|
||||||
|
- Enforce user/assistant turn order to avoid R1 errors, by miradnanali.
|
||||||
|
- Case-insensitive model name matching while preserving original case.
|
||||||
|
|
||||||
|
### Aider v0.72.2
|
||||||
|
- Harden against user/assistant turn order problems which cause R1 errors.
|
||||||
|
|
||||||
|
### Aider v0.72.1
|
||||||
|
- Fix model metadata for `openrouter/deepseek/deepseek-r1`
|
||||||
|
|
||||||
|
### Aider v0.72.0
|
||||||
|
- Support for DeepSeek R1.
|
||||||
|
- Use shortcut: `--model r1`
|
||||||
|
- Also via OpenRouter: `--model openrouter/deepseek/deepseek-r1`
|
||||||
|
- Added Kotlin syntax support to repo map, by Paul Walker.
|
||||||
|
- Added `--line-endings` for file writing, by Titusz Pan.
|
||||||
|
- Added examples_as_sys_msg=True for GPT-4o models, improves benchmark scores.
|
||||||
|
- Bumped all dependencies, to pick up litellm support for o1 system messages.
|
||||||
|
- Bugfix for turn taking when reflecting lint/test errors.
|
||||||
|
- Aider wrote 52% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.71.1
|
||||||
|
|
||||||
|
- Fix permissions issue in Docker images.
|
||||||
|
- Added read-only file announcements.
|
||||||
|
- Bugfix: ASCII fallback for unicode errors.
|
||||||
|
- Bugfix: integer indices for list slicing in repomap calculations.
|
||||||
|
|
||||||
|
### Aider v0.71.0
|
||||||
|
|
||||||
|
- Prompts to help DeepSeek work better when alternating between `/ask` and `/code`.
|
||||||
|
- Streaming pretty LLM responses is smoother and faster for long replies.
|
||||||
|
- Streaming automatically turns of for model that don't support it
|
||||||
|
- Can now switch to/from `/model o1` and a streaming model
|
||||||
|
- Pretty output remains enabled even when editing files with triple-backtick fences
|
||||||
|
- Bare `/ask`, `/code` and `/architect` commands now switch the chat mode.
|
||||||
|
- Increased default size of the repomap.
|
||||||
|
- Increased max chat history tokens limit from 4k to 8k.
|
||||||
|
- Turn off fancy input and watch files if terminal is dumb.
|
||||||
|
- Added support for custom voice format and input device settings.
|
||||||
|
- Disabled Streamlit email prompt, by apaz-cli.
|
||||||
|
- Docker container runs as non-root user.
|
||||||
|
- Fixed lint command handling of nested spaced strings, by Aaron Weisberg.
|
||||||
|
- Added token count feedback when adding command output to chat.
|
||||||
|
- Improved error handling for large audio files with automatic format conversion.
|
||||||
|
- Improved handling of git repo index errors, by Krazer.
|
||||||
|
- Improved unicode handling in console output with ASCII fallback.
|
||||||
|
- Added AssertionError, AttributeError to git error handling.
|
||||||
|
- Aider wrote 60% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.70.0
|
||||||
|
|
||||||
|
- Full support for o1 models.
|
||||||
|
- Watch files now honors `--subtree-only`, and only watches that subtree.
|
||||||
|
- Improved prompting for watch files, to work more reliably with more models.
|
||||||
|
- New install methods via uv, including one-liners.
|
||||||
|
- Support for openrouter/deepseek/deepseek-chat model.
|
||||||
|
- Better error handling when interactive commands are attempted via `/load` or `--load`.
|
||||||
|
- Display read-only files with abs path if its shorter than rel path.
|
||||||
|
- Ask 10% of users to opt-in to analytics.
|
||||||
|
- Bugfix for auto-suggest.
|
||||||
|
- Gracefully handle unicode errors in git path names.
|
||||||
|
- Aider wrote 74% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.69.1
|
||||||
|
|
||||||
|
- Fix for gemini model names in model metadata.
|
||||||
|
- Show hints about AI! and AI? when user makes AI comments.
|
||||||
|
- Support for running without git installed.
|
||||||
|
- Improved environment variable setup messages on Windows.
|
||||||
|
|
||||||
|
### Aider v0.69.0
|
||||||
|
|
||||||
|
- [Watch files](https://aider.chat/docs/usage/watch.html) improvements:
|
||||||
|
- Use `# ... AI?` comments to trigger aider and ask questions about your code.
|
||||||
|
- Now watches *all* files, not just certain source files.
|
||||||
|
- Use `# AI comments`, `// AI comments`, or `-- AI comments` to give aider instructions in any text file.
|
||||||
|
- Full support for Gemini Flash 2.0 Exp:
|
||||||
|
- `aider --model flash` or `aider --model gemini/gemini-2.0-flash-exp`
|
||||||
|
- [New `--multiline` flag and `/multiline-mode` command](https://aider.chat/docs/usage/commands.html#entering-multi-line-chat-messages) makes ENTER a soft newline and META-ENTER send the message, by @miradnanali.
|
||||||
|
- `/copy-context <instructions>` now takes optional "instructions" when [copying code context to the clipboard](https://aider.chat/docs/usage/copypaste.html#copy-aiders-code-context-to-your-clipboard-paste-into-the-web-ui).
|
||||||
|
- Improved clipboard error handling with helpful requirements install info.
|
||||||
|
- Ask 5% of users if they want to opt-in to analytics.
|
||||||
|
- `/voice` now lets you edit the transcribed text before sending.
|
||||||
|
- Disabled auto-complete in Y/N prompts.
|
||||||
|
- Aider wrote 68% of the code in this release.
|
||||||
|
|
||||||
|
### Aider v0.68.0
|
||||||
|
|
||||||
- [Aider works with LLM web chat UIs](https://aider.chat/docs/usage/copypaste.html).
|
- [Aider works with LLM web chat UIs](https://aider.chat/docs/usage/copypaste.html).
|
||||||
- New `--copy-paste` mode.
|
- New `--copy-paste` mode.
|
||||||
|
|||||||
20
MANIFEST.in
Normal file
20
MANIFEST.in
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# This needs to sync with aider/help_pats.py
|
||||||
|
|
||||||
|
global-exclude .DS_Store
|
||||||
|
|
||||||
|
recursive-exclude aider/website/examples *
|
||||||
|
recursive-exclude aider/website/_posts *
|
||||||
|
|
||||||
|
exclude aider/website/HISTORY.md
|
||||||
|
exclude aider/website/docs/benchmarks*.md
|
||||||
|
exclude aider/website/docs/ctags.md
|
||||||
|
exclude aider/website/docs/unified-diffs.md
|
||||||
|
|
||||||
|
exclude aider/website/install.ps1
|
||||||
|
exclude aider/website/install.sh
|
||||||
|
|
||||||
|
recursive-exclude aider/website/docs/leaderboards *
|
||||||
|
recursive-exclude aider/website/assets *
|
||||||
|
recursive-exclude aider/website *.js
|
||||||
|
recursive-exclude aider/website *.html
|
||||||
|
recursive-exclude aider/website *.yml
|
||||||
248
README.md
248
README.md
@@ -1,136 +1,172 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://aider.chat/"><img src="https://aider.chat/assets/logo.svg" alt="Aider Logo" width="300"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<!-- Edit README.md, not index.md -->
|
<h1 align="center">
|
||||||
|
AI Pair Programming in Your Terminal
|
||||||
|
</h1>
|
||||||
|
|
||||||
# Aider is AI pair programming in your terminal
|
|
||||||
|
|
||||||
Aider lets you pair program with LLMs,
|
<p align="center">
|
||||||
to edit code in your local git repository.
|
Aider lets you pair program with LLMs to start a new project or build on your existing codebase.
|
||||||
Start a new project or work with an existing git repo.
|
</p>
|
||||||
Aider works best with GPT-4o & Claude 3.5 Sonnet and can
|
|
||||||
[connect to almost any LLM](https://aider.chat/docs/llms.html).
|
|
||||||
|
|
||||||
<!-- SCREENCAST START -->
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img
|
<img
|
||||||
src="https://aider.chat/assets/screencast.svg"
|
src="https://aider.chat/assets/screencast.svg"
|
||||||
alt="aider screencast"
|
alt="aider screencast"
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<!-- SCREENCAST END -->
|
|
||||||
|
|
||||||
<!-- VIDEO START
|
|
||||||
<p align="center">
|
|
||||||
<video style="max-width: 100%; height: auto;" autoplay loop muted playsinline>
|
|
||||||
<source src="/assets/shell-cmds-small.mp4" type="video/mp4">
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</p>
|
|
||||||
VIDEO END -->
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://discord.gg/Tv2uQnR88V">
|
|
||||||
<img src="https://img.shields.io/badge/Join-Discord-blue.svg"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://aider.chat/docs/install.html">
|
|
||||||
<img src="https://img.shields.io/badge/Read-Docs-green.svg"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
<!--[[[cog
|
<!--[[[cog
|
||||||
# We can't "include" here.
|
from scripts.homepage import get_badges_md
|
||||||
# Because this page is rendered by GitHub as the repo README
|
text = get_badges_md()
|
||||||
cog.out(open("aider/website/_includes/get-started.md").read())
|
cog.out(text)
|
||||||
]]]-->
|
]]]-->
|
||||||
|
<a href="https://github.com/Aider-AI/aider/stargazers"><img alt="GitHub Stars" title="Total number of GitHub stars the Aider project has received"
|
||||||
You can get started quickly like this:
|
src="https://img.shields.io/github/stars/Aider-AI/aider?style=flat-square&logo=github&color=f1c40f&labelColor=555555"/></a>
|
||||||
|
<a href="https://pypi.org/project/aider-chat/"><img alt="PyPI Downloads" title="Total number of installations via pip from PyPI"
|
||||||
```
|
src="https://img.shields.io/badge/📦%20Installs-1.8M-2ecc71?style=flat-square&labelColor=555555"/></a>
|
||||||
python -m pip install -U aider-chat
|
<img alt="Tokens per week" title="Number of tokens processed weekly by Aider users"
|
||||||
|
src="https://img.shields.io/badge/📈%20Tokens%2Fweek-15B-3498db?style=flat-square&labelColor=555555"/>
|
||||||
# Change directory into a git repo
|
<a href="https://openrouter.ai/"><img alt="OpenRouter Ranking" title="Aider's ranking among applications on the OpenRouter platform"
|
||||||
cd /to/your/git/repo
|
src="https://img.shields.io/badge/🏆%20OpenRouter-Top%2020-9b59b6?style=flat-square&labelColor=555555"/></a>
|
||||||
|
<a href="https://aider.chat/HISTORY.html"><img alt="Singularity" title="Percentage of the new code in Aider's last release written by Aider itself"
|
||||||
# Work with Claude 3.5 Sonnet on your repo
|
src="https://img.shields.io/badge/🔄%20Singularity-87%25-e74c3c?style=flat-square&labelColor=555555"/></a>
|
||||||
export ANTHROPIC_API_KEY=your-key-goes-here
|
|
||||||
aider
|
|
||||||
|
|
||||||
# Work with GPT-4o on your repo
|
|
||||||
export OPENAI_API_KEY=your-key-goes-here
|
|
||||||
aider
|
|
||||||
```
|
|
||||||
<!--[[[end]]]-->
|
<!--[[[end]]]-->
|
||||||
|
</p>
|
||||||
See the
|
|
||||||
[installation instructions](https://aider.chat/docs/install.html)
|
|
||||||
and other
|
|
||||||
[documentation](https://aider.chat/docs/usage.html)
|
|
||||||
for more details.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Run aider with the files you want to edit: `aider <file1> <file2> ...`
|
### [Cloud and local LLMs](https://aider.chat/docs/llms.html)
|
||||||
- Ask for changes:
|
|
||||||
- Add new features or test cases.
|
|
||||||
- Describe a bug.
|
|
||||||
- Paste in an error message or or GitHub issue URL.
|
|
||||||
- Refactor code.
|
|
||||||
- Update docs.
|
|
||||||
- Aider will edit your files to complete your request.
|
|
||||||
- Aider [automatically git commits](https://aider.chat/docs/git.html) changes with a sensible commit message.
|
|
||||||
- Aider works with [most popular languages](https://aider.chat/docs/languages.html): python, javascript, typescript, php, html, css, and more...
|
|
||||||
- Aider works best with GPT-4o & Claude 3.5 Sonnet and can [connect to almost any LLM](https://aider.chat/docs/llms.html).
|
|
||||||
- Aider can edit multiple files at once for complex requests.
|
|
||||||
- Aider uses a [map of your entire git repo](https://aider.chat/docs/repomap.html), which helps it work well in larger codebases.
|
|
||||||
- Edit files in your editor while chatting with aider,
|
|
||||||
and it will always use the latest version.
|
|
||||||
Pair program with AI.
|
|
||||||
- [Add images to the chat](https://aider.chat/docs/usage/images-urls.html) (GPT-4o, Claude 3.5 Sonnet, etc).
|
|
||||||
- [Add URLs to the chat](https://aider.chat/docs/usage/images-urls.html) and aider will read their content.
|
|
||||||
- [Code with your voice](https://aider.chat/docs/usage/voice.html).
|
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/llms.html"><img src="https://aider.chat/assets/icons/brain.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Aider works best with Claude 3.7 Sonnet, DeepSeek R1 & Chat V3, OpenAI o1, o3-mini & GPT-4o, but can connect to almost any LLM, including local models.
|
||||||
|
|
||||||
## Top tier performance
|
<br>
|
||||||
|
|
||||||
[Aider has one of the top scores on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.html).
|
### [Maps your codebase](https://aider.chat/docs/repomap.html)
|
||||||
SWE Bench is a challenging software engineering benchmark where aider
|
|
||||||
solved *real* GitHub issues from popular open source
|
|
||||||
projects like django, scikitlearn, matplotlib, etc.
|
|
||||||
|
|
||||||
## More info
|
<a href="https://aider.chat/docs/repomap.html"><img src="https://aider.chat/assets/icons/map-outline.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Aider makes a map of your entire codebase, which helps it work well in larger projects.
|
||||||
|
|
||||||
- [Documentation](https://aider.chat/)
|
<br>
|
||||||
- [Installation](https://aider.chat/docs/install.html)
|
|
||||||
- [Usage](https://aider.chat/docs/usage.html)
|
### [100+ code languages](https://aider.chat/docs/languages.html)
|
||||||
- [Tutorial videos](https://aider.chat/docs/usage/tutorials.html)
|
|
||||||
|
<a href="https://aider.chat/docs/languages.html"><img src="https://aider.chat/assets/icons/code-tags.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Aider works with most popular programming languages: python, javascript, rust, ruby, go, cpp, php, html, css, and dozens more.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### [Git integration](https://aider.chat/docs/git.html)
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/git.html"><img src="https://aider.chat/assets/icons/source-branch.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Aider automatically commits changes with sensible commit messages. Use familiar git tools to easily diff, manage and undo AI changes.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### [Use in your IDE](https://aider.chat/docs/usage/watch.html)
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/usage/watch.html"><img src="https://aider.chat/assets/icons/monitor.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Use aider from within your favorite IDE or editor. Ask for changes by adding comments to your code and aider will get to work.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### [Images & web pages](https://aider.chat/docs/usage/images-urls.html)
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/usage/images-urls.html"><img src="https://aider.chat/assets/icons/image-multiple.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Add images and web pages to the chat to provide visual context, screenshots, reference docs, etc.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### [Voice-to-code](https://aider.chat/docs/usage/voice.html)
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/usage/voice.html"><img src="https://aider.chat/assets/icons/microphone.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Speak with aider about your code! Request new features, test cases or bug fixes using your voice and let aider implement the changes.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### [Linting & testing](https://aider.chat/docs/usage/lint-test.html)
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/usage/lint-test.html"><img src="https://aider.chat/assets/icons/check-all.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Automatically lint and test your code every time aider makes changes. Aider can fix problems detected by your linters and test suites.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
### [Copy/paste to web chat](https://aider.chat/docs/usage/copypaste.html)
|
||||||
|
|
||||||
|
<a href="https://aider.chat/docs/usage/copypaste.html"><img src="https://aider.chat/assets/icons/content-copy.svg" width="32" height="32" align="left" valign="middle" style="margin-right:10px"></a>
|
||||||
|
Work with any LLM via its web chat interface. Aider streamlines copy/pasting code context and edits back and forth with a browser.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pip install aider-install
|
||||||
|
aider-install
|
||||||
|
|
||||||
|
# Change directory into your codebase
|
||||||
|
cd /to/your/project
|
||||||
|
|
||||||
|
# DeepSeek
|
||||||
|
aider --model deepseek --api-key deepseek=<key>
|
||||||
|
|
||||||
|
# Claude 3.7 Sonnet
|
||||||
|
aider --model sonnet --api-key anthropic=<key>
|
||||||
|
|
||||||
|
# o3-mini
|
||||||
|
aider --model o3-mini --api-key openai=<key>
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [installation instructions](https://aider.chat/docs/install.html) and [usage documentation](https://aider.chat/docs/usage.html) for more details.
|
||||||
|
|
||||||
|
## More Information
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [Installation Guide](https://aider.chat/docs/install.html)
|
||||||
|
- [Usage Guide](https://aider.chat/docs/usage.html)
|
||||||
|
- [Tutorial Videos](https://aider.chat/docs/usage/tutorials.html)
|
||||||
- [Connecting to LLMs](https://aider.chat/docs/llms.html)
|
- [Connecting to LLMs](https://aider.chat/docs/llms.html)
|
||||||
- [Configuration](https://aider.chat/docs/config.html)
|
- [Configuration Options](https://aider.chat/docs/config.html)
|
||||||
- [Troubleshooting](https://aider.chat/docs/troubleshooting.html)
|
- [Troubleshooting](https://aider.chat/docs/troubleshooting.html)
|
||||||
|
- [FAQ](https://aider.chat/docs/faq.html)
|
||||||
|
|
||||||
|
### Community & Resources
|
||||||
- [LLM Leaderboards](https://aider.chat/docs/leaderboards/)
|
- [LLM Leaderboards](https://aider.chat/docs/leaderboards/)
|
||||||
- [GitHub](https://github.com/Aider-AI/aider)
|
- [GitHub Repository](https://github.com/Aider-AI/aider)
|
||||||
- [Discord](https://discord.gg/Tv2uQnR88V)
|
- [Discord Community](https://discord.gg/Tv2uQnR88V)
|
||||||
- [Blog](https://aider.chat/blog/)
|
- [Blog](https://aider.chat/blog/)
|
||||||
|
|
||||||
|
## Kind Words From Users
|
||||||
|
|
||||||
## Kind words from users
|
- *"The best free open source AI coding assistant."* — [IndyDevDan](https://youtu.be/YALpX8oOn78)
|
||||||
|
- *"The best AI coding assistant so far."* — [Matthew Berman](https://www.youtube.com/watch?v=df8afeb1FY8)
|
||||||
|
- *"Aider ... has easily quadrupled my coding productivity."* — [SOLAR_FIELDS](https://news.ycombinator.com/item?id=36212100)
|
||||||
|
- *"It's a cool workflow... Aider's ergonomics are perfect for me."* — [qup](https://news.ycombinator.com/item?id=38185326)
|
||||||
|
- *"It's really like having your senior developer live right in your Git repo - truly amazing!"* — [rappster](https://github.com/Aider-AI/aider/issues/124)
|
||||||
|
- *"What an amazing tool. It's incredible."* — [valyagolev](https://github.com/Aider-AI/aider/issues/6#issue-1722897858)
|
||||||
|
- *"Aider is such an astounding thing!"* — [cgrothaus](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700)
|
||||||
|
- *"It was WAY faster than I would be getting off the ground and making the first few working versions."* — [Daniel Feldman](https://twitter.com/d_feldman/status/1662295077387923456)
|
||||||
|
- *"THANK YOU for Aider! It really feels like a glimpse into the future of coding."* — [derwiki](https://news.ycombinator.com/item?id=38205643)
|
||||||
|
- *"It's just amazing. It is freeing me to do things I felt were out my comfort zone before."* — [Dougie](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656)
|
||||||
|
- *"This project is stellar."* — [funkytaco](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008)
|
||||||
|
- *"Amazing project, definitely the best AI coding assistant I've used."* — [joshuavial](https://github.com/Aider-AI/aider/issues/84)
|
||||||
|
- *"I absolutely love using Aider ... It makes software development feel so much lighter as an experience."* — [principalideal0](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468)
|
||||||
|
- *"I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity."* — [codeninja](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG)
|
||||||
|
- *"I am an aider addict. I'm getting so much more work done, but in less time."* — [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470)
|
||||||
|
- *"After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever."* — [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548)
|
||||||
|
- *"Aider is amazing, coupled with Sonnet 3.5 it's quite mind blowing."* — [Josh Dingus](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548)
|
||||||
|
- *"Hands down, this is the best AI coding assistant tool so far."* — [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs)
|
||||||
|
- *"[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life."* — [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264)
|
||||||
|
- *"Best agent for actual dev work in existing codebases."* — [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20)
|
||||||
|
- *"One of my favorite pieces of software. Blazing trails on new paradigms!"* — [Chris Wall](https://x.com/chris65536/status/1905053299251798432)
|
||||||
|
- *"Aider has been revolutionary for me and my work."* — [Starry Hope](https://x.com/starryhopeblog/status/1904985812137132056)
|
||||||
|
- *"Try aider! One of the best ways to vibe code."* — [Chris Wall](https://x.com/Chris65536/status/1905053418961391929)
|
||||||
|
- *"Aider is hands down the best. And it's free and opensource."* — [AriyaSavakaLurker](https://www.reddit.com/r/ChatGPTCoding/comments/1ik16y6/whats_your_take_on_aider/mbip39n/)
|
||||||
|
- *"Aider is also my best friend."* — [jzn21](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27dcnb/)
|
||||||
|
- *"Try Aider, it's worth it."* — [jorgejhms](https://www.reddit.com/r/ChatGPTCoding/comments/1heuvuo/aider_vs_cline_vs_windsurf_vs_cursor/m27cp99/)
|
||||||
|
- *"I like aider :)"* — [Chenwei Cui](https://x.com/ccui42/status/1904965344999145698)
|
||||||
|
- *"Aider is the precision tool of LLM code gen. It is minimal, thoughtful and capable of surgical changes to your codebase all while keeping the developer in control."* — [Reilly Sweetland](https://x.com/rsweetland/status/1904963807237259586)
|
||||||
|
- *"Cannot believe aider vibe coded a 650 LOC feature across service and cli today in 1 shot."* - [autopoietist](https://discord.com/channels/1131200896827654144/1131200896827654149/1355675042259796101)
|
||||||
|
|
||||||
- *The best free open source AI coding assistant.* -- [IndyDevDan](https://youtu.be/YALpX8oOn78)
|
|
||||||
- *The best AI coding assistant so far.* -- [Matthew Berman](https://www.youtube.com/watch?v=df8afeb1FY8)
|
|
||||||
- *Aider ... has easily quadrupled my coding productivity.* -- [SOLAR_FIELDS](https://news.ycombinator.com/item?id=36212100)
|
|
||||||
- *It's a cool workflow... Aider's ergonomics are perfect for me.* -- [qup](https://news.ycombinator.com/item?id=38185326)
|
|
||||||
- *It's really like having your senior developer live right in your Git repo - truly amazing!* -- [rappster](https://github.com/Aider-AI/aider/issues/124)
|
|
||||||
- *What an amazing tool. It's incredible.* -- [valyagolev](https://github.com/Aider-AI/aider/issues/6#issue-1722897858)
|
|
||||||
- *Aider is such an astounding thing!* -- [cgrothaus](https://github.com/Aider-AI/aider/issues/82#issuecomment-1631876700)
|
|
||||||
- *It was WAY faster than I would be getting off the ground and making the first few working versions.* -- [Daniel Feldman](https://twitter.com/d_feldman/status/1662295077387923456)
|
|
||||||
- *THANK YOU for Aider! It really feels like a glimpse into the future of coding.* -- [derwiki](https://news.ycombinator.com/item?id=38205643)
|
|
||||||
- *It's just amazing. It is freeing me to do things I felt were out my comfort zone before.* -- [Dougie](https://discord.com/channels/1131200896827654144/1174002618058678323/1174084556257775656)
|
|
||||||
- *This project is stellar.* -- [funkytaco](https://github.com/Aider-AI/aider/issues/112#issuecomment-1637429008)
|
|
||||||
- *Amazing project, definitely the best AI coding assistant I've used.* -- [joshuavial](https://github.com/Aider-AI/aider/issues/84)
|
|
||||||
- *I absolutely love using Aider ... It makes software development feel so much lighter as an experience.* -- [principalideal0](https://discord.com/channels/1131200896827654144/1133421607499595858/1229689636012691468)
|
|
||||||
- *I have been recovering from multiple shoulder surgeries ... and have used aider extensively. It has allowed me to continue productivity.* -- [codeninja](https://www.reddit.com/r/OpenAI/s/nmNwkHy1zG)
|
|
||||||
- *I am an aider addict. I'm getting so much more work done, but in less time.* -- [dandandan](https://discord.com/channels/1131200896827654144/1131200896827654149/1135913253483069470)
|
|
||||||
- *After wasting $100 on tokens trying to find something better, I'm back to Aider. It blows everything else out of the water hands down, there's no competition whatsoever.* -- [SystemSculpt](https://discord.com/channels/1131200896827654144/1131200896827654149/1178736602797846548)
|
|
||||||
- *Aider is amazing, coupled with Sonnet 3.5 it’s quite mind blowing.* -- [Josh Dingus](https://discord.com/channels/1131200896827654144/1133060684540813372/1262374225298198548)
|
|
||||||
- *Hands down, this is the best AI coding assistant tool so far.* -- [IndyDevDan](https://www.youtube.com/watch?v=MPYFPvxfGZs)
|
|
||||||
- *[Aider] changed my daily coding workflows. It's mind-blowing how a single Python application can change your life.* -- [maledorak](https://discord.com/channels/1131200896827654144/1131200896827654149/1258453375620747264)
|
|
||||||
- *Best agent for actual dev work in existing codebases.* -- [Nick Dobos](https://twitter.com/NickADobos/status/1690408967963652097?s=20)
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
__version__ = "0.68.1.dev"
|
__version__ = "0.80.3.dev"
|
||||||
safe_version = __version__
|
safe_version = __version__
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -12,6 +12,46 @@ from aider import __version__
|
|||||||
from aider.dump import dump # noqa: F401
|
from aider.dump import dump # noqa: F401
|
||||||
from aider.models import model_info_manager
|
from aider.models import model_info_manager
|
||||||
|
|
||||||
|
PERCENT = 10
|
||||||
|
|
||||||
|
|
||||||
|
def compute_hex_threshold(percent):
|
||||||
|
"""Convert percentage to 6-digit hex threshold.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
percent: Percentage threshold (0-100)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 6-digit hex threshold
|
||||||
|
"""
|
||||||
|
return format(int(0xFFFFFF * percent / 100), "06x")
|
||||||
|
|
||||||
|
|
||||||
|
def is_uuid_in_percentage(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 = compute_hex_threshold(percent)
|
||||||
|
return uuid_str[:6] <= threshold
|
||||||
|
|
||||||
|
|
||||||
mixpanel_project_token = "6da9a43058a5d1b9f3353153921fb04d"
|
mixpanel_project_token = "6da9a43058a5d1b9f3353153921fb04d"
|
||||||
posthog_project_api_key = "phc_99T7muzafUMMZX15H8XePbMSreEUzahHbtWjy3l5Qbv"
|
posthog_project_api_key = "phc_99T7muzafUMMZX15H8XePbMSreEUzahHbtWjy3l5Qbv"
|
||||||
posthog_host = "https://us.i.posthog.com"
|
posthog_host = "https://us.i.posthog.com"
|
||||||
@@ -84,31 +124,7 @@ class Analytics:
|
|||||||
if not self.user_id:
|
if not self.user_id:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
PERCENT = 2.5
|
return is_uuid_in_percentage(self.user_id, PERCENT)
|
||||||
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):
|
def get_data_file_path(self):
|
||||||
try:
|
try:
|
||||||
@@ -181,6 +197,7 @@ class Analytics:
|
|||||||
|
|
||||||
def posthog_error(self):
|
def posthog_error(self):
|
||||||
"""disable posthog if we get an error"""
|
"""disable posthog if we get an error"""
|
||||||
|
print("X" * 100)
|
||||||
# https://github.com/PostHog/posthog-python/blob/9e1bb8c58afaa229da24c4fb576c08bb88a75752/posthog/consumer.py#L86
|
# https://github.com/PostHog/posthog-python/blob/9e1bb8c58afaa229da24c4fb576c08bb88a75752/posthog/consumer.py#L86
|
||||||
# https://github.com/Aider-AI/aider/issues/2532
|
# https://github.com/Aider-AI/aider/issues/2532
|
||||||
self.ph = None
|
self.ph = None
|
||||||
@@ -227,3 +244,7 @@ class Analytics:
|
|||||||
f.write("\n")
|
f.write("\n")
|
||||||
except OSError:
|
except OSError:
|
||||||
pass # Ignore OS errors when writing to logfile
|
pass # Ignore OS errors when writing to logfile
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
dump(compute_hex_threshold(PERCENT))
|
||||||
|
|||||||
177
aider/args.py
177
aider/args.py
@@ -3,6 +3,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import configargparse
|
import configargparse
|
||||||
|
|
||||||
@@ -12,10 +13,20 @@ from aider.args_formatter import (
|
|||||||
MarkdownHelpFormatter,
|
MarkdownHelpFormatter,
|
||||||
YamlHelpFormatter,
|
YamlHelpFormatter,
|
||||||
)
|
)
|
||||||
|
from aider.deprecated import add_deprecated_model_args
|
||||||
|
|
||||||
from .dump import dump # noqa: F401
|
from .dump import dump # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_aiderignore_path(path_str, git_root=None):
|
||||||
|
path = Path(path_str)
|
||||||
|
if path.is_absolute():
|
||||||
|
return str(path)
|
||||||
|
elif git_root:
|
||||||
|
return str(Path(git_root) / path)
|
||||||
|
return str(path)
|
||||||
|
|
||||||
|
|
||||||
def default_env_file(git_root):
|
def default_env_file(git_root):
|
||||||
return os.path.join(git_root, ".env") if git_root else ".env"
|
return os.path.join(git_root, ".env") if git_root else ".env"
|
||||||
|
|
||||||
@@ -38,98 +49,6 @@ def get_parser(default_config_files, git_root):
|
|||||||
default=None,
|
default=None,
|
||||||
help="Specify the model to use for the main chat",
|
help="Specify the model to use for the main chat",
|
||||||
)
|
)
|
||||||
opus_model = "claude-3-opus-20240229"
|
|
||||||
group.add_argument(
|
|
||||||
"--opus",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=opus_model,
|
|
||||||
help=f"Use {opus_model} model for the main chat",
|
|
||||||
)
|
|
||||||
sonnet_model = "claude-3-5-sonnet-20241022"
|
|
||||||
group.add_argument(
|
|
||||||
"--sonnet",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
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",
|
|
||||||
"-4",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=gpt_4_model,
|
|
||||||
help=f"Use {gpt_4_model} model for the main chat",
|
|
||||||
)
|
|
||||||
gpt_4o_model = "gpt-4o"
|
|
||||||
group.add_argument(
|
|
||||||
"--4o",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=gpt_4o_model,
|
|
||||||
help=f"Use {gpt_4o_model} model for the main chat",
|
|
||||||
)
|
|
||||||
gpt_4o_mini_model = "gpt-4o-mini"
|
|
||||||
group.add_argument(
|
|
||||||
"--mini",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=gpt_4o_mini_model,
|
|
||||||
help=f"Use {gpt_4o_mini_model} model for the main chat",
|
|
||||||
)
|
|
||||||
gpt_4_turbo_model = "gpt-4-1106-preview"
|
|
||||||
group.add_argument(
|
|
||||||
"--4-turbo",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=gpt_4_turbo_model,
|
|
||||||
help=f"Use {gpt_4_turbo_model} model for the main chat",
|
|
||||||
)
|
|
||||||
gpt_3_model_name = "gpt-3.5-turbo"
|
|
||||||
group.add_argument(
|
|
||||||
"--35turbo",
|
|
||||||
"--35-turbo",
|
|
||||||
"--3",
|
|
||||||
"-3",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=gpt_3_model_name,
|
|
||||||
help=f"Use {gpt_3_model_name} model for the main chat",
|
|
||||||
)
|
|
||||||
deepseek_model = "deepseek/deepseek-coder"
|
|
||||||
group.add_argument(
|
|
||||||
"--deepseek",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=deepseek_model,
|
|
||||||
help=f"Use {deepseek_model} model for the main chat",
|
|
||||||
)
|
|
||||||
o1_mini_model = "o1-mini"
|
|
||||||
group.add_argument(
|
|
||||||
"--o1-mini",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=o1_mini_model,
|
|
||||||
help=f"Use {o1_mini_model} model for the main chat",
|
|
||||||
)
|
|
||||||
o1_preview_model = "o1-preview"
|
|
||||||
group.add_argument(
|
|
||||||
"--o1-preview",
|
|
||||||
action="store_const",
|
|
||||||
dest="model",
|
|
||||||
const=o1_preview_model,
|
|
||||||
help=f"Use {o1_preview_model} model for the main chat",
|
|
||||||
)
|
|
||||||
|
|
||||||
##########
|
##########
|
||||||
group = parser.add_argument_group("API Keys and settings")
|
group = parser.add_argument_group("API Keys and settings")
|
||||||
@@ -203,6 +122,16 @@ def get_parser(default_config_files, git_root):
|
|||||||
metavar="ALIAS:MODEL",
|
metavar="ALIAS:MODEL",
|
||||||
help="Add a model alias (can be used multiple times)",
|
help="Add a model alias (can be used multiple times)",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--reasoning-effort",
|
||||||
|
type=str,
|
||||||
|
help="Set the reasoning_effort API parameter (default: not set)",
|
||||||
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--thinking-tokens",
|
||||||
|
type=str,
|
||||||
|
help="Set the thinking token budget for models that support it (default: not set)",
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--verify-ssl",
|
"--verify-ssl",
|
||||||
action=argparse.BooleanOptionalAction,
|
action=argparse.BooleanOptionalAction,
|
||||||
@@ -211,7 +140,7 @@ def get_parser(default_config_files, git_root):
|
|||||||
)
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--timeout",
|
"--timeout",
|
||||||
type=int,
|
type=float,
|
||||||
default=None,
|
default=None,
|
||||||
help="Timeout in seconds for API calls (default: None)",
|
help="Timeout in seconds for API calls (default: None)",
|
||||||
)
|
)
|
||||||
@@ -229,6 +158,12 @@ def get_parser(default_config_files, git_root):
|
|||||||
const="architect",
|
const="architect",
|
||||||
help="Use architect edit format for the main chat",
|
help="Use architect edit format for the main chat",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--auto-accept-architect",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=True,
|
||||||
|
help="Enable/disable automatic acceptance of architect changes (default: True)",
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--weak-model",
|
"--weak-model",
|
||||||
metavar="WEAK_MODEL",
|
metavar="WEAK_MODEL",
|
||||||
@@ -256,6 +191,14 @@ def get_parser(default_config_files, git_root):
|
|||||||
default=True,
|
default=True,
|
||||||
help="Only work with models that have meta-data available (default: True)",
|
help="Only work with models that have meta-data available (default: True)",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--check-model-accepts-settings",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=True,
|
||||||
|
help=(
|
||||||
|
"Check if model accepts settings like reasoning_effort/thinking_tokens (default: True)"
|
||||||
|
),
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--max-chat-history-tokens",
|
"--max-chat-history-tokens",
|
||||||
type=int,
|
type=int,
|
||||||
@@ -287,7 +230,7 @@ def get_parser(default_config_files, git_root):
|
|||||||
"--map-tokens",
|
"--map-tokens",
|
||||||
type=int,
|
type=int,
|
||||||
default=None,
|
default=None,
|
||||||
help="Suggested number of tokens to use for repo map, use 0 to disable (default: 1024)",
|
help="Suggested number of tokens to use for repo map, use 0 to disable",
|
||||||
)
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--map-refresh",
|
"--map-refresh",
|
||||||
@@ -427,7 +370,8 @@ def get_parser(default_config_files, git_root):
|
|||||||
default="default",
|
default="default",
|
||||||
help=(
|
help=(
|
||||||
"Set the markdown code theme (default: default, other options include monokai,"
|
"Set the markdown code theme (default: default, other options include monokai,"
|
||||||
" solarized-dark, solarized-light)"
|
" solarized-dark, solarized-light, or a Pygments builtin style,"
|
||||||
|
" see https://pygments.org/styles for available themes)"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
@@ -454,9 +398,11 @@ def get_parser(default_config_files, git_root):
|
|||||||
default_aiderignore_file = (
|
default_aiderignore_file = (
|
||||||
os.path.join(git_root, ".aiderignore") if git_root else ".aiderignore"
|
os.path.join(git_root, ".aiderignore") if git_root else ".aiderignore"
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--aiderignore",
|
"--aiderignore",
|
||||||
metavar="AIDERIGNORE",
|
metavar="AIDERIGNORE",
|
||||||
|
type=lambda path_str: resolve_aiderignore_path(path_str, git_root),
|
||||||
default=default_aiderignore_file,
|
default=default_aiderignore_file,
|
||||||
help="Specify the aider ignore file (default: .aiderignore in git root)",
|
help="Specify the aider ignore file (default: .aiderignore in git root)",
|
||||||
)
|
)
|
||||||
@@ -502,6 +448,12 @@ def get_parser(default_config_files, git_root):
|
|||||||
default=False,
|
default=False,
|
||||||
help="Prefix all commit messages with 'aider: ' (default: False)",
|
help="Prefix all commit messages with 'aider: ' (default: False)",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--git-commit-verify",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
help="Enable/disable git pre-commit hooks with --no-verify (default: False)",
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--commit",
|
"--commit",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -765,6 +717,12 @@ def get_parser(default_config_files, git_root):
|
|||||||
default="utf-8",
|
default="utf-8",
|
||||||
help="Specify the encoding for input and output (default: utf-8)",
|
help="Specify the encoding for input and output (default: utf-8)",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--line-endings",
|
||||||
|
choices=["platform", "lf", "crlf"],
|
||||||
|
default="platform",
|
||||||
|
help="Line endings to use when writing files (default: platform)",
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"-c",
|
"-c",
|
||||||
"--config",
|
"--config",
|
||||||
@@ -795,6 +753,30 @@ def get_parser(default_config_files, git_root):
|
|||||||
default=True,
|
default=True,
|
||||||
help="Enable/disable fancy input with history and completion (default: True)",
|
help="Enable/disable fancy input with history and completion (default: True)",
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--multiline",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
help="Enable/disable multi-line input mode with Meta-Enter to submit (default: False)",
|
||||||
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--notifications",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
help=(
|
||||||
|
"Enable/disable terminal bell notifications when LLM responses are ready (default:"
|
||||||
|
" False)"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--notifications-command",
|
||||||
|
metavar="COMMAND",
|
||||||
|
default=None,
|
||||||
|
help=(
|
||||||
|
"Specify a command to run for notifications instead of the terminal bell. If not"
|
||||||
|
" specified, a default command for your OS may be used."
|
||||||
|
),
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--detect-urls",
|
"--detect-urls",
|
||||||
action=argparse.BooleanOptionalAction,
|
action=argparse.BooleanOptionalAction,
|
||||||
@@ -806,6 +788,11 @@ def get_parser(default_config_files, git_root):
|
|||||||
help="Specify which editor to use for the /editor command",
|
help="Specify which editor to use for the /editor command",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
##########
|
||||||
|
group = parser.add_argument_group("Deprecated model settings")
|
||||||
|
# Add deprecated model shortcut arguments
|
||||||
|
add_deprecated_model_args(parser, group)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -148,11 +148,14 @@ class YamlHelpFormatter(argparse.HelpFormatter):
|
|||||||
parts.append(f"#{switch}: xxx")
|
parts.append(f"#{switch}: xxx")
|
||||||
parts.append("## Specify multiple values like this:")
|
parts.append("## Specify multiple values like this:")
|
||||||
parts.append(f"#{switch}:")
|
parts.append(f"#{switch}:")
|
||||||
parts.append(f"# - xxx")
|
parts.append("# - xxx")
|
||||||
parts.append(f"# - yyy")
|
parts.append("# - yyy")
|
||||||
parts.append(f"# - zzz")
|
parts.append("# - zzz")
|
||||||
else:
|
else:
|
||||||
parts.append(f"#{switch}: xxx\n")
|
if switch.endswith("color"):
|
||||||
|
parts.append(f'#{switch}: "xxx"\n')
|
||||||
|
else:
|
||||||
|
parts.append(f"#{switch}: xxx\n")
|
||||||
|
|
||||||
###
|
###
|
||||||
# parts.append(str(action))
|
# parts.append(str(action))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from .architect_coder import ArchitectCoder
|
from .architect_coder import ArchitectCoder
|
||||||
from .ask_coder import AskCoder
|
from .ask_coder import AskCoder
|
||||||
from .base_coder import Coder
|
from .base_coder import Coder
|
||||||
|
from .context_coder import ContextCoder
|
||||||
from .editblock_coder import EditBlockCoder
|
from .editblock_coder import EditBlockCoder
|
||||||
from .editblock_fenced_coder import EditBlockFencedCoder
|
from .editblock_fenced_coder import EditBlockFencedCoder
|
||||||
from .editor_editblock_coder import EditorEditBlockCoder
|
from .editor_editblock_coder import EditorEditBlockCoder
|
||||||
@@ -23,4 +24,5 @@ __all__ = [
|
|||||||
ArchitectCoder,
|
ArchitectCoder,
|
||||||
EditorEditBlockCoder,
|
EditorEditBlockCoder,
|
||||||
EditorWholeFileCoder,
|
EditorWholeFileCoder,
|
||||||
|
ContextCoder,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from .base_coder import Coder
|
|||||||
class ArchitectCoder(AskCoder):
|
class ArchitectCoder(AskCoder):
|
||||||
edit_format = "architect"
|
edit_format = "architect"
|
||||||
gpt_prompts = ArchitectPrompts()
|
gpt_prompts = ArchitectPrompts()
|
||||||
|
auto_accept_architect = False
|
||||||
|
|
||||||
def reply_completed(self):
|
def reply_completed(self):
|
||||||
content = self.partial_response_content
|
content = self.partial_response_content
|
||||||
@@ -13,7 +14,7 @@ class ArchitectCoder(AskCoder):
|
|||||||
if not content or not content.strip():
|
if not content or not content.strip():
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.io.confirm_ask("Edit the files?"):
|
if not self.auto_accept_architect and not self.io.confirm_ask("Edit the files?"):
|
||||||
return
|
return
|
||||||
|
|
||||||
kwargs = dict()
|
kwargs = dict()
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ class AskPrompts(CoderPrompts):
|
|||||||
main_system = """Act as an expert code analyst.
|
main_system = """Act as an expert code analyst.
|
||||||
Answer questions about the supplied code.
|
Answer questions about the supplied code.
|
||||||
Always reply to the user in {language}.
|
Always reply to the user in {language}.
|
||||||
|
|
||||||
|
Describe code changes however you like. Don't use SEARCH/REPLACE blocks!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
example_messages = []
|
example_messages = []
|
||||||
|
|||||||
@@ -27,10 +27,16 @@ from aider.history import ChatSummary
|
|||||||
from aider.io import ConfirmGroup, InputOutput
|
from aider.io import ConfirmGroup, InputOutput
|
||||||
from aider.linter import Linter
|
from aider.linter import Linter
|
||||||
from aider.llm import litellm
|
from aider.llm import litellm
|
||||||
|
from aider.models import RETRY_TIMEOUT
|
||||||
|
from aider.reasoning_tags import (
|
||||||
|
REASONING_TAG,
|
||||||
|
format_reasoning_content,
|
||||||
|
remove_reasoning_content,
|
||||||
|
replace_reasoning_tags,
|
||||||
|
)
|
||||||
from aider.repo import ANY_GIT_ERROR, GitRepo
|
from aider.repo import ANY_GIT_ERROR, GitRepo
|
||||||
from aider.repomap import RepoMap
|
from aider.repomap import RepoMap
|
||||||
from aider.run_cmd import run_cmd
|
from aider.run_cmd import run_cmd
|
||||||
from aider.sendchat import RETRY_TIMEOUT, send_completion
|
|
||||||
from aider.utils import format_content, format_messages, format_tokens, is_image_file
|
from aider.utils import format_content, format_messages, format_tokens, is_image_file
|
||||||
|
|
||||||
from ..dump import dump # noqa: F401
|
from ..dump import dump # noqa: F401
|
||||||
@@ -59,7 +65,8 @@ def wrap_fence(name):
|
|||||||
|
|
||||||
|
|
||||||
all_fences = [
|
all_fences = [
|
||||||
("``" + "`", "``" + "`"),
|
("`" * 3, "`" * 3),
|
||||||
|
("`" * 4, "`" * 4), # LLMs ignore and revert to triple-backtick, causing #2879
|
||||||
wrap_fence("source"),
|
wrap_fence("source"),
|
||||||
wrap_fence("code"),
|
wrap_fence("code"),
|
||||||
wrap_fence("pre"),
|
wrap_fence("pre"),
|
||||||
@@ -84,7 +91,7 @@ class Coder:
|
|||||||
max_reflections = 3
|
max_reflections = 3
|
||||||
edit_format = None
|
edit_format = None
|
||||||
yield_stream = False
|
yield_stream = False
|
||||||
temperature = 0
|
temperature = None
|
||||||
auto_lint = True
|
auto_lint = True
|
||||||
auto_test = False
|
auto_test = False
|
||||||
test_cmd = None
|
test_cmd = None
|
||||||
@@ -143,7 +150,13 @@ class Coder:
|
|||||||
# the system prompt.
|
# the system prompt.
|
||||||
done_messages = from_coder.done_messages
|
done_messages = from_coder.done_messages
|
||||||
if edit_format != from_coder.edit_format and done_messages and summarize_from_coder:
|
if edit_format != from_coder.edit_format and done_messages and summarize_from_coder:
|
||||||
done_messages = from_coder.summarizer.summarize_all(done_messages)
|
try:
|
||||||
|
done_messages = from_coder.summarizer.summarize_all(done_messages)
|
||||||
|
except ValueError:
|
||||||
|
# If summarization fails, keep the original messages and warn the user
|
||||||
|
io.tool_warning(
|
||||||
|
"Chat history summarization failed, continuing with full history"
|
||||||
|
)
|
||||||
|
|
||||||
# Bring along context from the old Coder
|
# Bring along context from the old Coder
|
||||||
update = dict(
|
update = dict(
|
||||||
@@ -161,6 +174,7 @@ class Coder:
|
|||||||
use_kwargs.update(kwargs) # override passed kwargs
|
use_kwargs.update(kwargs) # override passed kwargs
|
||||||
|
|
||||||
kwargs = use_kwargs
|
kwargs = use_kwargs
|
||||||
|
from_coder.ok_to_warm_cache = False
|
||||||
|
|
||||||
for coder in coders.__all__:
|
for coder in coders.__all__:
|
||||||
if hasattr(coder, "edit_format") and coder.edit_format == edit_format:
|
if hasattr(coder, "edit_format") and coder.edit_format == edit_format:
|
||||||
@@ -193,10 +207,22 @@ class Coder:
|
|||||||
prefix = "Model"
|
prefix = "Model"
|
||||||
|
|
||||||
output = f"{prefix}: {main_model.name} with {self.edit_format} edit format"
|
output = f"{prefix}: {main_model.name} with {self.edit_format} edit format"
|
||||||
|
|
||||||
|
# Check for thinking token budget
|
||||||
|
thinking_tokens = main_model.get_thinking_tokens()
|
||||||
|
if thinking_tokens:
|
||||||
|
output += f", {thinking_tokens} think tokens"
|
||||||
|
|
||||||
|
# Check for reasoning effort
|
||||||
|
reasoning_effort = main_model.get_reasoning_effort()
|
||||||
|
if reasoning_effort:
|
||||||
|
output += f", reasoning {reasoning_effort}"
|
||||||
|
|
||||||
if self.add_cache_headers or main_model.caches_by_default:
|
if self.add_cache_headers or main_model.caches_by_default:
|
||||||
output += ", prompt cache"
|
output += ", prompt cache"
|
||||||
if main_model.info.get("supports_assistant_prefill"):
|
if main_model.info.get("supports_assistant_prefill"):
|
||||||
output += ", infinite output"
|
output += ", infinite output"
|
||||||
|
|
||||||
lines.append(output)
|
lines.append(output)
|
||||||
|
|
||||||
if self.edit_format == "architect":
|
if self.edit_format == "architect":
|
||||||
@@ -230,10 +256,10 @@ class Coder:
|
|||||||
if map_tokens > 0:
|
if map_tokens > 0:
|
||||||
refresh = self.repo_map.refresh
|
refresh = self.repo_map.refresh
|
||||||
lines.append(f"Repo-map: using {map_tokens} tokens, {refresh} refresh")
|
lines.append(f"Repo-map: using {map_tokens} tokens, {refresh} refresh")
|
||||||
max_map_tokens = 2048
|
max_map_tokens = self.main_model.get_repo_map_tokens() * 2
|
||||||
if map_tokens > max_map_tokens:
|
if map_tokens > max_map_tokens:
|
||||||
lines.append(
|
lines.append(
|
||||||
f"Warning: map-tokens > {max_map_tokens} is not recommended as too much"
|
f"Warning: map-tokens > {max_map_tokens} is not recommended. Too much"
|
||||||
" irrelevant code can confuse LLMs."
|
" irrelevant code can confuse LLMs."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -245,11 +271,20 @@ class Coder:
|
|||||||
for fname in self.get_inchat_relative_files():
|
for fname in self.get_inchat_relative_files():
|
||||||
lines.append(f"Added {fname} to the chat.")
|
lines.append(f"Added {fname} to the chat.")
|
||||||
|
|
||||||
|
for fname in self.abs_read_only_fnames:
|
||||||
|
rel_fname = self.get_rel_fname(fname)
|
||||||
|
lines.append(f"Added {rel_fname} to the chat (read-only).")
|
||||||
|
|
||||||
if self.done_messages:
|
if self.done_messages:
|
||||||
lines.append("Restored previous conversation history.")
|
lines.append("Restored previous conversation history.")
|
||||||
|
|
||||||
|
if self.io.multiline_mode:
|
||||||
|
lines.append("Multiline mode: Enabled. Enter inserts newline, Alt-Enter submits text")
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
ok_to_warm_cache = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
main_model,
|
main_model,
|
||||||
@@ -287,6 +322,7 @@ class Coder:
|
|||||||
ignore_mentions=None,
|
ignore_mentions=None,
|
||||||
file_watcher=None,
|
file_watcher=None,
|
||||||
auto_copy_context=False,
|
auto_copy_context=False,
|
||||||
|
auto_accept_architect=True,
|
||||||
):
|
):
|
||||||
# Fill in a dummy Analytics if needed, but it is never .enable()'d
|
# Fill in a dummy Analytics if needed, but it is never .enable()'d
|
||||||
self.analytics = analytics if analytics is not None else Analytics()
|
self.analytics = analytics if analytics is not None else Analytics()
|
||||||
@@ -299,6 +335,7 @@ class Coder:
|
|||||||
self.abs_root_path_cache = {}
|
self.abs_root_path_cache = {}
|
||||||
|
|
||||||
self.auto_copy_context = auto_copy_context
|
self.auto_copy_context = auto_copy_context
|
||||||
|
self.auto_accept_architect = auto_accept_architect
|
||||||
|
|
||||||
self.ignore_mentions = ignore_mentions
|
self.ignore_mentions = ignore_mentions
|
||||||
if not self.ignore_mentions:
|
if not self.ignore_mentions:
|
||||||
@@ -345,7 +382,6 @@ class Coder:
|
|||||||
self.done_messages = []
|
self.done_messages = []
|
||||||
|
|
||||||
self.io = io
|
self.io = io
|
||||||
self.stream = stream
|
|
||||||
|
|
||||||
self.shell_commands = []
|
self.shell_commands = []
|
||||||
|
|
||||||
@@ -359,6 +395,12 @@ class Coder:
|
|||||||
self.pretty = self.io.pretty
|
self.pretty = self.io.pretty
|
||||||
|
|
||||||
self.main_model = main_model
|
self.main_model = main_model
|
||||||
|
# Set the reasoning tag name based on model settings or default
|
||||||
|
self.reasoning_tag_name = (
|
||||||
|
self.main_model.reasoning_tag if self.main_model.reasoning_tag else REASONING_TAG
|
||||||
|
)
|
||||||
|
|
||||||
|
self.stream = stream and main_model.streaming
|
||||||
|
|
||||||
if cache_prompts and self.main_model.cache_control:
|
if cache_prompts and self.main_model.cache_control:
|
||||||
self.add_cache_headers = True
|
self.add_cache_headers = True
|
||||||
@@ -450,6 +492,7 @@ class Coder:
|
|||||||
|
|
||||||
self.summarizer_thread = None
|
self.summarizer_thread = None
|
||||||
self.summarized_done_messages = []
|
self.summarized_done_messages = []
|
||||||
|
self.summarizing_messages = None
|
||||||
|
|
||||||
if not self.done_messages and restore_chat_history:
|
if not self.done_messages and restore_chat_history:
|
||||||
history_md = self.io.read_text(self.io.chat_history_file)
|
history_md = self.io.read_text(self.io.chat_history_file)
|
||||||
@@ -516,7 +559,7 @@ class Coder:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# only show pretty output if fences are the normal triple-backtick
|
# only show pretty output if fences are the normal triple-backtick
|
||||||
if self.fence != self.fences[0]:
|
if self.fence[0][0] != "`":
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@@ -610,9 +653,19 @@ class Coder:
|
|||||||
def get_ident_filename_matches(self, idents):
|
def get_ident_filename_matches(self, idents):
|
||||||
all_fnames = defaultdict(set)
|
all_fnames = defaultdict(set)
|
||||||
for fname in self.get_all_relative_files():
|
for fname in self.get_all_relative_files():
|
||||||
base = Path(fname).with_suffix("").name.lower()
|
# Skip empty paths or just '.'
|
||||||
if len(base) >= 5:
|
if not fname or fname == ".":
|
||||||
all_fnames[base].add(fname)
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Handle dotfiles properly
|
||||||
|
path = Path(fname)
|
||||||
|
base = path.stem.lower() # Use stem instead of with_suffix("").name
|
||||||
|
if len(base) >= 5:
|
||||||
|
all_fnames[base].add(fname)
|
||||||
|
except ValueError:
|
||||||
|
# Skip paths that can't be processed
|
||||||
|
continue
|
||||||
|
|
||||||
matches = set()
|
matches = set()
|
||||||
for ident in idents:
|
for ident in idents:
|
||||||
@@ -923,8 +976,9 @@ class Coder:
|
|||||||
self.summarizer_thread.start()
|
self.summarizer_thread.start()
|
||||||
|
|
||||||
def summarize_worker(self):
|
def summarize_worker(self):
|
||||||
|
self.summarizing_messages = list(self.done_messages)
|
||||||
try:
|
try:
|
||||||
self.summarized_done_messages = self.summarizer.summarize(self.done_messages)
|
self.summarized_done_messages = self.summarizer.summarize(self.summarizing_messages)
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
self.io.tool_warning(err.args[0])
|
self.io.tool_warning(err.args[0])
|
||||||
|
|
||||||
@@ -938,7 +992,9 @@ class Coder:
|
|||||||
self.summarizer_thread.join()
|
self.summarizer_thread.join()
|
||||||
self.summarizer_thread = None
|
self.summarizer_thread = None
|
||||||
|
|
||||||
self.done_messages = self.summarized_done_messages
|
if self.summarizing_messages == self.done_messages:
|
||||||
|
self.done_messages = self.summarized_done_messages
|
||||||
|
self.summarizing_messages = None
|
||||||
self.summarized_done_messages = []
|
self.summarized_done_messages = []
|
||||||
|
|
||||||
def move_back_cur_messages(self, message):
|
def move_back_cur_messages(self, message):
|
||||||
@@ -974,7 +1030,13 @@ class Coder:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_platform_info(self):
|
def get_platform_info(self):
|
||||||
platform_text = f"- Platform: {platform.platform()}\n"
|
platform_text = ""
|
||||||
|
try:
|
||||||
|
platform_text = f"- Platform: {platform.platform()}\n"
|
||||||
|
except KeyError:
|
||||||
|
# Skip platform info if it can't be retrieved
|
||||||
|
platform_text = "- Platform information unavailable\n"
|
||||||
|
|
||||||
shell_var = "COMSPEC" if os.name == "nt" else "SHELL"
|
shell_var = "COMSPEC" if os.name == "nt" else "SHELL"
|
||||||
shell_val = os.getenv(shell_var)
|
shell_val = os.getenv(shell_var)
|
||||||
platform_text += f"- Shell: {shell_var}={shell_val}\n"
|
platform_text += f"- Shell: {shell_var}={shell_val}\n"
|
||||||
@@ -1015,7 +1077,13 @@ class Coder:
|
|||||||
return platform_text
|
return platform_text
|
||||||
|
|
||||||
def fmt_system_prompt(self, prompt):
|
def fmt_system_prompt(self, prompt):
|
||||||
lazy_prompt = self.gpt_prompts.lazy_prompt if self.main_model.lazy else ""
|
if self.main_model.lazy:
|
||||||
|
lazy_prompt = self.gpt_prompts.lazy_prompt
|
||||||
|
elif self.main_model.overeager:
|
||||||
|
lazy_prompt = self.gpt_prompts.overeager_prompt
|
||||||
|
else:
|
||||||
|
lazy_prompt = ""
|
||||||
|
|
||||||
platform_text = self.get_platform_info()
|
platform_text = self.get_platform_info()
|
||||||
|
|
||||||
if self.suggest_shell_commands:
|
if self.suggest_shell_commands:
|
||||||
@@ -1032,14 +1100,26 @@ class Coder:
|
|||||||
else:
|
else:
|
||||||
language = "the same language they are using"
|
language = "the same language they are using"
|
||||||
|
|
||||||
|
if self.fence[0] == "`" * 4:
|
||||||
|
quad_backtick_reminder = (
|
||||||
|
"\nIMPORTANT: Use *quadruple* backticks ```` as fences, not triple backticks!\n"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
quad_backtick_reminder = ""
|
||||||
|
|
||||||
prompt = prompt.format(
|
prompt = prompt.format(
|
||||||
fence=self.fence,
|
fence=self.fence,
|
||||||
|
quad_backtick_reminder=quad_backtick_reminder,
|
||||||
lazy_prompt=lazy_prompt,
|
lazy_prompt=lazy_prompt,
|
||||||
platform=platform_text,
|
platform=platform_text,
|
||||||
shell_cmd_prompt=shell_cmd_prompt,
|
shell_cmd_prompt=shell_cmd_prompt,
|
||||||
shell_cmd_reminder=shell_cmd_reminder,
|
shell_cmd_reminder=shell_cmd_reminder,
|
||||||
language=language,
|
language=language,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.main_model.system_prompt_prefix:
|
||||||
|
prompt = self.main_model.system_prompt_prefix + prompt
|
||||||
|
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def format_chat_chunks(self):
|
def format_chat_chunks(self):
|
||||||
@@ -1159,8 +1239,11 @@ class Coder:
|
|||||||
return
|
return
|
||||||
if not self.num_cache_warming_pings:
|
if not self.num_cache_warming_pings:
|
||||||
return
|
return
|
||||||
|
if not self.ok_to_warm_cache:
|
||||||
|
return
|
||||||
|
|
||||||
delay = 5 * 60 - 5
|
delay = 5 * 60 - 5
|
||||||
|
delay = float(os.environ.get("AIDER_CACHE_KEEPALIVE_DELAY", delay))
|
||||||
self.next_cache_warm = time.time() + delay
|
self.next_cache_warm = time.time() + delay
|
||||||
self.warming_pings_left = self.num_cache_warming_pings
|
self.warming_pings_left = self.num_cache_warming_pings
|
||||||
self.cache_warming_chunks = chunks
|
self.cache_warming_chunks = chunks
|
||||||
@@ -1169,7 +1252,7 @@ class Coder:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def warm_cache_worker():
|
def warm_cache_worker():
|
||||||
while True:
|
while self.ok_to_warm_cache:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if self.warming_pings_left <= 0:
|
if self.warming_pings_left <= 0:
|
||||||
continue
|
continue
|
||||||
@@ -1207,15 +1290,43 @@ class Coder:
|
|||||||
|
|
||||||
return chunks
|
return chunks
|
||||||
|
|
||||||
|
def check_tokens(self, messages):
|
||||||
|
"""Check if the messages will fit within the model's token limits."""
|
||||||
|
input_tokens = self.main_model.token_count(messages)
|
||||||
|
max_input_tokens = self.main_model.info.get("max_input_tokens") or 0
|
||||||
|
|
||||||
|
if max_input_tokens and input_tokens >= max_input_tokens:
|
||||||
|
self.io.tool_error(
|
||||||
|
f"Your estimated chat context of {input_tokens:,} tokens exceeds the"
|
||||||
|
f" {max_input_tokens:,} token limit for {self.main_model.name}!"
|
||||||
|
)
|
||||||
|
self.io.tool_output("To reduce the chat context:")
|
||||||
|
self.io.tool_output("- Use /drop to remove unneeded files from the chat")
|
||||||
|
self.io.tool_output("- Use /clear to clear the chat history")
|
||||||
|
self.io.tool_output("- Break your code into smaller files")
|
||||||
|
self.io.tool_output(
|
||||||
|
"It's probably safe to try and send the request, most providers won't charge if"
|
||||||
|
" the context limit is exceeded."
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.io.confirm_ask("Try to proceed anyway?"):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def send_message(self, inp):
|
def send_message(self, inp):
|
||||||
self.event("message_send_starting")
|
self.event("message_send_starting")
|
||||||
|
|
||||||
|
# Notify IO that LLM processing is starting
|
||||||
|
self.io.llm_started()
|
||||||
|
|
||||||
self.cur_messages += [
|
self.cur_messages += [
|
||||||
dict(role="user", content=inp),
|
dict(role="user", content=inp),
|
||||||
]
|
]
|
||||||
|
|
||||||
chunks = self.format_messages()
|
chunks = self.format_messages()
|
||||||
messages = chunks.all_messages()
|
messages = chunks.all_messages()
|
||||||
|
if not self.check_tokens(messages):
|
||||||
|
return
|
||||||
self.warm_cache(chunks)
|
self.warm_cache(chunks)
|
||||||
|
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
@@ -1276,7 +1387,7 @@ class Coder:
|
|||||||
exhausted = True
|
exhausted = True
|
||||||
break
|
break
|
||||||
|
|
||||||
self.multi_response_content = self.get_multi_response_content()
|
self.multi_response_content = self.get_multi_response_content_in_progress()
|
||||||
|
|
||||||
if messages[-1]["role"] == "assistant":
|
if messages[-1]["role"] == "assistant":
|
||||||
messages[-1]["content"] = self.multi_response_content
|
messages[-1]["content"] = self.multi_response_content
|
||||||
@@ -1296,14 +1407,30 @@ class Coder:
|
|||||||
self.live_incremental_response(True)
|
self.live_incremental_response(True)
|
||||||
self.mdstream = None
|
self.mdstream = None
|
||||||
|
|
||||||
self.partial_response_content = self.get_multi_response_content(True)
|
self.partial_response_content = self.get_multi_response_content_in_progress(True)
|
||||||
|
self.remove_reasoning_content()
|
||||||
self.multi_response_content = ""
|
self.multi_response_content = ""
|
||||||
|
|
||||||
|
###
|
||||||
|
# print()
|
||||||
|
# print("=" * 20)
|
||||||
|
# dump(self.partial_response_content)
|
||||||
|
|
||||||
self.io.tool_output()
|
self.io.tool_output()
|
||||||
|
|
||||||
self.show_usage_report()
|
self.show_usage_report()
|
||||||
|
|
||||||
|
self.add_assistant_reply_to_cur_messages()
|
||||||
|
|
||||||
if exhausted:
|
if exhausted:
|
||||||
|
if self.cur_messages and self.cur_messages[-1]["role"] == "user":
|
||||||
|
self.cur_messages += [
|
||||||
|
dict(
|
||||||
|
role="assistant",
|
||||||
|
content="FinishReasonLength exception: you sent too many tokens",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
self.show_exhausted_error()
|
self.show_exhausted_error()
|
||||||
self.num_exhausted_context_windows += 1
|
self.num_exhausted_context_windows += 1
|
||||||
return
|
return
|
||||||
@@ -1329,19 +1456,23 @@ class Coder:
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.reply_completed()
|
if self.reply_completed():
|
||||||
|
return
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
interrupted = True
|
interrupted = True
|
||||||
|
|
||||||
if interrupted:
|
if interrupted:
|
||||||
content += "\n^C KeyboardInterrupt"
|
if self.cur_messages and self.cur_messages[-1]["role"] == "user":
|
||||||
self.cur_messages += [dict(role="assistant", content=content)]
|
self.cur_messages[-1]["content"] += "\n^C KeyboardInterrupt"
|
||||||
|
else:
|
||||||
|
self.cur_messages += [dict(role="user", content="^C KeyboardInterrupt")]
|
||||||
|
self.cur_messages += [
|
||||||
|
dict(role="assistant", content="I see that you interrupted my previous reply.")
|
||||||
|
]
|
||||||
return
|
return
|
||||||
|
|
||||||
edited = self.apply_updates()
|
edited = self.apply_updates()
|
||||||
|
|
||||||
self.update_cur_messages()
|
|
||||||
|
|
||||||
if edited:
|
if edited:
|
||||||
self.aider_edited_files.update(edited)
|
self.aider_edited_files.update(edited)
|
||||||
saved_message = self.auto_commit(edited)
|
saved_message = self.auto_commit(edited)
|
||||||
@@ -1362,7 +1493,6 @@ class Coder:
|
|||||||
ok = self.io.confirm_ask("Attempt to fix lint errors?")
|
ok = self.io.confirm_ask("Attempt to fix lint errors?")
|
||||||
if ok:
|
if ok:
|
||||||
self.reflected_message = lint_errors
|
self.reflected_message = lint_errors
|
||||||
self.update_cur_messages()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
shared_output = self.run_shell_commands()
|
shared_output = self.run_shell_commands()
|
||||||
@@ -1379,7 +1509,6 @@ class Coder:
|
|||||||
ok = self.io.confirm_ask("Attempt to fix test errors?")
|
ok = self.io.confirm_ask("Attempt to fix test errors?")
|
||||||
if ok:
|
if ok:
|
||||||
self.reflected_message = test_errors
|
self.reflected_message = test_errors
|
||||||
self.update_cur_messages()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def reply_completed(self):
|
def reply_completed(self):
|
||||||
@@ -1455,7 +1584,11 @@ class Coder:
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def update_cur_messages(self):
|
def __del__(self):
|
||||||
|
"""Cleanup when the Coder object is destroyed."""
|
||||||
|
self.ok_to_warm_cache = False
|
||||||
|
|
||||||
|
def add_assistant_reply_to_cur_messages(self):
|
||||||
if self.partial_response_content:
|
if self.partial_response_content:
|
||||||
self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
|
self.cur_messages += [dict(role="assistant", content=self.partial_response_content)]
|
||||||
if self.partial_response_function_call:
|
if self.partial_response_function_call:
|
||||||
@@ -1467,22 +1600,26 @@ class Coder:
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_file_mentions(self, content):
|
def get_file_mentions(self, content, ignore_current=False):
|
||||||
words = set(word for word in content.split())
|
words = set(word for word in content.split())
|
||||||
|
|
||||||
# drop sentence punctuation from the end
|
# drop sentence punctuation from the end
|
||||||
words = set(word.rstrip(",.!;:") for word in words)
|
words = set(word.rstrip(",.!;:?") for word in words)
|
||||||
|
|
||||||
# strip away all kinds of quotes
|
# strip away all kinds of quotes
|
||||||
quotes = "".join(['"', "'", "`"])
|
quotes = "\"'`*_"
|
||||||
words = set(word.strip(quotes) for word in words)
|
words = set(word.strip(quotes) for word in words)
|
||||||
|
|
||||||
addable_rel_fnames = self.get_addable_relative_files()
|
if ignore_current:
|
||||||
|
addable_rel_fnames = self.get_all_relative_files()
|
||||||
|
existing_basenames = {}
|
||||||
|
else:
|
||||||
|
addable_rel_fnames = self.get_addable_relative_files()
|
||||||
|
|
||||||
# Get basenames of files already in chat or read-only
|
# Get basenames of files already in chat or read-only
|
||||||
existing_basenames = {os.path.basename(f) for f in self.get_inchat_relative_files()} | {
|
existing_basenames = {os.path.basename(f) for f in self.get_inchat_relative_files()} | {
|
||||||
os.path.basename(self.get_rel_fname(f)) for f in self.abs_read_only_fnames
|
os.path.basename(self.get_rel_fname(f)) for f in self.abs_read_only_fnames
|
||||||
}
|
}
|
||||||
|
|
||||||
mentioned_rel_fnames = set()
|
mentioned_rel_fnames = set()
|
||||||
fname_to_rel_fnames = {}
|
fname_to_rel_fnames = {}
|
||||||
@@ -1521,7 +1658,9 @@ class Coder:
|
|||||||
added_fnames = []
|
added_fnames = []
|
||||||
group = ConfirmGroup(new_mentions)
|
group = ConfirmGroup(new_mentions)
|
||||||
for rel_fname in sorted(new_mentions):
|
for rel_fname in sorted(new_mentions):
|
||||||
if self.io.confirm_ask(f"Add {rel_fname} to the chat?", group=group, allow_never=True):
|
if self.io.confirm_ask(
|
||||||
|
"Add file to the chat?", subject=rel_fname, group=group, allow_never=True
|
||||||
|
):
|
||||||
self.add_rel_fname(rel_fname)
|
self.add_rel_fname(rel_fname)
|
||||||
added_fnames.append(rel_fname)
|
added_fnames.append(rel_fname)
|
||||||
else:
|
else:
|
||||||
@@ -1531,6 +1670,9 @@ class Coder:
|
|||||||
return prompts.added_files.format(fnames=", ".join(added_fnames))
|
return prompts.added_files.format(fnames=", ".join(added_fnames))
|
||||||
|
|
||||||
def send(self, messages, model=None, functions=None):
|
def send(self, messages, model=None, functions=None):
|
||||||
|
self.got_reasoning_content = False
|
||||||
|
self.ended_reasoning_content = False
|
||||||
|
|
||||||
if not model:
|
if not model:
|
||||||
model = self.main_model
|
model = self.main_model
|
||||||
|
|
||||||
@@ -1539,20 +1681,13 @@ class Coder:
|
|||||||
|
|
||||||
self.io.log_llm_history("TO LLM", format_messages(messages))
|
self.io.log_llm_history("TO LLM", format_messages(messages))
|
||||||
|
|
||||||
if self.main_model.use_temperature:
|
|
||||||
temp = self.temperature
|
|
||||||
else:
|
|
||||||
temp = None
|
|
||||||
|
|
||||||
completion = None
|
completion = None
|
||||||
try:
|
try:
|
||||||
hash_object, completion = send_completion(
|
hash_object, completion = model.send_completion(
|
||||||
model.name,
|
|
||||||
messages,
|
messages,
|
||||||
functions,
|
functions,
|
||||||
self.stream,
|
self.stream,
|
||||||
temp,
|
self.temperature,
|
||||||
extra_params=model.extra_params,
|
|
||||||
)
|
)
|
||||||
self.chat_completion_call_hashes.append(hash_object.hexdigest())
|
self.chat_completion_call_hashes.append(hash_object.hexdigest())
|
||||||
|
|
||||||
@@ -1605,6 +1740,14 @@ class Coder:
|
|||||||
except AttributeError as func_err:
|
except AttributeError as func_err:
|
||||||
show_func_err = func_err
|
show_func_err = func_err
|
||||||
|
|
||||||
|
try:
|
||||||
|
reasoning_content = completion.choices[0].message.reasoning_content
|
||||||
|
except AttributeError:
|
||||||
|
try:
|
||||||
|
reasoning_content = completion.choices[0].message.reasoning
|
||||||
|
except AttributeError:
|
||||||
|
reasoning_content = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.partial_response_content = completion.choices[0].message.content or ""
|
self.partial_response_content = completion.choices[0].message.content or ""
|
||||||
except AttributeError as content_err:
|
except AttributeError as content_err:
|
||||||
@@ -1623,6 +1766,15 @@ class Coder:
|
|||||||
raise Exception("No data found in LLM response!")
|
raise Exception("No data found in LLM response!")
|
||||||
|
|
||||||
show_resp = self.render_incremental_response(True)
|
show_resp = self.render_incremental_response(True)
|
||||||
|
|
||||||
|
if reasoning_content:
|
||||||
|
formatted_reasoning = format_reasoning_content(
|
||||||
|
reasoning_content, self.reasoning_tag_name
|
||||||
|
)
|
||||||
|
show_resp = formatted_reasoning + show_resp
|
||||||
|
|
||||||
|
show_resp = replace_reasoning_tags(show_resp, self.reasoning_tag_name)
|
||||||
|
|
||||||
self.io.assistant_output(show_resp, pretty=self.show_pretty())
|
self.io.assistant_output(show_resp, pretty=self.show_pretty())
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -1632,6 +1784,8 @@ class Coder:
|
|||||||
raise FinishReasonLength()
|
raise FinishReasonLength()
|
||||||
|
|
||||||
def show_send_output_stream(self, completion):
|
def show_send_output_stream(self, completion):
|
||||||
|
received_content = False
|
||||||
|
|
||||||
for chunk in completion:
|
for chunk in completion:
|
||||||
if len(chunk.choices) == 0:
|
if len(chunk.choices) == 0:
|
||||||
continue
|
continue
|
||||||
@@ -1650,19 +1804,46 @@ class Coder:
|
|||||||
self.partial_response_function_call[k] += v
|
self.partial_response_function_call[k] += v
|
||||||
else:
|
else:
|
||||||
self.partial_response_function_call[k] = v
|
self.partial_response_function_call[k] = v
|
||||||
|
received_content = True
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
text = ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text = chunk.choices[0].delta.content
|
reasoning_content = chunk.choices[0].delta.reasoning_content
|
||||||
if text:
|
|
||||||
self.partial_response_content += text
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
text = None
|
try:
|
||||||
|
reasoning_content = chunk.choices[0].delta.reasoning
|
||||||
|
except AttributeError:
|
||||||
|
reasoning_content = None
|
||||||
|
|
||||||
|
if reasoning_content:
|
||||||
|
if not self.got_reasoning_content:
|
||||||
|
text += f"<{REASONING_TAG}>\n\n"
|
||||||
|
text += reasoning_content
|
||||||
|
self.got_reasoning_content = True
|
||||||
|
received_content = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
content = chunk.choices[0].delta.content
|
||||||
|
if content:
|
||||||
|
if self.got_reasoning_content and not self.ended_reasoning_content:
|
||||||
|
text += f"\n\n</{self.reasoning_tag_name}>\n\n"
|
||||||
|
self.ended_reasoning_content = True
|
||||||
|
|
||||||
|
text += content
|
||||||
|
received_content = True
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.partial_response_content += text
|
||||||
|
|
||||||
if self.show_pretty():
|
if self.show_pretty():
|
||||||
self.live_incremental_response(False)
|
self.live_incremental_response(False)
|
||||||
elif text:
|
elif text:
|
||||||
|
# Apply reasoning tag formatting
|
||||||
|
text = replace_reasoning_tags(text, self.reasoning_tag_name)
|
||||||
try:
|
try:
|
||||||
sys.stdout.write(text)
|
sys.stdout.write(text)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
@@ -1674,12 +1855,25 @@ class Coder:
|
|||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
yield text
|
yield text
|
||||||
|
|
||||||
|
if not received_content:
|
||||||
|
self.io.tool_warning("Empty response received from LLM. Check your provider account?")
|
||||||
|
|
||||||
def live_incremental_response(self, final):
|
def live_incremental_response(self, final):
|
||||||
show_resp = self.render_incremental_response(final)
|
show_resp = self.render_incremental_response(final)
|
||||||
|
# Apply any reasoning tag formatting
|
||||||
|
show_resp = replace_reasoning_tags(show_resp, self.reasoning_tag_name)
|
||||||
self.mdstream.update(show_resp, final=final)
|
self.mdstream.update(show_resp, final=final)
|
||||||
|
|
||||||
def render_incremental_response(self, final):
|
def render_incremental_response(self, final):
|
||||||
return self.get_multi_response_content()
|
return self.get_multi_response_content_in_progress()
|
||||||
|
|
||||||
|
def remove_reasoning_content(self):
|
||||||
|
"""Remove reasoning content from the model's response."""
|
||||||
|
|
||||||
|
self.partial_response_content = remove_reasoning_content(
|
||||||
|
self.partial_response_content,
|
||||||
|
self.reasoning_tag_name,
|
||||||
|
)
|
||||||
|
|
||||||
def calculate_and_show_tokens_and_cost(self, messages, completion=None):
|
def calculate_and_show_tokens_and_cost(self, messages, completion=None):
|
||||||
prompt_tokens = 0
|
prompt_tokens = 0
|
||||||
@@ -1767,11 +1961,6 @@ class Coder:
|
|||||||
f" ${format_cost(self.total_cost)} session."
|
f" ${format_cost(self.total_cost)} session."
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.add_cache_headers and self.stream:
|
|
||||||
warning = " Use --no-stream for accurate caching costs."
|
|
||||||
self.usage_report = tokens_report + "\n" + cost_report + warning
|
|
||||||
return
|
|
||||||
|
|
||||||
if cache_hit_tokens and cache_write_tokens:
|
if cache_hit_tokens and cache_write_tokens:
|
||||||
sep = "\n"
|
sep = "\n"
|
||||||
else:
|
else:
|
||||||
@@ -1802,12 +1991,13 @@ class Coder:
|
|||||||
self.message_tokens_sent = 0
|
self.message_tokens_sent = 0
|
||||||
self.message_tokens_received = 0
|
self.message_tokens_received = 0
|
||||||
|
|
||||||
def get_multi_response_content(self, final=False):
|
def get_multi_response_content_in_progress(self, final=False):
|
||||||
cur = self.multi_response_content or ""
|
cur = self.multi_response_content or ""
|
||||||
new = self.partial_response_content or ""
|
new = self.partial_response_content or ""
|
||||||
|
|
||||||
if new.rstrip() != new and not final:
|
if new.rstrip() != new and not final:
|
||||||
new = new.rstrip()
|
new = new.rstrip()
|
||||||
|
|
||||||
return cur + new
|
return cur + new
|
||||||
|
|
||||||
def get_rel_fname(self, fname):
|
def get_rel_fname(self, fname):
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ You NEVER leave comments describing code without implementing it!
|
|||||||
You always COMPLETELY IMPLEMENT the needed code!
|
You always COMPLETELY IMPLEMENT the needed code!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
overeager_prompt = """Pay careful attention to the scope of the user's request.
|
||||||
|
Do what they ask, but no more."""
|
||||||
|
|
||||||
example_messages = []
|
example_messages = []
|
||||||
|
|
||||||
files_content_prefix = """I have *added these files to the chat* so you can go ahead and edit them.
|
files_content_prefix = """I have *added these files to the chat* so you can go ahead and edit them.
|
||||||
|
|||||||
53
aider/coders/context_coder.py
Normal file
53
aider/coders/context_coder.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
from .base_coder import Coder
|
||||||
|
from .context_prompts import ContextPrompts
|
||||||
|
|
||||||
|
|
||||||
|
class ContextCoder(Coder):
|
||||||
|
"""Identify which files need to be edited for a given request."""
|
||||||
|
|
||||||
|
edit_format = "context"
|
||||||
|
gpt_prompts = ContextPrompts()
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if not self.repo_map:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.repo_map.refresh = "always"
|
||||||
|
self.repo_map.max_map_tokens *= self.repo_map.map_mul_no_files
|
||||||
|
self.repo_map.map_mul_no_files = 1.0
|
||||||
|
|
||||||
|
def reply_completed(self):
|
||||||
|
content = self.partial_response_content
|
||||||
|
if not content or not content.strip():
|
||||||
|
return True
|
||||||
|
|
||||||
|
# dump(repr(content))
|
||||||
|
current_rel_fnames = set(self.get_inchat_relative_files())
|
||||||
|
mentioned_rel_fnames = set(self.get_file_mentions(content, ignore_current=True))
|
||||||
|
|
||||||
|
# dump(current_rel_fnames)
|
||||||
|
# dump(mentioned_rel_fnames)
|
||||||
|
# dump(current_rel_fnames == mentioned_rel_fnames)
|
||||||
|
|
||||||
|
if mentioned_rel_fnames == current_rel_fnames:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.num_reflections >= self.max_reflections - 1:
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.abs_fnames = set()
|
||||||
|
for fname in mentioned_rel_fnames:
|
||||||
|
self.add_rel_fname(fname)
|
||||||
|
# dump(self.get_inchat_relative_files())
|
||||||
|
|
||||||
|
self.reflected_message = self.gpt_prompts.try_again
|
||||||
|
|
||||||
|
# mentioned_idents = self.get_ident_mentions(cur_msg_text)
|
||||||
|
# if mentioned_idents:
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_for_file_mentions(self, content):
|
||||||
|
pass
|
||||||
75
aider/coders/context_prompts.py
Normal file
75
aider/coders/context_prompts.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# flake8: noqa: E501
|
||||||
|
|
||||||
|
from .base_prompts import CoderPrompts
|
||||||
|
|
||||||
|
|
||||||
|
class ContextPrompts(CoderPrompts):
|
||||||
|
main_system = """Act as an expert code analyst.
|
||||||
|
Understand the user's question or request, solely to determine ALL the existing sources files which will need to be modified.
|
||||||
|
Return the *complete* list of files which will need to be modified based on the user's request.
|
||||||
|
Explain why each file is needed, including names of key classes/functions/methods/variables.
|
||||||
|
Be sure to include or omit the names of files already added to the chat, based on whether they are actually needed or not.
|
||||||
|
|
||||||
|
The user will use every file you mention, regardless of your commentary.
|
||||||
|
So *ONLY* mention the names of relevant files.
|
||||||
|
If a file is not relevant DO NOT mention it.
|
||||||
|
|
||||||
|
Only return files that will need to be modified, not files that contain useful/relevant functions.
|
||||||
|
|
||||||
|
You are only to discuss EXISTING files and symbols.
|
||||||
|
Only return existing files, don't suggest the names of new files or functions that we will need to create.
|
||||||
|
|
||||||
|
Always reply to the user in {language}.
|
||||||
|
|
||||||
|
Be concise in your replies.
|
||||||
|
Return:
|
||||||
|
1. A bulleted list of files the will need to be edited, and symbols that are highly relevant to the user's request.
|
||||||
|
2. A list of classes/functions/methods/variables that are located OUTSIDE those files which will need to be understood. Just the symbols names, *NOT* file names.
|
||||||
|
|
||||||
|
# Your response *MUST* use this format:
|
||||||
|
|
||||||
|
## ALL files we need to modify, with their relevant symbols:
|
||||||
|
|
||||||
|
- alarms/buzz.py
|
||||||
|
- `Buzzer` class which can make the needed sound
|
||||||
|
- `Buzzer.buzz_buzz()` method triggers the sound
|
||||||
|
- alarms/time.py
|
||||||
|
- `Time.set_alarm(hour, minute)` to set the alarm
|
||||||
|
|
||||||
|
## Relevant symbols from OTHER files:
|
||||||
|
|
||||||
|
- AlarmManager class for setup/teardown of alarms
|
||||||
|
- SoundFactory will be used to create a Buzzer
|
||||||
|
"""
|
||||||
|
|
||||||
|
example_messages = []
|
||||||
|
|
||||||
|
files_content_prefix = """These files have been *added these files to the chat* so we can see all of their contents.
|
||||||
|
*Trust this message as the true contents of the files!*
|
||||||
|
Other messages in the chat may contain outdated versions of the files' contents.
|
||||||
|
""" # noqa: E501
|
||||||
|
|
||||||
|
files_content_assistant_reply = (
|
||||||
|
"Ok, I will use that as the true, current contents of the files."
|
||||||
|
)
|
||||||
|
|
||||||
|
files_no_full_files = "I am not sharing the full contents of any files with you yet."
|
||||||
|
|
||||||
|
files_no_full_files_with_repo_map = ""
|
||||||
|
files_no_full_files_with_repo_map_reply = ""
|
||||||
|
|
||||||
|
repo_content_prefix = """I am working with you on code in a git repository.
|
||||||
|
Here are summaries of some files present in my git repo.
|
||||||
|
If you need to see the full contents of any files to answer my questions, ask me to *add them to the chat*.
|
||||||
|
"""
|
||||||
|
|
||||||
|
system_reminder = """
|
||||||
|
NEVER RETURN CODE!
|
||||||
|
"""
|
||||||
|
|
||||||
|
try_again = """I have updated the set of files added to the chat.
|
||||||
|
Review them to decide if this is the correct set of files or if we need to add more or remove files.
|
||||||
|
|
||||||
|
If this is the right set, just return the current list of files.
|
||||||
|
Or return a smaller or larger set of files which need to be edited, with symbols that are highly relevant to the user's request.
|
||||||
|
"""
|
||||||
@@ -401,6 +401,9 @@ missing_filename_err = (
|
|||||||
" {fence[0]}"
|
" {fence[0]}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Always be willing to treat triple-backticks as a fence when searching for filenames
|
||||||
|
triple_backticks = "`" * 3
|
||||||
|
|
||||||
|
|
||||||
def strip_filename(filename, fence):
|
def strip_filename(filename, fence):
|
||||||
filename = filename.strip()
|
filename = filename.strip()
|
||||||
@@ -409,7 +412,7 @@ def strip_filename(filename, fence):
|
|||||||
return
|
return
|
||||||
|
|
||||||
start_fence = fence[0]
|
start_fence = fence[0]
|
||||||
if filename.startswith(start_fence):
|
if filename.startswith(start_fence) or filename.startswith(triple_backticks):
|
||||||
return
|
return
|
||||||
|
|
||||||
filename = filename.rstrip(":")
|
filename = filename.rstrip(":")
|
||||||
@@ -546,7 +549,7 @@ def find_filename(lines, fence, valid_fnames):
|
|||||||
filenames.append(filename)
|
filenames.append(filename)
|
||||||
|
|
||||||
# Only continue as long as we keep seeing fences
|
# Only continue as long as we keep seeing fences
|
||||||
if not line.startswith(fence[0]):
|
if not line.startswith(fence[0]) and not line.startswith(triple_backticks):
|
||||||
break
|
break
|
||||||
|
|
||||||
if not filenames:
|
if not filenames:
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ Every *SEARCH/REPLACE block* must use this format:
|
|||||||
8. The closing fence: {fence[1]}
|
8. The closing fence: {fence[1]}
|
||||||
|
|
||||||
Use the *FULL* file path, as shown to you by the user.
|
Use the *FULL* file path, as shown to you by the user.
|
||||||
|
{quad_backtick_reminder}
|
||||||
Every *SEARCH* section must *EXACTLY MATCH* the existing file content, character for character, including all comments, docstrings, etc.
|
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.
|
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.
|
||||||
|
|
||||||
@@ -183,6 +183,9 @@ If you want to put code in a new file, use a *SEARCH/REPLACE block* with:
|
|||||||
|
|
||||||
To rename files which have been added to the chat, use shell commands at the end of your response.
|
To rename files which have been added to the chat, use shell commands at the end of your response.
|
||||||
|
|
||||||
|
If the user just says something like "ok" or "go ahead" or "do that" they probably want you to make SEARCH/REPLACE blocks for the code changes you just proposed.
|
||||||
|
The user will say when they've applied your edits. If they haven't explicitly confirmed the edits have been applied, they probably want proper SEARCH/REPLACE blocks.
|
||||||
|
|
||||||
{lazy_prompt}
|
{lazy_prompt}
|
||||||
ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!
|
ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!
|
||||||
{shell_cmd_reminder}
|
{shell_cmd_reminder}
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import git
|
try:
|
||||||
|
import git
|
||||||
|
except ImportError:
|
||||||
|
git = None
|
||||||
|
|
||||||
from diff_match_patch import diff_match_patch
|
from diff_match_patch import diff_match_patch
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class SingleWholeFileFunctionCoder(Coder):
|
|||||||
self.gpt_prompts = SingleWholeFileFunctionPrompts()
|
self.gpt_prompts = SingleWholeFileFunctionPrompts()
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def update_cur_messages(self, edited):
|
def add_assistant_reply_to_cur_messages(self, edited):
|
||||||
if edited:
|
if edited:
|
||||||
self.cur_messages += [
|
self.cur_messages += [
|
||||||
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
|
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ class WholeFileCoder(Coder):
|
|||||||
try:
|
try:
|
||||||
return self.get_edits(mode="diff")
|
return self.get_edits(mode="diff")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return self.get_multi_response_content()
|
return self.get_multi_response_content_in_progress()
|
||||||
|
|
||||||
def get_edits(self, mode="update"):
|
def get_edits(self, mode="update"):
|
||||||
content = self.get_multi_response_content()
|
content = self.get_multi_response_content_in_progress()
|
||||||
|
|
||||||
chat_files = self.get_inchat_relative_files()
|
chat_files = self.get_inchat_relative_files()
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class WholeFileFunctionCoder(Coder):
|
|||||||
self.gpt_prompts = WholeFileFunctionPrompts()
|
self.gpt_prompts = WholeFileFunctionPrompts()
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def update_cur_messages(self, edited):
|
def add_assistant_reply_to_cur_messages(self, edited):
|
||||||
if edited:
|
if edited:
|
||||||
self.cur_messages += [
|
self.cur_messages += [
|
||||||
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
|
dict(role="assistant", content=self.gpt_prompts.redacted_edit_message)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from aider import models, prompts, voice
|
|||||||
from aider.editor import pipe_editor
|
from aider.editor import pipe_editor
|
||||||
from aider.format_settings import format_settings
|
from aider.format_settings import format_settings
|
||||||
from aider.help import Help, install_help_extra
|
from aider.help import Help, install_help_extra
|
||||||
|
from aider.io import CommandCompletionException
|
||||||
from aider.llm import litellm
|
from aider.llm import litellm
|
||||||
from aider.repo import ANY_GIT_ERROR
|
from aider.repo import ANY_GIT_ERROR
|
||||||
from aider.run_cmd import run_cmd
|
from aider.run_cmd import run_cmd
|
||||||
@@ -27,8 +28,9 @@ from .dump import dump # noqa: F401
|
|||||||
|
|
||||||
|
|
||||||
class SwitchCoder(Exception):
|
class SwitchCoder(Exception):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, placeholder=None, **kwargs):
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
self.placeholder = placeholder
|
||||||
|
|
||||||
|
|
||||||
class Commands:
|
class Commands:
|
||||||
@@ -52,11 +54,14 @@ class Commands:
|
|||||||
io,
|
io,
|
||||||
coder,
|
coder,
|
||||||
voice_language=None,
|
voice_language=None,
|
||||||
|
voice_input_device=None,
|
||||||
|
voice_format=None,
|
||||||
verify_ssl=True,
|
verify_ssl=True,
|
||||||
args=None,
|
args=None,
|
||||||
parser=None,
|
parser=None,
|
||||||
verbose=False,
|
verbose=False,
|
||||||
editor=None,
|
editor=None,
|
||||||
|
original_read_only_fnames=None,
|
||||||
):
|
):
|
||||||
self.io = io
|
self.io = io
|
||||||
self.coder = coder
|
self.coder = coder
|
||||||
@@ -69,15 +74,58 @@ class Commands:
|
|||||||
voice_language = None
|
voice_language = None
|
||||||
|
|
||||||
self.voice_language = voice_language
|
self.voice_language = voice_language
|
||||||
|
self.voice_format = voice_format
|
||||||
|
self.voice_input_device = voice_input_device
|
||||||
|
|
||||||
self.help = None
|
self.help = None
|
||||||
self.editor = editor
|
self.editor = editor
|
||||||
|
|
||||||
|
# Store the original read-only filenames provided via args.read
|
||||||
|
self.original_read_only_fnames = set(original_read_only_fnames or [])
|
||||||
|
|
||||||
def cmd_model(self, args):
|
def cmd_model(self, args):
|
||||||
"Switch to a new LLM"
|
"Switch the Main Model to a new LLM"
|
||||||
|
|
||||||
model_name = args.strip()
|
model_name = args.strip()
|
||||||
model = models.Model(model_name)
|
model = models.Model(
|
||||||
|
model_name,
|
||||||
|
editor_model=self.coder.main_model.editor_model.name,
|
||||||
|
weak_model=self.coder.main_model.weak_model.name,
|
||||||
|
)
|
||||||
|
models.sanity_check_models(self.io, model)
|
||||||
|
|
||||||
|
# Check if the current edit format is the default for the old model
|
||||||
|
old_model_edit_format = self.coder.main_model.edit_format
|
||||||
|
current_edit_format = self.coder.edit_format
|
||||||
|
|
||||||
|
new_edit_format = current_edit_format
|
||||||
|
if current_edit_format == old_model_edit_format:
|
||||||
|
# If the user was using the old model's default, switch to the new model's default
|
||||||
|
new_edit_format = model.edit_format
|
||||||
|
|
||||||
|
raise SwitchCoder(main_model=model, edit_format=new_edit_format)
|
||||||
|
|
||||||
|
def cmd_editor_model(self, args):
|
||||||
|
"Switch the Editor Model to a new LLM"
|
||||||
|
|
||||||
|
model_name = args.strip()
|
||||||
|
model = models.Model(
|
||||||
|
self.coder.main_model.name,
|
||||||
|
editor_model=model_name,
|
||||||
|
weak_model=self.coder.main_model.weak_model.name,
|
||||||
|
)
|
||||||
|
models.sanity_check_models(self.io, model)
|
||||||
|
raise SwitchCoder(main_model=model)
|
||||||
|
|
||||||
|
def cmd_weak_model(self, args):
|
||||||
|
"Switch the Weak Model to a new LLM"
|
||||||
|
|
||||||
|
model_name = args.strip()
|
||||||
|
model = models.Model(
|
||||||
|
self.coder.main_model.name,
|
||||||
|
editor_model=self.coder.main_model.editor_model.name,
|
||||||
|
weak_model=model_name,
|
||||||
|
)
|
||||||
models.sanity_check_models(self.io, model)
|
models.sanity_check_models(self.io, model)
|
||||||
raise SwitchCoder(main_model=model)
|
raise SwitchCoder(main_model=model)
|
||||||
|
|
||||||
@@ -110,6 +158,10 @@ class Commands:
|
|||||||
" them."
|
" them."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"context",
|
||||||
|
"Automatically identify which files will need to be edited.",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -351,7 +403,21 @@ class Commands:
|
|||||||
|
|
||||||
def _drop_all_files(self):
|
def _drop_all_files(self):
|
||||||
self.coder.abs_fnames = set()
|
self.coder.abs_fnames = set()
|
||||||
self.coder.abs_read_only_fnames = set()
|
|
||||||
|
# When dropping all files, keep those that were originally provided via args.read
|
||||||
|
if self.original_read_only_fnames:
|
||||||
|
# Keep only the original read-only files
|
||||||
|
to_keep = set()
|
||||||
|
for abs_fname in self.coder.abs_read_only_fnames:
|
||||||
|
rel_fname = self.coder.get_rel_fname(abs_fname)
|
||||||
|
if (
|
||||||
|
abs_fname in self.original_read_only_fnames
|
||||||
|
or rel_fname in self.original_read_only_fnames
|
||||||
|
):
|
||||||
|
to_keep.add(abs_fname)
|
||||||
|
self.coder.abs_read_only_fnames = to_keep
|
||||||
|
else:
|
||||||
|
self.coder.abs_read_only_fnames = set()
|
||||||
|
|
||||||
def _clear_chat_history(self):
|
def _clear_chat_history(self):
|
||||||
self.coder.done_messages = []
|
self.coder.done_messages = []
|
||||||
@@ -400,6 +466,7 @@ class Commands:
|
|||||||
|
|
||||||
fence = "`" * 3
|
fence = "`" * 3
|
||||||
|
|
||||||
|
file_res = []
|
||||||
# files
|
# files
|
||||||
for fname in self.coder.abs_fnames:
|
for fname in self.coder.abs_fnames:
|
||||||
relative_fname = self.coder.get_rel_fname(fname)
|
relative_fname = self.coder.get_rel_fname(fname)
|
||||||
@@ -410,7 +477,7 @@ class Commands:
|
|||||||
# approximate
|
# approximate
|
||||||
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
|
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
|
||||||
tokens = self.coder.main_model.token_count(content)
|
tokens = self.coder.main_model.token_count(content)
|
||||||
res.append((tokens, f"{relative_fname}", "/drop to remove"))
|
file_res.append((tokens, f"{relative_fname}", "/drop to remove"))
|
||||||
|
|
||||||
# read-only files
|
# read-only files
|
||||||
for fname in self.coder.abs_read_only_fnames:
|
for fname in self.coder.abs_read_only_fnames:
|
||||||
@@ -420,7 +487,10 @@ class Commands:
|
|||||||
# approximate
|
# approximate
|
||||||
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
|
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
|
||||||
tokens = self.coder.main_model.token_count(content)
|
tokens = self.coder.main_model.token_count(content)
|
||||||
res.append((tokens, f"{relative_fname} (read-only)", "/drop to remove"))
|
file_res.append((tokens, f"{relative_fname} (read-only)", "/drop to remove"))
|
||||||
|
|
||||||
|
file_res.sort()
|
||||||
|
res.extend(file_res)
|
||||||
|
|
||||||
self.io.tool_output(
|
self.io.tool_output(
|
||||||
f"Approximate context window usage for {self.coder.main_model.name}, in tokens:"
|
f"Approximate context window usage for {self.coder.main_model.name}, in tokens:"
|
||||||
@@ -752,6 +822,7 @@ class Commands:
|
|||||||
|
|
||||||
if self.io.confirm_ask(f"No files matched '{word}'. Do you want to create {fname}?"):
|
if self.io.confirm_ask(f"No files matched '{word}'. Do you want to create {fname}?"):
|
||||||
try:
|
try:
|
||||||
|
fname.parent.mkdir(parents=True, exist_ok=True)
|
||||||
fname.touch()
|
fname.touch()
|
||||||
all_matched_files.add(str(fname))
|
all_matched_files.add(str(fname))
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
@@ -813,7 +884,12 @@ class Commands:
|
|||||||
"Remove files from the chat session to free up context space"
|
"Remove files from the chat session to free up context space"
|
||||||
|
|
||||||
if not args.strip():
|
if not args.strip():
|
||||||
self.io.tool_output("Dropping all files from the chat session.")
|
if self.original_read_only_fnames:
|
||||||
|
self.io.tool_output(
|
||||||
|
"Dropping all files from the chat session except originally read-only files."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.io.tool_output("Dropping all files from the chat session.")
|
||||||
self._drop_all_files()
|
self._drop_all_files()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -914,10 +990,14 @@ class Commands:
|
|||||||
if combined_output is None:
|
if combined_output is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Calculate token count of output
|
||||||
|
token_count = self.coder.main_model.token_count(combined_output)
|
||||||
|
k_tokens = token_count / 1000
|
||||||
|
|
||||||
if add_on_nonzero_exit:
|
if add_on_nonzero_exit:
|
||||||
add = exit_status != 0
|
add = exit_status != 0
|
||||||
else:
|
else:
|
||||||
add = self.io.confirm_ask("Add command output to the chat?")
|
add = self.io.confirm_ask(f"Add {k_tokens:.1f}k tokens of command output to the chat?")
|
||||||
|
|
||||||
if add:
|
if add:
|
||||||
num_lines = len(combined_output.strip().splitlines())
|
num_lines = len(combined_output.strip().splitlines())
|
||||||
@@ -934,8 +1014,14 @@ class Commands:
|
|||||||
dict(role="assistant", content="Ok."),
|
dict(role="assistant", content="Ok."),
|
||||||
]
|
]
|
||||||
|
|
||||||
if add and exit_status != 0:
|
if add_on_nonzero_exit and exit_status != 0:
|
||||||
self.io.placeholder = "Fix that"
|
# Return the formatted output message for test failures
|
||||||
|
return msg
|
||||||
|
elif add and exit_status != 0:
|
||||||
|
self.io.placeholder = "What's wrong? Fix"
|
||||||
|
|
||||||
|
# Return None if output wasn't added or command succeeded
|
||||||
|
return None
|
||||||
|
|
||||||
def cmd_exit(self, args):
|
def cmd_exit(self, args):
|
||||||
"Exit the application"
|
"Exit the application"
|
||||||
@@ -1009,7 +1095,7 @@ class Commands:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.coder.event("interactive help")
|
self.coder.event("interactive help")
|
||||||
from aider.coders import Coder
|
from aider.coders.base_coder import Coder
|
||||||
|
|
||||||
if not self.help:
|
if not self.help:
|
||||||
res = install_help_extra(self.io)
|
res = install_help_extra(self.io)
|
||||||
@@ -1052,24 +1138,40 @@ class Commands:
|
|||||||
show_announcements=False,
|
show_announcements=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def completions_ask(self):
|
||||||
|
raise CommandCompletionException()
|
||||||
|
|
||||||
|
def completions_code(self):
|
||||||
|
raise CommandCompletionException()
|
||||||
|
|
||||||
|
def completions_architect(self):
|
||||||
|
raise CommandCompletionException()
|
||||||
|
|
||||||
|
def completions_context(self):
|
||||||
|
raise CommandCompletionException()
|
||||||
|
|
||||||
def cmd_ask(self, args):
|
def cmd_ask(self, args):
|
||||||
"Ask questions about the code base without editing any files"
|
"""Ask questions about the code base without editing any files. If no prompt provided, switches to ask mode.""" # noqa
|
||||||
return self._generic_chat_command(args, "ask")
|
return self._generic_chat_command(args, "ask")
|
||||||
|
|
||||||
def cmd_code(self, args):
|
def cmd_code(self, args):
|
||||||
"Ask for changes to your code"
|
"""Ask for changes to your code. If no prompt provided, switches to code mode.""" # noqa
|
||||||
return self._generic_chat_command(args, self.coder.main_model.edit_format)
|
return self._generic_chat_command(args, self.coder.main_model.edit_format)
|
||||||
|
|
||||||
def cmd_architect(self, args):
|
def cmd_architect(self, args):
|
||||||
"Enter architect mode to discuss high-level design and architecture"
|
"""Enter architect/editor mode using 2 different models. If no prompt provided, switches to architect/editor mode.""" # noqa
|
||||||
return self._generic_chat_command(args, "architect")
|
return self._generic_chat_command(args, "architect")
|
||||||
|
|
||||||
def _generic_chat_command(self, args, edit_format):
|
def cmd_context(self, args):
|
||||||
if not args.strip():
|
"""Enter context mode to see surrounding code context. If no prompt provided, switches to context mode.""" # noqa
|
||||||
self.io.tool_error(f"Please provide a question or topic for the {edit_format} chat.")
|
return self._generic_chat_command(args, "context", placeholder=args.strip() or None)
|
||||||
return
|
|
||||||
|
|
||||||
from aider.coders import Coder
|
def _generic_chat_command(self, args, edit_format, placeholder=None):
|
||||||
|
if not args.strip():
|
||||||
|
# Switch to the corresponding chat mode if no args provided
|
||||||
|
return self.cmd_chat_mode(edit_format)
|
||||||
|
|
||||||
|
from aider.coders.base_coder import Coder
|
||||||
|
|
||||||
coder = Coder.create(
|
coder = Coder.create(
|
||||||
io=self.io,
|
io=self.io,
|
||||||
@@ -1081,11 +1183,13 @@ class Commands:
|
|||||||
user_msg = args
|
user_msg = args
|
||||||
coder.run(user_msg)
|
coder.run(user_msg)
|
||||||
|
|
||||||
|
# Use the provided placeholder if any
|
||||||
raise SwitchCoder(
|
raise SwitchCoder(
|
||||||
edit_format=self.coder.edit_format,
|
edit_format=self.coder.edit_format,
|
||||||
summarize_from_coder=False,
|
summarize_from_coder=False,
|
||||||
from_coder=coder,
|
from_coder=coder,
|
||||||
show_announcements=False,
|
show_announcements=False,
|
||||||
|
placeholder=placeholder,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_help_md(self):
|
def get_help_md(self):
|
||||||
@@ -1117,7 +1221,7 @@ class Commands:
|
|||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.voice = voice.Voice(
|
self.voice = voice.Voice(
|
||||||
audio_format=self.args.voice_format, device_name=self.args.voice_input_device
|
audio_format=self.voice_format or "wav", device_name=self.voice_input_device
|
||||||
)
|
)
|
||||||
except voice.SoundDeviceError:
|
except voice.SoundDeviceError:
|
||||||
self.io.tool_error(
|
self.io.tool_error(
|
||||||
@@ -1125,36 +1229,14 @@ class Commands:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
history_iter = self.io.get_input_history()
|
|
||||||
|
|
||||||
history = []
|
|
||||||
size = 0
|
|
||||||
for line in history_iter:
|
|
||||||
if line.startswith("/"):
|
|
||||||
continue
|
|
||||||
if line in history:
|
|
||||||
continue
|
|
||||||
if size + len(line) > 1024:
|
|
||||||
break
|
|
||||||
size += len(line)
|
|
||||||
history.append(line)
|
|
||||||
|
|
||||||
history.reverse()
|
|
||||||
history = "\n".join(history)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text = self.voice.record_and_transcribe(history, language=self.voice_language)
|
text = self.voice.record_and_transcribe(None, language=self.voice_language)
|
||||||
except litellm.OpenAIError as err:
|
except litellm.OpenAIError as err:
|
||||||
self.io.tool_error(f"Unable to use OpenAI whisper model: {err}")
|
self.io.tool_error(f"Unable to use OpenAI whisper model: {err}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if text:
|
if text:
|
||||||
self.io.add_to_input_history(text)
|
self.io.placeholder = text
|
||||||
self.io.print()
|
|
||||||
self.io.user_input(text, log_only=False)
|
|
||||||
self.io.print()
|
|
||||||
|
|
||||||
return text
|
|
||||||
|
|
||||||
def cmd_paste(self, args):
|
def cmd_paste(self, args):
|
||||||
"""Paste image/text from the clipboard into the chat.\
|
"""Paste image/text from the clipboard into the chat.\
|
||||||
@@ -1331,7 +1413,12 @@ class Commands:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
self.io.tool_output(f"\nExecuting: {cmd}")
|
self.io.tool_output(f"\nExecuting: {cmd}")
|
||||||
self.run(cmd)
|
try:
|
||||||
|
self.run(cmd)
|
||||||
|
except SwitchCoder:
|
||||||
|
self.io.tool_error(
|
||||||
|
f"Command '{cmd}' is only supported in interactive mode, skipping."
|
||||||
|
)
|
||||||
|
|
||||||
def completions_raw_save(self, document, complete_event):
|
def completions_raw_save(self, document, complete_event):
|
||||||
return self.completions_raw_read_only(document, complete_event)
|
return self.completions_raw_read_only(document, complete_event)
|
||||||
@@ -1363,6 +1450,10 @@ class Commands:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.io.tool_error(f"Error saving commands to file: {e}")
|
self.io.tool_error(f"Error saving commands to file: {e}")
|
||||||
|
|
||||||
|
def cmd_multiline_mode(self, args):
|
||||||
|
"Toggle multiline mode (swaps behavior of Enter and Meta+Enter)"
|
||||||
|
self.io.toggle_multiline_mode()
|
||||||
|
|
||||||
def cmd_copy(self, args):
|
def cmd_copy(self, args):
|
||||||
"Copy the last assistant message to the clipboard"
|
"Copy the last assistant message to the clipboard"
|
||||||
all_messages = self.coder.done_messages + self.coder.cur_messages
|
all_messages = self.coder.done_messages + self.coder.cur_messages
|
||||||
@@ -1411,6 +1502,62 @@ class Commands:
|
|||||||
if user_input.strip():
|
if user_input.strip():
|
||||||
self.io.set_placeholder(user_input.rstrip())
|
self.io.set_placeholder(user_input.rstrip())
|
||||||
|
|
||||||
|
def cmd_edit(self, args=""):
|
||||||
|
"Alias for /editor: Open an editor to write a prompt"
|
||||||
|
return self.cmd_editor(args)
|
||||||
|
|
||||||
|
def cmd_think_tokens(self, args):
|
||||||
|
"Set the thinking token budget (supports formats like 8096, 8k, 10.5k, 0.5M)"
|
||||||
|
model = self.coder.main_model
|
||||||
|
|
||||||
|
if not args.strip():
|
||||||
|
# Display current value if no args are provided
|
||||||
|
formatted_budget = model.get_thinking_tokens()
|
||||||
|
if formatted_budget is None:
|
||||||
|
self.io.tool_output("Thinking tokens are not currently set.")
|
||||||
|
else:
|
||||||
|
budget = model.get_raw_thinking_tokens()
|
||||||
|
self.io.tool_output(
|
||||||
|
f"Current thinking token budget: {budget:,} tokens ({formatted_budget})."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
value = args.strip()
|
||||||
|
model.set_thinking_tokens(value)
|
||||||
|
|
||||||
|
formatted_budget = model.get_thinking_tokens()
|
||||||
|
budget = model.get_raw_thinking_tokens()
|
||||||
|
|
||||||
|
self.io.tool_output(f"Set thinking token budget to {budget:,} tokens ({formatted_budget}).")
|
||||||
|
self.io.tool_output()
|
||||||
|
|
||||||
|
# Output announcements
|
||||||
|
announcements = "\n".join(self.coder.get_announcements())
|
||||||
|
self.io.tool_output(announcements)
|
||||||
|
|
||||||
|
def cmd_reasoning_effort(self, args):
|
||||||
|
"Set the reasoning effort level (values: number or low/medium/high depending on model)"
|
||||||
|
model = self.coder.main_model
|
||||||
|
|
||||||
|
if not args.strip():
|
||||||
|
# Display current value if no args are provided
|
||||||
|
reasoning_value = model.get_reasoning_effort()
|
||||||
|
if reasoning_value is None:
|
||||||
|
self.io.tool_output("Reasoning effort is not currently set.")
|
||||||
|
else:
|
||||||
|
self.io.tool_output(f"Current reasoning effort: {reasoning_value}")
|
||||||
|
return
|
||||||
|
|
||||||
|
value = args.strip()
|
||||||
|
model.set_reasoning_effort(value)
|
||||||
|
reasoning_value = model.get_reasoning_effort()
|
||||||
|
self.io.tool_output(f"Set reasoning effort to {reasoning_value}")
|
||||||
|
self.io.tool_output()
|
||||||
|
|
||||||
|
# Output announcements
|
||||||
|
announcements = "\n".join(self.coder.get_announcements())
|
||||||
|
self.io.tool_output(announcements)
|
||||||
|
|
||||||
def cmd_copy_context(self, args=None):
|
def cmd_copy_context(self, args=None):
|
||||||
"""Copy the current chat context as markdown, suitable to paste into a web UI"""
|
"""Copy the current chat context as markdown, suitable to paste into a web UI"""
|
||||||
|
|
||||||
@@ -1435,16 +1582,25 @@ class Commands:
|
|||||||
else:
|
else:
|
||||||
markdown += content + "\n\n"
|
markdown += content + "\n\n"
|
||||||
|
|
||||||
markdown += """
|
args = args or ""
|
||||||
|
markdown += f"""
|
||||||
Just tell me how to edit the files to make the changes.
|
Just tell me how to edit the files to make the changes.
|
||||||
Don't give me back entire files.
|
Don't give me back entire files.
|
||||||
Just show me the edits I need to make.
|
Just show me the edits I need to make.
|
||||||
|
|
||||||
|
{args}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pyperclip.copy(markdown)
|
try:
|
||||||
self.io.tool_output("Copied code context to clipboard.")
|
pyperclip.copy(markdown)
|
||||||
|
self.io.tool_output("Copied code context to clipboard.")
|
||||||
|
except pyperclip.PyperclipException as e:
|
||||||
|
self.io.tool_error(f"Failed to copy to clipboard: {str(e)}")
|
||||||
|
self.io.tool_output(
|
||||||
|
"You may need to install xclip or xsel on Linux, or pbcopy on macOS."
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.io.tool_error(f"An unexpected error occurred while copying to clipboard: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def expand_subdir(file_path):
|
def expand_subdir(file_path):
|
||||||
|
|||||||
126
aider/deprecated.py
Normal file
126
aider/deprecated.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
def add_deprecated_model_args(parser, group):
|
||||||
|
"""Add deprecated model shortcut arguments to the argparse parser."""
|
||||||
|
opus_model = "claude-3-opus-20240229"
|
||||||
|
group.add_argument(
|
||||||
|
"--opus",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {opus_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
sonnet_model = "anthropic/claude-3-7-sonnet-20250219"
|
||||||
|
group.add_argument(
|
||||||
|
"--sonnet",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {sonnet_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
haiku_model = "claude-3-5-haiku-20241022"
|
||||||
|
group.add_argument(
|
||||||
|
"--haiku",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {haiku_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
gpt_4_model = "gpt-4-0613"
|
||||||
|
group.add_argument(
|
||||||
|
"--4",
|
||||||
|
"-4",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {gpt_4_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
gpt_4o_model = "gpt-4o"
|
||||||
|
group.add_argument(
|
||||||
|
"--4o",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {gpt_4o_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
gpt_4o_mini_model = "gpt-4o-mini"
|
||||||
|
group.add_argument(
|
||||||
|
"--mini",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {gpt_4o_mini_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
gpt_4_turbo_model = "gpt-4-1106-preview"
|
||||||
|
group.add_argument(
|
||||||
|
"--4-turbo",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {gpt_4_turbo_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
gpt_3_model_name = "gpt-3.5-turbo"
|
||||||
|
group.add_argument(
|
||||||
|
"--35turbo",
|
||||||
|
"--35-turbo",
|
||||||
|
"--3",
|
||||||
|
"-3",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {gpt_3_model_name} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
deepseek_model = "deepseek/deepseek-chat"
|
||||||
|
group.add_argument(
|
||||||
|
"--deepseek",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {deepseek_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
o1_mini_model = "o1-mini"
|
||||||
|
group.add_argument(
|
||||||
|
"--o1-mini",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {o1_mini_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
o1_preview_model = "o1-preview"
|
||||||
|
group.add_argument(
|
||||||
|
"--o1-preview",
|
||||||
|
action="store_true",
|
||||||
|
help=f"Use {o1_preview_model} model for the main chat (deprecated, use --model)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_deprecated_model_args(args, io):
|
||||||
|
"""Handle deprecated model shortcut arguments and provide appropriate warnings."""
|
||||||
|
# Define model mapping
|
||||||
|
model_map = {
|
||||||
|
"opus": "claude-3-opus-20240229",
|
||||||
|
"sonnet": "anthropic/claude-3-7-sonnet-20250219",
|
||||||
|
"haiku": "claude-3-5-haiku-20241022",
|
||||||
|
"4": "gpt-4-0613",
|
||||||
|
"4o": "gpt-4o",
|
||||||
|
"mini": "gpt-4o-mini",
|
||||||
|
"4_turbo": "gpt-4-1106-preview",
|
||||||
|
"35turbo": "gpt-3.5-turbo",
|
||||||
|
"deepseek": "deepseek/deepseek-chat",
|
||||||
|
"o1_mini": "o1-mini",
|
||||||
|
"o1_preview": "o1-preview",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if any deprecated args are used
|
||||||
|
for arg_name, model_name in model_map.items():
|
||||||
|
arg_name_clean = arg_name.replace("-", "_")
|
||||||
|
if hasattr(args, arg_name_clean) and getattr(args, arg_name_clean):
|
||||||
|
# Find preferred name to display in warning
|
||||||
|
from aider.models import MODEL_ALIASES
|
||||||
|
|
||||||
|
display_name = model_name
|
||||||
|
# Check if there's a shorter alias for this model
|
||||||
|
for alias, full_name in MODEL_ALIASES.items():
|
||||||
|
if full_name == model_name:
|
||||||
|
display_name = alias
|
||||||
|
break
|
||||||
|
|
||||||
|
# Show the warning
|
||||||
|
io.tool_warning(
|
||||||
|
f"The --{arg_name.replace('_', '-')} flag is deprecated and will be removed in a"
|
||||||
|
f" future version. Please use --model {display_name} instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set the model
|
||||||
|
if not args.model:
|
||||||
|
args.model = model_name
|
||||||
|
break
|
||||||
@@ -10,12 +10,13 @@ This module provides functionality to:
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
|
from aider.dump import dump # noqa
|
||||||
|
|
||||||
DEFAULT_EDITOR_NIX = "vi"
|
DEFAULT_EDITOR_NIX = "vi"
|
||||||
DEFAULT_EDITOR_OS_X = "vim"
|
DEFAULT_EDITOR_OS_X = "vim"
|
||||||
DEFAULT_EDITOR_WINDOWS = "notepad"
|
DEFAULT_EDITOR_WINDOWS = "notepad"
|
||||||
@@ -87,13 +88,13 @@ def get_environment_editor(default=None):
|
|||||||
|
|
||||||
def discover_editor(editor_override=None):
|
def discover_editor(editor_override=None):
|
||||||
"""
|
"""
|
||||||
Discovers and returns the appropriate editor command as a list of arguments.
|
Discovers and returns the appropriate editor command.
|
||||||
|
|
||||||
Handles cases where the editor command includes arguments, including quoted arguments
|
Handles cases where the editor command includes arguments, including quoted arguments
|
||||||
with spaces (e.g. 'vim -c "set noswapfile"').
|
with spaces (e.g. 'vim -c "set noswapfile"').
|
||||||
|
|
||||||
:return: A list of command parts ready for subprocess execution
|
:return: The editor command as a string
|
||||||
:rtype: list[str]
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
if system == "Windows":
|
if system == "Windows":
|
||||||
@@ -102,14 +103,13 @@ def discover_editor(editor_override=None):
|
|||||||
default_editor = DEFAULT_EDITOR_OS_X
|
default_editor = DEFAULT_EDITOR_OS_X
|
||||||
else:
|
else:
|
||||||
default_editor = DEFAULT_EDITOR_NIX
|
default_editor = DEFAULT_EDITOR_NIX
|
||||||
|
|
||||||
if editor_override:
|
if editor_override:
|
||||||
editor = editor_override
|
editor = editor_override
|
||||||
else:
|
else:
|
||||||
editor = get_environment_editor(default_editor)
|
editor = get_environment_editor(default_editor)
|
||||||
try:
|
|
||||||
return shlex.split(editor)
|
return editor
|
||||||
except ValueError as e:
|
|
||||||
raise RuntimeError(f"Invalid editor command format '{editor}': {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def pipe_editor(input_data="", suffix=None, editor=None):
|
def pipe_editor(input_data="", suffix=None, editor=None):
|
||||||
@@ -128,9 +128,10 @@ def pipe_editor(input_data="", suffix=None, editor=None):
|
|||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
filepath = write_temp_file(input_data, suffix)
|
filepath = write_temp_file(input_data, suffix)
|
||||||
command_parts = discover_editor(editor)
|
command_str = discover_editor(editor)
|
||||||
command_parts.append(filepath)
|
command_str += " " + filepath
|
||||||
subprocess.call(command_parts)
|
|
||||||
|
subprocess.call(command_str, shell=True)
|
||||||
with open(filepath, "r") as f:
|
with open(filepath, "r") as f:
|
||||||
output_data = f.read()
|
output_data = f.read()
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from aider.dump import dump # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ExInfo:
|
class ExInfo:
|
||||||
@@ -50,6 +52,7 @@ EXCEPTIONS = [
|
|||||||
|
|
||||||
class LiteLLMExceptions:
|
class LiteLLMExceptions:
|
||||||
exceptions = dict()
|
exceptions = dict()
|
||||||
|
exception_info = {exi.name: exi for exi in EXCEPTIONS}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._load()
|
self._load()
|
||||||
@@ -58,20 +61,13 @@ class LiteLLMExceptions:
|
|||||||
import litellm
|
import litellm
|
||||||
|
|
||||||
for var in dir(litellm):
|
for var in dir(litellm):
|
||||||
if not var.endswith("Error"):
|
if var.endswith("Error"):
|
||||||
continue
|
if var not in self.exception_info:
|
||||||
|
raise ValueError(f"{var} is in litellm but not in aider's exceptions list")
|
||||||
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")
|
|
||||||
|
|
||||||
|
for var in self.exception_info:
|
||||||
ex = getattr(litellm, var)
|
ex = getattr(litellm, var)
|
||||||
self.exceptions[ex] = ex_info
|
self.exceptions[ex] = self.exception_info[var]
|
||||||
|
|
||||||
def exceptions_tuple(self):
|
def exceptions_tuple(self):
|
||||||
return tuple(self.exceptions)
|
return tuple(self.exceptions)
|
||||||
@@ -87,4 +83,8 @@ class LiteLLMExceptions:
|
|||||||
)
|
)
|
||||||
if "boto3" in str(ex):
|
if "boto3" in str(ex):
|
||||||
return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
|
return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
|
||||||
|
if "OpenrouterException" in str(ex) and "'choices'" in str(ex):
|
||||||
|
return ExInfo(
|
||||||
|
"APIConnectionError", True, "The OpenRouter API provider is down or overloaded."
|
||||||
|
)
|
||||||
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
# This needs to sync with MANIFEST.in
|
||||||
|
|
||||||
exclude_website_pats = [
|
exclude_website_pats = [
|
||||||
|
"**/.DS_Store",
|
||||||
"examples/**",
|
"examples/**",
|
||||||
"_posts/**",
|
"_posts/**",
|
||||||
"HISTORY.md",
|
"HISTORY.md",
|
||||||
@@ -7,5 +10,10 @@ exclude_website_pats = [
|
|||||||
"docs/unified-diffs.md",
|
"docs/unified-diffs.md",
|
||||||
"docs/leaderboards/index.md",
|
"docs/leaderboards/index.md",
|
||||||
"assets/**",
|
"assets/**",
|
||||||
"**/.DS_Store",
|
".jekyll-metadata",
|
||||||
|
"Gemfile.lock",
|
||||||
|
"Gemfile",
|
||||||
|
"_config.yml",
|
||||||
|
"**/OLD/**",
|
||||||
|
"OLD/**",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import argparse
|
|||||||
|
|
||||||
from aider import models, prompts
|
from aider import models, prompts
|
||||||
from aider.dump import dump # noqa: F401
|
from aider.dump import dump # noqa: F401
|
||||||
from aider.sendchat import simple_send_with_retries
|
|
||||||
|
|
||||||
|
|
||||||
class ChatSummary:
|
class ChatSummary:
|
||||||
@@ -26,6 +25,12 @@ class ChatSummary:
|
|||||||
return sized
|
return sized
|
||||||
|
|
||||||
def summarize(self, messages, depth=0):
|
def summarize(self, messages, depth=0):
|
||||||
|
messages = self.summarize_real(messages)
|
||||||
|
if messages and messages[-1]["role"] != "assistant":
|
||||||
|
messages.append(dict(role="assistant", content="Ok."))
|
||||||
|
return messages
|
||||||
|
|
||||||
|
def summarize_real(self, messages, depth=0):
|
||||||
if not self.models:
|
if not self.models:
|
||||||
raise ValueError("No models available for summarization")
|
raise ValueError("No models available for summarization")
|
||||||
|
|
||||||
@@ -88,7 +93,7 @@ class ChatSummary:
|
|||||||
if summary_tokens + tail_tokens < self.max_tokens:
|
if summary_tokens + tail_tokens < self.max_tokens:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return self.summarize(result, depth + 1)
|
return self.summarize_real(result, depth + 1)
|
||||||
|
|
||||||
def summarize_all(self, messages):
|
def summarize_all(self, messages):
|
||||||
content = ""
|
content = ""
|
||||||
@@ -108,7 +113,7 @@ class ChatSummary:
|
|||||||
|
|
||||||
for model in self.models:
|
for model in self.models:
|
||||||
try:
|
try:
|
||||||
summary = simple_send_with_retries(model, summarize_messages)
|
summary = model.simple_send_with_retries(summarize_messages)
|
||||||
if summary is not None:
|
if summary is not None:
|
||||||
summary = prompts.summary_prefix + summary
|
summary = prompts.summary_prefix + summary
|
||||||
return [dict(role="user", content=summary)]
|
return [dict(role="user", content=summary)]
|
||||||
|
|||||||
376
aider/io.py
376
aider/io.py
@@ -1,6 +1,9 @@
|
|||||||
import base64
|
import base64
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@@ -12,15 +15,18 @@ from pathlib import Path
|
|||||||
from prompt_toolkit.completion import Completer, Completion, ThreadedCompleter
|
from prompt_toolkit.completion import Completer, Completion, ThreadedCompleter
|
||||||
from prompt_toolkit.cursor_shapes import ModalCursorShapeConfig
|
from prompt_toolkit.cursor_shapes import ModalCursorShapeConfig
|
||||||
from prompt_toolkit.enums import EditingMode
|
from prompt_toolkit.enums import EditingMode
|
||||||
from prompt_toolkit.filters import Condition
|
from prompt_toolkit.filters import Condition, is_searching
|
||||||
from prompt_toolkit.history import FileHistory
|
from prompt_toolkit.history import FileHistory
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
|
from prompt_toolkit.key_binding.vi_state import InputMode
|
||||||
from prompt_toolkit.keys import Keys
|
from prompt_toolkit.keys import Keys
|
||||||
from prompt_toolkit.lexers import PygmentsLexer
|
from prompt_toolkit.lexers import PygmentsLexer
|
||||||
|
from prompt_toolkit.output.vt100 import is_dumb_terminal
|
||||||
from prompt_toolkit.shortcuts import CompleteStyle, PromptSession
|
from prompt_toolkit.shortcuts import CompleteStyle, PromptSession
|
||||||
from prompt_toolkit.styles import Style
|
from prompt_toolkit.styles import Style
|
||||||
from pygments.lexers import MarkdownLexer, guess_lexer_for_filename
|
from pygments.lexers import MarkdownLexer, guess_lexer_for_filename
|
||||||
from pygments.token import Token
|
from pygments.token import Token
|
||||||
|
from rich.color import ColorParseError
|
||||||
from rich.columns import Columns
|
from rich.columns import Columns
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import Markdown
|
||||||
@@ -30,8 +36,47 @@ from rich.text import Text
|
|||||||
from aider.mdstream import MarkdownStream
|
from aider.mdstream import MarkdownStream
|
||||||
|
|
||||||
from .dump import dump # noqa: F401
|
from .dump import dump # noqa: F401
|
||||||
|
from .editor import pipe_editor
|
||||||
from .utils import is_image_file
|
from .utils import is_image_file
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
NOTIFICATION_MESSAGE = "Aider is waiting for your input"
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_hash_prefix(color):
|
||||||
|
"""Ensure hex color values have a # prefix."""
|
||||||
|
if not color:
|
||||||
|
return color
|
||||||
|
if isinstance(color, str) and color.strip() and not color.startswith("#"):
|
||||||
|
# Check if it's a valid hex color (3 or 6 hex digits)
|
||||||
|
if all(c in "0123456789ABCDEFabcdef" for c in color) and len(color) in (3, 6):
|
||||||
|
return f"#{color}"
|
||||||
|
return color
|
||||||
|
|
||||||
|
|
||||||
|
def restore_multiline(func):
|
||||||
|
"""Decorator to restore multiline mode after function execution"""
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
orig_multiline = self.multiline_mode
|
||||||
|
self.multiline_mode = False
|
||||||
|
try:
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
self.multiline_mode = orig_multiline
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class CommandCompletionException(Exception):
|
||||||
|
"""Raised when a command should use the normal autocompleter instead of
|
||||||
|
command-specific completion."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ConfirmGroup:
|
class ConfirmGroup:
|
||||||
@@ -151,14 +196,23 @@ class AutoCompleter(Completer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if text[0] == "/":
|
if text[0] == "/":
|
||||||
yield from self.get_command_completions(document, complete_event, text, words)
|
try:
|
||||||
return
|
yield from self.get_command_completions(document, complete_event, text, words)
|
||||||
|
return
|
||||||
|
except CommandCompletionException:
|
||||||
|
# Fall through to normal completion
|
||||||
|
pass
|
||||||
|
|
||||||
candidates = self.words
|
candidates = self.words
|
||||||
candidates.update(set(self.fname_to_rel_fnames))
|
candidates.update(set(self.fname_to_rel_fnames))
|
||||||
candidates = [word if type(word) is tuple else (word, word) for word in candidates]
|
candidates = [word if type(word) is tuple else (word, word) for word in candidates]
|
||||||
|
|
||||||
last_word = words[-1]
|
last_word = words[-1]
|
||||||
|
|
||||||
|
# Only provide completions if the user has typed at least 3 characters
|
||||||
|
if len(last_word) < 3:
|
||||||
|
return
|
||||||
|
|
||||||
completions = []
|
completions = []
|
||||||
for word_match, word_insert in candidates:
|
for word_match, word_insert in candidates:
|
||||||
if word_match.lower().startswith(last_word.lower()):
|
if word_match.lower().startswith(last_word.lower()):
|
||||||
@@ -177,6 +231,8 @@ class InputOutput:
|
|||||||
num_error_outputs = 0
|
num_error_outputs = 0
|
||||||
num_user_asks = 0
|
num_user_asks = 0
|
||||||
clipboard_watcher = None
|
clipboard_watcher = None
|
||||||
|
bell_on_next_input = False
|
||||||
|
notifications_command = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -197,29 +253,48 @@ class InputOutput:
|
|||||||
completion_menu_current_bg_color=None,
|
completion_menu_current_bg_color=None,
|
||||||
code_theme="default",
|
code_theme="default",
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
|
line_endings="platform",
|
||||||
dry_run=False,
|
dry_run=False,
|
||||||
llm_history_file=None,
|
llm_history_file=None,
|
||||||
editingmode=EditingMode.EMACS,
|
editingmode=EditingMode.EMACS,
|
||||||
fancy_input=True,
|
fancy_input=True,
|
||||||
file_watcher=None,
|
file_watcher=None,
|
||||||
|
multiline_mode=False,
|
||||||
|
root=".",
|
||||||
|
notifications=False,
|
||||||
|
notifications_command=None,
|
||||||
):
|
):
|
||||||
self.placeholder = None
|
self.placeholder = None
|
||||||
self.interrupted = False
|
self.interrupted = False
|
||||||
self.never_prompts = set()
|
self.never_prompts = set()
|
||||||
self.editingmode = editingmode
|
self.editingmode = editingmode
|
||||||
|
self.multiline_mode = multiline_mode
|
||||||
|
self.bell_on_next_input = False
|
||||||
|
self.notifications = notifications
|
||||||
|
if notifications and notifications_command is None:
|
||||||
|
self.notifications_command = self.get_default_notification_command()
|
||||||
|
else:
|
||||||
|
self.notifications_command = notifications_command
|
||||||
|
|
||||||
no_color = os.environ.get("NO_COLOR")
|
no_color = os.environ.get("NO_COLOR")
|
||||||
if no_color is not None and no_color != "":
|
if no_color is not None and no_color != "":
|
||||||
pretty = False
|
pretty = False
|
||||||
|
|
||||||
self.user_input_color = user_input_color if pretty else None
|
self.user_input_color = ensure_hash_prefix(user_input_color) if pretty else None
|
||||||
self.tool_output_color = tool_output_color if pretty else None
|
self.tool_output_color = ensure_hash_prefix(tool_output_color) if pretty else None
|
||||||
self.tool_error_color = tool_error_color if pretty else None
|
self.tool_error_color = ensure_hash_prefix(tool_error_color) if pretty else None
|
||||||
self.tool_warning_color = tool_warning_color if pretty else None
|
self.tool_warning_color = ensure_hash_prefix(tool_warning_color) if pretty else None
|
||||||
self.assistant_output_color = assistant_output_color
|
self.assistant_output_color = ensure_hash_prefix(assistant_output_color)
|
||||||
self.completion_menu_color = completion_menu_color if pretty else None
|
self.completion_menu_color = ensure_hash_prefix(completion_menu_color) if pretty else None
|
||||||
self.completion_menu_bg_color = completion_menu_bg_color if pretty else None
|
self.completion_menu_bg_color = (
|
||||||
self.completion_menu_current_color = completion_menu_current_color if pretty else None
|
ensure_hash_prefix(completion_menu_bg_color) if pretty else None
|
||||||
self.completion_menu_current_bg_color = completion_menu_current_bg_color if pretty else None
|
)
|
||||||
|
self.completion_menu_current_color = (
|
||||||
|
ensure_hash_prefix(completion_menu_current_color) if pretty else None
|
||||||
|
)
|
||||||
|
self.completion_menu_current_bg_color = (
|
||||||
|
ensure_hash_prefix(completion_menu_current_bg_color) if pretty else None
|
||||||
|
)
|
||||||
|
|
||||||
self.code_theme = code_theme
|
self.code_theme = code_theme
|
||||||
|
|
||||||
@@ -240,14 +315,29 @@ class InputOutput:
|
|||||||
self.chat_history_file = None
|
self.chat_history_file = None
|
||||||
|
|
||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
|
valid_line_endings = {"platform", "lf", "crlf"}
|
||||||
|
if line_endings not in valid_line_endings:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid line_endings value: {line_endings}. "
|
||||||
|
f"Must be one of: {', '.join(valid_line_endings)}"
|
||||||
|
)
|
||||||
|
self.newline = (
|
||||||
|
None if line_endings == "platform" else "\n" if line_endings == "lf" else "\r\n"
|
||||||
|
)
|
||||||
self.dry_run = dry_run
|
self.dry_run = dry_run
|
||||||
|
|
||||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
self.append_chat_history(f"\n# aider chat started at {current_time}\n\n")
|
self.append_chat_history(f"\n# aider chat started at {current_time}\n\n")
|
||||||
|
|
||||||
self.prompt_session = None
|
self.prompt_session = None
|
||||||
|
self.is_dumb_terminal = is_dumb_terminal()
|
||||||
|
|
||||||
|
if self.is_dumb_terminal:
|
||||||
|
self.pretty = False
|
||||||
|
fancy_input = False
|
||||||
|
|
||||||
if fancy_input:
|
if fancy_input:
|
||||||
# Initialize PromptSession
|
# Initialize PromptSession only if we have a capable terminal
|
||||||
session_kwargs = {
|
session_kwargs = {
|
||||||
"input": self.input,
|
"input": self.input,
|
||||||
"output": self.output,
|
"output": self.output,
|
||||||
@@ -266,8 +356,40 @@ class InputOutput:
|
|||||||
self.tool_error(f"Can't initialize prompt toolkit: {err}") # non-pretty
|
self.tool_error(f"Can't initialize prompt toolkit: {err}") # non-pretty
|
||||||
else:
|
else:
|
||||||
self.console = Console(force_terminal=False, no_color=True) # non-pretty
|
self.console = Console(force_terminal=False, no_color=True) # non-pretty
|
||||||
|
if self.is_dumb_terminal:
|
||||||
|
self.tool_output("Detected dumb terminal, disabling fancy input and pretty output.")
|
||||||
|
|
||||||
self.file_watcher = file_watcher
|
self.file_watcher = file_watcher
|
||||||
|
self.root = root
|
||||||
|
|
||||||
|
# Validate color settings after console is initialized
|
||||||
|
self._validate_color_settings()
|
||||||
|
|
||||||
|
def _validate_color_settings(self):
|
||||||
|
"""Validate configured color strings and reset invalid ones."""
|
||||||
|
color_attributes = [
|
||||||
|
"user_input_color",
|
||||||
|
"tool_output_color",
|
||||||
|
"tool_error_color",
|
||||||
|
"tool_warning_color",
|
||||||
|
"assistant_output_color",
|
||||||
|
"completion_menu_color",
|
||||||
|
"completion_menu_bg_color",
|
||||||
|
"completion_menu_current_color",
|
||||||
|
"completion_menu_current_bg_color",
|
||||||
|
]
|
||||||
|
for attr_name in color_attributes:
|
||||||
|
color_value = getattr(self, attr_name, None)
|
||||||
|
if color_value:
|
||||||
|
try:
|
||||||
|
# Try creating a style to validate the color
|
||||||
|
RichStyle(color=color_value)
|
||||||
|
except ColorParseError as e:
|
||||||
|
self.console.print(
|
||||||
|
"[bold red]Warning:[/bold red] Invalid configuration for"
|
||||||
|
f" {attr_name}: '{color_value}'. {e}. Disabling this color."
|
||||||
|
)
|
||||||
|
setattr(self, attr_name, None) # Reset invalid color to None
|
||||||
|
|
||||||
def _get_style(self):
|
def _get_style(self):
|
||||||
style_dict = {}
|
style_dict = {}
|
||||||
@@ -294,9 +416,9 @@ class InputOutput:
|
|||||||
# Conditionally add 'completion-menu.completion.current' style
|
# Conditionally add 'completion-menu.completion.current' style
|
||||||
completion_menu_current_style = []
|
completion_menu_current_style = []
|
||||||
if self.completion_menu_current_bg_color:
|
if self.completion_menu_current_bg_color:
|
||||||
completion_menu_current_style.append(f"bg:{self.completion_menu_current_bg_color}")
|
completion_menu_current_style.append(self.completion_menu_current_bg_color)
|
||||||
if self.completion_menu_current_color:
|
if self.completion_menu_current_color:
|
||||||
completion_menu_current_style.append(self.completion_menu_current_color)
|
completion_menu_current_style.append(f"bg:{self.completion_menu_current_color}")
|
||||||
if completion_menu_current_style:
|
if completion_menu_current_style:
|
||||||
style_dict["completion-menu.completion.current"] = " ".join(
|
style_dict["completion-menu.completion.current"] = " ".join(
|
||||||
completion_menu_current_style
|
completion_menu_current_style
|
||||||
@@ -329,10 +451,6 @@ class InputOutput:
|
|||||||
try:
|
try:
|
||||||
with open(str(filename), "r", encoding=self.encoding) as f:
|
with open(str(filename), "r", encoding=self.encoding) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
except OSError as err:
|
|
||||||
if not silent:
|
|
||||||
self.tool_error(f"{filename}: unable to read: {err}")
|
|
||||||
return
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
if not silent:
|
if not silent:
|
||||||
self.tool_error(f"{filename}: file not found error")
|
self.tool_error(f"{filename}: file not found error")
|
||||||
@@ -341,6 +459,10 @@ class InputOutput:
|
|||||||
if not silent:
|
if not silent:
|
||||||
self.tool_error(f"{filename}: is a directory")
|
self.tool_error(f"{filename}: is a directory")
|
||||||
return
|
return
|
||||||
|
except OSError as err:
|
||||||
|
if not silent:
|
||||||
|
self.tool_error(f"{filename}: unable to read: {err}")
|
||||||
|
return
|
||||||
except UnicodeError as e:
|
except UnicodeError as e:
|
||||||
if not silent:
|
if not silent:
|
||||||
self.tool_error(f"{filename}: {e}")
|
self.tool_error(f"{filename}: {e}")
|
||||||
@@ -362,7 +484,7 @@ class InputOutput:
|
|||||||
delay = initial_delay
|
delay = initial_delay
|
||||||
for attempt in range(max_retries):
|
for attempt in range(max_retries):
|
||||||
try:
|
try:
|
||||||
with open(str(filename), "w", encoding=self.encoding) as f:
|
with open(str(filename), "w", encoding=self.encoding, newline=self.newline) as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
return # Successfully wrote the file
|
return # Successfully wrote the file
|
||||||
except PermissionError as err:
|
except PermissionError as err:
|
||||||
@@ -403,6 +525,9 @@ class InputOutput:
|
|||||||
):
|
):
|
||||||
self.rule()
|
self.rule()
|
||||||
|
|
||||||
|
# Ring the bell if needed
|
||||||
|
self.ring_bell()
|
||||||
|
|
||||||
rel_fnames = list(rel_fnames)
|
rel_fnames = list(rel_fnames)
|
||||||
show = ""
|
show = ""
|
||||||
if rel_fnames:
|
if rel_fnames:
|
||||||
@@ -410,9 +535,16 @@ class InputOutput:
|
|||||||
get_rel_fname(fname, root) for fname in (abs_read_only_fnames or [])
|
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)
|
show = self.format_files_for_input(rel_fnames, rel_read_only_fnames)
|
||||||
|
|
||||||
|
prompt_prefix = ""
|
||||||
if edit_format:
|
if edit_format:
|
||||||
show += edit_format
|
prompt_prefix += edit_format
|
||||||
show += "> "
|
if self.multiline_mode:
|
||||||
|
prompt_prefix += (" " if edit_format else "") + "multi"
|
||||||
|
prompt_prefix += "> "
|
||||||
|
|
||||||
|
show += prompt_prefix
|
||||||
|
self.prompt_prefix = prompt_prefix
|
||||||
|
|
||||||
inp = ""
|
inp = ""
|
||||||
multiline_input = False
|
multiline_input = False
|
||||||
@@ -456,13 +588,48 @@ class InputOutput:
|
|||||||
"Navigate forward through history"
|
"Navigate forward through history"
|
||||||
event.current_buffer.history_forward()
|
event.current_buffer.history_forward()
|
||||||
|
|
||||||
@kb.add("escape", "c-m", eager=True)
|
@kb.add("c-x", "c-e")
|
||||||
def _(event):
|
def _(event):
|
||||||
event.current_buffer.insert_text("\n")
|
"Edit current input in external editor (like Bash)"
|
||||||
|
buffer = event.current_buffer
|
||||||
|
current_text = buffer.text
|
||||||
|
|
||||||
|
# Open the editor with the current text
|
||||||
|
edited_text = pipe_editor(input_data=current_text)
|
||||||
|
|
||||||
|
# Replace the buffer with the edited text, strip any trailing newlines
|
||||||
|
buffer.text = edited_text.rstrip("\n")
|
||||||
|
|
||||||
|
# Move cursor to the end of the text
|
||||||
|
buffer.cursor_position = len(buffer.text)
|
||||||
|
|
||||||
|
@kb.add("enter", eager=True, filter=~is_searching)
|
||||||
|
def _(event):
|
||||||
|
"Handle Enter key press"
|
||||||
|
if self.multiline_mode and not (
|
||||||
|
self.editingmode == EditingMode.VI
|
||||||
|
and event.app.vi_state.input_mode == InputMode.NAVIGATION
|
||||||
|
):
|
||||||
|
# In multiline mode and if not in vi-mode or vi navigation/normal mode,
|
||||||
|
# Enter adds a newline
|
||||||
|
event.current_buffer.insert_text("\n")
|
||||||
|
else:
|
||||||
|
# In normal mode, Enter submits
|
||||||
|
event.current_buffer.validate_and_handle()
|
||||||
|
|
||||||
|
@kb.add("escape", "enter", eager=True, filter=~is_searching) # This is Alt+Enter
|
||||||
|
def _(event):
|
||||||
|
"Handle Alt+Enter key press"
|
||||||
|
if self.multiline_mode:
|
||||||
|
# In multiline mode, Alt+Enter submits
|
||||||
|
event.current_buffer.validate_and_handle()
|
||||||
|
else:
|
||||||
|
# In normal mode, Alt+Enter adds a newline
|
||||||
|
event.current_buffer.insert_text("\n")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if multiline_input:
|
if multiline_input:
|
||||||
show = ". "
|
show = self.prompt_prefix
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.prompt_session:
|
if self.prompt_session:
|
||||||
@@ -477,6 +644,9 @@ class InputOutput:
|
|||||||
if self.clipboard_watcher:
|
if self.clipboard_watcher:
|
||||||
self.clipboard_watcher.start()
|
self.clipboard_watcher.start()
|
||||||
|
|
||||||
|
def get_continuation(width, line_number, is_soft_wrap):
|
||||||
|
return self.prompt_prefix
|
||||||
|
|
||||||
line = self.prompt_session.prompt(
|
line = self.prompt_session.prompt(
|
||||||
show,
|
show,
|
||||||
default=default,
|
default=default,
|
||||||
@@ -485,6 +655,8 @@ class InputOutput:
|
|||||||
complete_style=CompleteStyle.MULTI_COLUMN,
|
complete_style=CompleteStyle.MULTI_COLUMN,
|
||||||
style=style,
|
style=style,
|
||||||
key_bindings=kb,
|
key_bindings=kb,
|
||||||
|
complete_while_typing=True,
|
||||||
|
prompt_continuation=get_continuation,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
line = input(show)
|
line = input(show)
|
||||||
@@ -620,6 +792,7 @@ class InputOutput:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@restore_multiline
|
||||||
def confirm_ask(
|
def confirm_ask(
|
||||||
self,
|
self,
|
||||||
question,
|
question,
|
||||||
@@ -631,6 +804,9 @@ class InputOutput:
|
|||||||
):
|
):
|
||||||
self.num_user_asks += 1
|
self.num_user_asks += 1
|
||||||
|
|
||||||
|
# Ring the bell if needed
|
||||||
|
self.ring_bell()
|
||||||
|
|
||||||
question_id = (question, subject)
|
question_id = (question, subject)
|
||||||
|
|
||||||
if question_id in self.never_prompts:
|
if question_id in self.never_prompts:
|
||||||
@@ -641,19 +817,22 @@ class InputOutput:
|
|||||||
if group:
|
if group:
|
||||||
allow_never = True
|
allow_never = True
|
||||||
|
|
||||||
valid_responses = ["yes", "no"]
|
valid_responses = ["yes", "no", "skip", "all"]
|
||||||
options = " (Y)es/(N)o"
|
options = " (Y)es/(N)o"
|
||||||
if group:
|
if group:
|
||||||
if not explicit_yes_required:
|
if not explicit_yes_required:
|
||||||
options += "/(A)ll"
|
options += "/(A)ll"
|
||||||
valid_responses.append("all")
|
|
||||||
options += "/(S)kip all"
|
options += "/(S)kip all"
|
||||||
valid_responses.append("skip")
|
|
||||||
if allow_never:
|
if allow_never:
|
||||||
options += "/(D)on't ask again"
|
options += "/(D)on't ask again"
|
||||||
valid_responses.append("don't")
|
valid_responses.append("don't")
|
||||||
|
|
||||||
question += options + " [Yes]: "
|
if default.lower().startswith("y"):
|
||||||
|
question += options + " [Yes]: "
|
||||||
|
elif default.lower().startswith("n"):
|
||||||
|
question += options + " [No]: "
|
||||||
|
else:
|
||||||
|
question += options + f" [{default}]: "
|
||||||
|
|
||||||
if subject:
|
if subject:
|
||||||
self.tool_output()
|
self.tool_output()
|
||||||
@@ -682,16 +861,22 @@ class InputOutput:
|
|||||||
self.user_input(f"{question}{res}", log_only=False)
|
self.user_input(f"{question}{res}", log_only=False)
|
||||||
else:
|
else:
|
||||||
while True:
|
while True:
|
||||||
if self.prompt_session:
|
try:
|
||||||
res = self.prompt_session.prompt(
|
if self.prompt_session:
|
||||||
question,
|
res = self.prompt_session.prompt(
|
||||||
style=style,
|
question,
|
||||||
)
|
style=style,
|
||||||
else:
|
complete_while_typing=False,
|
||||||
res = input(question)
|
)
|
||||||
|
else:
|
||||||
|
res = input(question)
|
||||||
|
except EOFError:
|
||||||
|
# Treat EOF (Ctrl+D) as if the user pressed Enter
|
||||||
|
res = default
|
||||||
|
break
|
||||||
|
|
||||||
if not res:
|
if not res:
|
||||||
res = "y" # Default to Yes if no input
|
res = default
|
||||||
break
|
break
|
||||||
res = res.lower()
|
res = res.lower()
|
||||||
good = any(valid_response.startswith(res) for valid_response in valid_responses)
|
good = any(valid_response.startswith(res) for valid_response in valid_responses)
|
||||||
@@ -728,9 +913,13 @@ class InputOutput:
|
|||||||
|
|
||||||
return is_yes
|
return is_yes
|
||||||
|
|
||||||
|
@restore_multiline
|
||||||
def prompt_ask(self, question, default="", subject=None):
|
def prompt_ask(self, question, default="", subject=None):
|
||||||
self.num_user_asks += 1
|
self.num_user_asks += 1
|
||||||
|
|
||||||
|
# Ring the bell if needed
|
||||||
|
self.ring_bell()
|
||||||
|
|
||||||
if subject:
|
if subject:
|
||||||
self.tool_output()
|
self.tool_output()
|
||||||
self.tool_output(subject, bold=True)
|
self.tool_output(subject, bold=True)
|
||||||
@@ -742,10 +931,19 @@ class InputOutput:
|
|||||||
elif self.yes is False:
|
elif self.yes is False:
|
||||||
res = "no"
|
res = "no"
|
||||||
else:
|
else:
|
||||||
if self.prompt_session:
|
try:
|
||||||
res = self.prompt_session.prompt(question + " ", default=default, style=style)
|
if self.prompt_session:
|
||||||
else:
|
res = self.prompt_session.prompt(
|
||||||
res = input(question + " ")
|
question + " ",
|
||||||
|
default=default,
|
||||||
|
style=style,
|
||||||
|
complete_while_typing=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
res = input(question + " ")
|
||||||
|
except EOFError:
|
||||||
|
# Treat EOF (Ctrl+D) as if the user pressed Enter
|
||||||
|
res = default
|
||||||
|
|
||||||
hist = f"{question.strip()} {res.strip()}"
|
hist = f"{question.strip()} {res.strip()}"
|
||||||
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
||||||
@@ -763,9 +961,18 @@ class InputOutput:
|
|||||||
hist = message.strip() if strip else message
|
hist = message.strip() if strip else message
|
||||||
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
||||||
|
|
||||||
message = Text(message)
|
if not isinstance(message, Text):
|
||||||
|
message = Text(message)
|
||||||
|
color = ensure_hash_prefix(color) if color else None
|
||||||
style = dict(style=color) if self.pretty and color else dict()
|
style = dict(style=color) if self.pretty and color else dict()
|
||||||
self.console.print(message, **style)
|
try:
|
||||||
|
self.console.print(message, **style)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
# Fallback to ASCII-safe output
|
||||||
|
if isinstance(message, Text):
|
||||||
|
message = message.plain
|
||||||
|
message = str(message).encode("ascii", errors="replace").decode("ascii")
|
||||||
|
self.console.print(message, **style)
|
||||||
|
|
||||||
def tool_error(self, message="", strip=True):
|
def tool_error(self, message="", strip=True):
|
||||||
self.num_error_outputs += 1
|
self.num_error_outputs += 1
|
||||||
@@ -787,7 +994,7 @@ class InputOutput:
|
|||||||
style = dict()
|
style = dict()
|
||||||
if self.pretty:
|
if self.pretty:
|
||||||
if self.tool_output_color:
|
if self.tool_output_color:
|
||||||
style["color"] = self.tool_output_color
|
style["color"] = ensure_hash_prefix(self.tool_output_color)
|
||||||
style["reverse"] = bold
|
style["reverse"] = bold
|
||||||
|
|
||||||
style = RichStyle(**style)
|
style = RichStyle(**style)
|
||||||
@@ -799,6 +1006,10 @@ class InputOutput:
|
|||||||
return mdStream
|
return mdStream
|
||||||
|
|
||||||
def assistant_output(self, message, pretty=None):
|
def assistant_output(self, message, pretty=None):
|
||||||
|
if not message:
|
||||||
|
self.tool_warning("Empty response received from LLM. Check your provider account?")
|
||||||
|
return
|
||||||
|
|
||||||
show_resp = message
|
show_resp = message
|
||||||
|
|
||||||
# Coder will force pretty off if fence is not triple-backticks
|
# Coder will force pretty off if fence is not triple-backticks
|
||||||
@@ -810,7 +1021,7 @@ class InputOutput:
|
|||||||
message, style=self.assistant_output_color, code_theme=self.code_theme
|
message, style=self.assistant_output_color, code_theme=self.code_theme
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
show_resp = Text(message or "<no response>")
|
show_resp = Text(message or "(empty response)")
|
||||||
|
|
||||||
self.console.print(show_resp)
|
self.console.print(show_resp)
|
||||||
|
|
||||||
@@ -821,6 +1032,73 @@ class InputOutput:
|
|||||||
def print(self, message=""):
|
def print(self, message=""):
|
||||||
print(message)
|
print(message)
|
||||||
|
|
||||||
|
def llm_started(self):
|
||||||
|
"""Mark that the LLM has started processing, so we should ring the bell on next input"""
|
||||||
|
self.bell_on_next_input = True
|
||||||
|
|
||||||
|
def get_default_notification_command(self):
|
||||||
|
"""Return a default notification command based on the operating system."""
|
||||||
|
import platform
|
||||||
|
|
||||||
|
system = platform.system()
|
||||||
|
|
||||||
|
if system == "Darwin": # macOS
|
||||||
|
# Check for terminal-notifier first
|
||||||
|
if shutil.which("terminal-notifier"):
|
||||||
|
return f"terminal-notifier -title 'Aider' -message '{NOTIFICATION_MESSAGE}'"
|
||||||
|
# Fall back to osascript
|
||||||
|
return (
|
||||||
|
f'osascript -e \'display notification "{NOTIFICATION_MESSAGE}" with title "Aider"\''
|
||||||
|
)
|
||||||
|
elif system == "Linux":
|
||||||
|
# Check for common Linux notification tools
|
||||||
|
for cmd in ["notify-send", "zenity"]:
|
||||||
|
if shutil.which(cmd):
|
||||||
|
if cmd == "notify-send":
|
||||||
|
return f"notify-send 'Aider' '{NOTIFICATION_MESSAGE}'"
|
||||||
|
elif cmd == "zenity":
|
||||||
|
return f"zenity --notification --text='{NOTIFICATION_MESSAGE}'"
|
||||||
|
return None # No known notification tool found
|
||||||
|
elif system == "Windows":
|
||||||
|
# PowerShell notification
|
||||||
|
return (
|
||||||
|
"powershell -command"
|
||||||
|
" \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');"
|
||||||
|
f" [System.Windows.Forms.MessageBox]::Show('{NOTIFICATION_MESSAGE}',"
|
||||||
|
" 'Aider')\""
|
||||||
|
)
|
||||||
|
|
||||||
|
return None # Unknown system
|
||||||
|
|
||||||
|
def ring_bell(self):
|
||||||
|
"""Ring the terminal bell if needed and clear the flag"""
|
||||||
|
if self.bell_on_next_input and self.notifications:
|
||||||
|
if self.notifications_command:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
self.notifications_command, shell=True, capture_output=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0 and result.stderr:
|
||||||
|
error_msg = result.stderr.decode("utf-8", errors="replace")
|
||||||
|
self.tool_warning(f"Failed to run notifications command: {error_msg}")
|
||||||
|
except Exception as e:
|
||||||
|
self.tool_warning(f"Failed to run notifications command: {e}")
|
||||||
|
else:
|
||||||
|
print("\a", end="", flush=True) # Ring the bell
|
||||||
|
self.bell_on_next_input = False # Clear the flag
|
||||||
|
|
||||||
|
def toggle_multiline_mode(self):
|
||||||
|
"""Toggle between normal and multiline input modes"""
|
||||||
|
self.multiline_mode = not self.multiline_mode
|
||||||
|
if self.multiline_mode:
|
||||||
|
self.tool_output(
|
||||||
|
"Multiline mode: Enabled. Enter inserts newline, Alt-Enter submits text"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.tool_output(
|
||||||
|
"Multiline mode: Disabled. Alt-Enter inserts newline, Enter submits text"
|
||||||
|
)
|
||||||
|
|
||||||
def append_chat_history(self, text, linebreak=False, blockquote=False, strip=True):
|
def append_chat_history(self, text, linebreak=False, blockquote=False, strip=True):
|
||||||
if blockquote:
|
if blockquote:
|
||||||
if strip:
|
if strip:
|
||||||
@@ -862,7 +1140,13 @@ class InputOutput:
|
|||||||
editable_files = [f for f in sorted(rel_fnames) if f not in rel_read_only_fnames]
|
editable_files = [f for f in sorted(rel_fnames) if f not in rel_read_only_fnames]
|
||||||
|
|
||||||
if read_only_files:
|
if read_only_files:
|
||||||
files_with_label = ["Readonly:"] + read_only_files
|
# Use shorter of abs/rel paths for readonly files
|
||||||
|
ro_paths = []
|
||||||
|
for rel_path in read_only_files:
|
||||||
|
abs_path = os.path.abspath(os.path.join(self.root, rel_path))
|
||||||
|
ro_paths.append(abs_path if len(abs_path) < len(rel_path) else rel_path)
|
||||||
|
|
||||||
|
files_with_label = ["Readonly:"] + ro_paths
|
||||||
read_only_output = StringIO()
|
read_only_output = StringIO()
|
||||||
Console(file=read_only_output, force_terminal=False).print(Columns(files_with_label))
|
Console(file=read_only_output, force_terminal=False).print(Columns(files_with_label))
|
||||||
read_only_lines = read_only_output.getvalue().splitlines()
|
read_only_lines = read_only_output.getvalue().splitlines()
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
|
import shlex
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from grep_ast import TreeContext, filename_to_lang
|
from grep_ast import TreeContext, filename_to_lang
|
||||||
from tree_sitter_languages import get_parser # noqa: E402
|
from grep_ast.tsl import get_parser # noqa: E402
|
||||||
|
|
||||||
from aider.dump import dump # noqa: F401
|
from aider.dump import dump # noqa: F401
|
||||||
|
from aider.run_cmd import run_cmd_subprocess # noqa: F401
|
||||||
|
|
||||||
# tree_sitter is throwing a FutureWarning
|
# tree_sitter is throwing a FutureWarning
|
||||||
warnings.simplefilter("ignore", category=FutureWarning)
|
warnings.simplefilter("ignore", category=FutureWarning)
|
||||||
@@ -43,27 +45,23 @@ class Linter:
|
|||||||
return fname
|
return fname
|
||||||
|
|
||||||
def run_cmd(self, cmd, rel_fname, code):
|
def run_cmd(self, cmd, rel_fname, code):
|
||||||
cmd += " " + rel_fname
|
cmd += " " + shlex.quote(rel_fname)
|
||||||
cmd = cmd.split()
|
|
||||||
|
|
||||||
|
returncode = 0
|
||||||
|
stdout = ""
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(
|
returncode, stdout = run_cmd_subprocess(
|
||||||
cmd,
|
cmd,
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
encoding=self.encoding,
|
|
||||||
errors="replace",
|
|
||||||
cwd=self.root,
|
cwd=self.root,
|
||||||
|
encoding=self.encoding,
|
||||||
)
|
)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
print(f"Unable to execute lint command: {err}")
|
print(f"Unable to execute lint command: {err}")
|
||||||
return
|
return
|
||||||
stdout, _ = process.communicate()
|
|
||||||
errors = stdout
|
errors = stdout
|
||||||
if process.returncode == 0:
|
if returncode == 0:
|
||||||
return # zero exit status
|
return # zero exit status
|
||||||
|
|
||||||
cmd = " ".join(cmd)
|
|
||||||
res = f"## Running: {cmd}\n\n"
|
res = f"## Running: {cmd}\n\n"
|
||||||
res += errors
|
res += errors
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import importlib
|
|||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from aider.dump import dump # noqa: F401
|
||||||
|
|
||||||
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
||||||
|
|
||||||
AIDER_SITE_URL = "https://aider.chat"
|
AIDER_SITE_URL = "https://aider.chat"
|
||||||
|
|||||||
253
aider/main.py
253
aider/main.py
@@ -1,4 +1,3 @@
|
|||||||
import configparser
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -9,7 +8,11 @@ import webbrowser
|
|||||||
from dataclasses import fields
|
from dataclasses import fields
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import git
|
try:
|
||||||
|
import git
|
||||||
|
except ImportError:
|
||||||
|
git = None
|
||||||
|
|
||||||
import importlib_resources
|
import importlib_resources
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from prompt_toolkit.enums import EditingMode
|
from prompt_toolkit.enums import EditingMode
|
||||||
@@ -21,11 +24,13 @@ from aider.coders import Coder
|
|||||||
from aider.coders.base_coder import UnknownEditFormat
|
from aider.coders.base_coder import UnknownEditFormat
|
||||||
from aider.commands import Commands, SwitchCoder
|
from aider.commands import Commands, SwitchCoder
|
||||||
from aider.copypaste import ClipboardWatcher
|
from aider.copypaste import ClipboardWatcher
|
||||||
|
from aider.deprecated import handle_deprecated_model_args
|
||||||
from aider.format_settings import format_settings, scrub_sensitive_info
|
from aider.format_settings import format_settings, scrub_sensitive_info
|
||||||
from aider.history import ChatSummary
|
from aider.history import ChatSummary
|
||||||
from aider.io import InputOutput
|
from aider.io import InputOutput
|
||||||
from aider.llm import litellm # noqa: F401; properly init litellm on launch
|
from aider.llm import litellm # noqa: F401; properly init litellm on launch
|
||||||
from aider.models import ModelSettings
|
from aider.models import ModelSettings
|
||||||
|
from aider.onboarding import select_default_model
|
||||||
from aider.repo import ANY_GIT_ERROR, GitRepo
|
from aider.repo import ANY_GIT_ERROR, GitRepo
|
||||||
from aider.report import report_uncaught_exceptions
|
from aider.report import report_uncaught_exceptions
|
||||||
from aider.versioncheck import check_version, install_from_main_branch, install_upgrade
|
from aider.versioncheck import check_version, install_from_main_branch, install_upgrade
|
||||||
@@ -93,6 +98,9 @@ def make_new_repo(git_root, io):
|
|||||||
|
|
||||||
|
|
||||||
def setup_git(git_root, io):
|
def setup_git(git_root, io):
|
||||||
|
if git is None:
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cwd = Path.cwd()
|
cwd = Path.cwd()
|
||||||
except OSError:
|
except OSError:
|
||||||
@@ -106,7 +114,9 @@ def setup_git(git_root, io):
|
|||||||
except ANY_GIT_ERROR:
|
except ANY_GIT_ERROR:
|
||||||
pass
|
pass
|
||||||
elif cwd == Path.home():
|
elif cwd == Path.home():
|
||||||
io.tool_warning("You should probably run aider in a directory, not your home dir.")
|
io.tool_warning(
|
||||||
|
"You should probably run aider in your project's directory, not your home dir."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
elif cwd and io.confirm_ask(
|
elif cwd and io.confirm_ask(
|
||||||
"No git repo found, create one to track aider's changes (recommended)?"
|
"No git repo found, create one to track aider's changes (recommended)?"
|
||||||
@@ -117,17 +127,15 @@ def setup_git(git_root, io):
|
|||||||
if not repo:
|
if not repo:
|
||||||
return
|
return
|
||||||
|
|
||||||
user_name = None
|
try:
|
||||||
user_email = None
|
user_name = repo.git.config("--get", "user.name") or None
|
||||||
with repo.config_reader() as config:
|
except git.exc.GitCommandError:
|
||||||
try:
|
user_name = None
|
||||||
user_name = config.get_value("user", "name", None)
|
|
||||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
try:
|
||||||
pass
|
user_email = repo.git.config("--get", "user.email") or None
|
||||||
try:
|
except git.exc.GitCommandError:
|
||||||
user_email = config.get_value("user", "email", None)
|
user_email = None
|
||||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if user_name and user_email:
|
if user_name and user_email:
|
||||||
return repo.working_tree_dir
|
return repo.working_tree_dir
|
||||||
@@ -149,39 +157,39 @@ def check_gitignore(git_root, io, ask=True):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
repo = git.Repo(git_root)
|
repo = git.Repo(git_root)
|
||||||
if repo.ignored(".aider") and repo.ignored(".env"):
|
patterns_to_add = []
|
||||||
|
|
||||||
|
if not repo.ignored(".aider"):
|
||||||
|
patterns_to_add.append(".aider*")
|
||||||
|
|
||||||
|
env_path = Path(git_root) / ".env"
|
||||||
|
if env_path.exists() and not repo.ignored(".env"):
|
||||||
|
patterns_to_add.append(".env")
|
||||||
|
|
||||||
|
if not patterns_to_add:
|
||||||
return
|
return
|
||||||
except ANY_GIT_ERROR:
|
|
||||||
pass
|
|
||||||
|
|
||||||
patterns = [".aider*", ".env"]
|
gitignore_file = Path(git_root) / ".gitignore"
|
||||||
patterns_to_add = []
|
if gitignore_file.exists():
|
||||||
|
try:
|
||||||
gitignore_file = Path(git_root) / ".gitignore"
|
content = io.read_text(gitignore_file)
|
||||||
if gitignore_file.exists():
|
if content is None:
|
||||||
try:
|
return
|
||||||
content = io.read_text(gitignore_file)
|
if not content.endswith("\n"):
|
||||||
if content is None:
|
content += "\n"
|
||||||
|
except OSError as e:
|
||||||
|
io.tool_error(f"Error when trying to read {gitignore_file}: {e}")
|
||||||
return
|
return
|
||||||
existing_lines = content.splitlines()
|
else:
|
||||||
for pat in patterns:
|
content = ""
|
||||||
if pat not in existing_lines:
|
except ANY_GIT_ERROR:
|
||||||
patterns_to_add.append(pat)
|
return
|
||||||
except OSError as e:
|
|
||||||
io.tool_error(f"Error when trying to read {gitignore_file}: {e}")
|
if ask:
|
||||||
|
io.tool_output("You can skip this check with --no-gitignore")
|
||||||
|
if not io.confirm_ask(f"Add {', '.join(patterns_to_add)} to .gitignore (recommended)?"):
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
content = ""
|
|
||||||
patterns_to_add = patterns
|
|
||||||
|
|
||||||
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 += "\n".join(patterns_to_add) + "\n"
|
content += "\n".join(patterns_to_add) + "\n"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -205,6 +213,22 @@ def check_streamlit_install(io):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def write_streamlit_credentials():
|
||||||
|
from streamlit.file_util import get_streamlit_file_path
|
||||||
|
|
||||||
|
# See https://github.com/Aider-AI/aider/issues/772
|
||||||
|
|
||||||
|
credential_path = Path(get_streamlit_file_path()) / "credentials.toml"
|
||||||
|
if not os.path.exists(credential_path):
|
||||||
|
empty_creds = '[general]\nemail = ""\n'
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(credential_path), exist_ok=True)
|
||||||
|
with open(credential_path, "w") as f:
|
||||||
|
f.write(empty_creds)
|
||||||
|
else:
|
||||||
|
print("Streamlit credentials already exist.")
|
||||||
|
|
||||||
|
|
||||||
def launch_gui(args):
|
def launch_gui(args):
|
||||||
from streamlit.web import cli
|
from streamlit.web import cli
|
||||||
|
|
||||||
@@ -213,6 +237,9 @@ def launch_gui(args):
|
|||||||
print()
|
print()
|
||||||
print("CONTROL-C to exit...")
|
print("CONTROL-C to exit...")
|
||||||
|
|
||||||
|
# Necessary so streamlit does not prompt the user for an email address.
|
||||||
|
write_streamlit_credentials()
|
||||||
|
|
||||||
target = gui.__file__
|
target = gui.__file__
|
||||||
|
|
||||||
st_args = ["run", target]
|
st_args = ["run", target]
|
||||||
@@ -331,11 +358,21 @@ def register_models(git_root, model_settings_fname, io, verbose=False):
|
|||||||
|
|
||||||
|
|
||||||
def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"):
|
def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"):
|
||||||
|
# Standard .env file search path
|
||||||
dotenv_files = generate_search_path_list(
|
dotenv_files = generate_search_path_list(
|
||||||
".env",
|
".env",
|
||||||
git_root,
|
git_root,
|
||||||
dotenv_fname,
|
dotenv_fname,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Explicitly add the OAuth keys file to the beginning of the list
|
||||||
|
oauth_keys_file = Path.home() / ".aider" / "oauth-keys.env"
|
||||||
|
if oauth_keys_file.exists():
|
||||||
|
# Insert at the beginning so it's loaded first (and potentially overridden)
|
||||||
|
dotenv_files.insert(0, str(oauth_keys_file.resolve()))
|
||||||
|
# Remove duplicates if it somehow got included by generate_search_path_list
|
||||||
|
dotenv_files = list(dict.fromkeys(dotenv_files))
|
||||||
|
|
||||||
loaded = []
|
loaded = []
|
||||||
for fname in dotenv_files:
|
for fname in dotenv_files:
|
||||||
try:
|
try:
|
||||||
@@ -350,18 +387,18 @@ def load_dotenv_files(git_root, dotenv_fname, encoding="utf-8"):
|
|||||||
|
|
||||||
|
|
||||||
def register_litellm_models(git_root, model_metadata_fname, io, verbose=False):
|
def register_litellm_models(git_root, model_metadata_fname, io, verbose=False):
|
||||||
model_metatdata_files = []
|
model_metadata_files = []
|
||||||
|
|
||||||
# Add the resource file path
|
# Add the resource file path
|
||||||
resource_metadata = importlib_resources.files("aider.resources").joinpath("model-metadata.json")
|
resource_metadata = importlib_resources.files("aider.resources").joinpath("model-metadata.json")
|
||||||
model_metatdata_files.append(str(resource_metadata))
|
model_metadata_files.append(str(resource_metadata))
|
||||||
|
|
||||||
model_metatdata_files += generate_search_path_list(
|
model_metadata_files += generate_search_path_list(
|
||||||
".aider.model.metadata.json", git_root, model_metadata_fname
|
".aider.model.metadata.json", git_root, model_metadata_fname
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
model_metadata_files_loaded = models.register_litellm_models(model_metatdata_files)
|
model_metadata_files_loaded = models.register_litellm_models(model_metadata_files)
|
||||||
if len(model_metadata_files_loaded) > 0 and verbose:
|
if len(model_metadata_files_loaded) > 0 and verbose:
|
||||||
io.tool_output("Loaded model metadata from:")
|
io.tool_output("Loaded model metadata from:")
|
||||||
for model_metadata_file in model_metadata_files_loaded:
|
for model_metadata_file in model_metadata_files_loaded:
|
||||||
@@ -385,6 +422,12 @@ def sanity_check_repo(repo, io):
|
|||||||
if not repo.git_repo_error:
|
if not repo.git_repo_error:
|
||||||
return True
|
return True
|
||||||
error_msg = str(repo.git_repo_error)
|
error_msg = str(repo.git_repo_error)
|
||||||
|
except UnicodeDecodeError as exc:
|
||||||
|
error_msg = (
|
||||||
|
"Failed to read the Git repository. This issue is likely caused by a path encoded "
|
||||||
|
f'in a format different from the expected encoding "{sys.getfilesystemencoding()}".\n'
|
||||||
|
f"Internal error: {str(exc)}"
|
||||||
|
)
|
||||||
except ANY_GIT_ERROR as exc:
|
except ANY_GIT_ERROR as exc:
|
||||||
error_msg = str(exc)
|
error_msg = str(exc)
|
||||||
bad_ver = "version in (1, 2)" in error_msg
|
bad_ver = "version in (1, 2)" in error_msg
|
||||||
@@ -410,7 +453,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
if argv is None:
|
if argv is None:
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
|
||||||
if force_git_root:
|
if git is None:
|
||||||
|
git_root = None
|
||||||
|
elif force_git_root:
|
||||||
git_root = force_git_root
|
git_root = force_git_root
|
||||||
else:
|
else:
|
||||||
git_root = get_git_root()
|
git_root = get_git_root()
|
||||||
@@ -457,6 +502,9 @@ 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
|
# Parse again to include any arguments that might have been defined in .env
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
|
if git is None:
|
||||||
|
args.git = False
|
||||||
|
|
||||||
if args.analytics_disable:
|
if args.analytics_disable:
|
||||||
analytics = Analytics(permanently_disable=True)
|
analytics = Analytics(permanently_disable=True)
|
||||||
print("Analytics have been permanently disabled.")
|
print("Analytics have been permanently disabled.")
|
||||||
@@ -468,10 +516,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
litellm._load_litellm()
|
litellm._load_litellm()
|
||||||
litellm._lazy_module.client_session = httpx.Client(verify=False)
|
litellm._lazy_module.client_session = httpx.Client(verify=False)
|
||||||
litellm._lazy_module.aclient_session = httpx.AsyncClient(verify=False)
|
litellm._lazy_module.aclient_session = httpx.AsyncClient(verify=False)
|
||||||
|
# Set verify_ssl on the model_info_manager
|
||||||
|
models.model_info_manager.set_verify_ssl(False)
|
||||||
|
|
||||||
if args.timeout:
|
if args.timeout:
|
||||||
litellm._load_litellm()
|
models.request_timeout = args.timeout
|
||||||
litellm._lazy_module.request_timeout = args.timeout
|
|
||||||
|
|
||||||
if args.dark_mode:
|
if args.dark_mode:
|
||||||
args.user_input_color = "#32FF32"
|
args.user_input_color = "#32FF32"
|
||||||
@@ -512,9 +561,13 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
code_theme=args.code_theme,
|
code_theme=args.code_theme,
|
||||||
dry_run=args.dry_run,
|
dry_run=args.dry_run,
|
||||||
encoding=args.encoding,
|
encoding=args.encoding,
|
||||||
|
line_endings=args.line_endings,
|
||||||
llm_history_file=args.llm_history_file,
|
llm_history_file=args.llm_history_file,
|
||||||
editingmode=editing_mode,
|
editingmode=editing_mode,
|
||||||
fancy_input=args.fancy_input,
|
fancy_input=args.fancy_input,
|
||||||
|
multiline_mode=args.multiline,
|
||||||
|
notifications=args.notifications,
|
||||||
|
notifications_command=args.notifications_command,
|
||||||
)
|
)
|
||||||
|
|
||||||
io = get_io(args.pretty)
|
io = get_io(args.pretty)
|
||||||
@@ -554,6 +607,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
|
|
||||||
if args.openai_api_key:
|
if args.openai_api_key:
|
||||||
os.environ["OPENAI_API_KEY"] = args.openai_api_key
|
os.environ["OPENAI_API_KEY"] = args.openai_api_key
|
||||||
|
|
||||||
|
# Handle deprecated model shortcut args
|
||||||
|
handle_deprecated_model_args(args, io)
|
||||||
if args.openai_api_base:
|
if args.openai_api_base:
|
||||||
os.environ["OPENAI_API_BASE"] = args.openai_api_base
|
os.environ["OPENAI_API_BASE"] = args.openai_api_base
|
||||||
if args.openai_api_version:
|
if args.openai_api_version:
|
||||||
@@ -645,7 +701,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
# We can't know the git repo for sure until after parsing the args.
|
# We can't know the git repo for sure until after parsing the args.
|
||||||
# If we guessed wrong, reparse because that changes things like
|
# If we guessed wrong, reparse because that changes things like
|
||||||
# the location of the config.yml and history files.
|
# the location of the config.yml and history files.
|
||||||
if args.git and not force_git_root:
|
if args.git and not force_git_root and git is not None:
|
||||||
right_repo_root = guessed_wrong_repo(io, git_root, fnames, git_dname)
|
right_repo_root = guessed_wrong_repo(io, git_root, fnames, git_dname)
|
||||||
if right_repo_root:
|
if right_repo_root:
|
||||||
analytics.event("exit", reason="Recursing with correct repo")
|
analytics.event("exit", reason="Recursing with correct repo")
|
||||||
@@ -669,11 +725,6 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
if args.check_update:
|
if args.check_update:
|
||||||
check_version(io, verbose=args.verbose)
|
check_version(io, verbose=args.verbose)
|
||||||
|
|
||||||
if args.list_models:
|
|
||||||
models.print_matching_models(io, args.list_models)
|
|
||||||
analytics.event("exit", reason="Listed models")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if args.git:
|
if args.git:
|
||||||
git_root = setup_git(git_root, io)
|
git_root = setup_git(git_root, io)
|
||||||
if args.gitignore:
|
if args.gitignore:
|
||||||
@@ -693,6 +744,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
register_models(git_root, args.model_settings_file, io, verbose=args.verbose)
|
register_models(git_root, args.model_settings_file, io, verbose=args.verbose)
|
||||||
register_litellm_models(git_root, args.model_metadata_file, io, verbose=args.verbose)
|
register_litellm_models(git_root, args.model_metadata_file, io, verbose=args.verbose)
|
||||||
|
|
||||||
|
if args.list_models:
|
||||||
|
models.print_matching_models(io, args.list_models)
|
||||||
|
analytics.event("exit", reason="Listed models")
|
||||||
|
return 0
|
||||||
|
|
||||||
# Process any command line aliases
|
# Process any command line aliases
|
||||||
if args.alias:
|
if args.alias:
|
||||||
for alias_def in args.alias:
|
for alias_def in args.alias:
|
||||||
@@ -706,18 +762,61 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
alias, model = parts
|
alias, model = parts
|
||||||
models.MODEL_ALIASES[alias.strip()] = model.strip()
|
models.MODEL_ALIASES[alias.strip()] = model.strip()
|
||||||
|
|
||||||
if not args.model:
|
selected_model_name = select_default_model(args, io, analytics)
|
||||||
args.model = "gpt-4o-2024-08-06"
|
if not selected_model_name:
|
||||||
if os.environ.get("ANTHROPIC_API_KEY"):
|
# Error message and analytics event are handled within select_default_model
|
||||||
args.model = "claude-3-5-sonnet-20241022"
|
return 1
|
||||||
|
args.model = selected_model_name # Update args with the selected model
|
||||||
|
|
||||||
main_model = models.Model(
|
main_model = models.Model(
|
||||||
args.model,
|
args.model,
|
||||||
weak_model=args.weak_model,
|
weak_model=args.weak_model,
|
||||||
editor_model=args.editor_model,
|
editor_model=args.editor_model,
|
||||||
editor_edit_format=args.editor_edit_format,
|
editor_edit_format=args.editor_edit_format,
|
||||||
|
verbose=args.verbose,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check if deprecated remove_reasoning is set
|
||||||
|
if main_model.remove_reasoning is not None:
|
||||||
|
io.tool_warning(
|
||||||
|
"Model setting 'remove_reasoning' is deprecated, please use 'reasoning_tag' instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set reasoning effort and thinking tokens if specified
|
||||||
|
if args.reasoning_effort is not None:
|
||||||
|
# Apply if check is disabled or model explicitly supports it
|
||||||
|
if not args.check_model_accepts_settings or (
|
||||||
|
main_model.accepts_settings and "reasoning_effort" in main_model.accepts_settings
|
||||||
|
):
|
||||||
|
main_model.set_reasoning_effort(args.reasoning_effort)
|
||||||
|
|
||||||
|
if args.thinking_tokens is not None:
|
||||||
|
# Apply if check is disabled or model explicitly supports it
|
||||||
|
if not args.check_model_accepts_settings or (
|
||||||
|
main_model.accepts_settings and "thinking_tokens" in main_model.accepts_settings
|
||||||
|
):
|
||||||
|
main_model.set_thinking_tokens(args.thinking_tokens)
|
||||||
|
|
||||||
|
# Show warnings about unsupported settings that are being ignored
|
||||||
|
if args.check_model_accepts_settings:
|
||||||
|
settings_to_check = [
|
||||||
|
{"arg": args.reasoning_effort, "name": "reasoning_effort"},
|
||||||
|
{"arg": args.thinking_tokens, "name": "thinking_tokens"},
|
||||||
|
]
|
||||||
|
|
||||||
|
for setting in settings_to_check:
|
||||||
|
if setting["arg"] is not None and (
|
||||||
|
not main_model.accepts_settings
|
||||||
|
or setting["name"] not in main_model.accepts_settings
|
||||||
|
):
|
||||||
|
io.tool_warning(
|
||||||
|
f"Warning: {main_model.name} does not support '{setting['name']}', ignoring."
|
||||||
|
)
|
||||||
|
io.tool_output(
|
||||||
|
f"Use --no-check-model-accepts-settings to force the '{setting['name']}'"
|
||||||
|
" setting."
|
||||||
|
)
|
||||||
|
|
||||||
if args.copy_paste and args.edit_format is None:
|
if args.copy_paste and args.edit_format is None:
|
||||||
if main_model.edit_format in ("diff", "whole"):
|
if main_model.edit_format in ("diff", "whole"):
|
||||||
main_model.edit_format = "editor-" + main_model.edit_format
|
main_model.edit_format = "editor-" + main_model.edit_format
|
||||||
@@ -765,6 +864,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
attribute_commit_message_committer=args.attribute_commit_message_committer,
|
attribute_commit_message_committer=args.attribute_commit_message_committer,
|
||||||
commit_prompt=args.commit_prompt,
|
commit_prompt=args.commit_prompt,
|
||||||
subtree_only=args.subtree_only,
|
subtree_only=args.subtree_only,
|
||||||
|
git_commit_verify=args.git_commit_verify,
|
||||||
)
|
)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
@@ -782,11 +882,15 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
commands = Commands(
|
commands = Commands(
|
||||||
io,
|
io,
|
||||||
None,
|
None,
|
||||||
|
voice_language=args.voice_language,
|
||||||
|
voice_input_device=args.voice_input_device,
|
||||||
|
voice_format=args.voice_format,
|
||||||
verify_ssl=args.verify_ssl,
|
verify_ssl=args.verify_ssl,
|
||||||
args=args,
|
args=args,
|
||||||
parser=parser,
|
parser=parser,
|
||||||
verbose=args.verbose,
|
verbose=args.verbose,
|
||||||
editor=args.editor,
|
editor=args.editor,
|
||||||
|
original_read_only_fnames=read_only_fnames,
|
||||||
)
|
)
|
||||||
|
|
||||||
summarizer = ChatSummary(
|
summarizer = ChatSummary(
|
||||||
@@ -804,6 +908,14 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
)
|
)
|
||||||
args.stream = False
|
args.stream = False
|
||||||
|
|
||||||
|
if args.map_tokens is None:
|
||||||
|
map_tokens = main_model.get_repo_map_tokens()
|
||||||
|
else:
|
||||||
|
map_tokens = args.map_tokens
|
||||||
|
|
||||||
|
# Track auto-commits configuration
|
||||||
|
analytics.event("auto_commits", enabled=bool(args.auto_commits))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
coder = Coder.create(
|
coder = Coder.create(
|
||||||
main_model=main_model,
|
main_model=main_model,
|
||||||
@@ -816,7 +928,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
auto_commits=args.auto_commits,
|
auto_commits=args.auto_commits,
|
||||||
dirty_commits=args.dirty_commits,
|
dirty_commits=args.dirty_commits,
|
||||||
dry_run=args.dry_run,
|
dry_run=args.dry_run,
|
||||||
map_tokens=args.map_tokens,
|
map_tokens=map_tokens,
|
||||||
verbose=args.verbose,
|
verbose=args.verbose,
|
||||||
stream=args.stream,
|
stream=args.stream,
|
||||||
use_git=args.git,
|
use_git=args.git,
|
||||||
@@ -836,6 +948,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
chat_language=args.chat_language,
|
chat_language=args.chat_language,
|
||||||
detect_urls=args.detect_urls,
|
detect_urls=args.detect_urls,
|
||||||
auto_copy_context=args.copy_paste,
|
auto_copy_context=args.copy_paste,
|
||||||
|
auto_accept_architect=args.auto_accept_architect,
|
||||||
)
|
)
|
||||||
except UnknownEditFormat as err:
|
except UnknownEditFormat as err:
|
||||||
io.tool_error(str(err))
|
io.tool_error(str(err))
|
||||||
@@ -859,7 +972,11 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
|
|
||||||
if args.watch_files:
|
if args.watch_files:
|
||||||
file_watcher = FileWatcher(
|
file_watcher = FileWatcher(
|
||||||
coder, gitignores=ignores, verbose=args.verbose, analytics=analytics
|
coder,
|
||||||
|
gitignores=ignores,
|
||||||
|
verbose=args.verbose,
|
||||||
|
analytics=analytics,
|
||||||
|
root=str(Path.cwd()) if args.subtree_only else None,
|
||||||
)
|
)
|
||||||
coder.file_watcher = file_watcher
|
coder.file_watcher = file_watcher
|
||||||
|
|
||||||
@@ -913,6 +1030,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
analytics.event("exit", reason="Failed to read apply content")
|
analytics.event("exit", reason="Failed to read apply content")
|
||||||
return
|
return
|
||||||
coder.partial_response_content = content
|
coder.partial_response_content = content
|
||||||
|
# For testing #2879
|
||||||
|
# from aider.coders.base_coder import all_fences
|
||||||
|
# coder.fence = all_fences[1]
|
||||||
coder.apply_updates()
|
coder.apply_updates()
|
||||||
analytics.event("exit", reason="Applied updates")
|
analytics.event("exit", reason="Applied updates")
|
||||||
return
|
return
|
||||||
@@ -942,6 +1062,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"Cur working dir: {Path.cwd()}")
|
||||||
io.tool_output(f"Git working dir: {git_root}")
|
io.tool_output(f"Git working dir: {git_root}")
|
||||||
|
|
||||||
|
if args.stream and args.cache_prompts:
|
||||||
|
io.tool_warning("Cost estimates may be inaccurate when using streaming and caching.")
|
||||||
|
|
||||||
if args.load:
|
if args.load:
|
||||||
commands.cmd_load(args.load)
|
commands.cmd_load(args.load)
|
||||||
|
|
||||||
@@ -980,10 +1103,17 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
coder.ok_to_warm_cache = bool(args.cache_keepalive_pings)
|
||||||
coder.run()
|
coder.run()
|
||||||
analytics.event("exit", reason="Completed main CLI coder.run")
|
analytics.event("exit", reason="Completed main CLI coder.run")
|
||||||
return
|
return
|
||||||
except SwitchCoder as switch:
|
except SwitchCoder as switch:
|
||||||
|
coder.ok_to_warm_cache = False
|
||||||
|
|
||||||
|
# Set the placeholder if provided
|
||||||
|
if hasattr(switch, "placeholder") and switch.placeholder is not None:
|
||||||
|
io.placeholder = switch.placeholder
|
||||||
|
|
||||||
kwargs = dict(io=io, from_coder=coder)
|
kwargs = dict(io=io, from_coder=coder)
|
||||||
kwargs.update(switch.kwargs)
|
kwargs.update(switch.kwargs)
|
||||||
if "show_announcements" in kwargs:
|
if "show_announcements" in kwargs:
|
||||||
@@ -1087,3 +1217,4 @@ def load_slow_imports(swallow=True):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
status = main()
|
status = main()
|
||||||
sys.exit(status)
|
sys.exit(status)
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,27 @@
|
|||||||
import io
|
import io
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from rich import box
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.live import Live
|
from rich.live import Live
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import CodeBlock, Heading, Markdown
|
||||||
|
from rich.panel import Panel
|
||||||
|
from rich.syntax import Syntax
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from aider.dump import dump # noqa: F401
|
from aider.dump import dump # noqa: F401
|
||||||
|
|
||||||
_text = """
|
_text_prefix = """
|
||||||
# Header
|
# Header
|
||||||
|
|
||||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
|
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
|
||||||
|
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
|
||||||
|
when an unknown printer took a galley of type and scrambled it to make a type
|
||||||
|
specimen book. It has survived not only five centuries, but also the leap into
|
||||||
|
electronic typesetting, remaining essentially unchanged. It was popularised in
|
||||||
|
the 1960s with the release of Letraset sheets containing Lorem Ipsum passages,
|
||||||
|
and more recently with desktop publishing software like Aldus PageMaker
|
||||||
|
including versions of Lorem Ipsum.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -27,10 +37,9 @@ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem
|
|||||||
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import sys
|
"""
|
||||||
|
|
||||||
def greeting():
|
_text_suffix = """
|
||||||
print("Hello world!")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sub header too
|
## Sub header too
|
||||||
@@ -40,82 +49,188 @@ The end.
|
|||||||
""" # noqa: E501
|
""" # noqa: E501
|
||||||
|
|
||||||
|
|
||||||
|
class NoInsetCodeBlock(CodeBlock):
|
||||||
|
"""A code block with syntax highlighting and no padding."""
|
||||||
|
|
||||||
|
def __rich_console__(self, console, options):
|
||||||
|
code = str(self.text).rstrip()
|
||||||
|
syntax = Syntax(code, self.lexer_name, theme=self.theme, word_wrap=True, padding=(1, 0))
|
||||||
|
yield syntax
|
||||||
|
|
||||||
|
|
||||||
|
class LeftHeading(Heading):
|
||||||
|
"""A heading class that renders left-justified."""
|
||||||
|
|
||||||
|
def __rich_console__(self, console, options):
|
||||||
|
text = self.text
|
||||||
|
text.justify = "left" # Override justification
|
||||||
|
if self.tag == "h1":
|
||||||
|
# Draw a border around h1s, but keep text left-aligned
|
||||||
|
yield Panel(
|
||||||
|
text,
|
||||||
|
box=box.HEAVY,
|
||||||
|
style="markdown.h1.border",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Styled text for h2 and beyond
|
||||||
|
if self.tag == "h2":
|
||||||
|
yield Text("") # Keep the blank line before h2
|
||||||
|
yield text
|
||||||
|
|
||||||
|
|
||||||
|
class NoInsetMarkdown(Markdown):
|
||||||
|
"""Markdown with code blocks that have no padding and left-justified headings."""
|
||||||
|
|
||||||
|
elements = {
|
||||||
|
**Markdown.elements,
|
||||||
|
"fence": NoInsetCodeBlock,
|
||||||
|
"code_block": NoInsetCodeBlock,
|
||||||
|
"heading_open": LeftHeading,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MarkdownStream:
|
class MarkdownStream:
|
||||||
live = None
|
"""Streaming markdown renderer that progressively displays content with a live updating window.
|
||||||
when = 0
|
|
||||||
min_delay = 0.050
|
Uses rich.console and rich.live to render markdown content with smooth scrolling
|
||||||
live_window = 6
|
and partial updates. Maintains a sliding window of visible content while streaming
|
||||||
|
in new markdown text.
|
||||||
|
"""
|
||||||
|
|
||||||
|
live = None # Rich Live display instance
|
||||||
|
when = 0 # Timestamp of last update
|
||||||
|
min_delay = 1.0 / 20 # Minimum time between updates (20fps)
|
||||||
|
live_window = 6 # Number of lines to keep visible at bottom during streaming
|
||||||
|
|
||||||
def __init__(self, mdargs=None):
|
def __init__(self, mdargs=None):
|
||||||
self.printed = []
|
"""Initialize the markdown stream.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mdargs (dict, optional): Additional arguments to pass to rich Markdown renderer
|
||||||
|
"""
|
||||||
|
self.printed = [] # Stores lines that have already been printed
|
||||||
|
|
||||||
if mdargs:
|
if mdargs:
|
||||||
self.mdargs = mdargs
|
self.mdargs = mdargs
|
||||||
else:
|
else:
|
||||||
self.mdargs = dict()
|
self.mdargs = dict()
|
||||||
|
|
||||||
|
# Initialize rich Live display with empty text
|
||||||
self.live = Live(Text(""), refresh_per_second=1.0 / self.min_delay)
|
self.live = Live(Text(""), refresh_per_second=1.0 / self.min_delay)
|
||||||
self.live.start()
|
self.live.start()
|
||||||
|
|
||||||
|
def _render_markdown_to_lines(self, text):
|
||||||
|
"""Render markdown text to a list of lines.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): Markdown text to render
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: List of rendered lines with line endings preserved
|
||||||
|
"""
|
||||||
|
# Render the markdown to a string buffer
|
||||||
|
string_io = io.StringIO()
|
||||||
|
console = Console(file=string_io, force_terminal=True)
|
||||||
|
markdown = NoInsetMarkdown(text, **self.mdargs)
|
||||||
|
console.print(markdown)
|
||||||
|
output = string_io.getvalue()
|
||||||
|
|
||||||
|
# Split rendered output into lines
|
||||||
|
return output.splitlines(keepends=True)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
"""Destructor to ensure Live display is properly cleaned up."""
|
||||||
if self.live:
|
if self.live:
|
||||||
try:
|
try:
|
||||||
self.live.stop()
|
self.live.stop()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass # Ignore any errors during cleanup
|
||||||
|
|
||||||
def update(self, text, final=False):
|
def update(self, text, final=False):
|
||||||
|
"""Update the displayed markdown content.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): The markdown text received so far
|
||||||
|
final (bool): If True, this is the final update and we should clean up
|
||||||
|
|
||||||
|
Splits the output into "stable" older lines and the "last few" lines
|
||||||
|
which aren't considered stable. They may shift around as new chunks
|
||||||
|
are appended to the markdown text.
|
||||||
|
|
||||||
|
The stable lines emit to the console above the Live window.
|
||||||
|
The unstable lines emit into the Live window so they can be repainted.
|
||||||
|
|
||||||
|
Markdown going to the console works better in terminal scrollback buffers.
|
||||||
|
The live window doesn't play nice with terminal scrollback.
|
||||||
|
"""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
# Throttle updates to maintain smooth rendering
|
||||||
if not final and now - self.when < self.min_delay:
|
if not final and now - self.when < self.min_delay:
|
||||||
return
|
return
|
||||||
self.when = now
|
self.when = now
|
||||||
|
|
||||||
string_io = io.StringIO()
|
# Measure render time and adjust min_delay to maintain smooth rendering
|
||||||
console = Console(file=string_io, force_terminal=True)
|
start = time.time()
|
||||||
|
lines = self._render_markdown_to_lines(text)
|
||||||
|
render_time = time.time() - start
|
||||||
|
|
||||||
markdown = Markdown(text, **self.mdargs)
|
# Set min_delay to render time plus a small buffer
|
||||||
|
self.min_delay = min(max(render_time * 10, 1.0 / 20), 2)
|
||||||
|
|
||||||
console.print(markdown)
|
|
||||||
output = string_io.getvalue()
|
|
||||||
|
|
||||||
lines = output.splitlines(keepends=True)
|
|
||||||
num_lines = len(lines)
|
num_lines = len(lines)
|
||||||
|
|
||||||
|
# How many lines have "left" the live window and are now considered stable?
|
||||||
|
# Or if final, consider all lines to be stable.
|
||||||
if not final:
|
if not final:
|
||||||
num_lines -= self.live_window
|
num_lines -= self.live_window
|
||||||
|
|
||||||
|
# If we have stable content to display...
|
||||||
if final or num_lines > 0:
|
if final or num_lines > 0:
|
||||||
|
# How many stable lines do we need to newly show above the live window?
|
||||||
num_printed = len(self.printed)
|
num_printed = len(self.printed)
|
||||||
|
|
||||||
show = num_lines - num_printed
|
show = num_lines - num_printed
|
||||||
|
|
||||||
|
# Skip if no new lines to show above live window
|
||||||
if show <= 0:
|
if show <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Get the new lines and display them
|
||||||
show = lines[num_printed:num_lines]
|
show = lines[num_printed:num_lines]
|
||||||
show = "".join(show)
|
show = "".join(show)
|
||||||
show = Text.from_ansi(show)
|
show = Text.from_ansi(show)
|
||||||
self.live.console.print(show)
|
self.live.console.print(show) # to the console above the live area
|
||||||
|
|
||||||
|
# Update our record of printed lines
|
||||||
self.printed = lines[:num_lines]
|
self.printed = lines[:num_lines]
|
||||||
|
|
||||||
|
# Handle final update cleanup
|
||||||
if final:
|
if final:
|
||||||
self.live.update(Text(""))
|
self.live.update(Text(""))
|
||||||
self.live.stop()
|
self.live.stop()
|
||||||
self.live = None
|
self.live = None
|
||||||
else:
|
return
|
||||||
rest = lines[num_lines:]
|
|
||||||
rest = "".join(rest)
|
# Update the live window with remaining lines
|
||||||
# rest = '...\n' + rest
|
rest = lines[num_lines:]
|
||||||
rest = Text.from_ansi(rest)
|
rest = "".join(rest)
|
||||||
self.live.update(rest)
|
rest = Text.from_ansi(rest)
|
||||||
|
self.live.update(rest)
|
||||||
|
|
||||||
|
def find_minimal_suffix(self, text, match_lines=50):
|
||||||
|
"""
|
||||||
|
Splits text into chunks on blank lines "\n\n".
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
_text = 5 * _text
|
with open("aider/io.py", "r") as f:
|
||||||
|
code = f.read()
|
||||||
|
_text = _text_prefix + code + _text_suffix
|
||||||
|
_text = _text * 10
|
||||||
|
|
||||||
pm = MarkdownStream()
|
pm = MarkdownStream()
|
||||||
for i in range(6, len(_text)):
|
print("Using NoInsetMarkdown for code blocks with padding=0")
|
||||||
|
for i in range(6, len(_text), 5):
|
||||||
pm.update(_text[:i])
|
pm.update(_text[:i])
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|||||||
1150
aider/models.py
1150
aider/models.py
File diff suppressed because it is too large
Load Diff
428
aider/onboarding.py
Normal file
428
aider/onboarding.py
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import http.server
|
||||||
|
import os
|
||||||
|
import secrets
|
||||||
|
import socketserver
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import webbrowser
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from aider import urls
|
||||||
|
from aider.io import InputOutput
|
||||||
|
|
||||||
|
|
||||||
|
def check_openrouter_tier(api_key):
|
||||||
|
"""
|
||||||
|
Checks if the user is on a free tier for OpenRouter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
api_key: The OpenRouter API key to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A boolean indicating if the user is on a free tier (True) or paid tier (False).
|
||||||
|
Returns True if the check fails.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
"https://openrouter.ai/api/v1/auth/key",
|
||||||
|
headers={"Authorization": f"Bearer {api_key}"},
|
||||||
|
timeout=5, # Add a reasonable timeout
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
# According to the documentation, 'is_free_tier' will be true if the user has never paid
|
||||||
|
return data.get("data", {}).get("is_free_tier", True) # Default to True if not found
|
||||||
|
except Exception:
|
||||||
|
# If there's any error, we'll default to assuming free tier
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def try_to_select_default_model():
|
||||||
|
"""
|
||||||
|
Attempts to select a default model based on available API keys.
|
||||||
|
Checks OpenRouter tier status to select appropriate model.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name of the selected model, or None if no suitable default is found.
|
||||||
|
"""
|
||||||
|
# Special handling for OpenRouter
|
||||||
|
openrouter_key = os.environ.get("OPENROUTER_API_KEY")
|
||||||
|
if openrouter_key:
|
||||||
|
# Check if the user is on a free tier
|
||||||
|
is_free_tier = check_openrouter_tier(openrouter_key)
|
||||||
|
if is_free_tier:
|
||||||
|
return "openrouter/google/gemini-2.5-pro-exp-03-25:free"
|
||||||
|
else:
|
||||||
|
return "openrouter/anthropic/claude-3.7-sonnet"
|
||||||
|
|
||||||
|
# Select model based on other available API keys
|
||||||
|
model_key_pairs = [
|
||||||
|
("ANTHROPIC_API_KEY", "sonnet"),
|
||||||
|
("DEEPSEEK_API_KEY", "deepseek"),
|
||||||
|
("OPENAI_API_KEY", "gpt-4o"),
|
||||||
|
("GEMINI_API_KEY", "gemini/gemini-2.5-pro-exp-03-25"),
|
||||||
|
("VERTEXAI_PROJECT", "vertex_ai/gemini-2.5-pro-exp-03-25"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for env_key, model_name in model_key_pairs:
|
||||||
|
api_key_value = os.environ.get(env_key)
|
||||||
|
if api_key_value:
|
||||||
|
return model_name
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def offer_openrouter_oauth(io, analytics):
|
||||||
|
"""
|
||||||
|
Offers OpenRouter OAuth flow to the user if no API keys are found.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
io: The InputOutput object for user interaction.
|
||||||
|
analytics: The Analytics object for tracking events.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if authentication was successful, False otherwise.
|
||||||
|
"""
|
||||||
|
# No API keys found - Offer OpenRouter OAuth
|
||||||
|
io.tool_output("OpenRouter provides free and paid access to many LLMs.")
|
||||||
|
# Use confirm_ask which handles non-interactive cases
|
||||||
|
if io.confirm_ask(
|
||||||
|
"Login to OpenRouter or create a free account?",
|
||||||
|
default="y",
|
||||||
|
):
|
||||||
|
analytics.event("oauth_flow_initiated", provider="openrouter")
|
||||||
|
openrouter_key = start_openrouter_oauth_flow(io, analytics)
|
||||||
|
if openrouter_key:
|
||||||
|
# Successfully got key via OAuth, use the default OpenRouter model
|
||||||
|
# Ensure OPENROUTER_API_KEY is now set in the environment for later use
|
||||||
|
os.environ["OPENROUTER_API_KEY"] = openrouter_key
|
||||||
|
# Track OAuth success leading to model selection
|
||||||
|
analytics.event("oauth_flow_success")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# OAuth failed or was cancelled by user implicitly (e.g., closing browser)
|
||||||
|
# Error messages are handled within start_openrouter_oauth_flow
|
||||||
|
analytics.event("oauth_flow_failure")
|
||||||
|
io.tool_error("OpenRouter authentication did not complete successfully.")
|
||||||
|
# Fall through to the final error message
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def select_default_model(args, io, analytics):
|
||||||
|
"""
|
||||||
|
Selects a default model based on available API keys if no model is specified.
|
||||||
|
Offers OAuth flow for OpenRouter if no keys are found.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args: The command line arguments object.
|
||||||
|
io: The InputOutput object for user interaction.
|
||||||
|
analytics: The Analytics object for tracking events.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name of the selected model, or None if no suitable default is found.
|
||||||
|
"""
|
||||||
|
if args.model:
|
||||||
|
return args.model # Model already specified
|
||||||
|
|
||||||
|
model = try_to_select_default_model()
|
||||||
|
if model:
|
||||||
|
io.tool_warning(f"Using {model} model with API key from environment.")
|
||||||
|
analytics.event("auto_model_selection", model=model)
|
||||||
|
return model
|
||||||
|
|
||||||
|
no_model_msg = "No LLM model was specified and no API keys were provided."
|
||||||
|
io.tool_warning(no_model_msg)
|
||||||
|
|
||||||
|
# Try OAuth if no model was detected
|
||||||
|
offer_openrouter_oauth(io, analytics)
|
||||||
|
|
||||||
|
# Check again after potential OAuth success
|
||||||
|
model = try_to_select_default_model()
|
||||||
|
if model:
|
||||||
|
return model
|
||||||
|
|
||||||
|
io.offer_url(urls.models_and_keys, "Open documentation URL for more info?")
|
||||||
|
|
||||||
|
|
||||||
|
# Helper function to find an available port
|
||||||
|
def find_available_port(start_port=8484, end_port=8584):
|
||||||
|
for port in range(start_port, end_port + 1):
|
||||||
|
try:
|
||||||
|
# Check if the port is available by trying to bind to it
|
||||||
|
with socketserver.TCPServer(("localhost", port), None):
|
||||||
|
return port
|
||||||
|
except OSError:
|
||||||
|
# Port is likely already in use
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# PKCE code generation
|
||||||
|
def generate_pkce_codes():
|
||||||
|
code_verifier = secrets.token_urlsafe(64)
|
||||||
|
hasher = hashlib.sha256()
|
||||||
|
hasher.update(code_verifier.encode("utf-8"))
|
||||||
|
code_challenge = base64.urlsafe_b64encode(hasher.digest()).rstrip(b"=").decode("utf-8")
|
||||||
|
return code_verifier, code_challenge
|
||||||
|
|
||||||
|
|
||||||
|
# Function to exchange the authorization code for an API key
|
||||||
|
def exchange_code_for_key(code, code_verifier, io):
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
"https://openrouter.ai/api/v1/auth/keys",
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json={
|
||||||
|
"code": code,
|
||||||
|
"code_verifier": code_verifier,
|
||||||
|
"code_challenge_method": "S256",
|
||||||
|
},
|
||||||
|
timeout=30, # Add a timeout
|
||||||
|
)
|
||||||
|
response.raise_for_status() # Raise exception for bad status codes (4xx or 5xx)
|
||||||
|
data = response.json()
|
||||||
|
api_key = data.get("key")
|
||||||
|
if not api_key:
|
||||||
|
io.tool_error("Error: 'key' not found in OpenRouter response.")
|
||||||
|
io.tool_error(f"Response: {response.text}")
|
||||||
|
return None
|
||||||
|
return api_key
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
io.tool_error("Error: Request to OpenRouter timed out during code exchange.")
|
||||||
|
return None
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
io.tool_error(
|
||||||
|
"Error exchanging code for OpenRouter key:"
|
||||||
|
f" {e.response.status_code} {e.response.reason}"
|
||||||
|
)
|
||||||
|
io.tool_error(f"Response: {e.response.text}")
|
||||||
|
return None
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
io.tool_error(f"Error exchanging code for OpenRouter key: {e}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
io.tool_error(f"Unexpected error during code exchange: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Function to start the OAuth flow
|
||||||
|
def start_openrouter_oauth_flow(io, analytics):
|
||||||
|
"""Initiates the OpenRouter OAuth PKCE flow using a local server."""
|
||||||
|
|
||||||
|
port = find_available_port()
|
||||||
|
if not port:
|
||||||
|
io.tool_error("Could not find an available port between 8484 and 8584.")
|
||||||
|
io.tool_error("Please ensure a port in this range is free, or configure manually.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
callback_url = f"http://localhost:{port}/callback/aider"
|
||||||
|
auth_code = None
|
||||||
|
server_error = None
|
||||||
|
server_started = threading.Event()
|
||||||
|
shutdown_server = threading.Event()
|
||||||
|
|
||||||
|
class OAuthCallbackHandler(http.server.SimpleHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
nonlocal auth_code, server_error
|
||||||
|
parsed_path = urlparse(self.path)
|
||||||
|
if parsed_path.path == "/callback/aider":
|
||||||
|
query_params = parse_qs(parsed_path.query)
|
||||||
|
if "code" in query_params:
|
||||||
|
auth_code = query_params["code"][0]
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "text/html")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(
|
||||||
|
b"<html><body><h1>Success!</h1>"
|
||||||
|
b"<p>Aider has received the authentication code. "
|
||||||
|
b"You can close this browser tab.</p></body></html>"
|
||||||
|
)
|
||||||
|
# Signal the main thread to shut down the server
|
||||||
|
# Signal the main thread to shut down the server
|
||||||
|
shutdown_server.set()
|
||||||
|
else:
|
||||||
|
# Redirect to aider website if 'code' is missing (e.g., user visited manually)
|
||||||
|
self.send_response(302) # Found (temporary redirect)
|
||||||
|
self.send_header("Location", urls.website)
|
||||||
|
self.end_headers()
|
||||||
|
# No need to set server_error, just redirect.
|
||||||
|
# Do NOT shut down the server here; wait for timeout or success.
|
||||||
|
else:
|
||||||
|
# Redirect anything else (e.g., favicon.ico) to the main website as well
|
||||||
|
self.send_response(302)
|
||||||
|
self.send_header("Location", urls.website)
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(b"Not Found")
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
# Suppress server logging to keep terminal clean
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run_server():
|
||||||
|
nonlocal server_error
|
||||||
|
try:
|
||||||
|
with socketserver.TCPServer(("localhost", port), OAuthCallbackHandler) as httpd:
|
||||||
|
io.tool_output(f"Temporary server listening on {callback_url}", log_only=True)
|
||||||
|
server_started.set() # Signal that the server is ready
|
||||||
|
# Wait until shutdown is requested or timeout occurs (handled by main thread)
|
||||||
|
while not shutdown_server.is_set():
|
||||||
|
httpd.handle_request() # Handle one request at a time
|
||||||
|
# Add a small sleep to prevent busy-waiting if needed,
|
||||||
|
# though handle_request should block appropriately.
|
||||||
|
time.sleep(0.1)
|
||||||
|
io.tool_output("Shutting down temporary server.", log_only=True)
|
||||||
|
except Exception as e:
|
||||||
|
server_error = f"Failed to start or run temporary server: {e}"
|
||||||
|
server_started.set() # Signal even if failed, error will be checked
|
||||||
|
shutdown_server.set() # Ensure shutdown logic proceeds
|
||||||
|
|
||||||
|
server_thread = threading.Thread(target=run_server, daemon=True)
|
||||||
|
server_thread.start()
|
||||||
|
|
||||||
|
# Wait briefly for the server to start, or for an error
|
||||||
|
if not server_started.wait(timeout=5):
|
||||||
|
io.tool_error("Temporary authentication server failed to start in time.")
|
||||||
|
shutdown_server.set() # Ensure thread exits if it eventually starts
|
||||||
|
server_thread.join(timeout=1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check if server failed during startup
|
||||||
|
if server_error:
|
||||||
|
io.tool_error(server_error)
|
||||||
|
shutdown_server.set() # Ensure thread exits
|
||||||
|
server_thread.join(timeout=1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Generate codes and URL
|
||||||
|
code_verifier, code_challenge = generate_pkce_codes()
|
||||||
|
auth_url_base = "https://openrouter.ai/auth"
|
||||||
|
auth_params = {
|
||||||
|
"callback_url": callback_url,
|
||||||
|
"code_challenge": code_challenge,
|
||||||
|
"code_challenge_method": "S256",
|
||||||
|
}
|
||||||
|
auth_url = f"{auth_url_base}?{'&'.join(f'{k}={v}' for k, v in auth_params.items())}"
|
||||||
|
|
||||||
|
io.tool_output("\nPlease open this URL in your browser to connect Aider with OpenRouter:")
|
||||||
|
io.tool_output()
|
||||||
|
print(auth_url)
|
||||||
|
|
||||||
|
MINUTES = 5
|
||||||
|
io.tool_output(f"\nWaiting up to {MINUTES} minutes for you to finish in the browser...")
|
||||||
|
io.tool_output("Use Control-C to interrupt.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
webbrowser.open(auth_url)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Wait for the callback to set the auth_code or for timeout/error
|
||||||
|
interrupted = False
|
||||||
|
try:
|
||||||
|
shutdown_server.wait(timeout=MINUTES * 60) # Convert minutes to seconds
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
io.tool_warning("\nOAuth flow interrupted.")
|
||||||
|
analytics.event("oauth_flow_failed", provider="openrouter", reason="user_interrupt")
|
||||||
|
interrupted = True
|
||||||
|
# Ensure the server thread is signaled to shut down
|
||||||
|
shutdown_server.set()
|
||||||
|
|
||||||
|
# Join the server thread to ensure it's cleaned up
|
||||||
|
server_thread.join(timeout=1)
|
||||||
|
|
||||||
|
if interrupted:
|
||||||
|
return None # Return None if interrupted by user
|
||||||
|
|
||||||
|
if server_error:
|
||||||
|
io.tool_error(f"Authentication failed: {server_error}")
|
||||||
|
analytics.event("oauth_flow_failed", provider="openrouter", reason=server_error)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not auth_code:
|
||||||
|
io.tool_error("Authentication with OpenRouter failed.")
|
||||||
|
analytics.event("oauth_flow_failed", provider="openrouter")
|
||||||
|
return None
|
||||||
|
|
||||||
|
io.tool_output("Completing authentication...")
|
||||||
|
analytics.event("oauth_flow_code_received", provider="openrouter")
|
||||||
|
|
||||||
|
# Exchange code for key
|
||||||
|
api_key = exchange_code_for_key(auth_code, code_verifier, io)
|
||||||
|
|
||||||
|
if api_key:
|
||||||
|
# Set env var for the current session immediately
|
||||||
|
os.environ["OPENROUTER_API_KEY"] = api_key
|
||||||
|
|
||||||
|
# Save the key to the oauth-keys.env file
|
||||||
|
try:
|
||||||
|
config_dir = os.path.expanduser("~/.aider")
|
||||||
|
os.makedirs(config_dir, exist_ok=True)
|
||||||
|
key_file = os.path.join(config_dir, "oauth-keys.env")
|
||||||
|
with open(key_file, "a", encoding="utf-8") as f:
|
||||||
|
f.write(f'OPENROUTER_API_KEY="{api_key}"\n')
|
||||||
|
|
||||||
|
io.tool_warning("Aider will load the OpenRouter key automatically in future sessions.")
|
||||||
|
io.tool_output()
|
||||||
|
|
||||||
|
analytics.event("oauth_flow_success", provider="openrouter")
|
||||||
|
return api_key
|
||||||
|
except Exception as e:
|
||||||
|
io.tool_error(f"Successfully obtained key, but failed to save it to file: {e}")
|
||||||
|
io.tool_warning("Set OPENROUTER_API_KEY environment variable for this session only.")
|
||||||
|
# Still return the key for the current session even if saving failed
|
||||||
|
analytics.event("oauth_flow_save_failed", provider="openrouter", reason=str(e))
|
||||||
|
return api_key
|
||||||
|
else:
|
||||||
|
io.tool_error("Authentication with OpenRouter failed.")
|
||||||
|
analytics.event("oauth_flow_failed", provider="openrouter", reason="code_exchange_failed")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Dummy Analytics class for testing
|
||||||
|
class DummyAnalytics:
|
||||||
|
def event(self, *args, **kwargs):
|
||||||
|
# print(f"Analytics Event: {args} {kwargs}") # Optional: print events
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to test the OpenRouter OAuth flow."""
|
||||||
|
print("Starting OpenRouter OAuth flow test...")
|
||||||
|
|
||||||
|
# Use a real IO object for interaction
|
||||||
|
io = InputOutput(
|
||||||
|
pretty=True,
|
||||||
|
yes=False,
|
||||||
|
input_history_file=None,
|
||||||
|
chat_history_file=None,
|
||||||
|
tool_output_color="BLUE",
|
||||||
|
tool_error_color="RED",
|
||||||
|
)
|
||||||
|
# Use a dummy analytics object
|
||||||
|
analytics = DummyAnalytics()
|
||||||
|
|
||||||
|
# Ensure OPENROUTER_API_KEY is not set, to trigger the flow naturally
|
||||||
|
# (though start_openrouter_oauth_flow doesn't check this itself)
|
||||||
|
if "OPENROUTER_API_KEY" in os.environ:
|
||||||
|
print("Warning: OPENROUTER_API_KEY is already set in environment.")
|
||||||
|
# del os.environ["OPENROUTER_API_KEY"] # Optionally unset it for testing
|
||||||
|
|
||||||
|
api_key = start_openrouter_oauth_flow(io, analytics)
|
||||||
|
|
||||||
|
if api_key:
|
||||||
|
print("\nOAuth flow completed successfully!")
|
||||||
|
print(f"Obtained API Key (first 5 chars): {api_key[:5]}...")
|
||||||
|
# Be careful printing the key, even partially
|
||||||
|
else:
|
||||||
|
print("\nOAuth flow failed or was cancelled.")
|
||||||
|
|
||||||
|
print("\nOpenRouter OAuth flow test finished.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
7
aider/queries/tree-sitter-language-pack/README.md
Normal file
7
aider/queries/tree-sitter-language-pack/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
These scm files are all adapted from the github repositories listed here:
|
||||||
|
|
||||||
|
https://github.com/Goldziher/tree-sitter-language-pack/blob/main/sources/language_definitions.json
|
||||||
|
|
||||||
|
See this URL for information on the licenses of each repo:
|
||||||
|
|
||||||
|
https://github.com/Goldziher/tree-sitter-language-pack/
|
||||||
5
aider/queries/tree-sitter-language-pack/arduino-tags.scm
Normal file
5
aider/queries/tree-sitter-language-pack/arduino-tags.scm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
(function_declarator
|
||||||
|
declarator: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @name.reference.call) @reference.call
|
||||||
16
aider/queries/tree-sitter-language-pack/chatito-tags.scm
Normal file
16
aider/queries/tree-sitter-language-pack/chatito-tags.scm
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
; Definitions
|
||||||
|
(intent_def
|
||||||
|
(intent) @name.definition.intent) @definition.intent
|
||||||
|
|
||||||
|
(slot_def
|
||||||
|
(slot) @name.definition.slot) @definition.slot
|
||||||
|
|
||||||
|
(alias_def
|
||||||
|
(alias) @name.definition.alias) @definition.alias
|
||||||
|
|
||||||
|
; References
|
||||||
|
(slot_ref
|
||||||
|
(slot) @name.reference.slot) @reference.slot
|
||||||
|
|
||||||
|
(alias_ref
|
||||||
|
(alias) @name.reference.alias) @reference.alias
|
||||||
122
aider/queries/tree-sitter-language-pack/commonlisp-tags.scm
Normal file
122
aider/queries/tree-sitter-language-pack/commonlisp-tags.scm
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;; Function Definitions ;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defun_header
|
||||||
|
function_name: (sym_lit) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;; Function Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;;
|
||||||
|
;;; Basically, we consider every list literal with symbol as the
|
||||||
|
;;; first element to be a call to a function named by that element.
|
||||||
|
;;; But we must exclude some cases. Note, tree-sitter @ignore
|
||||||
|
;;; cases only work if they are declared before the cases
|
||||||
|
;;; we want to include.
|
||||||
|
|
||||||
|
;; Exclude lambda lists for function definitions
|
||||||
|
;; For example:
|
||||||
|
;;
|
||||||
|
;; (defun my-func (arg1 arg2) ...)
|
||||||
|
;;
|
||||||
|
;; do not treat (arg1 arg2) as a call of function arg1
|
||||||
|
;;
|
||||||
|
(defun_header
|
||||||
|
lambda_list: (list_lit . [(sym_lit) (package_lit)] @ignore))
|
||||||
|
|
||||||
|
;; Similar to the above, but for
|
||||||
|
;;
|
||||||
|
;; (defmethod m ((type1 param1) (type2 param2)) ...)
|
||||||
|
;;
|
||||||
|
;; where list literals having symbol as their first element
|
||||||
|
;; are nested inside the lambda list.
|
||||||
|
(defun_header
|
||||||
|
lambda_list: (list_lit (list_lit . [(sym_lit) (package_lit)] @ignore)))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; (let ((var ...) (var2 ...)) ...)
|
||||||
|
;;
|
||||||
|
;; - exclude var, var2
|
||||||
|
;; - the same for let*, flet, labels, macrolet, symbol-macrolet
|
||||||
|
(list_lit . [(sym_lit) (package_lit)] @name.reference.call
|
||||||
|
. (list_lit (list_lit . [(sym_lit) (package_lit)] @ignore))
|
||||||
|
(#match? @name.reference.call
|
||||||
|
"(?i)^(cl:)?(let|let\\*|flet|labels|macrolet|symbol-macrolet)$")
|
||||||
|
)
|
||||||
|
|
||||||
|
;; TODO:
|
||||||
|
;; - exclude also:
|
||||||
|
;; - (defclass name (parent parent2)
|
||||||
|
;; ((slot1 ...)
|
||||||
|
;; (slot2 ...))
|
||||||
|
;; exclude the parent, slot1, slot2
|
||||||
|
;; - (flet ((func-1 (param1 param2))) ...)
|
||||||
|
;; - we already exclude func-1, but param1 is still recognized
|
||||||
|
;; as a function call - exclude it too
|
||||||
|
;; - the same for labels
|
||||||
|
;; - the same macrolet
|
||||||
|
;; - what else?
|
||||||
|
;; (that's a non-goal to completely support all macros
|
||||||
|
;; and special operators, but every one we support
|
||||||
|
;; makes the solution a little bit better)
|
||||||
|
;; - (flet ((func-1 (param1 param2))) ...)
|
||||||
|
;; - instead of simply excluding it, as we do today,
|
||||||
|
;; tag func-1 as @local.definition.function (I suppose)
|
||||||
|
;; - the same for labels, macrolet
|
||||||
|
;; - @local.scope for let, let*, flet, labels, macrolet
|
||||||
|
;; - I guess the whole span of the scope text,
|
||||||
|
;; till the closing paren, should be tagged as @local.scope;
|
||||||
|
;; Hopefully, combined with @local.definition.function
|
||||||
|
;; within the scope, the usual @reference.call within
|
||||||
|
;; that scope will refer to the local definition,
|
||||||
|
;; and there will be no need to use @local.reference.call
|
||||||
|
;; (which is more difficult to implement).
|
||||||
|
;; - When implementing, remember the scope rules differences
|
||||||
|
;; of let vs let*, flet vs labels.
|
||||||
|
|
||||||
|
|
||||||
|
;; Include all other cases - list literal with symbol as the
|
||||||
|
;; first element
|
||||||
|
(list_lit . [(sym_lit) (package_lit)] @name.reference.call) @reference.call
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;; classes
|
||||||
|
|
||||||
|
(list_lit . [(sym_lit) (package_lit)] @ignore
|
||||||
|
. [(sym_lit) (package_lit)] @name.definition.class
|
||||||
|
(#match? @ignore "(?i)^(cl:)?defclass$")
|
||||||
|
) @definition.class
|
||||||
|
|
||||||
|
(list_lit . [(sym_lit) (package_lit)] @ignore
|
||||||
|
. (quoting_lit [(sym_lit) (package_lit)] @name.reference.class)
|
||||||
|
(#match? @ignore "(?i)^(cl:)?make-instance$")
|
||||||
|
) @reference.class
|
||||||
|
|
||||||
|
;;; TODO:
|
||||||
|
;; - @reference.class for base classes
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;;; TODO:
|
||||||
|
;; - Symbols referenced in defpackage
|
||||||
|
;;
|
||||||
|
;; (defpackage ...
|
||||||
|
;; (:export (symbol-a :symbol-b #:symbol-c "SYMBOL-D")))
|
||||||
|
;;
|
||||||
|
;; The goal is to allow quick navigation from the API
|
||||||
|
;; overview in the form of defpackage, to the definition
|
||||||
|
;; where user can read parameters, docstring, etc.
|
||||||
|
;; - The @name must not include the colon, or sharpsign colon, quotes,
|
||||||
|
;; just symbol-a, symbol-b, symbol-c, sybmol-d
|
||||||
|
;; - Downcase the names specified as string literals?
|
||||||
|
;; ("SYMBOL-D" -> symbol-d)
|
||||||
|
;; - We don't know if the exported symbol is a function, variable,
|
||||||
|
;; class or something else. The official doc
|
||||||
|
;; (https://tree-sitter.github.io/tree-sitter/code-navigation-systems)
|
||||||
|
;; does not even suggest a tag for variable reference.
|
||||||
|
;; (Although in practice, the `tree-sitter tags` command
|
||||||
|
;; allows any @reference.* and @definition.* tags)
|
||||||
|
;; Probably it's better to just use @reference.call for all
|
||||||
|
;; the symbols in the :export clause.
|
||||||
|
;;
|
||||||
|
;; - The same for the export function call:
|
||||||
|
;;
|
||||||
|
;; (export '(symbol-a :symbol-b #:symbol-c "SYMBOL-D"))
|
||||||
15
aider/queries/tree-sitter-language-pack/cpp-tags.scm
Normal file
15
aider/queries/tree-sitter-language-pack/cpp-tags.scm
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class
|
||||||
|
|
||||||
|
(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class
|
||||||
|
|
||||||
|
(function_declarator declarator: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(function_declarator declarator: (field_identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @local.scope name: (identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(type_definition declarator: (type_identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(enum_specifier name: (type_identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(class_specifier name: (type_identifier) @name.definition.class) @definition.class
|
||||||
26
aider/queries/tree-sitter-language-pack/csharp-tags.scm
Normal file
26
aider/queries/tree-sitter-language-pack/csharp-tags.scm
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
; Based on https://github.com/tree-sitter/tree-sitter-c-sharp/blob/master/queries/tags.scm
|
||||||
|
; MIT License.
|
||||||
|
|
||||||
|
(class_declaration name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(class_declaration (base_list (_) @name.reference.class)) @reference.class
|
||||||
|
|
||||||
|
(interface_declaration name: (identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
(interface_declaration (base_list (_) @name.reference.interface)) @reference.interface
|
||||||
|
|
||||||
|
(method_declaration name: (identifier) @name.definition.method) @definition.method
|
||||||
|
|
||||||
|
(object_creation_expression type: (identifier) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
(type_parameter_constraints_clause (identifier) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
(type_parameter_constraint (type type: (identifier) @name.reference.class)) @reference.class
|
||||||
|
|
||||||
|
(variable_declaration type: (identifier) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
(invocation_expression function: (member_access_expression name: (identifier) @name.reference.send)) @reference.send
|
||||||
|
|
||||||
|
(namespace_declaration name: (identifier) @name.definition.module) @definition.module
|
||||||
|
|
||||||
|
(namespace_declaration name: (identifier) @name.definition.module) @module
|
||||||
26
aider/queries/tree-sitter-language-pack/d-tags.scm
Normal file
26
aider/queries/tree-sitter-language-pack/d-tags.scm
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
(module_def (module_declaration (module_fqn) @name.definition.module)) @definition.module
|
||||||
|
|
||||||
|
(struct_declaration (struct) . (identifier) @name.definition.class) @definition.class
|
||||||
|
(interface_declaration (interface) . (identifier) @name.definition.interface) @definition.interface
|
||||||
|
(enum_declaration (enum) . (identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(class_declaration (class) . (identifier) @name.definition.class) @definition.class
|
||||||
|
(constructor (this) @name.definition.method) @definition.method
|
||||||
|
(destructor (this) @name.definition.method) @definition.method
|
||||||
|
(postblit (this) @name.definition.method) @definition.method
|
||||||
|
|
||||||
|
(manifest_declarator . (identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(function_declaration (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(union_declaration (union) . (identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(anonymous_enum_declaration (enum_member . (identifier) @name.definition.constant)) @definition.constant
|
||||||
|
|
||||||
|
(enum_declaration (enum_member . (identifier) @name.definition.constant)) @definition.constant
|
||||||
|
|
||||||
|
(call_expression (identifier) @name.reference.call) @reference.call
|
||||||
|
(call_expression (type (template_instance (identifier) @name.reference.call))) @reference.call
|
||||||
|
(parameter (type (identifier) @name.reference.class) @reference.class (identifier))
|
||||||
|
|
||||||
|
(variable_declaration (type (identifier) @name.reference.class) @reference.class (declarator))
|
||||||
92
aider/queries/tree-sitter-language-pack/dart-tags.scm
Normal file
92
aider/queries/tree-sitter-language-pack/dart-tags.scm
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
(class_definition
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(function_signature)) @definition.method
|
||||||
|
|
||||||
|
(type_alias
|
||||||
|
(type_identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(getter_signature
|
||||||
|
name: (identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(setter_signature
|
||||||
|
name: (identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(function_signature
|
||||||
|
name: (identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(factory_constructor_signature
|
||||||
|
(identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(constructor_signature
|
||||||
|
name: (identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(method_signature
|
||||||
|
(operator_signature)) @definition.method
|
||||||
|
|
||||||
|
(method_signature) @definition.method
|
||||||
|
|
||||||
|
(mixin_declaration
|
||||||
|
(mixin)
|
||||||
|
(identifier) @name.definition.mixin) @definition.mixin
|
||||||
|
|
||||||
|
(extension_declaration
|
||||||
|
name: (identifier) @name.definition.extension) @definition.extension
|
||||||
|
|
||||||
|
|
||||||
|
(new_expression
|
||||||
|
(type_identifier) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
(enum_declaration
|
||||||
|
name: (identifier) @name.definition.enum) @definition.enum
|
||||||
|
|
||||||
|
(function_signature
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(initialized_variable_definition
|
||||||
|
name: (identifier)
|
||||||
|
value: (identifier) @name.reference.class
|
||||||
|
value: (selector
|
||||||
|
"!"?
|
||||||
|
(argument_part
|
||||||
|
(arguments
|
||||||
|
(argument)*))?)?) @reference.class
|
||||||
|
|
||||||
|
(assignment_expression
|
||||||
|
left: (assignable_expression
|
||||||
|
(identifier)
|
||||||
|
(unconditional_assignable_selector
|
||||||
|
"."
|
||||||
|
(identifier) @name.reference.send))) @reference.call
|
||||||
|
|
||||||
|
(assignment_expression
|
||||||
|
left: (assignable_expression
|
||||||
|
(identifier)
|
||||||
|
(conditional_assignable_selector
|
||||||
|
"?."
|
||||||
|
(identifier) @name.reference.send))) @reference.call
|
||||||
|
|
||||||
|
((identifier) @name.reference.send
|
||||||
|
(selector
|
||||||
|
"!"?
|
||||||
|
(conditional_assignable_selector
|
||||||
|
"?." (identifier) @name.reference.send)?
|
||||||
|
(unconditional_assignable_selector
|
||||||
|
"."? (identifier) @name.reference.send)?
|
||||||
|
(argument_part
|
||||||
|
(arguments
|
||||||
|
(argument)*))?)*
|
||||||
|
(cascade_section
|
||||||
|
(cascade_selector
|
||||||
|
(identifier)) @name.reference.send
|
||||||
|
(argument_part
|
||||||
|
(arguments
|
||||||
|
(argument)*))?)?) @reference.call
|
||||||
|
|
||||||
5
aider/queries/tree-sitter-language-pack/elisp-tags.scm
Normal file
5
aider/queries/tree-sitter-language-pack/elisp-tags.scm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
;; defun/defsubst
|
||||||
|
(function_definition name: (symbol) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
;; Treat macros as function definitions for the sake of TAGS.
|
||||||
|
(macro_definition name: (symbol) @name.definition.function) @definition.function
|
||||||
54
aider/queries/tree-sitter-language-pack/elixir-tags.scm
Normal file
54
aider/queries/tree-sitter-language-pack/elixir-tags.scm
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
; Definitions
|
||||||
|
|
||||||
|
; * modules and protocols
|
||||||
|
(call
|
||||||
|
target: (identifier) @ignore
|
||||||
|
(arguments (alias) @name.definition.module)
|
||||||
|
(#any-of? @ignore "defmodule" "defprotocol")) @definition.module
|
||||||
|
|
||||||
|
; * functions/macros
|
||||||
|
(call
|
||||||
|
target: (identifier) @ignore
|
||||||
|
(arguments
|
||||||
|
[
|
||||||
|
; zero-arity functions with no parentheses
|
||||||
|
(identifier) @name.definition.function
|
||||||
|
; regular function clause
|
||||||
|
(call target: (identifier) @name.definition.function)
|
||||||
|
; function clause with a guard clause
|
||||||
|
(binary_operator
|
||||||
|
left: (call target: (identifier) @name.definition.function)
|
||||||
|
operator: "when")
|
||||||
|
])
|
||||||
|
(#any-of? @ignore "def" "defp" "defdelegate" "defguard" "defguardp" "defmacro" "defmacrop" "defn" "defnp")) @definition.function
|
||||||
|
|
||||||
|
; References
|
||||||
|
|
||||||
|
; ignore calls to kernel/special-forms keywords
|
||||||
|
(call
|
||||||
|
target: (identifier) @ignore
|
||||||
|
(#any-of? @ignore "def" "defp" "defdelegate" "defguard" "defguardp" "defmacro" "defmacrop" "defn" "defnp" "defmodule" "defprotocol" "defimpl" "defstruct" "defexception" "defoverridable" "alias" "case" "cond" "else" "for" "if" "import" "quote" "raise" "receive" "require" "reraise" "super" "throw" "try" "unless" "unquote" "unquote_splicing" "use" "with"))
|
||||||
|
|
||||||
|
; ignore module attributes
|
||||||
|
(unary_operator
|
||||||
|
operator: "@"
|
||||||
|
operand: (call
|
||||||
|
target: (identifier) @ignore))
|
||||||
|
|
||||||
|
; * function call
|
||||||
|
(call
|
||||||
|
target: [
|
||||||
|
; local
|
||||||
|
(identifier) @name.reference.call
|
||||||
|
; remote
|
||||||
|
(dot
|
||||||
|
right: (identifier) @name.reference.call)
|
||||||
|
]) @reference.call
|
||||||
|
|
||||||
|
; * pipe into function call
|
||||||
|
(binary_operator
|
||||||
|
operator: "|>"
|
||||||
|
right: (identifier) @name.reference.call) @reference.call
|
||||||
|
|
||||||
|
; * modules
|
||||||
|
(alias) @name.reference.module @reference.module
|
||||||
19
aider/queries/tree-sitter-language-pack/elm-tags.scm
Normal file
19
aider/queries/tree-sitter-language-pack/elm-tags.scm
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
(value_declaration (function_declaration_left (lower_case_identifier) @name.definition.function)) @definition.function
|
||||||
|
|
||||||
|
(function_call_expr (value_expr (value_qid) @name.reference.function)) @reference.function
|
||||||
|
(exposed_value (lower_case_identifier) @name.reference.function) @reference.function
|
||||||
|
(type_annotation ((lower_case_identifier) @name.reference.function) (colon)) @reference.function
|
||||||
|
|
||||||
|
(type_declaration ((upper_case_identifier) @name.definition.type) ) @definition.type
|
||||||
|
|
||||||
|
(type_ref (upper_case_qid (upper_case_identifier) @name.reference.type)) @reference.type
|
||||||
|
(exposed_type (upper_case_identifier) @name.reference.type) @reference.type
|
||||||
|
|
||||||
|
(type_declaration (union_variant (upper_case_identifier) @name.definition.union)) @definition.union
|
||||||
|
|
||||||
|
(value_expr (upper_case_qid (upper_case_identifier) @name.reference.union)) @reference.union
|
||||||
|
|
||||||
|
|
||||||
|
(module_declaration
|
||||||
|
(upper_case_qid (upper_case_identifier)) @name.definition.module
|
||||||
|
) @definition.module
|
||||||
41
aider/queries/tree-sitter-language-pack/gleam-tags.scm
Normal file
41
aider/queries/tree-sitter-language-pack/gleam-tags.scm
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
; Modules
|
||||||
|
(module) @name.reference.module @reference.module
|
||||||
|
(import alias: (identifier) @name.reference.module) @reference.module
|
||||||
|
(remote_type_identifier
|
||||||
|
module: (identifier) @name.reference.module) @reference.module
|
||||||
|
((field_access
|
||||||
|
record: (identifier) @name.reference.module)
|
||||||
|
(#is-not? local)) @reference.module
|
||||||
|
|
||||||
|
; Functions
|
||||||
|
(function
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
(external_function
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
(unqualified_import (identifier) @name.reference.function) @reference.function
|
||||||
|
((function_call
|
||||||
|
function: (identifier) @name.reference.function) @reference.function
|
||||||
|
(#is-not? local))
|
||||||
|
((field_access
|
||||||
|
record: (identifier) @ignore
|
||||||
|
field: (label) @name.reference.function)
|
||||||
|
(#is-not? local)) @reference.function
|
||||||
|
((binary_expression
|
||||||
|
operator: "|>"
|
||||||
|
right: (identifier) @name.reference.function)
|
||||||
|
(#is-not? local)) @reference.function
|
||||||
|
|
||||||
|
; Types
|
||||||
|
(type_definition
|
||||||
|
(type_name
|
||||||
|
name: (type_identifier) @name.definition.type)) @definition.type
|
||||||
|
(type_definition
|
||||||
|
(data_constructors
|
||||||
|
(data_constructor
|
||||||
|
name: (constructor_name) @name.definition.constructor))) @definition.constructor
|
||||||
|
(external_type
|
||||||
|
(type_name
|
||||||
|
name: (type_identifier) @name.definition.type)) @definition.type
|
||||||
|
|
||||||
|
(type_identifier) @name.reference.type @reference.type
|
||||||
|
(constructor_name) @name.reference.constructor @reference.constructor
|
||||||
42
aider/queries/tree-sitter-language-pack/go-tags.scm
Normal file
42
aider/queries/tree-sitter-language-pack/go-tags.scm
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
(function_declaration
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
(#strip! @doc "^//\\s*")
|
||||||
|
(#set-adjacent! @doc @definition.function)
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
(method_declaration
|
||||||
|
name: (field_identifier) @name.definition.method) @definition.method
|
||||||
|
(#strip! @doc "^//\\s*")
|
||||||
|
(#set-adjacent! @doc @definition.method)
|
||||||
|
)
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: [
|
||||||
|
(identifier) @name.reference.call
|
||||||
|
(parenthesized_expression (identifier) @name.reference.call)
|
||||||
|
(selector_expression field: (field_identifier) @name.reference.call)
|
||||||
|
(parenthesized_expression (selector_expression field: (field_identifier) @name.reference.call))
|
||||||
|
]) @reference.call
|
||||||
|
|
||||||
|
(type_spec
|
||||||
|
name: (type_identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(type_identifier) @name.reference.type @reference.type
|
||||||
|
|
||||||
|
(package_clause "package" (package_identifier) @name.definition.module)
|
||||||
|
|
||||||
|
(type_declaration (type_spec name: (type_identifier) @name.definition.interface type: (interface_type)))
|
||||||
|
|
||||||
|
(type_declaration (type_spec name: (type_identifier) @name.definition.class type: (struct_type)))
|
||||||
|
|
||||||
|
(import_declaration (import_spec) @name.reference.module)
|
||||||
|
|
||||||
|
(var_declaration (var_spec name: (identifier) @name.definition.variable))
|
||||||
|
|
||||||
|
(const_declaration (const_spec name: (identifier) @name.definition.constant))
|
||||||
20
aider/queries/tree-sitter-language-pack/java-tags.scm
Normal file
20
aider/queries/tree-sitter-language-pack/java-tags.scm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
(class_declaration
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(method_declaration
|
||||||
|
name: (identifier) @name.definition.method) @definition.method
|
||||||
|
|
||||||
|
(method_invocation
|
||||||
|
name: (identifier) @name.reference.method
|
||||||
|
arguments: (argument_list) @reference.call)
|
||||||
|
|
||||||
|
(interface_declaration
|
||||||
|
name: (identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
(type_list
|
||||||
|
(type_identifier) @name.reference.interface) @reference.implementation
|
||||||
|
|
||||||
|
(object_creation_expression
|
||||||
|
type: (type_identifier) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
(superclass (type_identifier) @name.reference.class) @reference.class
|
||||||
88
aider/queries/tree-sitter-language-pack/javascript-tags.scm
Normal file
88
aider/queries/tree-sitter-language-pack/javascript-tags.scm
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
(method_definition
|
||||||
|
name: (property_identifier) @name.definition.method) @definition.method
|
||||||
|
(#not-eq? @name.definition.method "constructor")
|
||||||
|
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
|
||||||
|
(#select-adjacent! @doc @definition.method)
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(class
|
||||||
|
name: (_) @name.definition.class)
|
||||||
|
(class_declaration
|
||||||
|
name: (_) @name.definition.class)
|
||||||
|
] @definition.class
|
||||||
|
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
|
||||||
|
(#select-adjacent! @doc @definition.class)
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(function_expression
|
||||||
|
name: (identifier) @name.definition.function)
|
||||||
|
(function_declaration
|
||||||
|
name: (identifier) @name.definition.function)
|
||||||
|
(generator_function
|
||||||
|
name: (identifier) @name.definition.function)
|
||||||
|
(generator_function_declaration
|
||||||
|
name: (identifier) @name.definition.function)
|
||||||
|
] @definition.function
|
||||||
|
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
|
||||||
|
(#select-adjacent! @doc @definition.function)
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
(lexical_declaration
|
||||||
|
(variable_declarator
|
||||||
|
name: (identifier) @name.definition.function
|
||||||
|
value: [(arrow_function) (function_expression)]) @definition.function)
|
||||||
|
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
|
||||||
|
(#select-adjacent! @doc @definition.function)
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
(variable_declaration
|
||||||
|
(variable_declarator
|
||||||
|
name: (identifier) @name.definition.function
|
||||||
|
value: [(arrow_function) (function_expression)]) @definition.function)
|
||||||
|
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
|
||||||
|
(#select-adjacent! @doc @definition.function)
|
||||||
|
)
|
||||||
|
|
||||||
|
(assignment_expression
|
||||||
|
left: [
|
||||||
|
(identifier) @name.definition.function
|
||||||
|
(member_expression
|
||||||
|
property: (property_identifier) @name.definition.function)
|
||||||
|
]
|
||||||
|
right: [(arrow_function) (function_expression)]
|
||||||
|
) @definition.function
|
||||||
|
|
||||||
|
(pair
|
||||||
|
key: (property_identifier) @name.definition.function
|
||||||
|
value: [(arrow_function) (function_expression)]) @definition.function
|
||||||
|
|
||||||
|
(
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @name.reference.call) @reference.call
|
||||||
|
(#not-match? @name.reference.call "^(require)$")
|
||||||
|
)
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (member_expression
|
||||||
|
property: (property_identifier) @name.reference.call)
|
||||||
|
arguments: (_) @reference.call)
|
||||||
|
|
||||||
|
(new_expression
|
||||||
|
constructor: (_) @name.reference.class) @reference.class
|
||||||
34
aider/queries/tree-sitter-language-pack/lua-tags.scm
Normal file
34
aider/queries/tree-sitter-language-pack/lua-tags.scm
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
(function_declaration
|
||||||
|
name: [
|
||||||
|
(identifier) @name.definition.function
|
||||||
|
(dot_index_expression
|
||||||
|
field: (identifier) @name.definition.function)
|
||||||
|
]) @definition.function
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
name: (method_index_expression
|
||||||
|
method: (identifier) @name.definition.method)) @definition.method
|
||||||
|
|
||||||
|
(assignment_statement
|
||||||
|
(variable_list .
|
||||||
|
name: [
|
||||||
|
(identifier) @name.definition.function
|
||||||
|
(dot_index_expression
|
||||||
|
field: (identifier) @name.definition.function)
|
||||||
|
])
|
||||||
|
(expression_list .
|
||||||
|
value: (function_definition))) @definition.function
|
||||||
|
|
||||||
|
(table_constructor
|
||||||
|
(field
|
||||||
|
name: (identifier) @name.definition.function
|
||||||
|
value: (function_definition))) @definition.function
|
||||||
|
|
||||||
|
(function_call
|
||||||
|
name: [
|
||||||
|
(identifier) @name.reference.call
|
||||||
|
(dot_index_expression
|
||||||
|
field: (identifier) @name.reference.call)
|
||||||
|
(method_index_expression
|
||||||
|
method: (identifier) @name.reference.method)
|
||||||
|
]) @reference.call
|
||||||
39
aider/queries/tree-sitter-language-pack/pony-tags.scm
Normal file
39
aider/queries/tree-sitter-language-pack/pony-tags.scm
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
;Class definitions @definition.class
|
||||||
|
;Function definitions @definition.function
|
||||||
|
;Interface definitions @definition.interface
|
||||||
|
;Method definitions @definition.method
|
||||||
|
;Module definitions @definition.module
|
||||||
|
;Function/method calls @reference.call
|
||||||
|
;Class reference @reference.class
|
||||||
|
;Interface implementation @reference.implementation
|
||||||
|
(
|
||||||
|
(identifier) @reference.class
|
||||||
|
(#match? @reference.class "^_*[A-Z][a-zA-Z0-9_]*$")
|
||||||
|
)
|
||||||
|
|
||||||
|
(class_definition (identifier) @name.definition.class) @definition.class
|
||||||
|
(actor_definition (identifier) @name.definition.class) @definition.class
|
||||||
|
(primitive_definition (identifier) @name.definition.class) @definition.class
|
||||||
|
(struct_definition (identifier) @name.definition.class) @definition.class
|
||||||
|
(type_alias (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(trait_definition (identifier) @name.definition.interface) @definition.interface
|
||||||
|
(interface_definition (identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
(constructor (identifier) @name.definition.method) @definition.method
|
||||||
|
(method (identifier) @name.definition.method) @definition.method
|
||||||
|
(behavior (identifier) @name.definition.method) @definition.method
|
||||||
|
|
||||||
|
(class_definition (type) @name.reference.implementation) @reference.implementation
|
||||||
|
(actor_definition (type) @name.reference.implementation) @reference.implementation
|
||||||
|
(primitive_definition (type) @name.reference.implementation) @reference.implementation
|
||||||
|
(struct_definition (type) @name.reference.implementation) @reference.implementation
|
||||||
|
(type_alias (type) @name.reference.implementation) @reference.implementation
|
||||||
|
|
||||||
|
; calls - not catching all possible call cases of callees for capturing the method name
|
||||||
|
(call_expression callee: [(identifier) (ffi_identifier)] @name.reference.call) @reference.call
|
||||||
|
(call_expression callee: (generic_expression [(identifier) (ffi_identifier)] @name.reference.call)) @reference.call
|
||||||
|
(call_expression callee: (member_expression (identifier) @name.reference.call .)) @reference.call
|
||||||
|
(call_expression callee: (member_expression (generic_expression [(identifier) (ffi_identifier)] @name.reference.call) .)) @reference.call
|
||||||
|
; TODO: add more possible callee expressions
|
||||||
|
(call_expression) @reference.call
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
(property
|
||||||
|
(key) @name.definition.property) @definition.property
|
||||||
|
|
||||||
|
(substitution
|
||||||
|
(key) @name.reference.property) @reference.property
|
||||||
14
aider/queries/tree-sitter-language-pack/python-tags.scm
Normal file
14
aider/queries/tree-sitter-language-pack/python-tags.scm
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
(module (expression_statement (assignment left: (identifier) @name.definition.constant) @definition.constant))
|
||||||
|
|
||||||
|
(class_definition
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(function_definition
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(call
|
||||||
|
function: [
|
||||||
|
(identifier) @name.reference.call
|
||||||
|
(attribute
|
||||||
|
attribute: (identifier) @name.reference.call)
|
||||||
|
]) @reference.call
|
||||||
21
aider/queries/tree-sitter-language-pack/r-tags.scm
Normal file
21
aider/queries/tree-sitter-language-pack/r-tags.scm
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
(binary_operator
|
||||||
|
lhs: (identifier) @name.definition.function
|
||||||
|
operator: "<-"
|
||||||
|
rhs: (function_definition)
|
||||||
|
) @definition.function
|
||||||
|
|
||||||
|
(binary_operator
|
||||||
|
lhs: (identifier) @name.definition.function
|
||||||
|
operator: "="
|
||||||
|
rhs: (function_definition)
|
||||||
|
) @definition.function
|
||||||
|
|
||||||
|
(call
|
||||||
|
function: (identifier) @name.reference.call
|
||||||
|
) @reference.call
|
||||||
|
|
||||||
|
(call
|
||||||
|
function: (namespace_operator
|
||||||
|
rhs: (identifier) @name.reference.call
|
||||||
|
)
|
||||||
|
) @reference.call
|
||||||
12
aider/queries/tree-sitter-language-pack/racket-tags.scm
Normal file
12
aider/queries/tree-sitter-language-pack/racket-tags.scm
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
(list
|
||||||
|
.
|
||||||
|
(symbol) @reference._define
|
||||||
|
(#match? @reference._define "^(define|define/contract)$")
|
||||||
|
.
|
||||||
|
(list
|
||||||
|
.
|
||||||
|
(symbol) @name.definition.function) @definition.function)
|
||||||
|
|
||||||
|
(list
|
||||||
|
.
|
||||||
|
(symbol) @name.reference.call)
|
||||||
60
aider/queries/tree-sitter-language-pack/rust-tags.scm
Normal file
60
aider/queries/tree-sitter-language-pack/rust-tags.scm
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
; ADT definitions
|
||||||
|
|
||||||
|
(struct_item
|
||||||
|
name: (type_identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(enum_item
|
||||||
|
name: (type_identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(union_item
|
||||||
|
name: (type_identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
; type aliases
|
||||||
|
|
||||||
|
(type_item
|
||||||
|
name: (type_identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
; method definitions
|
||||||
|
|
||||||
|
(declaration_list
|
||||||
|
(function_item
|
||||||
|
name: (identifier) @name.definition.method) @definition.method)
|
||||||
|
|
||||||
|
; function definitions
|
||||||
|
|
||||||
|
(function_item
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
; trait definitions
|
||||||
|
(trait_item
|
||||||
|
name: (type_identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
; module definitions
|
||||||
|
(mod_item
|
||||||
|
name: (identifier) @name.definition.module) @definition.module
|
||||||
|
|
||||||
|
; macro definitions
|
||||||
|
|
||||||
|
(macro_definition
|
||||||
|
name: (identifier) @name.definition.macro) @definition.macro
|
||||||
|
|
||||||
|
; references
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (identifier) @name.reference.call) @reference.call
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
function: (field_expression
|
||||||
|
field: (field_identifier) @name.reference.call)) @reference.call
|
||||||
|
|
||||||
|
(macro_invocation
|
||||||
|
macro: (identifier) @name.reference.call) @reference.call
|
||||||
|
|
||||||
|
; implementations
|
||||||
|
|
||||||
|
(impl_item
|
||||||
|
trait: (type_identifier) @name.reference.implementation) @reference.implementation
|
||||||
|
|
||||||
|
(impl_item
|
||||||
|
type: (type_identifier) @name.reference.implementation
|
||||||
|
!trait) @reference.implementation
|
||||||
43
aider/queries/tree-sitter-language-pack/solidity-tags.scm
Normal file
43
aider/queries/tree-sitter-language-pack/solidity-tags.scm
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
;; Method and Function declarations
|
||||||
|
(contract_declaration (_
|
||||||
|
(function_definition
|
||||||
|
name: (identifier) @name.definition.function) @definition.method))
|
||||||
|
|
||||||
|
(source_file
|
||||||
|
(function_definition
|
||||||
|
name: (identifier) @name.definition.function) @definition.function)
|
||||||
|
|
||||||
|
;; Contract, struct, enum and interface declarations
|
||||||
|
(contract_declaration
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(interface_declaration
|
||||||
|
name: (identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
(library_declaration
|
||||||
|
name: (identifier) @name.definition.class) @definition.interface
|
||||||
|
|
||||||
|
(struct_declaration name: (identifier) @name.definition.class) @definition.class
|
||||||
|
(enum_declaration name: (identifier) @name.definition.class) @definition.class
|
||||||
|
(event_definition name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
;; Function calls
|
||||||
|
(call_expression (expression (identifier)) @name.reference.call ) @reference.call
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
(expression (member_expression
|
||||||
|
property: (_) @name.reference.method ))) @reference.call
|
||||||
|
|
||||||
|
;; Log emit
|
||||||
|
(emit_statement name: (_) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
|
||||||
|
;; Inheritance
|
||||||
|
|
||||||
|
(inheritance_specifier
|
||||||
|
ancestor: (user_defined_type (_) @name.reference.class . )) @reference.class
|
||||||
|
|
||||||
|
|
||||||
|
;; Imports ( note that unknown is not standardised )
|
||||||
|
(import_directive
|
||||||
|
import_name: (_) @name.reference.module ) @reference.unknown
|
||||||
51
aider/queries/tree-sitter-language-pack/swift-tags.scm
Normal file
51
aider/queries/tree-sitter-language-pack/swift-tags.scm
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
(class_declaration
|
||||||
|
name: (type_identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(protocol_declaration
|
||||||
|
name: (type_identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
(class_declaration
|
||||||
|
(class_body
|
||||||
|
[
|
||||||
|
(function_declaration
|
||||||
|
name: (simple_identifier) @name.definition.method
|
||||||
|
)
|
||||||
|
(subscript_declaration
|
||||||
|
(parameter (simple_identifier) @name.definition.method)
|
||||||
|
)
|
||||||
|
(init_declaration "init" @name.definition.method)
|
||||||
|
(deinit_declaration "deinit" @name.definition.method)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
) @definition.method
|
||||||
|
|
||||||
|
(protocol_declaration
|
||||||
|
(protocol_body
|
||||||
|
[
|
||||||
|
(protocol_function_declaration
|
||||||
|
name: (simple_identifier) @name.definition.method
|
||||||
|
)
|
||||||
|
(subscript_declaration
|
||||||
|
(parameter (simple_identifier) @name.definition.method)
|
||||||
|
)
|
||||||
|
(init_declaration "init" @name.definition.method)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
) @definition.method
|
||||||
|
|
||||||
|
(class_declaration
|
||||||
|
(class_body
|
||||||
|
[
|
||||||
|
(property_declaration
|
||||||
|
(pattern (simple_identifier) @name.definition.property)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
) @definition.property
|
||||||
|
|
||||||
|
(property_declaration
|
||||||
|
(pattern (simple_identifier) @name.definition.property)
|
||||||
|
) @definition.property
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
name: (simple_identifier) @name.definition.function) @definition.function
|
||||||
20
aider/queries/tree-sitter-language-pack/udev-tags.scm
Normal file
20
aider/queries/tree-sitter-language-pack/udev-tags.scm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
(assignment
|
||||||
|
key: "LABEL"
|
||||||
|
(value
|
||||||
|
(content) @name.definition.label)) @definition.label
|
||||||
|
|
||||||
|
(assignment
|
||||||
|
key: "GOTO"
|
||||||
|
(value
|
||||||
|
(content) @name.reference.label)) @reference.label
|
||||||
|
|
||||||
|
(assignment
|
||||||
|
key: "ENV"
|
||||||
|
(env_var) @name.definition.variable) @definition.variable
|
||||||
|
|
||||||
|
(match
|
||||||
|
key: "ENV"
|
||||||
|
(env_var) @name.reference.variable) @reference.variable
|
||||||
|
|
||||||
|
(var_sub
|
||||||
|
(env_var) @name.reference.variable) @reference.variable
|
||||||
9
aider/queries/tree-sitter-languages/c-tags.scm
Normal file
9
aider/queries/tree-sitter-languages/c-tags.scm
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class
|
||||||
|
|
||||||
|
(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class
|
||||||
|
|
||||||
|
(function_declarator declarator: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(type_definition declarator: (type_identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(enum_specifier name: (type_identifier) @name.definition.type) @definition.type
|
||||||
77
aider/queries/tree-sitter-languages/hcl-tags.scm
Normal file
77
aider/queries/tree-sitter-languages/hcl-tags.scm
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
;; Based on https://github.com/tree-sitter-grammars/tree-sitter-hcl/blob/main/make_grammar.js
|
||||||
|
;; Which has Apache 2.0 License
|
||||||
|
;; tags.scm for Terraform (tree-sitter-hcl)
|
||||||
|
|
||||||
|
; === Definitions: Terraform Blocks ===
|
||||||
|
(block
|
||||||
|
(identifier) @block_type
|
||||||
|
(string_lit (template_literal) @resource_type)
|
||||||
|
(string_lit (template_literal) @name.definition.resource)
|
||||||
|
(body) @definition.resource
|
||||||
|
) (#eq? @block_type "resource")
|
||||||
|
|
||||||
|
(block
|
||||||
|
(identifier) @block_type
|
||||||
|
(string_lit (template_literal) @name.definition.module)
|
||||||
|
(body) @definition.module
|
||||||
|
) (#eq? @block_type "module")
|
||||||
|
|
||||||
|
(block
|
||||||
|
(identifier) @block_type
|
||||||
|
(string_lit (template_literal) @name.definition.variable)
|
||||||
|
(body) @definition.variable
|
||||||
|
) (#eq? @block_type "variable")
|
||||||
|
|
||||||
|
(block
|
||||||
|
(identifier) @block_type
|
||||||
|
(string_lit (template_literal) @name.definition.output)
|
||||||
|
(body) @definition.output
|
||||||
|
) (#eq? @block_type "output")
|
||||||
|
|
||||||
|
(block
|
||||||
|
(identifier) @block_type
|
||||||
|
(string_lit (template_literal) @name.definition.provider)
|
||||||
|
(body) @definition.provider
|
||||||
|
) (#eq? @block_type "provider")
|
||||||
|
|
||||||
|
(block
|
||||||
|
(identifier) @block_type
|
||||||
|
(body
|
||||||
|
(attribute
|
||||||
|
(identifier) @name.definition.local
|
||||||
|
(expression) @definition.local
|
||||||
|
)+
|
||||||
|
)
|
||||||
|
) (#eq? @block_type "locals")
|
||||||
|
|
||||||
|
; === References: Variables, Locals, Modules, Data, Resources ===
|
||||||
|
((variable_expr) @ref_type
|
||||||
|
(get_attr (identifier) @name.reference.variable)
|
||||||
|
) @reference.variable
|
||||||
|
(#eq? @ref_type "var")
|
||||||
|
|
||||||
|
((variable_expr) @ref_type
|
||||||
|
(get_attr (identifier) @name.reference.local)
|
||||||
|
) @reference.local
|
||||||
|
(#eq? @ref_type "local")
|
||||||
|
|
||||||
|
((variable_expr) @ref_type
|
||||||
|
(get_attr (identifier) @name.reference.module)
|
||||||
|
) @reference.module
|
||||||
|
(#eq? @ref_type "module")
|
||||||
|
|
||||||
|
((variable_expr) @ref_type
|
||||||
|
(get_attr (identifier) @data_source_type)
|
||||||
|
(get_attr (identifier) @name.reference.data)
|
||||||
|
) @reference.data
|
||||||
|
(#eq? @ref_type "data")
|
||||||
|
|
||||||
|
((variable_expr) @resource_type
|
||||||
|
(get_attr (identifier) @name.reference.resource)
|
||||||
|
) @reference.resource
|
||||||
|
(#not-eq? @resource_type "var")
|
||||||
|
(#not-eq? @resource_type "local")
|
||||||
|
(#not-eq? @resource_type "module")
|
||||||
|
(#not-eq? @resource_type "data")
|
||||||
|
(#not-eq? @resource_type "provider")
|
||||||
|
(#not-eq? @resource_type "output")
|
||||||
27
aider/queries/tree-sitter-languages/kotlin-tags.scm
Normal file
27
aider/queries/tree-sitter-languages/kotlin-tags.scm
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
; Definitions
|
||||||
|
|
||||||
|
(class_declaration
|
||||||
|
(type_identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(function_declaration
|
||||||
|
(simple_identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(object_declaration
|
||||||
|
(type_identifier) @name.definition.object) @definition.object
|
||||||
|
|
||||||
|
; References
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
[
|
||||||
|
(simple_identifier) @name.reference.call
|
||||||
|
(navigation_expression
|
||||||
|
(navigation_suffix
|
||||||
|
(simple_identifier) @name.reference.call))
|
||||||
|
]) @reference.call
|
||||||
|
|
||||||
|
(delegation_specifier
|
||||||
|
[
|
||||||
|
(user_type) @name.reference.type
|
||||||
|
(constructor_invocation
|
||||||
|
(user_type) @name.reference.type)
|
||||||
|
]) @reference.type
|
||||||
64
aider/queries/tree-sitter-languages/ruby-tags.scm
Normal file
64
aider/queries/tree-sitter-languages/ruby-tags.scm
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
; Method definitions
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(method
|
||||||
|
name: (_) @name.definition.method) @definition.method
|
||||||
|
(singleton_method
|
||||||
|
name: (_) @name.definition.method) @definition.method
|
||||||
|
]
|
||||||
|
(#strip! @doc "^#\\s*")
|
||||||
|
(#select-adjacent! @doc @definition.method)
|
||||||
|
)
|
||||||
|
|
||||||
|
(alias
|
||||||
|
name: (_) @name.definition.method) @definition.method
|
||||||
|
|
||||||
|
(setter
|
||||||
|
(identifier) @ignore)
|
||||||
|
|
||||||
|
; Class definitions
|
||||||
|
|
||||||
|
(
|
||||||
|
(comment)* @doc
|
||||||
|
.
|
||||||
|
[
|
||||||
|
(class
|
||||||
|
name: [
|
||||||
|
(constant) @name.definition.class
|
||||||
|
(scope_resolution
|
||||||
|
name: (_) @name.definition.class)
|
||||||
|
]) @definition.class
|
||||||
|
(singleton_class
|
||||||
|
value: [
|
||||||
|
(constant) @name.definition.class
|
||||||
|
(scope_resolution
|
||||||
|
name: (_) @name.definition.class)
|
||||||
|
]) @definition.class
|
||||||
|
]
|
||||||
|
(#strip! @doc "^#\\s*")
|
||||||
|
(#select-adjacent! @doc @definition.class)
|
||||||
|
)
|
||||||
|
|
||||||
|
; Module definitions
|
||||||
|
|
||||||
|
(
|
||||||
|
(module
|
||||||
|
name: [
|
||||||
|
(constant) @name.definition.module
|
||||||
|
(scope_resolution
|
||||||
|
name: (_) @name.definition.module)
|
||||||
|
]) @definition.module
|
||||||
|
)
|
||||||
|
|
||||||
|
; Calls
|
||||||
|
|
||||||
|
(call method: (identifier) @name.reference.call) @reference.call
|
||||||
|
|
||||||
|
(
|
||||||
|
[(identifier) (constant)] @name.reference.call @reference.call
|
||||||
|
(#is-not? local)
|
||||||
|
(#not-match? @name.reference.call "^(lambda|load|require|require_relative|__FILE__|__LINE__)$")
|
||||||
|
)
|
||||||
65
aider/queries/tree-sitter-languages/scala-tags.scm
Normal file
65
aider/queries/tree-sitter-languages/scala-tags.scm
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
; Definitions
|
||||||
|
|
||||||
|
(package_clause
|
||||||
|
name: (package_identifier) @name.definition.module) @definition.module
|
||||||
|
|
||||||
|
(trait_definition
|
||||||
|
name: (identifier) @name.definition.interface) @definition.interface
|
||||||
|
|
||||||
|
(enum_definition
|
||||||
|
name: (identifier) @name.definition.enum) @definition.enum
|
||||||
|
|
||||||
|
(simple_enum_case
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(full_enum_case
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(class_definition
|
||||||
|
name: (identifier) @name.definition.class) @definition.class
|
||||||
|
|
||||||
|
(object_definition
|
||||||
|
name: (identifier) @name.definition.object) @definition.object
|
||||||
|
|
||||||
|
(function_definition
|
||||||
|
name: (identifier) @name.definition.function) @definition.function
|
||||||
|
|
||||||
|
(val_definition
|
||||||
|
pattern: (identifier) @name.definition.variable) @definition.variable
|
||||||
|
|
||||||
|
(given_definition
|
||||||
|
name: (identifier) @name.definition.variable) @definition.variable
|
||||||
|
|
||||||
|
(var_definition
|
||||||
|
pattern: (identifier) @name.definition.variable) @definition.variable
|
||||||
|
|
||||||
|
(val_declaration
|
||||||
|
name: (identifier) @name.definition.variable) @definition.variable
|
||||||
|
|
||||||
|
(var_declaration
|
||||||
|
name: (identifier) @name.definition.variable) @definition.variable
|
||||||
|
|
||||||
|
(type_definition
|
||||||
|
name: (type_identifier) @name.definition.type) @definition.type
|
||||||
|
|
||||||
|
(class_parameter
|
||||||
|
name: (identifier) @name.definition.property) @definition.property
|
||||||
|
|
||||||
|
; References
|
||||||
|
|
||||||
|
(call_expression
|
||||||
|
(identifier) @name.reference.call) @reference.call
|
||||||
|
|
||||||
|
(instance_expression
|
||||||
|
(type_identifier) @name.reference.interface) @reference.interface
|
||||||
|
|
||||||
|
(instance_expression
|
||||||
|
(generic_type
|
||||||
|
(type_identifier) @name.reference.interface)) @reference.interface
|
||||||
|
|
||||||
|
(extends_clause
|
||||||
|
(type_identifier) @name.reference.class) @reference.class
|
||||||
|
|
||||||
|
(extends_clause
|
||||||
|
(generic_type
|
||||||
|
(type_identifier) @name.reference.class)) @reference.class
|
||||||
82
aider/reasoning_tags.py
Normal file
82
aider/reasoning_tags.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from aider.dump import dump # noqa
|
||||||
|
|
||||||
|
# Standard tag identifier
|
||||||
|
REASONING_TAG = "thinking-content-" + "7bbeb8e1441453ad999a0bbba8a46d4b"
|
||||||
|
# Output formatting
|
||||||
|
REASONING_START = "--------------\n► **THINKING**"
|
||||||
|
REASONING_END = "------------\n► **ANSWER**"
|
||||||
|
|
||||||
|
|
||||||
|
def remove_reasoning_content(res, reasoning_tag):
|
||||||
|
"""
|
||||||
|
Remove reasoning content from text based on tags.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
res (str): The text to process
|
||||||
|
reasoning_tag (str): The tag name to remove
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Text with reasoning content removed
|
||||||
|
"""
|
||||||
|
if not reasoning_tag:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Try to match the complete tag pattern first
|
||||||
|
pattern = f"<{reasoning_tag}>.*?</{reasoning_tag}>"
|
||||||
|
res = re.sub(pattern, "", res, flags=re.DOTALL).strip()
|
||||||
|
|
||||||
|
# If closing tag exists but opening tag might be missing, remove everything before closing
|
||||||
|
# tag
|
||||||
|
closing_tag = f"</{reasoning_tag}>"
|
||||||
|
if closing_tag in res:
|
||||||
|
# Split on the closing tag and keep everything after it
|
||||||
|
parts = res.split(closing_tag, 1)
|
||||||
|
res = parts[1].strip() if len(parts) > 1 else res
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def replace_reasoning_tags(text, tag_name):
|
||||||
|
"""
|
||||||
|
Replace opening and closing reasoning tags with standard formatting.
|
||||||
|
Ensures exactly one blank line before START and END markers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): The text containing the tags
|
||||||
|
tag_name (str): The name of the tag to replace
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Text with reasoning tags replaced with standard format
|
||||||
|
"""
|
||||||
|
if not text:
|
||||||
|
return text
|
||||||
|
|
||||||
|
# Replace opening tag with proper spacing
|
||||||
|
text = re.sub(f"\\s*<{tag_name}>\\s*", f"\n{REASONING_START}\n\n", text)
|
||||||
|
|
||||||
|
# Replace closing tag with proper spacing
|
||||||
|
text = re.sub(f"\\s*</{tag_name}>\\s*", f"\n\n{REASONING_END}\n\n", text)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def format_reasoning_content(reasoning_content, tag_name):
|
||||||
|
"""
|
||||||
|
Format reasoning content with appropriate tags.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
reasoning_content (str): The content to format
|
||||||
|
tag_name (str): The tag name to use
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Formatted reasoning content with tags
|
||||||
|
"""
|
||||||
|
if not reasoning_content:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
formatted = f"<{tag_name}>\n\n{reasoning_content}\n\n</{tag_name}>"
|
||||||
|
return formatted
|
||||||
@@ -2,23 +2,36 @@ import os
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
|
|
||||||
import git
|
try:
|
||||||
|
import git
|
||||||
|
|
||||||
|
ANY_GIT_ERROR = [
|
||||||
|
git.exc.ODBError,
|
||||||
|
git.exc.GitError,
|
||||||
|
git.exc.InvalidGitRepositoryError,
|
||||||
|
git.exc.GitCommandNotFound,
|
||||||
|
]
|
||||||
|
except ImportError:
|
||||||
|
git = None
|
||||||
|
ANY_GIT_ERROR = []
|
||||||
|
|
||||||
import pathspec
|
import pathspec
|
||||||
|
|
||||||
from aider import prompts, utils
|
from aider import prompts, utils
|
||||||
from aider.sendchat import simple_send_with_retries
|
|
||||||
|
|
||||||
from .dump import dump # noqa: F401
|
from .dump import dump # noqa: F401
|
||||||
|
|
||||||
ANY_GIT_ERROR = (
|
ANY_GIT_ERROR += [
|
||||||
git.exc.ODBError,
|
|
||||||
git.exc.GitError,
|
|
||||||
OSError,
|
OSError,
|
||||||
IndexError,
|
IndexError,
|
||||||
BufferError,
|
BufferError,
|
||||||
TypeError,
|
TypeError,
|
||||||
ValueError,
|
ValueError,
|
||||||
)
|
AttributeError,
|
||||||
|
AssertionError,
|
||||||
|
TimeoutError,
|
||||||
|
]
|
||||||
|
ANY_GIT_ERROR = tuple(ANY_GIT_ERROR)
|
||||||
|
|
||||||
|
|
||||||
class GitRepo:
|
class GitRepo:
|
||||||
@@ -44,6 +57,7 @@ class GitRepo:
|
|||||||
attribute_commit_message_committer=False,
|
attribute_commit_message_committer=False,
|
||||||
commit_prompt=None,
|
commit_prompt=None,
|
||||||
subtree_only=False,
|
subtree_only=False,
|
||||||
|
git_commit_verify=True,
|
||||||
):
|
):
|
||||||
self.io = io
|
self.io = io
|
||||||
self.models = models
|
self.models = models
|
||||||
@@ -57,6 +71,7 @@ class GitRepo:
|
|||||||
self.attribute_commit_message_committer = attribute_commit_message_committer
|
self.attribute_commit_message_committer = attribute_commit_message_committer
|
||||||
self.commit_prompt = commit_prompt
|
self.commit_prompt = commit_prompt
|
||||||
self.subtree_only = subtree_only
|
self.subtree_only = subtree_only
|
||||||
|
self.git_commit_verify = git_commit_verify
|
||||||
self.ignore_file_cache = {}
|
self.ignore_file_cache = {}
|
||||||
|
|
||||||
if git_dname:
|
if git_dname:
|
||||||
@@ -121,7 +136,9 @@ class GitRepo:
|
|||||||
# if context:
|
# if context:
|
||||||
# full_commit_message += "\n\n# Aider chat conversation:\n\n" + context
|
# full_commit_message += "\n\n# Aider chat conversation:\n\n" + context
|
||||||
|
|
||||||
cmd = ["-m", full_commit_message, "--no-verify"]
|
cmd = ["-m", full_commit_message]
|
||||||
|
if not self.git_commit_verify:
|
||||||
|
cmd.append("--no-verify")
|
||||||
if fnames:
|
if fnames:
|
||||||
fnames = [str(self.abs_root_path(fn)) for fn in fnames]
|
fnames = [str(self.abs_root_path(fn)) for fn in fnames]
|
||||||
for fname in fnames:
|
for fname in fnames:
|
||||||
@@ -133,7 +150,7 @@ class GitRepo:
|
|||||||
else:
|
else:
|
||||||
cmd += ["-a"]
|
cmd += ["-a"]
|
||||||
|
|
||||||
original_user_name = self.repo.config_reader().get_value("user", "name")
|
original_user_name = self.repo.git.config("--get", "user.name")
|
||||||
original_committer_name_env = os.environ.get("GIT_COMMITTER_NAME")
|
original_committer_name_env = os.environ.get("GIT_COMMITTER_NAME")
|
||||||
committer_name = f"{original_user_name} (aider)"
|
committer_name = f"{original_user_name} (aider)"
|
||||||
|
|
||||||
@@ -141,7 +158,7 @@ class GitRepo:
|
|||||||
os.environ["GIT_COMMITTER_NAME"] = committer_name
|
os.environ["GIT_COMMITTER_NAME"] = committer_name
|
||||||
|
|
||||||
if aider_edits and self.attribute_author:
|
if aider_edits and self.attribute_author:
|
||||||
original_auther_name_env = os.environ.get("GIT_AUTHOR_NAME")
|
original_author_name_env = os.environ.get("GIT_AUTHOR_NAME")
|
||||||
os.environ["GIT_AUTHOR_NAME"] = committer_name
|
os.environ["GIT_AUTHOR_NAME"] = committer_name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -161,8 +178,8 @@ class GitRepo:
|
|||||||
del os.environ["GIT_COMMITTER_NAME"]
|
del os.environ["GIT_COMMITTER_NAME"]
|
||||||
|
|
||||||
if aider_edits and self.attribute_author:
|
if aider_edits and self.attribute_author:
|
||||||
if original_auther_name_env is not None:
|
if original_author_name_env is not None:
|
||||||
os.environ["GIT_AUTHOR_NAME"] = original_auther_name_env
|
os.environ["GIT_AUTHOR_NAME"] = original_author_name_env
|
||||||
else:
|
else:
|
||||||
del os.environ["GIT_AUTHOR_NAME"]
|
del os.environ["GIT_AUTHOR_NAME"]
|
||||||
|
|
||||||
@@ -192,7 +209,7 @@ class GitRepo:
|
|||||||
max_tokens = model.info.get("max_input_tokens") or 0
|
max_tokens = model.info.get("max_input_tokens") or 0
|
||||||
if max_tokens and num_tokens > max_tokens:
|
if max_tokens and num_tokens > max_tokens:
|
||||||
continue
|
continue
|
||||||
commit_message = simple_send_with_retries(model, messages)
|
commit_message = model.simple_send_with_retries(messages)
|
||||||
if commit_message:
|
if commit_message:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -276,9 +293,23 @@ class GitRepo:
|
|||||||
files = self.tree_files[commit]
|
files = self.tree_files[commit]
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
for blob in commit.tree.traverse():
|
iterator = commit.tree.traverse()
|
||||||
if blob.type == "blob": # blob is a file
|
blob = None # Initialize blob
|
||||||
files.add(blob.path)
|
while True:
|
||||||
|
try:
|
||||||
|
blob = next(iterator)
|
||||||
|
if blob.type == "blob": # blob is a file
|
||||||
|
files.add(blob.path)
|
||||||
|
except IndexError:
|
||||||
|
# Handle potential index error during tree traversal
|
||||||
|
# without relying on potentially unassigned 'blob'
|
||||||
|
self.io.tool_warning(
|
||||||
|
"GitRepo: Index error encountered while reading git tree object."
|
||||||
|
" Skipping."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
except ANY_GIT_ERROR as err:
|
except ANY_GIT_ERROR as err:
|
||||||
self.git_repo_error = err
|
self.git_repo_error = err
|
||||||
self.io.tool_error(f"Unable to list files in git repo: {err}")
|
self.io.tool_error(f"Unable to list files in git repo: {err}")
|
||||||
@@ -289,8 +320,11 @@ class GitRepo:
|
|||||||
|
|
||||||
# Add staged files
|
# Add staged files
|
||||||
index = self.repo.index
|
index = self.repo.index
|
||||||
staged_files = [path for path, _ in index.entries.keys()]
|
try:
|
||||||
files.update(self.normalize_path(path) for path in staged_files)
|
staged_files = [path for path, _ in index.entries.keys()]
|
||||||
|
files.update(self.normalize_path(path) for path in staged_files)
|
||||||
|
except ANY_GIT_ERROR as err:
|
||||||
|
self.io.tool_error(f"Unable to read staged files: {err}")
|
||||||
|
|
||||||
res = [fname for fname in files if not self.ignored_file(fname)]
|
res = [fname for fname in files if not self.ignored_file(fname)]
|
||||||
|
|
||||||
@@ -350,8 +384,8 @@ class GitRepo:
|
|||||||
|
|
||||||
def ignored_file_raw(self, fname):
|
def ignored_file_raw(self, fname):
|
||||||
if self.subtree_only:
|
if self.subtree_only:
|
||||||
fname_path = Path(self.normalize_path(fname))
|
|
||||||
try:
|
try:
|
||||||
|
fname_path = Path(self.normalize_path(fname))
|
||||||
cwd_path = Path.cwd().resolve().relative_to(Path(self.root).resolve())
|
cwd_path = Path.cwd().resolve().relative_to(Path(self.root).resolve())
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Issue #1524
|
# Issue #1524
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from aider.utils import Spinner
|
|||||||
|
|
||||||
# tree_sitter is throwing a FutureWarning
|
# tree_sitter is throwing a FutureWarning
|
||||||
warnings.simplefilter("ignore", category=FutureWarning)
|
warnings.simplefilter("ignore", category=FutureWarning)
|
||||||
from tree_sitter_languages import get_language, get_parser # noqa: E402
|
from grep_ast.tsl import USING_TSL_PACK, get_language, get_parser # noqa: E402
|
||||||
|
|
||||||
Tag = namedtuple("Tag", "rel_fname fname line name kind".split())
|
Tag = namedtuple("Tag", "rel_fname fname line name kind".split())
|
||||||
|
|
||||||
@@ -31,8 +31,12 @@ Tag = namedtuple("Tag", "rel_fname fname line name kind".split())
|
|||||||
SQLITE_ERRORS = (sqlite3.OperationalError, sqlite3.DatabaseError, OSError)
|
SQLITE_ERRORS = (sqlite3.OperationalError, sqlite3.DatabaseError, OSError)
|
||||||
|
|
||||||
|
|
||||||
|
CACHE_VERSION = 3
|
||||||
|
if USING_TSL_PACK:
|
||||||
|
CACHE_VERSION = 4
|
||||||
|
|
||||||
|
|
||||||
class RepoMap:
|
class RepoMap:
|
||||||
CACHE_VERSION = 3
|
|
||||||
TAGS_CACHE_DIR = f".aider.tags.cache.v{CACHE_VERSION}"
|
TAGS_CACHE_DIR = f".aider.tags.cache.v{CACHE_VERSION}"
|
||||||
|
|
||||||
warned_files = set()
|
warned_files = set()
|
||||||
@@ -282,10 +286,15 @@ class RepoMap:
|
|||||||
query = language.query(query_scm)
|
query = language.query(query_scm)
|
||||||
captures = query.captures(tree.root_node)
|
captures = query.captures(tree.root_node)
|
||||||
|
|
||||||
captures = list(captures)
|
|
||||||
|
|
||||||
saw = set()
|
saw = set()
|
||||||
for node, tag in captures:
|
if USING_TSL_PACK:
|
||||||
|
all_nodes = []
|
||||||
|
for tag, nodes in captures.items():
|
||||||
|
all_nodes += [(node, tag) for node in nodes]
|
||||||
|
else:
|
||||||
|
all_nodes = list(captures)
|
||||||
|
|
||||||
|
for node, tag in all_nodes:
|
||||||
if tag.startswith("name.definition."):
|
if tag.startswith("name.definition."):
|
||||||
kind = "def"
|
kind = "def"
|
||||||
elif tag.startswith("name.reference."):
|
elif tag.startswith("name.reference."):
|
||||||
@@ -389,13 +398,30 @@ class RepoMap:
|
|||||||
|
|
||||||
# dump(fname)
|
# dump(fname)
|
||||||
rel_fname = self.get_rel_fname(fname)
|
rel_fname = self.get_rel_fname(fname)
|
||||||
|
current_pers = 0.0 # Start with 0 personalization score
|
||||||
|
|
||||||
if fname in chat_fnames:
|
if fname in chat_fnames:
|
||||||
personalization[rel_fname] = personalize
|
current_pers += personalize
|
||||||
chat_rel_fnames.add(rel_fname)
|
chat_rel_fnames.add(rel_fname)
|
||||||
|
|
||||||
if rel_fname in mentioned_fnames:
|
if rel_fname in mentioned_fnames:
|
||||||
personalization[rel_fname] = personalize
|
# Use max to avoid double counting if in chat_fnames and mentioned_fnames
|
||||||
|
current_pers = max(current_pers, personalize)
|
||||||
|
|
||||||
|
# Check path components against mentioned_idents
|
||||||
|
path_obj = Path(rel_fname)
|
||||||
|
path_components = set(path_obj.parts)
|
||||||
|
basename_with_ext = path_obj.name
|
||||||
|
basename_without_ext, _ = os.path.splitext(basename_with_ext)
|
||||||
|
components_to_check = path_components.union({basename_with_ext, basename_without_ext})
|
||||||
|
|
||||||
|
matched_idents = components_to_check.intersection(mentioned_idents)
|
||||||
|
if matched_idents:
|
||||||
|
# Add personalization *once* if any path component matches a mentioned ident
|
||||||
|
current_pers += personalize
|
||||||
|
|
||||||
|
if current_pers > 0:
|
||||||
|
personalization[rel_fname] = current_pers # Assign the final calculated value
|
||||||
|
|
||||||
tags = list(self.get_tags(fname, rel_fname))
|
tags = list(self.get_tags(fname, rel_fname))
|
||||||
if tags is None:
|
if tags is None:
|
||||||
@@ -422,17 +448,33 @@ class RepoMap:
|
|||||||
|
|
||||||
G = nx.MultiDiGraph()
|
G = nx.MultiDiGraph()
|
||||||
|
|
||||||
|
# Add a small self-edge for every definition that has no references
|
||||||
|
# Helps with tree-sitter 0.23.2 with ruby, where "def greet(name)"
|
||||||
|
# isn't counted as a def AND a ref. tree-sitter 0.24.0 does.
|
||||||
|
for ident in defines.keys():
|
||||||
|
if ident in references:
|
||||||
|
continue
|
||||||
|
for definer in defines[ident]:
|
||||||
|
G.add_edge(definer, definer, weight=0.1, ident=ident)
|
||||||
|
|
||||||
for ident in idents:
|
for ident in idents:
|
||||||
if progress:
|
if progress:
|
||||||
progress()
|
progress()
|
||||||
|
|
||||||
definers = defines[ident]
|
definers = defines[ident]
|
||||||
|
|
||||||
|
mul = 1.0
|
||||||
|
|
||||||
|
is_snake = ("_" in ident) and any(c.isalpha() for c in ident)
|
||||||
|
is_camel = any(c.isupper() for c in ident) and any(c.islower() for c in ident)
|
||||||
if ident in mentioned_idents:
|
if ident in mentioned_idents:
|
||||||
mul = 10
|
mul *= 10
|
||||||
elif ident.startswith("_"):
|
if (is_snake or is_camel) and len(ident) >= 8:
|
||||||
mul = 0.1
|
mul *= 10
|
||||||
else:
|
if ident.startswith("_"):
|
||||||
mul = 1
|
mul *= 0.1
|
||||||
|
if len(defines[ident]) > 5:
|
||||||
|
mul *= 0.1
|
||||||
|
|
||||||
for referencer, num_refs in Counter(references[ident]).items():
|
for referencer, num_refs in Counter(references[ident]).items():
|
||||||
for definer in definers:
|
for definer in definers:
|
||||||
@@ -440,10 +482,14 @@ class RepoMap:
|
|||||||
# if referencer == definer:
|
# if referencer == definer:
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
|
use_mul = mul
|
||||||
|
if referencer in chat_rel_fnames:
|
||||||
|
use_mul *= 50
|
||||||
|
|
||||||
# scale down so high freq (low value) mentions don't dominate
|
# scale down so high freq (low value) mentions don't dominate
|
||||||
num_refs = math.sqrt(num_refs)
|
num_refs = math.sqrt(num_refs)
|
||||||
|
|
||||||
G.add_edge(referencer, definer, weight=mul * num_refs, ident=ident)
|
G.add_edge(referencer, definer, weight=use_mul * num_refs, ident=ident)
|
||||||
|
|
||||||
if not references:
|
if not references:
|
||||||
pass
|
pass
|
||||||
@@ -605,7 +651,7 @@ class RepoMap:
|
|||||||
|
|
||||||
self.tree_cache = dict()
|
self.tree_cache = dict()
|
||||||
|
|
||||||
middle = min(max_map_tokens // 25, num_tags)
|
middle = min(int(max_map_tokens // 25), num_tags)
|
||||||
while lower_bound <= upper_bound:
|
while lower_bound <= upper_bound:
|
||||||
# dump(lower_bound, middle, upper_bound)
|
# dump(lower_bound, middle, upper_bound)
|
||||||
|
|
||||||
@@ -628,7 +674,7 @@ class RepoMap:
|
|||||||
else:
|
else:
|
||||||
upper_bound = middle - 1
|
upper_bound = middle - 1
|
||||||
|
|
||||||
middle = (lower_bound + upper_bound) // 2
|
middle = int((lower_bound + upper_bound) // 2)
|
||||||
|
|
||||||
spin.end()
|
spin.end()
|
||||||
return best_tree
|
return best_tree
|
||||||
@@ -732,8 +778,27 @@ def get_random_color():
|
|||||||
|
|
||||||
def get_scm_fname(lang):
|
def get_scm_fname(lang):
|
||||||
# Load the tags queries
|
# Load the tags queries
|
||||||
|
if USING_TSL_PACK:
|
||||||
|
subdir = "tree-sitter-language-pack"
|
||||||
|
try:
|
||||||
|
path = resources.files(__package__).joinpath(
|
||||||
|
"queries",
|
||||||
|
subdir,
|
||||||
|
f"{lang}-tags.scm",
|
||||||
|
)
|
||||||
|
if path.exists():
|
||||||
|
return path
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fall back to tree-sitter-languages
|
||||||
|
subdir = "tree-sitter-languages"
|
||||||
try:
|
try:
|
||||||
return resources.files(__package__).joinpath("queries", f"tree-sitter-{lang}-tags.scm")
|
return resources.files(__package__).joinpath(
|
||||||
|
"queries",
|
||||||
|
subdir,
|
||||||
|
f"{lang}-tags.scm",
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,380 @@
|
|||||||
|
{
|
||||||
|
"deepseek-reasoner": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 64000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.00000055,
|
||||||
|
"input_cost_per_token_cache_hit": 0.00000014,
|
||||||
|
"cache_read_input_token_cost": 0.00000014,
|
||||||
|
"cache_creation_input_token_cost": 0.0,
|
||||||
|
"output_cost_per_token": 0.00000219,
|
||||||
|
"litellm_provider": "deepseek",
|
||||||
|
"mode": "chat",
|
||||||
|
//"supports_function_calling": true,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
//"supports_tool_choice": true,
|
||||||
|
"supports_prompt_caching": true
|
||||||
|
},
|
||||||
|
"openrouter/deepseek/deepseek-r1": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 64000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.00000055,
|
||||||
|
"input_cost_per_token_cache_hit": 0.00000014,
|
||||||
|
"cache_read_input_token_cost": 0.00000014,
|
||||||
|
"cache_creation_input_token_cost": 0.0,
|
||||||
|
"output_cost_per_token": 0.00000219,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
//"supports_function_calling": true,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
//"supports_tool_choice": true,
|
||||||
|
"supports_prompt_caching": true
|
||||||
|
},
|
||||||
|
"openrouter/deepseek/deepseek-r1:free": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 64000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.0,
|
||||||
|
"input_cost_per_token_cache_hit": 0.0,
|
||||||
|
"cache_read_input_token_cost": 0.00,
|
||||||
|
"cache_creation_input_token_cost": 0.0,
|
||||||
|
"output_cost_per_token": 0.0,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
//"supports_function_calling": true,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
//"supports_tool_choice": true,
|
||||||
|
"supports_prompt_caching": true
|
||||||
|
},
|
||||||
|
"openrouter/deepseek/deepseek-chat:free": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 64000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.0,
|
||||||
|
"input_cost_per_token_cache_hit": 0.0,
|
||||||
|
"cache_read_input_token_cost": 0.00,
|
||||||
|
"cache_creation_input_token_cost": 0.0,
|
||||||
|
"output_cost_per_token": 0.0,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
//"supports_function_calling": true,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
//"supports_tool_choice": true,
|
||||||
|
"supports_prompt_caching": true
|
||||||
|
},
|
||||||
|
"openrouter/deepseek/deepseek-chat-v3-0324": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 64000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.00000055,
|
||||||
|
"input_cost_per_token_cache_hit": 0.00000014,
|
||||||
|
"cache_read_input_token_cost": 0.00000014,
|
||||||
|
"cache_creation_input_token_cost": 0.0,
|
||||||
|
"output_cost_per_token": 0.00000219,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
//"supports_function_calling": true,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
//"supports_tool_choice": true,
|
||||||
|
"supports_prompt_caching": true
|
||||||
|
},
|
||||||
|
"openrouter/deepseek/deepseek-chat-v3-0324:free": {
|
||||||
|
"max_tokens": 131072,
|
||||||
|
"max_input_tokens": 131072,
|
||||||
|
"max_output_tokens": 131072,
|
||||||
|
"input_cost_per_token": 0,
|
||||||
|
"output_cost_per_token": 0,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_tool_choice": true
|
||||||
|
},
|
||||||
|
"fireworks_ai/accounts/fireworks/models/deepseek-r1": {
|
||||||
|
"max_tokens": 160000,
|
||||||
|
"max_input_tokens": 128000,
|
||||||
|
"max_output_tokens": 20480,
|
||||||
|
"litellm_provider": "fireworks_ai",
|
||||||
|
"input_cost_per_token": 0.000008,
|
||||||
|
"output_cost_per_token": 0.000008,
|
||||||
|
"mode": "chat",
|
||||||
|
},
|
||||||
|
"fireworks_ai/accounts/fireworks/models/deepseek-v3": {
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"max_input_tokens": 100000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"litellm_provider": "fireworks_ai",
|
||||||
|
"input_cost_per_token": 0.0000009,
|
||||||
|
"output_cost_per_token": 0.0000009,
|
||||||
|
"mode": "chat",
|
||||||
|
},
|
||||||
|
"o3-mini": {
|
||||||
|
"max_tokens": 100000,
|
||||||
|
"max_input_tokens": 200000,
|
||||||
|
"max_output_tokens": 100000,
|
||||||
|
"input_cost_per_token": 0.0000011,
|
||||||
|
"output_cost_per_token": 0.0000044,
|
||||||
|
"cache_read_input_token_cost": 0.00000055,
|
||||||
|
"litellm_provider": "openai",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_parallel_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_response_schema": true
|
||||||
|
},
|
||||||
|
"openrouter/openai/o3-mini": {
|
||||||
|
"max_tokens": 100000,
|
||||||
|
"max_input_tokens": 200000,
|
||||||
|
"max_output_tokens": 100000,
|
||||||
|
"input_cost_per_token": 0.0000011,
|
||||||
|
"output_cost_per_token": 0.0000044,
|
||||||
|
"cache_read_input_token_cost": 0.00000055,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_parallel_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_response_schema": true
|
||||||
|
},
|
||||||
|
"openrouter/openai/o3-mini-high": {
|
||||||
|
"max_tokens": 100000,
|
||||||
|
"max_input_tokens": 200000,
|
||||||
|
"max_output_tokens": 100000,
|
||||||
|
"input_cost_per_token": 0.0000011,
|
||||||
|
"output_cost_per_token": 0.0000044,
|
||||||
|
"cache_read_input_token_cost": 0.00000055,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_parallel_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_response_schema": true
|
||||||
|
},
|
||||||
|
"openrouter/openai/gpt-4o-mini": {
|
||||||
|
"max_tokens": 16384,
|
||||||
|
"max_input_tokens": 128000,
|
||||||
|
"max_output_tokens": 16384,
|
||||||
|
"input_cost_per_token": 0.00000015,
|
||||||
|
"output_cost_per_token": 0.00000060,
|
||||||
|
"input_cost_per_token_batches": 0.000000075,
|
||||||
|
"output_cost_per_token_batches": 0.00000030,
|
||||||
|
"cache_read_input_token_cost": 0.000000075,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_parallel_function_calling": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_system_messages": true
|
||||||
|
},
|
||||||
|
"claude-3-7-sonnet-20250219": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 200000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.000003,
|
||||||
|
"output_cost_per_token": 0.000015,
|
||||||
|
"cache_creation_input_token_cost": 0.00000375,
|
||||||
|
"cache_read_input_token_cost": 0.0000003,
|
||||||
|
"litellm_provider": "anthropic",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"tool_use_system_prompt_tokens": 159,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
"supports_pdf_input": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"deprecation_date": "2025-10-01",
|
||||||
|
"supports_tool_choice": true
|
||||||
|
},
|
||||||
|
"anthropic/claude-3-7-sonnet-20250219": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 200000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.000003,
|
||||||
|
"output_cost_per_token": 0.000015,
|
||||||
|
"cache_creation_input_token_cost": 0.00000375,
|
||||||
|
"cache_read_input_token_cost": 0.0000003,
|
||||||
|
"litellm_provider": "anthropic",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"tool_use_system_prompt_tokens": 159,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
"supports_pdf_input": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"deprecation_date": "2025-10-01",
|
||||||
|
"supports_tool_choice": true
|
||||||
|
},
|
||||||
|
"openrouter/anthropic/claude-3.7-sonnet": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 200000,
|
||||||
|
"max_output_tokens": 8192,
|
||||||
|
"input_cost_per_token": 0.000003,
|
||||||
|
"output_cost_per_token": 0.000015,
|
||||||
|
"cache_creation_input_token_cost": 0.00000375,
|
||||||
|
"cache_read_input_token_cost": 0.0000003,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"tool_use_system_prompt_tokens": 159,
|
||||||
|
"supports_assistant_prefill": true,
|
||||||
|
"supports_pdf_input": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"deprecation_date": "2025-10-01",
|
||||||
|
"supports_tool_choice": true
|
||||||
|
},
|
||||||
|
"gpt-4.5-preview": {
|
||||||
|
"max_tokens": 16384,
|
||||||
|
"max_input_tokens": 128000,
|
||||||
|
"max_output_tokens": 16384,
|
||||||
|
"input_cost_per_token": 0.000075,
|
||||||
|
"output_cost_per_token": 0.00015,
|
||||||
|
"cache_read_input_token_cost": 0.0000375,
|
||||||
|
"litellm_provider": "openai",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_parallel_function_calling": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_tool_choice": true
|
||||||
|
},
|
||||||
|
"openai/gpt-4.5-preview": {
|
||||||
|
"max_tokens": 16384,
|
||||||
|
"max_input_tokens": 128000,
|
||||||
|
"max_output_tokens": 16384,
|
||||||
|
"input_cost_per_token": 0.000075,
|
||||||
|
"output_cost_per_token": 0.00015,
|
||||||
|
"cache_read_input_token_cost": 0.0000375,
|
||||||
|
"litellm_provider": "openai",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_parallel_function_calling": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_prompt_caching": true,
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_tool_choice": true
|
||||||
|
},
|
||||||
|
"gemini/gemini-2.5-pro-exp-03-25": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 1048576,
|
||||||
|
"max_output_tokens": 64000,
|
||||||
|
"max_images_per_prompt": 3000,
|
||||||
|
"max_videos_per_prompt": 10,
|
||||||
|
"max_video_length": 1,
|
||||||
|
"max_audio_length_hours": 8.4,
|
||||||
|
"max_audio_per_prompt": 1,
|
||||||
|
"max_pdf_size_mb": 30,
|
||||||
|
"input_cost_per_image": 0,
|
||||||
|
"input_cost_per_video_per_second": 0,
|
||||||
|
"input_cost_per_audio_per_second": 0,
|
||||||
|
"input_cost_per_token": 0,
|
||||||
|
"input_cost_per_character": 0,
|
||||||
|
"input_cost_per_token_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_character_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_image_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_video_per_second_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_audio_per_second_above_128k_tokens": 0,
|
||||||
|
"output_cost_per_token": 0,
|
||||||
|
"output_cost_per_character": 0,
|
||||||
|
"output_cost_per_token_above_128k_tokens": 0,
|
||||||
|
"output_cost_per_character_above_128k_tokens": 0,
|
||||||
|
//"litellm_provider": "vertex_ai-language-models",
|
||||||
|
"litellm_provider": "gemini",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_audio_input": true,
|
||||||
|
"supports_video_input": true,
|
||||||
|
"supports_pdf_input": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"supports_tool_choice": true,
|
||||||
|
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||||
|
},
|
||||||
|
"vertex_ai/gemini-2.5-pro-exp-03-25": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 1048576,
|
||||||
|
"max_output_tokens": 64000,
|
||||||
|
"max_images_per_prompt": 3000,
|
||||||
|
"max_videos_per_prompt": 10,
|
||||||
|
"max_video_length": 1,
|
||||||
|
"max_audio_length_hours": 8.4,
|
||||||
|
"max_audio_per_prompt": 1,
|
||||||
|
"max_pdf_size_mb": 30,
|
||||||
|
"input_cost_per_image": 0,
|
||||||
|
"input_cost_per_video_per_second": 0,
|
||||||
|
"input_cost_per_audio_per_second": 0,
|
||||||
|
"input_cost_per_token": 0,
|
||||||
|
"input_cost_per_character": 0,
|
||||||
|
"input_cost_per_token_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_character_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_image_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_video_per_second_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_audio_per_second_above_128k_tokens": 0,
|
||||||
|
"output_cost_per_token": 0,
|
||||||
|
"output_cost_per_character": 0,
|
||||||
|
"output_cost_per_token_above_128k_tokens": 0,
|
||||||
|
"output_cost_per_character_above_128k_tokens": 0,
|
||||||
|
"litellm_provider": "vertex_ai-language-models",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_audio_input": true,
|
||||||
|
"supports_video_input": true,
|
||||||
|
"supports_pdf_input": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"supports_tool_choice": true,
|
||||||
|
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||||
|
},
|
||||||
|
"openrouter/google/gemini-2.5-pro-exp-03-25:free": {
|
||||||
|
"max_tokens": 8192,
|
||||||
|
"max_input_tokens": 1048576,
|
||||||
|
"max_output_tokens": 64000,
|
||||||
|
"max_images_per_prompt": 3000,
|
||||||
|
"max_videos_per_prompt": 10,
|
||||||
|
"max_video_length": 1,
|
||||||
|
"max_audio_length_hours": 8.4,
|
||||||
|
"max_audio_per_prompt": 1,
|
||||||
|
"max_pdf_size_mb": 30,
|
||||||
|
"input_cost_per_image": 0,
|
||||||
|
"input_cost_per_video_per_second": 0,
|
||||||
|
"input_cost_per_audio_per_second": 0,
|
||||||
|
"input_cost_per_token": 0,
|
||||||
|
"input_cost_per_character": 0,
|
||||||
|
"input_cost_per_token_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_character_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_image_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_video_per_second_above_128k_tokens": 0,
|
||||||
|
"input_cost_per_audio_per_second_above_128k_tokens": 0,
|
||||||
|
"output_cost_per_token": 0,
|
||||||
|
"output_cost_per_character": 0,
|
||||||
|
"output_cost_per_token_above_128k_tokens": 0,
|
||||||
|
"output_cost_per_character_above_128k_tokens": 0,
|
||||||
|
"litellm_provider": "openrouter",
|
||||||
|
"mode": "chat",
|
||||||
|
"supports_system_messages": true,
|
||||||
|
"supports_function_calling": true,
|
||||||
|
"supports_vision": true,
|
||||||
|
"supports_audio_input": true,
|
||||||
|
"supports_video_input": true,
|
||||||
|
"supports_pdf_input": true,
|
||||||
|
"supports_response_schema": true,
|
||||||
|
"supports_tool_choice": true,
|
||||||
|
"source": "https://cloud.google.com/vertex-ai/generative-ai/pricing"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
964
aider/resources/model-settings.yml
Normal file
964
aider/resources/model-settings.yml
Normal file
@@ -0,0 +1,964 @@
|
|||||||
|
- name: gpt-3.5-turbo
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-3.5-turbo-0125
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-3.5-turbo-1106
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-3.5-turbo-0613
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-3.5-turbo-16k-0613
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-4-turbo-2024-04-09
|
||||||
|
edit_format: udiff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-4-turbo
|
||||||
|
edit_format: udiff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: openai/gpt-4o
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openai/gpt-4o-2024-08-06
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gpt-4o-2024-08-06
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gpt-4o-2024-11-20
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: openai/gpt-4o-2024-11-20
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gpt-4o
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: gpt-4o-mini
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: openai/gpt-4o-mini
|
||||||
|
weak_model_name: openai/gpt-4o-mini
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-4-0125-preview
|
||||||
|
edit_format: udiff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gpt-4-1106-preview
|
||||||
|
edit_format: udiff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-4-vision-preview
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-4-0314
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gpt-4-0613
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: gpt-4-32k-0613
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
|
||||||
|
- name: claude-3-opus-20240229
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: openrouter/anthropic/claude-3-opus
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: claude-3-sonnet-20240229
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
|
||||||
|
- name: claude-3-5-sonnet-20240620
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: claude-3-5-sonnet-20240620
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-5-sonnet-20240620
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: anthropic/claude-3-5-sonnet-20240620
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-5-sonnet-20241022
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: anthropic/claude-3-5-sonnet-20241022
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-7-sonnet-20250219
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: anthropic/claude-3-7-sonnet-20250219
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-7-sonnet-latest
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: anthropic/claude-3-7-sonnet-latest
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: claude-3-7-sonnet-20250219
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: claude-3-7-sonnet-20250219
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: claude-3-7-sonnet-latest
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: claude-3-7-sonnet-latest
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: bedrock/us.anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: bedrock_converse/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: bedrock_converse/anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: bedrock_converse/us.anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: bedrock_converse/us.anthropic.claude-3-7-sonnet-20250219-v1:0
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: vertex_ai/claude-3-7-sonnet@20250219
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 64000
|
||||||
|
editor_model_name: vertex_ai/claude-3-7-sonnet@20250219
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 64000
|
||||||
|
editor_model_name: vertex_ai-anthropic_models/vertex_ai/claude-3-7-sonnet@20250219
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: openrouter/anthropic/claude-3.7-sonnet
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: openrouter/anthropic/claude-3.7-sonnet
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: openrouter/anthropic/claude-3.7-sonnet:beta
|
||||||
|
overeager: true
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25,output-128k-2025-02-19
|
||||||
|
max_tokens: 64000
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: openrouter/anthropic/claude-3.7-sonnet
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["thinking_tokens"]
|
||||||
|
|
||||||
|
- name: bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: bedrock/anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-5-sonnet-latest
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: anthropic/claude-3-5-sonnet-20241022
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: claude-3-5-sonnet-20241022
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: claude-3-5-sonnet-20241022
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-haiku-20240307
|
||||||
|
weak_model_name: anthropic/claude-3-haiku-20240307
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
cache_control: true
|
||||||
|
|
||||||
|
- name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: anthropic/claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
cache_control: true
|
||||||
|
|
||||||
|
- name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: bedrock/anthropic.claude-3-5-haiku-20241022-v1:0
|
||||||
|
use_repo_map: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
cache_control: true
|
||||||
|
|
||||||
|
- name: claude-3-5-haiku-20241022
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: claude-3-5-haiku-20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
cache_control: true
|
||||||
|
|
||||||
|
- name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
use_repo_map: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 4096
|
||||||
|
|
||||||
|
- name: claude-3-haiku-20240307
|
||||||
|
weak_model_name: claude-3-haiku-20240307
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
extra_headers:
|
||||||
|
anthropic-beta: prompt-caching-2024-07-31,pdfs-2024-09-25
|
||||||
|
cache_control: true
|
||||||
|
|
||||||
|
- name: openrouter/anthropic/claude-3.5-sonnet
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/anthropic/claude-3-5-haiku
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: openrouter/anthropic/claude-3.5-sonnet
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openrouter/anthropic/claude-3.5-sonnet:beta
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/anthropic/claude-3-5-haiku:beta
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
cache_control: true
|
||||||
|
editor_model_name: openrouter/anthropic/claude-3.5-sonnet:beta
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: vertex_ai/claude-3-5-sonnet@20240620
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
editor_model_name: vertex_ai/claude-3-5-sonnet@20240620
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: vertex_ai/claude-3-5-sonnet-v2@20241022
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
editor_model_name: vertex_ai/claude-3-5-sonnet-v2@20241022
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: vertex_ai/claude-3-opus@20240229
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: vertex_ai/claude-3-sonnet@20240229
|
||||||
|
weak_model_name: vertex_ai/claude-3-5-haiku@20241022
|
||||||
|
|
||||||
|
- name: command-r-plus
|
||||||
|
weak_model_name: command-r-plus
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: command-r-08-2024
|
||||||
|
weak_model_name: command-r-08-2024
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: command-r-plus-08-2024
|
||||||
|
weak_model_name: command-r-plus-08-2024
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: groq/llama3-70b-8192
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: groq/llama3-8b-8192
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: openrouter/meta-llama/llama-3-70b-instruct
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/meta-llama/llama-3-70b-instruct
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-1.5-pro-002
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-1.5-flash-002
|
||||||
|
|
||||||
|
- name: gemini/gemini-1.5-pro
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-1.5-pro-latest
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-1.5-pro-exp-0827
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-exp-1206
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-exp-1114
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-exp-1121
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: vertex_ai/gemini-pro-experimental
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-1.5-flash-exp-0827
|
||||||
|
|
||||||
|
- name: gemini/gemini-2.0-flash-exp
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: gemini/gemini-2.0-flash
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-r1
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/deepseek/deepseek-chat
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
include_reasoning: true
|
||||||
|
caches_by_default: true
|
||||||
|
editor_model_name: openrouter/deepseek/deepseek-chat
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-r1:free
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/deepseek/deepseek-r1:free
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-chat-v3-0324:free
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/deepseek/deepseek-chat-v3-0324:free
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 131072
|
||||||
|
caches_by_default: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openrouter/deepseek/deepseek-chat-v3-0324:free
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openrouter/deepseek/deepseek-r1:free
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: deepseek/deepseek-reasoner
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: deepseek/deepseek-chat
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: deepseek/deepseek-chat
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: deepseek/deepseek-chat
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-chat:free
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/deepseek/deepseek-chat:free
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openrouter/deepseek/deepseek-chat:free
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: deepseek/deepseek-coder
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
|
||||||
|
- name: deepseek-chat
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
|
||||||
|
- name: deepseek-coder
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-coder
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-chat
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-chat-v3-0324
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
|
||||||
|
- name: openrouter/openai/gpt-4o
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openai/o1-mini
|
||||||
|
weak_model_name: openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: azure/o1-mini
|
||||||
|
weak_model_name: azure/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: azure/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: o1-mini
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openai/o1-preview
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: azure/o1-preview
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: azure/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: azure/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: azure/o1
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: azure/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
streaming: false
|
||||||
|
editor_model_name: azure/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: o1-preview
|
||||||
|
edit_format: architect
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openrouter/openai/o1-mini
|
||||||
|
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
streaming: false
|
||||||
|
editor_model_name: openrouter/openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openrouter/openai/o1-preview
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_system_prompt: false
|
||||||
|
use_temperature: false
|
||||||
|
streaming: false
|
||||||
|
editor_model_name: openrouter/openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openrouter/openai/o1
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
streaming: false
|
||||||
|
editor_model_name: openrouter/openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: openai/o1
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
streaming: false
|
||||||
|
editor_model_name: openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: o1
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
streaming: false
|
||||||
|
editor_model_name: gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||||
|
use_repo_map: true
|
||||||
|
editor_model_name: openrouter/qwen/qwen-2.5-coder-32b-instruct
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openrouter/deepseek/deepseek-r1-distill-llama-70b
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/deepseek/deepseek-chat
|
||||||
|
use_repo_map: true
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 8192
|
||||||
|
caches_by_default: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openrouter/deepseek/deepseek-chat
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: fireworks_ai/accounts/fireworks/models/deepseek-r1
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
streaming: true
|
||||||
|
editor_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
reasoning_tag: think
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 160000
|
||||||
|
|
||||||
|
- name: fireworks_ai/accounts/fireworks/models/deepseek-v3
|
||||||
|
edit_format: diff
|
||||||
|
use_repo_map: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 128000
|
||||||
|
|
||||||
|
- name: openai/o3-mini
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: o3-mini
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: openrouter/openai/o3-mini
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openrouter/openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: openrouter/openai/o3-mini-high
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: openrouter/openai/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: openrouter/openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: azure/o3-mini
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: azure/gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
use_temperature: false
|
||||||
|
editor_model_name: azure/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
system_prompt_prefix: "Formatting re-enabled. "
|
||||||
|
accepts_settings: ["reasoning_effort"]
|
||||||
|
|
||||||
|
- name: gpt-4.5-preview
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
editor_model_name: gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: openai/gpt-4.5-preview
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: gpt-4o-mini
|
||||||
|
use_repo_map: true
|
||||||
|
lazy: true
|
||||||
|
reminder: sys
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
editor_model_name: openai/gpt-4o
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
|
||||||
|
- name: fireworks_ai/accounts/fireworks/models/qwq-32b
|
||||||
|
reasoning_tag: think
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct
|
||||||
|
use_repo_map: true
|
||||||
|
editor_model_name: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
reminder: user
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
use_temperature: 0.6
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 32000
|
||||||
|
top_p: 0.95
|
||||||
|
|
||||||
|
- name: groq/qwen-qwq-32b
|
||||||
|
reasoning_tag: think
|
||||||
|
edit_format: diff
|
||||||
|
weak_model_name: groq/qwen-2.5-coder-32b
|
||||||
|
use_repo_map: true
|
||||||
|
editor_model_name: groq/qwen-2.5-coder-32b
|
||||||
|
editor_edit_format: editor-diff
|
||||||
|
use_temperature: 0.6
|
||||||
|
extra_params:
|
||||||
|
max_tokens: 128000
|
||||||
|
top_p: 0.95
|
||||||
|
|
||||||
|
- name: cohere_chat/command-a-03-2025
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: openrouter/cohere/command-a-03-2025
|
||||||
|
examples_as_sys_msg: true
|
||||||
|
|
||||||
|
- name: gemini/gemma-3-27b-it
|
||||||
|
use_system_prompt: false
|
||||||
|
|
||||||
|
- name: openrouter/google/gemma-3-27b-it:free
|
||||||
|
use_system_prompt: false
|
||||||
|
|
||||||
|
- name: openrouter/google/gemma-3-27b-it
|
||||||
|
use_system_prompt: false
|
||||||
|
|
||||||
|
- name: gemini/gemini-2.5-pro-exp-03-25
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: openrouter/google/gemini-2.5-pro-exp-03-25:free
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
|
|
||||||
|
- name: vertex_ai/gemini-2.5-pro-exp-03-25
|
||||||
|
edit_format: diff-fenced
|
||||||
|
use_repo_map: true
|
||||||
@@ -39,7 +39,7 @@ def get_windows_parent_process_name():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def run_cmd_subprocess(command, verbose=False, cwd=None):
|
def run_cmd_subprocess(command, verbose=False, cwd=None, encoding=sys.stdout.encoding):
|
||||||
if verbose:
|
if verbose:
|
||||||
print("Using run_cmd_subprocess:", command)
|
print("Using run_cmd_subprocess:", command)
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ def run_cmd_subprocess(command, verbose=False, cwd=None):
|
|||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
text=True,
|
text=True,
|
||||||
shell=True,
|
shell=True,
|
||||||
encoding=sys.stdout.encoding,
|
encoding=encoding,
|
||||||
errors="replace",
|
errors="replace",
|
||||||
bufsize=0, # Set bufsize to 0 for unbuffered output
|
bufsize=0, # Set bufsize to 0 for unbuffered output
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
|
|||||||
@@ -159,7 +159,8 @@ class Scraper:
|
|||||||
try:
|
try:
|
||||||
response = page.goto(url, wait_until="networkidle", timeout=5000)
|
response = page.goto(url, wait_until="networkidle", timeout=5000)
|
||||||
except PlaywrightTimeoutError:
|
except PlaywrightTimeoutError:
|
||||||
self.print_error(f"Timeout while loading {url}")
|
print(f"Page didn't quiesce, scraping content anyway: {url}")
|
||||||
|
response = None
|
||||||
except PlaywrightError as e:
|
except PlaywrightError as e:
|
||||||
self.print_error(f"Error navigating to {url}: {str(e)}")
|
self.print_error(f"Error navigating to {url}: {str(e)}")
|
||||||
return None, None
|
return None, None
|
||||||
|
|||||||
@@ -1,98 +1,61 @@
|
|||||||
import hashlib
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
from aider.dump import dump # noqa: F401
|
from aider.dump import dump # noqa: F401
|
||||||
from aider.exceptions import LiteLLMExceptions
|
from aider.utils import format_messages
|
||||||
from aider.llm import litellm
|
|
||||||
|
|
||||||
# from diskcache import Cache
|
|
||||||
|
|
||||||
|
|
||||||
CACHE_PATH = "~/.aider.send.cache.v1"
|
def sanity_check_messages(messages):
|
||||||
CACHE = None
|
"""Check if messages alternate between user and assistant roles.
|
||||||
# CACHE = Cache(CACHE_PATH)
|
System messages can be interspersed anywhere.
|
||||||
|
Also verifies the last non-system message is from the user.
|
||||||
|
Returns True if valid, False otherwise."""
|
||||||
|
last_role = None
|
||||||
|
last_non_system_role = None
|
||||||
|
|
||||||
RETRY_TIMEOUT = 60
|
for msg in messages:
|
||||||
|
role = msg.get("role")
|
||||||
|
if role == "system":
|
||||||
def send_completion(
|
|
||||||
model_name,
|
|
||||||
messages,
|
|
||||||
functions,
|
|
||||||
stream,
|
|
||||||
temperature=0,
|
|
||||||
extra_params=None,
|
|
||||||
):
|
|
||||||
kwargs = dict(
|
|
||||||
model=model_name,
|
|
||||||
messages=messages,
|
|
||||||
stream=stream,
|
|
||||||
)
|
|
||||||
if temperature is not None:
|
|
||||||
kwargs["temperature"] = temperature
|
|
||||||
|
|
||||||
if functions is not None:
|
|
||||||
function = functions[0]
|
|
||||||
kwargs["tools"] = [dict(type="function", function=function)]
|
|
||||||
kwargs["tool_choice"] = {"type": "function", "function": {"name": function["name"]}}
|
|
||||||
|
|
||||||
if extra_params is not None:
|
|
||||||
kwargs.update(extra_params)
|
|
||||||
|
|
||||||
key = json.dumps(kwargs, sort_keys=True).encode()
|
|
||||||
|
|
||||||
# Generate SHA1 hash of kwargs and append it to chat_completion_call_hashes
|
|
||||||
hash_object = hashlib.sha1(key)
|
|
||||||
|
|
||||||
if not stream and CACHE is not None and key in CACHE:
|
|
||||||
return hash_object, CACHE[key]
|
|
||||||
|
|
||||||
res = litellm.completion(**kwargs)
|
|
||||||
|
|
||||||
if not stream and CACHE is not None:
|
|
||||||
CACHE[key] = res
|
|
||||||
|
|
||||||
return hash_object, res
|
|
||||||
|
|
||||||
|
|
||||||
def simple_send_with_retries(model, messages):
|
|
||||||
litellm_ex = LiteLLMExceptions()
|
|
||||||
|
|
||||||
retry_delay = 0.125
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
kwargs = {
|
|
||||||
"model_name": model.name,
|
|
||||||
"messages": messages,
|
|
||||||
"functions": None,
|
|
||||||
"stream": False,
|
|
||||||
"temperature": None if not model.use_temperature else 0,
|
|
||||||
"extra_params": model.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
|
continue
|
||||||
except AttributeError:
|
|
||||||
return None
|
if last_role and role == last_role:
|
||||||
|
turns = format_messages(messages)
|
||||||
|
raise ValueError("Messages don't properly alternate user/assistant:\n\n" + turns)
|
||||||
|
|
||||||
|
last_role = role
|
||||||
|
last_non_system_role = role
|
||||||
|
|
||||||
|
# Ensure last non-system message is from user
|
||||||
|
return last_non_system_role == "user"
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_alternating_roles(messages):
|
||||||
|
"""Ensure messages alternate between 'assistant' and 'user' roles.
|
||||||
|
|
||||||
|
Inserts empty messages of the opposite role when consecutive messages
|
||||||
|
of the same role are found.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
messages: List of message dictionaries with 'role' and 'content' keys.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of messages with alternating roles.
|
||||||
|
"""
|
||||||
|
if not messages:
|
||||||
|
return messages
|
||||||
|
|
||||||
|
fixed_messages = []
|
||||||
|
prev_role = None
|
||||||
|
|
||||||
|
for msg in messages:
|
||||||
|
current_role = msg.get("role") # Get 'role', None if missing
|
||||||
|
|
||||||
|
# If current role same as previous, insert empty message
|
||||||
|
# of the opposite role
|
||||||
|
if current_role == prev_role:
|
||||||
|
if current_role == "user":
|
||||||
|
fixed_messages.append({"role": "assistant", "content": ""})
|
||||||
|
else:
|
||||||
|
fixed_messages.append({"role": "user", "content": ""})
|
||||||
|
|
||||||
|
fixed_messages.append(msg)
|
||||||
|
prev_role = current_role
|
||||||
|
|
||||||
|
return fixed_messages
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ ROOT_IMPORTANT_FILES = [
|
|||||||
"composer.lock",
|
"composer.lock",
|
||||||
"pom.xml",
|
"pom.xml",
|
||||||
"build.gradle",
|
"build.gradle",
|
||||||
|
"build.gradle.kts",
|
||||||
"build.sbt",
|
"build.sbt",
|
||||||
"go.mod",
|
"go.mod",
|
||||||
"go.sum",
|
"go.sum",
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ install_properly = "https://aider.chat/docs/troubleshooting/imports.html"
|
|||||||
analytics = "https://aider.chat/docs/more/analytics.html"
|
analytics = "https://aider.chat/docs/more/analytics.html"
|
||||||
release_notes = "https://aider.chat/HISTORY.html#release-notes"
|
release_notes = "https://aider.chat/HISTORY.html#release-notes"
|
||||||
edit_formats = "https://aider.chat/docs/more/edit-formats.html"
|
edit_formats = "https://aider.chat/docs/more/edit-formats.html"
|
||||||
|
models_and_keys = "https://aider.chat/docs/troubleshooting/models-and-keys.html"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user