mirror of
https://github.com/Aider-AI/aider
synced 2026-04-26 01:25:17 +02:00
Compare commits
1459 Commits
v0.74.3.de
...
v0.81.2.de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bfb615d68 | ||
|
|
87275140f9 | ||
|
|
0672a68ba4 | ||
|
|
246e3ccfad | ||
|
|
b275ee919f | ||
|
|
eda796d5e0 | ||
|
|
d1b3917309 | ||
|
|
ffee2b971f | ||
|
|
b9a80f9c8c | ||
|
|
980f673ce2 | ||
|
|
55767a0003 | ||
|
|
fb44bebe40 | ||
|
|
b79f072499 | ||
|
|
d65a2e8b51 | ||
|
|
e0b42d51db | ||
|
|
c057dc9466 | ||
|
|
fff53a94d3 | ||
|
|
12beedd0a6 | ||
|
|
80f60a7394 | ||
|
|
2359348505 | ||
|
|
63e3e06a8c | ||
|
|
dca92b580c | ||
|
|
24e2960092 | ||
|
|
be1a52c5c1 | ||
|
|
8a34a6c8f4 | ||
|
|
7924ea9bb9 | ||
|
|
a3a17ae792 | ||
|
|
f8801d811b | ||
|
|
425284ac62 | ||
|
|
4872cdf905 | ||
|
|
88cd81c692 | ||
|
|
d45ecd0800 | ||
|
|
4bfcef60f4 | ||
|
|
e9b7e933f5 | ||
|
|
e5301cef49 | ||
|
|
01ca552174 | ||
|
|
4529d73bf3 | ||
|
|
0798906a51 | ||
|
|
8547c24dac | ||
|
|
0e1e1aae2e | ||
|
|
9cc31e4087 | ||
|
|
e9c7555bb9 | ||
|
|
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 | ||
|
|
3e8f9aa31c | ||
|
|
927b5bc8cc | ||
|
|
2a56d892d7 | ||
|
|
e3d5eaf388 | ||
|
|
5d1f50117b | ||
|
|
f6a2ec15d7 | ||
|
|
64a8d56725 | ||
|
|
71caea32e7 | ||
|
|
17993ef9ff | ||
|
|
b0aa4ef4c8 | ||
|
|
5c4aaa27d9 | ||
|
|
afebfe5f4f | ||
|
|
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."
|
||||
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."
|
||||
@@ -18,5 +18,6 @@ repos:
|
||||
rev: v2.2.6
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ["--skip", "aider/website/docs/languages.md"]
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
|
||||
183
HISTORY.md
183
HISTORY.md
@@ -1,5 +1,188 @@
|
||||
# Release history
|
||||
|
||||
### Aider v0.81.1
|
||||
|
||||
- Added support for the `gemini/gemini-2.5-pro-preview-03-25` model.
|
||||
- Updated the `gemini` alias to point to `gemini/gemini-2.5-pro-preview-03-25`.
|
||||
- Added the `gemini-exp` alias for `gemini/gemini-2.5-pro-exp-03-25`.
|
||||
- Aider wrote 87% of the code in this release.
|
||||
|
||||
### Aider v0.81.0
|
||||
|
||||
- Added support for the `openrouter/openrouter/quasar-alpha` model.
|
||||
- Run with `aider --model quasar`
|
||||
- Offer OpenRouter OAuth authentication if an OpenRouter model is specified but the API key is missing.
|
||||
- Prevent retrying API calls when the provider reports insufficient credits.
|
||||
- Improve URL detection to exclude trailing double quotes.
|
||||
- Aider wrote 86% of the code in this release.
|
||||
|
||||
### Aider v0.80.4
|
||||
|
||||
- Bumped deps to pickup litellm change to properly display the root cause of OpenRouter "choices" errors.
|
||||
|
||||
### Aider v0.80.3
|
||||
|
||||
- Improve error message for OpenRouter API connection issues to mention potential rate limiting or upstream provider issues.
|
||||
- Configure weak models (`gemini/gemini-2.0-flash` and `openrouter/google/gemini-2.0-flash-exp:free`) for Gemini 2.5 Pro models.
|
||||
- Add model metadata for `openrouter/google/gemini-2.0-flash-exp:free`.
|
||||
|
||||
### Aider v0.80.2
|
||||
|
||||
- Bumped deps.
|
||||
|
||||
### 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 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.
|
||||
|
||||
246
README.md
246
README.md
@@ -1,144 +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,
|
||||
to edit code in your local git repository.
|
||||
Start a new project or work with an existing code base.
|
||||
Aider works best with Claude 3.5 Sonnet, DeepSeek R1 & Chat V3, OpenAI o1, o3-mini & GPT-4o. Aider can [connect to almost any LLM, including local models](https://aider.chat/docs/llms.html).
|
||||
<p align="center">
|
||||
Aider lets you pair program with LLMs to start a new project or build on your existing codebase.
|
||||
</p>
|
||||
|
||||
<!-- SCREENCAST START -->
|
||||
<p align="center">
|
||||
<img
|
||||
src="https://aider.chat/assets/screencast.svg"
|
||||
alt="aider screencast"
|
||||
>
|
||||
</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">
|
||||
<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
|
||||
# We can't "include" here.
|
||||
# Because this page is rendered by GitHub as the repo README
|
||||
cog.out(open("aider/website/_includes/get-started.md").read())
|
||||
from scripts.homepage import get_badges_md
|
||||
text = get_badges_md()
|
||||
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"
|
||||
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>
|
||||
<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"/>
|
||||
<a href="https://openrouter.ai/"><img alt="OpenRouter Ranking" title="Aider's ranking among applications on the OpenRouter platform"
|
||||
src="https://img.shields.io/badge/🏆%20OpenRouter-Top%2020-9b59b6?style=flat-square&labelColor=555555"/></a>
|
||||
<a href="https://aider.chat/HISTORY.html"><img alt="Singularity" title="Percentage of the new code in Aider's last release written by Aider itself"
|
||||
src="https://img.shields.io/badge/🔄%20Singularity-86%25-e74c3c?style=flat-square&labelColor=555555"/></a>
|
||||
<!--[[[end]]]-->
|
||||
</p>
|
||||
|
||||
If you already have python 3.8-3.13 installed, you can get started quickly like this:
|
||||
## Features
|
||||
|
||||
### [Cloud and local LLMs](https://aider.chat/docs/llms.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.
|
||||
|
||||
<br>
|
||||
|
||||
### [Maps your codebase](https://aider.chat/docs/repomap.html)
|
||||
|
||||
<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.
|
||||
|
||||
<br>
|
||||
|
||||
### [100+ code languages](https://aider.chat/docs/languages.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 code base
|
||||
# Change directory into your codebase
|
||||
cd /to/your/project
|
||||
|
||||
# Work with DeepSeek via DeepSeek's API
|
||||
aider --model deepseek --api-key deepseek=your-key-goes-here
|
||||
# DeepSeek
|
||||
aider --model deepseek --api-key deepseek=<key>
|
||||
|
||||
# Work with Claude 3.5 Sonnet via Anthropic's API
|
||||
aider --model sonnet --api-key anthropic=your-key-goes-here
|
||||
# Claude 3.7 Sonnet
|
||||
aider --model sonnet --api-key anthropic=<key>
|
||||
|
||||
# Work with GPT-4o via OpenAI's API
|
||||
aider --model gpt-4o --api-key openai=your-key-goes-here
|
||||
|
||||
# Work with Sonnet via OpenRouter's API
|
||||
aider --model openrouter/anthropic/claude-3.5-sonnet --api-key openrouter=your-key-goes-here
|
||||
|
||||
# Work with DeepSeek via OpenRouter's API
|
||||
aider --model openrouter/deepseek/deepseek-chat --api-key openrouter=your-key-goes-here
|
||||
# o3-mini
|
||||
aider --model o3-mini --api-key openai=<key>
|
||||
```
|
||||
<!--[[[end]]]-->
|
||||
|
||||
See the
|
||||
[installation instructions](https://aider.chat/docs/install.html)
|
||||
and
|
||||
[usage documentation](https://aider.chat/docs/usage.html)
|
||||
for more details.
|
||||
See the [installation instructions](https://aider.chat/docs/install.html) and [usage documentation](https://aider.chat/docs/usage.html) for more details.
|
||||
|
||||
## Features
|
||||
## More Information
|
||||
|
||||
- Run aider with the files you want to edit: `aider <file1> <file2> ...`
|
||||
- Ask for changes:
|
||||
- Add new features or test cases.
|
||||
- Describe a bug.
|
||||
- Paste in an error message 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.
|
||||
- [Use aider inside your favorite editor or IDE](https://aider.chat/docs/usage/watch.html).
|
||||
- Aider works with [most popular languages](https://aider.chat/docs/languages.html): python, javascript, typescript, php, html, css, and more...
|
||||
- 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 or IDE 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).
|
||||
- Aider works best with Claude 3.5 Sonnet, DeepSeek V3, o1 & GPT-4o and can [connect to almost any LLM](https://aider.chat/docs/llms.html).
|
||||
|
||||
|
||||
## Top tier performance
|
||||
|
||||
[Aider has one of the top scores on SWE Bench](https://aider.chat/2024/06/02/main-swe-bench.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
|
||||
|
||||
- [Documentation](https://aider.chat/)
|
||||
- [Installation](https://aider.chat/docs/install.html)
|
||||
- [Usage](https://aider.chat/docs/usage.html)
|
||||
- [Tutorial videos](https://aider.chat/docs/usage/tutorials.html)
|
||||
### 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)
|
||||
- [Configuration](https://aider.chat/docs/config.html)
|
||||
- [Configuration Options](https://aider.chat/docs/config.html)
|
||||
- [Troubleshooting](https://aider.chat/docs/troubleshooting.html)
|
||||
- [FAQ](https://aider.chat/docs/faq.html)
|
||||
|
||||
### Community & Resources
|
||||
- [LLM Leaderboards](https://aider.chat/docs/leaderboards/)
|
||||
- [GitHub](https://github.com/Aider-AI/aider)
|
||||
- [Discord](https://discord.gg/Tv2uQnR88V)
|
||||
- [GitHub Repository](https://github.com/Aider-AI/aider)
|
||||
- [Discord Community](https://discord.gg/Tv2uQnR88V)
|
||||
- [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
|
||||
|
||||
__version__ = "0.74.3.dev"
|
||||
__version__ = "0.81.2.dev"
|
||||
safe_version = __version__
|
||||
|
||||
try:
|
||||
|
||||
153
aider/args.py
153
aider/args.py
@@ -3,6 +3,7 @@
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import configargparse
|
||||
|
||||
@@ -12,10 +13,20 @@ from aider.args_formatter import (
|
||||
MarkdownHelpFormatter,
|
||||
YamlHelpFormatter,
|
||||
)
|
||||
from aider.deprecated import add_deprecated_model_args
|
||||
|
||||
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):
|
||||
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,
|
||||
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-chat"
|
||||
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")
|
||||
@@ -208,6 +127,11 @@ def get_parser(default_config_files, git_root):
|
||||
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(
|
||||
"--verify-ssl",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
@@ -234,6 +158,12 @@ def get_parser(default_config_files, git_root):
|
||||
const="architect",
|
||||
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(
|
||||
"--weak-model",
|
||||
metavar="WEAK_MODEL",
|
||||
@@ -261,6 +191,14 @@ def get_parser(default_config_files, git_root):
|
||||
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(
|
||||
"--max-chat-history-tokens",
|
||||
type=int,
|
||||
@@ -460,9 +398,11 @@ def get_parser(default_config_files, git_root):
|
||||
default_aiderignore_file = (
|
||||
os.path.join(git_root, ".aiderignore") if git_root else ".aiderignore"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--aiderignore",
|
||||
metavar="AIDERIGNORE",
|
||||
type=lambda path_str: resolve_aiderignore_path(path_str, git_root),
|
||||
default=default_aiderignore_file,
|
||||
help="Specify the aider ignore file (default: .aiderignore in git root)",
|
||||
)
|
||||
@@ -508,6 +448,12 @@ def get_parser(default_config_files, git_root):
|
||||
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(
|
||||
"--commit",
|
||||
action="store_true",
|
||||
@@ -813,6 +759,24 @@ def get_parser(default_config_files, git_root):
|
||||
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(
|
||||
"--detect-urls",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
@@ -824,6 +788,11 @@ def get_parser(default_config_files, git_root):
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -148,11 +148,14 @@ class YamlHelpFormatter(argparse.HelpFormatter):
|
||||
parts.append(f"#{switch}: xxx")
|
||||
parts.append("## Specify multiple values like this:")
|
||||
parts.append(f"#{switch}:")
|
||||
parts.append(f"# - xxx")
|
||||
parts.append(f"# - yyy")
|
||||
parts.append(f"# - zzz")
|
||||
parts.append("# - xxx")
|
||||
parts.append("# - yyy")
|
||||
parts.append("# - zzz")
|
||||
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))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from .architect_coder import ArchitectCoder
|
||||
from .ask_coder import AskCoder
|
||||
from .base_coder import Coder
|
||||
from .context_coder import ContextCoder
|
||||
from .editblock_coder import EditBlockCoder
|
||||
from .editblock_fenced_coder import EditBlockFencedCoder
|
||||
from .editor_editblock_coder import EditorEditBlockCoder
|
||||
@@ -23,4 +24,5 @@ __all__ = [
|
||||
ArchitectCoder,
|
||||
EditorEditBlockCoder,
|
||||
EditorWholeFileCoder,
|
||||
ContextCoder,
|
||||
]
|
||||
|
||||
@@ -6,6 +6,7 @@ from .base_coder import Coder
|
||||
class ArchitectCoder(AskCoder):
|
||||
edit_format = "architect"
|
||||
gpt_prompts = ArchitectPrompts()
|
||||
auto_accept_architect = False
|
||||
|
||||
def reply_completed(self):
|
||||
content = self.partial_response_content
|
||||
@@ -13,7 +14,7 @@ class ArchitectCoder(AskCoder):
|
||||
if not content or not content.strip():
|
||||
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
|
||||
|
||||
kwargs = dict()
|
||||
|
||||
@@ -28,6 +28,12 @@ from aider.io import ConfirmGroup, InputOutput
|
||||
from aider.linter import Linter
|
||||
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.repomap import RepoMap
|
||||
from aider.run_cmd import run_cmd
|
||||
@@ -201,10 +207,22 @@ class Coder:
|
||||
prefix = "Model"
|
||||
|
||||
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:
|
||||
output += ", prompt cache"
|
||||
if main_model.info.get("supports_assistant_prefill"):
|
||||
output += ", infinite output"
|
||||
|
||||
lines.append(output)
|
||||
|
||||
if self.edit_format == "architect":
|
||||
@@ -304,6 +322,7 @@ class Coder:
|
||||
ignore_mentions=None,
|
||||
file_watcher=None,
|
||||
auto_copy_context=False,
|
||||
auto_accept_architect=True,
|
||||
):
|
||||
# Fill in a dummy Analytics if needed, but it is never .enable()'d
|
||||
self.analytics = analytics if analytics is not None else Analytics()
|
||||
@@ -316,6 +335,7 @@ class Coder:
|
||||
self.abs_root_path_cache = {}
|
||||
|
||||
self.auto_copy_context = auto_copy_context
|
||||
self.auto_accept_architect = auto_accept_architect
|
||||
|
||||
self.ignore_mentions = ignore_mentions
|
||||
if not self.ignore_mentions:
|
||||
@@ -375,6 +395,10 @@ class Coder:
|
||||
self.pretty = self.io.pretty
|
||||
|
||||
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
|
||||
|
||||
@@ -898,7 +922,8 @@ class Coder:
|
||||
else:
|
||||
self.io.tool_error(text)
|
||||
|
||||
url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*)")
|
||||
# Exclude double quotes from the matched URL characters
|
||||
url_pattern = re.compile(r'(https?://[^\s/$.?#].[^\s"]*)')
|
||||
urls = list(set(url_pattern.findall(text))) # Use set to remove duplicates
|
||||
for url in urls:
|
||||
url = url.rstrip(".',\"")
|
||||
@@ -910,7 +935,8 @@ class Coder:
|
||||
if not self.detect_urls:
|
||||
return inp
|
||||
|
||||
url_pattern = re.compile(r"(https?://[^\s/$.?#].[^\s]*[^\s,.])")
|
||||
# Exclude double quotes from the matched URL characters
|
||||
url_pattern = re.compile(r'(https?://[^\s/$.?#].[^\s"]*[^\s,.])')
|
||||
urls = list(set(url_pattern.findall(inp))) # Use set to remove duplicates
|
||||
group = ConfirmGroup(urls)
|
||||
for url in urls:
|
||||
@@ -1006,7 +1032,13 @@ class Coder:
|
||||
return None
|
||||
|
||||
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_val = os.getenv(shell_var)
|
||||
platform_text += f"- Shell: {shell_var}={shell_val}\n"
|
||||
@@ -1047,7 +1079,13 @@ class Coder:
|
||||
return platform_text
|
||||
|
||||
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()
|
||||
|
||||
if self.suggest_shell_commands:
|
||||
@@ -1280,6 +1318,9 @@ class Coder:
|
||||
def send_message(self, inp):
|
||||
self.event("message_send_starting")
|
||||
|
||||
# Notify IO that LLM processing is starting
|
||||
self.io.llm_started()
|
||||
|
||||
self.cur_messages += [
|
||||
dict(role="user", content=inp),
|
||||
]
|
||||
@@ -1369,11 +1410,14 @@ class Coder:
|
||||
self.mdstream = None
|
||||
|
||||
self.partial_response_content = self.get_multi_response_content_in_progress(True)
|
||||
self.partial_response_content = self.main_model.remove_reasoning_content(
|
||||
self.partial_response_content
|
||||
)
|
||||
self.remove_reasoning_content()
|
||||
self.multi_response_content = ""
|
||||
|
||||
###
|
||||
# print()
|
||||
# print("=" * 20)
|
||||
# dump(self.partial_response_content)
|
||||
|
||||
self.io.tool_output()
|
||||
|
||||
self.show_usage_report()
|
||||
@@ -1414,7 +1458,8 @@ class Coder:
|
||||
return
|
||||
|
||||
try:
|
||||
self.reply_completed()
|
||||
if self.reply_completed():
|
||||
return
|
||||
except KeyboardInterrupt:
|
||||
interrupted = True
|
||||
|
||||
@@ -1557,22 +1602,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())
|
||||
|
||||
# drop sentence punctuation from the end
|
||||
words = set(word.rstrip(",.!;:?") for word in words)
|
||||
|
||||
# strip away all kinds of quotes
|
||||
quotes = "".join(['"', "'", "`"])
|
||||
quotes = "\"'`*_"
|
||||
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
|
||||
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
|
||||
}
|
||||
# Get basenames of files already in chat or read-only
|
||||
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
|
||||
}
|
||||
|
||||
mentioned_rel_fnames = set()
|
||||
fname_to_rel_fnames = {}
|
||||
@@ -1623,6 +1672,9 @@ class Coder:
|
||||
return prompts.added_files.format(fnames=", ".join(added_fnames))
|
||||
|
||||
def send(self, messages, model=None, functions=None):
|
||||
self.got_reasoning_content = False
|
||||
self.ended_reasoning_content = False
|
||||
|
||||
if not model:
|
||||
model = self.main_model
|
||||
|
||||
@@ -1690,6 +1742,14 @@ class Coder:
|
||||
except AttributeError as 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:
|
||||
self.partial_response_content = completion.choices[0].message.content or ""
|
||||
except AttributeError as content_err:
|
||||
@@ -1708,6 +1768,15 @@ class Coder:
|
||||
raise Exception("No data found in LLM response!")
|
||||
|
||||
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())
|
||||
|
||||
if (
|
||||
@@ -1717,6 +1786,8 @@ class Coder:
|
||||
raise FinishReasonLength()
|
||||
|
||||
def show_send_output_stream(self, completion):
|
||||
received_content = False
|
||||
|
||||
for chunk in completion:
|
||||
if len(chunk.choices) == 0:
|
||||
continue
|
||||
@@ -1735,19 +1806,46 @@ class Coder:
|
||||
self.partial_response_function_call[k] += v
|
||||
else:
|
||||
self.partial_response_function_call[k] = v
|
||||
received_content = True
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
text = ""
|
||||
|
||||
try:
|
||||
text = chunk.choices[0].delta.content
|
||||
if text:
|
||||
self.partial_response_content += text
|
||||
reasoning_content = chunk.choices[0].delta.reasoning_content
|
||||
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():
|
||||
self.live_incremental_response(False)
|
||||
elif text:
|
||||
# Apply reasoning tag formatting
|
||||
text = replace_reasoning_tags(text, self.reasoning_tag_name)
|
||||
try:
|
||||
sys.stdout.write(text)
|
||||
except UnicodeEncodeError:
|
||||
@@ -1759,13 +1857,26 @@ class Coder:
|
||||
sys.stdout.flush()
|
||||
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):
|
||||
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)
|
||||
|
||||
def render_incremental_response(self, final):
|
||||
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):
|
||||
prompt_tokens = 0
|
||||
completion_tokens = 0
|
||||
@@ -1852,11 +1963,6 @@ class Coder:
|
||||
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:
|
||||
sep = "\n"
|
||||
else:
|
||||
|
||||
@@ -14,6 +14,9 @@ You NEVER leave comments describing code without implementing it!
|
||||
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 = []
|
||||
|
||||
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.
|
||||
"""
|
||||
@@ -17,6 +17,7 @@ from aider import models, prompts, voice
|
||||
from aider.editor import pipe_editor
|
||||
from aider.format_settings import format_settings
|
||||
from aider.help import Help, install_help_extra
|
||||
from aider.io import CommandCompletionException
|
||||
from aider.llm import litellm
|
||||
from aider.repo import ANY_GIT_ERROR
|
||||
from aider.run_cmd import run_cmd
|
||||
@@ -27,8 +28,9 @@ from .dump import dump # noqa: F401
|
||||
|
||||
|
||||
class SwitchCoder(Exception):
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, placeholder=None, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
self.placeholder = placeholder
|
||||
|
||||
|
||||
class Commands:
|
||||
@@ -59,6 +61,7 @@ class Commands:
|
||||
parser=None,
|
||||
verbose=False,
|
||||
editor=None,
|
||||
original_read_only_fnames=None,
|
||||
):
|
||||
self.io = io
|
||||
self.coder = coder
|
||||
@@ -77,11 +80,52 @@ class Commands:
|
||||
self.help = None
|
||||
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):
|
||||
"Switch to a new LLM"
|
||||
"Switch the Main Model to a new LLM"
|
||||
|
||||
model_name = args.strip()
|
||||
model = models.Model(model_name, weak_model=self.coder.main_model.weak_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)
|
||||
raise SwitchCoder(main_model=model)
|
||||
|
||||
@@ -114,6 +158,10 @@ class Commands:
|
||||
" them."
|
||||
),
|
||||
),
|
||||
(
|
||||
"context",
|
||||
"Automatically identify which files will need to be edited.",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -355,7 +403,21 @@ class Commands:
|
||||
|
||||
def _drop_all_files(self):
|
||||
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):
|
||||
self.coder.done_messages = []
|
||||
@@ -404,6 +466,7 @@ class Commands:
|
||||
|
||||
fence = "`" * 3
|
||||
|
||||
file_res = []
|
||||
# files
|
||||
for fname in self.coder.abs_fnames:
|
||||
relative_fname = self.coder.get_rel_fname(fname)
|
||||
@@ -414,7 +477,7 @@ class Commands:
|
||||
# approximate
|
||||
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
|
||||
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
|
||||
for fname in self.coder.abs_read_only_fnames:
|
||||
@@ -424,7 +487,10 @@ class Commands:
|
||||
# approximate
|
||||
content = f"{relative_fname}\n{fence}\n" + content + "{fence}\n"
|
||||
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(
|
||||
f"Approximate context window usage for {self.coder.main_model.name}, in tokens:"
|
||||
@@ -818,7 +884,12 @@ class Commands:
|
||||
"Remove files from the chat session to free up context space"
|
||||
|
||||
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()
|
||||
return
|
||||
|
||||
@@ -943,9 +1014,15 @@ class Commands:
|
||||
dict(role="assistant", content="Ok."),
|
||||
]
|
||||
|
||||
if add and exit_status != 0:
|
||||
if add_on_nonzero_exit and exit_status != 0:
|
||||
# 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):
|
||||
"Exit the application"
|
||||
self.coder.event("exit", reason="/exit")
|
||||
@@ -1061,6 +1138,18 @@ class Commands:
|
||||
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):
|
||||
"""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")
|
||||
@@ -1073,7 +1162,11 @@ class Commands:
|
||||
"""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")
|
||||
|
||||
def _generic_chat_command(self, args, edit_format):
|
||||
def cmd_context(self, args):
|
||||
"""Enter context mode to see surrounding code context. If no prompt provided, switches to context mode.""" # noqa
|
||||
return self._generic_chat_command(args, "context", placeholder=args.strip() or None)
|
||||
|
||||
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)
|
||||
@@ -1090,11 +1183,13 @@ class Commands:
|
||||
user_msg = args
|
||||
coder.run(user_msg)
|
||||
|
||||
# Use the provided placeholder if any
|
||||
raise SwitchCoder(
|
||||
edit_format=self.coder.edit_format,
|
||||
summarize_from_coder=False,
|
||||
from_coder=coder,
|
||||
show_announcements=False,
|
||||
placeholder=placeholder,
|
||||
)
|
||||
|
||||
def get_help_md(self):
|
||||
@@ -1407,6 +1502,62 @@ class Commands:
|
||||
if user_input.strip():
|
||||
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):
|
||||
"""Copy the current chat context as markdown, suitable to paste into a web UI"""
|
||||
|
||||
|
||||
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 platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
from aider.dump import dump # noqa
|
||||
|
||||
DEFAULT_EDITOR_NIX = "vi"
|
||||
DEFAULT_EDITOR_OS_X = "vim"
|
||||
DEFAULT_EDITOR_WINDOWS = "notepad"
|
||||
@@ -87,13 +88,13 @@ def get_environment_editor(default=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
|
||||
with spaces (e.g. 'vim -c "set noswapfile"').
|
||||
|
||||
:return: A list of command parts ready for subprocess execution
|
||||
:rtype: list[str]
|
||||
:return: The editor command as a string
|
||||
:rtype: str
|
||||
"""
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
@@ -102,14 +103,13 @@ def discover_editor(editor_override=None):
|
||||
default_editor = DEFAULT_EDITOR_OS_X
|
||||
else:
|
||||
default_editor = DEFAULT_EDITOR_NIX
|
||||
|
||||
if editor_override:
|
||||
editor = editor_override
|
||||
else:
|
||||
editor = get_environment_editor(default_editor)
|
||||
try:
|
||||
return shlex.split(editor)
|
||||
except ValueError as e:
|
||||
raise RuntimeError(f"Invalid editor command format '{editor}': {e}")
|
||||
|
||||
return editor
|
||||
|
||||
|
||||
def pipe_editor(input_data="", suffix=None, editor=None):
|
||||
@@ -128,9 +128,10 @@ def pipe_editor(input_data="", suffix=None, editor=None):
|
||||
:rtype: str
|
||||
"""
|
||||
filepath = write_temp_file(input_data, suffix)
|
||||
command_parts = discover_editor(editor)
|
||||
command_parts.append(filepath)
|
||||
subprocess.call(command_parts)
|
||||
command_str = discover_editor(editor)
|
||||
command_str += " " + filepath
|
||||
|
||||
subprocess.call(command_str, shell=True)
|
||||
with open(filepath, "r") as f:
|
||||
output_data = f.read()
|
||||
try:
|
||||
|
||||
@@ -83,4 +83,25 @@ class LiteLLMExceptions:
|
||||
)
|
||||
if "boto3" in str(ex):
|
||||
return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
|
||||
if "OpenrouterException" in str(ex) and "'choices'" in str(ex):
|
||||
return ExInfo(
|
||||
"APIConnectionError",
|
||||
True,
|
||||
(
|
||||
"OpenRouter or the upstream API provider is down, overloaded or rate"
|
||||
" limiting your requests."
|
||||
),
|
||||
)
|
||||
|
||||
# Check for specific non-retryable APIError cases like insufficient credits
|
||||
if ex.__class__ is litellm.APIError:
|
||||
err_str = str(ex).lower()
|
||||
if "insufficient credits" in err_str and '"code":402' in err_str:
|
||||
return ExInfo(
|
||||
"APIError",
|
||||
False,
|
||||
"Insufficient credits with the API provider. Please add credits.",
|
||||
)
|
||||
# Fall through to default APIError handling if not the specific credits error
|
||||
|
||||
return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
|
||||
|
||||
@@ -10,4 +10,10 @@ exclude_website_pats = [
|
||||
"docs/unified-diffs.md",
|
||||
"docs/leaderboards/index.md",
|
||||
"assets/**",
|
||||
".jekyll-metadata",
|
||||
"Gemfile.lock",
|
||||
"Gemfile",
|
||||
"_config.yml",
|
||||
"**/OLD/**",
|
||||
"OLD/**",
|
||||
]
|
||||
|
||||
261
aider/io.py
261
aider/io.py
@@ -1,7 +1,9 @@
|
||||
import base64
|
||||
import functools
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
import webbrowser
|
||||
from collections import defaultdict
|
||||
@@ -16,6 +18,7 @@ from prompt_toolkit.enums import EditingMode
|
||||
from prompt_toolkit.filters import Condition, is_searching
|
||||
from prompt_toolkit.history import FileHistory
|
||||
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.lexers import PygmentsLexer
|
||||
from prompt_toolkit.output.vt100 import is_dumb_terminal
|
||||
@@ -23,6 +26,7 @@ from prompt_toolkit.shortcuts import CompleteStyle, PromptSession
|
||||
from prompt_toolkit.styles import Style
|
||||
from pygments.lexers import MarkdownLexer, guess_lexer_for_filename
|
||||
from pygments.token import Token
|
||||
from rich.color import ColorParseError
|
||||
from rich.columns import Columns
|
||||
from rich.console import Console
|
||||
from rich.markdown import Markdown
|
||||
@@ -32,8 +36,23 @@ from rich.text import Text
|
||||
from aider.mdstream import MarkdownStream
|
||||
|
||||
from .dump import dump # noqa: F401
|
||||
from .editor import pipe_editor
|
||||
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"""
|
||||
@@ -52,6 +71,13 @@ def restore_multiline(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
class CommandCompletionException(Exception):
|
||||
"""Raised when a command should use the normal autocompleter instead of
|
||||
command-specific completion."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfirmGroup:
|
||||
preference: str = None
|
||||
@@ -170,14 +196,23 @@ class AutoCompleter(Completer):
|
||||
return
|
||||
|
||||
if text[0] == "/":
|
||||
yield from self.get_command_completions(document, complete_event, text, words)
|
||||
return
|
||||
try:
|
||||
yield from self.get_command_completions(document, complete_event, text, words)
|
||||
return
|
||||
except CommandCompletionException:
|
||||
# Fall through to normal completion
|
||||
pass
|
||||
|
||||
candidates = self.words
|
||||
candidates.update(set(self.fname_to_rel_fnames))
|
||||
candidates = [word if type(word) is tuple else (word, word) for word in candidates]
|
||||
|
||||
last_word = words[-1]
|
||||
|
||||
# Only provide completions if the user has typed at least 3 characters
|
||||
if len(last_word) < 3:
|
||||
return
|
||||
|
||||
completions = []
|
||||
for word_match, word_insert in candidates:
|
||||
if word_match.lower().startswith(last_word.lower()):
|
||||
@@ -196,6 +231,8 @@ class InputOutput:
|
||||
num_error_outputs = 0
|
||||
num_user_asks = 0
|
||||
clipboard_watcher = None
|
||||
bell_on_next_input = False
|
||||
notifications_command = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -224,25 +261,40 @@ class InputOutput:
|
||||
file_watcher=None,
|
||||
multiline_mode=False,
|
||||
root=".",
|
||||
notifications=False,
|
||||
notifications_command=None,
|
||||
):
|
||||
self.placeholder = None
|
||||
self.interrupted = False
|
||||
self.never_prompts = set()
|
||||
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")
|
||||
if no_color is not None and no_color != "":
|
||||
pretty = False
|
||||
|
||||
self.user_input_color = user_input_color if pretty else None
|
||||
self.tool_output_color = tool_output_color if pretty else None
|
||||
self.tool_error_color = tool_error_color if pretty else None
|
||||
self.tool_warning_color = tool_warning_color if pretty else None
|
||||
self.assistant_output_color = assistant_output_color
|
||||
self.completion_menu_color = completion_menu_color if pretty else None
|
||||
self.completion_menu_bg_color = completion_menu_bg_color if pretty else None
|
||||
self.completion_menu_current_color = completion_menu_current_color if pretty else None
|
||||
self.completion_menu_current_bg_color = completion_menu_current_bg_color if pretty else None
|
||||
self.user_input_color = ensure_hash_prefix(user_input_color) if pretty else None
|
||||
self.tool_output_color = ensure_hash_prefix(tool_output_color) if pretty else None
|
||||
self.tool_error_color = ensure_hash_prefix(tool_error_color) if pretty else None
|
||||
self.tool_warning_color = ensure_hash_prefix(tool_warning_color) if pretty else None
|
||||
self.assistant_output_color = ensure_hash_prefix(assistant_output_color)
|
||||
self.completion_menu_color = ensure_hash_prefix(completion_menu_color) if pretty else None
|
||||
self.completion_menu_bg_color = (
|
||||
ensure_hash_prefix(completion_menu_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
|
||||
|
||||
@@ -310,6 +362,35 @@ class InputOutput:
|
||||
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):
|
||||
style_dict = {}
|
||||
if not self.pretty:
|
||||
@@ -335,9 +416,9 @@ class InputOutput:
|
||||
# Conditionally add 'completion-menu.completion.current' style
|
||||
completion_menu_current_style = []
|
||||
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:
|
||||
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:
|
||||
style_dict["completion-menu.completion.current"] = " ".join(
|
||||
completion_menu_current_style
|
||||
@@ -444,6 +525,9 @@ class InputOutput:
|
||||
):
|
||||
self.rule()
|
||||
|
||||
# Ring the bell if needed
|
||||
self.ring_bell()
|
||||
|
||||
rel_fnames = list(rel_fnames)
|
||||
show = ""
|
||||
if rel_fnames:
|
||||
@@ -451,11 +535,16 @@ class InputOutput:
|
||||
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)
|
||||
|
||||
prompt_prefix = ""
|
||||
if edit_format:
|
||||
show += edit_format
|
||||
prompt_prefix += edit_format
|
||||
if self.multiline_mode:
|
||||
show += (" " if edit_format else "") + "multi"
|
||||
show += "> "
|
||||
prompt_prefix += (" " if edit_format else "") + "multi"
|
||||
prompt_prefix += "> "
|
||||
|
||||
show += prompt_prefix
|
||||
self.prompt_prefix = prompt_prefix
|
||||
|
||||
inp = ""
|
||||
multiline_input = False
|
||||
@@ -499,11 +588,30 @@ class InputOutput:
|
||||
"Navigate forward through history"
|
||||
event.current_buffer.history_forward()
|
||||
|
||||
@kb.add("c-x", "c-e")
|
||||
def _(event):
|
||||
"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:
|
||||
# In multiline mode, Enter adds a newline
|
||||
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
|
||||
@@ -521,7 +629,7 @@ class InputOutput:
|
||||
|
||||
while True:
|
||||
if multiline_input:
|
||||
show = ". "
|
||||
show = self.prompt_prefix
|
||||
|
||||
try:
|
||||
if self.prompt_session:
|
||||
@@ -537,7 +645,7 @@ class InputOutput:
|
||||
self.clipboard_watcher.start()
|
||||
|
||||
def get_continuation(width, line_number, is_soft_wrap):
|
||||
return ". "
|
||||
return self.prompt_prefix
|
||||
|
||||
line = self.prompt_session.prompt(
|
||||
show,
|
||||
@@ -696,6 +804,9 @@ class InputOutput:
|
||||
):
|
||||
self.num_user_asks += 1
|
||||
|
||||
# Ring the bell if needed
|
||||
self.ring_bell()
|
||||
|
||||
question_id = (question, subject)
|
||||
|
||||
if question_id in self.never_prompts:
|
||||
@@ -750,14 +861,19 @@ class InputOutput:
|
||||
self.user_input(f"{question}{res}", log_only=False)
|
||||
else:
|
||||
while True:
|
||||
if self.prompt_session:
|
||||
res = self.prompt_session.prompt(
|
||||
question,
|
||||
style=style,
|
||||
complete_while_typing=False,
|
||||
)
|
||||
else:
|
||||
res = input(question)
|
||||
try:
|
||||
if self.prompt_session:
|
||||
res = self.prompt_session.prompt(
|
||||
question,
|
||||
style=style,
|
||||
complete_while_typing=False,
|
||||
)
|
||||
else:
|
||||
res = input(question)
|
||||
except EOFError:
|
||||
# Treat EOF (Ctrl+D) as if the user pressed Enter
|
||||
res = default
|
||||
break
|
||||
|
||||
if not res:
|
||||
res = default
|
||||
@@ -801,6 +917,9 @@ class InputOutput:
|
||||
def prompt_ask(self, question, default="", subject=None):
|
||||
self.num_user_asks += 1
|
||||
|
||||
# Ring the bell if needed
|
||||
self.ring_bell()
|
||||
|
||||
if subject:
|
||||
self.tool_output()
|
||||
self.tool_output(subject, bold=True)
|
||||
@@ -812,15 +931,19 @@ class InputOutput:
|
||||
elif self.yes is False:
|
||||
res = "no"
|
||||
else:
|
||||
if self.prompt_session:
|
||||
res = self.prompt_session.prompt(
|
||||
question + " ",
|
||||
default=default,
|
||||
style=style,
|
||||
complete_while_typing=True,
|
||||
)
|
||||
else:
|
||||
res = input(question + " ")
|
||||
try:
|
||||
if self.prompt_session:
|
||||
res = self.prompt_session.prompt(
|
||||
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()}"
|
||||
self.append_chat_history(hist, linebreak=True, blockquote=True)
|
||||
@@ -840,6 +963,7 @@ class InputOutput:
|
||||
|
||||
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()
|
||||
try:
|
||||
self.console.print(message, **style)
|
||||
@@ -870,7 +994,7 @@ class InputOutput:
|
||||
style = dict()
|
||||
if self.pretty:
|
||||
if self.tool_output_color:
|
||||
style["color"] = self.tool_output_color
|
||||
style["color"] = ensure_hash_prefix(self.tool_output_color)
|
||||
style["reverse"] = bold
|
||||
|
||||
style = RichStyle(**style)
|
||||
@@ -882,6 +1006,10 @@ class InputOutput:
|
||||
return mdStream
|
||||
|
||||
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
|
||||
|
||||
# Coder will force pretty off if fence is not triple-backticks
|
||||
@@ -893,7 +1021,7 @@ class InputOutput:
|
||||
message, style=self.assistant_output_color, code_theme=self.code_theme
|
||||
)
|
||||
else:
|
||||
show_resp = Text(message or "<no response>")
|
||||
show_resp = Text(message or "(empty response)")
|
||||
|
||||
self.console.print(show_resp)
|
||||
|
||||
@@ -904,6 +1032,61 @@ class InputOutput:
|
||||
def print(self, 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
|
||||
|
||||
@@ -4,11 +4,12 @@ import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
import warnings
|
||||
import shlex
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
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.run_cmd import run_cmd_subprocess # noqa: F401
|
||||
@@ -44,7 +45,7 @@ class Linter:
|
||||
return fname
|
||||
|
||||
def run_cmd(self, cmd, rel_fname, code):
|
||||
cmd += " " + rel_fname
|
||||
cmd += " " + shlex.quote(rel_fname)
|
||||
|
||||
returncode = 0
|
||||
stdout = ""
|
||||
|
||||
168
aider/main.py
168
aider/main.py
@@ -1,4 +1,3 @@
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -25,11 +24,13 @@ from aider.coders import Coder
|
||||
from aider.coders.base_coder import UnknownEditFormat
|
||||
from aider.commands import Commands, SwitchCoder
|
||||
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.history import ChatSummary
|
||||
from aider.io import InputOutput
|
||||
from aider.llm import litellm # noqa: F401; properly init litellm on launch
|
||||
from aider.models import ModelSettings
|
||||
from aider.onboarding import offer_openrouter_oauth, select_default_model
|
||||
from aider.repo import ANY_GIT_ERROR, GitRepo
|
||||
from aider.report import report_uncaught_exceptions
|
||||
from aider.versioncheck import check_version, install_from_main_branch, install_upgrade
|
||||
@@ -126,17 +127,15 @@ def setup_git(git_root, io):
|
||||
if not repo:
|
||||
return
|
||||
|
||||
user_name = None
|
||||
user_email = None
|
||||
with repo.config_reader() as config:
|
||||
try:
|
||||
user_name = config.get_value("user", "name", None)
|
||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||
pass
|
||||
try:
|
||||
user_email = config.get_value("user", "email", None)
|
||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||
pass
|
||||
try:
|
||||
user_name = repo.git.config("--get", "user.name") or None
|
||||
except git.exc.GitCommandError:
|
||||
user_name = None
|
||||
|
||||
try:
|
||||
user_email = repo.git.config("--get", "user.email") or None
|
||||
except git.exc.GitCommandError:
|
||||
user_email = None
|
||||
|
||||
if user_name and user_email:
|
||||
return repo.working_tree_dir
|
||||
@@ -359,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"):
|
||||
# Standard .env file search path
|
||||
dotenv_files = generate_search_path_list(
|
||||
".env",
|
||||
git_root,
|
||||
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 = []
|
||||
for fname in dotenv_files:
|
||||
try:
|
||||
@@ -507,6 +516,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
litellm._load_litellm()
|
||||
litellm._lazy_module.client_session = httpx.Client(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:
|
||||
models.request_timeout = args.timeout
|
||||
@@ -555,6 +566,8 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
editingmode=editing_mode,
|
||||
fancy_input=args.fancy_input,
|
||||
multiline_mode=args.multiline,
|
||||
notifications=args.notifications,
|
||||
notifications_command=args.notifications_command,
|
||||
)
|
||||
|
||||
io = get_io(args.pretty)
|
||||
@@ -594,6 +607,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
|
||||
if 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:
|
||||
os.environ["OPENAI_API_BASE"] = args.openai_api_base
|
||||
if args.openai_api_version:
|
||||
@@ -709,11 +725,6 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
if args.check_update:
|
||||
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:
|
||||
git_root = setup_git(git_root, io)
|
||||
if args.gitignore:
|
||||
@@ -733,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_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
|
||||
if args.alias:
|
||||
for alias_def in args.alias:
|
||||
@@ -746,26 +762,49 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
alias, model = parts
|
||||
models.MODEL_ALIASES[alias.strip()] = model.strip()
|
||||
|
||||
if not args.model:
|
||||
# Select model based on available API keys
|
||||
model_key_pairs = [
|
||||
("ANTHROPIC_API_KEY", "sonnet"),
|
||||
("DEEPSEEK_API_KEY", "deepseek"),
|
||||
("OPENROUTER_API_KEY", "openrouter/anthropic/claude-3.5-sonnet"),
|
||||
("OPENAI_API_KEY", "gpt-4o"),
|
||||
("GEMINI_API_KEY", "flash"),
|
||||
]
|
||||
selected_model_name = select_default_model(args, io, analytics)
|
||||
if not selected_model_name:
|
||||
# Error message and analytics event are handled within select_default_model
|
||||
# It might have already offered OAuth if no model/keys were found.
|
||||
# If it failed here, we exit.
|
||||
return 1
|
||||
args.model = selected_model_name # Update args with the selected model
|
||||
|
||||
for env_key, model_name in model_key_pairs:
|
||||
if os.environ.get(env_key):
|
||||
args.model = model_name
|
||||
io.tool_warning(
|
||||
f"Found {env_key} so using {model_name} since no --model was specified."
|
||||
# Check if an OpenRouter model was selected/specified but the key is missing
|
||||
if args.model.startswith("openrouter/") and not os.environ.get("OPENROUTER_API_KEY"):
|
||||
io.tool_warning(
|
||||
f"The specified model '{args.model}' requires an OpenRouter API key, which was not"
|
||||
" found."
|
||||
)
|
||||
# Attempt OAuth flow because the specific model needs it
|
||||
if offer_openrouter_oauth(io, analytics):
|
||||
# OAuth succeeded, the key should now be in os.environ.
|
||||
# Check if the key is now present after the flow.
|
||||
if os.environ.get("OPENROUTER_API_KEY"):
|
||||
io.tool_output(
|
||||
"OpenRouter successfully connected."
|
||||
) # Inform user connection worked
|
||||
else:
|
||||
# This case should ideally not happen if offer_openrouter_oauth succeeded
|
||||
# but check defensively.
|
||||
io.tool_error(
|
||||
"OpenRouter authentication seemed successful, but the key is still missing."
|
||||
)
|
||||
break
|
||||
if not args.model:
|
||||
io.tool_error("You need to specify a --model and an --api-key to use.")
|
||||
io.offer_url(urls.models_and_keys, "Open documentation url for more info?")
|
||||
analytics.event(
|
||||
"exit",
|
||||
reason="OpenRouter key missing after successful OAuth for specified model",
|
||||
)
|
||||
return 1
|
||||
else:
|
||||
# OAuth failed or was declined by the user
|
||||
io.tool_error(
|
||||
f"Unable to proceed without an OpenRouter API key for model '{args.model}'."
|
||||
)
|
||||
io.offer_url(urls.models_and_keys, "Open documentation URL for more info?")
|
||||
analytics.event(
|
||||
"exit",
|
||||
reason="OpenRouter key missing for specified model and OAuth failed/declined",
|
||||
)
|
||||
return 1
|
||||
|
||||
main_model = models.Model(
|
||||
@@ -773,15 +812,49 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
weak_model=args.weak_model,
|
||||
editor_model=args.editor_model,
|
||||
editor_edit_format=args.editor_edit_format,
|
||||
verbose=args.verbose,
|
||||
)
|
||||
|
||||
# add --reasoning-effort cli param
|
||||
# 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:
|
||||
if not getattr(main_model, "extra_params", None):
|
||||
main_model.extra_params = {}
|
||||
if "extra_body" not in main_model.extra_params:
|
||||
main_model.extra_params["extra_body"] = {}
|
||||
main_model.extra_params["extra_body"]["reasoning_effort"] = args.reasoning_effort
|
||||
# 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 main_model.edit_format in ("diff", "whole"):
|
||||
@@ -830,6 +903,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,
|
||||
commit_prompt=args.commit_prompt,
|
||||
subtree_only=args.subtree_only,
|
||||
git_commit_verify=args.git_commit_verify,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
@@ -855,6 +929,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
parser=parser,
|
||||
verbose=args.verbose,
|
||||
editor=args.editor,
|
||||
original_read_only_fnames=read_only_fnames,
|
||||
)
|
||||
|
||||
summarizer = ChatSummary(
|
||||
@@ -877,6 +952,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
else:
|
||||
map_tokens = args.map_tokens
|
||||
|
||||
# Track auto-commits configuration
|
||||
analytics.event("auto_commits", enabled=bool(args.auto_commits))
|
||||
|
||||
try:
|
||||
coder = Coder.create(
|
||||
main_model=main_model,
|
||||
@@ -909,6 +987,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
chat_language=args.chat_language,
|
||||
detect_urls=args.detect_urls,
|
||||
auto_copy_context=args.copy_paste,
|
||||
auto_accept_architect=args.auto_accept_architect,
|
||||
)
|
||||
except UnknownEditFormat as err:
|
||||
io.tool_error(str(err))
|
||||
@@ -1022,6 +1101,9 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
io.tool_output(f"Cur working dir: {Path.cwd()}")
|
||||
io.tool_output(f"Git working dir: {git_root}")
|
||||
|
||||
if args.stream and args.cache_prompts:
|
||||
io.tool_warning("Cost estimates may be inaccurate when using streaming and caching.")
|
||||
|
||||
if args.load:
|
||||
commands.cmd_load(args.load)
|
||||
|
||||
@@ -1067,6 +1149,10 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
|
||||
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.update(switch.kwargs)
|
||||
if "show_announcements" in kwargs:
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
import io
|
||||
import time
|
||||
|
||||
from rich import box
|
||||
from rich.console import Console
|
||||
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 aider.dump import dump # noqa: F401
|
||||
@@ -46,6 +49,46 @@ The end.
|
||||
""" # 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:
|
||||
"""Streaming markdown renderer that progressively displays content with a live updating window.
|
||||
|
||||
@@ -88,7 +131,7 @@ class MarkdownStream:
|
||||
# Render the markdown to a string buffer
|
||||
string_io = io.StringIO()
|
||||
console = Console(file=string_io, force_terminal=True)
|
||||
markdown = Markdown(text, **self.mdargs)
|
||||
markdown = NoInsetMarkdown(text, **self.mdargs)
|
||||
console.print(markdown)
|
||||
output = string_io.getvalue()
|
||||
|
||||
@@ -186,6 +229,7 @@ if __name__ == "__main__":
|
||||
_text = _text * 10
|
||||
|
||||
pm = MarkdownStream()
|
||||
print("Using NoInsetMarkdown for code blocks with padding=0")
|
||||
for i in range(6, len(_text), 5):
|
||||
pm.update(_text[:i])
|
||||
time.sleep(0.01)
|
||||
|
||||
243
aider/models.py
243
aider/models.py
@@ -5,7 +5,6 @@ import json
|
||||
import math
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass, fields
|
||||
@@ -19,6 +18,7 @@ from PIL import Image
|
||||
from aider.dump import dump # noqa: F401
|
||||
from aider.llm import litellm
|
||||
from aider.sendchat import ensure_alternating_roles, sanity_check_messages
|
||||
from aider.utils import check_pip_install_extra
|
||||
|
||||
RETRY_TIMEOUT = 60
|
||||
|
||||
@@ -76,7 +76,7 @@ ANTHROPIC_MODELS = [ln.strip() for ln in ANTHROPIC_MODELS.splitlines() if ln.str
|
||||
# Mapping of model aliases to their canonical names
|
||||
MODEL_ALIASES = {
|
||||
# Claude models
|
||||
"sonnet": "claude-3-5-sonnet-20241022",
|
||||
"sonnet": "anthropic/claude-3-7-sonnet-20250219",
|
||||
"haiku": "claude-3-5-haiku-20241022",
|
||||
"opus": "claude-3-opus-20240229",
|
||||
# GPT models
|
||||
@@ -88,8 +88,12 @@ MODEL_ALIASES = {
|
||||
"3": "gpt-3.5-turbo",
|
||||
# Other models
|
||||
"deepseek": "deepseek/deepseek-chat",
|
||||
"r1": "deepseek/deepseek-reasoner",
|
||||
"flash": "gemini/gemini-2.0-flash-exp",
|
||||
"quasar": "openrouter/openrouter/quasar-alpha",
|
||||
"r1": "deepseek/deepseek-reasoner",
|
||||
"gemini-2.5-pro": "gemini/gemini-2.5-pro-exp-03-25",
|
||||
"gemini": "gemini/gemini-2.5-pro-preview-03-25",
|
||||
"gemini-exp": "gemini/gemini-2.5-pro-exp-03-25",
|
||||
}
|
||||
# Model metadata loaded from resources and user's files.
|
||||
|
||||
@@ -103,6 +107,7 @@ class ModelSettings:
|
||||
use_repo_map: bool = False
|
||||
send_undo_reply: bool = False
|
||||
lazy: bool = False
|
||||
overeager: bool = False
|
||||
reminder: str = "user"
|
||||
examples_as_sys_msg: bool = False
|
||||
extra_params: Optional[dict] = None
|
||||
@@ -113,8 +118,10 @@ class ModelSettings:
|
||||
streaming: bool = True
|
||||
editor_model_name: Optional[str] = None
|
||||
editor_edit_format: Optional[str] = None
|
||||
remove_reasoning: Optional[str] = None
|
||||
reasoning_tag: Optional[str] = None
|
||||
remove_reasoning: Optional[str] = None # Deprecated alias for reasoning_tag
|
||||
system_prompt_prefix: Optional[str] = None
|
||||
accepts_settings: Optional[list] = None
|
||||
|
||||
|
||||
# Load model settings from package resource
|
||||
@@ -137,23 +144,37 @@ class ModelInfoManager:
|
||||
self.cache_file = self.cache_dir / "model_prices_and_context_window.json"
|
||||
self.content = None
|
||||
self.local_model_metadata = {}
|
||||
self._load_cache()
|
||||
self.verify_ssl = True
|
||||
self._cache_loaded = False
|
||||
|
||||
def set_verify_ssl(self, verify_ssl):
|
||||
self.verify_ssl = verify_ssl
|
||||
|
||||
def _load_cache(self):
|
||||
if self._cache_loaded:
|
||||
return
|
||||
|
||||
try:
|
||||
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
if self.cache_file.exists():
|
||||
cache_age = time.time() - self.cache_file.stat().st_mtime
|
||||
if cache_age < self.CACHE_TTL:
|
||||
self.content = json.loads(self.cache_file.read_text())
|
||||
try:
|
||||
self.content = json.loads(self.cache_file.read_text())
|
||||
except json.JSONDecodeError:
|
||||
# If the cache file is corrupted, treat it as missing
|
||||
self.content = None
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
self._cache_loaded = True
|
||||
|
||||
def _update_cache(self):
|
||||
try:
|
||||
import requests
|
||||
|
||||
response = requests.get(self.MODEL_INFO_URL, timeout=5)
|
||||
# Respect the --no-verify-ssl switch
|
||||
response = requests.get(self.MODEL_INFO_URL, timeout=5, verify=self.verify_ssl)
|
||||
if response.status_code == 200:
|
||||
self.content = response.json()
|
||||
try:
|
||||
@@ -173,6 +194,9 @@ class ModelInfoManager:
|
||||
if data:
|
||||
return data
|
||||
|
||||
# Ensure cache is loaded before checking content
|
||||
self._load_cache()
|
||||
|
||||
if not self.content:
|
||||
self._update_cache()
|
||||
|
||||
@@ -212,11 +236,14 @@ model_info_manager = ModelInfoManager()
|
||||
|
||||
|
||||
class Model(ModelSettings):
|
||||
def __init__(self, model, weak_model=None, editor_model=None, editor_edit_format=None):
|
||||
def __init__(
|
||||
self, model, weak_model=None, editor_model=None, editor_edit_format=None, verbose=False
|
||||
):
|
||||
# Map any alias to its canonical name
|
||||
model = MODEL_ALIASES.get(model, model)
|
||||
|
||||
self.name = model
|
||||
self.verbose = verbose
|
||||
|
||||
self.max_chat_history_tokens = 1024
|
||||
self.weak_model = None
|
||||
@@ -259,6 +286,11 @@ class Model(ModelSettings):
|
||||
val = getattr(source, field.name)
|
||||
setattr(self, field.name, val)
|
||||
|
||||
# Handle backward compatibility: if remove_reasoning is set but reasoning_tag isn't,
|
||||
# use remove_reasoning's value for reasoning_tag
|
||||
if self.reasoning_tag is None and self.remove_reasoning is not None:
|
||||
self.reasoning_tag = self.remove_reasoning
|
||||
|
||||
def configure_model_settings(self, model):
|
||||
# Look for exact model match
|
||||
exact_match = False
|
||||
@@ -269,6 +301,10 @@ class Model(ModelSettings):
|
||||
exact_match = True
|
||||
break # Continue to apply overrides
|
||||
|
||||
# Initialize accepts_settings if it's None
|
||||
if self.accepts_settings is None:
|
||||
self.accepts_settings = []
|
||||
|
||||
model = model.lower()
|
||||
|
||||
# If no exact match, try generic settings
|
||||
@@ -296,6 +332,8 @@ class Model(ModelSettings):
|
||||
self.use_repo_map = True
|
||||
self.use_temperature = False
|
||||
self.system_prompt_prefix = "Formatting re-enabled. "
|
||||
if "reasoning_effort" not in self.accepts_settings:
|
||||
self.accepts_settings.append("reasoning_effort")
|
||||
return # <--
|
||||
|
||||
if "/o1-mini" in model:
|
||||
@@ -317,6 +355,8 @@ class Model(ModelSettings):
|
||||
self.use_temperature = False
|
||||
self.streaming = False
|
||||
self.system_prompt_prefix = "Formatting re-enabled. "
|
||||
if "reasoning_effort" not in self.accepts_settings:
|
||||
self.accepts_settings.append("reasoning_effort")
|
||||
return # <--
|
||||
|
||||
if "deepseek" in model and "v3" in model:
|
||||
@@ -331,7 +371,7 @@ class Model(ModelSettings):
|
||||
self.use_repo_map = True
|
||||
self.examples_as_sys_msg = True
|
||||
self.use_temperature = False
|
||||
self.remove_reasoning = "think"
|
||||
self.reasoning_tag = "think"
|
||||
return # <--
|
||||
|
||||
if ("llama3" in model or "llama-3" in model) and "70b" in model:
|
||||
@@ -357,6 +397,15 @@ class Model(ModelSettings):
|
||||
self.reminder = "sys"
|
||||
return # <--
|
||||
|
||||
if "3-7-sonnet" in model:
|
||||
self.edit_format = "diff"
|
||||
self.use_repo_map = True
|
||||
self.examples_as_sys_msg = True
|
||||
self.reminder = "user"
|
||||
if "thinking_tokens" not in self.accepts_settings:
|
||||
self.accepts_settings.append("thinking_tokens")
|
||||
return # <--
|
||||
|
||||
if "3.5-sonnet" in model or "3-5-sonnet" in model:
|
||||
self.edit_format = "diff"
|
||||
self.use_repo_map = True
|
||||
@@ -380,6 +429,16 @@ class Model(ModelSettings):
|
||||
self.use_repo_map = True
|
||||
return # <--
|
||||
|
||||
if "qwq" in model and "32b" in model and "preview" not in model:
|
||||
self.edit_format = "diff"
|
||||
self.editor_edit_format = "editor-diff"
|
||||
self.use_repo_map = True
|
||||
self.reasoning_tag = "think"
|
||||
self.examples_as_sys_msg = True
|
||||
self.use_temperature = 0.6
|
||||
self.extra_params = dict(top_p=0.95)
|
||||
return # <--
|
||||
|
||||
# use the defaults
|
||||
if self.edit_format == "diff":
|
||||
self.use_repo_map = True
|
||||
@@ -535,6 +594,21 @@ class Model(ModelSettings):
|
||||
|
||||
model = self.name
|
||||
res = litellm.validate_environment(model)
|
||||
|
||||
# If missing AWS credential keys but AWS_PROFILE is set, consider AWS credentials valid
|
||||
if res["missing_keys"] and any(
|
||||
key in ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"] for key in res["missing_keys"]
|
||||
):
|
||||
if model.startswith("bedrock/") or model.startswith("us.anthropic."):
|
||||
if os.environ.get("AWS_PROFILE"):
|
||||
res["missing_keys"] = [
|
||||
k
|
||||
for k in res["missing_keys"]
|
||||
if k not in ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
||||
]
|
||||
if not res["missing_keys"]:
|
||||
res["keys_in_environment"] = True
|
||||
|
||||
if res["keys_in_environment"]:
|
||||
return res
|
||||
if res["missing_keys"]:
|
||||
@@ -559,6 +633,108 @@ class Model(ModelSettings):
|
||||
map_tokens = max(map_tokens, 1024)
|
||||
return map_tokens
|
||||
|
||||
def set_reasoning_effort(self, effort):
|
||||
"""Set the reasoning effort parameter for models that support it"""
|
||||
if effort is not None:
|
||||
if not self.extra_params:
|
||||
self.extra_params = {}
|
||||
if "extra_body" not in self.extra_params:
|
||||
self.extra_params["extra_body"] = {}
|
||||
self.extra_params["extra_body"]["reasoning_effort"] = effort
|
||||
|
||||
def parse_token_value(self, value):
|
||||
"""
|
||||
Parse a token value string into an integer.
|
||||
Accepts formats: 8096, "8k", "10.5k", "0.5M", "10K", etc.
|
||||
|
||||
Args:
|
||||
value: String or int token value
|
||||
|
||||
Returns:
|
||||
Integer token value
|
||||
"""
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
|
||||
if not isinstance(value, str):
|
||||
return int(value) # Try to convert to int
|
||||
|
||||
value = value.strip().upper()
|
||||
|
||||
if value.endswith("K"):
|
||||
multiplier = 1024
|
||||
value = value[:-1]
|
||||
elif value.endswith("M"):
|
||||
multiplier = 1024 * 1024
|
||||
value = value[:-1]
|
||||
else:
|
||||
multiplier = 1
|
||||
|
||||
# Convert to float first to handle decimal values like "10.5k"
|
||||
return int(float(value) * multiplier)
|
||||
|
||||
def set_thinking_tokens(self, value):
|
||||
"""
|
||||
Set the thinking token budget for models that support it.
|
||||
Accepts formats: 8096, "8k", "10.5k", "0.5M", "10K", etc.
|
||||
"""
|
||||
if value is not None:
|
||||
num_tokens = self.parse_token_value(value)
|
||||
self.use_temperature = False
|
||||
if not self.extra_params:
|
||||
self.extra_params = {}
|
||||
|
||||
# OpenRouter models use 'reasoning' instead of 'thinking'
|
||||
if self.name.startswith("openrouter/"):
|
||||
self.extra_params["reasoning"] = {"max_tokens": num_tokens}
|
||||
else:
|
||||
self.extra_params["thinking"] = {"type": "enabled", "budget_tokens": num_tokens}
|
||||
|
||||
def get_raw_thinking_tokens(self):
|
||||
"""Get formatted thinking token budget if available"""
|
||||
budget = None
|
||||
|
||||
if self.extra_params:
|
||||
# Check for OpenRouter reasoning format
|
||||
if "reasoning" in self.extra_params and "max_tokens" in self.extra_params["reasoning"]:
|
||||
budget = self.extra_params["reasoning"]["max_tokens"]
|
||||
# Check for standard thinking format
|
||||
elif (
|
||||
"thinking" in self.extra_params and "budget_tokens" in self.extra_params["thinking"]
|
||||
):
|
||||
budget = self.extra_params["thinking"]["budget_tokens"]
|
||||
|
||||
return budget
|
||||
|
||||
def get_thinking_tokens(self):
|
||||
budget = self.get_raw_thinking_tokens()
|
||||
|
||||
if budget is not None:
|
||||
# Format as xx.yK for thousands, xx.yM for millions
|
||||
if budget >= 1024 * 1024:
|
||||
value = budget / (1024 * 1024)
|
||||
if value == int(value):
|
||||
return f"{int(value)}M"
|
||||
else:
|
||||
return f"{value:.1f}M"
|
||||
else:
|
||||
value = budget / 1024
|
||||
if value == int(value):
|
||||
return f"{int(value)}k"
|
||||
else:
|
||||
return f"{value:.1f}k"
|
||||
return None
|
||||
|
||||
def get_reasoning_effort(self):
|
||||
"""Get reasoning effort value if available"""
|
||||
if (
|
||||
self.extra_params
|
||||
and "extra_body" in self.extra_params
|
||||
and "reasoning_effort" in self.extra_params["extra_body"]
|
||||
):
|
||||
return self.extra_params["extra_body"]["reasoning_effort"]
|
||||
return None
|
||||
|
||||
def is_deepseek_r1(self):
|
||||
name = self.name.lower()
|
||||
if "deepseek" not in name:
|
||||
@@ -577,7 +753,6 @@ class Model(ModelSettings):
|
||||
|
||||
kwargs = dict(
|
||||
model=self.name,
|
||||
messages=messages,
|
||||
stream=stream,
|
||||
)
|
||||
|
||||
@@ -606,17 +781,13 @@ class Model(ModelSettings):
|
||||
hash_object = hashlib.sha1(key)
|
||||
if "timeout" not in kwargs:
|
||||
kwargs["timeout"] = request_timeout
|
||||
if self.verbose:
|
||||
dump(kwargs)
|
||||
kwargs["messages"] = messages
|
||||
|
||||
res = litellm.completion(**kwargs)
|
||||
return hash_object, res
|
||||
|
||||
def remove_reasoning_content(self, res):
|
||||
if not self.remove_reasoning:
|
||||
return res
|
||||
|
||||
pattern = f"<{self.remove_reasoning}>.*?</{self.remove_reasoning}>"
|
||||
res = re.sub(pattern, "", res, flags=re.DOTALL).strip()
|
||||
return res
|
||||
|
||||
def simple_send_with_retries(self, messages):
|
||||
from aider.exceptions import LiteLLMExceptions
|
||||
|
||||
@@ -637,7 +808,9 @@ class Model(ModelSettings):
|
||||
if not response or not hasattr(response, "choices") or not response.choices:
|
||||
return None
|
||||
res = response.choices[0].message.content
|
||||
return self.remove_reasoning_content(res)
|
||||
from aider.reasoning_tags import remove_reasoning_content
|
||||
|
||||
return remove_reasoning_content(res, self.reasoning_tag)
|
||||
|
||||
except litellm_ex.exceptions_tuple() as err:
|
||||
ex_info = litellm_ex.get_ex_info(err)
|
||||
@@ -760,6 +933,9 @@ def sanity_check_model(io, model):
|
||||
show = True
|
||||
io.tool_warning(f"Warning for {model}: Unknown which environment variables are required.")
|
||||
|
||||
# Check for model-specific dependencies
|
||||
check_for_dependencies(io, model.name)
|
||||
|
||||
if not model.info:
|
||||
show = True
|
||||
io.tool_warning(
|
||||
@@ -775,11 +951,38 @@ def sanity_check_model(io, model):
|
||||
return show
|
||||
|
||||
|
||||
def check_for_dependencies(io, model_name):
|
||||
"""
|
||||
Check for model-specific dependencies and install them if needed.
|
||||
|
||||
Args:
|
||||
io: The IO object for user interaction
|
||||
model_name: The name of the model to check dependencies for
|
||||
"""
|
||||
# Check if this is a Bedrock model and ensure boto3 is installed
|
||||
if model_name.startswith("bedrock/"):
|
||||
check_pip_install_extra(
|
||||
io, "boto3", "AWS Bedrock models require the boto3 package.", ["boto3"]
|
||||
)
|
||||
|
||||
# Check if this is a Vertex AI model and ensure google-cloud-aiplatform is installed
|
||||
elif model_name.startswith("vertex_ai/"):
|
||||
check_pip_install_extra(
|
||||
io,
|
||||
"google.cloud.aiplatform",
|
||||
"Google Vertex AI models require the google-cloud-aiplatform package.",
|
||||
["google-cloud-aiplatform"],
|
||||
)
|
||||
|
||||
|
||||
def fuzzy_match_models(name):
|
||||
name = name.lower()
|
||||
|
||||
chat_models = set()
|
||||
for orig_model, attrs in litellm.model_cost.items():
|
||||
model_metadata = list(litellm.model_cost.items())
|
||||
model_metadata += list(model_info_manager.local_model_metadata.items())
|
||||
|
||||
for orig_model, attrs in model_metadata:
|
||||
model = orig_model.lower()
|
||||
if attrs.get("mode") != "chat":
|
||||
continue
|
||||
|
||||
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
|
||||
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
|
||||
@@ -9,6 +9,7 @@ try:
|
||||
git.exc.ODBError,
|
||||
git.exc.GitError,
|
||||
git.exc.InvalidGitRepositoryError,
|
||||
git.exc.GitCommandNotFound,
|
||||
]
|
||||
except ImportError:
|
||||
git = None
|
||||
@@ -56,6 +57,7 @@ class GitRepo:
|
||||
attribute_commit_message_committer=False,
|
||||
commit_prompt=None,
|
||||
subtree_only=False,
|
||||
git_commit_verify=True,
|
||||
):
|
||||
self.io = io
|
||||
self.models = models
|
||||
@@ -69,6 +71,7 @@ class GitRepo:
|
||||
self.attribute_commit_message_committer = attribute_commit_message_committer
|
||||
self.commit_prompt = commit_prompt
|
||||
self.subtree_only = subtree_only
|
||||
self.git_commit_verify = git_commit_verify
|
||||
self.ignore_file_cache = {}
|
||||
|
||||
if git_dname:
|
||||
@@ -133,7 +136,9 @@ class GitRepo:
|
||||
# if 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:
|
||||
fnames = [str(self.abs_root_path(fn)) for fn in fnames]
|
||||
for fname in fnames:
|
||||
@@ -145,7 +150,7 @@ class GitRepo:
|
||||
else:
|
||||
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")
|
||||
committer_name = f"{original_user_name} (aider)"
|
||||
|
||||
@@ -289,13 +294,19 @@ class GitRepo:
|
||||
else:
|
||||
try:
|
||||
iterator = commit.tree.traverse()
|
||||
blob = None # Initialize blob
|
||||
while True:
|
||||
try:
|
||||
blob = next(iterator)
|
||||
if blob.type == "blob": # blob is a file
|
||||
files.add(blob.path)
|
||||
except IndexError:
|
||||
self.io.tool_warning(f"GitRepo: read error skipping {blob.path}")
|
||||
# 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
|
||||
@@ -309,8 +320,11 @@ class GitRepo:
|
||||
|
||||
# Add staged files
|
||||
index = self.repo.index
|
||||
staged_files = [path for path, _ in index.entries.keys()]
|
||||
files.update(self.normalize_path(path) for path in staged_files)
|
||||
try:
|
||||
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)]
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ from aider.utils import Spinner
|
||||
|
||||
# tree_sitter is throwing a 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())
|
||||
|
||||
@@ -31,8 +31,12 @@ Tag = namedtuple("Tag", "rel_fname fname line name kind".split())
|
||||
SQLITE_ERRORS = (sqlite3.OperationalError, sqlite3.DatabaseError, OSError)
|
||||
|
||||
|
||||
CACHE_VERSION = 3
|
||||
if USING_TSL_PACK:
|
||||
CACHE_VERSION = 4
|
||||
|
||||
|
||||
class RepoMap:
|
||||
CACHE_VERSION = 3
|
||||
TAGS_CACHE_DIR = f".aider.tags.cache.v{CACHE_VERSION}"
|
||||
|
||||
warned_files = set()
|
||||
@@ -282,10 +286,15 @@ class RepoMap:
|
||||
query = language.query(query_scm)
|
||||
captures = query.captures(tree.root_node)
|
||||
|
||||
captures = list(captures)
|
||||
|
||||
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."):
|
||||
kind = "def"
|
||||
elif tag.startswith("name.reference."):
|
||||
@@ -389,13 +398,30 @@ class RepoMap:
|
||||
|
||||
# dump(fname)
|
||||
rel_fname = self.get_rel_fname(fname)
|
||||
current_pers = 0.0 # Start with 0 personalization score
|
||||
|
||||
if fname in chat_fnames:
|
||||
personalization[rel_fname] = personalize
|
||||
current_pers += personalize
|
||||
chat_rel_fnames.add(rel_fname)
|
||||
|
||||
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))
|
||||
if tags is None:
|
||||
@@ -422,17 +448,33 @@ class RepoMap:
|
||||
|
||||
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:
|
||||
if progress:
|
||||
progress()
|
||||
|
||||
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:
|
||||
mul = 10
|
||||
elif ident.startswith("_"):
|
||||
mul = 0.1
|
||||
else:
|
||||
mul = 1
|
||||
mul *= 10
|
||||
if (is_snake or is_camel) and len(ident) >= 8:
|
||||
mul *= 10
|
||||
if ident.startswith("_"):
|
||||
mul *= 0.1
|
||||
if len(defines[ident]) > 5:
|
||||
mul *= 0.1
|
||||
|
||||
for referencer, num_refs in Counter(references[ident]).items():
|
||||
for definer in definers:
|
||||
@@ -440,10 +482,14 @@ class RepoMap:
|
||||
# if referencer == definer:
|
||||
# continue
|
||||
|
||||
use_mul = mul
|
||||
if referencer in chat_rel_fnames:
|
||||
use_mul *= 50
|
||||
|
||||
# scale down so high freq (low value) mentions don't dominate
|
||||
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:
|
||||
pass
|
||||
@@ -732,8 +778,27 @@ def get_random_color():
|
||||
|
||||
def get_scm_fname(lang):
|
||||
# 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:
|
||||
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:
|
||||
return
|
||||
|
||||
|
||||
@@ -47,6 +47,49 @@
|
||||
//"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,
|
||||
@@ -97,6 +140,34 @@
|
||||
"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/openrouter/quasar-alpha": {
|
||||
"max_input_tokens": 1000000,
|
||||
"max_output_tokens": 32000,
|
||||
"input_cost_per_token": 0.0,
|
||||
"output_cost_per_token": 0.0,
|
||||
"litellm_provider": "openrouter",
|
||||
"mode": "chat",
|
||||
"supports_vision": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_prompt_caching": true
|
||||
},
|
||||
"openrouter/openai/gpt-4o-mini": {
|
||||
"max_tokens": 16384,
|
||||
"max_input_tokens": 128000,
|
||||
@@ -115,4 +186,298 @@
|
||||
"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"
|
||||
},
|
||||
"gemini/gemini-2.5-pro-preview-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.00000125,
|
||||
"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.000010,
|
||||
"output_cost_per_character": 0,
|
||||
"output_cost_per_token_above_128k_tokens": 0,
|
||||
"output_cost_per_character_above_128k_tokens": 0,
|
||||
"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"
|
||||
},
|
||||
"vertex_ai/gemini-2.5-pro-preview-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.00000125,
|
||||
"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.000010,
|
||||
"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"
|
||||
},
|
||||
"openrouter/google/gemini-2.0-flash-exp:free": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 8192,
|
||||
"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,
|
||||
"litellm_provider": "openrouter",
|
||||
"mode": "chat",
|
||||
"supports_system_messages": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_audio_output": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
}
|
||||
|
||||
@@ -184,6 +184,179 @@
|
||||
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
|
||||
@@ -397,8 +570,8 @@
|
||||
examples_as_sys_msg: true
|
||||
extra_params:
|
||||
max_tokens: 8192
|
||||
include_reasoning: true
|
||||
caches_by_default: true
|
||||
use_temperature: false
|
||||
editor_model_name: openrouter/deepseek/deepseek-chat
|
||||
editor_edit_format: editor-diff
|
||||
|
||||
@@ -410,6 +583,16 @@
|
||||
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
|
||||
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
|
||||
@@ -435,6 +618,18 @@
|
||||
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
|
||||
@@ -473,6 +668,15 @@
|
||||
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
|
||||
@@ -532,6 +736,7 @@
|
||||
streaming: false
|
||||
editor_model_name: azure/gpt-4o
|
||||
editor_edit_format: editor-diff
|
||||
accepts_settings: ["reasoning_effort"]
|
||||
|
||||
- name: o1-preview
|
||||
edit_format: architect
|
||||
@@ -570,6 +775,7 @@
|
||||
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
|
||||
@@ -580,6 +786,7 @@
|
||||
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
|
||||
@@ -590,6 +797,7 @@
|
||||
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
|
||||
@@ -618,7 +826,7 @@
|
||||
streaming: true
|
||||
editor_model_name: fireworks_ai/accounts/fireworks/models/deepseek-v3
|
||||
editor_edit_format: editor-diff
|
||||
remove_reasoning: think
|
||||
reasoning_tag: think
|
||||
extra_params:
|
||||
max_tokens: 160000
|
||||
|
||||
@@ -638,6 +846,7 @@
|
||||
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
|
||||
@@ -647,6 +856,7 @@
|
||||
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
|
||||
@@ -656,6 +866,17 @@
|
||||
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
|
||||
@@ -665,5 +886,97 @@
|
||||
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-preview-03-25
|
||||
edit_format: diff-fenced
|
||||
use_repo_map: true
|
||||
weak_model_name: gemini/gemini-2.0-flash
|
||||
|
||||
- name: gemini/gemini-2.5-pro-exp-03-25
|
||||
edit_format: diff-fenced
|
||||
use_repo_map: true
|
||||
weak_model_name: gemini/gemini-2.0-flash
|
||||
|
||||
- name: openrouter/google/gemini-2.5-pro-exp-03-25:free
|
||||
edit_format: diff-fenced
|
||||
use_repo_map: true
|
||||
weak_model_name: openrouter/google/gemini-2.0-flash-exp:free
|
||||
|
||||
- name: vertex_ai/gemini-2.5-pro-exp-03-25
|
||||
edit_format: diff-fenced
|
||||
use_repo_map: true
|
||||
# Need metadata for this one...
|
||||
#weak_model_name: vertex_ai/gemini-2.0-flash
|
||||
|
||||
- name: vertex_ai/gemini-2.5-pro-preview-03-25
|
||||
edit_format: diff-fenced
|
||||
use_repo_map: true
|
||||
# Need metadata for this one...
|
||||
#weak_model_name: vertex_ai/gemini-2.0-flash
|
||||
|
||||
- name: openrouter/openrouter/quasar-alpha
|
||||
use_repo_map: true
|
||||
edit_format: diff
|
||||
examples_as_sys_msg: true
|
||||
|
||||
@@ -159,7 +159,8 @@ class Scraper:
|
||||
try:
|
||||
response = page.goto(url, wait_until="networkidle", timeout=5000)
|
||||
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:
|
||||
self.print_error(f"Error navigating to {url}: {str(e)}")
|
||||
return None, None
|
||||
|
||||
@@ -41,6 +41,7 @@ ROOT_IMPORTANT_FILES = [
|
||||
"composer.lock",
|
||||
"pom.xml",
|
||||
"build.gradle",
|
||||
"build.gradle.kts",
|
||||
"build.sbt",
|
||||
"go.mod",
|
||||
"go.sum",
|
||||
|
||||
@@ -308,7 +308,11 @@ def find_common_root(abs_fnames):
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return safe_abs_path(os.getcwd())
|
||||
try:
|
||||
return safe_abs_path(os.getcwd())
|
||||
except FileNotFoundError:
|
||||
# Fallback if cwd is deleted
|
||||
return "."
|
||||
|
||||
|
||||
def format_tokens(count):
|
||||
|
||||
@@ -64,7 +64,7 @@ class FileWatcher:
|
||||
"""Watches source files for changes and AI comments"""
|
||||
|
||||
# Compiled regex pattern for AI comments
|
||||
ai_comment_pattern = re.compile(r"(?:#|//|--) *(ai\b.*|ai\b.*|.*\bai[?!]?) *$", re.IGNORECASE)
|
||||
ai_comment_pattern = re.compile(r"(?:#|//|--|;+) *(ai\b.*|ai\b.*|.*\bai[?!]?) *$", re.IGNORECASE)
|
||||
|
||||
def __init__(self, coder, gitignores=None, verbose=False, analytics=None, root=None):
|
||||
self.coder = coder
|
||||
@@ -140,7 +140,10 @@ class FileWatcher:
|
||||
roots_to_watch = self.get_roots_to_watch()
|
||||
|
||||
for changes in watch(
|
||||
*roots_to_watch, watch_filter=self.filter_func, stop_event=self.stop_event
|
||||
*roots_to_watch,
|
||||
watch_filter=self.filter_func,
|
||||
stop_event=self.stop_event,
|
||||
ignore_permission_denied=True,
|
||||
):
|
||||
if self.handle_changes(changes):
|
||||
return
|
||||
@@ -259,7 +262,7 @@ class FileWatcher:
|
||||
line_nums.append(i)
|
||||
comments.append(comment)
|
||||
comment = comment.lower()
|
||||
comment = comment.lstrip("/#-")
|
||||
comment = comment.lstrip("/#-;") # Added semicolon for Lisp comments
|
||||
comment = comment.strip()
|
||||
if comment.startswith("ai!") or comment.endswith("ai!"):
|
||||
has_action = "!"
|
||||
|
||||
@@ -7,12 +7,13 @@ description: Release notes and stats on aider writing its own code.
|
||||
|
||||
# Release history
|
||||
|
||||
{% include blame.md %}
|
||||
|
||||
The above
|
||||
[stats are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed)
|
||||
Aider writes most of its own code, usually about 70-80% of the new code in each release.
|
||||
These
|
||||
[statistics are based on the git commit history](/docs/faq.html#how-are-the-aider-wrote-xx-of-code-stats-computed)
|
||||
of the aider repo.
|
||||
|
||||
{% include blame.md %}
|
||||
|
||||
## Release notes
|
||||
|
||||
<!--[[[cog
|
||||
@@ -23,6 +24,189 @@ cog.out(text)
|
||||
]]]-->
|
||||
|
||||
|
||||
### Aider v0.81.1
|
||||
|
||||
- Added support for the `gemini/gemini-2.5-pro-preview-03-25` model.
|
||||
- Updated the `gemini` alias to point to `gemini/gemini-2.5-pro-preview-03-25`.
|
||||
- Added the `gemini-exp` alias for `gemini/gemini-2.5-pro-exp-03-25`.
|
||||
- Aider wrote 87% of the code in this release.
|
||||
|
||||
### Aider v0.81.0
|
||||
|
||||
- Added support for the `openrouter/openrouter/quasar-alpha` model.
|
||||
- Run with `aider --model quasar`
|
||||
- Offer OpenRouter OAuth authentication if an OpenRouter model is specified but the API key is missing.
|
||||
- Prevent retrying API calls when the provider reports insufficient credits.
|
||||
- Improve URL detection to exclude trailing double quotes.
|
||||
- Aider wrote 86% of the code in this release.
|
||||
|
||||
### Aider v0.80.4
|
||||
|
||||
- Bumped deps to pickup litellm change to properly display the root cause of OpenRouter "choices" errors.
|
||||
|
||||
### Aider v0.80.3
|
||||
|
||||
- Improve error message for OpenRouter API connection issues to mention potential rate limiting or upstream provider issues.
|
||||
- Configure weak models (`gemini/gemini-2.0-flash` and `openrouter/google/gemini-2.0-flash-exp:free`) for Gemini 2.5 Pro models.
|
||||
- Add model metadata for `openrouter/google/gemini-2.0-flash-exp:free`.
|
||||
|
||||
### Aider v0.80.2
|
||||
|
||||
- Bumped deps.
|
||||
|
||||
### 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 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.
|
||||
|
||||
@@ -51,4 +51,19 @@ callouts:
|
||||
note:
|
||||
title: Note
|
||||
color: yellow
|
||||
|
||||
# Custom CSS for our table of contents
|
||||
kramdown:
|
||||
syntax_highlighter_opts:
|
||||
css_class: highlight
|
||||
|
||||
sass:
|
||||
style: compressed
|
||||
|
||||
# Additional CSS
|
||||
compress_html:
|
||||
clippings: all
|
||||
comments: all
|
||||
endings: all
|
||||
startings: []
|
||||
|
||||
|
||||
@@ -3723,7 +3723,7 @@
|
||||
Titusz Pan: 9
|
||||
start_tag: v0.71.0
|
||||
total_lines: 283
|
||||
- aider_percentage: 69.44
|
||||
- aider_percentage: 37.47
|
||||
aider_total: 284
|
||||
end_date: '2025-01-31'
|
||||
end_tag: v0.73.0
|
||||
@@ -3746,6 +3746,10 @@
|
||||
aider/models.py:
|
||||
Paul Gauthier: 8
|
||||
Paul Gauthier (aider): 33
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 334
|
||||
kennyfrc: 11
|
||||
xqyz: 4
|
||||
aider/sendchat.py:
|
||||
Mir Adnan ALI: 28
|
||||
Paul Gauthier: 11
|
||||
@@ -3770,12 +3774,13 @@
|
||||
Paul Gauthier (aider): 77
|
||||
grand_total:
|
||||
Mir Adnan ALI: 28
|
||||
Paul Gauthier: 96
|
||||
Paul Gauthier: 430
|
||||
Paul Gauthier (aider): 284
|
||||
xqyz: 1
|
||||
kennyfrc: 11
|
||||
xqyz: 5
|
||||
start_tag: v0.72.0
|
||||
total_lines: 409
|
||||
- aider_percentage: 77.14
|
||||
total_lines: 758
|
||||
- aider_percentage: 76.07
|
||||
aider_total: 604
|
||||
end_date: '2025-02-06'
|
||||
end_tag: v0.74.0
|
||||
@@ -3813,6 +3818,8 @@
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 2
|
||||
"Viktor Sz\xE9pe": 3
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 11
|
||||
aider/watch.py:
|
||||
Paul Gauthier (aider): 45
|
||||
benchmark/docker.sh:
|
||||
@@ -3839,8 +3846,605 @@
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 42
|
||||
grand_total:
|
||||
Paul Gauthier: 176
|
||||
Paul Gauthier: 187
|
||||
Paul Gauthier (aider): 604
|
||||
"Viktor Sz\xE9pe": 3
|
||||
start_tag: v0.73.0
|
||||
total_lines: 783
|
||||
total_lines: 794
|
||||
- aider_percentage: 44.78
|
||||
aider_total: 163
|
||||
end_date: '2025-02-24'
|
||||
end_tag: v0.75.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/args.py:
|
||||
Paul Gauthier: 7
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 12
|
||||
Paul Gauthier (aider): 4
|
||||
aider/commands.py:
|
||||
FeepingCreature (aider): 6
|
||||
aider/editor.py:
|
||||
Paul Gauthier: 7
|
||||
Paul Gauthier (aider): 5
|
||||
aider/io.py:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 4
|
||||
aider/linter.py:
|
||||
Paul Gauthier: 1
|
||||
aider/main.py:
|
||||
Paul Gauthier: 16
|
||||
aider/models.py:
|
||||
Paul Gauthier: 4
|
||||
aider/queries/tree-sitter-language-pack/javascript-tags.scm:
|
||||
Paul Gauthier: 5
|
||||
aider/queries/tree-sitter-languages/hcl-tags.scm:
|
||||
Paul Gauthier: 3
|
||||
Warren Krewenki: 74
|
||||
aider/queries/tree-sitter-languages/javascript-tags.scm:
|
||||
Paul Gauthier: 5
|
||||
aider/repomap.py:
|
||||
Paul Gauthier: 43
|
||||
Paul Gauthier (aider): 11
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 12
|
||||
aider/special.py:
|
||||
Lucas Shadler: 1
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
benchmark/Dockerfile:
|
||||
Paul Gauthier (aider): 1
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 4
|
||||
benchmark/cpp-test.sh:
|
||||
Paul Gauthier: 1
|
||||
scripts/blame.py:
|
||||
Paul Gauthier (aider): 2
|
||||
scripts/issues.py:
|
||||
Paul Gauthier (aider): 17
|
||||
tests/basic/test_coder.py:
|
||||
Paul Gauthier (aider): 18
|
||||
tests/basic/test_editor.py:
|
||||
Antti Kaihola: 1
|
||||
Paul Gauthier (aider): 41
|
||||
tests/basic/test_models.py:
|
||||
Paul Gauthier (aider): 1
|
||||
tests/basic/test_repomap.py:
|
||||
Paul Gauthier (aider): 1
|
||||
tests/fixtures/languages/hcl/test.tf:
|
||||
Paul Gauthier (aider): 52
|
||||
grand_total:
|
||||
Antti Kaihola: 1
|
||||
FeepingCreature (aider): 6
|
||||
Lucas Shadler: 1
|
||||
Paul Gauthier: 125
|
||||
Paul Gauthier (aider): 157
|
||||
Warren Krewenki: 74
|
||||
start_tag: v0.74.0
|
||||
total_lines: 364
|
||||
- aider_percentage: 84.75
|
||||
aider_total: 1589
|
||||
end_date: '2025-03-10'
|
||||
end_tag: v0.76.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/args.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 25
|
||||
aider/args_formatter.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 3
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 54
|
||||
Paul Gauthier (aider): 29
|
||||
aider/deprecated.py:
|
||||
Paul Gauthier (aider): 107
|
||||
aider/io.py:
|
||||
Paul Gauthier: 7
|
||||
Paul Gauthier (aider): 127
|
||||
aider/main.py:
|
||||
Akira Komamura: 2
|
||||
Mattias: 1
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 16
|
||||
aider/models.py:
|
||||
Paul Gauthier: 6
|
||||
Paul Gauthier (aider): 68
|
||||
aider/queries/tree-sitter-language-pack/csharp-tags.scm:
|
||||
Paul Gauthier: 14
|
||||
Paul Gauthier (aider): 12
|
||||
aider/reasoning_tags.py:
|
||||
Paul Gauthier: 14
|
||||
Paul Gauthier (aider): 68
|
||||
aider/repo.py:
|
||||
Akira Komamura: 1
|
||||
Paul Gauthier (aider): 4
|
||||
aider/repomap.py:
|
||||
Paul Gauthier: 9
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 61
|
||||
Paul Gauthier (aider): 32
|
||||
gmoz22: 4
|
||||
aider/website/_includes/leaderboard.js:
|
||||
Paul Gauthier (aider): 48
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 2
|
||||
benchmark/benchmark.py:
|
||||
Paul Gauthier: 1
|
||||
benchmark/problem_stats.py:
|
||||
Paul Gauthier (aider): 2
|
||||
docker/Dockerfile:
|
||||
Paul Gauthier: 1
|
||||
scripts/blame.py:
|
||||
Paul Gauthier: 1
|
||||
scripts/pip-compile.sh:
|
||||
Claudia Pellegrino: 10
|
||||
Paul Gauthier: 6
|
||||
Paul Gauthier (aider): 11
|
||||
scripts/update-history.py:
|
||||
Paul Gauthier: 1
|
||||
scripts/versionbump.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 64
|
||||
tests/basic/test_deprecated.py:
|
||||
Paul Gauthier: 10
|
||||
Paul Gauthier (aider): 130
|
||||
tests/basic/test_io.py:
|
||||
Paul Gauthier (aider): 54
|
||||
tests/basic/test_main.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 93
|
||||
tests/basic/test_model_info_manager.py:
|
||||
Paul Gauthier (aider): 72
|
||||
tests/basic/test_models.py:
|
||||
Paul Gauthier: 27
|
||||
Paul Gauthier (aider): 34
|
||||
tests/basic/test_reasoning.py:
|
||||
Paul Gauthier: 36
|
||||
Paul Gauthier (aider): 525
|
||||
tests/basic/test_repomap.py:
|
||||
Paul Gauthier: 2
|
||||
tests/basic/test_ssl_verification.py:
|
||||
Paul Gauthier (aider): 65
|
||||
grand_total:
|
||||
Akira Komamura: 3
|
||||
Claudia Pellegrino: 10
|
||||
Mattias: 1
|
||||
Paul Gauthier: 268
|
||||
Paul Gauthier (aider): 1589
|
||||
gmoz22: 4
|
||||
start_tag: v0.75.0
|
||||
total_lines: 1875
|
||||
- aider_percentage: 71.93
|
||||
aider_total: 1399
|
||||
end_date: '2025-03-13'
|
||||
end_tag: v0.77.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/args.py:
|
||||
Paul Gauthier (aider): 5
|
||||
aider/coders/architect_coder.py:
|
||||
Paul Gauthier (aider): 2
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier (aider): 14
|
||||
aider/commands.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 71
|
||||
aider/deprecated.py:
|
||||
Paul Gauthier: 2
|
||||
aider/io.py:
|
||||
Paul Gauthier (aider): 5
|
||||
aider/main.py:
|
||||
Paul Gauthier (aider): 12
|
||||
aider/models.py:
|
||||
Paul Gauthier (aider): 83
|
||||
aider/queries/tree-sitter-language-pack/arduino-tags.scm:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 2
|
||||
aider/queries/tree-sitter-language-pack/c-tags.scm:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 5
|
||||
aider/queries/tree-sitter-language-pack/chatito-tags.scm:
|
||||
Paul Gauthier: 11
|
||||
Paul Gauthier (aider): 5
|
||||
aider/queries/tree-sitter-language-pack/commonlisp-tags.scm:
|
||||
Paul Gauthier: 116
|
||||
Paul Gauthier (aider): 6
|
||||
aider/queries/tree-sitter-language-pack/cpp-tags.scm:
|
||||
Paul Gauthier: 7
|
||||
Paul Gauthier (aider): 8
|
||||
aider/queries/tree-sitter-language-pack/d-tags.scm:
|
||||
Paul Gauthier: 9
|
||||
Paul Gauthier (aider): 17
|
||||
aider/queries/tree-sitter-language-pack/dart-tags.scm:
|
||||
Paul Gauthier: 42
|
||||
Paul Gauthier (aider): 19
|
||||
aider/queries/tree-sitter-language-pack/elisp-tags.scm:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 2
|
||||
aider/queries/tree-sitter-language-pack/elixir-tags.scm:
|
||||
Paul Gauthier: 10
|
||||
Paul Gauthier (aider): 8
|
||||
aider/queries/tree-sitter-language-pack/elm-tags.scm:
|
||||
Paul Gauthier: 8
|
||||
Paul Gauthier (aider): 11
|
||||
aider/queries/tree-sitter-language-pack/gleam-tags.scm:
|
||||
Paul Gauthier: 26
|
||||
Paul Gauthier (aider): 15
|
||||
aider/queries/tree-sitter-language-pack/go-tags.scm:
|
||||
Paul Gauthier: 14
|
||||
Paul Gauthier (aider): 14
|
||||
aider/queries/tree-sitter-language-pack/java-tags.scm:
|
||||
Paul Gauthier: 10
|
||||
Paul Gauthier (aider): 7
|
||||
aider/queries/tree-sitter-language-pack/lua-tags.scm:
|
||||
Paul Gauthier: 25
|
||||
Paul Gauthier (aider): 9
|
||||
aider/queries/tree-sitter-language-pack/pony-tags.scm:
|
||||
Paul Gauthier: 20
|
||||
Paul Gauthier (aider): 19
|
||||
aider/queries/tree-sitter-language-pack/properties-tags.scm:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 2
|
||||
aider/queries/tree-sitter-language-pack/python-tags.scm:
|
||||
Paul Gauthier: 9
|
||||
Paul Gauthier (aider): 5
|
||||
aider/queries/tree-sitter-language-pack/r-tags.scm:
|
||||
Paul Gauthier: 17
|
||||
Paul Gauthier (aider): 4
|
||||
aider/queries/tree-sitter-language-pack/racket-tags.scm:
|
||||
Paul Gauthier: 10
|
||||
Paul Gauthier (aider): 2
|
||||
aider/queries/tree-sitter-language-pack/ruby-tags.scm:
|
||||
Paul Gauthier: 23
|
||||
Paul Gauthier (aider): 12
|
||||
aider/queries/tree-sitter-language-pack/rust-tags.scm:
|
||||
Paul Gauthier: 41
|
||||
Paul Gauthier (aider): 14
|
||||
aider/queries/tree-sitter-language-pack/solidity-tags.scm:
|
||||
Paul Gauthier: 30
|
||||
Paul Gauthier (aider): 13
|
||||
aider/queries/tree-sitter-language-pack/swift-tags.scm:
|
||||
Paul Gauthier: 39
|
||||
Paul Gauthier (aider): 12
|
||||
aider/queries/tree-sitter-language-pack/udev-tags.scm:
|
||||
Paul Gauthier: 15
|
||||
Paul Gauthier (aider): 5
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 9
|
||||
aider/watch.py:
|
||||
Yutaka Matsubara: 4
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 8
|
||||
scripts/redact-cast.py:
|
||||
Paul Gauthier: 27
|
||||
Paul Gauthier (aider): 98
|
||||
scripts/tsl_pack_langs.py:
|
||||
Paul Gauthier (aider): 145
|
||||
scripts/versionbump.py:
|
||||
Paul Gauthier (aider): 1
|
||||
tests/basic/test_coder.py:
|
||||
Paul Gauthier (aider): 104
|
||||
tests/basic/test_commands.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 190
|
||||
tests/basic/test_models.py:
|
||||
Paul Gauthier (aider): 44
|
||||
tests/basic/test_repomap.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 125
|
||||
tests/fixtures/languages/arduino/test.ino:
|
||||
Paul Gauthier (aider): 21
|
||||
tests/fixtures/languages/c/test.c:
|
||||
Paul Gauthier (aider): 12
|
||||
tests/fixtures/languages/chatito/test.chatito:
|
||||
Paul Gauthier (aider): 20
|
||||
tests/fixtures/languages/commonlisp/test.lisp:
|
||||
Paul Gauthier (aider): 17
|
||||
tests/fixtures/languages/d/test.d:
|
||||
Paul Gauthier (aider): 26
|
||||
tests/fixtures/languages/dart/test.dart:
|
||||
Paul Gauthier (aider): 21
|
||||
tests/fixtures/languages/elm/test.elm:
|
||||
Paul Gauthier (aider): 16
|
||||
tests/fixtures/languages/gleam/test.gleam:
|
||||
Paul Gauthier (aider): 10
|
||||
tests/fixtures/languages/lua/test.lua:
|
||||
Paul Gauthier (aider): 25
|
||||
tests/fixtures/languages/pony/test.pony:
|
||||
Paul Gauthier (aider): 8
|
||||
tests/fixtures/languages/properties/test.properties:
|
||||
Paul Gauthier (aider): 14
|
||||
tests/fixtures/languages/r/test.r:
|
||||
Paul Gauthier (aider): 17
|
||||
tests/fixtures/languages/racket/test.rkt:
|
||||
Paul Gauthier (aider): 8
|
||||
tests/fixtures/languages/solidity/test.sol:
|
||||
Paul Gauthier (aider): 21
|
||||
tests/fixtures/languages/swift/test.swift:
|
||||
Paul Gauthier (aider): 18
|
||||
tests/fixtures/languages/udev/test.rules:
|
||||
Paul Gauthier (aider): 22
|
||||
grand_total:
|
||||
Paul Gauthier: 542
|
||||
Paul Gauthier (aider): 1399
|
||||
Yutaka Matsubara: 4
|
||||
start_tag: v0.76.0
|
||||
total_lines: 1945
|
||||
- aider_percentage: 91.82
|
||||
aider_total: 2682
|
||||
end_date: '2025-03-21'
|
||||
end_tag: v0.78.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/args.py:
|
||||
Paul Gauthier (aider): 24
|
||||
Yutaka Matsubara: 2
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 6
|
||||
aider/commands.py:
|
||||
Carles Sala (aider): 30
|
||||
Paul Gauthier (aider): 10
|
||||
aider/help_pats.py:
|
||||
Paul Gauthier: 6
|
||||
aider/io.py:
|
||||
Marco Mayer: 2
|
||||
Paul Gauthier (aider): 17
|
||||
aider/main.py:
|
||||
Paul Gauthier: 5
|
||||
Paul Gauthier (aider): 29
|
||||
aider/mdstream.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 22
|
||||
aider/models.py:
|
||||
Paul Gauthier (aider): 41
|
||||
lentil32 (aider): 15
|
||||
aider/repo.py:
|
||||
Paul Gauthier (aider): 5
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 22
|
||||
aider/website/_includes/head_custom.html:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 53
|
||||
aider/website/_includes/recording.js:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 424
|
||||
aider/website/assets/asciinema/asciinema-player.min.js:
|
||||
Paul Gauthier: 1
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
aider/website/index.html:
|
||||
Paul Gauthier: 173
|
||||
Paul Gauthier (aider): 371
|
||||
scripts/badges.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 496
|
||||
scripts/blame.py:
|
||||
Paul Gauthier: 2
|
||||
scripts/jekyll_run.sh:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 5
|
||||
scripts/logo_svg.py:
|
||||
Paul Gauthier: 5
|
||||
Paul Gauthier (aider): 169
|
||||
scripts/recording_audio.py:
|
||||
Paul Gauthier (aider): 338
|
||||
scripts/redact-cast.py:
|
||||
Paul Gauthier: 22
|
||||
Paul Gauthier (aider): 37
|
||||
scripts/tmux_record.sh:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 17
|
||||
scripts/update-docs.sh:
|
||||
Paul Gauthier: 1
|
||||
scripts/update-history.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 52
|
||||
tests/basic/test_aws_credentials.py:
|
||||
lentil32 (aider): 169
|
||||
tests/basic/test_commands.py:
|
||||
Carles Sala (aider): 40
|
||||
tests/basic/test_main.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 193
|
||||
tests/basic/test_repo.py:
|
||||
Paul Gauthier (aider): 48
|
||||
tests/help/test_help.py:
|
||||
Paul Gauthier (aider): 49
|
||||
grand_total:
|
||||
Carles Sala (aider): 70
|
||||
Marco Mayer: 2
|
||||
Paul Gauthier: 235
|
||||
Paul Gauthier (aider): 2428
|
||||
Yutaka Matsubara: 2
|
||||
lentil32 (aider): 184
|
||||
start_tag: v0.77.0
|
||||
total_lines: 2921
|
||||
- aider_percentage: 65.38
|
||||
aider_total: 221
|
||||
end_date: '2025-03-25'
|
||||
end_tag: v0.79.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/coders/__init__.py:
|
||||
Paul Gauthier: 2
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 15
|
||||
Paul Gauthier (aider): 5
|
||||
aider/coders/context_coder.py:
|
||||
Paul Gauthier: 45
|
||||
Paul Gauthier (aider): 8
|
||||
aider/commands.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 20
|
||||
aider/io.py:
|
||||
Paul Gauthier: 11
|
||||
Paul Gauthier (aider): 2
|
||||
aider/main.py:
|
||||
Paul Gauthier (aider): 4
|
||||
aider/models.py:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 1
|
||||
aider/repomap.py:
|
||||
Paul Gauthier: 17
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 13
|
||||
Paul Gauthier (aider): 10
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
aider/website/index.html:
|
||||
Paul Gauthier: 3
|
||||
Paul Gauthier (aider): 16
|
||||
scripts/badges.py:
|
||||
Paul Gauthier (aider): 2
|
||||
scripts/blame.py:
|
||||
Paul Gauthier (aider): 16
|
||||
scripts/dl_icons.py:
|
||||
Paul Gauthier (aider): 60
|
||||
scripts/tmux_record.sh:
|
||||
Paul Gauthier: 1
|
||||
tests/basic/test_coder.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 77
|
||||
grand_total:
|
||||
Paul Gauthier: 117
|
||||
Paul Gauthier (aider): 221
|
||||
start_tag: v0.78.0
|
||||
total_lines: 338
|
||||
- aider_percentage: 86.86
|
||||
aider_total: 1837
|
||||
end_date: '2025-03-31'
|
||||
end_tag: v0.80.0
|
||||
file_counts:
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier: 2
|
||||
aider/commands.py:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 20
|
||||
aider/exceptions.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 3
|
||||
aider/io.py:
|
||||
Andrey Ivanov: 2
|
||||
Matteo Landi (aider): 11
|
||||
Paul Gauthier (aider): 38
|
||||
aider/linter.py:
|
||||
Mir Adnan ALI: 2
|
||||
aider/main.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 21
|
||||
aider/mdstream.py:
|
||||
Peter Schilling (aider) (aider): 25
|
||||
aider/models.py:
|
||||
Paul Gauthier: 12
|
||||
Paul Gauthier (aider): 9
|
||||
aider/onboarding.py:
|
||||
Paul Gauthier: 44
|
||||
Paul Gauthier (aider): 389
|
||||
aider/queries/tree-sitter-languages/scala-tags.scm:
|
||||
Vasil Markoukin: 65
|
||||
aider/repo.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 7
|
||||
aider/repomap.py:
|
||||
Paul Gauthier (aider): 19
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier (aider): 13
|
||||
aider/scrape.py:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 1
|
||||
aider/utils.py:
|
||||
Paul Gauthier (aider): 5
|
||||
aider/watch.py:
|
||||
Matteo Landi (aider): 2
|
||||
aider/website/_includes/leaderboard.js:
|
||||
Paul Gauthier: 1
|
||||
Paul Gauthier (aider): 2
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
aider/website/index.html:
|
||||
Paul Gauthier: 51
|
||||
Paul Gauthier (aider): 175
|
||||
scripts/30k-image.py:
|
||||
Paul Gauthier: 8
|
||||
Paul Gauthier (aider): 227
|
||||
scripts/homepage.py:
|
||||
Paul Gauthier (aider): 122
|
||||
tests/basic/test_commands.py:
|
||||
Paul Gauthier: 2
|
||||
Paul Gauthier (aider): 48
|
||||
tests/basic/test_exceptions.py:
|
||||
Paul Gauthier (aider): 17
|
||||
tests/basic/test_io.py:
|
||||
Paul Gauthier (aider): 28
|
||||
tests/basic/test_main.py:
|
||||
Paul Gauthier: 15
|
||||
Paul Gauthier (aider): 199
|
||||
tests/basic/test_onboarding.py:
|
||||
Paul Gauthier (aider): 439
|
||||
tests/basic/test_repomap.py:
|
||||
Vasil Markoukin: 3
|
||||
tests/basic/test_ssl_verification.py:
|
||||
Paul Gauthier (aider): 8
|
||||
tests/basic/test_watch.py:
|
||||
Matteo Landi (aider): 9
|
||||
tests/fixtures/languages/scala/test.scala:
|
||||
Vasil Markoukin: 61
|
||||
grand_total:
|
||||
Andrey Ivanov: 2
|
||||
Matteo Landi (aider): 22
|
||||
Mir Adnan ALI: 2
|
||||
Paul Gauthier: 145
|
||||
Paul Gauthier (aider): 1790
|
||||
Peter Schilling (aider) (aider): 25
|
||||
Vasil Markoukin: 129
|
||||
start_tag: v0.79.0
|
||||
total_lines: 2115
|
||||
- aider_percentage: 85.55
|
||||
aider_total: 225
|
||||
end_date: '2025-04-04'
|
||||
end_tag: v0.81.0
|
||||
file_counts:
|
||||
.github/workflows/check_pypi_version.yml:
|
||||
Paul Gauthier: 11
|
||||
Paul Gauthier (aider): 75
|
||||
.github/workflows/windows_check_pypi_version.yml:
|
||||
Paul Gauthier: 4
|
||||
Paul Gauthier (aider): 86
|
||||
aider/__init__.py:
|
||||
Paul Gauthier: 1
|
||||
aider/coders/base_coder.py:
|
||||
Paul Gauthier (aider): 4
|
||||
aider/exceptions.py:
|
||||
Paul Gauthier: 6
|
||||
Paul Gauthier (aider): 12
|
||||
aider/main.py:
|
||||
Paul Gauthier (aider): 40
|
||||
aider/models.py:
|
||||
Paul Gauthier (aider): 2
|
||||
aider/resources/model-settings.yml:
|
||||
Paul Gauthier: 9
|
||||
Paul Gauthier (aider): 1
|
||||
aider/website/_includes/leaderboard.js:
|
||||
Paul Gauthier (aider): 5
|
||||
aider/website/docs/leaderboards/index.md:
|
||||
Paul Gauthier: 1
|
||||
aider/website/index.html:
|
||||
Paul Gauthier: 3
|
||||
tests/basic/test_exceptions.py:
|
||||
Paul Gauthier: 3
|
||||
grand_total:
|
||||
Paul Gauthier: 38
|
||||
Paul Gauthier (aider): 225
|
||||
start_tag: v0.80.0
|
||||
total_lines: 263
|
||||
|
||||
@@ -256,4 +256,4 @@
|
||||
date: 2024-12-22
|
||||
versions: 0.69.2.dev
|
||||
seconds_per_case: 12.2
|
||||
total_cost: 0.0000
|
||||
total_cost: 0.0000
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
- dirname: 2025-02-25-20-23-07--gemini-pro
|
||||
test_cases: 225
|
||||
model: Gemini 2.0 Pro exp-02-05
|
||||
edit_format: whole
|
||||
commit_hash: 2fccd47
|
||||
pass_rate_1: 20.4
|
||||
pass_rate_2: 35.6
|
||||
pass_num_1: 46
|
||||
pass_num_2: 80
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 430
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 13
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 5
|
||||
total_tests: 225
|
||||
command: aider --model gemini/gemini-2.0-pro-exp-02-05
|
||||
date: 2025-02-25
|
||||
versions: 0.75.2.dev
|
||||
seconds_per_case: 34.8
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2024-12-21-18-41-18--polyglot-gpt-4o-mini
|
||||
test_cases: 225
|
||||
model: gpt-4o-mini-2024-07-18
|
||||
@@ -312,7 +338,7 @@
|
||||
|
||||
- dirname: 2024-12-25-13-31-51--deepseekv3preview-diff2
|
||||
test_cases: 225
|
||||
model: DeepSeek Chat V3
|
||||
model: DeepSeek Chat V3 (prev)
|
||||
edit_format: diff
|
||||
commit_hash: 0a23c4a-dirty
|
||||
pass_rate_1: 22.7
|
||||
@@ -543,4 +569,318 @@
|
||||
date: 2025-01-21
|
||||
versions: 0.72.2.dev
|
||||
seconds_per_case: 24.2
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2025-02-15-19-51-22--chatgpt4o-feb15-diff
|
||||
test_cases: 223
|
||||
model: chatgpt-4o-latest (2025-02-15)
|
||||
edit_format: diff
|
||||
commit_hash: 108ce18-dirty
|
||||
pass_rate_1: 9.0
|
||||
pass_rate_2: 27.1
|
||||
pass_num_1: 20
|
||||
pass_num_2: 61
|
||||
percent_cases_well_formed: 93.3
|
||||
error_outputs: 66
|
||||
num_malformed_responses: 21
|
||||
num_with_malformed_responses: 15
|
||||
user_asks: 57
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 2
|
||||
total_tests: 225
|
||||
command: aider --model chatgpt-4o-latest
|
||||
date: 2025-02-15
|
||||
versions: 0.74.3.dev
|
||||
seconds_per_case: 12.4
|
||||
total_cost: 14.3703
|
||||
|
||||
- dirname: 2025-02-24-19-54-07--sonnet37-diff
|
||||
test_cases: 225
|
||||
model: claude-3-7-sonnet-20250219 (no thinking)
|
||||
edit_format: diff
|
||||
commit_hash: 75e9ee6
|
||||
pass_rate_1: 24.4
|
||||
pass_rate_2: 60.4
|
||||
pass_num_1: 55
|
||||
pass_num_2: 136
|
||||
percent_cases_well_formed: 93.3
|
||||
error_outputs: 16
|
||||
num_malformed_responses: 16
|
||||
num_with_malformed_responses: 15
|
||||
user_asks: 12
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 0
|
||||
total_tests: 225
|
||||
command: aider --model sonnet
|
||||
date: 2025-02-24
|
||||
versions: 0.74.4.dev
|
||||
seconds_per_case: 28.3
|
||||
total_cost: 17.7191
|
||||
|
||||
- dirname: 2025-02-24-21-47-23--sonnet37-diff-think-32k-64k
|
||||
test_cases: 225
|
||||
model: claude-3-7-sonnet-20250219 (32k thinking tokens)
|
||||
edit_format: diff
|
||||
commit_hash: 60d11a6, 93edbda
|
||||
pass_rate_1: 29.3
|
||||
pass_rate_2: 64.9
|
||||
pass_num_1: 66
|
||||
pass_num_2: 146
|
||||
percent_cases_well_formed: 97.8
|
||||
error_outputs: 66
|
||||
num_malformed_responses: 5
|
||||
num_with_malformed_responses: 5
|
||||
user_asks: 5
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 1
|
||||
total_tests: 225
|
||||
command: "aider --model anthropic/claude-3-7-sonnet-20250219 # plus yml config"
|
||||
date: 2025-02-24
|
||||
versions: 0.75.1.dev
|
||||
seconds_per_case: 105.2
|
||||
total_cost: 36.8343
|
||||
|
||||
- dirname: 2025-02-27-20-26-15--gpt45-diff3
|
||||
test_cases: 224
|
||||
model: gpt-4.5-preview
|
||||
edit_format: diff
|
||||
commit_hash: b462e55-dirty
|
||||
pass_rate_1: 22.3
|
||||
pass_rate_2: 44.9
|
||||
pass_num_1: 50
|
||||
pass_num_2: 101
|
||||
percent_cases_well_formed: 97.3
|
||||
error_outputs: 10
|
||||
num_malformed_responses: 8
|
||||
num_with_malformed_responses: 6
|
||||
user_asks: 15
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 2
|
||||
total_tests: 225
|
||||
command: aider --model openai/gpt-4.5-preview
|
||||
date: 2025-02-27
|
||||
versions: 0.75.2.dev
|
||||
seconds_per_case: 113.5
|
||||
total_cost: 183.1802
|
||||
|
||||
- dirname: 2025-03-06-17-40-24--qwq32b-diff-temp-topp-ex-sys-remind-user-for-real
|
||||
test_cases: 225
|
||||
model: QwQ-32B
|
||||
edit_format: diff
|
||||
commit_hash: 51d118f-dirty
|
||||
pass_rate_1: 8.0
|
||||
pass_rate_2: 20.9
|
||||
pass_num_1: 18
|
||||
pass_num_2: 47
|
||||
percent_cases_well_formed: 67.6
|
||||
error_outputs: 145
|
||||
num_malformed_responses: 143
|
||||
num_with_malformed_responses: 73
|
||||
user_asks: 17
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 4
|
||||
total_tests: 225
|
||||
command: aider --model fireworks_ai/accounts/fireworks/models/qwq-32b
|
||||
date: 2025-03-06
|
||||
versions: 0.75.3.dev
|
||||
seconds_per_case: 228.6
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2025-03-07-15-11-27--qwq32b-arch-temp-topp-again
|
||||
test_cases: 225
|
||||
model: QwQ-32B + Qwen 2.5 Coder Instruct
|
||||
edit_format: architect
|
||||
commit_hash: 52162a5
|
||||
editor_model: fireworks_ai/accounts/fireworks/models/qwen2p5-coder-32b-instruct
|
||||
editor_edit_format: editor-diff
|
||||
pass_rate_1: 9.8
|
||||
pass_rate_2: 26.2
|
||||
pass_num_1: 22
|
||||
pass_num_2: 59
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 122
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 489
|
||||
lazy_comments: 8
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 2
|
||||
total_tests: 225
|
||||
command: aider --model fireworks_ai/accounts/fireworks/models/qwq-32b --architect
|
||||
date: 2025-03-07
|
||||
versions: 0.75.3.dev
|
||||
seconds_per_case: 137.4
|
||||
total_cost: 0
|
||||
|
||||
- dirname: 2025-03-14-23-40-00--cmda-quality-whole2
|
||||
test_cases: 225
|
||||
model: command-a-03-2025-quality
|
||||
edit_format: whole
|
||||
commit_hash: a1aa63f
|
||||
pass_rate_1: 2.2
|
||||
pass_rate_2: 12.0
|
||||
pass_num_1: 5
|
||||
pass_num_2: 27
|
||||
percent_cases_well_formed: 99.6
|
||||
error_outputs: 2
|
||||
num_malformed_responses: 1
|
||||
num_with_malformed_responses: 1
|
||||
user_asks: 215
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 4
|
||||
total_tests: 225
|
||||
command: OPENAI_API_BASE=https://api.cohere.ai/compatibility/v1 aider --model openai/command-a-03-2025-quality
|
||||
date: 2025-03-14
|
||||
versions: 0.77.1.dev
|
||||
seconds_per_case: 85.1
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2025-03-15-01-21-24--gemma3-27b-or
|
||||
test_cases: 225
|
||||
model: gemma-3-27b-it
|
||||
edit_format: whole
|
||||
commit_hash: fd21f51-dirty
|
||||
pass_rate_1: 1.8
|
||||
pass_rate_2: 4.9
|
||||
pass_num_1: 4
|
||||
pass_num_2: 11
|
||||
percent_cases_well_formed: 100.0
|
||||
error_outputs: 3
|
||||
num_malformed_responses: 0
|
||||
num_with_malformed_responses: 0
|
||||
user_asks: 181
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 1
|
||||
test_timeouts: 3
|
||||
total_tests: 225
|
||||
command: aider --model openrouter/google/gemma-3-27b-it
|
||||
date: 2025-03-15
|
||||
versions: 0.77.1.dev
|
||||
seconds_per_case: 79.7
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2025-03-24-15-41-33--deepseek-v3-0324-polyglot-diff
|
||||
test_cases: 225
|
||||
model: DeepSeek V3 (0324)
|
||||
edit_format: diff
|
||||
commit_hash: 502b863
|
||||
pass_rate_1: 28.0
|
||||
pass_rate_2: 55.1
|
||||
pass_num_1: 63
|
||||
pass_num_2: 124
|
||||
percent_cases_well_formed: 99.6
|
||||
error_outputs: 32
|
||||
num_malformed_responses: 1
|
||||
num_with_malformed_responses: 1
|
||||
user_asks: 96
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 2
|
||||
test_timeouts: 4
|
||||
total_tests: 225
|
||||
command: aider --model deepseek/deepseek-chat
|
||||
date: 2025-03-24
|
||||
versions: 0.78.1.dev
|
||||
seconds_per_case: 290.0
|
||||
total_cost: 1.1164
|
||||
|
||||
- dirname: 2025-03-25-19-46-45--gemini-25-pro-exp-diff-fenced
|
||||
test_cases: 225
|
||||
model: Gemini 2.5 Pro exp-03-25
|
||||
edit_format: diff-fenced
|
||||
commit_hash: 33413ec
|
||||
pass_rate_1: 39.1
|
||||
pass_rate_2: 72.9
|
||||
pass_num_1: 88
|
||||
pass_num_2: 164
|
||||
percent_cases_well_formed: 89.8
|
||||
error_outputs: 30
|
||||
num_malformed_responses: 30
|
||||
num_with_malformed_responses: 23
|
||||
user_asks: 57
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 3
|
||||
total_tests: 225
|
||||
command: aider --model gemini/gemini-2.5-pro-exp-03-25
|
||||
date: 2025-03-25
|
||||
versions: 0.78.1.dev
|
||||
seconds_per_case: 47.1
|
||||
total_cost: 0.0000
|
||||
|
||||
- dirname: 2025-03-29-05-24-55--chatgpt4o-mar28-diff
|
||||
test_cases: 225
|
||||
model: chatgpt-4o-latest (2025-03-29)
|
||||
edit_format: diff
|
||||
commit_hash: 0decbad
|
||||
pass_rate_1: 16.4
|
||||
pass_rate_2: 45.3
|
||||
pass_num_1: 37
|
||||
pass_num_2: 102
|
||||
percent_cases_well_formed: 64.4
|
||||
error_outputs: 85
|
||||
num_malformed_responses: 85
|
||||
num_with_malformed_responses: 80
|
||||
user_asks: 174
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 4
|
||||
total_tests: 225
|
||||
command: aider --model chatgpt-4o-latest
|
||||
date: 2025-03-29
|
||||
versions: 0.79.3.dev
|
||||
seconds_per_case: 10.3
|
||||
total_cost: 19.7416
|
||||
|
||||
- dirname: 2025-04-04-02-57-25--qalpha-diff-exsys
|
||||
test_cases: 225
|
||||
model: Quasar Alpha
|
||||
edit_format: diff
|
||||
commit_hash: 8a34a6c-dirty
|
||||
pass_rate_1: 21.8
|
||||
pass_rate_2: 54.7
|
||||
pass_num_1: 49
|
||||
pass_num_2: 123
|
||||
percent_cases_well_formed: 98.2
|
||||
error_outputs: 4
|
||||
num_malformed_responses: 4
|
||||
num_with_malformed_responses: 4
|
||||
user_asks: 187
|
||||
lazy_comments: 0
|
||||
syntax_errors: 0
|
||||
indentation_errors: 0
|
||||
exhausted_context_windows: 0
|
||||
test_timeouts: 4
|
||||
total_tests: 225
|
||||
command: aider --model openrouter/openrouter/quasar-alpha
|
||||
date: 2025-04-04
|
||||
versions: 0.80.5.dev
|
||||
seconds_per_case: 14.8
|
||||
total_cost: 0.0000
|
||||
@@ -5,21 +5,15 @@ If you already have python 3.8-3.13 installed, you can get started quickly like
|
||||
python -m pip install aider-install
|
||||
aider-install
|
||||
|
||||
# Change directory into your code base
|
||||
# Change directory into your codebase
|
||||
cd /to/your/project
|
||||
|
||||
# Work with DeepSeek via DeepSeek's API
|
||||
aider --model deepseek --api-key deepseek=your-key-goes-here
|
||||
# DeepSeek
|
||||
aider --model deepseek --api-key deepseek=<key>
|
||||
|
||||
# Work with Claude 3.5 Sonnet via Anthropic's API
|
||||
aider --model sonnet --api-key anthropic=your-key-goes-here
|
||||
# Claude 3.7 Sonnet
|
||||
aider --model sonnet --api-key anthropic=<key>
|
||||
|
||||
# Work with GPT-4o via OpenAI's API
|
||||
aider --model gpt-4o --api-key openai=your-key-goes-here
|
||||
|
||||
# Work with Sonnet via OpenRouter's API
|
||||
aider --model openrouter/anthropic/claude-3.5-sonnet --api-key openrouter=your-key-goes-here
|
||||
|
||||
# Work with DeepSeek via OpenRouter's API
|
||||
aider --model openrouter/deepseek/deepseek-chat --api-key openrouter=your-key-goes-here
|
||||
# o3-mini
|
||||
aider --model o3-mini --api-key openai=<key>
|
||||
```
|
||||
|
||||
@@ -5,10 +5,66 @@
|
||||
<meta property="og:image" content="{{ site.url }}/assets/aider.jpg">
|
||||
<meta property="twitter:image" content="{{ site.url }}/assets/aider-square.jpg">
|
||||
{% endif %}
|
||||
|
||||
<!-- Custom site title styling -->
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: GlassTTYVT220;
|
||||
src: local("Glass TTY VT220"), local("Glass TTY VT220 Medium"), url(/assets/Glass_TTY_VT220.ttf) format("truetype");
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
font-family: 'GlassTTYVT220', monospace;
|
||||
color: #14b014; /* terminal green color */
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* For SVG logo inside site-title */
|
||||
.site-title img {
|
||||
height: 1.8rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Sidebar gradient styling to match hero section */
|
||||
.side-bar {
|
||||
background: linear-gradient(135deg, #ffffff 0%, rgba(20, 176, 20, 0.01) 25%, rgba(20, 176, 20, 0.04) 40%, rgba(220, 230, 255, 0.4) 60%, rgba(205, 218, 255, 0.4) 80%, #F5F6FA 100%);
|
||||
}
|
||||
</style>
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="{{ site.url }}/feed.xml">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link rel="preload" href="https://fonts.googleapis.com/css?family=Open+Sans:400,700&display=swap" as="style" type="text/css" crossorigin>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Logo Progressive Enhancement for Jekyll pages -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const siteTitle = document.querySelector('.site-title');
|
||||
if (siteTitle) {
|
||||
const textContent = siteTitle.textContent; // Save the text for fallback
|
||||
|
||||
// Create new image element
|
||||
const logoImg = new Image();
|
||||
logoImg.src = '/assets/logo.svg';
|
||||
logoImg.alt = 'Aider Logo';
|
||||
logoImg.style.height = '1.8rem';
|
||||
logoImg.style.verticalAlign = 'middle';
|
||||
|
||||
// Only replace if image loads successfully
|
||||
logoImg.onload = function() {
|
||||
siteTitle.textContent = ''; // Clear text
|
||||
siteTitle.appendChild(logoImg);
|
||||
};
|
||||
|
||||
// If image fails to load, do nothing (keep the text)
|
||||
logoImg.onerror = function() {
|
||||
console.log('SVG logo failed to load, keeping text fallback');
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<meta name="theme-color" content="#157878">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ '/assets/icons/favicon-32x32.png' | relative_url }}">
|
||||
|
||||
@@ -4,7 +4,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const redDiagonalPattern = pattern.draw('diagonal', 'rgba(255, 99, 132, 0.2)');
|
||||
let displayedData = [];
|
||||
|
||||
const HIGHLIGHT_MODEL = '{{ highlight_model | default: "no no no" }}';
|
||||
// Get highlight model from query string or Jekyll variable
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const queryHighlight = urlParams.get('highlight');
|
||||
const HIGHLIGHT_MODEL = queryHighlight || '{{ highlight_model | default: "no no no" }}';
|
||||
|
||||
var leaderboardData = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
@@ -23,6 +27,16 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
return (label && label.includes(HIGHLIGHT_MODEL)) ? 'rgba(255, 99, 132, 1)' : 'rgba(54, 162, 235, 1)';
|
||||
},
|
||||
borderWidth: 1
|
||||
}, {
|
||||
label: 'Total Cost ($)',
|
||||
data: [],
|
||||
type: 'scatter',
|
||||
yAxisID: 'y1',
|
||||
backgroundColor: 'rgba(153, 102, 255, 1)',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1,
|
||||
pointRadius: 5,
|
||||
pointHoverRadius: 7
|
||||
}]
|
||||
};
|
||||
|
||||
@@ -32,7 +46,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
model: '{{ row.model }}',
|
||||
pass_rate: {{ row[pass_rate_field] }},
|
||||
percent_cases_well_formed: {{ row.percent_cases_well_formed }},
|
||||
edit_format: '{{ row.edit_format | default: "diff" }}'
|
||||
edit_format: '{{ row.edit_format | default: "diff" }}',
|
||||
total_cost: {{ row.total_cost | default: 0 }}
|
||||
});
|
||||
{% endfor %}
|
||||
|
||||
@@ -43,6 +58,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
displayedData = [];
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
leaderboardData.datasets[1].data = [];
|
||||
|
||||
allData.forEach(function(row, index) {
|
||||
var rowElement = document.getElementById('edit-row-' + index);
|
||||
@@ -53,6 +69,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
displayedData.push(row);
|
||||
leaderboardData.labels.push(row.model);
|
||||
leaderboardData.datasets[0].data.push(row.pass_rate);
|
||||
// Only include cost if it's not zero (placeholder for unknown)
|
||||
leaderboardData.datasets[1].data.push(row.total_cost > 0 ? row.total_cost : null);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,10 +129,29 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
fillStyle: blueDiagonalPattern,
|
||||
strokeStyle: 'rgba(54, 162, 235, 1)',
|
||||
lineWidth: 1
|
||||
},
|
||||
{
|
||||
text: 'Total Cost ($)',
|
||||
fillStyle: 'rgba(153, 102, 255, 1)',
|
||||
strokeStyle: '#fff',
|
||||
lineWidth: 1,
|
||||
pointStyle: 'circle'
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
const datasetLabel = context.dataset.label || '';
|
||||
const value = context.parsed.y;
|
||||
if (datasetLabel === 'Total Cost ($)') {
|
||||
return datasetLabel + ': $' + value.toFixed(2);
|
||||
}
|
||||
return datasetLabel + ': ' + value.toFixed(1) + '%';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
@@ -125,8 +162,22 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
text: 'Percent completed correctly'
|
||||
}
|
||||
},
|
||||
y1: {
|
||||
beginAtZero: true,
|
||||
position: 'right',
|
||||
grid: {
|
||||
drawOnChartArea: false
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Total Cost ($)'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
ticks: {
|
||||
autoSkip: false, // Prevent labels from being automatically skipped
|
||||
maxRotation: 90, // Allow labels to rotate up to 90 degrees
|
||||
minRotation: 0,
|
||||
callback: function(value, index) {
|
||||
const label = this.getLabelForValue(value);
|
||||
if (label.length <= "claude-3-5-sonnet".length) {
|
||||
@@ -173,6 +224,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
displayedData = [];
|
||||
leaderboardData.labels = [];
|
||||
leaderboardData.datasets[0].data = [];
|
||||
leaderboardData.datasets[1].data = [];
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var rowText = rows[i].textContent;
|
||||
@@ -181,6 +233,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
displayedData.push(allData[i]);
|
||||
leaderboardData.labels.push(allData[i].model);
|
||||
leaderboardData.datasets[0].data.push(allData[i].pass_rate);
|
||||
// Only include cost if it's not zero (placeholder for unknown)
|
||||
leaderboardData.datasets[1].data.push(allData[i].total_cost > 0 ? allData[i].total_cost : null);
|
||||
} else {
|
||||
rows[i].style.display = 'none';
|
||||
}
|
||||
|
||||
228
aider/website/_includes/recording.css
Normal file
228
aider/website/_includes/recording.css
Normal file
@@ -0,0 +1,228 @@
|
||||
/* Terminal header styling */
|
||||
.terminal-header {
|
||||
background-color: #e0e0e0;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
padding: 4px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #c0c0c0;
|
||||
}
|
||||
|
||||
.terminal-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.terminal-button {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.terminal-close {
|
||||
background-color: #ff5f56;
|
||||
border: 1px solid #e0443e;
|
||||
}
|
||||
|
||||
.terminal-minimize {
|
||||
background-color: #ffbd2e;
|
||||
border: 1px solid #dea123;
|
||||
}
|
||||
|
||||
.terminal-expand {
|
||||
background-color: #27c93f;
|
||||
border: 1px solid #1aab29;
|
||||
}
|
||||
|
||||
.terminal-title {
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Toast notification styling */
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toast-notification {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
padding: 12px 25px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
min-width: 200px;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
/* Page container styling */
|
||||
.page-container {
|
||||
max-width: 950px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* macOS backdrop styling */
|
||||
.macos-backdrop {
|
||||
background: linear-gradient(135deg, #ff9966, #ff5e62, #6666ff, #0066ff);
|
||||
border-radius: 12px;
|
||||
padding: clamp(5px, 5vw, 50px) clamp(5px, 2.5vw, 50px);
|
||||
margin: 20px 0;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Add subtle wave animation to backdrop */
|
||||
.macos-backdrop::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: radial-gradient(circle at center, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Add decorative curved lines to the backdrop */
|
||||
.macos-backdrop::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 30%, transparent 0%, transparent 60%, rgba(255,255,255,0.2) 61%, transparent 62%),
|
||||
radial-gradient(circle at 80% 70%, transparent 0%, transparent 40%, rgba(255,255,255,0.2) 41%, transparent 42%),
|
||||
radial-gradient(circle at 40% 90%, transparent 0%, transparent 70%, rgba(255,255,255,0.2) 71%, transparent 72%),
|
||||
radial-gradient(circle at 60% 10%, transparent 0%, transparent 50%, rgba(255,255,255,0.2) 51%, transparent 52%);
|
||||
background-size: 100% 100%;
|
||||
opacity: 1;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.terminal-container {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
position: relative;
|
||||
background-color: white; /* Add background color to terminal container */
|
||||
z-index: 2; /* Ensure terminal appears above the backdrop effects */
|
||||
}
|
||||
|
||||
/* Timestamp link styling */
|
||||
.timestamp-link {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timestamp-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Active timestamp styling */
|
||||
.timestamp-active {
|
||||
background-color: #f0f8ff; /* Light blue background */
|
||||
border-radius: 3px;
|
||||
padding: 2px 4px;
|
||||
margin: -2px -4px;
|
||||
}
|
||||
|
||||
/* Highlight the list item containing the active timestamp */
|
||||
li.active-marker {
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
/* Make list items clickable */
|
||||
.transcript-item {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
padding: 4px 8px;
|
||||
margin-left: -8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.transcript-item:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* Keyboard shortcuts styling */
|
||||
.keyboard-shortcuts {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Hide keyboard shortcuts on devices likely without physical keyboards */
|
||||
.no-physical-keyboard .keyboard-shortcuts {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.keyboard-shortcuts kbd {
|
||||
background-color: #f7f7f7;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.2);
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
font-family: monospace;
|
||||
line-height: 1;
|
||||
margin: 0 2px;
|
||||
padding: 3px 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.asciinema-player-theme-aider {
|
||||
/* Foreground (default text) color */
|
||||
--term-color-foreground: #444444; /* colour238 */
|
||||
|
||||
/* Background color */
|
||||
--term-color-background: #dadada; /* colour253 */
|
||||
|
||||
/* Palette of 16 standard ANSI colors */
|
||||
--term-color-0: #21222c;
|
||||
--term-color-1: #ff5555;
|
||||
--term-color-2: #50fa7b;
|
||||
--term-color-3: #f1fa8c;
|
||||
--term-color-4: #bd93f9;
|
||||
--term-color-5: #ff79c6;
|
||||
--term-color-6: #8be9fd;
|
||||
--term-color-7: #f8f8f2;
|
||||
--term-color-8: #6272a4;
|
||||
--term-color-9: #ff6e6e;
|
||||
--term-color-10: #69ff94;
|
||||
--term-color-11: #ffffa5;
|
||||
--term-color-12: #d6acff;
|
||||
--term-color-13: #ff92df;
|
||||
--term-color-14: #a4ffff;
|
||||
--term-color-15: #ffffff;
|
||||
}
|
||||
428
aider/website/_includes/recording.js
Normal file
428
aider/website/_includes/recording.js
Normal file
@@ -0,0 +1,428 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let player; // Store player reference to make it accessible to click handlers
|
||||
let globalAudio; // Global audio element to be reused
|
||||
|
||||
// Detect if device likely has no physical keyboard
|
||||
function detectNoKeyboard() {
|
||||
// Check if it's a touch device (most mobile devices)
|
||||
const isTouchDevice = ('ontouchstart' in window) ||
|
||||
(navigator.maxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0);
|
||||
|
||||
// Check common mobile user agents as additional signal
|
||||
const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
|
||||
// If it's a touch device and has a mobile user agent, likely has no physical keyboard
|
||||
if (isTouchDevice && isMobileUA) {
|
||||
document.body.classList.add('no-physical-keyboard');
|
||||
}
|
||||
}
|
||||
|
||||
// Run detection
|
||||
detectNoKeyboard();
|
||||
|
||||
// Parse the transcript section to create markers and convert timestamps to links
|
||||
function parseTranscript() {
|
||||
const markers = [];
|
||||
// Find the Commentary heading
|
||||
const transcriptHeading = Array.from(document.querySelectorAll('h2')).find(el => el.textContent.trim() === 'Commentary');
|
||||
|
||||
if (transcriptHeading) {
|
||||
// Get all list items after the transcript heading
|
||||
let currentElement = transcriptHeading.nextElementSibling;
|
||||
|
||||
while (currentElement && currentElement.tagName === 'UL') {
|
||||
const listItems = currentElement.querySelectorAll('li');
|
||||
|
||||
listItems.forEach(item => {
|
||||
const text = item.textContent.trim();
|
||||
const match = text.match(/(\d+):(\d+)\s+(.*)/);
|
||||
|
||||
if (match) {
|
||||
const minutes = parseInt(match[1], 10);
|
||||
const seconds = parseInt(match[2], 10);
|
||||
const timeInSeconds = minutes * 60 + seconds;
|
||||
const formattedTime = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||||
const message = match[3].trim();
|
||||
|
||||
// Create link for the timestamp
|
||||
const timeLink = document.createElement('a');
|
||||
timeLink.href = '#';
|
||||
timeLink.textContent = formattedTime;
|
||||
timeLink.className = 'timestamp-link';
|
||||
timeLink.dataset.time = timeInSeconds;
|
||||
timeLink.dataset.message = message;
|
||||
|
||||
// Add click event to seek the player
|
||||
timeLink.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (player && typeof player.seek === 'function') {
|
||||
player.seek(timeInSeconds);
|
||||
player.play();
|
||||
|
||||
// Also trigger toast and speech
|
||||
showToast(message);
|
||||
speakText(message, timeInSeconds);
|
||||
|
||||
// Highlight this timestamp
|
||||
highlightTimestamp(timeInSeconds);
|
||||
}
|
||||
});
|
||||
|
||||
// Replace text with the link + message
|
||||
item.textContent = '';
|
||||
item.appendChild(timeLink);
|
||||
item.appendChild(document.createTextNode(' ' + message));
|
||||
|
||||
// Add class and click handler to the entire list item
|
||||
item.classList.add('transcript-item');
|
||||
item.dataset.time = timeInSeconds;
|
||||
item.dataset.message = message;
|
||||
|
||||
item.addEventListener('click', function(e) {
|
||||
// Prevent click event if the user clicked directly on the timestamp link
|
||||
// This prevents double-firing of the event
|
||||
if (e.target !== timeLink) {
|
||||
e.preventDefault();
|
||||
if (player && typeof player.seek === 'function') {
|
||||
player.seek(timeInSeconds);
|
||||
player.play();
|
||||
|
||||
// Also trigger toast and speech
|
||||
showToast(message);
|
||||
speakText(message, timeInSeconds);
|
||||
|
||||
// Highlight this timestamp
|
||||
highlightTimestamp(timeInSeconds);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
markers.push([timeInSeconds, message]);
|
||||
}
|
||||
});
|
||||
|
||||
currentElement = currentElement.nextElementSibling;
|
||||
}
|
||||
}
|
||||
|
||||
return markers;
|
||||
}
|
||||
|
||||
// Parse transcript and create markers
|
||||
const markers = parseTranscript();
|
||||
|
||||
// Create player with a single call
|
||||
player = AsciinemaPlayer.create(
|
||||
recording_url,
|
||||
document.getElementById('demo'),
|
||||
{
|
||||
speed: 1.25,
|
||||
idleTimeLimit: 1,
|
||||
theme: "aider",
|
||||
poster: "npt:0:01",
|
||||
markers: markers,
|
||||
controls: true
|
||||
}
|
||||
);
|
||||
|
||||
// Focus on the player element so keyboard shortcuts work immediately
|
||||
setTimeout(() => {
|
||||
// Use setTimeout to ensure the player is fully initialized
|
||||
if (player && typeof player.focus === 'function') {
|
||||
player.focus();
|
||||
} else {
|
||||
// If player doesn't have a focus method, try to find and focus the terminal element
|
||||
const playerElement = document.querySelector('.asciinema-terminal');
|
||||
if (playerElement) {
|
||||
playerElement.focus();
|
||||
} else {
|
||||
// Last resort - try to find element with tabindex
|
||||
const tabbableElement = document.querySelector('[tabindex]');
|
||||
if (tabbableElement) {
|
||||
tabbableElement.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Track active toast elements
|
||||
let activeToast = null;
|
||||
|
||||
// Function to display toast notification
|
||||
function showToast(text) {
|
||||
// Get the appropriate container based on fullscreen state
|
||||
let container = document.getElementById('toast-container');
|
||||
const isFullscreen = document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement;
|
||||
|
||||
// If in fullscreen, check if we need to create a fullscreen toast container
|
||||
if (isFullscreen) {
|
||||
// Target the fullscreen element as the container parent
|
||||
const fullscreenElement = document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.msFullscreenElement;
|
||||
|
||||
// Look for an existing fullscreen toast container
|
||||
let fsContainer = fullscreenElement.querySelector('.fs-toast-container');
|
||||
|
||||
if (!fsContainer) {
|
||||
// Create a new container for fullscreen mode
|
||||
fsContainer = document.createElement('div');
|
||||
fsContainer.className = 'toast-container fs-toast-container';
|
||||
fsContainer.id = 'fs-toast-container';
|
||||
fullscreenElement.appendChild(fsContainer);
|
||||
}
|
||||
|
||||
container = fsContainer;
|
||||
}
|
||||
|
||||
// Remove any existing toast
|
||||
if (activeToast) {
|
||||
hideToast(activeToast);
|
||||
}
|
||||
|
||||
// Create toast element
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast-notification';
|
||||
toast.textContent = text;
|
||||
|
||||
// Add to container
|
||||
container.appendChild(toast);
|
||||
|
||||
// Store reference to active toast
|
||||
activeToast = {
|
||||
element: toast,
|
||||
container: container
|
||||
};
|
||||
|
||||
// Trigger animation
|
||||
setTimeout(() => {
|
||||
toast.style.opacity = '1';
|
||||
}, 10);
|
||||
|
||||
return activeToast;
|
||||
}
|
||||
|
||||
// Function to hide a toast
|
||||
function hideToast(toastInfo) {
|
||||
if (!toastInfo || !toastInfo.element) return;
|
||||
|
||||
toastInfo.element.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
if (toastInfo.container && toastInfo.container.contains(toastInfo.element)) {
|
||||
toastInfo.container.removeChild(toastInfo.element);
|
||||
}
|
||||
|
||||
// If this was the active toast, clear the reference
|
||||
if (activeToast === toastInfo) {
|
||||
activeToast = null;
|
||||
}
|
||||
}, 300); // Wait for fade out animation
|
||||
}
|
||||
|
||||
// Track if TTS is currently in progress to prevent duplicates
|
||||
let ttsInProgress = false;
|
||||
let currentToast = null;
|
||||
|
||||
// Improved browser TTS function
|
||||
function useBrowserTTS(text) {
|
||||
// Don't start new speech if already in progress
|
||||
if (ttsInProgress) {
|
||||
console.log('Speech synthesis already in progress, skipping');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ('speechSynthesis' in window) {
|
||||
console.log('Using browser TTS fallback');
|
||||
|
||||
// Set flag to prevent duplicate speech
|
||||
ttsInProgress = true;
|
||||
|
||||
// Cancel any ongoing speech
|
||||
window.speechSynthesis.cancel();
|
||||
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.rate = 1.0;
|
||||
utterance.pitch = 1.0;
|
||||
utterance.volume = 1.0;
|
||||
|
||||
// For iOS, use a shorter utterance if possible
|
||||
if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
|
||||
utterance.text = text.length > 100 ? text.substring(0, 100) + '...' : text;
|
||||
}
|
||||
|
||||
utterance.onstart = () => console.log('Speech started');
|
||||
utterance.onend = () => {
|
||||
console.log('Speech ended');
|
||||
ttsInProgress = false; // Reset flag when speech completes
|
||||
|
||||
// Hide toast when speech ends
|
||||
if (currentToast) {
|
||||
hideToast(currentToast);
|
||||
currentToast = null;
|
||||
}
|
||||
};
|
||||
utterance.onerror = (e) => {
|
||||
console.warn('Speech error:', e);
|
||||
ttsInProgress = false; // Reset flag on error
|
||||
|
||||
// Also hide toast on error
|
||||
if (currentToast) {
|
||||
hideToast(currentToast);
|
||||
currentToast = null;
|
||||
}
|
||||
};
|
||||
|
||||
window.speechSynthesis.speak(utterance);
|
||||
return true;
|
||||
}
|
||||
console.warn('SpeechSynthesis not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to play pre-generated TTS audio files
|
||||
function speakText(text, timeInSeconds) {
|
||||
// Show the toast and keep reference
|
||||
currentToast = showToast(text);
|
||||
|
||||
// Format time for filename (MM-SS)
|
||||
const minutes = Math.floor(timeInSeconds / 60);
|
||||
const seconds = timeInSeconds % 60;
|
||||
const formattedTime = `${minutes.toString().padStart(2, '0')}-${seconds.toString().padStart(2, '0')}`;
|
||||
|
||||
// Get recording_id from the page or use default from the URL
|
||||
const recordingId = typeof recording_id !== 'undefined' ? recording_id :
|
||||
window.location.pathname.split('/').pop().replace('.html', '');
|
||||
|
||||
// Construct audio file path
|
||||
const audioPath = `/assets/audio/${recordingId}/${formattedTime}.mp3`;
|
||||
|
||||
// Log for debugging
|
||||
console.log(`Attempting to play audio: ${audioPath}`);
|
||||
|
||||
// Detect iOS
|
||||
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||
console.log(`Device is iOS: ${isIOS}`);
|
||||
|
||||
// Flag to track if we've already fallen back to TTS
|
||||
let fallenBackToTTS = false;
|
||||
|
||||
try {
|
||||
// Create or reuse audio element
|
||||
if (!globalAudio) {
|
||||
globalAudio = new Audio();
|
||||
console.log("Created new global Audio element");
|
||||
}
|
||||
|
||||
// Set up event handlers
|
||||
globalAudio.onended = () => {
|
||||
console.log('Audio playback ended');
|
||||
// Hide toast when audio ends
|
||||
if (currentToast) {
|
||||
hideToast(currentToast);
|
||||
currentToast = null;
|
||||
}
|
||||
};
|
||||
|
||||
globalAudio.onerror = (e) => {
|
||||
console.warn(`Audio error: ${e.type}`, e);
|
||||
if (!fallenBackToTTS) {
|
||||
fallenBackToTTS = true;
|
||||
useBrowserTTS(text);
|
||||
} else if (currentToast) {
|
||||
// If we've already tried TTS and that failed too, hide the toast
|
||||
hideToast(currentToast);
|
||||
currentToast = null;
|
||||
}
|
||||
};
|
||||
|
||||
// For iOS, preload might help with subsequent plays
|
||||
if (isIOS) {
|
||||
globalAudio.preload = "auto";
|
||||
}
|
||||
|
||||
// Set the new source
|
||||
globalAudio.src = audioPath;
|
||||
|
||||
// Play with proper error handling
|
||||
const playPromise = globalAudio.play();
|
||||
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(error => {
|
||||
console.warn(`Play error: ${error.message}`);
|
||||
|
||||
// On iOS, a user gesture might be required
|
||||
if (isIOS) {
|
||||
console.log("iOS playback failed, trying SpeechSynthesis");
|
||||
}
|
||||
|
||||
if (!fallenBackToTTS) {
|
||||
fallenBackToTTS = true;
|
||||
useBrowserTTS(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Exception in audio playback: ${e.message}`);
|
||||
useBrowserTTS(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to highlight the active timestamp in the transcript
|
||||
function highlightTimestamp(timeInSeconds) {
|
||||
// Remove previous highlights
|
||||
document.querySelectorAll('.timestamp-active').forEach(el => {
|
||||
el.classList.remove('timestamp-active');
|
||||
});
|
||||
|
||||
document.querySelectorAll('.active-marker').forEach(el => {
|
||||
el.classList.remove('active-marker');
|
||||
});
|
||||
|
||||
// Find the timestamp link with matching time
|
||||
const timestampLinks = document.querySelectorAll('.timestamp-link');
|
||||
let activeLink = null;
|
||||
|
||||
for (const link of timestampLinks) {
|
||||
if (parseInt(link.dataset.time) === timeInSeconds) {
|
||||
activeLink = link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeLink) {
|
||||
// Add highlight class to the link
|
||||
activeLink.classList.add('timestamp-active');
|
||||
|
||||
// Also highlight the parent list item
|
||||
const listItem = activeLink.closest('li');
|
||||
if (listItem) {
|
||||
listItem.classList.add('active-marker');
|
||||
|
||||
// No longer scrolling into view to avoid shifting focus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listener with safety checks
|
||||
if (player && typeof player.addEventListener === 'function') {
|
||||
player.addEventListener('marker', function(event) {
|
||||
try {
|
||||
const { index, time, label } = event;
|
||||
console.log(`marker! ${index} - ${time} - ${label}`);
|
||||
|
||||
// Speak the marker label (toast is now shown within speakText)
|
||||
speakText(label, time);
|
||||
|
||||
// Highlight the corresponding timestamp in the transcript
|
||||
highlightTimestamp(time);
|
||||
} catch (error) {
|
||||
console.error('Error in marker event handler:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
34
aider/website/_includes/recording.md
Normal file
34
aider/website/_includes/recording.md
Normal file
@@ -0,0 +1,34 @@
|
||||
<link rel="stylesheet" type="text/css" href="/assets/asciinema/asciinema-player.css" />
|
||||
|
||||
<style>
|
||||
{% include recording.css %}
|
||||
</style>
|
||||
|
||||
<script src="/assets/asciinema/asciinema-player.min.js"></script>
|
||||
<script>
|
||||
{% include recording.js %}
|
||||
</script>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="toast-container" id="toast-container"></div>
|
||||
|
||||
<div class="macos-backdrop">
|
||||
<div class="terminal-container">
|
||||
<div class="terminal-header">
|
||||
<div class="terminal-buttons">
|
||||
<div class="terminal-button terminal-close"></div>
|
||||
<div class="terminal-button terminal-minimize"></div>
|
||||
<div class="terminal-button terminal-expand"></div>
|
||||
</div>
|
||||
<div class="terminal-title">aider</div>
|
||||
</div>
|
||||
<div id="demo"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="keyboard-shortcuts">
|
||||
<kbd>Space</kbd> Play/pause —
|
||||
<kbd>f</kbd> Fullscreen —
|
||||
<kbd>←</kbd><kbd>→</kbd> ±5s
|
||||
</div>
|
||||
@@ -39,9 +39,7 @@ Aider will directly edit the code in your local source files,
|
||||
and [git commit the changes](https://aider.chat/docs/git.html)
|
||||
with sensible commit messages.
|
||||
You can start a new project or work with an existing git repo.
|
||||
Aider works well with GPT 3.5, GPT-4, GPT-4 Turbo with Vision,
|
||||
and Claude 3 Opus.
|
||||
It also supports [connecting to almost any LLM](https://aider.chat/docs/llms.html).
|
||||
{% include works-best.md %}
|
||||
|
||||
Use the `--browser` switch to launch the browser version of aider:
|
||||
|
||||
|
||||
BIN
aider/website/assets/Glass_TTY_VT220.ttf
Normal file
BIN
aider/website/assets/Glass_TTY_VT220.ttf
Normal file
Binary file not shown.
2366
aider/website/assets/asciinema/asciinema-player.css
Normal file
2366
aider/website/assets/asciinema/asciinema-player.css
Normal file
File diff suppressed because it is too large
Load Diff
1
aider/website/assets/asciinema/asciinema-player.min.js
vendored
Normal file
1
aider/website/assets/asciinema/asciinema-player.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
aider/website/assets/audio/auto-accept-architect/00-01.mp3
Normal file
BIN
aider/website/assets/audio/auto-accept-architect/00-01.mp3
Normal file
Binary file not shown.
BIN
aider/website/assets/audio/auto-accept-architect/00-11.mp3
Normal file
BIN
aider/website/assets/audio/auto-accept-architect/00-11.mp3
Normal file
Binary file not shown.
BIN
aider/website/assets/audio/auto-accept-architect/00-40.mp3
Normal file
BIN
aider/website/assets/audio/auto-accept-architect/00-40.mp3
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user