mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1244 Commits
reproduce
...
eval/visua
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
532a284d5c | ||
|
|
43f6104967 | ||
|
|
e619929909 | ||
|
|
93753ac2e0 | ||
|
|
38e19d214d | ||
|
|
19a4f1c3ec | ||
|
|
45a048f9e3 | ||
|
|
358d9cb3f4 | ||
|
|
e6a2fd3fd4 | ||
|
|
c2f308f397 | ||
|
|
a1f1c802d9 | ||
|
|
ad2237d7dd | ||
|
|
aa0cd51967 | ||
|
|
081a1305f0 | ||
|
|
9912e28576 | ||
|
|
b19a33ccad | ||
|
|
21e912d6fb | ||
|
|
0dd9b95dbe | ||
|
|
aebb583779 | ||
|
|
2ff9ba1229 | ||
|
|
a7e6068ba8 | ||
|
|
24adcee9e3 | ||
|
|
21d4ba0bbd | ||
|
|
5ba9a6d321 | ||
|
|
aa223734d4 | ||
|
|
053723a4d4 | ||
|
|
5a6dbac5a3 | ||
|
|
93d74e9b41 | ||
|
|
1337d03816 | ||
|
|
04e36df4d7 | ||
|
|
f9ba16b648 | ||
|
|
f0dbb02ee1 | ||
|
|
318c811817 | ||
|
|
b468150f2a | ||
|
|
b9a3f1c753 | ||
|
|
09e8a1eeba | ||
|
|
ff3880c76d | ||
|
|
8bd7613724 | ||
|
|
5b7fcfbe1a | ||
|
|
8ae36481df | ||
|
|
25fdb0c3bf | ||
|
|
7f57dbebda | ||
|
|
54589d7e83 | ||
|
|
b7f34c3f8d | ||
|
|
210eeee94a | ||
|
|
509892cf0e | ||
|
|
89963e93d8 | ||
|
|
b6804f9e1e | ||
|
|
d30211da18 | ||
|
|
06121bf20f | ||
|
|
541a445dfc | ||
|
|
03e496fb60 | ||
|
|
1b6e444ecb | ||
|
|
2b04ee2e62 | ||
|
|
4383be1ab4 | ||
|
|
b4d20e3e18 | ||
|
|
532c7cdf02 | ||
|
|
987861b5e7 | ||
|
|
f07ec7a09c | ||
|
|
b1fa6301f0 | ||
|
|
62fbe4c622 | ||
|
|
a1a87af69d | ||
|
|
a12087243a | ||
|
|
899c1f8360 | ||
|
|
c5d7caf01f | ||
|
|
85a760e561 | ||
|
|
8e9c315729 | ||
|
|
000055ba73 | ||
|
|
2edb2337c2 | ||
|
|
7c8a0162ae | ||
|
|
c10f18b3bd | ||
|
|
313c8eca20 | ||
|
|
eff9e07272 | ||
|
|
f8a3aeccd6 | ||
|
|
9375e0d756 | ||
|
|
72af7bbba2 | ||
|
|
0661c69bd3 | ||
|
|
0c961bfd8b | ||
|
|
0bed17758f | ||
|
|
da1a6035ac | ||
|
|
df050e4786 | ||
|
|
e211647eba | ||
|
|
8579710c82 | ||
|
|
8c35150c3e | ||
|
|
578291e961 | ||
|
|
efe04baf34 | ||
|
|
6e089619e0 | ||
|
|
179a89a211 | ||
|
|
8795ee6c6e | ||
|
|
97e938d545 | ||
|
|
b9a70c8d5c | ||
|
|
082d0b25c5 | ||
|
|
c5797d1d5a | ||
|
|
7ce1fb85ff | ||
|
|
fa6792e5a6 | ||
|
|
3d9b4c4af6 | ||
|
|
e21cbf67ee | ||
|
|
6b2e3f938f | ||
|
|
580d7b938c | ||
|
|
28178a2940 | ||
|
|
04382b2b19 | ||
|
|
4da812c781 | ||
|
|
37b7173481 | ||
|
|
f0ebf3eba8 | ||
|
|
92b8d55c2d | ||
|
|
a125b6cd43 | ||
|
|
01ac207b92 | ||
|
|
6d015a5dca | ||
|
|
275512305d | ||
|
|
3a4bc10b29 | ||
|
|
bbd31b32f3 | ||
|
|
295c6fd629 | ||
|
|
5a809c9b53 | ||
|
|
0b74fd71d9 | ||
|
|
4fa5c329d6 | ||
|
|
5b1dcf83a6 | ||
|
|
b9884f7609 | ||
|
|
99eda0e571 | ||
|
|
5832463088 | ||
|
|
045ec2b95d | ||
|
|
23473070b9 | ||
|
|
63133c0ba9 | ||
|
|
2023fb767f | ||
|
|
23f40a1c01 | ||
|
|
873dddb4e8 | ||
|
|
fe50cd1f9f | ||
|
|
516e2da520 | ||
|
|
1dd6f544bc | ||
|
|
40c52feb5b | ||
|
|
f31ccad48b | ||
|
|
828d169b82 | ||
|
|
a622d27016 | ||
|
|
5507b131fe | ||
|
|
0f102e4c71 | ||
|
|
157a1a24f6 | ||
|
|
fcfbcb64d4 | ||
|
|
931792e87a | ||
|
|
ee701eacc2 | ||
|
|
8907fed78e | ||
|
|
3cc20a2576 | ||
|
|
01cf0d433c | ||
|
|
f6bed82ae2 | ||
|
|
3eae2e2aca | ||
|
|
b45fc522c7 | ||
|
|
0d409c8c24 | ||
|
|
5458ebbd7d | ||
|
|
c411a29db4 | ||
|
|
386e04a2ba | ||
|
|
62c4bab6ba | ||
|
|
e308b6fb6f | ||
|
|
27a660fb6b | ||
|
|
27d761a1fe | ||
|
|
8028e2c2dd | ||
|
|
ff9058e28a | ||
|
|
c45caaef1f | ||
|
|
a3c107daa4 | ||
|
|
040839bdd1 | ||
|
|
aabbbb6c6a | ||
|
|
9747c9e9f8 | ||
|
|
bb85542aca | ||
|
|
6e4ff56934 | ||
|
|
561f308401 | ||
|
|
3733c646af | ||
|
|
cf0f6e5e38 | ||
|
|
77aa843d53 | ||
|
|
affbc49b08 | ||
|
|
9016b9c434 | ||
|
|
e3a96097ba | ||
|
|
6a41a3cb4f | ||
|
|
d1555e093c | ||
|
|
5469d5311d | ||
|
|
eaf4c610b2 | ||
|
|
aad7a612c1 | ||
|
|
23425c85aa | ||
|
|
fb53ae43c0 | ||
|
|
1f8a0180d3 | ||
|
|
8cfcdd7ba3 | ||
|
|
9515ac5e62 | ||
|
|
cebd391b7a | ||
|
|
343b86429e | ||
|
|
09734467c0 | ||
|
|
17d722f3b3 | ||
|
|
e310f6b776 | ||
|
|
5626a22e42 | ||
|
|
cde8aad47f | ||
|
|
efd0267919 | ||
|
|
f8735efadf | ||
|
|
00d7395e09 | ||
|
|
150463e629 | ||
|
|
b7bbf0f5eb | ||
|
|
d2790c8b21 | ||
|
|
3d2138d9ce | ||
|
|
e4cf2eee2d | ||
|
|
79551e67f6 | ||
|
|
f5f988e552 | ||
|
|
0c58f469b4 | ||
|
|
56d7dccec9 | ||
|
|
411b63159f | ||
|
|
5ca0beadfb | ||
|
|
aaff3dd075 | ||
|
|
ef2053011d | ||
|
|
e6499a68f6 | ||
|
|
33cb1d5f3c | ||
|
|
5bdebac741 | ||
|
|
510c1644dd | ||
|
|
ec70af9412 | ||
|
|
761a574b09 | ||
|
|
825a9ba893 | ||
|
|
a6d392322a | ||
|
|
1ddf398a81 | ||
|
|
4de6c782cc | ||
|
|
9fef6f909a | ||
|
|
ff466d0f17 | ||
|
|
4c59cff2a3 | ||
|
|
fa44bdb390 | ||
|
|
dd10f37f66 | ||
|
|
3b26678a77 | ||
|
|
f14f75b064 | ||
|
|
ef8e04aee3 | ||
|
|
23df4a09d2 | ||
|
|
eb93113b7a | ||
|
|
c40b0b9ae1 | ||
|
|
61ebec9ff7 | ||
|
|
c567c11267 | ||
|
|
e628615094 | ||
|
|
50f821f9b9 | ||
|
|
15e0a50ff4 | ||
|
|
e52cdfd70a | ||
|
|
c1b514e9d3 | ||
|
|
8983d719bd | ||
|
|
9dd5463e06 | ||
|
|
d5b2ce18cb | ||
|
|
8d627e52cb | ||
|
|
a1b59b6185 | ||
|
|
b73bac62f2 | ||
|
|
ee88af8563 | ||
|
|
f846b31eb8 | ||
|
|
50a0b1d91d | ||
|
|
3d4d66a8c2 | ||
|
|
f3885cadc1 | ||
|
|
2ec2f2538f | ||
|
|
40d8245089 | ||
|
|
d29cc61aa2 | ||
|
|
a2e9e206e8 | ||
|
|
7ae1f768fc | ||
|
|
e692e06acf | ||
|
|
6f80dc5eee | ||
|
|
aa6070624b | ||
|
|
b6c8aa27fa | ||
|
|
3e9ba40a92 | ||
|
|
ab0eabd88c | ||
|
|
4de0a27ed2 | ||
|
|
c37e865c56 | ||
|
|
6523fcae6b | ||
|
|
d7a3ec69d9 | ||
|
|
bb578a2e9d | ||
|
|
d8b33c4e78 | ||
|
|
0e8e3c87f3 | ||
|
|
5e8c20f406 | ||
|
|
4dd40049ab | ||
|
|
c7a8dcf079 | ||
|
|
2869d646b6 | ||
|
|
0e4e1b3316 | ||
|
|
37363a0a8d | ||
|
|
14dc04474c | ||
|
|
5502a9c448 | ||
|
|
e718089802 | ||
|
|
95f7a6a4dc | ||
|
|
7318c22928 | ||
|
|
78aef2b150 | ||
|
|
194181a420 | ||
|
|
2933f07440 | ||
|
|
037457dec9 | ||
|
|
7f665c2fb6 | ||
|
|
ebb2d86ce3 | ||
|
|
6a4442e590 | ||
|
|
157ff4a4b9 | ||
|
|
cc928e6d3f | ||
|
|
6a75800e1b | ||
|
|
c9cecbc461 | ||
|
|
97b1867ea1 | ||
|
|
9bdc1df2df | ||
|
|
9d984aaa30 | ||
|
|
5ed80b5c32 | ||
|
|
df82202178 | ||
|
|
500598666e | ||
|
|
69a9080480 | ||
|
|
b72f50cc4a | ||
|
|
f1a8be3817 | ||
|
|
b34209c9a0 | ||
|
|
a021045dce | ||
|
|
ad45f8dab0 | ||
|
|
3bf5956493 | ||
|
|
d86b536d2f | ||
|
|
e2a25f1b31 | ||
|
|
32ee500c65 | ||
|
|
03d8a263ac | ||
|
|
5b0883ca0d | ||
|
|
31bf9360fd | ||
|
|
c195e467ee | ||
|
|
8975fcd714 | ||
|
|
95b416f092 | ||
|
|
172183f1af | ||
|
|
ee766f6e39 | ||
|
|
1668d58aec | ||
|
|
0d3188914a | ||
|
|
f045c6eb17 | ||
|
|
44d12f8f42 | ||
|
|
ff418a77dc | ||
|
|
3dee207529 | ||
|
|
b6448b9575 | ||
|
|
bfb191b5c7 | ||
|
|
3dd39c59b6 | ||
|
|
642e962f89 | ||
|
|
d4e670a3e7 | ||
|
|
f9cc0bce53 | ||
|
|
2c8b1ee136 | ||
|
|
31dda63f43 | ||
|
|
ecff5c67fb | ||
|
|
725e71ad22 | ||
|
|
200270ba8f | ||
|
|
5bf55a0035 | ||
|
|
96329190d1 | ||
|
|
faf8b5829c | ||
|
|
d62cf7e731 | ||
|
|
4a8bf3d2d0 | ||
|
|
2cfbd26df7 | ||
|
|
b51dd3bc75 | ||
|
|
b1719bb3db | ||
|
|
ee5f49afc1 | ||
|
|
7fe692a7bd | ||
|
|
21948fa81b | ||
|
|
d646b2089d | ||
|
|
f54d953fe1 | ||
|
|
4e7af78b39 | ||
|
|
252c70984c | ||
|
|
5ea096e95b | ||
|
|
a01fb9dca3 | ||
|
|
51af29208f | ||
|
|
e77f435901 | ||
|
|
5fb0eec61e | ||
|
|
4af84a29dc | ||
|
|
7a0488c012 | ||
|
|
581d5ec7a8 | ||
|
|
cfbe77b367 | ||
|
|
3236602919 | ||
|
|
aa2f34a1f5 | ||
|
|
73c38f1163 | ||
|
|
0dd919bacf | ||
|
|
5ad361623d | ||
|
|
c333938384 | ||
|
|
ebf3bf606a | ||
|
|
c2293ad1dd | ||
|
|
6f7d054385 | ||
|
|
e9cafb0372 | ||
|
|
13097f9d1d | ||
|
|
2a66439ca6 | ||
|
|
3876f4a59c | ||
|
|
3db118f3d9 | ||
|
|
fe1bb1c233 | ||
|
|
154ef7391a | ||
|
|
5498ca1f8b | ||
|
|
2cc6a51fe8 | ||
|
|
409d132747 | ||
|
|
2c47a1b33f | ||
|
|
8983eb4cc1 | ||
|
|
bd3e38fe67 | ||
|
|
8488dd2a03 | ||
|
|
d16842f413 | ||
|
|
9cdb8d06c0 | ||
|
|
3297e4d5a8 | ||
|
|
f9d052c493 | ||
|
|
dc3e43b999 | ||
|
|
8bd2205258 | ||
|
|
6ae84bf992 | ||
|
|
afea9f4bec | ||
|
|
8b1a7dff7e | ||
|
|
5e3123964f | ||
|
|
1ffd66f62e | ||
|
|
b04ec03062 | ||
|
|
ee8438cd59 | ||
|
|
7071742d4a | ||
|
|
d76e83b55e | ||
|
|
239619a0a1 | ||
|
|
50478c7d21 | ||
|
|
4998b5de32 | ||
|
|
dd79acdae1 | ||
|
|
b295f5775c | ||
|
|
dabf0ce3af | ||
|
|
09735c7869 | ||
|
|
e0b231092a | ||
|
|
d6a2c4b167 | ||
|
|
6db32025b4 | ||
|
|
fdc00fbca0 | ||
|
|
08b1031666 | ||
|
|
ad822a31e1 | ||
|
|
590ebb6e47 | ||
|
|
4716955960 | ||
|
|
f0257c793b | ||
|
|
7ef6fa666d | ||
|
|
e0626a5741 | ||
|
|
deb2d330b6 | ||
|
|
d733bc6bdd | ||
|
|
d782bdf691 | ||
|
|
9c950f499e | ||
|
|
8ae2fb636e | ||
|
|
de75bd0690 | ||
|
|
2fb45d410d | ||
|
|
8300cf0436 | ||
|
|
7dd2bc569f | ||
|
|
6e1fae29c9 | ||
|
|
19525a487c | ||
|
|
7d0405282a | ||
|
|
92c166551f | ||
|
|
ebb68b33db | ||
|
|
37c46f1ed8 | ||
|
|
ac5190c283 | ||
|
|
ed3916b79b | ||
|
|
27a647cd3e | ||
|
|
42a536d450 | ||
|
|
41e564dc41 | ||
|
|
e979f51ea5 | ||
|
|
425ccb0fbb | ||
|
|
7e4c1c733b | ||
|
|
ffd472d6b8 | ||
|
|
2f2ea9ec91 | ||
|
|
6a6ce5f3ee | ||
|
|
907c65cc00 | ||
|
|
a6d1a4c98f | ||
|
|
a60ee09881 | ||
|
|
246107c618 | ||
|
|
5fa18511b3 | ||
|
|
a482182a9e | ||
|
|
58d22a1905 | ||
|
|
17bbfa29a1 | ||
|
|
5fe116cfb1 | ||
|
|
e9637d40b9 | ||
|
|
6de177521f | ||
|
|
9d36b80b96 | ||
|
|
b11e905988 | ||
|
|
39e5311233 | ||
|
|
651ed1c3c8 | ||
|
|
e27c2e9c99 | ||
|
|
cfe222e1d5 | ||
|
|
c872af4658 | ||
|
|
99fa6c6a4a | ||
|
|
3946f813a4 | ||
|
|
455e667739 | ||
|
|
2874041381 | ||
|
|
279e1d7abc | ||
|
|
a7e4a7aa63 | ||
|
|
2466d903df | ||
|
|
424cdf121a | ||
|
|
6972f4806f | ||
|
|
78cc552e3a | ||
|
|
a241b9ff98 | ||
|
|
c757d7c613 | ||
|
|
2b06e4e5d0 | ||
|
|
cf157c86b3 | ||
|
|
f2dc3663d7 | ||
|
|
e4e3e4abb8 | ||
|
|
22292f72cd | ||
|
|
f4ee3a4cb6 | ||
|
|
2df426732a | ||
|
|
e81623110d | ||
|
|
de81020a8d | ||
|
|
1146b6248b | ||
|
|
c3ddb26e43 | ||
|
|
3d853f7db3 | ||
|
|
027c642268 | ||
|
|
910b2a9b9e | ||
|
|
ea96ffca9b | ||
|
|
7ec407dc50 | ||
|
|
83b94786a3 | ||
|
|
786cde39fd | ||
|
|
ceb60b9a37 | ||
|
|
794408cd31 | ||
|
|
9aa89e8f2f | ||
|
|
3314b97cb2 | ||
|
|
8f47547b08 | ||
|
|
c5117bc48d | ||
|
|
851d88593c | ||
|
|
9908e1b285 | ||
|
|
793e142c4a | ||
|
|
d617f6f6d8 | ||
|
|
438f19c80e | ||
|
|
1b8104ba14 | ||
|
|
f07a4c6074 | ||
|
|
26a38fce98 | ||
|
|
05cc6d4fc3 | ||
|
|
43e074c3ed | ||
|
|
0dde1602c2 | ||
|
|
d0b5dd3000 | ||
|
|
bf2688de7e | ||
|
|
990f277132 | ||
|
|
2f11634cca | ||
|
|
871c544b74 | ||
|
|
92b38dcea1 | ||
|
|
a378ff0965 | ||
|
|
5069a8700a | ||
|
|
96c429df00 | ||
|
|
d96118af4d | ||
|
|
809b58de89 | ||
|
|
cd22817004 | ||
|
|
4b633782e5 | ||
|
|
b9b6cfd406 | ||
|
|
3e49f0f827 | ||
|
|
3e4220b66d | ||
|
|
64a7fef57e | ||
|
|
59c57ac2f0 | ||
|
|
5672a317ac | ||
|
|
afc94a2f0c | ||
|
|
6a79f19c8f | ||
|
|
eb5f4f5ebc | ||
|
|
6ee9028d4a | ||
|
|
e249b920ff | ||
|
|
d920a69f69 | ||
|
|
b156b237ec | ||
|
|
a8ce888981 | ||
|
|
4c432d35e2 | ||
|
|
7afdf0659e | ||
|
|
16a7dd52ae | ||
|
|
ea994b6209 | ||
|
|
8f750de752 | ||
|
|
59532c916e | ||
|
|
3ac57a61a7 | ||
|
|
b808a639d9 | ||
|
|
4374b4aba4 | ||
|
|
4d3b035e00 | ||
|
|
f0ca2239f3 | ||
|
|
082a55195f | ||
|
|
9fab9ae8a6 | ||
|
|
1a06906743 | ||
|
|
c70d160637 | ||
|
|
5d366129d1 | ||
|
|
e22ddc0dd6 | ||
|
|
c370912f12 | ||
|
|
9a96e9f1e4 | ||
|
|
f4ef6ab50f | ||
|
|
8fad6e6e36 | ||
|
|
a8bb35eccb | ||
|
|
cac3b6d7f7 | ||
|
|
be6ca4a3ce | ||
|
|
71be744f2e | ||
|
|
0aa4a7184f | ||
|
|
12dd3352c5 | ||
|
|
c7d89713e8 | ||
|
|
0b05c296d8 | ||
|
|
3b18d77d31 | ||
|
|
c9315d6447 | ||
|
|
6184b9d7f4 | ||
|
|
cd47100888 | ||
|
|
d267c066e7 | ||
|
|
678436da30 | ||
|
|
1725627c7d | ||
|
|
066547cfe8 | ||
|
|
5460a666fa | ||
|
|
36d85b65c8 | ||
|
|
eb2a0b13d9 | ||
|
|
da7963eb5a | ||
|
|
a2779fe2f6 | ||
|
|
4b0faaf79b | ||
|
|
7db0a35305 | ||
|
|
98b2994af2 | ||
|
|
463d4e9a46 | ||
|
|
1021e0c416 | ||
|
|
becb17f0c8 | ||
|
|
bb8b4a0b18 | ||
|
|
36e3dc5c19 | ||
|
|
24d5facec5 | ||
|
|
135a62ca9c | ||
|
|
83add62991 | ||
|
|
68d1e76ccd | ||
|
|
39dad706ca | ||
|
|
ea6809b283 | ||
|
|
d08886f30e | ||
|
|
68e52a9c62 | ||
|
|
7e38297732 | ||
|
|
12ed523c01 | ||
|
|
ebce77ab56 | ||
|
|
f4a2df859f | ||
|
|
94a8f58ece | ||
|
|
746722e1b5 | ||
|
|
27f136b802 | ||
|
|
e211152f93 | ||
|
|
07b96cc8c9 | ||
|
|
3a65b7b07d | ||
|
|
5c83698524 | ||
|
|
cde7ce49be | ||
|
|
24a83eb52d | ||
|
|
2a78b3323b | ||
|
|
a3977621ed | ||
|
|
018080aae0 | ||
|
|
302e41d7bb | ||
|
|
3c61a9521b | ||
|
|
c9ed9b166b | ||
|
|
e052c25572 | ||
|
|
f0ca45c59e | ||
|
|
7f5022c8fe | ||
|
|
de07fcfddc | ||
|
|
ff84a3eede | ||
|
|
1f723293db | ||
|
|
2c580387c5 | ||
|
|
ca64c69b4a | ||
|
|
a531413d86 | ||
|
|
422104c877 | ||
|
|
c75ca7d976 | ||
|
|
6b89386398 | ||
|
|
a87b8599eb | ||
|
|
de821718fd | ||
|
|
088e895a3d | ||
|
|
104f52bcdd | ||
|
|
97f3249205 | ||
|
|
9d47ddba38 | ||
|
|
f7652bd558 | ||
|
|
2b7932b46c | ||
|
|
7074e45ec3 | ||
|
|
a679fcc3b5 | ||
|
|
8b1d5f5a3b | ||
|
|
9882b62777 | ||
|
|
b49bdb9d85 | ||
|
|
00ffc33d1b | ||
|
|
1acb66c2b3 | ||
|
|
5b3db1bd33 | ||
|
|
bdc4513937 | ||
|
|
ffc4d32440 | ||
|
|
9cd248d475 | ||
|
|
5f52eebb40 | ||
|
|
b0c4580999 | ||
|
|
f3b35663e9 | ||
|
|
be92965209 | ||
|
|
89b304ccb7 | ||
|
|
01cacf7c33 | ||
|
|
fac5237c69 | ||
|
|
c784151765 | ||
|
|
ce6f99d80e | ||
|
|
852c90f64a | ||
|
|
42b49e6c43 | ||
|
|
07f0d1ccb3 | ||
|
|
52a428d74a | ||
|
|
27cd507cd2 | ||
|
|
a753babb7a | ||
|
|
38dc41ca42 | ||
|
|
8dee334236 | ||
|
|
a93f1402de | ||
|
|
bc3f0ac24a | ||
|
|
f55ddbed0e | ||
|
|
fd81670ba8 | ||
|
|
79ed4e3567 | ||
|
|
b3fbbbaa9d | ||
|
|
87c02177d7 | ||
|
|
207df9dd30 | ||
|
|
59f7093428 | ||
|
|
123fb4b75d | ||
|
|
40e2d28e87 | ||
|
|
c555611d58 | ||
|
|
50e7da9c3d | ||
|
|
0cfb132ab7 | ||
|
|
17f4c6e1a9 | ||
|
|
910b283ac2 | ||
|
|
b54724ac3f | ||
|
|
0633a99298 | ||
|
|
d9c5f11046 | ||
|
|
32fdcd58e5 | ||
|
|
de71b7cdb8 | ||
|
|
04aeccfb69 | ||
|
|
4eea1286d4 | ||
|
|
488a320ffd | ||
|
|
377fadc2eb | ||
|
|
7df7f43e3c | ||
|
|
a45aba512a | ||
|
|
a1a9d2f175 | ||
|
|
79492b6551 | ||
|
|
80fdb9a2f4 | ||
|
|
975e75531d | ||
|
|
1b5f5bcdad | ||
|
|
8c00d96024 | ||
|
|
bf8ccc8fc3 | ||
|
|
037d770f66 | ||
|
|
dd50246672 | ||
|
|
090771674c | ||
|
|
d8ab0208ba | ||
|
|
a07e8272da | ||
|
|
be82832eb1 | ||
|
|
67c8915d51 | ||
|
|
40b3ccb17c | ||
|
|
35c68863dc | ||
|
|
8bfee87bcf | ||
|
|
e1383afbc3 | ||
|
|
4ce3b9094a | ||
|
|
0a4e196670 | ||
|
|
8d32a59f55 | ||
|
|
38b92f4251 | ||
|
|
88dbe85594 | ||
|
|
f5003a7449 | ||
|
|
a6810fa6ad | ||
|
|
fc05d8d4eb | ||
|
|
1d6ef0e18e | ||
|
|
dc0e223d1a | ||
|
|
932de79154 | ||
|
|
fa625fed70 | ||
|
|
f9fa1d95cb | ||
|
|
5615d54f81 | ||
|
|
8166bf768a | ||
|
|
c3991c870d | ||
|
|
1a27619b39 | ||
|
|
cc15aee405 | ||
|
|
53390d9885 | ||
|
|
0335b1a634 | ||
|
|
bb362cd377 | ||
|
|
4405b109e3 | ||
|
|
47464a9cfa | ||
|
|
2b3fd94540 | ||
|
|
1bd46f3832 | ||
|
|
8a063fdf6a | ||
|
|
025dac5d8f | ||
|
|
0e5e754420 | ||
|
|
7a8e207985 | ||
|
|
a4de0f2142 | ||
|
|
27716171bf | ||
|
|
e5d7735d75 | ||
|
|
83ccb74d36 | ||
|
|
118957235d | ||
|
|
4a6406ed71 | ||
|
|
4bef974a89 | ||
|
|
e497438085 | ||
|
|
74b3335b7d | ||
|
|
55c41212c8 | ||
|
|
4374ea08d3 | ||
|
|
436ecb80a3 | ||
|
|
df9e9fca5a | ||
|
|
add0e7d05c | ||
|
|
145194c87b | ||
|
|
6eafe0d2a8 | ||
|
|
eeb2342509 | ||
|
|
edfba4618a | ||
|
|
98751a3ee2 | ||
|
|
24117143ae | ||
|
|
78f4712080 | ||
|
|
1d2a616be7 | ||
|
|
ba25b02978 | ||
|
|
966da7b7c8 | ||
|
|
f0af90bff3 | ||
|
|
1638968509 | ||
|
|
250fcbe62c | ||
|
|
0595d2336a | ||
|
|
387c8f1df3 | ||
|
|
f6c2b287bc | ||
|
|
ab188d026d | ||
|
|
316fc260f6 | ||
|
|
aab7fa483b | ||
|
|
496364ce53 | ||
|
|
4446d3180f | ||
|
|
7b8241e424 | ||
|
|
8857f02083 | ||
|
|
1747b3d6b2 | ||
|
|
36623a16da | ||
|
|
9d3b77bffc | ||
|
|
2682518d0e | ||
|
|
b27fabe504 | ||
|
|
adf7ab5849 | ||
|
|
456998175f | ||
|
|
b4afd9f170 | ||
|
|
73c7375b92 | ||
|
|
6414b1af6e | ||
|
|
dd55290f4e | ||
|
|
be77baea31 | ||
|
|
a812e2b5f1 | ||
|
|
4ebff5aaf3 | ||
|
|
0687608feb | ||
|
|
db4e1dbbec | ||
|
|
9442e4f9e3 | ||
|
|
e17f7b22a6 | ||
|
|
ce6939fc0d | ||
|
|
4705ef9ec2 | ||
|
|
9c2b48ff5d | ||
|
|
87906b96a7 | ||
|
|
c0a0d46eb2 | ||
|
|
0ea5dcc781 | ||
|
|
d9e0344619 | ||
|
|
1c9cdaf1a2 | ||
|
|
bde978cf0f | ||
|
|
2587220b12 | ||
|
|
87bc35d2c8 | ||
|
|
866ba6e3b2 | ||
|
|
2b0eada176 | ||
|
|
2e50a5bef5 | ||
|
|
3ae4bc0f8e | ||
|
|
faf774cdbd | ||
|
|
05645d1bbd | ||
|
|
e21abce786 | ||
|
|
75ee54bbc5 | ||
|
|
89406bac44 | ||
|
|
572b3ad682 | ||
|
|
981b05fc2b | ||
|
|
997dc80d18 | ||
|
|
e231776be8 | ||
|
|
d50425865a | ||
|
|
6d19c93d19 | ||
|
|
30eeaa641c | ||
|
|
ae13171194 | ||
|
|
421b4c108a | ||
|
|
affb2123d9 | ||
|
|
fdb385ab93 | ||
|
|
13d101e092 | ||
|
|
6cf3728247 | ||
|
|
ae188458ef | ||
|
|
a20da54e3a | ||
|
|
2a6740f4ba | ||
|
|
5ba7bc6be1 | ||
|
|
98d4884ced | ||
|
|
be3cbb045e | ||
|
|
8bfd2fcf4f | ||
|
|
d4e3982a6b | ||
|
|
1f23dc89b6 | ||
|
|
7340b78962 | ||
|
|
c3da25febc | ||
|
|
8d2b2d4318 | ||
|
|
c4f5c07be1 | ||
|
|
349e2dbe50 | ||
|
|
dcd4b04f57 | ||
|
|
78eacc4489 | ||
|
|
60990c128a | ||
|
|
c4c25ea229 | ||
|
|
930726f4e8 | ||
|
|
ee2c2ff2b8 | ||
|
|
8c064fe3df | ||
|
|
e878741ae7 | ||
|
|
90e2bf4883 | ||
|
|
615b94cf2f | ||
|
|
ce2430180f | ||
|
|
eaea94cc1b | ||
|
|
385cc8f512 | ||
|
|
2d5b360505 | ||
|
|
9b6fd239d0 | ||
|
|
dd15845b91 | ||
|
|
64adb64fef | ||
|
|
6573304014 | ||
|
|
29ddcdaf46 | ||
|
|
d0bbad8eda | ||
|
|
7b81df2a94 | ||
|
|
550044454c | ||
|
|
3927fc3616 | ||
|
|
864f81bc71 | ||
|
|
54250e3fe2 | ||
|
|
da548d308c | ||
|
|
6fe5482b20 | ||
|
|
520586a89c | ||
|
|
263798584e | ||
|
|
a9a593bb21 | ||
|
|
6471d0f94d | ||
|
|
5cc16cb82a | ||
|
|
cc68756b26 | ||
|
|
126bf316bc | ||
|
|
91308ba4dc | ||
|
|
b660aa99b8 | ||
|
|
cf793582a7 | ||
|
|
56fe905241 | ||
|
|
02abf60433 | ||
|
|
e6a5e39047 | ||
|
|
feee509de7 | ||
|
|
fd6facbf03 | ||
|
|
1ea3087eec | ||
|
|
2e09b4f95e | ||
|
|
d2d55f5ea2 | ||
|
|
0e467b1429 | ||
|
|
ec3152b6e1 | ||
|
|
642e01b673 | ||
|
|
6cb174b7d1 | ||
|
|
154854bbe3 | ||
|
|
678630c5bd | ||
|
|
ad800bf373 | ||
|
|
206788a0e8 | ||
|
|
ca3fbb2a80 | ||
|
|
cc500a622a | ||
|
|
5fb3dece93 | ||
|
|
83c096b974 | ||
|
|
015df47e53 | ||
|
|
c1b323a076 | ||
|
|
84a578ad20 | ||
|
|
8e5db345b2 | ||
|
|
f61266841c | ||
|
|
277d991b37 | ||
|
|
20aa66d5e2 | ||
|
|
9bc6252967 | ||
|
|
bb416009c5 | ||
|
|
226ea545fa | ||
|
|
e12bff5189 | ||
|
|
23d3becf1d | ||
|
|
be79ccdb39 | ||
|
|
2277897f86 | ||
|
|
be9619be3a | ||
|
|
cb58dab82b | ||
|
|
8ab293a667 | ||
|
|
8a93da51be | ||
|
|
6f2e678028 | ||
|
|
da23189e4c | ||
|
|
2cf77e2589 | ||
|
|
58e9b31d08 | ||
|
|
53c2932fa5 | ||
|
|
ad4e5b3851 | ||
|
|
a56850a4e3 | ||
|
|
173018eb58 | ||
|
|
6bbd75c6e7 | ||
|
|
50c13aad98 | ||
|
|
15df12cf15 | ||
|
|
9862d93cfb | ||
|
|
9566ca4a3c | ||
|
|
0ca66beac9 | ||
|
|
0d93c5914d | ||
|
|
d47f3e854b | ||
|
|
f60652dc5a | ||
|
|
746e6595d5 | ||
|
|
7c95fd6038 | ||
|
|
78cf9d5dec | ||
|
|
5eae10d1e2 | ||
|
|
8fa3591073 | ||
|
|
70bd710e82 | ||
|
|
63ff69fd97 | ||
|
|
640ce0f60d | ||
|
|
25f9413965 | ||
|
|
4e8cfb0d60 | ||
|
|
4dfc7a7ef0 | ||
|
|
7186224899 | ||
|
|
343cc8710f | ||
|
|
20455cea3e | ||
|
|
df23168c10 | ||
|
|
ff8a9a1a56 | ||
|
|
edcc391768 | ||
|
|
87021bd78f | ||
|
|
2692c0c8fd | ||
|
|
81d455a33b | ||
|
|
495fc47c28 | ||
|
|
6a7abcc7c9 | ||
|
|
4c5e2a339f | ||
|
|
caa77cf7a6 | ||
|
|
a3c49538fc | ||
|
|
c3764a7422 | ||
|
|
36e304b3da | ||
|
|
62a58ea5d3 | ||
|
|
33a74e2792 | ||
|
|
f867fda2f9 | ||
|
|
2d2d3ccfa5 | ||
|
|
b23c7aab5a | ||
|
|
a6993b7bf5 | ||
|
|
77772b6954 | ||
|
|
ea883d4d18 | ||
|
|
3f36338d19 | ||
|
|
ae6c489423 | ||
|
|
8937c2ff12 | ||
|
|
45fb4fb9bc | ||
|
|
aae9b5ba5d | ||
|
|
f2321dbfae | ||
|
|
5ec3cb0ac9 | ||
|
|
217eb5dee2 | ||
|
|
f47afa9ebc | ||
|
|
72db908251 | ||
|
|
45a9d0ba9a | ||
|
|
1a65094377 | ||
|
|
2d4c79f181 | ||
|
|
d5fc8bc65f | ||
|
|
51387a52c1 | ||
|
|
fe084b4b16 | ||
|
|
5097c4fe71 | ||
|
|
be5f9772e6 | ||
|
|
39798e9758 | ||
|
|
d81a330a13 | ||
|
|
81d3a2881a | ||
|
|
59fbc11afe | ||
|
|
1db912aed3 | ||
|
|
e459069941 | ||
|
|
411e66395d | ||
|
|
bf61bcb34a | ||
|
|
69f2bf93c1 | ||
|
|
3661893161 | ||
|
|
ebeda8bcfb | ||
|
|
ce18792b12 | ||
|
|
ef3e106543 | ||
|
|
9d6c1e569d | ||
|
|
cdd05a98db | ||
|
|
568c8ce993 | ||
|
|
9296cedbed | ||
|
|
98b39023f4 | ||
|
|
e6847e9e61 | ||
|
|
a3571ec510 | ||
|
|
0809d26f4d | ||
|
|
9c07370559 | ||
|
|
bfdd7fd620 | ||
|
|
0186674352 | ||
|
|
d4666cdc7d | ||
|
|
3886fa8b04 | ||
|
|
46299301f2 | ||
|
|
7be224e595 | ||
|
|
11ea248b41 | ||
|
|
097de51be2 | ||
|
|
cd0198b87f | ||
|
|
01ae54a69d | ||
|
|
93f95d85de | ||
|
|
6b1f23a20a | ||
|
|
09243eba07 | ||
|
|
e3450bb8c9 | ||
|
|
583b54c854 | ||
|
|
8c32ef2234 | ||
|
|
9d0e6a24bc | ||
|
|
40d2935911 | ||
|
|
42c118f4b4 | ||
|
|
e60eaf9a52 | ||
|
|
1354675ce3 | ||
|
|
2eb42cb4f4 | ||
|
|
00f961822a | ||
|
|
62c39def7c | ||
|
|
e986e78b31 | ||
|
|
6d461a4934 | ||
|
|
25462ae394 | ||
|
|
ad60ef11ad | ||
|
|
ceebbcac2a | ||
|
|
245334e89d | ||
|
|
80a631361b | ||
|
|
9cc9b19958 | ||
|
|
0c2a35b256 | ||
|
|
641a15356f | ||
|
|
42649745bd | ||
|
|
4678ae4ebd | ||
|
|
2f310d9338 | ||
|
|
ee6a1cf334 | ||
|
|
152f99c64f | ||
|
|
909e332207 | ||
|
|
053e2f90d5 | ||
|
|
bd4640924c | ||
|
|
f26861aa93 | ||
|
|
cf9f980a22 | ||
|
|
16a2cf37da | ||
|
|
9c95d0ff58 | ||
|
|
9641bfbd3e | ||
|
|
5c31fd9357 | ||
|
|
1abfd3b808 | ||
|
|
e0594432e2 | ||
|
|
e81c5597d6 | ||
|
|
c8a933590a | ||
|
|
dd228c07e0 | ||
|
|
e0f8a5d508 | ||
|
|
e93db80769 | ||
|
|
14a4e1018a | ||
|
|
bb151655cc | ||
|
|
471867859f | ||
|
|
a1d09c4437 | ||
|
|
240b500acf | ||
|
|
d518ca08b7 | ||
|
|
52e0630af8 | ||
|
|
178dbfaf4a | ||
|
|
240a470a1d | ||
|
|
dd2cb4399a | ||
|
|
4eaf28d7b1 | ||
|
|
5a45c648a8 | ||
|
|
3cf794faef | ||
|
|
04643d6f3c | ||
|
|
53a015f718 | ||
|
|
148d22e1af | ||
|
|
31b2e4b5b2 | ||
|
|
1d6633164f | ||
|
|
dd89cfba2a | ||
|
|
0144caaf1f | ||
|
|
ec1a86f150 | ||
|
|
926af7f5fd | ||
|
|
cc55c6dbe5 | ||
|
|
c777cfeacf | ||
|
|
823966c24e | ||
|
|
adba7dad96 | ||
|
|
1109637efb | ||
|
|
71adfeebab | ||
|
|
8059e8e298 | ||
|
|
54ac340e0b | ||
|
|
13901b4b5a | ||
|
|
0b27d51135 | ||
|
|
f0ce682fa0 | ||
|
|
3567911da8 | ||
|
|
215d227e5a | ||
|
|
50e6cc6156 | ||
|
|
8d6eda3623 | ||
|
|
d5f965b474 | ||
|
|
c3bbe604eb | ||
|
|
c7fe39998c | ||
|
|
ec6e07647f | ||
|
|
e744eadb8b | ||
|
|
e582806004 | ||
|
|
f427f9d8d4 | ||
|
|
d669c7b60d | ||
|
|
42be4ee5bc | ||
|
|
f994277d0f | ||
|
|
5ccee7c8a7 | ||
|
|
575a829d94 | ||
|
|
2bed3a424c | ||
|
|
a4cc010110 | ||
|
|
9651368e6a | ||
|
|
c5025fb66e | ||
|
|
3f9111c615 | ||
|
|
89e95f2671 | ||
|
|
5bfa0c2f8d | ||
|
|
84141f656d | ||
|
|
6ff7506581 | ||
|
|
41dc7f0256 | ||
|
|
34f3b61536 | ||
|
|
4533c47595 | ||
|
|
47774e60b0 | ||
|
|
b78f646b65 | ||
|
|
3e5c01dfc8 | ||
|
|
29c34e0b6a | ||
|
|
c919086e25 | ||
|
|
0a03c802f5 | ||
|
|
081ebdbdd8 | ||
|
|
572c7b726d | ||
|
|
cfc5bb70c1 | ||
|
|
008b866a38 | ||
|
|
676ad3e140 | ||
|
|
19278de5d0 | ||
|
|
891e4a8d34 | ||
|
|
85be8607e0 | ||
|
|
49b244610c | ||
|
|
b347b1d06f | ||
|
|
0c86a60b35 | ||
|
|
01317138e2 | ||
|
|
e03855cd7f | ||
|
|
757c9593f1 | ||
|
|
266e8ff951 | ||
|
|
3e79cd12a6 | ||
|
|
2cc1c3ef42 | ||
|
|
ef0b08a46e | ||
|
|
f1d5202884 | ||
|
|
11cedfb854 | ||
|
|
6d103a0db2 | ||
|
|
798aaeaef6 | ||
|
|
0df4b97e5b | ||
|
|
81b3cd71b3 | ||
|
|
9241ae2148 | ||
|
|
d3f86e052a | ||
|
|
e0c65f8f9c | ||
|
|
394ab360a8 | ||
|
|
8a146d5ced | ||
|
|
1d052818ae | ||
|
|
ee284bae8f | ||
|
|
1b1d8f0b02 | ||
|
|
c32cec7f89 | ||
|
|
7b2b1eff57 | ||
|
|
2f1b537471 | ||
|
|
f2a71eb388 | ||
|
|
63c5d74169 | ||
|
|
5d77aec90b | ||
|
|
a66e738957 | ||
|
|
582f07f9c9 | ||
|
|
83b7fcc3ed | ||
|
|
dc418e7b71 | ||
|
|
dd3d1497f6 | ||
|
|
8ea2d61ff2 | ||
|
|
73ded7de10 | ||
|
|
3f6aa0d1f1 | ||
|
|
2556767ccb | ||
|
|
fbef93b762 | ||
|
|
3a93fd4c64 | ||
|
|
a12e8cf06a | ||
|
|
69479852ff | ||
|
|
7024e973d4 | ||
|
|
a260cc8dc8 | ||
|
|
7e53c96b14 | ||
|
|
96b23d2e4c | ||
|
|
b97aa10b66 | ||
|
|
b61455042f | ||
|
|
c84495830e | ||
|
|
3435f1e5d8 | ||
|
|
714e46f29a | ||
|
|
e0608af0b3 | ||
|
|
402a03cb9a | ||
|
|
01462e11d7 | ||
|
|
ebd93977cd | ||
|
|
ef189d52a5 | ||
|
|
3c77cc80dc | ||
|
|
31b189c9af | ||
|
|
a7630c399a | ||
|
|
86521c971b | ||
|
|
6682e0f1dd | ||
|
|
031b91457a | ||
|
|
b13ed017d8 | ||
|
|
72ca1690a7 | ||
|
|
45066f19dc | ||
|
|
809903bad4 | ||
|
|
071f1f0e13 | ||
|
|
08a505406b | ||
|
|
1756b85675 | ||
|
|
e8ab8f1146 | ||
|
|
a6ea4c2a60 | ||
|
|
1e7f398376 | ||
|
|
0f6fb0f80e | ||
|
|
bd1f0f1671 | ||
|
|
2842efa0e8 | ||
|
|
620526b8b4 | ||
|
|
31dbd3d02e | ||
|
|
dd7174e559 | ||
|
|
8fdfece059 | ||
|
|
ad0b549d8b | ||
|
|
47f60b8275 | ||
|
|
5d7f2fd4ae | ||
|
|
22e885736b | ||
|
|
f9589a552e | ||
|
|
043fb2771d | ||
|
|
b95740cc55 | ||
|
|
c864715b43 | ||
|
|
a7f1bca586 | ||
|
|
b4408b41c9 | ||
|
|
e3be71f523 | ||
|
|
dbe767325f | ||
|
|
ec0fe35d48 | ||
|
|
883f36b005 | ||
|
|
dc6b4b7296 | ||
|
|
fe0a8eb036 | ||
|
|
c3117e8c39 | ||
|
|
f7ebc1cf1f | ||
|
|
8a419b5c45 | ||
|
|
9787a31ba1 | ||
|
|
31296624d1 | ||
|
|
656222f416 | ||
|
|
a4e61faf56 | ||
|
|
f4657edc48 | ||
|
|
ef09f0fe37 | ||
|
|
f996b31d64 | ||
|
|
fa0d9cfa42 | ||
|
|
07a094e701 | ||
|
|
52c5abccbf | ||
|
|
29b0e62cd7 | ||
|
|
2a10ff1374 | ||
|
|
592c9580d2 | ||
|
|
804674bb9f | ||
|
|
41a54378dc | ||
|
|
9d4eb7d19d | ||
|
|
d886bf7e2b | ||
|
|
dcede97f68 | ||
|
|
531aec404a | ||
|
|
b7e050eaf6 | ||
|
|
1cc1016768 | ||
|
|
d3c2a022b3 | ||
|
|
88abf1b5d8 | ||
|
|
834296f7b4 | ||
|
|
243cb492aa | ||
|
|
f11e767eb5 | ||
|
|
0db664986d | ||
|
|
a33f61c025 |
@@ -1 +0,0 @@
|
||||
The files in this directory configure a development container for GitHub Codespaces.
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "OpenHands Codespaces",
|
||||
"image": "mcr.microsoft.com/devcontainers/universal",
|
||||
"customizations":{
|
||||
"vscode":{
|
||||
"extensions": [
|
||||
"ms-python.python"
|
||||
]
|
||||
}
|
||||
},
|
||||
"onCreateCommand": "sh ./.devcontainer/on_create.sh",
|
||||
"postCreateCommand": "make build",
|
||||
"postStartCommand": "USE_HOST_NETWORK=True nohup bash -c 'make run &'"
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
sudo apt update
|
||||
sudo apt install -y netcat
|
||||
sudo add-apt-repository -y ppa:deadsnakes/ppa
|
||||
sudo apt install -y python3.11
|
||||
curl -sSL https://install.python-poetry.org | python3.11 -
|
||||
# chromadb requires SQLite > 3.35 but SQLite in Python3.11.9 comes with 3.31.1
|
||||
sudo cp /opt/conda/lib/libsqlite3.so.0 /lib/x86_64-linux-gnu/libsqlite3.so.0
|
||||
70
.github/ISSUE_TEMPLATE/bug_template.yml
vendored
70
.github/ISSUE_TEMPLATE/bug_template.yml
vendored
@@ -5,71 +5,57 @@ labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thank you for taking the time to fill out this bug report. We greatly appreciate your effort to complete this template fully. Please provide as much information as possible to help us understand and address the issue effectively.
|
||||
value: Thank you for taking the time to fill out this bug report. Please provide as much information as possible to help us understand and address the issue effectively.
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for the same bug?
|
||||
description: Please check if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have checked the troubleshooting document at https://docs.all-hands.dev/modules/usage/troubleshooting
|
||||
required: true
|
||||
- label: I have checked the existing issues.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: Provide a short description of the problem.
|
||||
label: Describe the bug and reproduction steps
|
||||
description: Provide a description of the issue along with any reproduction steps.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current-version
|
||||
- type: dropdown
|
||||
id: installation
|
||||
attributes:
|
||||
label: Current OpenHands version
|
||||
description: What version of OpenHands are you using? If you're running in docker, tell us the tag you're using (e.g. ghcr.io/all-hands-ai/openhands:0.3.1).
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
label: OpenHands Installation
|
||||
description: How are you running OpenHands?
|
||||
options:
|
||||
- Docker command in README
|
||||
- Development workflow
|
||||
- app.all-hands.dev
|
||||
- Other
|
||||
default: 0
|
||||
|
||||
- type: textarea
|
||||
id: config
|
||||
- type: input
|
||||
id: openhands-version
|
||||
attributes:
|
||||
label: Installation and Configuration
|
||||
description: Please provide any commands you ran and any configuration (redacting API keys)
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
label: OpenHands Version
|
||||
description: What version of OpenHands are you using?
|
||||
placeholder: ex. 0.9.8, main, etc.
|
||||
|
||||
- type: textarea
|
||||
id: model-agent
|
||||
attributes:
|
||||
label: Model and Agent
|
||||
description: What model and agent are you using? You can see these settings in the UI by clicking the settings wheel.
|
||||
placeholder: |
|
||||
- Model:
|
||||
- Agent:
|
||||
|
||||
- type: textarea
|
||||
id: os-version
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: What Operating System are you using? Linux, Mac OS, WSL on Windows
|
||||
|
||||
- type: textarea
|
||||
id: repro-steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: Please list the steps to reproduce the issue.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
options:
|
||||
- MacOS
|
||||
- Linux
|
||||
- WSL on Windows
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Logs, Errors, Screenshots, and Additional Context
|
||||
description: If you want to share the chat history you can click the thumbs-down (👎) button above the input field and you will get a shareable link (you can also click thumbs up when things are going well of course!). LLM logs will be stored in the `logs/llm/default` folder. Please add any additional context about the problem here.
|
||||
description: Please provide any additional information you think might help. If you want to share the chat history
|
||||
you can click the thumbs-down (👎) button above the input field and you will get a shareable link
|
||||
(you can also click thumbs up when things are going well of course!). LLM logs will be stored in the
|
||||
`logs/llm/default` folder. Please add any additional context about the problem here.
|
||||
|
||||
57
.github/dependabot.yml
vendored
57
.github/dependabot.yml
vendored
@@ -1,21 +1,38 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 20
|
||||
open-pull-requests-limit: 1
|
||||
groups:
|
||||
# put packages in their own group if they have a history of breaking the build or needing to be reverted
|
||||
pre-commit:
|
||||
patterns:
|
||||
- "pre-commit"
|
||||
llama:
|
||||
patterns:
|
||||
- "llama*"
|
||||
chromadb:
|
||||
patterns:
|
||||
- "chromadb"
|
||||
browsergym:
|
||||
patterns:
|
||||
- "browsergym*"
|
||||
security-all:
|
||||
applies-to: "security-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
version-all:
|
||||
applies-to: "version-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/frontend"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 20
|
||||
open-pull-requests-limit: 1
|
||||
groups:
|
||||
docusaurus:
|
||||
patterns:
|
||||
@@ -23,12 +40,21 @@ updates:
|
||||
eslint:
|
||||
patterns:
|
||||
- "*eslint*"
|
||||
security-all:
|
||||
applies-to: "security-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
version-all:
|
||||
applies-to: "version-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/docs"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 20
|
||||
interval: "weekly"
|
||||
day: "wednesday"
|
||||
open-pull-requests-limit: 1
|
||||
groups:
|
||||
docusaurus:
|
||||
patterns:
|
||||
@@ -36,3 +62,16 @@ updates:
|
||||
eslint:
|
||||
patterns:
|
||||
- "*eslint*"
|
||||
security-all:
|
||||
applies-to: "security-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
version-all:
|
||||
applies-to: "version-updates"
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
@@ -1,6 +1,6 @@
|
||||
**Short description of the problem this fixes or functionality that this introduces. This may be used for the CHANGELOG**
|
||||
|
||||
**End-user friendly description of the problem this fixes or functionality that this introduces**
|
||||
|
||||
- [ ] Include this change in the Release Notes. If checked, you must provide an **end-user friendly** description for your change below
|
||||
|
||||
---
|
||||
**Give a summary of what the PR does, explaining any non-trivial design decisions**
|
||||
|
||||
66
.github/scripts/check_version_consistency.py
vendored
Executable file
66
.github/scripts/check_version_consistency.py
vendored
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Set, Tuple
|
||||
|
||||
|
||||
def find_version_references(directory: str) -> Tuple[Set[str], Set[str]]:
|
||||
openhands_versions = set()
|
||||
runtime_versions = set()
|
||||
|
||||
version_pattern_openhands = re.compile(r'openhands:(\d{1})\.(\d{2})')
|
||||
version_pattern_runtime = re.compile(r'runtime:(\d{1})\.(\d{2})')
|
||||
|
||||
for root, _, files in os.walk(directory):
|
||||
# Skip .git directory
|
||||
if '.git' in root:
|
||||
continue
|
||||
|
||||
for file in files:
|
||||
if file.endswith(
|
||||
('.md', '.yml', '.yaml', '.txt', '.html', '.py', '.js', '.ts')
|
||||
):
|
||||
file_path = os.path.join(root, file)
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Find all openhands version references
|
||||
matches = version_pattern_openhands.findall(content)
|
||||
openhands_versions.update(matches)
|
||||
|
||||
# Find all runtime version references
|
||||
matches = version_pattern_runtime.findall(content)
|
||||
runtime_versions.update(matches)
|
||||
except Exception as e:
|
||||
print(f'Error reading {file_path}: {e}', file=sys.stderr)
|
||||
|
||||
return openhands_versions, runtime_versions
|
||||
|
||||
|
||||
def main():
|
||||
repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
openhands_versions, runtime_versions = find_version_references(repo_root)
|
||||
|
||||
exit_code = 0
|
||||
|
||||
if len(openhands_versions) > 1:
|
||||
print('Error: Multiple openhands versions found:', file=sys.stderr)
|
||||
print('Found versions:', sorted(openhands_versions), file=sys.stderr)
|
||||
exit_code = 1
|
||||
elif len(openhands_versions) == 0:
|
||||
print('Warning: No openhands version references found', file=sys.stderr)
|
||||
|
||||
if len(runtime_versions) > 1:
|
||||
print('Error: Multiple runtime versions found:', file=sys.stderr)
|
||||
print('Found versions:', sorted(runtime_versions), file=sys.stderr)
|
||||
exit_code = 1
|
||||
elif len(runtime_versions) == 0:
|
||||
print('Warning: No runtime version references found', file=sys.stderr)
|
||||
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
7
.github/workflows/deploy-docs.yml
vendored
7
.github/workflows/deploy-docs.yml
vendored
@@ -14,6 +14,11 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Build the documentation website
|
||||
build:
|
||||
@@ -32,7 +37,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.12'
|
||||
- name: Generate Python Docs
|
||||
run: rm -rf docs/modules/python && pip install pydoc-markdown && pydoc-markdown
|
||||
- name: Install dependencies
|
||||
|
||||
41
.github/workflows/dummy-agent-test.yml
vendored
41
.github/workflows/dummy-agent-test.yml
vendored
@@ -9,25 +9,50 @@ on:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Install tmux
|
||||
run: sudo apt-get update && sudo apt-get install -y tmux
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Set up environment
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
poetry install --without evaluation,llama-index
|
||||
poetry run playwright install --with-deps chromium
|
||||
wget https://huggingface.co/BAAI/bge-small-en-v1.5/raw/main/1_Pooling/config.json -P /tmp/llama_index/models--BAAI--bge-small-en-v1.5/snapshots/5c38ec7c405ec4b44b94cc5a9bb96e735b38267a/1_Pooling/
|
||||
python-version: '3.12'
|
||||
cache: 'poetry'
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation,llama-index
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Run tests
|
||||
run: |
|
||||
set -e
|
||||
poetry run python openhands/core/main.py -t "do a flip" -d ./workspace/ -c DummyAgent
|
||||
SANDBOX_FORCE_REBUILD_RUNTIME=True poetry run python3 openhands/core/main.py -t "do a flip" -d ./workspace/ -c DummyAgent
|
||||
- name: Check exit code
|
||||
run: |
|
||||
if [ $? -ne 0 ]; then
|
||||
|
||||
139
.github/workflows/eval-runner.yml
vendored
Normal file
139
.github/workflows/eval-runner.yml
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
name: Run SWE-Bench Evaluation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
reason:
|
||||
description: "Reason for manual trigger"
|
||||
required: true
|
||||
default: ""
|
||||
|
||||
env:
|
||||
N_PROCESSES: 32 # Global configuration for number of parallel processes for evaluation
|
||||
|
||||
jobs:
|
||||
run-evaluation:
|
||||
if: github.event.label.name == 'eval-this' || github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
pull-requests: "write"
|
||||
issues: "write"
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12"]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install tmux
|
||||
run: sudo apt-get update && sudo apt-get install -y tmux
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: "poetry"
|
||||
|
||||
- name: Comment on PR if 'eval-this' label is present
|
||||
if: github.event_name == 'pull_request' && github.event.label.name == 'eval-this'
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
unique: false
|
||||
comment: |
|
||||
Hi! I started running the evaluation on your PR. You will receive a comment with the results shortly.
|
||||
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install
|
||||
|
||||
- name: Configure config.toml for evaluation
|
||||
env:
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_LLM_API_KEY }}
|
||||
run: |
|
||||
echo "[llm.eval]" > config.toml
|
||||
echo "model = \"deepseek/deepseek-chat\"" >> config.toml
|
||||
echo "api_key = \"$DEEPSEEK_API_KEY\"" >> config.toml
|
||||
echo "temperature = 0.0" >> config.toml
|
||||
|
||||
- name: Run SWE-Bench evaluation
|
||||
env:
|
||||
ALLHANDS_API_KEY: ${{ secrets.ALLHANDS_EVAL_RUNTIME_API_KEY }}
|
||||
RUNTIME: remote
|
||||
SANDBOX_REMOTE_RUNTIME_API_URL: https://runtime.eval.all-hands.dev
|
||||
EVAL_DOCKER_IMAGE_PREFIX: us-central1-docker.pkg.dev/evaluation-092424/swe-bench-images
|
||||
|
||||
run: |
|
||||
poetry run ./evaluation/benchmarks/swe_bench/scripts/run_infer.sh llm.eval HEAD CodeActAgent 300 30 $N_PROCESSES "princeton-nlp/SWE-bench_Lite" test
|
||||
OUTPUT_FOLDER=$(find evaluation/evaluation_outputs/outputs/princeton-nlp__SWE-bench_Lite-test/CodeActAgent -name "deepseek-chat_maxiter_50_N_*-no-hint-run_1" -type d | head -n 1)
|
||||
echo "OUTPUT_FOLDER for SWE-bench evaluation: $OUTPUT_FOLDER"
|
||||
poetry run ./evaluation/benchmarks/swe_bench/scripts/eval_infer_remote.sh $OUTPUT_FOLDER/output.jsonl $N_PROCESSES "princeton-nlp/SWE-bench_Lite" test
|
||||
|
||||
poetry run ./evaluation/benchmarks/swe_bench/scripts/eval/summarize_outputs.py $OUTPUT_FOLDER/output.jsonl > summarize_outputs.log 2>&1
|
||||
echo "SWEBENCH_REPORT<<EOF" >> $GITHUB_ENV
|
||||
cat summarize_outputs.log >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: Create tar.gz of evaluation outputs
|
||||
run: |
|
||||
TIMESTAMP=$(date +'%y-%m-%d-%H-%M')
|
||||
tar -czvf evaluation_outputs_${TIMESTAMP}.tar.gz evaluation/evaluation_outputs/outputs
|
||||
|
||||
- name: Upload evaluation results as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload_results_artifact
|
||||
with:
|
||||
name: evaluation-outputs
|
||||
path: evaluation_outputs_*.tar.gz
|
||||
|
||||
- name: Get artifact URL
|
||||
run: echo "ARTIFACT_URL=${{ steps.upload_results_artifact.outputs.artifact-url }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
with:
|
||||
credentials_json: ${{ secrets.GCP_RESEARCH_OBJECT_CREATOR_SA_KEY }}
|
||||
|
||||
- name: Set timestamp and trigger reason
|
||||
run: |
|
||||
echo "TIMESTAMP=$(date +'%Y-%m-%d-%H-%M')" >> $GITHUB_ENV
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "TRIGGER_REASON=pr-${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
||||
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||
echo "TRIGGER_REASON=schedule" >> $GITHUB_ENV
|
||||
else
|
||||
echo "TRIGGER_REASON=manual-${{ github.event.inputs.reason }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Upload evaluation results to Google Cloud Storage
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
with:
|
||||
path: 'evaluation/evaluation_outputs/outputs'
|
||||
destination: 'openhands-oss-eval-results/${{ env.TIMESTAMP }}-${{ env.TRIGGER_REASON }}'
|
||||
|
||||
- name: Comment with evaluation results and artifact link
|
||||
id: create_comment
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
number: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || 4504 }}
|
||||
unique: false
|
||||
comment: |
|
||||
Trigger by: ${{ github.event_name == 'pull_request' && format('Pull Request (eval-this label on PR #{0})', github.event.pull_request.number) || github.event_name == 'schedule' && 'Daily Schedule' || format('Manual Trigger: {0}', github.event.inputs.reason) }}
|
||||
Commit: ${{ github.sha }}
|
||||
**SWE-Bench Evaluation Report**
|
||||
${{ env.SWEBENCH_REPORT }}
|
||||
---
|
||||
You can download the full evaluation outputs [here](${{ env.ARTIFACT_URL }}).
|
||||
|
||||
- name: Post to a Slack channel
|
||||
id: slack
|
||||
uses: slackapi/slack-github-action@v2.0.0
|
||||
with:
|
||||
channel-id: 'C07SVQSCR6F'
|
||||
slack-message: "*Evaluation Trigger:* ${{ github.event_name == 'pull_request' && format('Pull Request (eval-this label on PR #{0})', github.event.pull_request.number) || github.event_name == 'schedule' && 'Daily Schedule' || format('Manual Trigger: {0}', github.event.inputs.reason) }}\n\nLink to summary: [here](https://github.com/${{ github.repository }}/issues/${{ github.event_name == 'pull_request' && github.event.pull_request.number || 4504 }}#issuecomment-${{ steps.create_comment.outputs.comment-id }})"
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.EVAL_NOTIF_SLACK_BOT_TOKEN }}
|
||||
13
.github/workflows/fe-unit-tests.yml
vendored
13
.github/workflows/fe-unit-tests.yml
vendored
@@ -12,6 +12,11 @@ on:
|
||||
- 'frontend/**'
|
||||
- '.github/workflows/fe-unit-tests.yml'
|
||||
|
||||
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Run frontend unit tests
|
||||
fe-test:
|
||||
@@ -19,7 +24,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20]
|
||||
node-version: [20, 22]
|
||||
fail-fast: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -30,10 +36,13 @@ jobs:
|
||||
- name: Install dependencies
|
||||
working-directory: ./frontend
|
||||
run: npm ci
|
||||
- name: Run TypeScript compilation
|
||||
working-directory: ./frontend
|
||||
run: npm run make-i18n && tsc
|
||||
- name: Run tests and collect coverage
|
||||
working-directory: ./frontend
|
||||
run: npm run test:coverage
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
# Workflow that builds, tests and then pushes the runtime docker images to the ghcr.io repository
|
||||
name: Build, Test and Publish RT Image
|
||||
|
||||
# Only run one workflow of the same group at a time.
|
||||
# There can be at most one running and one pending job in a concurrency group at any time.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
# Workflow that builds, tests and then pushes the OpenHands and runtime docker images to the ghcr.io repository
|
||||
name: Docker
|
||||
|
||||
# Always run on "main"
|
||||
# Always run on tags
|
||||
@@ -25,7 +19,76 @@ on:
|
||||
required: true
|
||||
default: ''
|
||||
|
||||
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST: nikolaik/python-nodejs:python3.12-nodejs22
|
||||
RELEVANT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
jobs:
|
||||
# Builds the OpenHands Docker images
|
||||
ghcr_build_app:
|
||||
name: Build App Image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
hash_from_app_image: ${{ steps.get_hash_in_app_image.outputs.hash_from_app_image }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build and push app image
|
||||
if: "!github.event.pull_request.head.repo.fork"
|
||||
run: |
|
||||
./containers/build.sh -i openhands -o ${{ github.repository_owner }} --push
|
||||
- name: Build app image
|
||||
if: "github.event.pull_request.head.repo.fork"
|
||||
run: |
|
||||
./containers/build.sh -i openhands -o ${{ github.repository_owner }} --load
|
||||
- name: Get hash in App Image
|
||||
id: get_hash_in_app_image
|
||||
run: |
|
||||
# Lowercase the repository owner
|
||||
export REPO_OWNER=${{ github.repository_owner }}
|
||||
REPO_OWNER=$(echo $REPO_OWNER | tr '[:upper:]' '[:lower:]')
|
||||
# Run the build script in the app image
|
||||
docker run -e SANDBOX_USER_ID=0 -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/${REPO_OWNER}/openhands:${{ env.RELEVANT_SHA }} /bin/bash -c "mkdir -p containers/runtime; python3 openhands/runtime/utils/runtime_build.py --base_image ${{ env.BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST }} --build_folder containers/runtime --force_rebuild" 2>&1 | tee docker-outputs.txt
|
||||
# Get the hash from the build script
|
||||
hash_from_app_image=$(cat docker-outputs.txt | grep "Hash for docker build directory" | awk -F "): " '{print $2}' | uniq | head -n1)
|
||||
echo "hash_from_app_image=$hash_from_app_image" >> $GITHUB_OUTPUT
|
||||
echo "Hash from app image: $hash_from_app_image"
|
||||
|
||||
# Builds the runtime Docker images
|
||||
ghcr_build_runtime:
|
||||
name: Build Image
|
||||
@@ -36,7 +99,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
base_image:
|
||||
- image: 'nikolaik/python-nodejs:python3.11-nodejs22'
|
||||
- image: 'nikolaik/python-nodejs:python3.12-nodejs22'
|
||||
tag: nikolaik
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -56,7 +119,9 @@ jobs:
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -69,7 +134,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.12'
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -88,13 +153,13 @@ jobs:
|
||||
- name: Build and push runtime image ${{ matrix.base_image.image }}
|
||||
if: github.event.pull_request.head.repo.fork != true
|
||||
run: |
|
||||
./containers/build.sh runtime ${{ github.repository_owner }} --push ${{ matrix.base_image.tag }}
|
||||
./containers/build.sh -i runtime -o ${{ github.repository_owner }} --push -t ${{ matrix.base_image.tag }}
|
||||
# Forked repos can't push to GHCR, so we need to upload the image as an artifact
|
||||
- name: Build runtime image ${{ matrix.base_image.image }} for fork
|
||||
if: github.event.pull_request.head.repo.fork
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
tags: ghcr.io/all-hands-ai/runtime:${{ github.sha }}-${{ matrix.base_image.tag }}
|
||||
tags: ghcr.io/all-hands-ai/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image.tag }}
|
||||
outputs: type=docker,dest=/tmp/runtime-${{ matrix.base_image.tag }}.tar
|
||||
context: containers/runtime
|
||||
- name: Upload runtime image for fork
|
||||
@@ -104,6 +169,56 @@ jobs:
|
||||
name: runtime-${{ matrix.base_image.tag }}
|
||||
path: /tmp/runtime-${{ matrix.base_image.tag }}.tar
|
||||
|
||||
verify_hash_equivalence_in_runtime_and_app:
|
||||
name: Verify Hash Equivalence in Runtime and Docker images
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ghcr_build_runtime, ghcr_build_app]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
base_image: ['nikolaik']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pypoetry
|
||||
~/.virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: make install-python-dependencies
|
||||
- name: Get hash in App Image
|
||||
run: |
|
||||
echo "Hash from app image: ${{ needs.ghcr_build_app.outputs.hash_from_app_image }}"
|
||||
echo "hash_from_app_image=${{ needs.ghcr_build_app.outputs.hash_from_app_image }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Get hash using code (development mode)
|
||||
run: |
|
||||
mkdir -p containers/runtime
|
||||
poetry run python3 openhands/runtime/utils/runtime_build.py --base_image ${{ env.BASE_IMAGE_FOR_HASH_EQUIVALENCE_TEST }} --build_folder containers/runtime --force_rebuild > output.txt 2>&1
|
||||
hash_from_code=$(cat output.txt | grep "Hash for docker build directory" | awk -F "): " '{print $2}' | uniq | head -n1)
|
||||
echo "hash_from_code=$hash_from_code" >> $GITHUB_ENV
|
||||
|
||||
- name: Compare hashes
|
||||
run: |
|
||||
echo "Hash from App Image: ${{ env.hash_from_app_image }}"
|
||||
echo "Hash from Code: ${{ env.hash_from_code }}"
|
||||
if [ "${{ env.hash_from_app_image }}" = "${{ env.hash_from_code }}" ]; then
|
||||
echo "Hashes match!"
|
||||
else
|
||||
echo "Hashes do not match!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run unit tests with the EventStream runtime Docker images as root
|
||||
test_runtime_root:
|
||||
name: RT Unit Tests (Root)
|
||||
@@ -118,12 +233,20 @@ jobs:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
# Forked repos can't push to GHCR, so we need to download the image as an artifact
|
||||
- name: Download runtime image for fork
|
||||
if: github.event.pull_request.head.repo.fork
|
||||
@@ -147,32 +270,30 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.12'
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: make install-python-dependencies
|
||||
- name: Run runtime tests
|
||||
run: |
|
||||
# We install pytest-xdist in order to run tests across CPUs. However, tests start to fail when we run
|
||||
# then across more than 2 CPUs for some reason
|
||||
# We install pytest-xdist in order to run tests across CPUs
|
||||
poetry run pip install pytest-xdist
|
||||
|
||||
# Install to be able to retry on failures for flaky tests
|
||||
poetry run pip install pytest-rerunfailures
|
||||
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ github.sha }}-${{ matrix.base_image }}
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image }}
|
||||
image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
SKIP_CONTAINER_LOGS=true \
|
||||
TEST_RUNTIME=eventstream \
|
||||
SANDBOX_USER_ID=$(id -u) \
|
||||
SANDBOX_BASE_CONTAINER_IMAGE=$image_name \
|
||||
SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
|
||||
TEST_IN_CI=true \
|
||||
RUN_AS_OPENHANDS=false \
|
||||
poetry run pytest -n 3 --reruns 1 --reruns-delay 3 --cov=agenthub --cov=openhands --cov-report=xml -s ./tests/runtime
|
||||
poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -189,12 +310,20 @@ jobs:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
# Forked repos can't push to GHCR, so we need to download the image as an artifact
|
||||
- name: Download runtime image for fork
|
||||
if: github.event.pull_request.head.repo.fork
|
||||
@@ -218,87 +347,30 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
python-version: '3.12'
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: make install-python-dependencies
|
||||
- name: Run runtime tests
|
||||
run: |
|
||||
# We install pytest-xdist in order to run tests across CPUs. However, tests start to fail when we run
|
||||
# then across more than 2 CPUs for some reason
|
||||
# We install pytest-xdist in order to run tests across CPUs
|
||||
poetry run pip install pytest-xdist
|
||||
|
||||
# Install to be able to retry on failures for flaky tests
|
||||
poetry run pip install pytest-rerunfailures
|
||||
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ github.sha }}-${{ matrix.base_image }}
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ env.RELEVANT_SHA }}-${{ matrix.base_image }}
|
||||
image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
SKIP_CONTAINER_LOGS=true \
|
||||
TEST_RUNTIME=eventstream \
|
||||
SANDBOX_USER_ID=$(id -u) \
|
||||
SANDBOX_BASE_CONTAINER_IMAGE=$image_name \
|
||||
SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
|
||||
TEST_IN_CI=true \
|
||||
RUN_AS_OPENHANDS=true \
|
||||
poetry run pytest -n 3 --reruns 1 --reruns-delay 3 --cov=agenthub --cov=openhands --cov-report=xml -s ./tests/runtime
|
||||
poetry run pytest -n 3 -raRs --reruns 2 --reruns-delay 5 --cov=openhands --cov-report=xml -s ./tests/runtime --ignore=tests/runtime/test_browsergym_envs.py
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
# Run integration tests with the eventstream runtime Docker image
|
||||
runtime_integration_tests_on_linux:
|
||||
name: RT Integration Tests (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ghcr_build_runtime]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
base_image: ['nikolaik']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Forked repos can't push to GHCR, so we need to download the image as an artifact
|
||||
- name: Download runtime image for fork
|
||||
if: github.event.pull_request.head.repo.fork
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: runtime-${{ matrix.base_image }}
|
||||
path: /tmp
|
||||
- name: Load runtime image for fork
|
||||
if: github.event.pull_request.head.repo.fork
|
||||
run: |
|
||||
docker load --input /tmp/runtime-${{ matrix.base_image }}.tar
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pypoetry
|
||||
~/.virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: make install-python-dependencies
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ github.sha }}-${{ matrix.base_image }}
|
||||
image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
TEST_RUNTIME=eventstream \
|
||||
SANDBOX_USER_ID=$(id -u) \
|
||||
SANDBOX_BASE_CONTAINER_IMAGE=$image_name \
|
||||
TEST_IN_CI=true \
|
||||
TEST_ONLY=true \
|
||||
./tests/integration/regenerate.sh
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -310,7 +382,7 @@ jobs:
|
||||
name: All Runtime Tests Passed
|
||||
if: ${{ !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_runtime_root, test_runtime_oh, runtime_integration_tests_on_linux]
|
||||
needs: [test_runtime_root, test_runtime_oh, verify_hash_equivalence_in_runtime_and_app]
|
||||
steps:
|
||||
- name: All tests passed
|
||||
run: echo "All runtime tests have passed successfully!"
|
||||
@@ -319,9 +391,55 @@ jobs:
|
||||
name: All Runtime Tests Passed
|
||||
if: ${{ cancelled() || contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_runtime_root, test_runtime_oh, runtime_integration_tests_on_linux]
|
||||
needs: [test_runtime_root, test_runtime_oh, verify_hash_equivalence_in_runtime_and_app]
|
||||
steps:
|
||||
- name: Some tests failed
|
||||
run: |
|
||||
echo "Some runtime tests failed or were cancelled"
|
||||
exit 1
|
||||
update_pr_description:
|
||||
name: Update PR Description
|
||||
if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]'
|
||||
needs: [ghcr_build_runtime]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get short SHA
|
||||
id: short_sha
|
||||
run: echo "SHORT_SHA=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update PR Description
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
SHORT_SHA: ${{ steps.short_sha.outputs.SHORT_SHA }}
|
||||
run: |
|
||||
echo "updating PR description"
|
||||
DOCKER_RUN_COMMAND="docker run -it --rm \
|
||||
-p 3000:3000 \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:$SHORT_SHA-nikolaik \
|
||||
--name openhands-app-$SHORT_SHA \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:$SHORT_SHA"
|
||||
|
||||
PR_BODY=$(gh pr view $PR_NUMBER --json body --jq .body)
|
||||
|
||||
if echo "$PR_BODY" | grep -q "To run this PR locally, use the following command:"; then
|
||||
UPDATED_PR_BODY=$(echo "${PR_BODY}" | sed -E "s|docker run -it --rm.*|$DOCKER_RUN_COMMAND|")
|
||||
else
|
||||
UPDATED_PR_BODY="${PR_BODY}
|
||||
|
||||
---
|
||||
|
||||
To run this PR locally, use the following command:
|
||||
\`\`\`
|
||||
$DOCKER_RUN_COMMAND
|
||||
\`\`\`"
|
||||
fi
|
||||
|
||||
echo "updated body: $UPDATED_PR_BODY"
|
||||
gh pr edit $PR_NUMBER --body "$UPDATED_PR_BODY"
|
||||
65
.github/workflows/ghcr_app.yml
vendored
65
.github/workflows/ghcr_app.yml
vendored
@@ -1,65 +0,0 @@
|
||||
# Workflow that builds, tests and then pushes the app docker images to the ghcr.io repository
|
||||
name: Build and Publish App Image
|
||||
|
||||
# Always run on "main"
|
||||
# Always run on tags
|
||||
# Always run on PRs
|
||||
# Can also be triggered manually
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
reason:
|
||||
description: 'Reason for manual trigger'
|
||||
required: true
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
# Builds the OpenHands Docker images
|
||||
ghcr_build:
|
||||
name: Build App Image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed,
|
||||
# if set to "true" but frees about 6 GB
|
||||
tool-cache: true
|
||||
# all of these default to true, but feel free to set to
|
||||
# "false" if necessary for your workflow
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: false
|
||||
swap-storage: true
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build and push app image
|
||||
if: "!github.event.pull_request.head.repo.fork"
|
||||
run: |
|
||||
./containers/build.sh openhands ${{ github.repository_owner }} --push
|
||||
- name: Build app image
|
||||
if: "github.event.pull_request.head.repo.fork"
|
||||
run: |
|
||||
./containers/build.sh openhands image ${{ github.repository_owner }}
|
||||
262
.github/workflows/integration-runner.yml
vendored
Normal file
262
.github/workflows/integration-runner.yml
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
name: Run Integration Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
reason:
|
||||
description: 'Reason for manual trigger'
|
||||
required: true
|
||||
default: ''
|
||||
schedule:
|
||||
- cron: '30 22 * * *' # Runs at 10:30pm UTC every day
|
||||
|
||||
env:
|
||||
N_PROCESSES: 10 # Global configuration for number of parallel processes for evaluation
|
||||
|
||||
jobs:
|
||||
run-integration-tests:
|
||||
if: github.event.label.name == 'integration-test' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: "read"
|
||||
id-token: "write"
|
||||
pull-requests: "write"
|
||||
issues: "write"
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12"]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: "poetry"
|
||||
|
||||
- name: Comment on PR if 'integration-test' label is present
|
||||
if: github.event_name == 'pull_request' && github.event.label.name == 'integration-test'
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
unique: false
|
||||
comment: |
|
||||
Hi! I started running the integration tests on your PR. You will receive a comment with the results shortly.
|
||||
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation,llama-index
|
||||
|
||||
- name: Configure config.toml for testing with Haiku
|
||||
env:
|
||||
LLM_MODEL: "litellm_proxy/claude-3-5-haiku-20241022"
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
MAX_ITERATIONS: 10
|
||||
run: |
|
||||
echo "[llm.eval]" > config.toml
|
||||
echo "model = \"$LLM_MODEL\"" >> config.toml
|
||||
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
|
||||
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
|
||||
echo "temperature = 0.0" >> config.toml
|
||||
|
||||
- name: Build environment
|
||||
run: make build
|
||||
|
||||
- name: Run integration test evaluation for Haiku
|
||||
env:
|
||||
SANDBOX_FORCE_REBUILD_RUNTIME: True
|
||||
run: |
|
||||
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD CodeActAgent '' 10 $N_PROCESSES '' 'haiku_run'
|
||||
|
||||
# get integration tests report
|
||||
REPORT_FILE_HAIKU=$(find evaluation/evaluation_outputs/outputs/integration_tests/CodeActAgent/*haiku*_maxiter_10_N* -name "report.md" -type f | head -n 1)
|
||||
echo "REPORT_FILE: $REPORT_FILE_HAIKU"
|
||||
echo "INTEGRATION_TEST_REPORT_HAIKU<<EOF" >> $GITHUB_ENV
|
||||
cat $REPORT_FILE_HAIKU >> $GITHUB_ENV
|
||||
echo >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: Wait a little bit
|
||||
run: sleep 10
|
||||
|
||||
- name: Configure config.toml for testing with DeepSeek
|
||||
env:
|
||||
LLM_MODEL: "litellm_proxy/deepseek-chat"
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
MAX_ITERATIONS: 10
|
||||
run: |
|
||||
echo "[llm.eval]" > config.toml
|
||||
echo "model = \"$LLM_MODEL\"" >> config.toml
|
||||
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
|
||||
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
|
||||
echo "temperature = 0.0" >> config.toml
|
||||
|
||||
- name: Run integration test evaluation for DeepSeek
|
||||
env:
|
||||
SANDBOX_FORCE_REBUILD_RUNTIME: True
|
||||
run: |
|
||||
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD CodeActAgent '' 10 $N_PROCESSES '' 'deepseek_run'
|
||||
|
||||
# get integration tests report
|
||||
REPORT_FILE_DEEPSEEK=$(find evaluation/evaluation_outputs/outputs/integration_tests/CodeActAgent/deepseek*_maxiter_10_N* -name "report.md" -type f | head -n 1)
|
||||
echo "REPORT_FILE: $REPORT_FILE_DEEPSEEK"
|
||||
echo "INTEGRATION_TEST_REPORT_DEEPSEEK<<EOF" >> $GITHUB_ENV
|
||||
cat $REPORT_FILE_DEEPSEEK >> $GITHUB_ENV
|
||||
echo >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Run DelegatorAgent tests for Haiku, limited to t01 and t02
|
||||
- name: Wait a little bit (again)
|
||||
run: sleep 5
|
||||
|
||||
- name: Configure config.toml for testing DelegatorAgent (Haiku)
|
||||
env:
|
||||
LLM_MODEL: "litellm_proxy/claude-3-5-haiku-20241022"
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
MAX_ITERATIONS: 30
|
||||
run: |
|
||||
echo "[llm.eval]" > config.toml
|
||||
echo "model = \"$LLM_MODEL\"" >> config.toml
|
||||
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
|
||||
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
|
||||
echo "temperature = 0.0" >> config.toml
|
||||
|
||||
- name: Run integration test evaluation for DelegatorAgent (Haiku)
|
||||
env:
|
||||
SANDBOX_FORCE_REBUILD_RUNTIME: True
|
||||
run: |
|
||||
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD DelegatorAgent '' 30 $N_PROCESSES "t01_fix_simple_typo,t02_add_bash_hello" 'delegator_haiku_run'
|
||||
|
||||
# Find and export the delegator test results
|
||||
REPORT_FILE_DELEGATOR_HAIKU=$(find evaluation/evaluation_outputs/outputs/integration_tests/DelegatorAgent/*haiku*_maxiter_30_N* -name "report.md" -type f | head -n 1)
|
||||
echo "REPORT_FILE_DELEGATOR_HAIKU: $REPORT_FILE_DELEGATOR_HAIKU"
|
||||
echo "INTEGRATION_TEST_REPORT_DELEGATOR_HAIKU<<EOF" >> $GITHUB_ENV
|
||||
cat $REPORT_FILE_DELEGATOR_HAIKU >> $GITHUB_ENV
|
||||
echo >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Run DelegatorAgent tests for DeepSeek, limited to t01 and t02
|
||||
- name: Wait a little bit (again)
|
||||
run: sleep 5
|
||||
|
||||
- name: Configure config.toml for testing DelegatorAgent (DeepSeek)
|
||||
env:
|
||||
LLM_MODEL: "litellm_proxy/deepseek-chat"
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
MAX_ITERATIONS: 30
|
||||
run: |
|
||||
echo "[llm.eval]" > config.toml
|
||||
echo "model = \"$LLM_MODEL\"" >> config.toml
|
||||
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
|
||||
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
|
||||
echo "temperature = 0.0" >> config.toml
|
||||
- name: Run integration test evaluation for DelegatorAgent (DeepSeek)
|
||||
env:
|
||||
SANDBOX_FORCE_REBUILD_RUNTIME: True
|
||||
run: |
|
||||
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD DelegatorAgent '' 30 $N_PROCESSES "t01_fix_simple_typo,t02_add_bash_hello" 'delegator_deepseek_run'
|
||||
|
||||
# Find and export the delegator test results
|
||||
REPORT_FILE_DELEGATOR_DEEPSEEK=$(find evaluation/evaluation_outputs/outputs/integration_tests/DelegatorAgent/deepseek*_maxiter_30_N* -name "report.md" -type f | head -n 1)
|
||||
echo "REPORT_FILE_DELEGATOR_DEEPSEEK: $REPORT_FILE_DELEGATOR_DEEPSEEK"
|
||||
echo "INTEGRATION_TEST_REPORT_DELEGATOR_DEEPSEEK<<EOF" >> $GITHUB_ENV
|
||||
cat $REPORT_FILE_DELEGATOR_DEEPSEEK >> $GITHUB_ENV
|
||||
echo >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
# -------------------------------------------------------------
|
||||
# Run VisualBrowsingAgent tests for DeepSeek, limited to t05 and t06
|
||||
- name: Wait a little bit (again)
|
||||
run: sleep 5
|
||||
|
||||
- name: Configure config.toml for testing VisualBrowsingAgent (DeepSeek)
|
||||
env:
|
||||
LLM_MODEL: "litellm_proxy/deepseek-chat"
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
MAX_ITERATIONS: 15
|
||||
run: |
|
||||
echo "[llm.eval]" > config.toml
|
||||
echo "model = \"$LLM_MODEL\"" >> config.toml
|
||||
echo "api_key = \"$LLM_API_KEY\"" >> config.toml
|
||||
echo "base_url = \"$LLM_BASE_URL\"" >> config.toml
|
||||
echo "temperature = 0.0" >> config.toml
|
||||
- name: Run integration test evaluation for VisualBrowsingAgent (DeepSeek)
|
||||
env:
|
||||
SANDBOX_FORCE_REBUILD_RUNTIME: True
|
||||
run: |
|
||||
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD VisualBrowsingAgent '' 15 $N_PROCESSES "t05_simple_browsing,t06_github_pr_browsing.py" 'visualbrowsing_deepseek_run'
|
||||
|
||||
# Find and export the visual browsing agent test results
|
||||
REPORT_FILE_VISUALBROWSING_DEEPSEEK=$(find evaluation/evaluation_outputs/outputs/integration_tests/VisualBrowsingAgent/deepseek*_maxiter_15_N* -name "report.md" -type f | head -n 1)
|
||||
echo "REPORT_FILE_VISUALBROWSING_DEEPSEEK: $REPORT_FILE_VISUALBROWSING_DEEPSEEK"
|
||||
echo "INTEGRATION_TEST_REPORT_VISUALBROWSING_DEEPSEEK<<EOF" >> $GITHUB_ENV
|
||||
cat $REPORT_FILE_VISUALBROWSING_DEEPSEEK >> $GITHUB_ENV
|
||||
echo >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: Create archive of evaluation outputs
|
||||
run: |
|
||||
TIMESTAMP=$(date +'%y-%m-%d-%H-%M')
|
||||
cd evaluation/evaluation_outputs/outputs # Change to the outputs directory
|
||||
tar -czvf ../../../integration_tests_${TIMESTAMP}.tar.gz integration_tests/CodeActAgent/* integration_tests/DelegatorAgent/* integration_tests/VisualBrowsingAgent/* # Only include the actual result directories
|
||||
|
||||
- name: Upload evaluation results as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload_results_artifact
|
||||
with:
|
||||
name: integration-test-outputs-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
path: integration_tests_*.tar.gz
|
||||
|
||||
- name: Get artifact URLs
|
||||
run: |
|
||||
echo "ARTIFACT_URL=${{ steps.upload_results_artifact.outputs.artifact-url }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set timestamp and trigger reason
|
||||
run: |
|
||||
echo "TIMESTAMP=$(date +'%Y-%m-%d-%H-%M')" >> $GITHUB_ENV
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo "TRIGGER_REASON=pr-${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo "TRIGGER_REASON=manual-${{ github.event.inputs.reason }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "TRIGGER_REASON=nightly-scheduled" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Comment with results and artifact link
|
||||
id: create_comment
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
# if triggered by PR, use PR number, otherwise use 5318 as fallback issue number for manual triggers
|
||||
number: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || 5318 }}
|
||||
unique: false
|
||||
comment: |
|
||||
Trigger by: ${{ github.event_name == 'pull_request' && format('Pull Request (integration-test label on PR #{0})', github.event.pull_request.number) || (github.event_name == 'workflow_dispatch' && format('Manual Trigger: {0}', github.event.inputs.reason)) || 'Nightly Scheduled Run' }}
|
||||
Commit: ${{ github.sha }}
|
||||
**Integration Tests Report (Haiku)**
|
||||
Haiku LLM Test Results:
|
||||
${{ env.INTEGRATION_TEST_REPORT_HAIKU }}
|
||||
---
|
||||
**Integration Tests Report (DeepSeek)**
|
||||
DeepSeek LLM Test Results:
|
||||
${{ env.INTEGRATION_TEST_REPORT_DEEPSEEK }}
|
||||
---
|
||||
**Integration Tests Report Delegator (Haiku)**
|
||||
${{ env.INTEGRATION_TEST_REPORT_DELEGATOR_HAIKU }}
|
||||
---
|
||||
**Integration Tests Report Delegator (DeepSeek)**
|
||||
${{ env.INTEGRATION_TEST_REPORT_DELEGATOR_DEEPSEEK }}
|
||||
---
|
||||
**Integration Tests Report VisualBrowsing (DeepSeek)**
|
||||
${{ env.INTEGRATION_TEST_REPORT_VISUALBROWSING_DEEPSEEK }}
|
||||
---
|
||||
Download testing outputs (includes both Haiku and DeepSeek results): [Download](${{ steps.upload_results_artifact.outputs.artifact-url }})
|
||||
91
.github/workflows/lint-fix.yml
vendored
Normal file
91
.github/workflows/lint-fix.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: Lint Fix
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
# Frontend lint fixes
|
||||
lint-fix-frontend:
|
||||
if: github.event.label.name == 'lint-fix'
|
||||
name: Fix frontend linting issues
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
npm install --frozen-lockfile
|
||||
- name: Fix frontend lint issues
|
||||
run: |
|
||||
cd frontend
|
||||
npm run lint:fix
|
||||
|
||||
# Commit and push changes if any
|
||||
- name: Check for changes
|
||||
id: git-check
|
||||
run: |
|
||||
git diff --quiet || echo "changes=true" >> $GITHUB_OUTPUT
|
||||
- name: Commit and push if there are changes
|
||||
if: steps.git-check.outputs.changes == 'true'
|
||||
run: |
|
||||
git config --local user.email "openhands@all-hands.dev"
|
||||
git config --local user.name "OpenHands Bot"
|
||||
git add -A
|
||||
git commit -m "🤖 Auto-fix frontend linting issues"
|
||||
git push
|
||||
|
||||
# Python lint fixes
|
||||
lint-fix-python:
|
||||
if: github.event.label.name == 'lint-fix'
|
||||
name: Fix Python linting issues
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
cache: 'pip'
|
||||
- name: Install pre-commit
|
||||
run: pip install pre-commit==3.7.0
|
||||
- name: Fix python lint issues
|
||||
run: |
|
||||
# Run all pre-commit hooks and continue even if they modify files (exit code 1)
|
||||
pre-commit run --config ./dev_config/python/.pre-commit-config.yaml --files openhands/**/* evaluation/**/* tests/**/* || true
|
||||
|
||||
# Commit and push changes if any
|
||||
- name: Check for changes
|
||||
id: git-check
|
||||
run: |
|
||||
git diff --quiet || echo "changes=true" >> $GITHUB_OUTPUT
|
||||
- name: Commit and push if there are changes
|
||||
if: steps.git-check.outputs.changes == 'true'
|
||||
run: |
|
||||
git config --local user.email "openhands@all-hands.dev"
|
||||
git config --local user.name "OpenHands Bot"
|
||||
git add -A
|
||||
git commit -m "🤖 Auto-fix Python linting issues"
|
||||
git push
|
||||
25
.github/workflows/lint.yml
vendored
25
.github/workflows/lint.yml
vendored
@@ -10,6 +10,11 @@ on:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Run lint on the frontend code
|
||||
lint-frontend:
|
||||
@@ -25,10 +30,11 @@ jobs:
|
||||
run: |
|
||||
cd frontend
|
||||
npm install --frozen-lockfile
|
||||
- name: Lint
|
||||
- name: Lint and TypeScript compilation
|
||||
run: |
|
||||
cd frontend
|
||||
npm run lint
|
||||
npm run make-i18n && tsc
|
||||
|
||||
# Run lint on the python code
|
||||
lint-python:
|
||||
@@ -41,9 +47,22 @@ jobs:
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.12
|
||||
cache: 'pip'
|
||||
- name: Install pre-commit
|
||||
run: pip install pre-commit==3.7.0
|
||||
- name: Run pre-commit hooks
|
||||
run: pre-commit run --files openhands/**/* agenthub/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
|
||||
run: pre-commit run --files openhands/**/* evaluation/**/* tests/**/* --show-diff-on-failure --config ./dev_config/python/.pre-commit-config.yaml
|
||||
|
||||
# Check version consistency across documentation
|
||||
check-version-consistency:
|
||||
name: Check version consistency
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Run version consistency check
|
||||
run: .github/scripts/check_version_consistency.py
|
||||
|
||||
406
.github/workflows/openhands-resolver.yml
vendored
Normal file
406
.github/workflows/openhands-resolver.yml
vendored
Normal file
@@ -0,0 +1,406 @@
|
||||
name: Auto-Fix Tagged Issue with OpenHands
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
max_iterations:
|
||||
required: false
|
||||
type: number
|
||||
default: 50
|
||||
macro:
|
||||
required: false
|
||||
type: string
|
||||
default: "@openhands-agent"
|
||||
target_branch:
|
||||
required: false
|
||||
type: string
|
||||
default: "main"
|
||||
description: "Target branch to pull and create PR against"
|
||||
LLM_MODEL:
|
||||
required: false
|
||||
type: string
|
||||
default: "anthropic/claude-3-5-sonnet-20241022"
|
||||
base_container_image:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
description: "Custom sandbox env"
|
||||
secrets:
|
||||
LLM_MODEL:
|
||||
required: false
|
||||
LLM_API_KEY:
|
||||
required: true
|
||||
LLM_BASE_URL:
|
||||
required: false
|
||||
PAT_TOKEN:
|
||||
required: false
|
||||
PAT_USERNAME:
|
||||
required: false
|
||||
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
auto-fix:
|
||||
if: |
|
||||
github.event_name == 'workflow_call' ||
|
||||
github.event.label.name == 'fix-me' ||
|
||||
github.event.label.name == 'fix-me-experimental' ||
|
||||
(
|
||||
((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') &&
|
||||
contains(github.event.comment.body, inputs.macro || '@openhands-agent') &&
|
||||
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'MEMBER')
|
||||
) ||
|
||||
|
||||
(github.event_name == 'pull_request_review' &&
|
||||
contains(github.event.review.body, inputs.macro || '@openhands-agent') &&
|
||||
(github.event.review.author_association == 'OWNER' || github.event.review.author_association == 'COLLABORATOR' || github.event.review.author_association == 'MEMBER')
|
||||
)
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Get latest versions and create requirements.txt
|
||||
run: |
|
||||
python -m pip index versions openhands-ai > openhands_versions.txt
|
||||
OPENHANDS_VERSION=$(head -n 1 openhands_versions.txt | awk '{print $2}' | tr -d '()')
|
||||
# Ensure requirements.txt ends with newline before appending
|
||||
if [ -f requirements.txt ] && [ -s requirements.txt ]; then
|
||||
sed -i -e '$a\' requirements.txt
|
||||
fi
|
||||
echo "openhands-ai==${OPENHANDS_VERSION}" >> requirements.txt
|
||||
cat requirements.txt
|
||||
|
||||
- name: Cache pip dependencies
|
||||
if: |
|
||||
!(
|
||||
github.event.label.name == 'fix-me-experimental' ||
|
||||
(
|
||||
(github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') &&
|
||||
contains(github.event.comment.body, '@openhands-agent-exp')
|
||||
) ||
|
||||
(
|
||||
github.event_name == 'pull_request_review' &&
|
||||
contains(github.event.review.body, '@openhands-agent-exp')
|
||||
)
|
||||
)
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}/lib/python3.12/site-packages/*
|
||||
key: ${{ runner.os }}-pip-openhands-resolver-${{ hashFiles('requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-openhands-resolver-${{ hashFiles('requirements.txt') }}
|
||||
|
||||
- name: Check required environment variables
|
||||
env:
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || inputs.LLM_MODEL }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
|
||||
PAT_USERNAME: ${{ secrets.PAT_USERNAME }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
required_vars=("LLM_API_KEY")
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
echo "Error: Required environment variable $var is not set."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check optional variables and warn about fallbacks
|
||||
if [ -z "$LLM_BASE_URL" ]; then
|
||||
echo "Warning: LLM_BASE_URL is not set, will use default API endpoint"
|
||||
fi
|
||||
|
||||
if [ -z "$PAT_TOKEN" ]; then
|
||||
echo "Warning: PAT_TOKEN is not set, falling back to GITHUB_TOKEN"
|
||||
fi
|
||||
|
||||
if [ -z "$PAT_USERNAME" ]; then
|
||||
echo "Warning: PAT_USERNAME is not set, will use openhands-agent"
|
||||
fi
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
# Handle pull request events first
|
||||
if [ -n "${{ github.event.pull_request.number }}" ]; then
|
||||
echo "ISSUE_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
||||
echo "ISSUE_TYPE=pr" >> $GITHUB_ENV
|
||||
# Handle pull request review events
|
||||
elif [ -n "${{ github.event.review.body }}" ]; then
|
||||
echo "ISSUE_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
||||
echo "ISSUE_TYPE=pr" >> $GITHUB_ENV
|
||||
# Handle issue comment events that reference a PR
|
||||
elif [ -n "${{ github.event.issue.pull_request }}" ]; then
|
||||
echo "ISSUE_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV
|
||||
echo "ISSUE_TYPE=pr" >> $GITHUB_ENV
|
||||
# Handle regular issue events
|
||||
else
|
||||
echo "ISSUE_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV
|
||||
echo "ISSUE_TYPE=issue" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
if [ -n "${{ github.event.review.body }}" ]; then
|
||||
echo "COMMENT_ID=${{ github.event.review.id || 'None' }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "COMMENT_ID=${{ github.event.comment.id || 'None' }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
echo "MAX_ITERATIONS=${{ inputs.max_iterations || 50 }}" >> $GITHUB_ENV
|
||||
echo "SANDBOX_ENV_GITHUB_TOKEN=${{ secrets.PAT_TOKEN || github.token }}" >> $GITHUB_ENV
|
||||
echo "SANDBOX_ENV_BASE_CONTAINER_IMAGE=${{ inputs.base_container_image }}" >> $GITHUB_ENV
|
||||
|
||||
# Set branch variables
|
||||
echo "TARGET_BRANCH=${{ inputs.target_branch }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Comment on issue with start message
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.PAT_TOKEN || github.token }}
|
||||
script: |
|
||||
const issueType = process.env.ISSUE_TYPE;
|
||||
github.rest.issues.createComment({
|
||||
issue_number: ${{ env.ISSUE_NUMBER }},
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `[OpenHands](https://github.com/All-Hands-AI/OpenHands) started fixing the ${issueType}! You can monitor the progress [here](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).`
|
||||
});
|
||||
|
||||
- name: Install OpenHands
|
||||
id: install_openhands
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
COMMENT_BODY: ${{ github.event.comment.body || '' }}
|
||||
REVIEW_BODY: ${{ github.event.review.body || '' }}
|
||||
LABEL_NAME: ${{ github.event.label.name || '' }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
with:
|
||||
script: |
|
||||
const commentBody = process.env.COMMENT_BODY.trim();
|
||||
const reviewBody = process.env.REVIEW_BODY.trim();
|
||||
const labelName = process.env.LABEL_NAME.trim();
|
||||
const eventName = process.env.EVENT_NAME.trim();
|
||||
// Check conditions
|
||||
const isExperimentalLabel = labelName === "fix-me-experimental";
|
||||
const isIssueCommentExperimental =
|
||||
(eventName === "issue_comment" || eventName === "pull_request_review_comment") &&
|
||||
commentBody.includes("@openhands-agent-exp");
|
||||
const isReviewCommentExperimental =
|
||||
eventName === "pull_request_review" && reviewBody.includes("@openhands-agent-exp");
|
||||
|
||||
// Set output variable
|
||||
core.setOutput('isExperimental', isExperimentalLabel || isIssueCommentExperimental || isReviewCommentExperimental);
|
||||
|
||||
// Perform package installation
|
||||
if (isExperimentalLabel || isIssueCommentExperimental || isReviewCommentExperimental) {
|
||||
console.log("Installing experimental OpenHands...");
|
||||
await exec.exec("python -m pip install --upgrade pip");
|
||||
await exec.exec("pip install git+https://github.com/all-hands-ai/openhands.git");
|
||||
} else {
|
||||
console.log("Installing from requirements.txt...");
|
||||
await exec.exec("python -m pip install --upgrade pip");
|
||||
await exec.exec("pip install -r requirements.txt");
|
||||
}
|
||||
|
||||
- name: Attempt to resolve issue
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || github.token }}
|
||||
GITHUB_USERNAME: ${{ secrets.PAT_USERNAME || 'openhands-agent' }}
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || inputs.LLM_MODEL }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
PYTHONPATH: ""
|
||||
run: |
|
||||
cd /tmp && python -m openhands.resolver.resolve_issue \
|
||||
--repo ${{ github.repository }} \
|
||||
--issue-number ${{ env.ISSUE_NUMBER }} \
|
||||
--issue-type ${{ env.ISSUE_TYPE }} \
|
||||
--max-iterations ${{ env.MAX_ITERATIONS }} \
|
||||
--comment-id ${{ env.COMMENT_ID }} \
|
||||
--is-experimental ${{ steps.install_openhands.outputs.isExperimental }}
|
||||
|
||||
- name: Check resolution result
|
||||
id: check_result
|
||||
run: |
|
||||
if cd /tmp && grep -q '"success":true' output/output.jsonl; then
|
||||
echo "RESOLUTION_SUCCESS=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "RESOLUTION_SUCCESS=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Upload output.jsonl as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always() # Upload even if the previous steps fail
|
||||
with:
|
||||
name: resolver-output
|
||||
path: /tmp/output/output.jsonl
|
||||
retention-days: 30 # Keep the artifact for 30 days
|
||||
|
||||
- name: Create draft PR or push branch
|
||||
if: always() # Create PR or branch even if the previous steps fail
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || github.token }}
|
||||
GITHUB_USERNAME: ${{ secrets.PAT_USERNAME || 'openhands-agent' }}
|
||||
LLM_MODEL: ${{ secrets.LLM_MODEL || inputs.LLM_MODEL }}
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
|
||||
PYTHONPATH: ""
|
||||
run: |
|
||||
if [ "${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}" == "true" ]; then
|
||||
cd /tmp && python -m openhands.resolver.send_pull_request \
|
||||
--issue-number ${{ env.ISSUE_NUMBER }} \
|
||||
--pr-type draft \
|
||||
--reviewer ${{ github.actor }} | tee pr_result.txt && \
|
||||
grep "draft created" pr_result.txt | sed 's/.*\///g' > pr_number.txt
|
||||
else
|
||||
cd /tmp && python -m openhands.resolver.send_pull_request \
|
||||
--issue-number ${{ env.ISSUE_NUMBER }} \
|
||||
--pr-type branch \
|
||||
--send-on-failure | tee branch_result.txt && \
|
||||
grep "branch created" branch_result.txt | sed 's/.*\///g; s/.expand=1//g' > branch_name.txt
|
||||
fi
|
||||
|
||||
# Step leaves comment for when agent is invoked on PR
|
||||
- name: Analyze Push Logs (Updated PR or No Changes) # Skip comment if PR update was successful OR leave comment if the agent made no code changes
|
||||
uses: actions/github-script@v7
|
||||
if: always()
|
||||
env:
|
||||
AGENT_RESPONDED: ${{ env.AGENT_RESPONDED || 'false' }}
|
||||
with:
|
||||
github-token: ${{ secrets.PAT_TOKEN || github.token }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const issueNumber = ${{ env.ISSUE_NUMBER }};
|
||||
let logContent = '';
|
||||
|
||||
try {
|
||||
logContent = fs.readFileSync('/tmp/pr_result.txt', 'utf8').trim();
|
||||
} catch (error) {
|
||||
console.error('Error reading pr_result.txt file:', error);
|
||||
}
|
||||
|
||||
const noChangesMessage = `No changes to commit for issue #${issueNumber}. Skipping commit.`;
|
||||
|
||||
// Check logs from send_pull_request.py (pushes code to GitHub)
|
||||
if (logContent.includes("Updated pull request")) {
|
||||
console.log("Updated pull request found. Skipping comment.");
|
||||
process.env.AGENT_RESPONDED = 'true';
|
||||
} else if (logContent.includes(noChangesMessage)) {
|
||||
github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `The workflow to fix this issue encountered an error. Openhands failed to create any code changes.`
|
||||
});
|
||||
process.env.AGENT_RESPONDED = 'true';
|
||||
}
|
||||
|
||||
# Step leaves comment for when agent is invoked on issue
|
||||
- name: Comment on issue # Comment link to either PR or branch created by agent
|
||||
uses: actions/github-script@v7
|
||||
if: always() # Comment on issue even if the previous steps fail
|
||||
env:
|
||||
AGENT_RESPONDED: ${{ env.AGENT_RESPONDED || 'false' }}
|
||||
with:
|
||||
github-token: ${{ secrets.PAT_TOKEN || github.token }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const issueNumber = ${{ env.ISSUE_NUMBER }};
|
||||
const success = ${{ steps.check_result.outputs.RESOLUTION_SUCCESS }};
|
||||
|
||||
let prNumber = '';
|
||||
let branchName = '';
|
||||
let resultExplanation = '';
|
||||
|
||||
try {
|
||||
if (success) {
|
||||
prNumber = fs.readFileSync('/tmp/pr_number.txt', 'utf8').trim();
|
||||
} else {
|
||||
branchName = fs.readFileSync('/tmp/branch_name.txt', 'utf8').trim();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error reading file:', error);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (!success){
|
||||
// Read result_explanation from JSON file for failed resolution
|
||||
const outputFilePath = path.resolve('/tmp/output/output.jsonl');
|
||||
if (fs.existsSync(outputFilePath)) {
|
||||
const outputContent = fs.readFileSync(outputFilePath, 'utf8');
|
||||
const jsonLines = outputContent.split('\n').filter(line => line.trim() !== '');
|
||||
|
||||
if (jsonLines.length > 0) {
|
||||
// First entry in JSON lines has the key 'result_explanation'
|
||||
const firstEntry = JSON.parse(jsonLines[0]);
|
||||
resultExplanation = firstEntry.result_explanation || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error){
|
||||
console.error('Error reading file:', error);
|
||||
}
|
||||
|
||||
// Check "success" log from resolver output
|
||||
if (success && prNumber) {
|
||||
github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `A potential fix has been generated and a draft PR #${prNumber} has been created. Please review the changes.`
|
||||
});
|
||||
process.env.AGENT_RESPONDED = 'true';
|
||||
} else if (!success && branchName) {
|
||||
let commentBody = `An attempt was made to automatically fix this issue, but it was unsuccessful. A branch named '${branchName}' has been created with the attempted changes. You can view the branch [here](https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${branchName}). Manual intervention may be required.`;
|
||||
|
||||
if (resultExplanation) {
|
||||
commentBody += `\n\nAdditional details about the failure:\n${resultExplanation}`;
|
||||
}
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: commentBody
|
||||
});
|
||||
process.env.AGENT_RESPONDED = 'true';
|
||||
}
|
||||
|
||||
# Leave error comment when both PR/Issue comment handling fail
|
||||
- name: Fallback Error Comment
|
||||
uses: actions/github-script@v7
|
||||
if: ${{ env.AGENT_RESPONDED == 'false' }} # Only run if no conditions were met in previous steps
|
||||
with:
|
||||
github-token: ${{ secrets.PAT_TOKEN || github.token }}
|
||||
script: |
|
||||
const issueNumber = ${{ env.ISSUE_NUMBER }};
|
||||
|
||||
github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `The workflow to fix this issue encountered an error. Please check the [workflow logs](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) for more information.`
|
||||
});
|
||||
98
.github/workflows/py-unit-tests-mac.yml
vendored
Normal file
98
.github/workflows/py-unit-tests-mac.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
# Workflow that runs python unit tests on mac
|
||||
name: Run Python Unit Tests Mac
|
||||
|
||||
# This job is flaky so only run it nightly
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
# Run python unit tests on macOS
|
||||
test-on-macos:
|
||||
name: Python Unit Tests on macOS
|
||||
runs-on: macos-14
|
||||
env:
|
||||
INSTALL_DOCKER: '1' # Set to '0' to skip Docker installation
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.12']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pypoetry
|
||||
~/.virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
- name: Install tmux
|
||||
run: brew install tmux
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation,llama-index
|
||||
- name: Install & Start Docker
|
||||
if: env.INSTALL_DOCKER == '1'
|
||||
run: |
|
||||
INSTANCE_NAME="colima-${GITHUB_RUN_ID}"
|
||||
|
||||
# Uninstall colima to upgrade to the latest version
|
||||
if brew list colima &>/dev/null; then
|
||||
brew uninstall colima
|
||||
# unlinking colima dependency: go
|
||||
brew uninstall go@1.21
|
||||
fi
|
||||
rm -rf ~/.colima ~/.lima
|
||||
brew install --HEAD colima
|
||||
brew install docker
|
||||
|
||||
start_colima() {
|
||||
# Find a free port in the range 10000-20000
|
||||
RANDOM_PORT=$((RANDOM % 10001 + 10000))
|
||||
|
||||
# Original line:
|
||||
if ! colima start --network-address --arch x86_64 --cpu=1 --memory=1 --verbose --ssh-port $RANDOM_PORT; then
|
||||
echo "Failed to start Colima."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Attempt to start Colima for 5 total attempts:
|
||||
ATTEMPT_LIMIT=5
|
||||
for ((i=1; i<=ATTEMPT_LIMIT; i++)); do
|
||||
|
||||
if start_colima; then
|
||||
echo "Colima started successfully."
|
||||
break
|
||||
else
|
||||
colima stop -f
|
||||
sleep 10
|
||||
colima delete -f
|
||||
if [ $i -eq $ATTEMPT_LIMIT ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
# For testcontainers to find the Colima socket
|
||||
# https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running
|
||||
sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Run Tests
|
||||
run: poetry run pytest --forked --cov=openhands --cov-report=xml ./tests/unit --ignore=tests/unit/test_memory.py
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
101
.github/workflows/py-unit-tests.yml
vendored
101
.github/workflows/py-unit-tests.yml
vendored
@@ -10,92 +10,12 @@ on:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
# If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Run python unit tests on macOS
|
||||
test-on-macos:
|
||||
name: Python Unit Tests on macOS
|
||||
runs-on: macos-12
|
||||
env:
|
||||
INSTALL_DOCKER: '1' # Set to '0' to skip Docker installation
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.11']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pypoetry
|
||||
~/.virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: poetry install --without evaluation,llama-index
|
||||
- name: Install & Start Docker
|
||||
if: env.INSTALL_DOCKER == '1'
|
||||
run: |
|
||||
INSTANCE_NAME="colima-${GITHUB_RUN_ID}"
|
||||
|
||||
# Uninstall colima to upgrade to the latest version
|
||||
if brew list colima &>/dev/null; then
|
||||
brew uninstall colima
|
||||
# unlinking colima dependency: go
|
||||
brew uninstall go@1.21
|
||||
fi
|
||||
rm -rf ~/.colima ~/.lima
|
||||
brew install --HEAD colima
|
||||
brew install docker
|
||||
|
||||
start_colima() {
|
||||
# Find a free port in the range 10000-20000
|
||||
RANDOM_PORT=$((RANDOM % 10001 + 10000))
|
||||
|
||||
# Original line:
|
||||
if ! colima start --network-address --arch x86_64 --cpu=1 --memory=1 --verbose --ssh-port $RANDOM_PORT; then
|
||||
echo "Failed to start Colima."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Attempt to start Colima for 5 total attempts:
|
||||
ATTEMPT_LIMIT=5
|
||||
for ((i=1; i<=ATTEMPT_LIMIT; i++)); do
|
||||
|
||||
if start_colima; then
|
||||
echo "Colima started successfully."
|
||||
break
|
||||
else
|
||||
colima stop -f
|
||||
sleep 10
|
||||
colima delete -f
|
||||
if [ $i -eq $ATTEMPT_LIMIT ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
# For testcontainers to find the Colima socket
|
||||
# https://github.com/abiosoft/colima/blob/main/docs/FAQ.md#cannot-connect-to-the-docker-daemon-at-unixvarrundockersock-is-the-docker-daemon-running
|
||||
sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Run Tests
|
||||
run: poetry run pytest --forked --cov=agenthub --cov=openhands --cov-report=xml ./tests/unit
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
# Run python unit tests on Linux
|
||||
test-on-linux:
|
||||
name: Python Unit Tests on Linux
|
||||
@@ -104,9 +24,14 @@ jobs:
|
||||
INSTALL_DOCKER: '0' # Set to '0' to skip Docker installation
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.11']
|
||||
python-version: ['3.12']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Install tmux
|
||||
run: sudo apt-get update && sudo apt-get install -y tmux
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Set up Python
|
||||
@@ -119,8 +44,8 @@ jobs:
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Run Tests
|
||||
run: poetry run pytest --forked --cov=agenthub --cov=openhands --cov-report=xml -svv ./tests/unit
|
||||
run: poetry run pytest --forked -n auto --cov=openhands --cov-report=xml -svv ./tests/unit --ignore=tests/unit/test_memory.py
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
4
.github/workflows/pypi-release.yml
vendored
4
.github/workflows/pypi-release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
python-version: 3.12
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1.4.1
|
||||
with:
|
||||
@@ -26,6 +26,6 @@ jobs:
|
||||
- name: Install Poetry Dependencies
|
||||
run: poetry install --no-interaction --no-root
|
||||
- name: Build poetry project
|
||||
run: poetry build -v
|
||||
run: ./build.sh
|
||||
- name: publish
|
||||
run: poetry publish -u __token__ -p ${{ secrets.PYPI_TOKEN }}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
name: Regenerate Integration Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
debug:
|
||||
description: 'Enable debug mode'
|
||||
type: boolean
|
||||
default: true
|
||||
log_to_file:
|
||||
description: 'Enable logging to file'
|
||||
type: boolean
|
||||
default: true
|
||||
force_regenerate_tests:
|
||||
description: 'Force regeneration of tests'
|
||||
type: boolean
|
||||
default: false
|
||||
force_use_llm:
|
||||
description: 'Force use of LLM'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
regenerate_integration_tests:
|
||||
if: github.ref != 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- name: Cache Poetry dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pypoetry
|
||||
~/.virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
- name: Install poetry via pipx
|
||||
run: pipx install poetry
|
||||
- name: Install Python dependencies using Poetry
|
||||
run: make install-python-dependencies
|
||||
- name: Build Environment
|
||||
run: make build
|
||||
- name: Regenerate integration tests
|
||||
run: |
|
||||
DEBUG=${{ inputs.debug }} \
|
||||
LOG_TO_FILE=${{ inputs.log_to_file }} \
|
||||
FORCE_REGENERATE_TESTS=${{ inputs.force_regenerate_tests }} \
|
||||
FORCE_USE_LLM=${{ inputs.force_use_llm }} \
|
||||
./tests/integration/regenerate.sh
|
||||
- name: Commit changes
|
||||
run: |
|
||||
if git diff --quiet --exit-code; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git add .
|
||||
# run it twice in case pre-commit makes changes
|
||||
git commit -am "Regenerate integration tests" || git commit -am "Regenerate integration tests"
|
||||
git push
|
||||
78
.github/workflows/review-pr.yml
vendored
78
.github/workflows/review-pr.yml
vendored
@@ -1,78 +0,0 @@
|
||||
# Workflow that uses OpenHands to review a pull request. PR must be labeled 'review-this'
|
||||
name: Use OpenHands to Review Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [synchronize, labeled]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dogfood:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'review-this')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: install git, github cli
|
||||
run: |
|
||||
sudo apt-get install -y git gh
|
||||
git config --global --add safe.directory $PWD
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.ref }} # check out the target branch
|
||||
- name: Download Diff
|
||||
run: |
|
||||
curl -O "${{ github.event.pull_request.diff_url }}" -L
|
||||
- name: Write Task File
|
||||
run: |
|
||||
echo "Your coworker wants to apply a pull request to this project." > task.txt
|
||||
echo "Read and review ${{ github.event.pull_request.number }}.diff file. Create a review-${{ github.event.pull_request.number }}.txt and write your concise comments and suggestions there." >> task.txt
|
||||
echo "Do not ask me for confirmation at any point." >> task.txt
|
||||
echo "" >> task.txt
|
||||
echo "Title" >> task.txt
|
||||
echo "${{ github.event.pull_request.title }}" >> task.txt
|
||||
echo "" >> task.txt
|
||||
echo "Description" >> task.txt
|
||||
echo "${{ github.event.pull_request.body }}" >> task.txt
|
||||
echo "" >> task.txt
|
||||
echo "Diff file is: ${{ github.event.pull_request.number }}.diff" >> task.txt
|
||||
- name: Set up environment
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
export PATH="/github/home/.local/bin:$PATH"
|
||||
poetry install --without evaluation,llama-index
|
||||
poetry run playwright install --with-deps chromium
|
||||
- name: Run OpenHands
|
||||
env:
|
||||
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
|
||||
LLM_MODEL: ${{ vars.LLM_MODEL }}
|
||||
run: |
|
||||
# Append path to launch poetry
|
||||
export PATH="/github/home/.local/bin:$PATH"
|
||||
# Append path to correctly import package, note: must set pwd at first
|
||||
export PYTHONPATH=$(pwd):$PYTHONPATH
|
||||
export WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE
|
||||
export WORKSPACE_BASE=$GITHUB_WORKSPACE
|
||||
echo -e "/exit\n" | poetry run python openhands/core/main.py -i 50 -f task.txt
|
||||
rm task.txt
|
||||
- name: Check if review file is non-empty
|
||||
id: check_file
|
||||
run: |
|
||||
ls -la
|
||||
if [[ -s review-${{ github.event.pull_request.number }}.txt ]]; then
|
||||
echo "non_empty=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
shell: bash
|
||||
- name: Create PR review if file is non-empty
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
if: steps.check_file.outputs.non_empty == 'true'
|
||||
run: |
|
||||
gh pr review ${{ github.event.pull_request.number }} --comment --body-file "review-${{ github.event.pull_request.number }}.txt"
|
||||
53
.github/workflows/run-eval.yml
vendored
Normal file
53
.github/workflows/run-eval.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Run evaluation on a PR
|
||||
name: Run Eval
|
||||
|
||||
# Runs when a PR is labeled with one of the "run-eval-" labels
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
trigger-job:
|
||||
name: Trigger remote eval job
|
||||
if: ${{ github.event.label.name == 'run-eval-xs' || github.event.label.name == 'run-eval-s' || github.event.label.name == 'run-eval-m' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout PR branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Trigger remote job
|
||||
run: |
|
||||
REPO_URL="https://github.com/${{ github.repository }}"
|
||||
PR_BRANCH="${{ github.head_ref }}"
|
||||
echo "Repository URL: $REPO_URL"
|
||||
echo "PR Branch: $PR_BRANCH"
|
||||
|
||||
if [[ "${{ github.event.label.name }}" == "run-eval-xs" ]]; then
|
||||
EVAL_INSTANCES="1"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-s" ]]; then
|
||||
EVAL_INSTANCES="5"
|
||||
elif [[ "${{ github.event.label.name }}" == "run-eval-m" ]]; then
|
||||
EVAL_INSTANCES="30"
|
||||
fi
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{ secrets.PAT_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-d "{\"ref\": \"main\", \"inputs\": {\"github-repo\": \"${REPO_URL}\", \"github-branch\": \"${PR_BRANCH}\", \"pr-number\": \"${{ github.event.pull_request.number }}\", \"eval-instances\": \"${EVAL_INSTANCES}\"}}" \
|
||||
https://api.github.com/repos/All-Hands-AI/evaluation/actions/workflows/create-branch.yml/dispatches
|
||||
|
||||
# Send Slack message
|
||||
PR_URL="https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
|
||||
slack_text="PR $PR_URL has triggered evaluation on $EVAL_INSTANCES instances..."
|
||||
curl -X POST -H 'Content-type: application/json' --data '{"text":"'"$slack_text"'"}' \
|
||||
https://hooks.slack.com/services/${{ secrets.SLACK_TOKEN }}
|
||||
|
||||
- name: Comment on PR
|
||||
uses: KeisukeYamashita/create-comment@v1
|
||||
with:
|
||||
unique: false
|
||||
comment: |
|
||||
Running evaluation on the PR. Once eval is done, the results will be posted.
|
||||
113
.github/workflows/solve-issue.yml
vendored
113
.github/workflows/solve-issue.yml
vendored
@@ -1,113 +0,0 @@
|
||||
# Workflow that uses OpenHands to resolve a GitHub issue. Issue must be labeled 'solve-this'
|
||||
name: Use OpenHands to Resolve GitHub Issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
dogfood:
|
||||
if: github.event.label.name == 'solve-this'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/all-hands-ai/openhands
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
steps:
|
||||
- name: install git, github cli
|
||||
run: apt-get install -y git gh
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Write Task File
|
||||
env:
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
run: |
|
||||
echo "TITLE:" > task.txt
|
||||
echo "${ISSUE_TITLE}" >> task.txt
|
||||
echo "" >> task.txt
|
||||
echo "BODY:" >> task.txt
|
||||
echo "${ISSUE_BODY}" >> task.txt
|
||||
- name: Set up environment
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
export PATH="/github/home/.local/bin:$PATH"
|
||||
poetry install --without evaluation,llama-index
|
||||
poetry run playwright install --with-deps chromium
|
||||
- name: Run OpenHands
|
||||
env:
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
run: |
|
||||
# Append path to launch poetry
|
||||
export PATH="/github/home/.local/bin:$PATH"
|
||||
# Append path to correctly import package, note: must set pwd at first
|
||||
export PYTHONPATH=$(pwd):$PYTHONPATH
|
||||
WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE poetry run python ./openhands/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE
|
||||
rm task.txt
|
||||
- name: Setup Git, Create Branch, and Commit Changes
|
||||
run: |
|
||||
# Setup Git configuration
|
||||
git config --global --add safe.directory $PWD
|
||||
git config --global user.name 'OpenHands'
|
||||
git config --global user.email 'OpenHands@users.noreply.github.com'
|
||||
|
||||
# Create a unique branch name with a timestamp
|
||||
BRANCH_NAME="fix/${{ github.event.issue.number }}-$(date +%Y%m%d%H%M%S)"
|
||||
|
||||
# Checkout new branch
|
||||
git checkout -b $BRANCH_NAME
|
||||
|
||||
# Add all changes to staging, except task.txt
|
||||
git add --all -- ':!task.txt'
|
||||
|
||||
# Commit the changes, if any
|
||||
git commit -m "OpenHands: Resolve Issue #${{ github.event.issue.number }}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "No changes to commit."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Push changes
|
||||
git push --set-upstream origin $BRANCH_NAME
|
||||
- name: Fetch Default Branch
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Fetch the default branch using gh cli
|
||||
DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
|
||||
echo "Default branch is $DEFAULT_BRANCH"
|
||||
echo "DEFAULT_BRANCH=$DEFAULT_BRANCH" >> $GITHUB_ENV
|
||||
- name: Generate PR
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Create PR and capture URL
|
||||
PR_URL=$(gh pr create \
|
||||
--title "OpenHands: Resolve Issue #2" \
|
||||
--body "This PR was generated by OpenHands to resolve issue #2" \
|
||||
--repo "foragerr/OpenHands" \
|
||||
--head "${{ github.head_ref }}" \
|
||||
--base "${{ env.DEFAULT_BRANCH }}" \
|
||||
| grep -o 'https://github.com/[^ ]*')
|
||||
|
||||
# Extract PR number from URL
|
||||
PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]\+$')
|
||||
|
||||
# Set environment vars
|
||||
echo "PR_URL=$PR_URL" >> $GITHUB_ENV
|
||||
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
|
||||
|
||||
- name: Post Comment
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh issue comment ${{ github.event.issue.number }} \
|
||||
-b "OpenHands raised [PR #${{ env.PR_NUMBER }}](${{ env.PR_URL }}) to resolve this issue."
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -121,6 +121,7 @@ celerybeat.pid
|
||||
|
||||
# Environments
|
||||
.env
|
||||
frontend/.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
@@ -173,11 +174,16 @@ evaluation/bird/data
|
||||
evaluation/gaia/data
|
||||
evaluation/gorilla/data
|
||||
evaluation/toolqa/data
|
||||
evaluation/scienceagentbench/benchmark
|
||||
evaluation/commit0_bench/repos
|
||||
evaluation/visualcodebench/
|
||||
|
||||
# openhands resolver
|
||||
output/
|
||||
|
||||
# frontend
|
||||
|
||||
# dependencies
|
||||
frontend/node_modules
|
||||
frontend/.pnp
|
||||
frontend/bun.lockb
|
||||
frontend/yarn.lock
|
||||
@@ -217,8 +223,6 @@ config.toml
|
||||
config.toml_
|
||||
config.toml.bak
|
||||
|
||||
containers/agnostic_sandbox
|
||||
|
||||
# swe-bench-eval
|
||||
image_build_logs
|
||||
run_instance_logs
|
||||
@@ -228,3 +232,5 @@ runtime_*.tar
|
||||
# docker build
|
||||
containers/runtime/Dockerfile
|
||||
containers/runtime/project.tar.gz
|
||||
containers/runtime/code
|
||||
**/node_modules/
|
||||
|
||||
42
.openhands/microagents/repo.md
Normal file
42
.openhands/microagents/repo.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: repo
|
||||
type: repo
|
||||
agent: CodeActAgent
|
||||
---
|
||||
This repository contains the code for OpenHands, an automated AI software engineer. It has a Python backend
|
||||
(in the `openhands` directory) and React frontend (in the `frontend` directory).
|
||||
|
||||
## General Setup:
|
||||
To set up the entire repo, including frontend and backend, run `make build`.
|
||||
You don't need to do this unless the user asks you to, or if you're trying to run the entire application.
|
||||
|
||||
Before pushing any changes, you should ensure that any lint errors or simple test errors have been fixed.
|
||||
|
||||
* If you've made changes to the backend, you should run `pre-commit run --all-files --config ./dev_config/python/.pre-commit-config.yaml`
|
||||
* If you've made changes to the frontend, you should run `cd frontend && npm run lint:fix && npm run build ; cd ..`
|
||||
|
||||
If either command fails, it may have automatically fixed some issues. You should fix any issues that weren't automatically fixed,
|
||||
then re-run the command to ensure it passes.
|
||||
|
||||
## Repository Structure
|
||||
Backend:
|
||||
- Located in the `openhands` directory
|
||||
- Testing:
|
||||
- All tests are in `tests/unit/test_*.py`
|
||||
- To test new code, run `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate file for the current functionality
|
||||
- Write all tests with pytest
|
||||
|
||||
Frontend:
|
||||
- Located in the `frontend` directory
|
||||
- Prerequisites: A recent version of NodeJS / NPM
|
||||
- Setup: Run `npm install` in the frontend directory
|
||||
- Testing:
|
||||
- Run tests: `npm run test`
|
||||
- To run specific tests: `npm run test -- -t "TestName"`
|
||||
- Building:
|
||||
- Build for production: `npm run build`
|
||||
- Environment Variables:
|
||||
- Set in `frontend/.env` or as environment variables
|
||||
- Available variables: VITE_BACKEND_HOST, VITE_USE_TLS, VITE_INSECURE_SKIP_VERIFY, VITE_FRONTEND_PORT
|
||||
- Internationalization:
|
||||
- Generate i18n declaration file: `npm run make-i18n`
|
||||
@@ -18,24 +18,24 @@ diverse, inclusive, and healthy community.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Demonstrating empathy and kindness toward other people.
|
||||
* Being respectful of differing opinions, viewpoints, and experiences.
|
||||
* Giving and gracefully accepting constructive feedback.
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
and learning from the experience.
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
community.
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
any kind.
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks.
|
||||
* Public or private harassment.
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
without their explicit permission.
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
professional setting.
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
@@ -61,7 +61,7 @@ representative at an online or offline event.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
contact@all-hands.dev
|
||||
contact@all-hands.dev.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
@@ -113,6 +113,20 @@ individual, or aggression toward or disparagement of classes of individuals.
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
### Slack and Discord Etiquettes
|
||||
|
||||
These Slack and Discord etiquette guidelines are designed to foster an inclusive, respectful, and productive environment for all community members. By following these best practices, we ensure effective communication and collaboration while minimizing disruptions. Let’s work together to build a supportive and welcoming community!
|
||||
|
||||
- Communicate respectfully and professionally, avoiding sarcasm or harsh language, and remember that tone can be difficult to interpret in text.
|
||||
- Use threads for specific discussions to keep channels organized and easier to follow.
|
||||
- Tag others only when their input is critical or urgent, and use @here, @channel or @everyone sparingly to minimize disruptions.
|
||||
- Be patient, as open-source contributors and maintainers often have other commitments and may need time to respond.
|
||||
- Post questions or discussions in the most relevant channel (e.g., for [slack - #general](https://app.slack.com/client/T06P212QSEA/C06P5NCGSFP) for general topics, [slack - #questions](https://openhands-ai.slack.com/archives/C06U8UTKSAD) for queries/questions, [discord - #general](https://discord.com/channels/1222935860639563850/1222935861386018885)).
|
||||
- When asking for help or raising issues, include necessary details like links, screenshots, or clear explanations to provide context.
|
||||
- Keep discussions in public channels whenever possible to allow others to benefit from the conversation, unless the matter is sensitive or private.
|
||||
- Always adhere to [our standards](https://github.com/All-Hands-AI/OpenHands/blob/main/CODE_OF_CONDUCT.md#our-standards) to ensure a welcoming and collaborative environment.
|
||||
- If you choose to mute a channel, consider setting up alerts for topics that still interest you to stay engaged. For Slack, Go to Settings → Notifications → My Keywords to add specific keywords that will notify you when mentioned. For example, if you're here for discussions about LLMs, mute the channel if it’s too busy, but set notifications to alert you only when “LLMs” appears in messages. Also for Discord, go to the channel notifications and choose the option that best describes your need.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
|
||||
43
COMMUNITY.md
Normal file
43
COMMUNITY.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# 🙌 The OpenHands Community
|
||||
|
||||
The OpenHands community is built around the belief that (1) AI and AI agents are going to fundamentally change the way
|
||||
we build software, and (2) if this is true, we should do everything we can to make sure that the benefits provided by
|
||||
such powerful technology are accessible to everyone.
|
||||
|
||||
If this resonates with you, we'd love to have you join us in our quest!
|
||||
|
||||
## 🤝 How to Join
|
||||
|
||||
Check out our [How to Join the Community section.](https://github.com/All-Hands-AI/OpenHands?tab=readme-ov-file#-how-to-join-the-community)
|
||||
|
||||
## 💪 Becoming a Contributor
|
||||
|
||||
We welcome contributions from everyone! Whether you're a developer, a researcher, or simply enthusiastic about advancing
|
||||
the field of software engineering with AI, there are many ways to get involved:
|
||||
|
||||
- **Code Contributions:** Help us develop new core functionality, improve our agents, improve the frontend and other
|
||||
interfaces, or anything else that would help make OpenHands better.
|
||||
- **Research and Evaluation:** Contribute to our understanding of LLMs in software engineering, participate in
|
||||
evaluating the models, or suggest improvements.
|
||||
- **Feedback and Testing:** Use the OpenHands toolset, report bugs, suggest features, or provide feedback on usability.
|
||||
|
||||
For details, please check [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
We have a [Code of Conduct](./CODE_OF_CONDUCT.md) that we expect all contributors to adhere to.
|
||||
Long story short, we are aiming for an open, welcoming, diverse, inclusive, and healthy community.
|
||||
All contributors are expected to contribute to building this sort of community.
|
||||
|
||||
## 🛠️ Becoming a Maintainer
|
||||
|
||||
For contributors who have made significant and sustained contributions to the project, there is a possibility of joining
|
||||
the maintainer team. The process for this is as follows:
|
||||
|
||||
1. Any contributor who has made sustained and high-quality contributions to the codebase can be nominated by any
|
||||
maintainer. If you feel that you may qualify you can reach out to any of the maintainers that have reviewed your PRs and ask if you can be nominated.
|
||||
2. Once a maintainer nominates a new maintainer, there will be a discussion period among the maintainers for at least 3 days.
|
||||
3. If no concerns are raised the nomination will be accepted by acclamation, and if concerns are raised there will be a discussion and possible vote.
|
||||
|
||||
Note that just making many PRs does not immediately imply that you will become a maintainer. We will be looking
|
||||
at sustained high-quality contributions over a period of time, as well as good teamwork and adherence to our [Code of Conduct](./CODE_OF_CONDUCT.md).
|
||||
161
CONTRIBUTING.md
161
CONTRIBUTING.md
@@ -2,95 +2,70 @@
|
||||
|
||||
Thanks for your interest in contributing to OpenHands! We welcome and appreciate contributions.
|
||||
|
||||
## Understanding OpenHands's CodeBase
|
||||
|
||||
To understand the codebase, please refer to the README in each module:
|
||||
- [frontend](./frontend/README.md)
|
||||
- [evaluation](./evaluation/README.md)
|
||||
- [openhands](./openhands/README.md)
|
||||
- [agenthub](./openhands/agenthub/README.md)
|
||||
- [server](./openhands/server/README.md)
|
||||
|
||||
## Setting up Your Development Environment
|
||||
|
||||
We have a separate doc [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md) that tells you how to set up a development workflow.
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
There are many ways that you can contribute:
|
||||
|
||||
1. **Download and use** OpenHands, and send [issues](https://github.com/All-Hands-AI/OpenHands/issues) when you encounter something that isn't working or a feature that you'd like to see.
|
||||
2. **Send feedback** after each session by [clicking the thumbs-up thumbs-down buttons](https://docs.all-hands.dev/modules/usage/feedback), so we can see where things are working and failing, and also build an open dataset for training code agents.
|
||||
3. **Improve the Codebase** by sending PRs (see details below). In particular, we have some [good first issue](https://github.com/All-Hands-AI/OpenHands/labels/good%20first%20issue) issues that may be ones to start on.
|
||||
3. **Improve the Codebase** by sending [PRs](#sending-pull-requests-to-openhands) (see details below). In particular, we have some [good first issues](https://github.com/All-Hands-AI/OpenHands/labels/good%20first%20issue) that may be ones to start on.
|
||||
|
||||
## Understanding OpenHands's CodeBase
|
||||
## What Can I Build?
|
||||
Here are a few ways you can help improve the codebase.
|
||||
|
||||
To understand the codebase, please refer to the README in each module:
|
||||
- [frontend](./frontend/README.md)
|
||||
- [agenthub](./agenthub/README.md)
|
||||
- [evaluation](./evaluation/README.md)
|
||||
- [openhands](./openhands/README.md)
|
||||
- [server](./openhands/server/README.md)
|
||||
#### UI/UX
|
||||
We're always looking to improve the look and feel of the application. If you've got a small fix
|
||||
for something that's bugging you, feel free to open up a PR that changes the [`./frontend`](./frontend) directory.
|
||||
|
||||
When you write code, it is also good to write tests. Please navigate to the `tests` folder to see existing test suites.
|
||||
At the moment, we have two kinds of tests: `unit` and `integration`. Please refer to the README for each test suite. These tests also run on GitHub's continuous integration to ensure quality of the project.
|
||||
If you're looking to make a bigger change, add a new UI element, or significantly alter the style
|
||||
of the application, please open an issue first, or better, join the #frontend channel in our Slack
|
||||
to gather consensus from our design team first.
|
||||
|
||||
#### Improving the agent
|
||||
Our main agent is the CodeAct agent. You can [see its prompts here](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/agenthub/codeact_agent).
|
||||
|
||||
Changes to these prompts, and to the underlying behavior in Python, can have a huge impact on user experience.
|
||||
You can try modifying the prompts to see how they change the behavior of the agent as you use the app
|
||||
locally, but we will need to do an end-to-end evaluation of any changes here to ensure that the agent
|
||||
is getting better over time.
|
||||
|
||||
We use the [SWE-bench](https://www.swebench.com/) benchmark to test our agent. You can join the #evaluation
|
||||
channel in Slack to learn more.
|
||||
|
||||
#### Adding a new agent
|
||||
You may want to experiment with building new types of agents. You can add an agent to [`openhands/agenthub`](./openhands/agenthub)
|
||||
to help expand the capabilities of OpenHands.
|
||||
|
||||
#### Adding a new runtime
|
||||
The agent needs a place to run code and commands. When you run OpenHands on your laptop, it uses a Docker container
|
||||
to do this by default. But there are other ways of creating a sandbox for the agent.
|
||||
|
||||
If you work for a company that provides a cloud-based runtime, you could help us add support for that runtime
|
||||
by implementing the [interface specified here](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/base.py).
|
||||
|
||||
#### Testing
|
||||
When you write code, it is also good to write tests. Please navigate to the [`./tests`](./tests) folder to see existing test suites.
|
||||
At the moment, we have two kinds of tests: [`unit`](./tests/unit) and [`integration`](./evaluation/integration_tests). Please refer to the README for each test suite. These tests also run on GitHub's continuous integration to ensure quality of the project.
|
||||
|
||||
## Sending Pull Requests to OpenHands
|
||||
|
||||
### 1. Fork the Official Repository
|
||||
Fork the [OpenHands repository](https://github.com/All-Hands-AI/OpenHands) into your own account.
|
||||
Clone your own forked repository into your local environment:
|
||||
You'll need to fork our repository to send us a Pull Request. You can learn more
|
||||
about how to fork a GitHub repo and open a PR with your changes in [this article](https://medium.com/swlh/forks-and-pull-requests-how-to-contribute-to-github-repos-8843fac34ce8).
|
||||
|
||||
```shell
|
||||
git clone git@github.com:<YOUR-USERNAME>/OpenHands.git
|
||||
```
|
||||
|
||||
### 2. Configure Git
|
||||
|
||||
Set the official repository as your [upstream](https://www.atlassian.com/git/tutorials/git-forks-and-upstreams) to synchronize with the latest update in the official repository.
|
||||
Add the original repository as upstream:
|
||||
|
||||
```shell
|
||||
cd OpenHands
|
||||
git remote add upstream git@github.com:All-Hands-AI/OpenHands.git
|
||||
```
|
||||
|
||||
Verify that the remote is set:
|
||||
|
||||
```shell
|
||||
git remote -v
|
||||
```
|
||||
|
||||
You should see both `origin` and `upstream` in the output.
|
||||
|
||||
### 3. Synchronize with Official Repository
|
||||
Synchronize latest commit with official repository before coding:
|
||||
|
||||
```shell
|
||||
git fetch upstream
|
||||
git checkout main
|
||||
git merge upstream/main
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### 4. Set up the Development Environment
|
||||
|
||||
We have a separate doc [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md) that tells you how to set up a development workflow.
|
||||
|
||||
### 5. Write Code and Commit It
|
||||
|
||||
Once you have done this, you can write code, test it, and commit it to a branch (replace `my_branch` with an appropriate name):
|
||||
|
||||
```shell
|
||||
git checkout -b my_branch
|
||||
git add .
|
||||
git commit
|
||||
git push origin my_branch
|
||||
```
|
||||
|
||||
### 6. Open a Pull Request
|
||||
|
||||
* On GitHub, go to the page of your forked repository, and create a Pull Request:
|
||||
- Click on `Branches`
|
||||
- Click on the `...` beside your branch and click on `New pull request`
|
||||
- Set `base repository` to `All-Hands-AI/OpenHands`
|
||||
- Set `base` to `main`
|
||||
- Click `Create pull request`
|
||||
|
||||
The PR should appear in [OpenHands PRs](https://github.com/All-Hands-AI/OpenHands/pulls).
|
||||
|
||||
Then the OpenHands team will review your code.
|
||||
|
||||
## PR Rules
|
||||
|
||||
### 1. Pull Request title
|
||||
### Pull Request title
|
||||
As described [here](https://github.com/commitizen/conventional-commit-types/blob/master/index.json), a valid PR title should begin with one of the following prefixes:
|
||||
|
||||
- `feat`: A new feature
|
||||
@@ -111,6 +86,38 @@ For example, a PR title could be:
|
||||
|
||||
You may also check out previous PRs in the [PR list](https://github.com/All-Hands-AI/OpenHands/pulls).
|
||||
|
||||
### 2. Pull Request description
|
||||
### Pull Request description
|
||||
- If your PR is small (such as a typo fix), you can go brief.
|
||||
- If it contains a lot of changes, it's better to write more details.
|
||||
|
||||
If your changes are user-facing (e.g. a new feature in the UI, a change in behavior, or a bugfix)
|
||||
please include a short message that we can add to our changelog.
|
||||
|
||||
## How to Make Effective Contributions
|
||||
|
||||
### Opening Issues
|
||||
|
||||
If you notice any bugs or have any feature requests please open them via the [issues page](https://github.com/All-Hands-AI/OpenHands/issues). We will triage based on how critical the bug is or how potentially useful the improvement is, discuss, and implement the ones that the community has interest/effort for.
|
||||
|
||||
Further, if you see an issue you like, please leave a "thumbs-up" or a comment, which will help us prioritize.
|
||||
|
||||
### Making Pull Requests
|
||||
|
||||
We're generally happy to consider all pull requests with the evaluation process varying based on the type of change:
|
||||
|
||||
#### For Small Improvements
|
||||
|
||||
Small improvements with few downsides are typically reviewed and approved quickly.
|
||||
One thing to check when making changes is to ensure that all continuous integration tests pass, which you can check before getting a review.
|
||||
|
||||
#### For Core Agent Changes
|
||||
|
||||
We need to be more careful with changes to the core agent, as it is imperative to maintain high quality. These PRs are evaluated based on three key metrics:
|
||||
|
||||
1. **Accuracy**
|
||||
2. **Efficiency**
|
||||
3. **Code Complexity**
|
||||
|
||||
If it improves accuracy, efficiency, or both with only a minimal change to code quality, that's great we're happy to merge it in!
|
||||
If there are bigger tradeoffs (e.g. helping efficiency a lot and hurting accuracy a little) we might want to put it behind a feature flag.
|
||||
Either way, please feel free to discuss on github issues or slack, and we will give guidance and preliminary feedback.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Contributors
|
||||
|
||||
We would like to thank all the [contributors](https://github.com/All-Hands-AI/OpenHands/graphs/contributors) who have helped make OpenHands possible. Your dedication and hard work are greatly appreciated.
|
||||
We would like to thank all the [contributors](https://github.com/All-Hands-AI/OpenHands/graphs/contributors) who have helped make OpenHands possible. We greatly appreciate your dedication and hard work.
|
||||
|
||||
## Open Source Projects
|
||||
|
||||
@@ -10,7 +10,7 @@ OpenHands includes and adapts the following open source projects. We are gratefu
|
||||
|
||||
#### [SWE Agent](https://github.com/princeton-nlp/swe-agent)
|
||||
- License: MIT License
|
||||
- Description: Adapted for use in OpenHands's agenthub
|
||||
- Description: Adapted for use in OpenHands's agent hub
|
||||
|
||||
#### [Aider](https://github.com/paul-gauthier/aider)
|
||||
- License: Apache License 2.0
|
||||
|
||||
@@ -3,14 +3,16 @@ This guide is for people working on OpenHands and editing the source code.
|
||||
If you wish to contribute your changes, check out the [CONTRIBUTING.md](https://github.com/All-Hands-AI/OpenHands/blob/main/CONTRIBUTING.md) on how to clone and setup the project initially before moving on.
|
||||
Otherwise, you can clone the OpenHands project directly.
|
||||
|
||||
## Start the server for development
|
||||
## Start the Server for Development
|
||||
### 1. Requirements
|
||||
* Linux, Mac OS, or [WSL on Windows](https://learn.microsoft.com/en-us/windows/wsl/install) [ Ubuntu <= 22.04]
|
||||
* Linux, Mac OS, or [WSL on Windows](https://learn.microsoft.com/en-us/windows/wsl/install) [Ubuntu >= 22.04]
|
||||
* [Docker](https://docs.docker.com/engine/install/) (For those on MacOS, make sure to allow the default Docker socket to be used from advanced settings!)
|
||||
* [Python](https://www.python.org/downloads/) = 3.11
|
||||
* [NodeJS](https://nodejs.org/en/download/package-manager) >= 18.17.1
|
||||
* [Python](https://www.python.org/downloads/) = 3.12
|
||||
* [NodeJS](https://nodejs.org/en/download/package-manager) >= 20.x
|
||||
* [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) >= 1.8
|
||||
* netcat => sudo apt-get install netcat
|
||||
* OS-specific dependencies:
|
||||
- Ubuntu: build-essential => `sudo apt-get install build-essential`
|
||||
- WSL: netcat => `sudo apt-get install netcat`
|
||||
|
||||
Make sure you have all these dependencies installed before moving on to `make build`.
|
||||
|
||||
@@ -22,8 +24,8 @@ If you want to develop without system admin/sudo access to upgrade/install `Pyth
|
||||
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
|
||||
bash Miniforge3-$(uname)-$(uname -m).sh
|
||||
|
||||
# Install Python 3.11, nodejs, and poetry
|
||||
mamba install python=3.11
|
||||
# Install Python 3.12, nodejs, and poetry
|
||||
mamba install python=3.12
|
||||
mamba install conda-forge::nodejs
|
||||
mamba install conda-forge::poetry
|
||||
```
|
||||
@@ -36,7 +38,9 @@ make build
|
||||
```
|
||||
|
||||
### 3. Configuring the Language Model
|
||||
OpenHands supports a diverse array of Language Models (LMs) through the powerful [litellm](https://docs.litellm.ai) library. By default, we've chosen the mighty GPT-4 from OpenAI as our go-to model, but the world is your oyster! You can unleash the potential of Anthropic's suave Claude, the enigmatic Llama, or any other LM that piques your interest.
|
||||
OpenHands supports a diverse array of Language Models (LMs) through the powerful [litellm](https://docs.litellm.ai) library.
|
||||
By default, we've chosen Claude Sonnet 3.5 as our go-to model, but the world is your oyster! You can unleash the
|
||||
potential of any other LM that piques your interest.
|
||||
|
||||
To configure the LM of your choice, run:
|
||||
|
||||
@@ -50,14 +54,11 @@ To configure the LM of your choice, run:
|
||||
Environment variables > config.toml variables > default variables
|
||||
|
||||
**Note on Alternative Models:**
|
||||
Some alternative models may prove more challenging to tame than others. Fear not, brave adventurer! We shall soon unveil LLM-specific documentation to guide you on your quest.
|
||||
And if you've already mastered the art of wielding a model other than OpenAI's GPT, we encourage you to share your setup instructions with us by creating instructions and adding it [to our documentation](https://github.com/All-Hands-AI/OpenHands/tree/main/docs/modules/usage/llms).
|
||||
|
||||
For a full list of the LM providers and models available, please consult the [litellm documentation](https://docs.litellm.ai/docs/providers).
|
||||
See [our documentation](https://docs.all-hands.dev/modules/usage/llms) for recommended models.
|
||||
|
||||
### 4. Running the application
|
||||
#### Option A: Run the Full Application
|
||||
Once the setup is complete, launching OpenHands is as simple as running a single command. This command starts both the backend and frontend servers seamlessly, allowing you to interact with OpenHands:
|
||||
Once the setup is complete, this command starts both the backend and frontend servers, allowing you to interact with OpenHands:
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
@@ -74,11 +75,11 @@ make run
|
||||
```
|
||||
|
||||
### 6. LLM Debugging
|
||||
If you encounter any issues with the Language Model (LM) or you're simply curious, you can inspect the actual LLM prompts and responses. To do so, export DEBUG=1 in the environment and restart the backend.
|
||||
OpenHands will then log the prompts and responses in the logs/llm/CURRENT_DATE directory, allowing you to identify the causes.
|
||||
If you encounter any issues with the Language Model (LM) or you're simply curious, export DEBUG=1 in the environment and restart the backend.
|
||||
OpenHands will log the prompts and responses in the logs/llm/CURRENT_DATE directory, allowing you to identify the causes.
|
||||
|
||||
### 7. Help
|
||||
Need assistance or information on available targets and commands? The help command provides all the necessary guidance to ensure a smooth experience with OpenHands.
|
||||
Need help or info on available targets and commands? Use the help command for all the guidance you need with OpenHands.
|
||||
```bash
|
||||
make help
|
||||
```
|
||||
@@ -91,9 +92,37 @@ To run tests, refer to the following:
|
||||
poetry run pytest ./tests/unit/test_*.py
|
||||
```
|
||||
|
||||
#### Integration tests
|
||||
Please refer to [this README](./tests/integration/README.md) for details.
|
||||
|
||||
### 9. Add or update dependency
|
||||
1. Add your dependency in `pyproject.toml` or use `poetry add xxx`
|
||||
2. Update the poetry.lock file via `poetry lock --no-update`
|
||||
1. Add your dependency in `pyproject.toml` or use `poetry add xxx`.
|
||||
2. Update the poetry.lock file via `poetry lock --no-update`.
|
||||
|
||||
### 9. Use existing Docker image
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker container image by
|
||||
setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.21-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
TL;DR
|
||||
|
||||
```bash
|
||||
make docker-dev
|
||||
```
|
||||
|
||||
See more details [here](./containers/dev/README.md).
|
||||
|
||||
If you are just interested in running `OpenHands` without installing all the required tools on your host.
|
||||
|
||||
```bash
|
||||
make docker-run
|
||||
```
|
||||
|
||||
If you do not have `make` on your host, run:
|
||||
|
||||
```bash
|
||||
cd ./containers/dev
|
||||
./dev.sh
|
||||
```
|
||||
|
||||
You do need [Docker](https://docs.docker.com/engine/install/) installed on your host though.
|
||||
|
||||
@@ -2,24 +2,24 @@
|
||||
These are the procedures and guidelines on how issues are triaged in this repo by the maintainers.
|
||||
|
||||
## General
|
||||
* Most issues must be tagged with **enhancement** or **bug**
|
||||
* Issues may be tagged with what it relates to (**backend**, **frontend**, **agent quality**, etc.)
|
||||
* Most issues must be tagged with **enhancement** or **bug**.
|
||||
* Issues may be tagged with what it relates to (**backend**, **frontend**, **agent quality**, etc.).
|
||||
|
||||
## Severity
|
||||
* **Low**: Minor issues, single user report
|
||||
* **Medium**: Affecting multiple users
|
||||
* **Critical**: Affecting all users or potential security issues
|
||||
* **Low**: Minor issues or affecting single user.
|
||||
* **Medium**: Affecting multiple users.
|
||||
* **Critical**: Affecting all users or potential security issues.
|
||||
|
||||
## Effort
|
||||
* Issues may be estimated with effort required (**small effort**, **medium effort**, **large effort**)
|
||||
* Issues may be estimated with effort required (**small effort**, **medium effort**, **large effort**).
|
||||
|
||||
## Difficulty
|
||||
* Issues with low implementation difficulty may be tagged with **good first issue**
|
||||
* Issues with low implementation difficulty may be tagged with **good first issue**.
|
||||
|
||||
## Not Enough Information
|
||||
* User is asked to provide more information (logs, how to reproduce, etc.) when the issue is not clear
|
||||
* If an issue is unclear and the author does not provide more information or respond to a request, the issue may be closed as **not planned** (Usually after a week)
|
||||
* User is asked to provide more information (logs, how to reproduce, etc.) when the issue is not clear.
|
||||
* If an issue is unclear and the author does not provide more information or respond to a request, the issue may be closed as **not planned** (Usually after a week).
|
||||
|
||||
## Multiple Requests/Fixes in One Issue
|
||||
* These issues will be narrowed down to one request/fix so the issue is more easily tracked and fixed
|
||||
* Issues may be broken down into multiple issues if required
|
||||
* These issues will be narrowed down to one request/fix so the issue is more easily tracked and fixed.
|
||||
* Issues may be broken down into multiple issues if required.
|
||||
|
||||
46
Makefile
46
Makefile
@@ -2,14 +2,15 @@ SHELL=/bin/bash
|
||||
# Makefile for OpenHands project
|
||||
|
||||
# Variables
|
||||
BACKEND_HOST ?= "127.0.0.1"
|
||||
BACKEND_PORT = 3000
|
||||
BACKEND_HOST = "127.0.0.1:$(BACKEND_PORT)"
|
||||
BACKEND_HOST_PORT = "$(BACKEND_HOST):$(BACKEND_PORT)"
|
||||
FRONTEND_PORT = 3001
|
||||
DEFAULT_WORKSPACE_DIR = "./workspace"
|
||||
DEFAULT_MODEL = "gpt-4o"
|
||||
CONFIG_FILE = config.toml
|
||||
PRE_COMMIT_CONFIG_PATH = "./dev_config/python/.pre-commit-config.yaml"
|
||||
PYTHON_VERSION = 3.11
|
||||
PYTHON_VERSION = 3.12
|
||||
|
||||
# ANSI color codes
|
||||
GREEN=$(shell tput -Txterm setaf 2)
|
||||
@@ -80,10 +81,10 @@ check-nodejs:
|
||||
@if command -v node > /dev/null; then \
|
||||
NODE_VERSION=$(shell node --version | sed -E 's/v//g'); \
|
||||
IFS='.' read -r -a NODE_VERSION_ARRAY <<< "$$NODE_VERSION"; \
|
||||
if [ "$${NODE_VERSION_ARRAY[0]}" -gt 18 ] || ([ "$${NODE_VERSION_ARRAY[0]}" -eq 18 ] && [ "$${NODE_VERSION_ARRAY[1]}" -gt 17 ]) || ([ "$${NODE_VERSION_ARRAY[0]}" -eq 18 ] && [ "$${NODE_VERSION_ARRAY[1]}" -eq 17 ] && [ "$${NODE_VERSION_ARRAY[2]}" -ge 1 ]); then \
|
||||
if [ "$${NODE_VERSION_ARRAY[0]}" -ge 20 ]; then \
|
||||
echo "$(BLUE)Node.js $$NODE_VERSION is already installed.$(RESET)"; \
|
||||
else \
|
||||
echo "$(RED)Node.js 18.17.1 or later is required. Please install Node.js 18.17.1 or later to continue.$(RESET)"; \
|
||||
echo "$(RED)Node.js 20.x or later is required. Please install Node.js 20.x or later to continue.$(RESET)"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
else \
|
||||
@@ -105,7 +106,7 @@ check-poetry:
|
||||
@if command -v poetry > /dev/null; then \
|
||||
POETRY_VERSION=$(shell poetry --version 2>&1 | sed -E 's/Poetry \(version ([0-9]+\.[0-9]+\.[0-9]+)\)/\1/'); \
|
||||
IFS='.' read -r -a POETRY_VERSION_ARRAY <<< "$$POETRY_VERSION"; \
|
||||
if [ $${POETRY_VERSION_ARRAY[0]} -ge 1 ] && [ $${POETRY_VERSION_ARRAY[1]} -ge 8 ]; then \
|
||||
if [ $${POETRY_VERSION_ARRAY[0]} -gt 1 ] || ([ $${POETRY_VERSION_ARRAY[0]} -eq 1 ] && [ $${POETRY_VERSION_ARRAY[1]} -ge 8 ]); then \
|
||||
echo "$(BLUE)$(shell poetry --version) is already installed.$(RESET)"; \
|
||||
else \
|
||||
echo "$(RED)Poetry 1.8 or later is required. You can install poetry by running the following command, then adding Poetry to your PATH:"; \
|
||||
@@ -189,12 +190,12 @@ build-frontend:
|
||||
# Start backend
|
||||
start-backend:
|
||||
@echo "$(YELLOW)Starting backend...$(RESET)"
|
||||
@poetry run uvicorn openhands.server.listen:app --port $(BACKEND_PORT) --reload --reload-exclude "workspace/*"
|
||||
@poetry run uvicorn openhands.server.listen:app --host $(BACKEND_HOST) --port $(BACKEND_PORT) --reload --reload-exclude "./workspace"
|
||||
|
||||
# Start frontend
|
||||
start-frontend:
|
||||
@echo "$(YELLOW)Starting frontend...$(RESET)"
|
||||
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run start
|
||||
@cd frontend && VITE_BACKEND_HOST=$(BACKEND_HOST_PORT) VITE_FRONTEND_PORT=$(FRONTEND_PORT) npm run dev -- --port $(FRONTEND_PORT) --host $(BACKEND_HOST)
|
||||
|
||||
# Common setup for running the app (non-callable)
|
||||
_run_setup:
|
||||
@@ -204,7 +205,7 @@ _run_setup:
|
||||
fi
|
||||
@mkdir -p logs
|
||||
@echo "$(YELLOW)Starting backend server...$(RESET)"
|
||||
@poetry run uvicorn openhands.server.listen:app --port $(BACKEND_PORT) &
|
||||
@poetry run uvicorn openhands.server.listen:app --host $(BACKEND_HOST) --port $(BACKEND_PORT) &
|
||||
@echo "$(YELLOW)Waiting for the backend to start...$(RESET)"
|
||||
@until nc -z localhost $(BACKEND_PORT); do sleep 0.1; done
|
||||
@echo "$(GREEN)Backend started successfully.$(RESET)"
|
||||
@@ -213,9 +214,23 @@ _run_setup:
|
||||
run:
|
||||
@echo "$(YELLOW)Running the app...$(RESET)"
|
||||
@$(MAKE) -s _run_setup
|
||||
@cd frontend && echo "$(BLUE)Starting frontend with npm...$(RESET)" && npm run start -- --port $(FRONTEND_PORT)
|
||||
@$(MAKE) -s start-frontend
|
||||
@echo "$(GREEN)Application started successfully.$(RESET)"
|
||||
|
||||
# Run the app (in docker)
|
||||
docker-run: WORKSPACE_BASE ?= $(PWD)/workspace
|
||||
docker-run:
|
||||
@if [ -f /.dockerenv ]; then \
|
||||
echo "Running inside a Docker container. Exiting..."; \
|
||||
exit 0; \
|
||||
else \
|
||||
echo "$(YELLOW)Running the app in Docker $(OPTIONS)...$(RESET)"; \
|
||||
export WORKSPACE_BASE=${WORKSPACE_BASE}; \
|
||||
export SANDBOX_USER_ID=$(shell id -u); \
|
||||
export DATE=$(shell date +%Y%m%d%H%M%S); \
|
||||
docker compose up $(OPTIONS); \
|
||||
fi
|
||||
|
||||
# Run the app (WSL mode)
|
||||
run-wsl:
|
||||
@echo "$(YELLOW)Running the app in WSL mode...$(RESET)"
|
||||
@@ -280,6 +295,16 @@ setup-config-prompts:
|
||||
fi
|
||||
|
||||
|
||||
# Develop in container
|
||||
docker-dev:
|
||||
@if [ -f /.dockerenv ]; then \
|
||||
echo "Running inside a Docker container. Exiting..."; \
|
||||
exit 0; \
|
||||
else \
|
||||
echo "$(YELLOW)Build and run in Docker $(OPTIONS)...$(RESET)"; \
|
||||
./containers/dev/dev.sh $(OPTIONS); \
|
||||
fi
|
||||
|
||||
# Clean up all caches
|
||||
clean:
|
||||
@echo "$(YELLOW)Cleaning up caches...$(RESET)"
|
||||
@@ -298,7 +323,10 @@ help:
|
||||
@echo " $(GREEN)start-frontend$(RESET) - Start the frontend server for the OpenHands project."
|
||||
@echo " $(GREEN)run$(RESET) - Run the OpenHands application, starting both backend and frontend servers."
|
||||
@echo " Backend Log file will be stored in the 'logs' directory."
|
||||
@echo " $(GREEN)docker-dev$(RESET) - Build and run the OpenHands application in Docker."
|
||||
@echo " $(GREEN)docker-run$(RESET) - Run the OpenHands application, starting both backend and frontend servers in Docker."
|
||||
@echo " $(GREEN)help$(RESET) - Display this help message, providing information on available targets."
|
||||
|
||||
# Phony targets
|
||||
.PHONY: build check-dependencies check-python check-npm check-docker check-poetry install-python-dependencies install-frontend-dependencies install-pre-commit-hooks lint start-backend start-frontend run run-wsl setup-config setup-config-prompts help
|
||||
.PHONY: docker-dev docker-run
|
||||
|
||||
81
README.md
81
README.md
@@ -12,7 +12,7 @@
|
||||
<a href="https://codecov.io/github/All-Hands-AI/OpenHands?branch=main"><img alt="CodeCov" src="https://img.shields.io/codecov/c/github/All-Hands-AI/OpenHands?style=for-the-badge&color=blue"></a>
|
||||
<a href="https://github.com/All-Hands-AI/OpenHands/blob/main/LICENSE"><img src="https://img.shields.io/github/license/All-Hands-AI/OpenHands?style=for-the-badge&color=blue" alt="MIT License"></a>
|
||||
<br/>
|
||||
<a href="https://join.slack.com/t/opendevin/shared_invite/zt-2oikve2hu-UDxHeo8nsE69y6T7yFX_BA"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="Join our Slack community"></a>
|
||||
<a href="https://join.slack.com/t/openhands-ai/shared_invite/zt-2wkh4pklz-w~h_DVDtEe9H5kyQlcNxVw"><img src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge" alt="Join our Slack community"></a>
|
||||
<a href="https://discord.gg/ESHStjSjD4"><img src="https://img.shields.io/badge/Discord-Join%20Us-purple?logo=discord&logoColor=white&style=for-the-badge" alt="Join our Discord community"></a>
|
||||
<a href="https://github.com/All-Hands-AI/OpenHands/blob/main/CREDITS.md"><img src="https://img.shields.io/badge/Project-Credits-blue?style=for-the-badge&color=FFE165&logo=github&logoColor=white" alt="Credits"></a>
|
||||
<br/>
|
||||
@@ -29,37 +29,55 @@ call APIs, and yes—even copy code snippets from StackOverflow.
|
||||
|
||||
Learn more at [docs.all-hands.dev](https://docs.all-hands.dev), or jump to the [Quick Start](#-quick-start).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Using OpenHands for work? We'd love to chat! Fill out
|
||||
> [this short form](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform)
|
||||
> to join our Design Partner program, where you'll get early access to commercial features and the opportunity to provide input on our product roadmap.
|
||||
|
||||

|
||||
|
||||
## ⚡ Quick Start
|
||||
|
||||
The easiest way to run OpenHands is in Docker. You can change `WORKSPACE_BASE` below to
|
||||
point OpenHands to existing code that you'd like to modify.
|
||||
|
||||
See the [Getting Started](https://docs.all-hands.dev/modules/usage/getting-started) guide for
|
||||
The easiest way to run OpenHands is in Docker.
|
||||
See the [Running OpenHands](https://docs.all-hands.dev/modules/usage/installation) guide for
|
||||
system requirements and more information.
|
||||
|
||||
```bash
|
||||
export WORKSPACE_BASE=$(pwd)/workspace
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik
|
||||
|
||||
docker run -it --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.9-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-v $WORKSPACE_BASE:/opt/workspace_base \
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v ~/.openhands-state:/.openhands-state \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
ghcr.io/all-hands-ai/openhands:0.9
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21
|
||||
```
|
||||
|
||||
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!
|
||||
|
||||
You can also run OpenHands in a scriptable [headless mode](https://docs.all-hands.dev/modules/usage/how-to/headless-mode),
|
||||
or as an [interactive CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode).
|
||||
Finally, you'll need a model provider and API key.
|
||||
[Anthropic's Claude 3.5 Sonnet](https://www.anthropic.com/api) (`anthropic/claude-3-5-sonnet-20241022`)
|
||||
works best, but you have [many options](https://docs.all-hands.dev/modules/usage/llms).
|
||||
|
||||
Visit [Getting Started](https://docs.all-hands.dev/modules/usage/getting-started) for more information and setup instructions.
|
||||
---
|
||||
|
||||
You can also [connect OpenHands to your local filesystem](https://docs.all-hands.dev/modules/usage/runtimes#connecting-to-your-filesystem),
|
||||
run OpenHands in a scriptable [headless mode](https://docs.all-hands.dev/modules/usage/how-to/headless-mode),
|
||||
interact with it via a [friendly CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode),
|
||||
or run it on tagged issues with [a github action](https://docs.all-hands.dev/modules/usage/how-to/github-action).
|
||||
|
||||
Visit [Running OpenHands](https://docs.all-hands.dev/modules/usage/installation) for more information and setup instructions.
|
||||
|
||||
> [!CAUTION]
|
||||
> OpenHands is meant to be run by a single user on their local workstation.
|
||||
> It is not appropriate for multi-tenant deployments where multiple users share the same instance. There is no built-in isolation or scalability.
|
||||
>
|
||||
> If you're interested in running OpenHands in a multi-tenant environment, please
|
||||
> [get in touch with us](https://docs.google.com/forms/d/e/1FAIpQLSet3VbGaz8z32gW9Wm-Grl4jpt5WgMXPgJ4EDPVmCETCBpJtQ/viewform)
|
||||
> for advanced deployment options.
|
||||
|
||||
If you want to modify the OpenHands source code, check out [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
|
||||
|
||||
@@ -68,33 +86,26 @@ Having issues? The [Troubleshooting Guide](https://docs.all-hands.dev/modules/us
|
||||
## 📖 Documentation
|
||||
|
||||
To learn more about the project, and for tips on using OpenHands,
|
||||
**check out our [documentation](https://docs.all-hands.dev/modules/usage/getting-started)**.
|
||||
check out our [documentation](https://docs.all-hands.dev/modules/usage/getting-started).
|
||||
|
||||
There you'll find resources on how to use different LLM providers,
|
||||
troubleshooting resources, and advanced configuration options.
|
||||
|
||||
## 🤝 How to Contribute
|
||||
## 🤝 How to Join the Community
|
||||
|
||||
OpenHands is a community-driven project, and we welcome contributions from everyone.
|
||||
Whether you're a developer, a researcher, or simply enthusiastic about advancing the field of
|
||||
software engineering with AI, there are many ways to get involved:
|
||||
OpenHands is a community-driven project, and we welcome contributions from everyone. We do most of our communication
|
||||
through Slack, so this is the best place to start, but we also are happy to have you contact us on Discord or Github:
|
||||
|
||||
- **Code Contributions:** Help us develop new agents, core functionality, the frontend and other interfaces, or sandboxing solutions.
|
||||
- **Research and Evaluation:** Contribute to our understanding of LLMs in software engineering, participate in evaluating the models, or suggest improvements.
|
||||
- **Feedback and Testing:** Use the OpenHands toolset, report bugs, suggest features, or provide feedback on usability.
|
||||
- [Join our Slack workspace](https://join.slack.com/t/openhands-ai/shared_invite/zt-2wkh4pklz-w~h_DVDtEe9H5kyQlcNxVw) - Here we talk about research, architecture, and future development.
|
||||
- [Join our Discord server](https://discord.gg/ESHStjSjD4) - This is a community-run server for general discussion, questions, and feedback.
|
||||
- [Read or post Github Issues](https://github.com/All-Hands-AI/OpenHands/issues) - Check out the issues we're working on, or add your own ideas.
|
||||
|
||||
For details, please check [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
## 🤖 Join Our Community
|
||||
|
||||
Whether you're a developer, a researcher, or simply enthusiastic about OpenHands, we'd love to have you in our community.
|
||||
Let's make software engineering better together!
|
||||
|
||||
- [Slack workspace](https://join.slack.com/t/opendevin/shared_invite/zt-2oikve2hu-UDxHeo8nsE69y6T7yFX_BA) - Here we talk about research, architecture, and future development.
|
||||
- [Discord server](https://discord.gg/ESHStjSjD4) - This is a community-run server for general discussion, questions, and feedback.
|
||||
See more about the community in [COMMUNITY.md](./COMMUNITY.md) or find details on contributing in [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
## 📈 Progress
|
||||
|
||||
See the monthly OpenHands roadmap [here](https://github.com/orgs/All-Hands-AI/projects/1) (updated at the maintainer's meeting at the end of each month).
|
||||
|
||||
<p align="center">
|
||||
<a href="https://star-history.com/#All-Hands-AI/OpenHands&Date">
|
||||
<img src="https://api.star-history.com/svg?repos=All-Hands-AI/OpenHands&type=Date" width="500" alt="Star History Chart">
|
||||
@@ -114,8 +125,8 @@ For a list of open source projects and licenses used in OpenHands, please see ou
|
||||
## 📚 Cite
|
||||
|
||||
```
|
||||
@misc{opendevin,
|
||||
title={{OpenDevin: An Open Platform for AI Software Developers as Generalist Agents}},
|
||||
@misc{openhands,
|
||||
title={{OpenHands: An Open Platform for AI Software Developers as Generalist Agents}},
|
||||
author={Xingyao Wang and Boxuan Li and Yufan Song and Frank F. Xu and Xiangru Tang and Mingchen Zhuge and Jiayi Pan and Yueqi Song and Bowen Li and Jaskirat Singh and Hoang H. Tran and Fuqiang Li and Ren Ma and Mingzhang Zheng and Bill Qian and Yanjun Shao and Niklas Muennighoff and Yizhe Zhang and Binyuan Hui and Junyang Lin and Robert Brennan and Hao Peng and Heng Ji and Graham Neubig},
|
||||
year={2024},
|
||||
eprint={2407.16741},
|
||||
|
||||
@@ -1,786 +0,0 @@
|
||||
import abc
|
||||
import difflib
|
||||
import logging
|
||||
import platform
|
||||
from copy import deepcopy
|
||||
from dataclasses import asdict, dataclass
|
||||
from textwrap import dedent
|
||||
from typing import Literal, Union
|
||||
from warnings import warn
|
||||
|
||||
from browsergym.core.action.base import AbstractActionSet
|
||||
from browsergym.core.action.highlevel import HighLevelActionSet
|
||||
from browsergym.core.action.python import PythonActionSet
|
||||
|
||||
from agenthub.browsing_agent.utils import (
|
||||
ParseError,
|
||||
parse_html_tags_raise,
|
||||
)
|
||||
from openhands.runtime.browser.browser_env import BrowserEnv
|
||||
|
||||
|
||||
@dataclass
|
||||
class Flags:
|
||||
use_html: bool = True
|
||||
use_ax_tree: bool = False
|
||||
drop_ax_tree_first: bool = True # This flag is no longer active TODO delete
|
||||
use_thinking: bool = False
|
||||
use_error_logs: bool = False
|
||||
use_past_error_logs: bool = False
|
||||
use_history: bool = False
|
||||
use_action_history: bool = False
|
||||
use_memory: bool = False
|
||||
use_diff: bool = False
|
||||
html_type: str = 'pruned_html'
|
||||
use_concrete_example: bool = True
|
||||
use_abstract_example: bool = False
|
||||
multi_actions: bool = False
|
||||
action_space: Literal[
|
||||
'python', 'bid', 'coord', 'bid+coord', 'bid+nav', 'coord+nav', 'bid+coord+nav'
|
||||
] = 'bid'
|
||||
is_strict: bool = False
|
||||
# This flag will be automatically disabled `if not chat_model_args.has_vision()`
|
||||
use_screenshot: bool = True
|
||||
enable_chat: bool = False
|
||||
max_prompt_tokens: int = 100_000
|
||||
extract_visible_tag: bool = False
|
||||
extract_coords: Literal['False', 'center', 'box'] = 'False'
|
||||
extract_visible_elements_only: bool = False
|
||||
demo_mode: Literal['off', 'default', 'only_visible_elements'] = 'off'
|
||||
|
||||
def copy(self):
|
||||
return deepcopy(self)
|
||||
|
||||
def asdict(self):
|
||||
"""Helper for JSON serializble requirement."""
|
||||
return asdict(self)
|
||||
|
||||
@classmethod
|
||||
def from_dict(self, flags_dict):
|
||||
"""Helper for JSON serializble requirement."""
|
||||
if isinstance(flags_dict, Flags):
|
||||
return flags_dict
|
||||
|
||||
if not isinstance(flags_dict, dict):
|
||||
raise ValueError(
|
||||
f'Unregcognized type for flags_dict of type {type(flags_dict)}.'
|
||||
)
|
||||
return Flags(**flags_dict)
|
||||
|
||||
|
||||
class PromptElement:
|
||||
"""Base class for all prompt elements. Prompt elements can be hidden.
|
||||
|
||||
Prompt elements are used to build the prompt. Use flags to control which
|
||||
prompt elements are visible. We use class attributes as a convenient way
|
||||
to implement static prompts, but feel free to override them with instance
|
||||
attributes or @property decorator.
|
||||
"""
|
||||
|
||||
_prompt = ''
|
||||
_abstract_ex = ''
|
||||
_concrete_ex = ''
|
||||
|
||||
def __init__(self, visible: bool = True) -> None:
|
||||
"""Prompt element that can be hidden.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
visible : bool, optional
|
||||
Whether the prompt element should be visible, by default True. Can
|
||||
be a callable that returns a bool. This is useful when a specific
|
||||
flag changes during a shrink iteration.
|
||||
"""
|
||||
self._visible = visible
|
||||
|
||||
@property
|
||||
def prompt(self):
|
||||
"""Avoid overriding this method. Override _prompt instead."""
|
||||
return self._hide(self._prompt)
|
||||
|
||||
@property
|
||||
def abstract_ex(self):
|
||||
"""Useful when this prompt element is requesting an answer from the llm.
|
||||
Provide an abstract example of the answer here. See Memory for an
|
||||
example.
|
||||
|
||||
Avoid overriding this method. Override _abstract_ex instead
|
||||
"""
|
||||
return self._hide(self._abstract_ex)
|
||||
|
||||
@property
|
||||
def concrete_ex(self):
|
||||
"""Useful when this prompt element is requesting an answer from the llm.
|
||||
Provide a concrete example of the answer here. See Memory for an
|
||||
example.
|
||||
|
||||
Avoid overriding this method. Override _concrete_ex instead
|
||||
"""
|
||||
return self._hide(self._concrete_ex)
|
||||
|
||||
@property
|
||||
def is_visible(self):
|
||||
"""Handle the case where visible is a callable."""
|
||||
visible = self._visible
|
||||
if callable(visible):
|
||||
visible = visible()
|
||||
return visible
|
||||
|
||||
def _hide(self, value):
|
||||
"""Return value if visible is True, else return empty string."""
|
||||
if self.is_visible:
|
||||
return value
|
||||
else:
|
||||
return ''
|
||||
|
||||
def _parse_answer(self, text_answer) -> dict:
|
||||
if self.is_visible:
|
||||
return self._parse_answer(text_answer)
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
class Shrinkable(PromptElement, abc.ABC):
|
||||
@abc.abstractmethod
|
||||
def shrink(self) -> None:
|
||||
"""Implement shrinking of this prompt element.
|
||||
|
||||
You need to recursively call all shrinkable elements that are part of
|
||||
this prompt. You can also implement a shrinking strategy for this prompt.
|
||||
Shrinking is can be called multiple times to progressively shrink the
|
||||
prompt until it fits max_tokens. Default max shrink iterations is 20.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Truncater(Shrinkable):
|
||||
"""A prompt element that can be truncated to fit the context length of the LLM.
|
||||
Of course, it will be great that we never have to use the functionality here to `shrink()` the prompt.
|
||||
Extend this class for prompt elements that can be truncated. Usually long observations such as AxTree or HTML.
|
||||
"""
|
||||
|
||||
def __init__(self, visible, shrink_speed=0.3, start_truncate_iteration=10):
|
||||
super().__init__(visible=visible)
|
||||
self.shrink_speed = shrink_speed # the percentage shrunk in each iteration
|
||||
self.start_truncate_iteration = (
|
||||
start_truncate_iteration # the iteration to start truncating
|
||||
)
|
||||
self.shrink_calls = 0
|
||||
self.deleted_lines = 0
|
||||
|
||||
def shrink(self) -> None:
|
||||
if self.is_visible and self.shrink_calls >= self.start_truncate_iteration:
|
||||
# remove the fraction of _prompt
|
||||
lines = self._prompt.splitlines()
|
||||
new_line_count = int(len(lines) * (1 - self.shrink_speed))
|
||||
self.deleted_lines += len(lines) - new_line_count
|
||||
self._prompt = '\n'.join(lines[:new_line_count])
|
||||
self._prompt += (
|
||||
f'\n... Deleted {self.deleted_lines} lines to reduce prompt size.'
|
||||
)
|
||||
|
||||
self.shrink_calls += 1
|
||||
|
||||
|
||||
def fit_tokens(
|
||||
shrinkable: Shrinkable,
|
||||
max_prompt_chars=None,
|
||||
max_iterations=20,
|
||||
):
|
||||
"""Shrink a prompt element until it fits max_tokens.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
shrinkable : Shrinkable
|
||||
The prompt element to shrink.
|
||||
max_prompt_chars : int
|
||||
The maximum number of chars allowed.
|
||||
max_iterations : int, optional
|
||||
The maximum number of shrink iterations, by default 20.
|
||||
model_name : str, optional
|
||||
The name of the model used when tokenizing.
|
||||
|
||||
Returns:
|
||||
-------
|
||||
str : the prompt after shrinking.
|
||||
"""
|
||||
if max_prompt_chars is None:
|
||||
return shrinkable.prompt
|
||||
|
||||
for _ in range(max_iterations):
|
||||
prompt = shrinkable.prompt
|
||||
if isinstance(prompt, str):
|
||||
prompt_str = prompt
|
||||
elif isinstance(prompt, list):
|
||||
prompt_str = '\n'.join([p['text'] for p in prompt if p['type'] == 'text'])
|
||||
else:
|
||||
raise ValueError(f'Unrecognized type for prompt: {type(prompt)}')
|
||||
n_chars = len(prompt_str)
|
||||
if n_chars <= max_prompt_chars:
|
||||
return prompt
|
||||
shrinkable.shrink()
|
||||
|
||||
logging.info(
|
||||
dedent(
|
||||
f"""\
|
||||
After {max_iterations} shrink iterations, the prompt is still
|
||||
{len(prompt_str)} chars (greater than {max_prompt_chars}). Returning the prompt as is."""
|
||||
)
|
||||
)
|
||||
return prompt
|
||||
|
||||
|
||||
class HTML(Truncater):
|
||||
def __init__(self, html, visible: bool = True, prefix='') -> None:
|
||||
super().__init__(visible=visible, start_truncate_iteration=5)
|
||||
self._prompt = f'\n{prefix}HTML:\n{html}\n'
|
||||
|
||||
|
||||
class AXTree(Truncater):
|
||||
def __init__(
|
||||
self, ax_tree, visible: bool = True, coord_type=None, prefix=''
|
||||
) -> None:
|
||||
super().__init__(visible=visible, start_truncate_iteration=10)
|
||||
if coord_type == 'center':
|
||||
coord_note = """\
|
||||
Note: center coordinates are provided in parenthesis and are
|
||||
relative to the top left corner of the page.\n\n"""
|
||||
elif coord_type == 'box':
|
||||
coord_note = """\
|
||||
Note: bounding box of each object are provided in parenthesis and are
|
||||
relative to the top left corner of the page.\n\n"""
|
||||
else:
|
||||
coord_note = ''
|
||||
self._prompt = f'\n{prefix}AXTree:\n{coord_note}{ax_tree}\n'
|
||||
|
||||
|
||||
class Error(PromptElement):
|
||||
def __init__(self, error, visible: bool = True, prefix='') -> None:
|
||||
super().__init__(visible=visible)
|
||||
self._prompt = f'\n{prefix}Error from previous action:\n{error}\n'
|
||||
|
||||
|
||||
class Observation(Shrinkable):
|
||||
"""Observation of the current step.
|
||||
|
||||
Contains the html, the accessibility tree and the error logs.
|
||||
"""
|
||||
|
||||
def __init__(self, obs, flags: Flags) -> None:
|
||||
super().__init__()
|
||||
self.flags = flags
|
||||
self.obs = obs
|
||||
self.html = HTML(obs[flags.html_type], visible=flags.use_html, prefix='## ')
|
||||
self.ax_tree = AXTree(
|
||||
obs['axtree_txt'],
|
||||
visible=flags.use_ax_tree,
|
||||
coord_type=flags.extract_coords,
|
||||
prefix='## ',
|
||||
)
|
||||
self.error = Error(
|
||||
obs['last_action_error'],
|
||||
visible=flags.use_error_logs and obs['last_action_error'],
|
||||
prefix='## ',
|
||||
)
|
||||
|
||||
def shrink(self):
|
||||
self.ax_tree.shrink()
|
||||
self.html.shrink()
|
||||
|
||||
@property
|
||||
def _prompt(self) -> str: # type: ignore
|
||||
return f'\n# Observation of current step:\n{self.html.prompt}{self.ax_tree.prompt}{self.error.prompt}\n\n'
|
||||
|
||||
def add_screenshot(self, prompt):
|
||||
if self.flags.use_screenshot:
|
||||
if isinstance(prompt, str):
|
||||
prompt = [{'type': 'text', 'text': prompt}]
|
||||
img_url = BrowserEnv.image_to_jpg_base64_url(
|
||||
self.obs['screenshot'], add_data_prefix=True
|
||||
)
|
||||
prompt.append({'type': 'image_url', 'image_url': img_url})
|
||||
|
||||
return prompt
|
||||
|
||||
|
||||
class MacNote(PromptElement):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(visible=platform.system() == 'Darwin')
|
||||
self._prompt = '\nNote: you are on mac so you should use Meta instead of Control for Control+C etc.\n'
|
||||
|
||||
|
||||
class BeCautious(PromptElement):
|
||||
def __init__(self, visible: bool = True) -> None:
|
||||
super().__init__(visible=visible)
|
||||
self._prompt = """\
|
||||
\nBe very cautious. Avoid submitting anything before verifying the effect of your
|
||||
actions. Take the time to explore the effect of safe actions first. For example
|
||||
you can fill a few elements of a form, but don't click submit before verifying
|
||||
that everything was filled correctly.\n"""
|
||||
|
||||
|
||||
class GoalInstructions(PromptElement):
|
||||
def __init__(self, goal, visible: bool = True) -> None:
|
||||
super().__init__(visible)
|
||||
self._prompt = f"""\
|
||||
# Instructions
|
||||
Review the current state of the page and all other information to find the best
|
||||
possible next action to accomplish your goal. Your answer will be interpreted
|
||||
and executed by a program, make sure to follow the formatting instructions.
|
||||
|
||||
## Goal:
|
||||
{goal}
|
||||
"""
|
||||
|
||||
|
||||
class ChatInstructions(PromptElement):
|
||||
def __init__(self, chat_messages, visible: bool = True) -> None:
|
||||
super().__init__(visible)
|
||||
self._prompt = """\
|
||||
# Instructions
|
||||
|
||||
You are a UI Assistant, your goal is to help the user perform tasks using a web browser. You can
|
||||
communicate with the user via a chat, in which the user gives you instructions and in which you
|
||||
can send back messages. You have access to a web browser that both you and the user can see,
|
||||
and with which only you can interact via specific commands.
|
||||
|
||||
Review the instructions from the user, the current state of the page and all other information
|
||||
to find the best possible next action to accomplish your goal. Your answer will be interpreted
|
||||
and executed by a program, make sure to follow the formatting instructions.
|
||||
|
||||
## Chat messages:
|
||||
|
||||
"""
|
||||
self._prompt += '\n'.join(
|
||||
[
|
||||
f"""\
|
||||
- [{msg['role']}], {msg['message']}"""
|
||||
for msg in chat_messages
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class SystemPrompt(PromptElement):
|
||||
_prompt = """\
|
||||
You are an agent trying to solve a web task based on the content of the page and
|
||||
a user instructions. You can interact with the page and explore. Each time you
|
||||
submit an action it will be sent to the browser and you will receive a new page."""
|
||||
|
||||
|
||||
class MainPrompt(Shrinkable):
|
||||
def __init__(
|
||||
self,
|
||||
obs_history,
|
||||
actions,
|
||||
memories,
|
||||
thoughts,
|
||||
flags: Flags,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.flags = flags
|
||||
self.history = History(obs_history, actions, memories, thoughts, flags)
|
||||
if self.flags.enable_chat:
|
||||
self.instructions: Union[ChatInstructions, GoalInstructions] = (
|
||||
ChatInstructions(obs_history[-1]['chat_messages'])
|
||||
)
|
||||
else:
|
||||
if (
|
||||
'chat_messages' in obs_history[-1]
|
||||
and sum(
|
||||
[msg['role'] == 'user' for msg in obs_history[-1]['chat_messages']]
|
||||
)
|
||||
> 1
|
||||
):
|
||||
logging.warning(
|
||||
'Agent is in goal mode, but multiple user messages are present in the chat. Consider switching to `enable_chat=True`.'
|
||||
)
|
||||
self.instructions = GoalInstructions(obs_history[-1]['goal'])
|
||||
|
||||
self.obs = Observation(obs_history[-1], self.flags)
|
||||
self.action_space = ActionSpace(self.flags)
|
||||
|
||||
self.think = Think(visible=flags.use_thinking)
|
||||
self.memory = Memory(visible=flags.use_memory)
|
||||
|
||||
@property
|
||||
def _prompt(self) -> str: # type: ignore
|
||||
prompt = f"""\
|
||||
{self.instructions.prompt}\
|
||||
{self.obs.prompt}\
|
||||
{self.history.prompt}\
|
||||
{self.action_space.prompt}\
|
||||
{self.think.prompt}\
|
||||
{self.memory.prompt}\
|
||||
"""
|
||||
|
||||
if self.flags.use_abstract_example:
|
||||
prompt += f"""
|
||||
# Abstract Example
|
||||
|
||||
Here is an abstract version of the answer with description of the content of
|
||||
each tag. Make sure you follow this structure, but replace the content with your
|
||||
answer:
|
||||
{self.think.abstract_ex}\
|
||||
{self.memory.abstract_ex}\
|
||||
{self.action_space.abstract_ex}\
|
||||
"""
|
||||
|
||||
if self.flags.use_concrete_example:
|
||||
prompt += f"""
|
||||
# Concrete Example
|
||||
|
||||
Here is a concrete example of how to format your answer.
|
||||
Make sure to follow the template with proper tags:
|
||||
{self.think.concrete_ex}\
|
||||
{self.memory.concrete_ex}\
|
||||
{self.action_space.concrete_ex}\
|
||||
"""
|
||||
return self.obs.add_screenshot(prompt)
|
||||
|
||||
def shrink(self):
|
||||
self.history.shrink()
|
||||
self.obs.shrink()
|
||||
|
||||
def _parse_answer(self, text_answer):
|
||||
ans_dict = {}
|
||||
ans_dict.update(self.think._parse_answer(text_answer))
|
||||
ans_dict.update(self.memory._parse_answer(text_answer))
|
||||
ans_dict.update(self.action_space._parse_answer(text_answer))
|
||||
return ans_dict
|
||||
|
||||
|
||||
class ActionSpace(PromptElement):
|
||||
def __init__(self, flags: Flags) -> None:
|
||||
super().__init__()
|
||||
self.flags = flags
|
||||
self.action_space = _get_action_space(flags)
|
||||
|
||||
self._prompt = (
|
||||
f'# Action space:\n{self.action_space.describe()}{MacNote().prompt}\n'
|
||||
)
|
||||
self._abstract_ex = f"""
|
||||
<action>
|
||||
{self.action_space.example_action(abstract=True)}
|
||||
</action>
|
||||
"""
|
||||
self._concrete_ex = f"""
|
||||
<action>
|
||||
{self.action_space.example_action(abstract=False)}
|
||||
</action>
|
||||
"""
|
||||
|
||||
def _parse_answer(self, text_answer):
|
||||
ans_dict = parse_html_tags_raise(
|
||||
text_answer, keys=['action'], merge_multiple=True
|
||||
)
|
||||
|
||||
try:
|
||||
# just check if action can be mapped to python code but keep action as is
|
||||
# the environment will be responsible for mapping it to python
|
||||
self.action_space.to_python_code(ans_dict['action'])
|
||||
except Exception as e:
|
||||
raise ParseError(
|
||||
f'Error while parsing action\n: {e}\n'
|
||||
'Make sure your answer is restricted to the allowed actions.'
|
||||
)
|
||||
|
||||
return ans_dict
|
||||
|
||||
|
||||
def _get_action_space(flags: Flags) -> AbstractActionSet:
|
||||
match flags.action_space:
|
||||
case 'python':
|
||||
action_space = PythonActionSet(strict=flags.is_strict)
|
||||
if flags.multi_actions:
|
||||
warn(
|
||||
f'Flag action_space={repr(flags.action_space)} incompatible with multi_actions={repr(flags.multi_actions)}.',
|
||||
stacklevel=2,
|
||||
)
|
||||
if flags.demo_mode != 'off':
|
||||
warn(
|
||||
f'Flag action_space={repr(flags.action_space)} incompatible with demo_mode={repr(flags.demo_mode)}.',
|
||||
stacklevel=2,
|
||||
)
|
||||
return action_space
|
||||
case 'bid':
|
||||
action_subsets = ['chat', 'bid']
|
||||
case 'coord':
|
||||
action_subsets = ['chat', 'coord']
|
||||
case 'bid+coord':
|
||||
action_subsets = ['chat', 'bid', 'coord']
|
||||
case 'bid+nav':
|
||||
action_subsets = ['chat', 'bid', 'nav']
|
||||
case 'coord+nav':
|
||||
action_subsets = ['chat', 'coord', 'nav']
|
||||
case 'bid+coord+nav':
|
||||
action_subsets = ['chat', 'bid', 'coord', 'nav']
|
||||
case _:
|
||||
raise NotImplementedError(
|
||||
f'Unknown action_space {repr(flags.action_space)}'
|
||||
)
|
||||
|
||||
action_space = HighLevelActionSet(
|
||||
subsets=action_subsets,
|
||||
multiaction=flags.multi_actions,
|
||||
strict=flags.is_strict,
|
||||
demo_mode=flags.demo_mode,
|
||||
)
|
||||
|
||||
return action_space
|
||||
|
||||
|
||||
class Memory(PromptElement):
|
||||
_prompt = '' # provided in the abstract and concrete examples
|
||||
|
||||
_abstract_ex = """
|
||||
<memory>
|
||||
Write down anything you need to remember for next steps. You will be presented
|
||||
with the list of previous memories and past actions.
|
||||
</memory>
|
||||
"""
|
||||
|
||||
_concrete_ex = """
|
||||
<memory>
|
||||
I clicked on bid 32 to activate tab 2. The accessibility tree should mention
|
||||
focusable for elements of the form at next step.
|
||||
</memory>
|
||||
"""
|
||||
|
||||
def _parse_answer(self, text_answer):
|
||||
return parse_html_tags_raise(
|
||||
text_answer, optional_keys=['memory'], merge_multiple=True
|
||||
)
|
||||
|
||||
|
||||
class Think(PromptElement):
|
||||
_prompt = ''
|
||||
|
||||
_abstract_ex = """
|
||||
<think>
|
||||
Think step by step. If you need to make calculations such as coordinates, write them here. Describe the effect
|
||||
that your previous action had on the current content of the page.
|
||||
</think>
|
||||
"""
|
||||
_concrete_ex = """
|
||||
<think>
|
||||
My memory says that I filled the first name and last name, but I can't see any
|
||||
content in the form. I need to explore different ways to fill the form. Perhaps
|
||||
the form is not visible yet or some fields are disabled. I need to replan.
|
||||
</think>
|
||||
"""
|
||||
|
||||
def _parse_answer(self, text_answer):
|
||||
return parse_html_tags_raise(
|
||||
text_answer, optional_keys=['think'], merge_multiple=True
|
||||
)
|
||||
|
||||
|
||||
def diff(previous, new):
|
||||
"""Return a string showing the difference between original and new.
|
||||
|
||||
If the difference is above diff_threshold, return the diff string.
|
||||
"""
|
||||
if previous == new:
|
||||
return 'Identical', []
|
||||
|
||||
if len(previous) == 0 or previous is None:
|
||||
return 'previous is empty', []
|
||||
|
||||
diff_gen = difflib.ndiff(previous.splitlines(), new.splitlines())
|
||||
|
||||
diff_lines = []
|
||||
plus_count = 0
|
||||
minus_count = 0
|
||||
for line in diff_gen:
|
||||
if line.strip().startswith('+'):
|
||||
diff_lines.append(line)
|
||||
plus_count += 1
|
||||
elif line.strip().startswith('-'):
|
||||
diff_lines.append(line)
|
||||
minus_count += 1
|
||||
else:
|
||||
continue
|
||||
|
||||
header = f'{plus_count} lines added and {minus_count} lines removed:'
|
||||
|
||||
return header, diff_lines
|
||||
|
||||
|
||||
class Diff(Shrinkable):
|
||||
def __init__(
|
||||
self, previous, new, prefix='', max_line_diff=20, shrink_speed=2, visible=True
|
||||
) -> None:
|
||||
super().__init__(visible=visible)
|
||||
self.max_line_diff = max_line_diff
|
||||
self.header, self.diff_lines = diff(previous, new)
|
||||
self.shrink_speed = shrink_speed
|
||||
self.prefix = prefix
|
||||
|
||||
def shrink(self):
|
||||
self.max_line_diff -= self.shrink_speed
|
||||
self.max_line_diff = max(1, self.max_line_diff)
|
||||
|
||||
@property
|
||||
def _prompt(self) -> str: # type: ignore
|
||||
diff_str = '\n'.join(self.diff_lines[: self.max_line_diff])
|
||||
if len(self.diff_lines) > self.max_line_diff:
|
||||
original_count = len(self.diff_lines)
|
||||
diff_str = f'{diff_str}\nDiff truncated, {original_count - self.max_line_diff} changes now shown.'
|
||||
return f'{self.prefix}{self.header}\n{diff_str}\n'
|
||||
|
||||
|
||||
class HistoryStep(Shrinkable):
|
||||
def __init__(
|
||||
self, previous_obs, current_obs, action, memory, flags: Flags, shrink_speed=1
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.html_diff = Diff(
|
||||
previous_obs[flags.html_type],
|
||||
current_obs[flags.html_type],
|
||||
prefix='\n### HTML diff:\n',
|
||||
shrink_speed=shrink_speed,
|
||||
visible=lambda: flags.use_html and flags.use_diff,
|
||||
)
|
||||
self.ax_tree_diff = Diff(
|
||||
previous_obs['axtree_txt'],
|
||||
current_obs['axtree_txt'],
|
||||
prefix='\n### Accessibility tree diff:\n',
|
||||
shrink_speed=shrink_speed,
|
||||
visible=lambda: flags.use_ax_tree and flags.use_diff,
|
||||
)
|
||||
self.error = Error(
|
||||
current_obs['last_action_error'],
|
||||
visible=(
|
||||
flags.use_error_logs
|
||||
and current_obs['last_action_error']
|
||||
and flags.use_past_error_logs
|
||||
),
|
||||
prefix='### ',
|
||||
)
|
||||
self.shrink_speed = shrink_speed
|
||||
self.action = action
|
||||
self.memory = memory
|
||||
self.flags = flags
|
||||
|
||||
def shrink(self):
|
||||
super().shrink()
|
||||
self.html_diff.shrink()
|
||||
self.ax_tree_diff.shrink()
|
||||
|
||||
@property
|
||||
def _prompt(self) -> str: # type: ignore
|
||||
prompt = ''
|
||||
|
||||
if self.flags.use_action_history:
|
||||
prompt += f'\n### Action:\n{self.action}\n'
|
||||
|
||||
prompt += (
|
||||
f'{self.error.prompt}{self.html_diff.prompt}{self.ax_tree_diff.prompt}'
|
||||
)
|
||||
|
||||
if self.flags.use_memory and self.memory is not None:
|
||||
prompt += f'\n### Memory:\n{self.memory}\n'
|
||||
|
||||
return prompt
|
||||
|
||||
|
||||
class History(Shrinkable):
|
||||
def __init__(
|
||||
self, history_obs, actions, memories, thoughts, flags: Flags, shrink_speed=1
|
||||
) -> None:
|
||||
super().__init__(visible=flags.use_history)
|
||||
assert len(history_obs) == len(actions) + 1
|
||||
assert len(history_obs) == len(memories) + 1
|
||||
|
||||
self.shrink_speed = shrink_speed
|
||||
self.history_steps: list[HistoryStep] = []
|
||||
|
||||
for i in range(1, len(history_obs)):
|
||||
self.history_steps.append(
|
||||
HistoryStep(
|
||||
history_obs[i - 1],
|
||||
history_obs[i],
|
||||
actions[i - 1],
|
||||
memories[i - 1],
|
||||
flags,
|
||||
)
|
||||
)
|
||||
|
||||
def shrink(self):
|
||||
"""Shrink individual steps"""
|
||||
# TODO set the shrink speed of older steps to be higher
|
||||
super().shrink()
|
||||
for step in self.history_steps:
|
||||
step.shrink()
|
||||
|
||||
@property
|
||||
def _prompt(self):
|
||||
prompts = ['# History of interaction with the task:\n']
|
||||
for i, step in enumerate(self.history_steps):
|
||||
prompts.append(f'## step {i}')
|
||||
prompts.append(step.prompt)
|
||||
return '\n'.join(prompts) + '\n'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
html_template = """
|
||||
<html>
|
||||
<body>
|
||||
<div>
|
||||
Hello World.
|
||||
Step {}.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
OBS_HISTORY = [
|
||||
{
|
||||
'goal': 'do this and that',
|
||||
'pruned_html': html_template.format(1),
|
||||
'axtree_txt': '[1] Click me',
|
||||
'last_action_error': '',
|
||||
},
|
||||
{
|
||||
'goal': 'do this and that',
|
||||
'pruned_html': html_template.format(2),
|
||||
'axtree_txt': '[1] Click me',
|
||||
'last_action_error': '',
|
||||
},
|
||||
{
|
||||
'goal': 'do this and that',
|
||||
'pruned_html': html_template.format(3),
|
||||
'axtree_txt': '[1] Click me',
|
||||
'last_action_error': 'Hey, there is an error now',
|
||||
},
|
||||
]
|
||||
ACTIONS = ["click('41')", "click('42')"]
|
||||
MEMORIES = ['memory A', 'memory B']
|
||||
THOUGHTS = ['thought A', 'thought B']
|
||||
|
||||
flags = Flags(
|
||||
use_html=True,
|
||||
use_ax_tree=True,
|
||||
use_thinking=True,
|
||||
use_error_logs=True,
|
||||
use_past_error_logs=True,
|
||||
use_history=True,
|
||||
use_action_history=True,
|
||||
use_memory=True,
|
||||
use_diff=True,
|
||||
html_type='pruned_html',
|
||||
use_concrete_example=True,
|
||||
use_abstract_example=True,
|
||||
use_screenshot=False,
|
||||
multi_actions=True,
|
||||
)
|
||||
|
||||
print(
|
||||
MainPrompt(
|
||||
obs_history=OBS_HISTORY,
|
||||
actions=ACTIONS,
|
||||
memories=MEMORIES,
|
||||
thoughts=THOUGHTS,
|
||||
flags=flags,
|
||||
).prompt
|
||||
)
|
||||
@@ -1,88 +0,0 @@
|
||||
import ast
|
||||
|
||||
from openhands.controller.action_parser import ActionParser, ResponseParser
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
BrowseInteractiveAction,
|
||||
)
|
||||
|
||||
|
||||
class BrowsingResponseParser(ResponseParser):
|
||||
def __init__(self):
|
||||
# Need to pay attention to the item order in self.action_parsers
|
||||
super().__init__()
|
||||
self.action_parsers = [BrowsingActionParserMessage()]
|
||||
self.default_parser = BrowsingActionParserBrowseInteractive()
|
||||
|
||||
def parse(self, response: str) -> Action:
|
||||
action_str = self.parse_response(response)
|
||||
return self.parse_action(action_str)
|
||||
|
||||
def parse_response(self, response) -> str:
|
||||
action_str = response['choices'][0]['message']['content']
|
||||
if action_str is None:
|
||||
return ''
|
||||
action_str = action_str.strip()
|
||||
if action_str and not action_str.endswith('```'):
|
||||
action_str = action_str + ')```'
|
||||
logger.debug(action_str)
|
||||
return action_str
|
||||
|
||||
def parse_action(self, action_str: str) -> Action:
|
||||
for action_parser in self.action_parsers:
|
||||
if action_parser.check_condition(action_str):
|
||||
return action_parser.parse(action_str)
|
||||
return self.default_parser.parse(action_str)
|
||||
|
||||
|
||||
class BrowsingActionParserMessage(ActionParser):
|
||||
"""Parser action:
|
||||
- BrowseInteractiveAction(browser_actions) - unexpected response format, message back to user
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
pass
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
return '```' not in action_str
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
msg = f'send_msg_to_user("""{action_str}""")'
|
||||
return BrowseInteractiveAction(
|
||||
browser_actions=msg,
|
||||
thought=action_str,
|
||||
browsergym_send_msg_to_user=action_str,
|
||||
)
|
||||
|
||||
|
||||
class BrowsingActionParserBrowseInteractive(ActionParser):
|
||||
"""Parser action:
|
||||
- BrowseInteractiveAction(browser_actions) - handle send message to user function call in BrowserGym
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
pass
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
return True
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
thought = action_str.split('```')[0].strip()
|
||||
action_str = action_str.split('```')[1].strip()
|
||||
msg_content = ''
|
||||
for sub_action in action_str.split('\n'):
|
||||
if 'send_msg_to_user(' in sub_action:
|
||||
tree = ast.parse(sub_action)
|
||||
args = tree.body[0].value.args # type: ignore
|
||||
msg_content = args[0].value
|
||||
|
||||
return BrowseInteractiveAction(
|
||||
browser_actions=action_str,
|
||||
thought=thought,
|
||||
browsergym_send_msg_to_user=msg_content,
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
# CodeAct Agent Framework
|
||||
|
||||
This folder implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.01030), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents’ **act**ions into a unified **code** action space for both *simplicity* and *performance* (see paper for more details).
|
||||
|
||||
The conceptual idea is illustrated below. At each turn, the agent can:
|
||||
|
||||
1. **Converse**: Communicate with humans in natural language to ask for clarification, confirmation, etc.
|
||||
2. **CodeAct**: Choose to perform the task by executing code
|
||||
- Execute any valid Linux `bash` command
|
||||
- Execute any valid `Python` code with [an interactive Python interpreter](https://ipython.org/). This is simulated through `bash` command, see plugin system below for more details.
|
||||
|
||||

|
||||
|
||||
## Plugin System
|
||||
|
||||
To make the CodeAct agent more powerful with only access to `bash` action space, CodeAct agent leverages OpenHands's plugin system:
|
||||
- [Jupyter plugin](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/runtime/plugins/jupyter): for IPython execution via bash command
|
||||
- [Agent Skills plugin](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/runtime/plugins/agent_skills): Powerful bash command line tools for software development tasks introduced by [swe-agent](https://github.com/princeton-nlp/swe-agent).
|
||||
|
||||
## Demo
|
||||
|
||||
https://github.com/All-Hands-AI/OpenHands/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac
|
||||
|
||||
*Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)*
|
||||
|
||||
## Work-in-progress & Next step
|
||||
|
||||
[] Support web-browsing
|
||||
[] Complete the workflow for CodeAct agent to submit Github PRs
|
||||
@@ -1,177 +0,0 @@
|
||||
import re
|
||||
|
||||
from openhands.controller.action_parser import ActionParser, ResponseParser
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
AgentDelegateAction,
|
||||
AgentFinishAction,
|
||||
CmdRunAction,
|
||||
IPythonRunCellAction,
|
||||
MessageAction,
|
||||
)
|
||||
|
||||
|
||||
class CodeActResponseParser(ResponseParser):
|
||||
"""Parser action:
|
||||
- CmdRunAction(command) - bash command to run
|
||||
- IPythonRunCellAction(code) - IPython code to run
|
||||
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
|
||||
- MessageAction(content) - Message action to run (e.g. ask for clarification)
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Need pay attention to the item order in self.action_parsers
|
||||
super().__init__()
|
||||
self.action_parsers = [
|
||||
CodeActActionParserFinish(),
|
||||
CodeActActionParserCmdRun(),
|
||||
CodeActActionParserIPythonRunCell(),
|
||||
CodeActActionParserAgentDelegate(),
|
||||
]
|
||||
self.default_parser = CodeActActionParserMessage()
|
||||
|
||||
def parse(self, response) -> Action:
|
||||
action_str = self.parse_response(response)
|
||||
return self.parse_action(action_str)
|
||||
|
||||
def parse_response(self, response) -> str:
|
||||
action = response.choices[0].message.content
|
||||
if action is None:
|
||||
return ''
|
||||
for lang in ['bash', 'ipython', 'browse']:
|
||||
if f'<execute_{lang}>' in action and f'</execute_{lang}>' not in action:
|
||||
action += f'</execute_{lang}>'
|
||||
return action
|
||||
|
||||
def parse_action(self, action_str: str) -> Action:
|
||||
for action_parser in self.action_parsers:
|
||||
if action_parser.check_condition(action_str):
|
||||
return action_parser.parse(action_str)
|
||||
return self.default_parser.parse(action_str)
|
||||
|
||||
|
||||
class CodeActActionParserFinish(ActionParser):
|
||||
"""Parser action:
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.finish_command = None
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.finish_command = re.search(r'<finish>.*</finish>', action_str, re.DOTALL)
|
||||
return self.finish_command is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.finish_command is not None
|
||||
), 'self.finish_command should not be None when parse is called'
|
||||
thought = action_str.replace(self.finish_command.group(0), '').strip()
|
||||
return AgentFinishAction(thought=thought)
|
||||
|
||||
|
||||
class CodeActActionParserCmdRun(ActionParser):
|
||||
"""Parser action:
|
||||
- CmdRunAction(command) - bash command to run
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.bash_command = None
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.bash_command = re.search(
|
||||
r'<execute_bash>(.*?)</execute_bash>', action_str, re.DOTALL
|
||||
)
|
||||
return self.bash_command is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.bash_command is not None
|
||||
), 'self.bash_command should not be None when parse is called'
|
||||
thought = action_str.replace(self.bash_command.group(0), '').strip()
|
||||
# a command was found
|
||||
command_group = self.bash_command.group(1).strip()
|
||||
if command_group.strip() == 'exit':
|
||||
return AgentFinishAction(thought=thought)
|
||||
return CmdRunAction(command=command_group, thought=thought)
|
||||
|
||||
|
||||
class CodeActActionParserIPythonRunCell(ActionParser):
|
||||
"""Parser action:
|
||||
- IPythonRunCellAction(code) - IPython code to run
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.python_code = None
|
||||
self.jupyter_kernel_init_code: str = 'from agentskills import *'
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.python_code = re.search(
|
||||
r'<execute_ipython>(.*?)</execute_ipython>', action_str, re.DOTALL
|
||||
)
|
||||
return self.python_code is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.python_code is not None
|
||||
), 'self.python_code should not be None when parse is called'
|
||||
code_group = self.python_code.group(1).strip()
|
||||
thought = action_str.replace(self.python_code.group(0), '').strip()
|
||||
return IPythonRunCellAction(
|
||||
code=code_group,
|
||||
thought=thought,
|
||||
kernel_init_code=self.jupyter_kernel_init_code,
|
||||
)
|
||||
|
||||
|
||||
class CodeActActionParserAgentDelegate(ActionParser):
|
||||
"""Parser action:
|
||||
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.agent_delegate = None
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.agent_delegate = re.search(
|
||||
r'<execute_browse>(.*)</execute_browse>', action_str, re.DOTALL
|
||||
)
|
||||
return self.agent_delegate is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.agent_delegate is not None
|
||||
), 'self.agent_delegate should not be None when parse is called'
|
||||
thought = action_str.replace(self.agent_delegate.group(0), '').strip()
|
||||
browse_actions = self.agent_delegate.group(1).strip()
|
||||
task = f'{thought}. I should start with: {browse_actions}'
|
||||
return AgentDelegateAction(agent='BrowsingAgent', inputs={'task': task})
|
||||
|
||||
|
||||
class CodeActActionParserMessage(ActionParser):
|
||||
"""Parser action:
|
||||
- MessageAction(content) - Message action to run (e.g. ask for clarification)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
pass
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
# We assume the LLM is GOOD enough that when it returns pure natural language
|
||||
# it wants to talk to the user
|
||||
return True
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
return MessageAction(content=action_str, wait_for_response=True)
|
||||
@@ -1,291 +0,0 @@
|
||||
import os
|
||||
from itertools import islice
|
||||
|
||||
from agenthub.codeact_agent.action_parser import CodeActResponseParser
|
||||
from openhands.controller.agent import Agent
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import AgentConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.message import ImageContent, Message, TextContent
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
AgentDelegateAction,
|
||||
AgentFinishAction,
|
||||
CmdRunAction,
|
||||
IPythonRunCellAction,
|
||||
MessageAction,
|
||||
)
|
||||
from openhands.events.observation import (
|
||||
AgentDelegateObservation,
|
||||
CmdOutputObservation,
|
||||
IPythonRunCellObservation,
|
||||
UserRejectObservation,
|
||||
)
|
||||
from openhands.events.observation.error import ErrorObservation
|
||||
from openhands.events.observation.observation import Observation
|
||||
from openhands.events.serialization.event import truncate_content
|
||||
from openhands.llm.llm import LLM
|
||||
from openhands.runtime.plugins import (
|
||||
AgentSkillsRequirement,
|
||||
JupyterRequirement,
|
||||
PluginRequirement,
|
||||
)
|
||||
from openhands.utils.microagent import MicroAgent
|
||||
from openhands.utils.prompt import PromptManager
|
||||
|
||||
|
||||
class CodeActAgent(Agent):
|
||||
VERSION = '1.9'
|
||||
"""
|
||||
The Code Act Agent is a minimalist agent.
|
||||
The agent works by passing the model a list of action-observation pairs and prompting the model to take the next step.
|
||||
|
||||
### Overview
|
||||
|
||||
This agent implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.01030), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents’ **act**ions into a unified **code** action space for both *simplicity* and *performance* (see paper for more details).
|
||||
|
||||
The conceptual idea is illustrated below. At each turn, the agent can:
|
||||
|
||||
1. **Converse**: Communicate with humans in natural language to ask for clarification, confirmation, etc.
|
||||
2. **CodeAct**: Choose to perform the task by executing code
|
||||
- Execute any valid Linux `bash` command
|
||||
- Execute any valid `Python` code with [an interactive Python interpreter](https://ipython.org/). This is simulated through `bash` command, see plugin system below for more details.
|
||||
|
||||

|
||||
|
||||
"""
|
||||
|
||||
sandbox_plugins: list[PluginRequirement] = [
|
||||
# NOTE: AgentSkillsRequirement need to go before JupyterRequirement, since
|
||||
# AgentSkillsRequirement provides a lot of Python functions,
|
||||
# and it needs to be initialized before Jupyter for Jupyter to use those functions.
|
||||
AgentSkillsRequirement(),
|
||||
JupyterRequirement(),
|
||||
]
|
||||
|
||||
action_parser = CodeActResponseParser()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
llm: LLM,
|
||||
config: AgentConfig,
|
||||
) -> None:
|
||||
"""Initializes a new instance of the CodeActAgent class.
|
||||
|
||||
Parameters:
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
"""
|
||||
super().__init__(llm, config)
|
||||
self.reset()
|
||||
|
||||
self.micro_agent = (
|
||||
MicroAgent(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__), 'micro', f'{config.micro_agent_name}.md'
|
||||
)
|
||||
)
|
||||
if config.micro_agent_name
|
||||
else None
|
||||
)
|
||||
|
||||
self.prompt_manager = PromptManager(
|
||||
prompt_dir=os.path.join(os.path.dirname(__file__)),
|
||||
agent_skills_docs=AgentSkillsRequirement.documentation,
|
||||
micro_agent=self.micro_agent,
|
||||
)
|
||||
|
||||
def action_to_str(self, action: Action) -> str:
|
||||
if isinstance(action, CmdRunAction):
|
||||
return (
|
||||
f'{action.thought}\n<execute_bash>\n{action.command}\n</execute_bash>'
|
||||
)
|
||||
elif isinstance(action, IPythonRunCellAction):
|
||||
return f'{action.thought}\n<execute_ipython>\n{action.code}\n</execute_ipython>'
|
||||
elif isinstance(action, AgentDelegateAction):
|
||||
return f'{action.thought}\n<execute_browse>\n{action.inputs["task"]}\n</execute_browse>'
|
||||
elif isinstance(action, MessageAction):
|
||||
return action.content
|
||||
elif isinstance(action, AgentFinishAction) and action.source == 'agent':
|
||||
return action.thought
|
||||
return ''
|
||||
|
||||
def get_action_message(self, action: Action) -> Message | None:
|
||||
if (
|
||||
isinstance(action, AgentDelegateAction)
|
||||
or isinstance(action, CmdRunAction)
|
||||
or isinstance(action, IPythonRunCellAction)
|
||||
or isinstance(action, MessageAction)
|
||||
or (isinstance(action, AgentFinishAction) and action.source == 'agent')
|
||||
):
|
||||
content = [TextContent(text=self.action_to_str(action))]
|
||||
|
||||
if (
|
||||
self.llm.vision_is_active()
|
||||
and isinstance(action, MessageAction)
|
||||
and action.images_urls
|
||||
):
|
||||
content.append(ImageContent(image_urls=action.images_urls))
|
||||
|
||||
return Message(
|
||||
role='user' if action.source == 'user' else 'assistant', content=content
|
||||
)
|
||||
return None
|
||||
|
||||
def get_observation_message(self, obs: Observation) -> Message | None:
|
||||
max_message_chars = self.llm.config.max_message_chars
|
||||
obs_prefix = 'OBSERVATION:\n'
|
||||
if isinstance(obs, CmdOutputObservation):
|
||||
text = obs_prefix + truncate_content(obs.content, max_message_chars)
|
||||
text += (
|
||||
f'\n[Command {obs.command_id} finished with exit code {obs.exit_code}]'
|
||||
)
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
elif isinstance(obs, IPythonRunCellObservation):
|
||||
text = obs_prefix + obs.content
|
||||
# replace base64 images with a placeholder
|
||||
splitted = text.split('\n')
|
||||
for i, line in enumerate(splitted):
|
||||
if ' already displayed to user'
|
||||
)
|
||||
text = '\n'.join(splitted)
|
||||
text = truncate_content(text, max_message_chars)
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
elif isinstance(obs, AgentDelegateObservation):
|
||||
text = obs_prefix + truncate_content(str(obs.outputs), max_message_chars)
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
elif isinstance(obs, ErrorObservation):
|
||||
text = obs_prefix + truncate_content(obs.content, max_message_chars)
|
||||
text += '\n[Error occurred in processing last action]'
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
elif isinstance(obs, UserRejectObservation):
|
||||
text = 'OBSERVATION:\n' + truncate_content(obs.content, max_message_chars)
|
||||
text += '\n[Last action has been rejected by the user]'
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
else:
|
||||
# If an observation message is not returned, it will cause an error
|
||||
# when the LLM tries to return the next message
|
||||
raise ValueError(f'Unknown observation type: {type(obs)}')
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Resets the CodeAct Agent."""
|
||||
super().reset()
|
||||
|
||||
def step(self, state: State) -> Action:
|
||||
"""Performs one step using the CodeAct Agent.
|
||||
This includes gathering info on previous steps and prompting the model to make a command to execute.
|
||||
|
||||
Parameters:
|
||||
- state (State): used to get updated info
|
||||
|
||||
Returns:
|
||||
- CmdRunAction(command) - bash command to run
|
||||
- IPythonRunCellAction(code) - IPython code to run
|
||||
- AgentDelegateAction(agent, inputs) - delegate action for (sub)task
|
||||
- MessageAction(content) - Message action to run (e.g. ask for clarification)
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
# if we're done, go back
|
||||
latest_user_message = state.history.get_last_user_message()
|
||||
if latest_user_message and latest_user_message.strip() == '/exit':
|
||||
return AgentFinishAction()
|
||||
|
||||
# prepare what we want to send to the LLM
|
||||
messages = self._get_messages(state)
|
||||
params = {
|
||||
'messages': self.llm.format_messages_for_llm(messages),
|
||||
'stop': [
|
||||
'</execute_ipython>',
|
||||
'</execute_bash>',
|
||||
'</execute_browse>',
|
||||
],
|
||||
'temperature': 0.0,
|
||||
}
|
||||
|
||||
if self.llm.is_caching_prompt_active():
|
||||
params['extra_headers'] = {
|
||||
'anthropic-beta': 'prompt-caching-2024-07-31',
|
||||
}
|
||||
|
||||
try:
|
||||
response = self.llm.completion(**params)
|
||||
except Exception as e:
|
||||
logger.error(f'{e}')
|
||||
error_message = '{}: {}'.format(type(e).__name__, str(e).split('\n')[0])
|
||||
return AgentFinishAction(
|
||||
thought=f'Agent encountered an error while processing the last action.\nError: {error_message}\nPlease try again.'
|
||||
)
|
||||
|
||||
return self.action_parser.parse(response)
|
||||
|
||||
def _get_messages(self, state: State) -> list[Message]:
|
||||
messages: list[Message] = [
|
||||
Message(
|
||||
role='system',
|
||||
content=[
|
||||
TextContent(
|
||||
text=self.prompt_manager.system_message,
|
||||
cache_prompt=self.llm.is_caching_prompt_active(), # Cache system prompt
|
||||
)
|
||||
],
|
||||
),
|
||||
Message(
|
||||
role='user',
|
||||
content=[
|
||||
TextContent(
|
||||
text=self.prompt_manager.initial_user_message,
|
||||
cache_prompt=self.llm.is_caching_prompt_active(), # if the user asks the same query,
|
||||
)
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
for event in state.history.get_events():
|
||||
# create a regular message from an event
|
||||
if isinstance(event, Action):
|
||||
message = self.get_action_message(event)
|
||||
elif isinstance(event, Observation):
|
||||
message = self.get_observation_message(event)
|
||||
else:
|
||||
raise ValueError(f'Unknown event type: {type(event)}')
|
||||
|
||||
# add regular message
|
||||
if message:
|
||||
# handle error if the message is the SAME role as the previous message
|
||||
# litellm.exceptions.BadRequestError: litellm.BadRequestError: OpenAIException - Error code: 400 - {'detail': 'Only supports u/a/u/a/u...'}
|
||||
# there shouldn't be two consecutive messages from the same role
|
||||
if messages and messages[-1].role == message.role:
|
||||
messages[-1].content.extend(message.content)
|
||||
else:
|
||||
messages.append(message)
|
||||
|
||||
# Add caching to the last 2 user messages
|
||||
if self.llm.is_caching_prompt_active():
|
||||
user_turns_processed = 0
|
||||
for message in reversed(messages):
|
||||
if message.role == 'user' and user_turns_processed < 2:
|
||||
message.content[
|
||||
-1
|
||||
].cache_prompt = True # Last item inside the message content
|
||||
user_turns_processed += 1
|
||||
|
||||
# The latest user message is important:
|
||||
# we want to remind the agent of the environment constraints
|
||||
latest_user_message = next(
|
||||
islice(
|
||||
(
|
||||
m
|
||||
for m in reversed(messages)
|
||||
if m.role == 'user'
|
||||
and any(isinstance(c, TextContent) for c in m.content)
|
||||
),
|
||||
1,
|
||||
),
|
||||
None,
|
||||
)
|
||||
if latest_user_message:
|
||||
reminder_text = f'\n\nENVIRONMENT REMINDER: You have {state.max_iterations - state.iteration} turns left to complete the task. When finished reply with <finish></finish>.'
|
||||
latest_user_message.content.append(TextContent(text=reminder_text))
|
||||
|
||||
return messages
|
||||
@@ -1,69 +0,0 @@
|
||||
---
|
||||
name: github
|
||||
agent: CodeActAgent
|
||||
require_env_var:
|
||||
SANDBOX_ENV_GITHUB_TOKEN: "Create a GitHub Personal Access Token (https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) and set it as SANDBOX_GITHUB_TOKEN in your environment variables."
|
||||
---
|
||||
|
||||
# How to Interact with Github
|
||||
|
||||
## Environment Variable Available
|
||||
|
||||
- `GITHUB_TOKEN`: A read-only token for Github.
|
||||
|
||||
## Using GitHub's RESTful API
|
||||
|
||||
Use `curl` with the `GITHUB_TOKEN` to interact with GitHub's API. Here are some common operations:
|
||||
|
||||
Here's a template for API calls:
|
||||
|
||||
```sh
|
||||
curl -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/{endpoint}"
|
||||
```
|
||||
|
||||
First replace `{endpoint}` with the specific API path. Common operations:
|
||||
|
||||
1. View an issue or pull request:
|
||||
- Issues: `/repos/{owner}/{repo}/issues/{issue_number}`
|
||||
- Pull requests: `/repos/{owner}/{repo}/pulls/{pull_request_number}`
|
||||
|
||||
2. List repository issues or pull requests:
|
||||
- Issues: `/repos/{owner}/{repo}/issues`
|
||||
- Pull requests: `/repos/{owner}/{repo}/pulls`
|
||||
|
||||
3. Search issues or pull requests:
|
||||
- `/search/issues?q=repo:{owner}/{repo}+is:{type}+{search_term}+state:{state}`
|
||||
- Replace `{type}` with `issue` or `pr`
|
||||
|
||||
4. List repository branches:
|
||||
`/repos/{owner}/{repo}/branches`
|
||||
|
||||
5. Get commit details:
|
||||
`/repos/{owner}/{repo}/commits/{commit_sha}`
|
||||
|
||||
6. Get repository details:
|
||||
`/repos/{owner}/{repo}`
|
||||
|
||||
7. Get user information:
|
||||
`/user`
|
||||
|
||||
8. Search repositories:
|
||||
`/search/repositories?q={query}`
|
||||
|
||||
9. Get rate limit status:
|
||||
`/rate_limit`
|
||||
|
||||
Replace `{owner}`, `{repo}`, `{commit_sha}`, `{issue_number}`, `{pull_request_number}`,
|
||||
`{search_term}`, `{state}`, and `{query}` with appropriate values.
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. Always use the GitHub API for operations instead of a web browser.
|
||||
2. The `GITHUB_TOKEN` is read-only. Avoid operations that require write access.
|
||||
3. Git config (username and email) is pre-set. Do not modify.
|
||||
4. Edit and test code locally. Never push directly to remote.
|
||||
5. Verify correct branch before committing.
|
||||
6. Commit changes frequently.
|
||||
7. If the issue or task is ambiguous or lacks sufficient detail, always request clarification from the user before proceeding.
|
||||
8. You should avoid using command line tools like `sed` for file editing.
|
||||
@@ -1,52 +0,0 @@
|
||||
{% set MINIMAL_SYSTEM_PREFIX %}
|
||||
A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed answers to the user's questions.
|
||||
The assistant can use a Python environment with <execute_ipython>, e.g.:
|
||||
<execute_ipython>
|
||||
print("Hello World!")
|
||||
</execute_ipython>
|
||||
The assistant can execute bash commands wrapped with <execute_bash>, e.g. <execute_bash> ls </execute_bash>.
|
||||
If a bash command returns exit code `-1`, this means the process is not yet finished.
|
||||
The assistant must then send a second <execute_bash>. The second <execute_bash> can be empty
|
||||
(which will retrieve any additional logs), or it can contain text to be sent to STDIN of the running process,
|
||||
or it can contain the text `ctrl+c` to interrupt the process.
|
||||
|
||||
For commands that may run indefinitely, the output should be redirected to a file and the command run
|
||||
in the background, e.g. <execute_bash> python3 app.py > server.log 2>&1 & </execute_bash>
|
||||
If a command execution result says "Command timed out. Sending SIGINT to the process",
|
||||
the assistant should retry running the command in the background.
|
||||
{% endset %}
|
||||
{% set BROWSING_PREFIX %}
|
||||
The assistant can browse the Internet with <execute_browse> and </execute_browse>.
|
||||
For example, <execute_browse> Tell me the usa's president using google search </execute_browse>.
|
||||
Or <execute_browse> Tell me what is in http://example.com </execute_browse>.
|
||||
{% endset %}
|
||||
{% set PIP_INSTALL_PREFIX %}
|
||||
The assistant can install Python packages using the %pip magic command in an IPython environment by using the following syntax: <execute_ipython> %pip install [package needed] </execute_ipython> and should always import packages and define variables before starting to use them.
|
||||
{% endset %}
|
||||
{% set SYSTEM_PREFIX = MINIMAL_SYSTEM_PREFIX + BROWSING_PREFIX + PIP_INSTALL_PREFIX %}
|
||||
{% set COMMAND_DOCS %}
|
||||
Apart from the standard Python library, the assistant can also use the following functions (already imported) in <execute_ipython> environment:
|
||||
{{ agent_skills_docs }}
|
||||
IMPORTANT:
|
||||
- `open_file` only returns the first 100 lines of the file by default! The assistant MUST use `scroll_down` repeatedly to read the full file BEFORE making edits!
|
||||
- The assistant shall adhere to THE `edit_file_by_replace`, `append_file` and `insert_content_at_line` FUNCTIONS REQUIRING PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write the line out, with all leading spaces before the code!
|
||||
- Indentation is important and code that is not indented correctly will fail and require fixing before it can be run.
|
||||
- Any code issued should be less than 50 lines to avoid context being cut off!
|
||||
- After EVERY `create_file` the method `append_file` shall be used to write the FIRST content!
|
||||
- For `edit_file_by_replace` NEVER provide empty parameters!
|
||||
- For `edit_file_by_replace` the file must be read fully before any replacements!
|
||||
{% endset %}
|
||||
{% set SYSTEM_SUFFIX %}
|
||||
Responses should be concise.
|
||||
The assistant should attempt fewer things at a time instead of putting too many commands OR too much code in one "execute" block.
|
||||
Include ONLY ONE <execute_ipython>, <execute_bash>, or <execute_browse> per response, unless the assistant is finished with the task or needs more input or action from the user in order to proceed.
|
||||
If the assistant is finished with the task you MUST include <finish></finish> in your response.
|
||||
IMPORTANT: Execute code using <execute_ipython>, <execute_bash>, or <execute_browse> whenever possible.
|
||||
The assistant should utilize full file paths and the `pwd` command to prevent path-related errors.
|
||||
The assistant must avoid apologies and thanks in its responses.
|
||||
|
||||
{% endset %}
|
||||
{# Combine all parts without newlines between them #}
|
||||
{{ SYSTEM_PREFIX -}}
|
||||
{{- COMMAND_DOCS -}}
|
||||
{{- SYSTEM_SUFFIX }}
|
||||
@@ -1,231 +0,0 @@
|
||||
{% set DEFAULT_EXAMPLE %}
|
||||
--- START OF EXAMPLE ---
|
||||
|
||||
USER: Create a list of numbers from 1 to 10, and display them in a web page at port 5000.
|
||||
|
||||
ASSISTANT:
|
||||
Sure! Let me create a Python file `app.py`:
|
||||
<execute_ipython>
|
||||
create_file('app.py')
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[File: /workspace/app.py (1 lines total)]
|
||||
(this is the beginning of the file)
|
||||
1|
|
||||
(this is the end of the file)
|
||||
[File app.py created.]
|
||||
|
||||
ASSISTANT:
|
||||
Now I will write the Python code for starting a web server and save it to the file `app.py`:
|
||||
<execute_ipython>
|
||||
EDITED_CODE="""from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
numbers = list(range(1, 11))
|
||||
return str(numbers)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(port=5000)"""
|
||||
|
||||
insert_content_at_line(
|
||||
'app.py',
|
||||
1,
|
||||
EDITED_CODE,
|
||||
)
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
(this is the beginning of the file)
|
||||
1|from flask import Flask
|
||||
2|app = Flask(__name__)
|
||||
3|
|
||||
4|@app.route('/')
|
||||
5|def index():
|
||||
6| numbers = list(range(1, 11))
|
||||
7| return str(numbers)
|
||||
8|
|
||||
9|if __name__ == '__main__':
|
||||
10| app.run(port=5000)
|
||||
(this is the end of the file)
|
||||
[File updated (edited at line 1). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]
|
||||
|
||||
ASSISTANT:
|
||||
I have created a Python file `app.py` that will display a list of numbers from 1 to 10 when you run it. Let me run the Python file for you:
|
||||
<execute_bash>
|
||||
python3 app.py > server.log 2>&1 &
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[1] 121[1]+ Exit 1 python3 app.py > server.log 2>&1
|
||||
|
||||
ASSISTANT: Looks like the server is running with PID 121 then crashed. Let me check the server log:
|
||||
<execute_bash>
|
||||
cat server.log
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
Traceback (most recent call last):
|
||||
File "/workspace/app.py", line 2, in <module>
|
||||
from flask import Flask
|
||||
ModuleNotFoundError: No module named 'flask'
|
||||
|
||||
ASSISTANT:
|
||||
It seems that Flask is not installed. Let me install Flask for you:
|
||||
<execute_bash>
|
||||
pip install flask
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
Defaulting to user installation because normal site-packages is not writeable
|
||||
Collecting flask
|
||||
Using cached flask-3.0.3-py3-none-any.whl (101 kB)
|
||||
Collecting blinker>=1.6.2
|
||||
Using cached blinker-1.7.0-py3-none-any.whl (13 kB)
|
||||
Collecting Werkzeug>=3.0.0
|
||||
Using cached werkzeug-3.0.2-py3-none-any.whl (226 kB)
|
||||
Collecting click>=8.1.3
|
||||
Using cached click-8.1.7-py3-none-any.whl (97 kB)
|
||||
Collecting itsdangerous>=2.1.2
|
||||
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
|
||||
Requirement already satisfied: Jinja2>=3.1.2 in /home/openhands/.local/lib/python3.10/site-packages (from flask) (3.1.3)
|
||||
Requirement already satisfied: MarkupSafe>=2.0 in /home/openhands/.local/lib/python3.10/site-packages (from Jinja2>=3.1.2->flask) (2.1.5)
|
||||
Installing collected packages: Werkzeug, itsdangerous, click, blinker, flask
|
||||
Successfully installed Werkzeug-3.0.2 blinker-1.7.0 click-8.1.7 flask-3.0.3 itsdangerous-2.2.0
|
||||
|
||||
ASSISTANT:
|
||||
Now that Flask is installed, let me run the Python file again:
|
||||
<execute_bash>
|
||||
python3 app.py > server.log 2>&1 &
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[1] 124
|
||||
|
||||
ASSISTANT:
|
||||
Let me check the server log again:
|
||||
<execute_bash>
|
||||
cat server.log
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
* Serving Flask app 'app'
|
||||
* Debug mode: off
|
||||
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
|
||||
* Running on http://127.0.0.1:5000
|
||||
Press CTRL+C to quit
|
||||
|
||||
ASSISTANT:
|
||||
The server is running on port 5000 with PID 124. You can access the list of numbers by visiting http://127.0.0.1:5000. If you have any further questions, feel free to ask!
|
||||
|
||||
USER: Now browse the newly started server's homepage and show me the content.
|
||||
|
||||
ASSISTANT:
|
||||
Sure! Let me browse the server's homepage at http://127.0.0.1:5000:
|
||||
<execute_browse>
|
||||
Get the content on "http://127.0.0.1:5000"
|
||||
</execute_browse>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
ASSISTANT:
|
||||
The content of the server's homepage is "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]". If you have any further questions, feel free to ask!
|
||||
|
||||
USER: Now kill the server, make it display the numbers in a table format.
|
||||
|
||||
ASSISTANT:
|
||||
Sure! Let me stop the server first:
|
||||
<execute_bash>
|
||||
kill 124
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[1]+ Terminated python3 app.py > server.log 2>&1
|
||||
|
||||
ASSISTANT:
|
||||
The server has been stopped. Let me open the Python file and modify it.
|
||||
<execute_ipython>
|
||||
open_file('app.py')
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
[File: /workspace/app.py (10 lines total)]
|
||||
(this is the beginning of the file)
|
||||
1|from flask import Flask
|
||||
2|app = Flask(__name__)
|
||||
3|
|
||||
4|@app.route('/')
|
||||
5|def index():
|
||||
6| numbers = list(range(1, 11))
|
||||
7| return str(numbers)
|
||||
8|
|
||||
9|if __name__ == '__main__':
|
||||
10| app.run(port=5000)
|
||||
(this is the end of the file)
|
||||
|
||||
ASSISTANT:
|
||||
I should edit the file to display the numbers in a table format. I should include correct indentation. Let me update the file:
|
||||
<execute_ipython>
|
||||
edit_file_by_replace(
|
||||
'app.py',
|
||||
to_replace=" return str(numbers)",
|
||||
new_content=" return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'",
|
||||
)
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
Observation:
|
||||
[File: /workspace/app.py (10 lines total after edit)]
|
||||
(this is the beginning of the file)
|
||||
1|from flask import Flask
|
||||
2|app = Flask(__name__)
|
||||
3|
|
||||
4|@app.route('/')
|
||||
5|def index():
|
||||
6| numbers = list(range(1, 11))
|
||||
7| return '<table>' + ''.join([f'<tr><td>{i}</td></tr>' for i in numbers]) + '</table>'
|
||||
8|
|
||||
9|if __name__ == '__main__':
|
||||
10| app.run(port=5000)
|
||||
(this is the end of the file)
|
||||
[File updated (edited at line 7). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]
|
||||
|
||||
ASSISTANT:
|
||||
Running the updated file:
|
||||
<execute_bash>
|
||||
python3 app.py > server.log 2>&1 &
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
Observation:
|
||||
[1] 126
|
||||
|
||||
ASSISTANT:
|
||||
The server is running on port 5000 with PID 126. You can access the list of numbers in a table format by visiting http://127.0.0.1:5000. Let me know if you have any further requests!
|
||||
<finish></finish>
|
||||
|
||||
--- END OF EXAMPLE ---
|
||||
{% endset %}
|
||||
Here is an example of how you can interact with the environment for task solving:
|
||||
{{ DEFAULT_EXAMPLE }}
|
||||
{% if micro_agent %}
|
||||
--- BEGIN OF GUIDELINE ---
|
||||
The following information may assist you in completing your task:
|
||||
|
||||
{{ micro_agent }}
|
||||
--- END OF GUIDELINE ---
|
||||
{% endif %}
|
||||
|
||||
NOW, LET'S START!
|
||||
@@ -1,7 +0,0 @@
|
||||
# CodeAct (SWE Edit Specialized)
|
||||
|
||||
This agent is an adaptation of the original [SWE Agent](https://swe-agent.com/) based on CodeAct using the `agentskills` library of OpenHands.
|
||||
|
||||
Its intended use is **solving GitHub issues**.
|
||||
|
||||
It removes web-browsing and GitHub capability from the original CodeAct agent to avoid confusion to the agent.
|
||||
@@ -1,4 +0,0 @@
|
||||
from agenthub.codeact_swe_agent.codeact_swe_agent import CodeActSWEAgent
|
||||
from openhands.controller.agent import Agent
|
||||
|
||||
Agent.register('CodeActSWEAgent', CodeActSWEAgent)
|
||||
@@ -1,110 +0,0 @@
|
||||
import re
|
||||
|
||||
from openhands.controller.action_parser import ActionParser
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
AgentFinishAction,
|
||||
CmdRunAction,
|
||||
IPythonRunCellAction,
|
||||
MessageAction,
|
||||
)
|
||||
|
||||
|
||||
class CodeActSWEActionParserFinish(ActionParser):
|
||||
"""Parser action:
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.finish_command = None
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.finish_command = re.search(r'<finish>.*</finish>', action_str, re.DOTALL)
|
||||
return self.finish_command is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.finish_command is not None
|
||||
), 'self.finish_command should not be None when parse is called'
|
||||
thought = action_str.replace(self.finish_command.group(0), '').strip()
|
||||
return AgentFinishAction(thought=thought)
|
||||
|
||||
|
||||
class CodeActSWEActionParserCmdRun(ActionParser):
|
||||
"""Parser action:
|
||||
- CmdRunAction(command) - bash command to run
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.bash_command = None
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.bash_command = re.search(
|
||||
r'<execute_bash>(.*?)</execute_bash>', action_str, re.DOTALL
|
||||
)
|
||||
return self.bash_command is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.bash_command is not None
|
||||
), 'self.bash_command should not be None when parse is called'
|
||||
thought = action_str.replace(self.bash_command.group(0), '').strip()
|
||||
# a command was found
|
||||
command_group = self.bash_command.group(1).strip()
|
||||
if command_group.strip() == 'exit':
|
||||
return AgentFinishAction()
|
||||
return CmdRunAction(command=command_group, thought=thought)
|
||||
|
||||
|
||||
class CodeActSWEActionParserIPythonRunCell(ActionParser):
|
||||
"""Parser action:
|
||||
- IPythonRunCellAction(code) - IPython code to run
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
self.python_code = None
|
||||
self.jupyter_kernel_init_code: str = 'from agentskills import *'
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
self.python_code = re.search(
|
||||
r'<execute_ipython>(.*?)</execute_ipython>', action_str, re.DOTALL
|
||||
)
|
||||
return self.python_code is not None
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
assert (
|
||||
self.python_code is not None
|
||||
), 'self.python_code should not be None when parse is called'
|
||||
code_group = self.python_code.group(1).strip()
|
||||
thought = action_str.replace(self.python_code.group(0), '').strip()
|
||||
return IPythonRunCellAction(
|
||||
code=code_group,
|
||||
thought=thought,
|
||||
kernel_init_code=self.jupyter_kernel_init_code,
|
||||
)
|
||||
|
||||
|
||||
class CodeActSWEActionParserMessage(ActionParser):
|
||||
"""Parser action:
|
||||
- MessageAction(content) - Message action to run (e.g. ask for clarification)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
):
|
||||
pass
|
||||
|
||||
def check_condition(self, action_str: str) -> bool:
|
||||
# We assume the LLM is GOOD enough that when it returns pure natural language
|
||||
# it wants to talk to the user
|
||||
return True
|
||||
|
||||
def parse(self, action_str: str) -> Action:
|
||||
return MessageAction(content=action_str, wait_for_response=True)
|
||||
@@ -1,225 +0,0 @@
|
||||
from agenthub.codeact_swe_agent.prompt import (
|
||||
COMMAND_DOCS,
|
||||
SWE_EXAMPLE,
|
||||
SYSTEM_PREFIX,
|
||||
SYSTEM_SUFFIX,
|
||||
)
|
||||
from agenthub.codeact_swe_agent.response_parser import CodeActSWEResponseParser
|
||||
from openhands.controller.agent import Agent
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import AgentConfig
|
||||
from openhands.core.message import ImageContent, Message, TextContent
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
AgentFinishAction,
|
||||
CmdRunAction,
|
||||
IPythonRunCellAction,
|
||||
MessageAction,
|
||||
)
|
||||
from openhands.events.observation import (
|
||||
CmdOutputObservation,
|
||||
IPythonRunCellObservation,
|
||||
)
|
||||
from openhands.events.observation.error import ErrorObservation
|
||||
from openhands.events.observation.observation import Observation
|
||||
from openhands.events.serialization.event import truncate_content
|
||||
from openhands.llm.llm import LLM
|
||||
from openhands.runtime.plugins import (
|
||||
AgentSkillsRequirement,
|
||||
JupyterRequirement,
|
||||
PluginRequirement,
|
||||
)
|
||||
|
||||
|
||||
def get_system_message() -> str:
|
||||
return f'{SYSTEM_PREFIX}\n\n{COMMAND_DOCS}\n\n{SYSTEM_SUFFIX}'
|
||||
|
||||
|
||||
def get_in_context_example() -> str:
|
||||
return SWE_EXAMPLE
|
||||
|
||||
|
||||
class CodeActSWEAgent(Agent):
|
||||
VERSION = '1.6'
|
||||
"""
|
||||
This agent is an adaptation of the original [SWE Agent](https://swe-agent.com/) based on CodeAct 1.5 using the `agentskills` library of OpenHands.
|
||||
|
||||
It is intended use is **solving Github issues**.
|
||||
|
||||
It removes web-browsing and Github capability from the original CodeAct agent to avoid confusion to the agent.
|
||||
"""
|
||||
|
||||
sandbox_plugins: list[PluginRequirement] = [
|
||||
# NOTE: AgentSkillsRequirement need to go before JupyterRequirement, since
|
||||
# AgentSkillsRequirement provides a lot of Python functions,
|
||||
# and it needs to be initialized before Jupyter for Jupyter to use those functions.
|
||||
AgentSkillsRequirement(),
|
||||
JupyterRequirement(),
|
||||
]
|
||||
|
||||
system_message: str = get_system_message()
|
||||
in_context_example: str = f"Here is an example of how you can interact with the environment for task solving:\n{get_in_context_example()}\n\nNOW, LET'S START!"
|
||||
|
||||
response_parser = CodeActSWEResponseParser()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
llm: LLM,
|
||||
config: AgentConfig,
|
||||
) -> None:
|
||||
"""Initializes a new instance of the CodeActSWEAgent class.
|
||||
|
||||
Parameters:
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
"""
|
||||
super().__init__(llm, config)
|
||||
self.reset()
|
||||
|
||||
def action_to_str(self, action: Action) -> str:
|
||||
if isinstance(action, CmdRunAction):
|
||||
return (
|
||||
f'{action.thought}\n<execute_bash>\n{action.command}\n</execute_bash>'
|
||||
)
|
||||
elif isinstance(action, IPythonRunCellAction):
|
||||
return f'{action.thought}\n<execute_ipython>\n{action.code}\n</execute_ipython>'
|
||||
elif isinstance(action, MessageAction):
|
||||
return action.content
|
||||
return ''
|
||||
|
||||
def get_action_message(self, action: Action) -> Message | None:
|
||||
if (
|
||||
isinstance(action, CmdRunAction)
|
||||
or isinstance(action, IPythonRunCellAction)
|
||||
or isinstance(action, MessageAction)
|
||||
):
|
||||
content = [TextContent(text=self.action_to_str(action))]
|
||||
|
||||
if (
|
||||
self.llm.vision_is_active()
|
||||
and isinstance(action, MessageAction)
|
||||
and action.images_urls
|
||||
):
|
||||
content.append(ImageContent(image_urls=action.images_urls))
|
||||
|
||||
return Message(
|
||||
role='user' if action.source == 'user' else 'assistant', content=content
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def get_observation_message(self, obs: Observation) -> Message | None:
|
||||
max_message_chars = self.llm.config.max_message_chars
|
||||
if isinstance(obs, CmdOutputObservation):
|
||||
text = 'OBSERVATION:\n' + truncate_content(obs.content, max_message_chars)
|
||||
text += (
|
||||
f'\n[Command {obs.command_id} finished with exit code {obs.exit_code}]'
|
||||
)
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
elif isinstance(obs, IPythonRunCellObservation):
|
||||
text = 'OBSERVATION:\n' + obs.content
|
||||
# replace base64 images with a placeholder
|
||||
splitted = text.split('\n')
|
||||
for i, line in enumerate(splitted):
|
||||
if ' already displayed to user'
|
||||
)
|
||||
text = '\n'.join(splitted)
|
||||
text = truncate_content(text, max_message_chars)
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
elif isinstance(obs, ErrorObservation):
|
||||
text = 'OBSERVATION:\n' + truncate_content(obs.content, max_message_chars)
|
||||
text += '\n[Error occurred in processing last action]'
|
||||
return Message(role='user', content=[TextContent(text=text)])
|
||||
else:
|
||||
# If an observation message is not returned, it will cause an error
|
||||
# when the LLM tries to return the next message
|
||||
raise ValueError(f'Unknown observation type: {type(obs)}')
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Resets the CodeAct Agent."""
|
||||
super().reset()
|
||||
|
||||
def step(self, state: State) -> Action:
|
||||
"""Performs one step using the CodeAct Agent.
|
||||
This includes gathering info on previous steps and prompting the model to make a command to execute.
|
||||
|
||||
Parameters:
|
||||
- state (State): used to get updated info and background commands
|
||||
|
||||
Returns:
|
||||
- CmdRunAction(command) - bash command to run
|
||||
- IPythonRunCellAction(code) - IPython code to run
|
||||
- MessageAction(content) - Message action to run (e.g. ask for clarification)
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
# if we're done, go back
|
||||
latest_user_message = state.history.get_last_user_message()
|
||||
if latest_user_message and latest_user_message.strip() == '/exit':
|
||||
return AgentFinishAction()
|
||||
|
||||
# prepare what we want to send to the LLM
|
||||
messages: list[Message] = self._get_messages(state)
|
||||
response = self.llm.completion(
|
||||
messages=self.llm.format_messages_for_llm(messages),
|
||||
stop=[
|
||||
'</execute_ipython>',
|
||||
'</execute_bash>',
|
||||
],
|
||||
temperature=0.0,
|
||||
)
|
||||
|
||||
return self.response_parser.parse(response)
|
||||
|
||||
def _get_messages(self, state: State) -> list[Message]:
|
||||
messages: list[Message] = [
|
||||
Message(role='system', content=[TextContent(text=self.system_message)]),
|
||||
Message(role='user', content=[TextContent(text=self.in_context_example)]),
|
||||
]
|
||||
|
||||
for event in state.history.get_events():
|
||||
# create a regular message from an event
|
||||
if isinstance(event, Action):
|
||||
message = self.get_action_message(event)
|
||||
elif isinstance(event, Observation):
|
||||
message = self.get_observation_message(event)
|
||||
else:
|
||||
raise ValueError(f'Unknown event type: {type(event)}')
|
||||
|
||||
# add regular message
|
||||
if message:
|
||||
# handle error if the message is the SAME role as the previous message
|
||||
# litellm.exceptions.BadRequestError: litellm.BadRequestError: OpenAIException - Error code: 400 - {'detail': 'Only supports u/a/u/a/u...'}
|
||||
# there should not have two consecutive messages from the same role
|
||||
if messages and messages[-1].role == message.role:
|
||||
messages[-1].content.extend(message.content)
|
||||
else:
|
||||
messages.append(message)
|
||||
|
||||
# the latest user message is important:
|
||||
# we want to remind the agent of the environment constraints
|
||||
latest_user_message = next(
|
||||
(m for m in reversed(messages) if m.role == 'user'), None
|
||||
)
|
||||
|
||||
# Get the last user text inside content
|
||||
if latest_user_message:
|
||||
latest_user_message_text = next(
|
||||
(
|
||||
t
|
||||
for t in reversed(latest_user_message.content)
|
||||
if isinstance(t, TextContent)
|
||||
)
|
||||
)
|
||||
# add a reminder to the prompt
|
||||
reminder_text = f'\n\nENVIRONMENT REMINDER: You have {state.max_iterations - state.iteration} turns left to complete the task. When finished reply with <finish></finish>.'
|
||||
|
||||
if latest_user_message_text:
|
||||
latest_user_message_text.text = (
|
||||
latest_user_message_text.text + reminder_text
|
||||
)
|
||||
else:
|
||||
latest_user_message_text = TextContent(text=reminder_text)
|
||||
latest_user_message.content.append(latest_user_message_text)
|
||||
|
||||
return messages
|
||||
@@ -1,455 +0,0 @@
|
||||
from openhands.runtime.plugins import AgentSkillsRequirement
|
||||
|
||||
_AGENT_SKILLS_DOCS = AgentSkillsRequirement.documentation
|
||||
|
||||
COMMAND_DOCS = (
|
||||
'\nApart from the standard Python library, the assistant can also use the following functions (already imported) in <execute_ipython> environment:\n'
|
||||
f'{_AGENT_SKILLS_DOCS}'
|
||||
"Please note that THE `edit_file` FUNCTION REQUIRES PROPER INDENTATION. If the assistant would like to add the line ' print(x)', it must fully write that out, with all those spaces before the code! Indentation is important and code that is not indented correctly will fail and require fixing before it can be run."
|
||||
)
|
||||
|
||||
# ======= SYSTEM MESSAGE =======
|
||||
MINIMAL_SYSTEM_PREFIX = """A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.
|
||||
The assistant can interact with an interactive Python (Jupyter Notebook) environment and receive the corresponding output when needed. The code should be enclosed using "<execute_ipython>" tag, for example:
|
||||
<execute_ipython>
|
||||
print("Hello World!")
|
||||
</execute_ipython>
|
||||
The assistant can execute bash commands on behalf of the user by wrapping them with <execute_bash> and </execute_bash>.
|
||||
For example, you can list the files in the current directory by <execute_bash> ls </execute_bash>.
|
||||
"""
|
||||
|
||||
PIP_INSTALL_PREFIX = """The assistant can install Python packages using the %pip magic command in an IPython environment by using the following syntax: <execute_ipython> %pip install [package needed] </execute_ipython> and should always import packages and define variables before starting to use them."""
|
||||
|
||||
SYSTEM_PREFIX = MINIMAL_SYSTEM_PREFIX + PIP_INSTALL_PREFIX
|
||||
|
||||
SYSTEM_SUFFIX = """The assistant's response should be concise.
|
||||
The assistant should include ONLY ONE <execute_ipython> or <execute_bash> in every one of the responses, unless the assistant is finished with the task or need more input or action from the user in order to proceed.
|
||||
IMPORTANT: Whenever possible, execute the code for the user using <execute_ipython> or <execute_bash> instead of providing it.
|
||||
"""
|
||||
|
||||
SWE_EXAMPLE = """
|
||||
--- START OF EXAMPLE ---
|
||||
|
||||
USER: We are currently solving the following issue within our repository. Here is the issue text:
|
||||
--- BEGIN ISSUE ---
|
||||
TimeDelta serialization precision
|
||||
Hi there!
|
||||
|
||||
I just found quite strange behaviour of `TimeDelta` field serialization
|
||||
|
||||
```python3
|
||||
from marshmallow.fields import TimeDelta
|
||||
from datetime import timedelta
|
||||
|
||||
td_field = TimeDelta(precision="milliseconds")
|
||||
|
||||
obj = dict()
|
||||
obj["td_field"] = timedelta(milliseconds=345)
|
||||
|
||||
print(td_field.serialize("td_field", obj))
|
||||
```
|
||||
|
||||
Output of this snippet is `344`, but it seems that `345` is correct.
|
||||
|
||||
Looks like a rounding issue here: https://github.com/marshmallow-code/marshmallow/blob/dev/src/marshmallow/fields.py#L1474
|
||||
--- END ISSUE ---
|
||||
|
||||
[Current directory: /workspace/marshmallow-code__marshmallow]
|
||||
|
||||
ASSISTANT:
|
||||
Let's first start by reproducing the results of the issue. The issue includes some example code for reproduction, which we can use. We'll create a new file called `reproduce.py` and paste the example code into it.
|
||||
<execute_ipython>
|
||||
create_file('/workspace/marshmallow-code__marshmallow/reproduce.py')
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[File: /workspace/marshmallow-code__marshmallow/reproduce.py (1 lines total)]
|
||||
1|
|
||||
[File /workspace/marshmallow-code__marshmallow/reproduce.py created.]
|
||||
|
||||
ASSISTANT:
|
||||
Now let's paste in the example code from the issue.
|
||||
<execute_ipython>
|
||||
EDITED_CODE = \"\"\"from marshmallow.fields import TimeDelta
|
||||
from datetime import timedelta
|
||||
|
||||
td_field = TimeDelta(precision="milliseconds")
|
||||
|
||||
obj = dict()
|
||||
obj["td_field"] = timedelta(milliseconds=345)
|
||||
|
||||
print(td_field.serialize("td_field", obj))
|
||||
\"\"\"
|
||||
edit_file(start=1, end=1, content=EDITED_CODE)
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[File: /workspace/marshmallow-code__marshmallow/reproduce.py (9 lines total)]
|
||||
1|from marshmallow.fields import TimeDelta
|
||||
2|from datetime import timedelta
|
||||
3|
|
||||
4|td_field = TimeDelta(precision="milliseconds")
|
||||
5|
|
||||
6|obj = dict()
|
||||
7|obj["td_field"] = timedelta(milliseconds=345)
|
||||
8|
|
||||
9|print(td_field.serialize("td_field", obj))
|
||||
[File updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]
|
||||
|
||||
ASSISTANT:
|
||||
Now let's run the code to see if we see the same output as the issue.
|
||||
<execute_bash>
|
||||
python reproduce.py
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
344
|
||||
|
||||
ASSISTANT:
|
||||
We are indeed seeing the same output as the issue. The issue suggests that we should look at line 1474 of the `fields.py` file to see if there is a rounding issue. Let's open the `fields.py` file and navigate to line 1474 to see the relevant code. First we should find the `fields.py` file in the repository. It is likely to be in the `src/marshmallow` directory, but we should check to be sure.
|
||||
<execute_bash>
|
||||
ls -F
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
AUTHORS.rst
|
||||
CHANGELOG.rst
|
||||
CODE_OF_CONDUCT.md
|
||||
CONTRIBUTING.rst
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
NOTICE
|
||||
README.rst
|
||||
RELEASING.md
|
||||
azure-pipelines.yml
|
||||
docs/
|
||||
examples/
|
||||
performance/
|
||||
pyproject.toml
|
||||
reproduce.py
|
||||
setup.cfg
|
||||
setup.py
|
||||
src/
|
||||
tests/
|
||||
tox.ini
|
||||
|
||||
ASSISTANT:
|
||||
It looks like the `src` directory is present, which suggests that the `fields.py` file is likely to be in the `src` directory. Let's use find_file to see where it is.
|
||||
<execute_ipython>
|
||||
find_file("fields.py", "/workspace/marshmallow-code__marshmallow/src")
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[Found 1 matches for "fields.py" in /workspace/marshmallow-code__marshmallow/src]
|
||||
/workspace/marshmallow-code__marshmallow/src/marshmallow/fields.py
|
||||
|
||||
ASSISTANT:
|
||||
It looks like the `fields.py` file is present in the `./src/marshmallow/` directory. The issue also points to a specific URL with line number 1474. We should navigate to that line in fields.py to see the relevant code for the `TimeDelta` serialization.
|
||||
<execute_ipython>
|
||||
open_file("/workspace/marshmallow-code__marshmallow/src/marshmallow/fields.py", 1474)
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[File: /workspace/marshmallow-code__marshmallow/src/marshmallow/fields.py (1997 lines total)]
|
||||
(1458 more lines above)
|
||||
1459| self.WEEKS,
|
||||
1460| )
|
||||
1461|
|
||||
1462| if precision not in units:
|
||||
1463| msg = 'The precision must be {} or "{}".'.format(
|
||||
1464| ", ".join([f'"{each}"' for each in units[:-1]]), units[-1]
|
||||
1465| )
|
||||
1466| raise ValueError(msg)
|
||||
1467|
|
||||
1468| self.precision = precision
|
||||
1469| super().__init__(**kwargs)
|
||||
1470|
|
||||
1471| def _serialize(self, value, attr, obj, **kwargs):
|
||||
1472| if value is None:
|
||||
1473| return None
|
||||
1474| base_unit = dt.timedelta(**{self.precision: 1})
|
||||
1475| return int(value.total_seconds() / base_unit.total_seconds())
|
||||
1476|
|
||||
1477| def _deserialize(self, value, attr, data, **kwargs):
|
||||
1478| try:
|
||||
1479| value = int(value)
|
||||
1480| except (TypeError, ValueError) as error:
|
||||
1481| raise self.make_error("invalid") from error
|
||||
1482|
|
||||
1483| kwargs = {self.precision: value}
|
||||
1484|
|
||||
1485| try:
|
||||
1486| return dt.timedelta(**kwargs)
|
||||
1487| except OverflowError as error:
|
||||
1488| raise self.make_error("invalid") from error
|
||||
1489|
|
||||
1490|
|
||||
1491|class Mapping(Field):
|
||||
1492| \"\"\"An abstract class for objects with key-value pairs.
|
||||
1493|
|
||||
1494| :param keys: A field class or instance for dict keys.
|
||||
1495| :param values: A field class or instance for dict values.
|
||||
1496| :param kwargs: The same keyword arguments that :class:`Field` receives.
|
||||
1497|
|
||||
1498| .. note::
|
||||
1499| When the structure of nested data is not known, you may omit the
|
||||
1500| `keys` and `values` arguments to prevent content validation.
|
||||
1501|
|
||||
1502| .. versionadded:: 3.0.0rc4
|
||||
1503| \"\"\"
|
||||
1504|
|
||||
1505| mapping_type = dict
|
||||
1506|
|
||||
1507| #: Default error messages.
|
||||
1508| default_error_messages = {"invalid": "Not a valid mapping type."}
|
||||
1509|
|
||||
1510| def __init__(
|
||||
1511| self,
|
||||
1512| keys: typing.Optional[typing.Union[Field, type]] = None,
|
||||
1513| values: typing.Optional[typing.Union[Field, type]] = None,
|
||||
1514| **kwargs,
|
||||
1515| ):
|
||||
1516| super().__init__(**kwargs)
|
||||
1517| if keys is None:
|
||||
1518| self.key_field = None
|
||||
1519| else:
|
||||
1520| try:
|
||||
1521| self.key_field = resolve_field_instance(keys)
|
||||
1522| except FieldInstanceResolutionError as error:
|
||||
1523| raise ValueError(
|
||||
1524| '"keys" must be a subclass or instance of '
|
||||
1525| "marshmallow.base.FieldABC."
|
||||
1526| ) from error
|
||||
1527|
|
||||
1528| if values is None:
|
||||
1529| self.value_field = None
|
||||
1530| else:
|
||||
1531| try:
|
||||
1532| self.value_field = resolve_field_instance(values)
|
||||
1533| except FieldInstanceResolutionError as error:
|
||||
1534| raise ValueError(
|
||||
1535| '"values" must be a subclass or instance of '
|
||||
1536| "marshmallow.base.FieldABC."
|
||||
1537| ) from error
|
||||
1538| if isinstance(self.value_field, Nested):
|
||||
1539| self.only = self.value_field.only
|
||||
1540| self.exclude = self.value_field.exclude
|
||||
1541|
|
||||
1542| def _bind_to_schema(self, field_name, schema):
|
||||
1543| super()._bind_to_schema(field_name, schema)
|
||||
1544| if self.value_field:
|
||||
1545| self.value_field = copy.deepcopy(self.value_field)
|
||||
1546| self.value_field._bind_to_schema(field_name, self)
|
||||
1547| if isinstance(self.value_field, Nested):
|
||||
1548| self.value_field.only = self.only
|
||||
1549| self.value_field.exclude = self.exclude
|
||||
1550| if self.key_field:
|
||||
1551| self.key_field = copy.deepcopy(self.key_field)
|
||||
1552| self.key_field._bind_to_schema(field_name, self)
|
||||
1553|
|
||||
1554| def _serialize(self, value, attr, obj, **kwargs):
|
||||
1555| if value is None:
|
||||
1556| return None
|
||||
1557| if not self.value_field and not self.key_field:
|
||||
1558| return self.mapping_type(value)
|
||||
(439 more lines below)
|
||||
|
||||
ASSISTANT:
|
||||
We are now looking at the relevant section of the `fields.py` file where the `TimeDelta` serialization occurs. The issue suggests that there is a rounding problem near line 1474. The current implementation uses integer division, which truncates the decimal part and can lead to incorrect rounding on line 1475. To fix this issue, we should ensure that the division results in a float, and then round the result to the nearest integer before converting it to an int. This should preserve the milliseconds precision as expected. Let's make the necessary edit to the code.
|
||||
<execute_ipython>
|
||||
edit_file(start=1475, end=1475, content="return int(round(value.total_seconds() / base_unit.total_seconds())) # round to nearest int")
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[Your proposed edit has introduced new syntax error(s). Please understand the errors and retry your edit command.]
|
||||
|
||||
ERRORS:
|
||||
- E999 IndentationError: unexpected indent
|
||||
|
||||
[This is how your edit would have looked if applied]
|
||||
-------------------------------------------------
|
||||
[File: /marshmallow-code__marshmallow/src/marshmallow/fields.py (1997 lines total)]
|
||||
(1468 more lines above)
|
||||
1469| super().__init__(**kwargs)
|
||||
1470|
|
||||
1471| def _serialize(self, value, attr, obj, **kwargs):
|
||||
1472| if value is None:
|
||||
1473| return None
|
||||
1474| base_unit = dt.timedelta(**{self.precision: 1})
|
||||
1475|return int(round(value.total_seconds() / base_unit.total_seconds())) # round to nearest int
|
||||
1476|
|
||||
1477| def _deserialize(self, value, attr, data, **kwargs):
|
||||
1478| try:
|
||||
1479| value = int(value)
|
||||
(518 more lines below)
|
||||
-------------------------------------------------
|
||||
|
||||
[This is the original code before your edit]
|
||||
-------------------------------------------------
|
||||
[File: /marshmallow-code__marshmallow/src/marshmallow/fields.py (1997 lines total)]
|
||||
(1469 more lines above)
|
||||
1470|
|
||||
1471| def _serialize(self, value, attr, obj, **kwargs):
|
||||
1472| if value is None:
|
||||
1473| return None
|
||||
1474| base_unit = dt.timedelta(**{self.precision: 1})
|
||||
1475| return int(value.total_seconds() / base_unit.total_seconds())
|
||||
1476|
|
||||
1477| def _deserialize(self, value, attr, data, **kwargs):
|
||||
1478| try:
|
||||
1479| value = int(value)
|
||||
1480| except (TypeError, ValueError) as error:
|
||||
(517 more lines below)
|
||||
-------------------------------------------------
|
||||
Your changes have NOT been applied. Please fix your edit command and try again.
|
||||
You either need to 1) Specify the correct start/end line arguments or 2) Correct your edit code.
|
||||
DO NOT re-run the same failed edit command. Running it again will lead to the same error.
|
||||
|
||||
ASSISTANT:
|
||||
My edit command did not use the proper indentation, I will fix my syntax in this follow up edit command.
|
||||
<execute_ipython>
|
||||
EDITED_CODE=" return int(round(value.total_seconds() / base_unit.total_seconds())) # round to nearest int"
|
||||
edit_file(start=1475, end=1475, content=EDITED_CODE)
|
||||
</execute_ipython>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
[File: /marshmallow-code__marshmallow/src/marshmallow/fields.py (1997 lines total)]
|
||||
(1424 more lines above)
|
||||
1425|
|
||||
1426| :param precision: Influences how the integer is interpreted during
|
||||
1427| (de)serialization. Must be 'days', 'seconds', 'microseconds',
|
||||
1428| 'milliseconds', 'minutes', 'hours' or 'weeks'.
|
||||
1429| :param kwargs: The same keyword arguments that :class:`Field` receives.
|
||||
1430|
|
||||
1431| .. versionchanged:: 2.0.0
|
||||
1432| Always serializes to an integer value to avoid rounding errors.
|
||||
1433| Add `precision` parameter.
|
||||
1434| \"\"\"
|
||||
1435|
|
||||
1436| DAYS = "days"
|
||||
1437| SECONDS = "seconds"
|
||||
1438| MICROSECONDS = "microseconds"
|
||||
1439| MILLISECONDS = "milliseconds"
|
||||
1440| MINUTES = "minutes"
|
||||
1441| HOURS = "hours"
|
||||
1442| WEEKS = "weeks"
|
||||
1443|
|
||||
1444| #: Default error messages.
|
||||
1445| default_error_messages = {
|
||||
1446| "invalid": "Not a valid period of time.",
|
||||
1447| "format": "{input!r} cannot be formatted as a timedelta.",
|
||||
1448| }
|
||||
1449|
|
||||
1450| def __init__(self, precision: str = SECONDS, **kwargs):
|
||||
1451| precision = precision.lower()
|
||||
1452| units = (
|
||||
1453| self.DAYS,
|
||||
1454| self.SECONDS,
|
||||
1455| self.MICROSECONDS,
|
||||
1456| self.MILLISECONDS,
|
||||
1457| self.MINUTES,
|
||||
1458| self.HOURS,
|
||||
1459| self.WEEKS,
|
||||
1460| )
|
||||
1461|
|
||||
1462| if precision not in units:
|
||||
1463| msg = 'The precision must be {} or "{}".'.format(
|
||||
1464| ", ".join([f'"{each}"' for each in units[:-1]]), units[-1]
|
||||
1465| )
|
||||
1466| raise ValueError(msg)
|
||||
1467|
|
||||
1468| self.precision = precision
|
||||
1469| super().__init__(**kwargs)
|
||||
1470|
|
||||
1471| def _serialize(self, value, attr, obj, **kwargs):
|
||||
1472| if value is None:
|
||||
1473| return None
|
||||
1474| base_unit = dt.timedelta(**{self.precision: 1})
|
||||
1475| return int(round(value.total_seconds() / base_unit.total_seconds())) # round to nearest int
|
||||
1476|
|
||||
1477| def _deserialize(self, value, attr, data, **kwargs):
|
||||
1478| try:
|
||||
1479| value = int(value)
|
||||
1480| except (TypeError, ValueError) as error:
|
||||
1481| raise self.make_error("invalid") from error
|
||||
1482|
|
||||
1483| kwargs = {self.precision: value}
|
||||
1484|
|
||||
1485| try:
|
||||
1486| return dt.timedelta(**kwargs)
|
||||
1487| except OverflowError as error:
|
||||
1488| raise self.make_error("invalid") from error
|
||||
1489|
|
||||
1490|
|
||||
1491|class Mapping(Field):
|
||||
1492| \"\"\"An abstract class for objects with key-value pairs.
|
||||
1493|
|
||||
1494| :param keys: A field class or instance for dict keys.
|
||||
1495| :param values: A field class or instance for dict values.
|
||||
1496| :param kwargs: The same keyword arguments that :class:`Field` receives.
|
||||
1497|
|
||||
1498| .. note::
|
||||
1499| When the structure of nested data is not known, you may omit the
|
||||
1500| `keys` and `values` arguments to prevent content validation.
|
||||
1501|
|
||||
1502| .. versionadded:: 3.0.0rc4
|
||||
1503| \"\"\"
|
||||
1504|
|
||||
1505| mapping_type = dict
|
||||
1506|
|
||||
1507| #: Default error messages.
|
||||
1508| default_error_messages = {"invalid": "Not a valid mapping type."}
|
||||
1509|
|
||||
1510| def __init__(
|
||||
1511| self,
|
||||
1512| keys: typing.Optional[typing.Union[Field, type]] = None,
|
||||
1513| values: typing.Optional[typing.Union[Field, type]] = None,
|
||||
1514| **kwargs,
|
||||
1515| ):
|
||||
1516| super().__init__(**kwargs)
|
||||
1517| if keys is None:
|
||||
1518| self.key_field = None
|
||||
1519| else:
|
||||
1520| try:
|
||||
1521| self.key_field = resolve_field_instance(keys)
|
||||
1522| except FieldInstanceResolutionError as error:
|
||||
1523| raise ValueError(
|
||||
1524| '"keys" must be a subclass or instance of '
|
||||
(473 more lines below)
|
||||
[File updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]
|
||||
|
||||
ASSISTANT:
|
||||
The code has been updated to use the `round` function, which should fix the rounding issue. Before submitting the changes, it would be prudent to run the reproduce.py code again to ensure that this change has the desired effect. I see that "Current directory" is still `/marshmallow-code__marshmallow`, so we can just run `reproduce.py` directly.
|
||||
<execute_bash>
|
||||
python reproduce.py
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
345
|
||||
|
||||
ASSISTANT:
|
||||
The output has changed from 344 to 345, which suggests that the rounding issue has been fixed. Let's remove the reproduce.py file since it is no longer needed.
|
||||
<execute_bash>
|
||||
rm reproduce.py
|
||||
</execute_bash>
|
||||
|
||||
USER:
|
||||
OBSERVATION:
|
||||
|
||||
ASSISTANT:
|
||||
rm doesn't have any output when it deletes successfully, so that must have worked. Now that we have fixed the issue, we can exit the current task.
|
||||
<execute_bash>
|
||||
exit
|
||||
</execute_bash>
|
||||
|
||||
--- END OF EXAMPLE ---
|
||||
"""
|
||||
@@ -1,46 +0,0 @@
|
||||
from agenthub.codeact_swe_agent.action_parser import (
|
||||
CodeActSWEActionParserCmdRun,
|
||||
CodeActSWEActionParserFinish,
|
||||
CodeActSWEActionParserIPythonRunCell,
|
||||
CodeActSWEActionParserMessage,
|
||||
)
|
||||
from openhands.controller.action_parser import ResponseParser
|
||||
from openhands.events.action import Action
|
||||
|
||||
|
||||
class CodeActSWEResponseParser(ResponseParser):
|
||||
"""Parser action:
|
||||
- CmdRunAction(command) - bash command to run
|
||||
- IPythonRunCellAction(code) - IPython code to run
|
||||
- MessageAction(content) - Message action to run (e.g. ask for clarification)
|
||||
- AgentFinishAction() - end the interaction
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Need pay attention to the item order in self.action_parsers
|
||||
super().__init__()
|
||||
self.action_parsers = [
|
||||
CodeActSWEActionParserFinish(),
|
||||
CodeActSWEActionParserCmdRun(),
|
||||
CodeActSWEActionParserIPythonRunCell(),
|
||||
]
|
||||
self.default_parser = CodeActSWEActionParserMessage()
|
||||
|
||||
def parse(self, response: str) -> Action:
|
||||
action_str = self.parse_response(response)
|
||||
return self.parse_action(action_str)
|
||||
|
||||
def parse_response(self, response) -> str:
|
||||
action = response.choices[0].message.content
|
||||
if action is None:
|
||||
return ''
|
||||
for lang in ['bash', 'ipython']:
|
||||
if f'<execute_{lang}>' in action and f'</execute_{lang}>' not in action:
|
||||
action += f'</execute_{lang}>'
|
||||
return action
|
||||
|
||||
def parse_action(self, action_str: str) -> Action:
|
||||
for action_parser in self.action_parsers:
|
||||
if action_parser.check_condition(action_str):
|
||||
return action_parser.parse(action_str)
|
||||
return self.default_parser.parse(action_str)
|
||||
@@ -1,4 +0,0 @@
|
||||
from agenthub.planner_agent.agent import PlannerAgent
|
||||
from openhands.controller.agent import Agent
|
||||
|
||||
Agent.register('PlannerAgent', PlannerAgent)
|
||||
@@ -1,53 +0,0 @@
|
||||
from agenthub.planner_agent.prompt import get_prompt_and_images
|
||||
from agenthub.planner_agent.response_parser import PlannerResponseParser
|
||||
from openhands.controller.agent import Agent
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import AgentConfig
|
||||
from openhands.core.message import ImageContent, Message, TextContent
|
||||
from openhands.events.action import Action, AgentFinishAction
|
||||
from openhands.llm.llm import LLM
|
||||
|
||||
|
||||
class PlannerAgent(Agent):
|
||||
VERSION = '1.0'
|
||||
"""
|
||||
The planner agent utilizes a special prompting strategy to create long term plans for solving problems.
|
||||
The agent is given its previous action-observation pairs, current task, and hint based on last action taken at every step.
|
||||
"""
|
||||
response_parser = PlannerResponseParser()
|
||||
|
||||
def __init__(self, llm: LLM, config: AgentConfig):
|
||||
"""Initialize the Planner Agent with an LLM
|
||||
|
||||
Parameters:
|
||||
- llm (LLM): The llm to be used by this agent
|
||||
"""
|
||||
super().__init__(llm, config)
|
||||
|
||||
def step(self, state: State) -> Action:
|
||||
"""Checks to see if current step is completed, returns AgentFinishAction if True.
|
||||
Otherwise, creates a plan prompt and sends to model for inference, returning the result as the next action.
|
||||
|
||||
Parameters:
|
||||
- state (State): The current state given the previous actions and observations
|
||||
|
||||
Returns:
|
||||
- AgentFinishAction: If the last state was 'completed', 'verified', or 'abandoned'
|
||||
- Action: The next action to take based on llm response
|
||||
"""
|
||||
if state.root_task.state in [
|
||||
'completed',
|
||||
'verified',
|
||||
'abandoned',
|
||||
]:
|
||||
return AgentFinishAction()
|
||||
|
||||
prompt, image_urls = get_prompt_and_images(
|
||||
state, self.llm.config.max_message_chars
|
||||
)
|
||||
content = [TextContent(text=prompt)]
|
||||
if self.llm.vision_is_active() and image_urls:
|
||||
content.append(ImageContent(image_urls=image_urls))
|
||||
message = Message(role='user', content=content)
|
||||
resp = self.llm.completion(messages=self.llm.format_messages_for_llm(message))
|
||||
return self.response_parser.parse(resp)
|
||||
@@ -1,191 +0,0 @@
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.schema import ActionType
|
||||
from openhands.core.utils import json
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
NullAction,
|
||||
)
|
||||
from openhands.events.serialization.action import action_from_dict
|
||||
from openhands.events.serialization.event import event_to_memory
|
||||
|
||||
HISTORY_SIZE = 20
|
||||
|
||||
prompt = """
|
||||
# Task
|
||||
You're a diligent software engineer AI. You can't see, draw, or interact with a
|
||||
browser, but you can read and write files, and you can run commands, and you can think.
|
||||
|
||||
You've been given the following task:
|
||||
|
||||
%(task)s
|
||||
|
||||
## Plan
|
||||
As you complete this task, you're building a plan and keeping
|
||||
track of your progress. Here's a JSON representation of your plan:
|
||||
|
||||
%(plan)s
|
||||
|
||||
|
||||
%(plan_status)s
|
||||
|
||||
You're responsible for managing this plan and the status of tasks in
|
||||
it, by using the `add_task` and `modify_task` actions described below.
|
||||
|
||||
If the History below contradicts the state of any of these tasks, you
|
||||
MUST modify the task using the `modify_task` action described below.
|
||||
|
||||
Be sure NOT to duplicate any tasks. Do NOT use the `add_task` action for
|
||||
a task that's already represented. Every task must be represented only once.
|
||||
|
||||
Tasks that are sequential MUST be siblings. They must be added in order
|
||||
to their parent task.
|
||||
|
||||
If you mark a task as 'completed', 'verified', or 'abandoned',
|
||||
all non-abandoned subtasks will be marked the same way.
|
||||
So before closing a task this way, you MUST not only be sure that it has
|
||||
been completed successfully--you must ALSO be sure that all its subtasks
|
||||
are ready to be marked the same way.
|
||||
|
||||
If, and only if, ALL tasks have already been marked verified,
|
||||
you MUST respond with the `finish` action.
|
||||
|
||||
## History
|
||||
Here is a recent history of actions you've taken in service of this plan,
|
||||
as well as observations you've made. This only includes the MOST RECENT
|
||||
ten actions--more happened before that.
|
||||
|
||||
%(history)s
|
||||
|
||||
|
||||
Your most recent action is at the bottom of that history.
|
||||
|
||||
## Action
|
||||
What is your next thought or action? Your response must be in JSON format.
|
||||
|
||||
It must be an object, and it must contain two fields:
|
||||
* `action`, which is one of the actions below
|
||||
* `args`, which is a map of key-value pairs, specifying the arguments for that action
|
||||
|
||||
* `read` - reads the content of a file. Arguments:
|
||||
* `path` - the path of the file to read
|
||||
* `write` - writes the content to a file. Arguments:
|
||||
* `path` - the path of the file to write
|
||||
* `content` - the content to write to the file
|
||||
* `run` - runs a command on the command line in a Linux shell. Arguments:
|
||||
* `command` - the command to run
|
||||
* `browse` - opens a web page. Arguments:
|
||||
* `url` - the URL to open
|
||||
* `message` - make a plan, set a goal, record your thoughts, or ask for more input from the user. Arguments:
|
||||
* `content` - the message to record
|
||||
* `wait_for_response` - set to `true` to wait for the user to respond before proceeding
|
||||
* `add_task` - add a task to your plan. Arguments:
|
||||
* `parent` - the ID of the parent task (leave empty if it should go at the top level)
|
||||
* `goal` - the goal of the task
|
||||
* `subtasks` - a list of subtasks, each of which is a map with a `goal` key.
|
||||
* `modify_task` - close a task. Arguments:
|
||||
* `task_id` - the ID of the task to close
|
||||
* `state` - set to 'in_progress' to start the task, 'completed' to finish it, 'verified' to assert that it was successful, 'abandoned' to give up on it permanently, or `open` to stop working on it for now.
|
||||
* `finish` - if ALL of your tasks and subtasks have been verified or abandoned, and you're absolutely certain that you've completed your task and have tested your work, use the finish action to stop working.
|
||||
|
||||
You MUST take time to think in between read, write, run, and browse actions--do this with the `message` action.
|
||||
You should never act twice in a row without thinking. But if your last several
|
||||
actions are all `message` actions, you should consider taking a different action.
|
||||
|
||||
What is your next thought or action? Again, you must reply with JSON, and only with JSON.
|
||||
|
||||
%(hint)s
|
||||
"""
|
||||
|
||||
|
||||
def get_hint(latest_action_id: str) -> str:
|
||||
"""Returns action type hint based on given action_id"""
|
||||
hints = {
|
||||
'': "You haven't taken any actions yet. Start by using `ls` to check out what files you're working with.",
|
||||
ActionType.RUN: 'You should think about the command you just ran, what output it gave, and how that affects your plan.',
|
||||
ActionType.READ: 'You should think about the file you just read, what you learned from it, and how that affects your plan.',
|
||||
ActionType.WRITE: 'You just changed a file. You should think about how it affects your plan.',
|
||||
ActionType.BROWSE: 'You should think about the page you just visited, and what you learned from it.',
|
||||
ActionType.MESSAGE: "Look at your last thought in the history above. What does it suggest? Don't think anymore--take action.",
|
||||
ActionType.ADD_TASK: 'You should think about the next action to take.',
|
||||
ActionType.MODIFY_TASK: 'You should think about the next action to take.',
|
||||
ActionType.SUMMARIZE: '',
|
||||
ActionType.FINISH: '',
|
||||
}
|
||||
return hints.get(latest_action_id, '')
|
||||
|
||||
|
||||
def get_prompt_and_images(
|
||||
state: State, max_message_chars: int
|
||||
) -> tuple[str, list[str]]:
|
||||
"""Gets the prompt for the planner agent.
|
||||
|
||||
Formatted with the most recent action-observation pairs, current task, and hint based on last action
|
||||
|
||||
Parameters:
|
||||
- state (State): The state of the current agent
|
||||
|
||||
Returns:
|
||||
- str: The formatted string prompt with historical values
|
||||
"""
|
||||
# the plan
|
||||
plan_str = json.dumps(state.root_task.to_dict(), indent=2)
|
||||
|
||||
# the history
|
||||
history_dicts = []
|
||||
latest_action: Action = NullAction()
|
||||
|
||||
# retrieve the latest HISTORY_SIZE events
|
||||
for event_count, event in enumerate(state.history.get_events(reverse=True)):
|
||||
if event_count >= HISTORY_SIZE:
|
||||
break
|
||||
if latest_action == NullAction() and isinstance(event, Action):
|
||||
latest_action = event
|
||||
history_dicts.append(event_to_memory(event, max_message_chars))
|
||||
|
||||
# history_dicts is in reverse order, lets fix it
|
||||
history_dicts.reverse()
|
||||
|
||||
# and get it as a JSON string
|
||||
history_str = json.dumps(history_dicts, indent=2)
|
||||
|
||||
# the plan status
|
||||
current_task = state.root_task.get_current_task()
|
||||
if current_task is not None:
|
||||
plan_status = f"You're currently working on this task:\n{current_task.goal}."
|
||||
if len(current_task.subtasks) == 0:
|
||||
plan_status += "\nIf it's not achievable AND verifiable with a SINGLE action, you MUST break it down into subtasks NOW."
|
||||
else:
|
||||
plan_status = "You're not currently working on any tasks. Your next action MUST be to mark a task as in_progress."
|
||||
|
||||
# the hint, based on the last action
|
||||
hint = get_hint(event_to_memory(latest_action, max_message_chars).get('action', ''))
|
||||
logger.info('HINT:\n' + hint, extra={'msg_type': 'DETAIL'})
|
||||
|
||||
# the last relevant user message (the task)
|
||||
message, image_urls = state.get_current_user_intent()
|
||||
|
||||
# finally, fill in the prompt
|
||||
return prompt % {
|
||||
'task': message,
|
||||
'plan': plan_str,
|
||||
'history': history_str,
|
||||
'hint': hint,
|
||||
'plan_status': plan_status,
|
||||
}, image_urls
|
||||
|
||||
|
||||
def parse_response(response: str) -> Action:
|
||||
"""Parses the model output to find a valid action to take
|
||||
Parameters:
|
||||
- response (str): A response from the model that potentially contains an Action.
|
||||
|
||||
Returns:
|
||||
- Action: A valid next action to perform from model output
|
||||
"""
|
||||
action_dict = json.loads(response)
|
||||
if 'contents' in action_dict:
|
||||
# The LLM gets confused here. Might as well be robust
|
||||
action_dict['content'] = action_dict.pop('contents')
|
||||
action = action_from_dict(action_dict)
|
||||
return action
|
||||
@@ -1,37 +0,0 @@
|
||||
from openhands.controller.action_parser import ResponseParser
|
||||
from openhands.core.utils import json
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
)
|
||||
from openhands.events.serialization.action import action_from_dict
|
||||
|
||||
|
||||
class PlannerResponseParser(ResponseParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def parse(self, response: str) -> Action:
|
||||
action_str = self.parse_response(response)
|
||||
return self.parse_action(action_str)
|
||||
|
||||
def parse_response(self, response) -> str:
|
||||
# get the next action from the response
|
||||
return response['choices'][0]['message']['content']
|
||||
|
||||
def parse_action(self, action_str: str) -> Action:
|
||||
"""Parses a string to find an action within it
|
||||
|
||||
Parameters:
|
||||
- response (str): The string to be parsed
|
||||
|
||||
Returns:
|
||||
- Action: The action that was found in the response string
|
||||
"""
|
||||
# attempt to load the JSON dict from the response
|
||||
action_dict = json.loads(action_str)
|
||||
|
||||
if 'content' in action_dict:
|
||||
# The LLM gets confused here. Might as well be robust
|
||||
action_dict['contents'] = action_dict.pop('content')
|
||||
|
||||
return action_from_dict(action_dict)
|
||||
5
build.sh
Executable file
5
build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cp pyproject.toml poetry.lock openhands
|
||||
poetry build -v
|
||||
@@ -13,12 +13,19 @@
|
||||
# API key for E2B
|
||||
#e2b_api_key = ""
|
||||
|
||||
# API key for Modal
|
||||
#modal_api_token_id = ""
|
||||
#modal_api_token_secret = ""
|
||||
|
||||
# Base path for the workspace
|
||||
workspace_base = "./workspace"
|
||||
|
||||
# Cache directory path
|
||||
#cache_dir = "/tmp/cache"
|
||||
|
||||
# Reasoning effort for o1 models (low, medium, high, or not set)
|
||||
#reasoning_effort = "medium"
|
||||
|
||||
# Debugging enabled
|
||||
#debug = false
|
||||
|
||||
@@ -28,6 +35,15 @@ workspace_base = "./workspace"
|
||||
# Enable saving and restoring the session when run from CLI
|
||||
#enable_cli_session = false
|
||||
|
||||
# Path to store trajectories, can be a folder or a file
|
||||
# If it's a folder, the session id will be used as the file name
|
||||
#save_trajectory_path="./trajectories"
|
||||
|
||||
# Path to replay a trajectory, must be a file path
|
||||
# If provided, trajectory will be loaded and replayed before the
|
||||
# agent responds to any user instruction
|
||||
#replay_trajectory_path = ""
|
||||
|
||||
# File store path
|
||||
#file_store_path = "/tmp/file_store"
|
||||
|
||||
@@ -59,7 +75,7 @@ workspace_base = "./workspace"
|
||||
#run_as_openhands = true
|
||||
|
||||
# Runtime environment
|
||||
#runtime = "eventstream"
|
||||
#runtime = "docker"
|
||||
|
||||
# Name of the default agent
|
||||
#default_agent = "CodeActAgent"
|
||||
@@ -87,10 +103,10 @@ workspace_base = "./workspace"
|
||||
# AWS secret access key
|
||||
#aws_secret_access_key = ""
|
||||
|
||||
# API key to use
|
||||
# API key to use (For Headless / CLI only - In Web this is overridden by Session Init)
|
||||
api_key = "your-api-key"
|
||||
|
||||
# API base URL
|
||||
# API base URL (For Headless / CLI only - In Web this is overridden by Session Init)
|
||||
#base_url = ""
|
||||
|
||||
# API version
|
||||
@@ -112,7 +128,7 @@ api_key = "your-api-key"
|
||||
#embedding_deployment_name = ""
|
||||
|
||||
# Embedding model to use
|
||||
embedding_model = ""
|
||||
embedding_model = "local"
|
||||
|
||||
# Maximum number of characters in an observation's content
|
||||
#max_message_chars = 10000
|
||||
@@ -123,7 +139,7 @@ embedding_model = ""
|
||||
# Maximum number of output tokens
|
||||
#max_output_tokens = 0
|
||||
|
||||
# Model to use
|
||||
# Model to use. (For Headless / CLI only - In Web this is overridden by Session Init)
|
||||
model = "gpt-4o"
|
||||
|
||||
# Number of retries to attempt when an operation fails with the LLM.
|
||||
@@ -146,8 +162,12 @@ model = "gpt-4o"
|
||||
# Drop any unmapped (unsupported) params without causing an exception
|
||||
#drop_params = false
|
||||
|
||||
# Using the prompt caching feature provided by the LLM
|
||||
#caching_prompt = false
|
||||
# Modify params for litellm to do transformations like adding a default message, when a message is empty.
|
||||
# Note: this setting is global, unlike drop_params, it cannot be overridden in each call to litellm.
|
||||
#modify_params = true
|
||||
|
||||
# Using the prompt caching feature if provided by the LLM and supported
|
||||
#caching_prompt = true
|
||||
|
||||
# Base URL for the OLLAMA API
|
||||
#ollama_base_url = ""
|
||||
@@ -159,17 +179,25 @@ model = "gpt-4o"
|
||||
#timeout = 0
|
||||
|
||||
# Top p for the API
|
||||
#top_p = 0.5
|
||||
#top_p = 1.0
|
||||
|
||||
# If model is vision capable, this option allows to disable image processing (useful for cost reduction).
|
||||
#disable_vision = true
|
||||
|
||||
[llm.gpt3]
|
||||
# API key to use
|
||||
api_key = "your-api-key"
|
||||
# Custom tokenizer to use for token counting
|
||||
# https://docs.litellm.ai/docs/completion/token_usage
|
||||
#custom_tokenizer = ""
|
||||
|
||||
# Whether to use native tool calling if supported by the model. Can be true, false, or None by default, which chooses the model's default behavior based on the evaluation.
|
||||
# ATTENTION: Based on evaluation, enabling native function calling may lead to worse results
|
||||
# in some scenarios. Use with caution and consider testing with your specific use case.
|
||||
# https://github.com/All-Hands-AI/OpenHands/pull/4711
|
||||
#native_tool_calling = None
|
||||
|
||||
[llm.gpt4o-mini]
|
||||
api_key = "your-api-key"
|
||||
model = "gpt-4o"
|
||||
|
||||
# Model to use
|
||||
model = "gpt-3.5"
|
||||
|
||||
#################################### Agent ###################################
|
||||
# Configuration for agents (group name starts with 'agent')
|
||||
@@ -178,6 +206,16 @@ model = "gpt-3.5"
|
||||
# agent.CodeActAgent
|
||||
##############################################################################
|
||||
[agent]
|
||||
|
||||
# whether the browsing tool is enabled
|
||||
codeact_enable_browsing = true
|
||||
|
||||
# whether the LLM draft editor is enabled
|
||||
codeact_enable_llm_editor = false
|
||||
|
||||
# whether the IPython tool is enabled
|
||||
codeact_enable_jupyter = true
|
||||
|
||||
# Name of the micro agent to use for this agent
|
||||
#micro_agent_name = ""
|
||||
|
||||
@@ -185,10 +223,16 @@ model = "gpt-3.5"
|
||||
#memory_enabled = false
|
||||
|
||||
# Memory maximum threads
|
||||
#memory_max_threads = 2
|
||||
#memory_max_threads = 3
|
||||
|
||||
# LLM config group to use
|
||||
#llm_config = 'llm'
|
||||
#llm_config = 'your-llm-config-group'
|
||||
|
||||
# Whether to use prompt extension (e.g., microagent, repo/runtime info) at all
|
||||
#enable_prompt_extensions = true
|
||||
|
||||
# List of microagents to disable
|
||||
#disabled_microagents = []
|
||||
|
||||
[agent.RepoExplorerAgent]
|
||||
# Example: use a cheaper model for RepoExplorerAgent to reduce cost, especially
|
||||
@@ -206,11 +250,14 @@ llm_config = 'gpt3'
|
||||
#user_id = 1000
|
||||
|
||||
# Container image to use for the sandbox
|
||||
#base_container_image = "nikolaik/python-nodejs:python3.11-nodejs22"
|
||||
#base_container_image = "nikolaik/python-nodejs:python3.12-nodejs22"
|
||||
|
||||
# Use host network
|
||||
#use_host_network = false
|
||||
|
||||
# runtime extra build args
|
||||
#runtime_extra_build_args = ["--network=host", "--add-host=host.docker.internal:host-gateway"]
|
||||
|
||||
# Enable auto linting after editing
|
||||
#enable_auto_lint = false
|
||||
|
||||
@@ -231,10 +278,10 @@ llm_config = 'gpt3'
|
||||
##############################################################################
|
||||
[security]
|
||||
|
||||
# Enable confirmation mode
|
||||
#confirmation_mode = true
|
||||
# Enable confirmation mode (For Headless / CLI only - In Web this is overridden by Session Init)
|
||||
#confirmation_mode = false
|
||||
|
||||
# The security analyzer to use
|
||||
# The security analyzer to use (For Headless / CLI only - In Web this is overridden by Session Init)
|
||||
#security_analyzer = ""
|
||||
|
||||
#################################### Eval ####################################
|
||||
|
||||
@@ -8,7 +8,7 @@ RUN npm install -g npm@10.5.1
|
||||
RUN npm ci
|
||||
|
||||
COPY ./frontend ./
|
||||
RUN npm run make-i18n && npm run build
|
||||
RUN npm run build
|
||||
|
||||
FROM python:3.12.3-slim AS backend-builder
|
||||
|
||||
@@ -28,7 +28,7 @@ COPY ./pyproject.toml ./poetry.lock ./
|
||||
RUN touch README.md
|
||||
RUN export POETRY_CACHE_DIR && poetry install --without evaluation,llama-index --no-root && rm -rf $POETRY_CACHE_DIR
|
||||
|
||||
FROM python:3.12.3-slim AS runtime
|
||||
FROM python:3.12.3-slim AS openhands-app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -37,10 +37,14 @@ ARG OPENHANDS_BUILD_VERSION #re-declare for this section
|
||||
ENV RUN_AS_OPENHANDS=true
|
||||
# A random number--we need this to be different from the user's UID on the host machine
|
||||
ENV OPENHANDS_USER_ID=42420
|
||||
ENV SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
ENV SANDBOX_LOCAL_RUNTIME_URL=http://host.docker.internal
|
||||
ENV USE_HOST_NETWORK=false
|
||||
ENV WORKSPACE_BASE=/opt/workspace_base
|
||||
ENV OPENHANDS_BUILD_VERSION=$OPENHANDS_BUILD_VERSION
|
||||
ENV SANDBOX_USER_ID=0
|
||||
ENV FILE_STORE=local
|
||||
ENV FILE_STORE_PATH=/.openhands-state
|
||||
RUN mkdir -p $FILE_STORE_PATH
|
||||
RUN mkdir -p $WORKSPACE_BASE
|
||||
|
||||
RUN apt-get update -y \
|
||||
@@ -67,13 +71,15 @@ ENV VIRTUAL_ENV=/app/.venv \
|
||||
COPY --chown=openhands:app --chmod=770 --from=backend-builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||
RUN playwright install --with-deps chromium
|
||||
|
||||
COPY --chown=openhands:app --chmod=770 ./microagents ./microagents
|
||||
COPY --chown=openhands:app --chmod=770 ./openhands ./openhands
|
||||
COPY --chown=openhands:app --chmod=777 ./openhands/runtime/plugins ./openhands/runtime/plugins
|
||||
COPY --chown=openhands:app --chmod=770 ./agenthub ./agenthub
|
||||
COPY --chown=openhands:app --chmod=770 ./pyproject.toml ./pyproject.toml
|
||||
COPY --chown=openhands:app --chmod=770 ./poetry.lock ./poetry.lock
|
||||
COPY --chown=openhands:app --chmod=770 ./README.md ./README.md
|
||||
COPY --chown=openhands:app --chmod=770 ./MANIFEST.in ./MANIFEST.in
|
||||
COPY --chown=openhands:app --chmod=770 ./openhands/agenthub ./openhands/agenthub
|
||||
COPY --chown=openhands:app ./pyproject.toml ./pyproject.toml
|
||||
COPY --chown=openhands:app ./poetry.lock ./poetry.lock
|
||||
COPY --chown=openhands:app ./README.md ./README.md
|
||||
COPY --chown=openhands:app ./MANIFEST.in ./MANIFEST.in
|
||||
COPY --chown=openhands:app ./LICENSE ./LICENSE
|
||||
|
||||
# This is run as "openhands" user, and will create __pycache__ with openhands:openhands ownership
|
||||
RUN python openhands/core/download.py # No-op to download assets
|
||||
@@ -81,7 +87,7 @@ RUN python openhands/core/download.py # No-op to download assets
|
||||
# openhands:openhands -> openhands:app
|
||||
RUN find /app \! -group app -exec chgrp app {} +
|
||||
|
||||
COPY --chown=openhands:app --chmod=770 --from=frontend-builder /app/dist ./frontend/dist
|
||||
COPY --chown=openhands:app --chmod=770 --from=frontend-builder /app/build ./frontend/build
|
||||
COPY --chown=openhands:app --chmod=770 ./containers/app/entrypoint.sh /app/entrypoint.sh
|
||||
|
||||
USER root
|
||||
|
||||
@@ -18,6 +18,11 @@ if [ -z "$SANDBOX_USER_ID" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$WORKSPACE_MOUNT_PATH" ]; then
|
||||
# This is set to /opt/workspace in the Dockerfile. But if the user isn't mounting, we want to unset it so that OpenHands doesn't mount at all
|
||||
unset WORKSPACE_BASE
|
||||
fi
|
||||
|
||||
if [[ "$SANDBOX_USER_ID" -eq 0 ]]; then
|
||||
echo "Running OpenHands as root"
|
||||
export RUN_AS_OPENHANDS=false
|
||||
|
||||
@@ -1,13 +1,40 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
image_name=$1
|
||||
org_name=$2
|
||||
# Initialize variables with default values
|
||||
image_name=""
|
||||
org_name=""
|
||||
push=0
|
||||
if [[ $3 == "--push" ]]; then
|
||||
push=1
|
||||
load=0
|
||||
tag_suffix=""
|
||||
|
||||
# Function to display usage information
|
||||
usage() {
|
||||
echo "Usage: $0 -i <image_name> [-o <org_name>] [--push] [--load] [-t <tag_suffix>]"
|
||||
echo " -i: Image name (required)"
|
||||
echo " -o: Organization name"
|
||||
echo " --push: Push the image"
|
||||
echo " --load: Load the image"
|
||||
echo " -t: Tag suffix"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parse command-line options
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-i) image_name="$2"; shift 2 ;;
|
||||
-o) org_name="$2"; shift 2 ;;
|
||||
--push) push=1; shift ;;
|
||||
--load) load=1; shift ;;
|
||||
-t) tag_suffix="$2"; shift 2 ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
# Check if required arguments are provided
|
||||
if [[ -z "$image_name" ]]; then
|
||||
echo "Error: Image name is required."
|
||||
usage
|
||||
fi
|
||||
tag_suffix=$4
|
||||
|
||||
echo "Building: $image_name"
|
||||
tags=()
|
||||
@@ -17,10 +44,10 @@ OPENHANDS_BUILD_VERSION="dev"
|
||||
cache_tag_base="buildcache"
|
||||
cache_tag="$cache_tag_base"
|
||||
|
||||
if [[ -n $GITHUB_SHA ]]; then
|
||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||
if [[ -n $RELEVANT_SHA ]]; then
|
||||
git_hash=$(git rev-parse --short "$RELEVANT_SHA")
|
||||
tags+=("$git_hash")
|
||||
tags+=("$GITHUB_SHA")
|
||||
tags+=("$RELEVANT_SHA")
|
||||
fi
|
||||
|
||||
if [[ -n $GITHUB_REF_NAME ]]; then
|
||||
@@ -71,9 +98,9 @@ if [[ -n "$org_name" ]]; then
|
||||
DOCKER_ORG="$org_name"
|
||||
fi
|
||||
|
||||
# If $DOCKER_IMAGE_HASH_TAG is set, add it to the tags
|
||||
if [[ -n "$DOCKER_IMAGE_HASH_TAG" ]]; then
|
||||
tags+=("$DOCKER_IMAGE_HASH_TAG")
|
||||
# If $DOCKER_IMAGE_SOURCE_TAG is set, add it to the tags
|
||||
if [[ -n "$DOCKER_IMAGE_SOURCE_TAG" ]]; then
|
||||
tags+=("$DOCKER_IMAGE_SOURCE_TAG")
|
||||
fi
|
||||
# If $DOCKER_IMAGE_TAG is set, add it to the tags
|
||||
if [[ -n "$DOCKER_IMAGE_TAG" ]]; then
|
||||
@@ -95,14 +122,35 @@ if [[ $push -eq 1 ]]; then
|
||||
args+=" --cache-to=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag,mode=max"
|
||||
fi
|
||||
|
||||
if [[ $load -eq 1 ]]; then
|
||||
args+=" --load"
|
||||
fi
|
||||
|
||||
echo "Args: $args"
|
||||
|
||||
# Modify the platform selection based on --load flag
|
||||
if [[ $load -eq 1 ]]; then
|
||||
# When loading, build only for the current platform
|
||||
platform=$(docker version -f '{{.Server.Os}}/{{.Server.Arch}}')
|
||||
else
|
||||
# For push or without load, build for multiple platforms
|
||||
platform="linux/amd64,linux/arm64"
|
||||
fi
|
||||
|
||||
echo "Building for platform(s): $platform"
|
||||
|
||||
docker buildx build \
|
||||
$args \
|
||||
--build-arg OPENHANDS_BUILD_VERSION="$OPENHANDS_BUILD_VERSION" \
|
||||
--cache-from=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag \
|
||||
--cache-from=type=registry,ref=$DOCKER_REPOSITORY:$cache_tag_base-main \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--platform $platform \
|
||||
--provenance=false \
|
||||
-f "$dir/Dockerfile" \
|
||||
"$DOCKER_BASE_DIR"
|
||||
|
||||
# If load was requested, print the loaded images
|
||||
if [[ $load -eq 1 ]]; then
|
||||
echo "Local images built:"
|
||||
docker images "$DOCKER_REPOSITORY" --format "{{.Repository}}:{{.Tag}}"
|
||||
fi
|
||||
|
||||
124
containers/dev/Dockerfile
Normal file
124
containers/dev/Dockerfile
Normal file
@@ -0,0 +1,124 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
###
|
||||
FROM ubuntu:22.04 AS dind
|
||||
|
||||
# https://docs.docker.com/engine/install/ubuntu/
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
curl \
|
||||
&& install -m 0755 -d /etc/apt/keyrings \
|
||||
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \
|
||||
&& chmod a+r /etc/apt/keyrings/docker.asc \
|
||||
&& echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
|
||||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
docker-ce \
|
||||
docker-ce-cli \
|
||||
containerd.io \
|
||||
docker-buildx-plugin \
|
||||
docker-compose-plugin \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get clean \
|
||||
&& apt-get autoremove -y
|
||||
|
||||
###
|
||||
FROM dind AS openhands
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
#
|
||||
RUN apt-get update && apt-get install -y \
|
||||
bash \
|
||||
build-essential \
|
||||
curl \
|
||||
git \
|
||||
git-lfs \
|
||||
software-properties-common \
|
||||
make \
|
||||
netcat \
|
||||
sudo \
|
||||
wget \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get clean \
|
||||
&& apt-get autoremove -y
|
||||
|
||||
# https://github.com/cli/cli/blob/trunk/docs/install_linux.md
|
||||
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
|
||||
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
|
||||
&& apt-get update && apt-get -y install \
|
||||
gh \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get clean \
|
||||
&& apt-get autoremove -y
|
||||
|
||||
# Python 3.12
|
||||
RUN add-apt-repository ppa:deadsnakes/ppa \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y python3.12 python3.12-venv python3.12-dev python3-pip \
|
||||
&& ln -s /usr/bin/python3.12 /usr/bin/python
|
||||
|
||||
# NodeJS >= 18.17.1
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
|
||||
&& apt-get install -y nodejs
|
||||
|
||||
# Poetry >= 1.8
|
||||
RUN curl -fsSL https://install.python-poetry.org | python3.12 - \
|
||||
&& ln -s ~/.local/bin/poetry /usr/local/bin/poetry
|
||||
|
||||
#
|
||||
RUN <<EOF
|
||||
#!/bin/bash
|
||||
printf "#!/bin/bash
|
||||
set +x
|
||||
uname -a
|
||||
docker --version
|
||||
gh --version | head -n 1
|
||||
git --version
|
||||
#
|
||||
python --version
|
||||
echo node `node --version`
|
||||
echo npm `npm --version`
|
||||
poetry --version
|
||||
netcat -h 2>&1 | head -n 1
|
||||
" > /version.sh
|
||||
chmod a+x /version.sh
|
||||
EOF
|
||||
|
||||
###
|
||||
FROM openhands AS dev
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
dnsutils \
|
||||
file \
|
||||
iproute2 \
|
||||
jq \
|
||||
lsof \
|
||||
ripgrep \
|
||||
silversearcher-ag \
|
||||
vim \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get clean \
|
||||
&& apt-get autoremove -y
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# cache build dependencies
|
||||
RUN \
|
||||
--mount=type=bind,source=./,target=/app/ \
|
||||
<<EOF
|
||||
#!/bin/bash
|
||||
make -s clean
|
||||
make -s check-dependencies
|
||||
make -s install-python-dependencies
|
||||
|
||||
# NOTE
|
||||
# node_modules are .dockerignore-d therefore not mountable
|
||||
# make -s install-frontend-dependencies
|
||||
EOF
|
||||
|
||||
#
|
||||
CMD ["bash"]
|
||||
57
containers/dev/README.md
Normal file
57
containers/dev/README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Develop in Docker
|
||||
|
||||
> [!WARNING]
|
||||
> This is not officially supported and may not work.
|
||||
|
||||
Install [Docker](https://docs.docker.com/engine/install/) on your host machine and run:
|
||||
|
||||
```bash
|
||||
make docker-dev
|
||||
# same as:
|
||||
cd ./containers/dev
|
||||
./dev.sh
|
||||
```
|
||||
|
||||
It could take some time if you are running for the first time as Docker will pull all the tools required for building OpenHands. The next time you run again, it should be instant.
|
||||
|
||||
## Build and run
|
||||
|
||||
If everything goes well, you should be inside a container after Docker finishes building the `openhands:dev` image similar to the following:
|
||||
|
||||
```bash
|
||||
Build and run in Docker ...
|
||||
root@93fc0005fcd2:/app#
|
||||
```
|
||||
|
||||
You may now proceed with the normal [build and run](../../Development.md) workflow as if you were on the host.
|
||||
|
||||
## Make changes
|
||||
|
||||
The source code on the host is mounted as `/app` inside docker. You may edit the files as usual either inside the Docker container or on your host with your favorite IDE/editors.
|
||||
|
||||
The following are also mapped as readonly from your host:
|
||||
|
||||
```yaml
|
||||
# host credentials
|
||||
- $HOME/.git-credentials:/root/.git-credentials:ro
|
||||
- $HOME/.gitconfig:/root/.gitconfig:ro
|
||||
- $HOME/.npmrc:/root/.npmrc:ro
|
||||
```
|
||||
|
||||
## VSCode
|
||||
|
||||
Alternatively, if you use VSCode, you could also [attach to the running container](https://code.visualstudio.com/docs/devcontainers/attach-container).
|
||||
|
||||
See details for [developing in docker](https://code.visualstudio.com/docs/devcontainers/containers) or simply ask `OpenHands` ;-)
|
||||
|
||||
## Rebuild dev image
|
||||
|
||||
You could optionally pass additional options to the build script.
|
||||
|
||||
```bash
|
||||
make docker-dev OPTIONS="--build"
|
||||
# or
|
||||
./containers/dev/dev.sh --build
|
||||
```
|
||||
|
||||
See [docker compose run](https://docs.docker.com/reference/cli/docker/compose/run/) for more options.
|
||||
38
containers/dev/compose.yml
Normal file
38
containers/dev/compose.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
#
|
||||
services:
|
||||
dev:
|
||||
privileged: true
|
||||
build:
|
||||
context: ${OPENHANDS_WORKSPACE:-../../}
|
||||
dockerfile: ./containers/dev/Dockerfile
|
||||
image: openhands:dev
|
||||
container_name: openhands-dev
|
||||
environment:
|
||||
- BACKEND_HOST=${BACKEND_HOST:-"0.0.0.0"}
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.21-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ${WORKSPACE_BASE:-$PWD/workspace}:/opt/workspace_base
|
||||
# source code
|
||||
- ${OPENHANDS_WORKSPACE:-../../}:/app
|
||||
# host credentials
|
||||
- $HOME/.git-credentials:/root/.git-credentials:ro
|
||||
- $HOME/.gitconfig:/root/.gitconfig:ro
|
||||
- $HOME/.npmrc:/root/.npmrc:ro
|
||||
# cache
|
||||
- cache-data:/root/.cache
|
||||
pull_policy: never
|
||||
stdin_open: true
|
||||
tty: true
|
||||
|
||||
##
|
||||
volumes:
|
||||
cache-data:
|
||||
39
containers/dev/dev.sh
Executable file
39
containers/dev/dev.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
set -o pipefail
|
||||
|
||||
function get_docker() {
|
||||
echo "Docker is required to build and run OpenHands."
|
||||
echo "https://docs.docker.com/get-started/get-docker/"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function check_tools() {
|
||||
command -v docker &>/dev/null || get_docker
|
||||
}
|
||||
|
||||
function exit_if_indocker() {
|
||||
if [ -f /.dockerenv ]; then
|
||||
echo "Running inside a Docker container. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
exit_if_indocker
|
||||
|
||||
check_tools
|
||||
|
||||
##
|
||||
OPENHANDS_WORKSPACE=$(git rev-parse --show-toplevel)
|
||||
|
||||
cd "$OPENHANDS_WORKSPACE/containers/dev/" || exit 1
|
||||
|
||||
##
|
||||
export BACKEND_HOST="0.0.0.0"
|
||||
#
|
||||
export SANDBOX_USER_ID=$(id -u)
|
||||
export WORKSPACE_BASE=${WORKSPACE_BASE:-$OPENHANDS_WORKSPACE/workspace}
|
||||
|
||||
docker compose run --rm --service-ports "$@" dev
|
||||
|
||||
##
|
||||
@@ -1,11 +1,12 @@
|
||||
# Dynamic constructed Dockerfile
|
||||
# Dynamically constructed Dockerfile
|
||||
|
||||
This folder builds runtime image (sandbox), which will use a `Dockerfile` that is dynamically generated depends on the `base_image` AND a [Python source distribution](https://docs.python.org/3.10/distutils/sourcedist.html) that's based on the current commit of `openhands`.
|
||||
This folder builds a runtime image (sandbox), which will use a dynamically generated `Dockerfile`
|
||||
that depends on the `base_image` **AND** a [Python source distribution](https://docs.python.org/3.10/distutils/sourcedist.html) that is based on the current commit of `openhands`.
|
||||
|
||||
The following command will generate Dockerfile for `ubuntu:22.04` and the source distribution `.tar` into `containers/runtime`.
|
||||
The following command will generate a `Dockerfile` file for `nikolaik/python-nodejs:python3.12-nodejs22` (the default base image), an updated `config.sh` and the runtime source distribution files/folders into `containers/runtime`:
|
||||
|
||||
```bash
|
||||
poetry run python3 openhands/runtime/utils/runtime_build.py \
|
||||
--base_image ubuntu:22.04 \
|
||||
--base_image nikolaik/python-nodejs:python3.12-nodejs22 \
|
||||
--build_folder containers/runtime
|
||||
```
|
||||
|
||||
@@ -4,4 +4,4 @@ DOCKER_BASE_DIR="./containers/runtime"
|
||||
DOCKER_IMAGE=runtime
|
||||
# These variables will be appended by the runtime_build.py script
|
||||
# DOCKER_IMAGE_TAG=
|
||||
# DOCKER_IMAGE_HASH_TAG=
|
||||
# DOCKER_IMAGE_SOURCE_TAG=
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# install basic packages
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
vim \
|
||||
nano \
|
||||
unzip \
|
||||
zip \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
python3-dev \
|
||||
build-essential \
|
||||
openssh-server \
|
||||
sudo \
|
||||
gcc \
|
||||
jq \
|
||||
g++ \
|
||||
make \
|
||||
iproute2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p -m0755 /var/run/sshd
|
||||
|
||||
# symlink python3 to python
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python
|
||||
|
||||
# ==== OpenHands Runtime Client ====
|
||||
RUN mkdir -p /openhands && mkdir -p /openhands/logs && chmod 777 /openhands/logs
|
||||
RUN wget --progress=bar:force -O Miniforge3.sh "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
|
||||
RUN bash Miniforge3.sh -b -p /openhands/miniforge3
|
||||
RUN chmod -R g+w /openhands/miniforge3
|
||||
RUN bash -c ". /openhands/miniforge3/etc/profile.d/conda.sh && conda config --set changeps1 False && conda config --append channels conda-forge"
|
||||
RUN echo "" > /openhands/bash.bashrc
|
||||
RUN rm -f Miniforge3.sh
|
||||
|
||||
# - agentskills dependencies
|
||||
RUN /openhands/miniforge3/bin/pip install --upgrade pip
|
||||
RUN /openhands/miniforge3/bin/pip install jupyterlab notebook jupyter_kernel_gateway flake8
|
||||
RUN /openhands/miniforge3/bin/pip install python-docx PyPDF2 python-pptx pylatexenc openai
|
||||
RUN /openhands/miniforge3/bin/pip install python-dotenv toml termcolor pydantic python-docx pyyaml docker pexpect tenacity e2b browsergym minio
|
||||
@@ -1,4 +0,0 @@
|
||||
DOCKER_REGISTRY=ghcr.io
|
||||
DOCKER_ORG=all-hands-ai
|
||||
DOCKER_IMAGE=sandbox
|
||||
DOCKER_BASE_DIR="."
|
||||
@@ -38,6 +38,6 @@ repos:
|
||||
- id: mypy
|
||||
additional_dependencies:
|
||||
[types-requests, types-setuptools, types-pyyaml, types-toml]
|
||||
entry: mypy --config-file dev_config/python/mypy.ini openhands/ agenthub/
|
||||
entry: mypy --config-file dev_config/python/mypy.ini openhands/
|
||||
always_run: true
|
||||
pass_filenames: false
|
||||
|
||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
services:
|
||||
openhands:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./containers/app/Dockerfile
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of openhands-state for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ~/.openhands-state:/.openhands-state
|
||||
- ${WORKSPACE_BASE:-$PWD/workspace}:/opt/workspace_base
|
||||
pull_policy: build
|
||||
stdin_open: true
|
||||
tty: true
|
||||
48
docs/DOC_STYLE_GUIDE.md
Normal file
48
docs/DOC_STYLE_GUIDE.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Documentation Style Guide
|
||||
|
||||
## General Writing Principles
|
||||
|
||||
- **Clarity & Conciseness**: Always prioritize clarity and brevity. Avoid unnecessary jargon or overly complex explanations.
|
||||
Keep sentences short and to the point.
|
||||
- **Gradual Complexity**: Start with the simplest, most basic setup, and then gradually introduce more advanced
|
||||
concepts and configurations.
|
||||
|
||||
## Formatting Guidelines
|
||||
|
||||
### Headers
|
||||
|
||||
Use **Title Case** for the first and second level headers.
|
||||
|
||||
Example:
|
||||
- **Basic Usage**
|
||||
- **Advanced Configuration Options**
|
||||
|
||||
### Lists
|
||||
|
||||
When listing items or options, use bullet points to enhance readability.
|
||||
|
||||
Example:
|
||||
- Option A
|
||||
- Option B
|
||||
- Option C
|
||||
|
||||
### Procedures
|
||||
|
||||
For instructions or processes that need to be followed in a specific order, use numbered steps.
|
||||
|
||||
Example:
|
||||
1. Step one: Do this.
|
||||
2. Step two: Complete this action.
|
||||
3. Step three: Verify the result.
|
||||
|
||||
### Code Blocks
|
||||
|
||||
* Use code blocks for multi-line inputs, outputs, commands and code samples.
|
||||
|
||||
Example:
|
||||
```bash
|
||||
docker run -it \
|
||||
-e THIS=this \
|
||||
-e THAT=that
|
||||
...
|
||||
```
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
# Documentation Python
|
||||
|
||||
Les documents apparaîtront ici après le déploiement.
|
||||
La documentation apparaîtra ici après le déploiement.
|
||||
|
||||
@@ -1,53 +1,28 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
# 📚 Divers {#misc}
|
||||
|
||||
## ⭐️ Stratégie de Recherche {#research-strategy}
|
||||
# À propos d'OpenHands
|
||||
|
||||
La réalisation d'une réplication complète des applications de production avec les LLM est une entreprise complexe. Notre stratégie implique :
|
||||
## Stratégie de recherche
|
||||
|
||||
1. **Recherche Technique de Base :** Se concentrer sur la recherche fondamentale pour comprendre et améliorer les aspects techniques de la génération et de la gestion de code.
|
||||
2. **Compétences Spécialisées :** Améliorer l'efficacité des composants de base grâce à la curation des données, aux méthodes de formation, et plus encore.
|
||||
3. **Planification des Tâches :** Développer des capacités pour la détection de bogues, la gestion du code source et l'optimisation.
|
||||
4. **Évaluation :** Établir des métriques d'évaluation complètes pour mieux comprendre et améliorer nos modèles.
|
||||
La réplication complète d'applications de niveau production avec des LLM est une entreprise complexe. Notre stratégie implique :
|
||||
|
||||
## 🚧 Agent Par Défaut {#default-agent}
|
||||
1. **Recherche technique fondamentale :** Se concentrer sur la recherche fondamentale pour comprendre et améliorer les aspects techniques de la génération et de la gestion du code
|
||||
2. **Capacités spécialisées :** Améliorer l'efficacité des composants de base grâce à la curation des données, aux méthodes d'entraînement, et plus encore
|
||||
3. **Planification des tâches :** Développer des capacités pour la détection des bugs, la gestion des bases de code et l'optimisation
|
||||
4. **Évaluation :** Établir des métriques d'évaluation complètes pour mieux comprendre et améliorer nos modèles
|
||||
|
||||
- Notre agent par défaut est actuellement le CodeActAgent, capable de générer du code et de gérer des fichiers. Nous travaillons sur d'autres implémentations d'agents, y compris [SWE Agent](https://swe-agent.com/). Vous pouvez [lire à propos de notre ensemble actuel d'agents ici](./agents).
|
||||
## Agent par défaut
|
||||
|
||||
## 🤝 Comment Contribuer {#how-to-contribute}
|
||||
Notre Agent par défaut est actuellement le [CodeActAgent](agents), qui est capable de générer du code et de gérer des fichiers.
|
||||
|
||||
OpenHands est un projet communautaire, et nous accueillons les contributions de tout le monde. Que vous soyez développeur, chercheur, ou simplement enthousiaste à l'idée de faire progresser le domaine de l'ingénierie logicielle avec l'IA, il existe de nombreuses façons de vous impliquer :
|
||||
## Construit avec
|
||||
|
||||
- **Contributions de Code :** Aidez-nous à développer les fonctionnalités de base, l'interface frontend ou les solutions de sandboxing.
|
||||
- **Recherche et Évaluation :** Contribuez à notre compréhension des LLM en ingénierie logicielle, participez à l'évaluation des modèles ou suggérez des améliorations.
|
||||
- **Retour d'Information et Tests :** Utilisez l'ensemble d'outils OpenHands, signalez des bogues, suggérez des fonctionnalités ou fournissez des retours sur l'ergonomie.
|
||||
|
||||
Pour plus de détails, veuillez consulter [ce document](https://github.com/All-Hands-AI/OpenHands/blob/main/CONTRIBUTING.md).
|
||||
|
||||
## 🤖 Rejoignez Notre Communauté {#join-our-community}
|
||||
|
||||
Nous avons maintenant à la fois un espace de travail Slack pour la collaboration sur la construction d'OpenHands et un serveur Discord pour discuter de tout ce qui est lié, par exemple, à ce projet, aux LLM, aux agents, etc.
|
||||
|
||||
- [Espace de travail Slack](https://join.slack.com/t/opendevin/shared_invite/zt-2oikve2hu-UDxHeo8nsE69y6T7yFX_BA)
|
||||
- [Serveur Discord](https://discord.gg/ESHStjSjD4)
|
||||
|
||||
Si vous souhaitez contribuer, n'hésitez pas à rejoindre notre communauté. Simplifions l'ingénierie logicielle ensemble !
|
||||
|
||||
🐚 **Codez moins, créez plus avec OpenHands.**
|
||||
|
||||
[](https://star-history.com/#All-Hands-AI/OpenHands&Date)
|
||||
|
||||
## 🛠️ Construit Avec {#built-with}
|
||||
|
||||
OpenHands est construit en utilisant une combinaison de cadres et de bibliothèques puissants, offrant une base robuste pour son développement. Voici les technologies clés utilisées dans le projet :
|
||||
OpenHands est construit en utilisant une combinaison de frameworks et de bibliothèques puissants, fournissant une base solide pour son développement. Voici les principales technologies utilisées dans le projet :
|
||||
|
||||
       
|
||||
|
||||
Veuillez noter que la sélection de ces technologies est en cours, et que des technologies supplémentaires peuvent être ajoutées ou des existantes supprimées au fur et à mesure de l'évolution du projet. Nous nous efforçons d'adopter les outils les plus adaptés et efficaces pour améliorer les capacités d'OpenHands.
|
||||
Veuillez noter que la sélection de ces technologies est en cours et que des technologies supplémentaires peuvent être ajoutées ou des technologies existantes peuvent être supprimées à mesure que le projet évolue. Nous nous efforçons d'adopter les outils les plus appropriés et les plus efficaces pour améliorer les capacités d'OpenHands.
|
||||
|
||||
## 📜 Licence {#license}
|
||||
## Licence
|
||||
|
||||
Distribué sous la licence MIT. Voir [notre licence](https://github.com/All-Hands-AI/OpenHands/blob/main/LICENSE) pour plus d'informations.
|
||||
Distribué sous la [Licence](https://github.com/All-Hands-AI/OpenHands/blob/main/LICENSE) MIT.
|
||||
|
||||
@@ -1,98 +1,25 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# 🧠 Agents et Capacités
|
||||
|
||||
## Agent CodeAct
|
||||
# 🧠 Agent Principal et Capacités
|
||||
|
||||
## CodeActAgent
|
||||
|
||||
### Description
|
||||
|
||||
Cet agent implémente l'idée CodeAct ([article](https://arxiv.org/abs/2402.01030), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) qui consolide les **act**ions des agents LLM en un espace d'action **code** unifié pour à la fois la _simplicité_ et la _performance_ (voir article pour plus de détails).
|
||||
Cet agent implémente l'idée de CodeAct ([article](https://arxiv.org/abs/2402.01030), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) qui consolide les **act**ions des agents LLM dans un espace d'action de **code** unifié à la fois pour la _simplicité_ et la _performance_.
|
||||
|
||||
L'idée conceptuelle est illustrée ci-dessous. À chaque tour, l'agent peut :
|
||||
|
||||
1. **Converse** : Communiquer avec les humains en langage naturel pour demander des clarifications, des confirmations, etc.
|
||||
2. **CodeAct** : Choisir d'accomplir la tâche en exécutant du code
|
||||
1. **Converser** : Communiquer avec les humains en langage naturel pour demander des clarifications, des confirmations, etc.
|
||||
2. **CodeAct** : Choisir d'effectuer la tâche en exécutant du code
|
||||
|
||||
- Exécuter toute commande `bash` Linux valide
|
||||
- Exécuter tout code `Python` valide avec [un interpréteur Python interactif](https://ipython.org/). Cela est simulé à travers la commande `bash`, voir le système de plugin ci-dessous pour plus de détails.
|
||||
- Exécuter n'importe quelle commande Linux `bash` valide
|
||||
- Exécuter n'importe quel code `Python` valide avec [un interpréteur Python interactif](https://ipython.org/). Ceci est simulé via une commande `bash`, voir le système de plugin ci-dessous pour plus de détails.
|
||||
|
||||

|
||||
|
||||
### Système de Plugin
|
||||
|
||||
Pour rendre l'agent CodeAct plus puissant avec seulement l'accès à l'espace d'action `bash`, l'agent CodeAct exploite le système de plugins d'OpenHands:
|
||||
|
||||
- [Plugin Jupyter](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/runtime/plugins/jupyter) : pour l'exécution d'IPython via la commande bash
|
||||
- [Plugin outil agent SWE](https://github.com/All-Hands-AI/OpenHands/tree/main/openhands/runtime/plugins/swe_agent_commands) : Outils de ligne de commande bash puissants pour les tâches de développement logiciel introduits par [swe-agent](https://github.com/princeton-nlp/swe-agent).
|
||||
|
||||
### Démonstration
|
||||
### Démo
|
||||
|
||||
https://github.com/All-Hands-AI/OpenHands/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac
|
||||
|
||||
_Exemple de CodeActAgent avec `gpt-4-turbo-2024-04-09` effectuant une tâche de science des données (régression linéaire)_
|
||||
|
||||
### Actions
|
||||
|
||||
`Action`,
|
||||
`CmdRunAction`,
|
||||
`IPythonRunCellAction`,
|
||||
`AgentEchoAction`,
|
||||
`AgentFinishAction`,
|
||||
`AgentTalkAction`
|
||||
|
||||
### Observations
|
||||
|
||||
`CmdOutputObservation`,
|
||||
`IPythonRunCellObservation`,
|
||||
`AgentMessageObservation`,
|
||||
`UserMessageObservation`
|
||||
|
||||
### Méthodes
|
||||
|
||||
| Méthode | Description |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `__init__` | Initialise un agent avec `llm` et une liste de messages `list[Mapping[str, str]]` |
|
||||
| `step` | Effectue une étape en utilisant l'agent CodeAct. Cela inclut la collecte d'informations sur les étapes précédentes et invite le modèle à exécuter une commande. |
|
||||
|
||||
### En cours de réalisation & prochaine étape
|
||||
|
||||
[] Support de la navigation sur le web
|
||||
[] Compléter le workflow pour l'agent CodeAct afin de soumettre des PRs Github
|
||||
|
||||
## Agent Planificateur
|
||||
|
||||
### Description
|
||||
|
||||
L'agent planificateur utilise une stratégie d'incitation spéciale pour créer des plans à long terme pour résoudre les problèmes.
|
||||
L'agent reçoit ses paires action-observation précédentes, la tâche actuelle, et un indice basé sur la dernière action effectuée à chaque étape.
|
||||
|
||||
### Actions
|
||||
|
||||
`NullAction`,
|
||||
`CmdRunAction`,
|
||||
`BrowseURLAction`,
|
||||
`GithubPushAction`,
|
||||
`FileReadAction`,
|
||||
`FileWriteAction`,
|
||||
`AgentThinkAction`,
|
||||
`AgentFinishAction`,
|
||||
`AgentSummarizeAction`,
|
||||
`AddTaskAction`,
|
||||
`ModifyTaskAction`,
|
||||
|
||||
### Observations
|
||||
|
||||
`Observation`,
|
||||
`NullObservation`,
|
||||
`CmdOutputObservation`,
|
||||
`FileReadObservation`,
|
||||
`BrowserOutputObservation`
|
||||
|
||||
### Méthodes
|
||||
|
||||
| Méthode | Description |
|
||||
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `__init__` | Initialise un agent avec `llm` |
|
||||
| `step` | Vérifie si l'étape actuelle est terminée, retourne `AgentFinishAction` si oui. Sinon, crée une incitation de planification et l'envoie au modèle pour inférence, en ajoutant le résultat comme prochaine action. |
|
||||
_Exemple de CodeActAgent avec `gpt-4-turbo-2024-04-09` effectuant une tâche de science des données (régression linéaire)_.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
# 🏛️ Architecture du Système
|
||||
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<img src="https://github.com/All-Hands-AI/OpenHands/assets/16201837/97d747e3-29d8-4ccb-8d34-6ad1adb17f38" alt="OpenHands System Architecture Diagram Jul 4 2024" />
|
||||
<p><em>Diagramme de l'Architecture du Système OpenHands (4 juillet 2024)</em></p>
|
||||
</div>
|
||||
|
||||
Ceci est une vue d'ensemble de haut niveau de l'architecture du système. Le système est divisé en deux composants principaux : le frontend et le backend. Le frontend est responsable de la gestion des interactions utilisateur et de l'affichage des résultats. Le backend est responsable de la gestion de la logique métier et de l'exécution des agents.
|
||||
|
||||
# Architecture du Frontend {#frontend-architecture-fr}
|
||||
|
||||

|
||||
|
||||
Cette vue d'ensemble est simplifiée pour montrer les principaux composants et leurs interactions. Pour une vue plus détaillée de l'architecture du backend, voir la section Architecture du Backend ci-dessous.
|
||||
|
||||
# Architecture du Backend {#backend-architecture-fr}
|
||||
|
||||
_**Avertissement** : L'architecture du backend est en cours de développement et est sujette à changement. Le diagramme suivant montre l'architecture actuelle du backend basée sur le commit indiqué dans le pied de page du diagramme._
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Mise à jour de ce Diagramme</summary>
|
||||
<div>
|
||||
La génération du diagramme d'architecture du backend est partiellement automatisée.
|
||||
Le diagramme est généré à partir des indications de type dans le code en utilisant l'outil py2puml. Le diagramme est ensuite manuellement revu, ajusté et exporté en PNG et SVG.
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Environnement python fonctionnel dans lequel openhands est exécutable
|
||||
(selon les instructions du fichier README.md à la racine du dépôt)
|
||||
- [py2puml](https://github.com/lucsorel/py2puml) installé
|
||||
|
||||
## Étapes
|
||||
|
||||
1. Générer automatiquement le diagramme en exécutant la commande suivante depuis la racine du dépôt :
|
||||
`py2puml openhands openhands > docs/architecture/backend_architecture.puml`
|
||||
|
||||
2. Ouvrir le fichier généré dans un éditeur PlantUML, par ex. Visual Studio Code avec l'extension PlantUML ou [PlantText](https://www.planttext.com/)
|
||||
|
||||
3. Revoir le PUML généré et effectuer tous les ajustements nécessaires au diagramme (ajouter les parties manquantes, corriger les erreurs, améliorer le positionnement).
|
||||
_py2puml crée le diagramme en se basant sur les indications de type dans le code, donc des indications manquantes ou incorrectes peuvent entraîner un diagramme incomplet ou incorrect._
|
||||
|
||||
4. Revoir la différence entre le nouveau diagramme et le précédent et vérifier manuellement si les changements sont corrects.
|
||||
_S'assurer de ne pas supprimer des parties qui ont été ajoutées manuellement au diagramme par le passé et qui sont toujours pertinentes._
|
||||
|
||||
5. Ajouter le hash du commit qui a été utilisé pour générer le diagramme dans le pied de page du diagramme.
|
||||
|
||||
6. Exporter le diagramme sous forme de fichiers PNG et SVG et remplacer les diagrammes existants dans le répertoire `docs/architecture`. Cela peut être fait avec (par ex. [PlantText](https://www.planttext.com/))
|
||||
|
||||
</div>
|
||||
</details>
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
|
||||
# 📦 Runtime EventStream
|
||||
|
||||
Le Runtime EventStream d'OpenHands est le composant principal qui permet l'exécution sécurisée et flexible des actions des agents d'IA.
|
||||
Il crée un environnement en bac à sable (sandbox) en utilisant Docker, où du code arbitraire peut être exécuté en toute sécurité sans risquer le système hôte.
|
||||
|
||||
## Pourquoi avons-nous besoin d'un runtime en bac à sable ?
|
||||
|
||||
OpenHands doit exécuter du code arbitraire dans un environnement sécurisé et isolé pour plusieurs raisons :
|
||||
|
||||
1. Sécurité : L'exécution de code non fiable peut poser des risques importants pour le système hôte. Un environnement en bac à sable empêche le code malveillant d'accéder ou de modifier les ressources du système hôte
|
||||
2. Cohérence : Un environnement en bac à sable garantit que l'exécution du code est cohérente sur différentes machines et configurations, éliminant les problèmes du type "ça fonctionne sur ma machine"
|
||||
3. Contrôle des ressources : Le bac à sable permet un meilleur contrôle de l'allocation et de l'utilisation des ressources, empêchant les processus incontrôlés d'affecter le système hôte
|
||||
4. Isolation : Différents projets ou utilisateurs peuvent travailler dans des environnements isolés sans interférer les uns avec les autres ou avec le système hôte
|
||||
5. Reproductibilité : Les environnements en bac à sable facilitent la reproduction des bugs et des problèmes, car l'environnement d'exécution est cohérent et contrôlable
|
||||
|
||||
## Comment fonctionne le Runtime ?
|
||||
|
||||
Le système Runtime d'OpenHands utilise une architecture client-serveur implémentée avec des conteneurs Docker. Voici un aperçu de son fonctionnement :
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Image Docker personnalisée fournie par l'utilisateur] --> B[Backend OpenHands]
|
||||
B -->|Construit| C[Image OH Runtime]
|
||||
C -->|Lance| D[Exécuteur d'actions]
|
||||
D -->|Initialise| E[Navigateur]
|
||||
D -->|Initialise| F[Shell Bash]
|
||||
D -->|Initialise| G[Plugins]
|
||||
G -->|Initialise| L[Serveur Jupyter]
|
||||
|
||||
B -->|Génère| H[Agent]
|
||||
B -->|Génère| I[EventStream]
|
||||
I <--->|Exécute l'action pour
|
||||
obtenir l'observation
|
||||
via l'API REST
|
||||
| D
|
||||
|
||||
H -->|Génère l'action| I
|
||||
I -->|Obtient l'observation| H
|
||||
|
||||
subgraph "Conteneur Docker"
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
L
|
||||
end
|
||||
```
|
||||
|
||||
1. Entrée utilisateur : L'utilisateur fournit une image Docker de base personnalisée
|
||||
2. Construction de l'image : OpenHands construit une nouvelle image Docker (l'"image OH runtime") basée sur l'image fournie par l'utilisateur. Cette nouvelle image inclut le code spécifique à OpenHands, principalement le "client runtime"
|
||||
3. Lancement du conteneur : Lorsqu'OpenHands démarre, il lance un conteneur Docker en utilisant l'image OH runtime
|
||||
4. Initialisation du serveur d'exécution des actions : Le serveur d'exécution des actions initialise un `ActionExecutor` à l'intérieur du conteneur, mettant en place les composants nécessaires comme un shell bash et chargeant les plugins spécifiés
|
||||
5. Communication : Le backend OpenHands (`openhands/runtime/impl/eventstream/eventstream_runtime.py`) communique avec le serveur d'exécution des actions via une API RESTful, envoyant des actions et recevant des observations
|
||||
6. Exécution des actions : Le client runtime reçoit les actions du backend, les exécute dans l'environnement en bac à sable et renvoie les observations
|
||||
7. Retour des observations : Le serveur d'exécution des actions renvoie les résultats d'exécution au backend OpenHands sous forme d'observations
|
||||
|
||||
|
||||
Le rôle du client :
|
||||
- Il agit comme un intermédiaire entre le backend OpenHands et l'environnement en bac à sable
|
||||
- Il exécute différents types d'actions (commandes shell, opérations sur les fichiers, code Python, etc.) en toute sécurité dans le conteneur
|
||||
- Il gère l'état de l'environnement en bac à sable, y compris le répertoire de travail courant et les plugins chargés
|
||||
- Il formate et renvoie les observations au backend, assurant une interface cohérente pour le traitement des résultats
|
||||
|
||||
|
||||
## Comment OpenHands construit et maintient les images OH Runtime
|
||||
|
||||
L'approche d'OpenHands pour la construction et la gestion des images runtime assure l'efficacité, la cohérence et la flexibilité dans la création et la maintenance des images Docker pour les environnements de production et de développement.
|
||||
|
||||
Consultez le [code pertinent](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/utils/runtime_build.py) si vous souhaitez plus de détails.
|
||||
|
||||
### Système de balises d'images
|
||||
|
||||
OpenHands utilise un système à trois balises pour ses images runtime afin d'équilibrer la reproductibilité et la flexibilité.
|
||||
Les balises peuvent être dans l'un des 2 formats suivants :
|
||||
|
||||
- **Balise versionnée** : `oh_v{openhands_version}_{base_image}` (ex : `oh_v0.9.9_nikolaik_s_python-nodejs_t_python3.12-nodejs22`)
|
||||
- **Balise de verrouillage** : `oh_v{openhands_version}_{16_digit_lock_hash}` (ex : `oh_v0.9.9_1234567890abcdef`)
|
||||
- **Balise source** : `oh_v{openhands_version}_{16_digit_lock_hash}_{16_digit_source_hash}`
|
||||
(ex : `oh_v0.9.9_1234567890abcdef_1234567890abcdef`)
|
||||
|
||||
|
||||
#### Balise source - La plus spécifique
|
||||
|
||||
Il s'agit des 16 premiers chiffres du MD5 du hash du répertoire pour le répertoire source. Cela donne un hash
|
||||
uniquement pour la source d'openhands
|
||||
|
||||
|
||||
#### Balise de verrouillage
|
||||
|
||||
Ce hash est construit à partir des 16 premiers chiffres du MD5 de :
|
||||
- Le nom de l'image de base sur laquelle l'image a été construite (ex : `nikolaik/python-nodejs:python3.12-nodejs22`)
|
||||
- Le contenu du `pyproject.toml` inclus dans l'image.
|
||||
- Le contenu du `poetry.lock` inclus dans l'image.
|
||||
|
||||
Cela donne effectivement un hash pour les dépendances d'Openhands indépendamment du code source.
|
||||
|
||||
#### Balise versionnée - La plus générique
|
||||
|
||||
Cette balise est une concaténation de la version d'openhands et du nom de l'image de base (transformé pour s'adapter au standard des balises).
|
||||
|
||||
#### Processus de construction
|
||||
|
||||
Lors de la génération d'une image...
|
||||
|
||||
- **Pas de reconstruction** : OpenHands vérifie d'abord si une image avec la même **balise source la plus spécifique** existe. S'il existe une telle image,
|
||||
aucune construction n'est effectuée - l'image existante est utilisée.
|
||||
- **Reconstruction la plus rapide** : OpenHands vérifie ensuite si une image avec la **balise de verrouillage générique** existe. S'il existe une telle image,
|
||||
OpenHands construit une nouvelle image basée sur celle-ci, en contournant toutes les étapes d'installation (comme `poetry install` et
|
||||
`apt-get`) sauf une opération finale pour copier le code source actuel. La nouvelle image est balisée avec une
|
||||
balise **source** uniquement.
|
||||
- **Reconstruction correcte** : Si ni une balise **source** ni une balise **de verrouillage** n'existe, une image sera construite sur la base de l'image avec la balise **versionnée**.
|
||||
Dans l'image avec la balise versionnée, la plupart des dépendances devraient déjà être installées, ce qui permet de gagner du temps.
|
||||
- **Reconstruction la plus lente** : Si les trois balises n'existent pas, une toute nouvelle image est construite à partir de
|
||||
l'image de base (ce qui est une opération plus lente). Cette nouvelle image est balisée avec toutes les balises **source**, **de verrouillage** et **versionnée**.
|
||||
|
||||
Cette approche de balisage permet à OpenHands de gérer efficacement les environnements de développement et de production.
|
||||
|
||||
1. Un code source et un Dockerfile identiques produisent toujours la même image (via des balises basées sur des hashs)
|
||||
2. Le système peut reconstruire rapidement les images lorsque des changements mineurs se produisent (en s'appuyant sur des images compatibles récentes)
|
||||
3. La balise **de verrouillage** (ex : `runtime:oh_v0.9.3_1234567890abcdef`) pointe toujours vers la dernière version pour une combinaison particulière d'image de base, de dépendances et de version d'OpenHands
|
||||
|
||||
## Système de plugins du Runtime
|
||||
|
||||
Le Runtime d'OpenHands prend en charge un système de plugins qui permet d'étendre les fonctionnalités et de personnaliser l'environnement d'exécution. Les plugins sont initialisés lorsque le client runtime démarre.
|
||||
|
||||
Consultez [un exemple de plugin Jupyter ici](https://github.com/All-Hands-AI/OpenHands/blob/ecf4aed28b0cf7c18d4d8ff554883ba182fc6bdd/openhands/runtime/plugins/jupyter/__init__.py#L21-L55) si vous souhaitez implémenter votre propre plugin.
|
||||
|
||||
*Plus de détails sur le système de plugins sont encore en construction - les contributions sont les bienvenues !*
|
||||
|
||||
Aspects clés du système de plugins :
|
||||
|
||||
1. Définition des plugins : Les plugins sont définis comme des classes Python qui héritent d'une classe de base `Plugin`
|
||||
2. Enregistrement des plugins : Les plugins disponibles sont enregistrés dans un dictionnaire `ALL_PLUGINS`
|
||||
3. Spécification des plugins : Les plugins sont associés à `Agent.sandbox_plugins: list[PluginRequirement]`. Les utilisateurs peuvent spécifier quels plugins charger lors de l'initialisation du runtime
|
||||
4. Initialisation : Les plugins sont initialisés de manière asynchrone lorsque le client runtime démarre
|
||||
5. Utilisation : Le client runtime peut utiliser les plugins initialisés pour étendre ses capacités (par exemple, le JupyterPlugin pour exécuter des cellules IPython)
|
||||
@@ -0,0 +1,395 @@
|
||||
# Options de configuration
|
||||
|
||||
Ce guide détaille toutes les options de configuration disponibles pour OpenHands, vous aidant à personnaliser son comportement et à l'intégrer avec d'autres services.
|
||||
|
||||
:::note
|
||||
Si vous exécutez en [Mode GUI](https://docs.all-hands.dev/modules/usage/how-to/gui-mode), les paramètres disponibles dans l'interface utilisateur des paramètres auront toujours
|
||||
la priorité.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
# Table des matières
|
||||
|
||||
1. [Configuration de base](#configuration-de-base)
|
||||
- [Clés API](#clés-api)
|
||||
- [Espace de travail](#espace-de-travail)
|
||||
- [Débogage et journalisation](#débogage-et-journalisation)
|
||||
- [Gestion des sessions](#gestion-des-sessions)
|
||||
- [Trajectoires](#trajectoires)
|
||||
- [Stockage de fichiers](#stockage-de-fichiers)
|
||||
- [Gestion des tâches](#gestion-des-tâches)
|
||||
- [Configuration du bac à sable](#configuration-du-bac-à-sable)
|
||||
- [Divers](#divers)
|
||||
2. [Configuration LLM](#configuration-llm)
|
||||
- [Informations d'identification AWS](#informations-didentification-aws)
|
||||
- [Configuration de l'API](#configuration-de-lapi)
|
||||
- [Fournisseur LLM personnalisé](#fournisseur-llm-personnalisé)
|
||||
- [Embeddings](#embeddings)
|
||||
- [Gestion des messages](#gestion-des-messages)
|
||||
- [Sélection du modèle](#sélection-du-modèle)
|
||||
- [Nouvelles tentatives](#nouvelles-tentatives)
|
||||
- [Options avancées](#options-avancées)
|
||||
3. [Configuration de l'agent](#configuration-de-lagent)
|
||||
- [Configuration du micro-agent](#configuration-du-micro-agent)
|
||||
- [Configuration de la mémoire](#configuration-de-la-mémoire)
|
||||
- [Configuration LLM](#configuration-llm-2)
|
||||
- [Configuration de l'espace d'action](#configuration-de-lespace-daction)
|
||||
- [Utilisation du micro-agent](#utilisation-du-micro-agent)
|
||||
4. [Configuration du bac à sable](#configuration-du-bac-à-sable-2)
|
||||
- [Exécution](#exécution)
|
||||
- [Image de conteneur](#image-de-conteneur)
|
||||
- [Mise en réseau](#mise-en-réseau)
|
||||
- [Linting et plugins](#linting-et-plugins)
|
||||
- [Dépendances et environnement](#dépendances-et-environnement)
|
||||
- [Évaluation](#évaluation)
|
||||
5. [Configuration de sécurité](#configuration-de-sécurité)
|
||||
- [Mode de confirmation](#mode-de-confirmation)
|
||||
- [Analyseur de sécurité](#analyseur-de-sécurité)
|
||||
|
||||
---
|
||||
|
||||
## Configuration de base
|
||||
|
||||
Les options de configuration de base sont définies dans la section `[core]` du fichier `config.toml`.
|
||||
|
||||
**Clés API**
|
||||
- `e2b_api_key`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Clé API pour E2B
|
||||
|
||||
- `modal_api_token_id`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : ID du jeton API pour Modal
|
||||
|
||||
- `modal_api_token_secret`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Secret du jeton API pour Modal
|
||||
|
||||
**Espace de travail**
|
||||
- `workspace_base`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"./workspace"`
|
||||
- Description : Chemin de base pour l'espace de travail
|
||||
|
||||
- `cache_dir`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"/tmp/cache"`
|
||||
- Description : Chemin du répertoire de cache
|
||||
|
||||
**Débogage et journalisation**
|
||||
- `debug`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Activer le débogage
|
||||
|
||||
- `disable_color`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Désactiver la couleur dans la sortie du terminal
|
||||
|
||||
**Trajectoires**
|
||||
- `save_trajectory_path`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"./trajectories"`
|
||||
- Description : Chemin pour stocker les trajectoires (peut être un dossier ou un fichier). Si c'est un dossier, les trajectoires seront enregistrées dans un fichier nommé avec l'ID de session et l'extension .json, dans ce dossier.
|
||||
|
||||
**Stockage de fichiers**
|
||||
- `file_store_path`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"/tmp/file_store"`
|
||||
- Description : Chemin de stockage des fichiers
|
||||
|
||||
- `file_store`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"memory"`
|
||||
- Description : Type de stockage de fichiers
|
||||
|
||||
- `file_uploads_allowed_extensions`
|
||||
- Type : `list of str`
|
||||
- Valeur par défaut : `[".*"]`
|
||||
- Description : Liste des extensions de fichiers autorisées pour les téléchargements
|
||||
|
||||
- `file_uploads_max_file_size_mb`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `0`
|
||||
- Description : Taille maximale des fichiers pour les téléchargements, en mégaoctets
|
||||
|
||||
- `file_uploads_restrict_file_types`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Restreindre les types de fichiers pour les téléchargements de fichiers
|
||||
|
||||
- `file_uploads_allowed_extensions`
|
||||
- Type : `list of str`
|
||||
- Valeur par défaut : `[".*"]`
|
||||
- Description : Liste des extensions de fichiers autorisées pour les téléchargements
|
||||
|
||||
**Gestion des tâches**
|
||||
- `max_budget_per_task`
|
||||
- Type : `float`
|
||||
- Valeur par défaut : `0.0`
|
||||
- Description : Budget maximal par tâche (0.0 signifie aucune limite)
|
||||
|
||||
- `max_iterations`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `100`
|
||||
- Description : Nombre maximal d'itérations
|
||||
|
||||
**Configuration du bac à sable**
|
||||
- `workspace_mount_path_in_sandbox`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"/workspace"`
|
||||
- Description : Chemin de montage de l'espace de travail dans le bac à sable
|
||||
|
||||
- `workspace_mount_path`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Chemin de montage de l'espace de travail
|
||||
|
||||
- `workspace_mount_rewrite`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Chemin pour réécrire le chemin de montage de l'espace de travail. Vous pouvez généralement ignorer cela, cela fait référence à des cas spéciaux d'exécution à l'intérieur d'un autre conteneur.
|
||||
|
||||
**Divers**
|
||||
- `run_as_openhands`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `true`
|
||||
- Description : Exécuter en tant qu'OpenHands
|
||||
|
||||
- `runtime`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"eventstream"`
|
||||
- Description : Environnement d'exécution
|
||||
|
||||
- `default_agent`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"CodeActAgent"`
|
||||
- Description : Nom de l'agent par défaut
|
||||
|
||||
- `jwt_secret`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `uuid.uuid4().hex`
|
||||
- Description : Secret JWT pour l'authentification. Veuillez le définir sur votre propre valeur.
|
||||
|
||||
## Configuration LLM
|
||||
|
||||
Les options de configuration LLM (Large Language Model) sont définies dans la section `[llm]` du fichier `config.toml`.
|
||||
|
||||
Pour les utiliser avec la commande docker, passez `-e LLM_<option>`. Exemple : `-e LLM_NUM_RETRIES`.
|
||||
|
||||
:::note
|
||||
Pour les configurations de développement, vous pouvez également définir des configurations LLM personnalisées. Voir [Configurations LLM personnalisées](./llms/custom-llm-configs) pour plus de détails.
|
||||
:::
|
||||
|
||||
**Informations d'identification AWS**
|
||||
- `aws_access_key_id`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : ID de clé d'accès AWS
|
||||
|
||||
- `aws_region_name`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Nom de la région AWS
|
||||
|
||||
- `aws_secret_access_key`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Clé d'accès secrète AWS
|
||||
|
||||
**Configuration de l'API**
|
||||
- `api_key`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `None`
|
||||
- Description : Clé API à utiliser
|
||||
|
||||
- `base_url`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : URL de base de l'API
|
||||
|
||||
- `api_version`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Version de l'API
|
||||
|
||||
- `input_cost_per_token`
|
||||
- Type : `float`
|
||||
- Valeur par défaut : `0.0`
|
||||
- Description : Coût par jeton d'entrée
|
||||
|
||||
- `output_cost_per_token`
|
||||
- Type : `float`
|
||||
- Valeur par défaut : `0.0`
|
||||
- Description : Coût par jeton de sortie
|
||||
|
||||
**Fournisseur LLM personnalisé**
|
||||
- `custom_llm_provider`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Fournisseur LLM personnalisé
|
||||
|
||||
**Embeddings**
|
||||
- `embedding_base_url`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : URL de base de l'API d'embedding
|
||||
|
||||
- `embedding_deployment_name`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Nom du déploiement d'embedding
|
||||
|
||||
- `embedding_model`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"local"`
|
||||
- Description : Modèle d'embedding à utiliser
|
||||
|
||||
**Gestion des messages**
|
||||
- `max_message_chars`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `30000`
|
||||
- Description : Le nombre maximum approximatif de caractères dans le contenu d'un événement inclus dans l'invite au LLM. Les observations plus grandes sont tronquées.
|
||||
|
||||
- `max_input_tokens`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `0`
|
||||
- Description : Nombre maximal de jetons d'entrée
|
||||
|
||||
- `max_output_tokens`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `0`
|
||||
- Description : Nombre maximal de jetons de sortie
|
||||
|
||||
**Sélection du modèle**
|
||||
- `model`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `"claude-3-5-sonnet-20241022"`
|
||||
- Description : Modèle à utiliser
|
||||
|
||||
**Nouvelles tentatives**
|
||||
- `num_retries`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `8`
|
||||
- Description : Nombre de nouvelles tentatives à effectuer
|
||||
|
||||
- `retry_max_wait`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `120`
|
||||
- Description : Temps d'attente maximal (en secondes) entre les tentatives de nouvelle tentative
|
||||
|
||||
- `retry_min_wait`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `15`
|
||||
- Description : Temps d'attente minimal (en secondes) entre les tentatives de nouvelle tentative
|
||||
|
||||
- `retry_multiplier`
|
||||
- Type : `float`
|
||||
- Valeur par défaut : `2.0`
|
||||
- Description : Multiplicateur pour le calcul du backoff exponentiel
|
||||
|
||||
**Options avancées**
|
||||
- `drop_params`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Supprimer tous les paramètres non mappés (non pris en charge) sans provoquer d'exception
|
||||
|
||||
- `caching_prompt`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `true`
|
||||
- Description : Utiliser la fonctionnalité de mise en cache des invites si elle est fournie par le LLM et prise en charge
|
||||
|
||||
- `ollama_base_url`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : URL de base pour l'API OLLAMA
|
||||
|
||||
- `temperature`
|
||||
- Type : `float`
|
||||
- Valeur par défaut : `0.0`
|
||||
- Description : Température pour l'API
|
||||
|
||||
- `timeout`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `0`
|
||||
- Description : Délai d'expiration pour l'API
|
||||
|
||||
- `top_p`
|
||||
- Type : `float`
|
||||
- Valeur par défaut : `1.0`
|
||||
- Description : Top p pour l'API
|
||||
|
||||
- `disable_vision`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `None`
|
||||
- Description : Si le modèle est capable de vision, cette option permet de désactiver le traitement des images (utile pour réduire les coûts)
|
||||
|
||||
## Configuration de l'agent
|
||||
|
||||
Les options de configuration de l'agent sont définies dans les sections `[agent]` et `[agent.<agent_name>]` du fichier `config.toml`.
|
||||
|
||||
**Configuration du micro-agent**
|
||||
- `micro_agent_name`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `""`
|
||||
- Description : Nom du micro-agent à utiliser pour cet agent
|
||||
|
||||
**Configuration de la mémoire**
|
||||
- `memory_enabled`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Si la mémoire à long terme (embeddings) est activée
|
||||
|
||||
- `memory_max_threads`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `3`
|
||||
- Description : Le nombre maximum de threads indexant en même temps pour les embeddings
|
||||
|
||||
**Configuration LLM**
|
||||
- `llm_config`
|
||||
- Type : `str`
|
||||
- Valeur par défaut : `'your-llm-config-group'`
|
||||
- Description : Le nom de la configuration LLM à utiliser
|
||||
|
||||
**Configuration de l'espace d'action**
|
||||
- `function_calling`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `true`
|
||||
- Description : Si l'appel de fonction est activé
|
||||
|
||||
- `codeact_enable_browsing`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Si le délégué de navigation est activé dans l'espace d'action (fonctionne uniquement avec l'appel de fonction)
|
||||
|
||||
- `codeact_enable_llm_editor`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `false`
|
||||
- Description : Si l'éditeur LLM est activé dans l'espace d'action (fonctionne uniquement avec l'appel de fonction)
|
||||
|
||||
**Utilisation du micro-agent**
|
||||
- `enable_prompt_extensions`
|
||||
- Type : `bool`
|
||||
- Valeur par défaut : `true`
|
||||
- Description : Indique si l'utilisation des micro-agents est activée ou non
|
||||
|
||||
- `disabled_microagents`
|
||||
- Type : `list of str`
|
||||
- Valeur par défaut : `None`
|
||||
- Description : Liste des micro-agents à désactiver
|
||||
|
||||
### Exécution
|
||||
- `timeout`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `120`
|
||||
- Description : Délai d'expiration du bac à sable, en secondes
|
||||
|
||||
- `user_id`
|
||||
- Type : `int`
|
||||
- Valeur par défaut : `1000`
|
||||
- Description : ID de l'utilisateur du bac à sable
|
||||
@@ -59,10 +59,6 @@ Félicitations !
|
||||
|
||||
## Explication technique
|
||||
|
||||
Le code pertinent est défini dans [ssh_box.py](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/docker/ssh_box.py) et [image_agnostic_util.py](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/docker/image_agnostic_util.py).
|
||||
|
||||
En particulier, ssh_box.py vérifie l'objet config pour ```config.sandbox.base_container_image``` et ensuite tente de récupérer l'image à l'aide de [get_od_sandbox_image](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/docker/image_agnostic_util.py#L72), qui est défini dans image_agnostic_util.py.
|
||||
|
||||
Lorsqu'une image personnalisée est utilisée pour la première fois, elle ne sera pas trouvée et donc elle sera construite (à l'exécution ultérieure, l'image construite sera trouvée et renvoyée).
|
||||
|
||||
L'image personnalisée est construite avec [_build_sandbox_image()](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/docker/image_agnostic_util.py#L29), qui crée un fichier docker en utilisant votre image personnalisée comme base et configure ensuite l'environnement pour OpenHands, comme ceci:
|
||||
@@ -102,4 +98,4 @@ Si vous voyez un message d'erreur indiquant que le port est utilisé ou indispon
|
||||
|
||||
## Discuter
|
||||
|
||||
Pour d'autres problèmes ou questions rejoignez le [Slack](https://join.slack.com/t/opendevin/shared_invite/zt-2oikve2hu-UDxHeo8nsE69y6T7yFX_BA) ou le [Discord](https://discord.gg/ESHStjSjD4) et demandez!
|
||||
Pour d'autres problèmes ou questions rejoignez le [Slack](https://join.slack.com/t/openhands-ai/shared_invite/zt-2wkh4pklz-w~h_DVDtEe9H5kyQlcNxVw) ou le [Discord](https://discord.gg/ESHStjSjD4) et demandez!
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# ✅ Fournir des Commentaires
|
||||
|
||||
Lorsque vous utilisez OpenHands, vous rencontrerez sans aucun doute des cas où les choses fonctionnent bien et d'autres où elles ne fonctionnent pas. Nous vous encourageons à fournir des commentaires lorsque vous utilisez OpenHands pour aider l'équipe de développement et, peut-être plus important encore, créer un corpus ouvert d'exemples de formation pour les agents de codage -- Partagez-OpenHands !
|
||||
# ✅ Fournir des commentaires
|
||||
|
||||
## 📝 Comment Fournir des Commentaires
|
||||
Lorsque vous utilisez OpenHands, vous rencontrerez des cas où les choses fonctionnent bien, et d'autres où elles ne fonctionnent pas. Nous vous encourageons à fournir des commentaires lorsque vous utilisez OpenHands pour aider à donner des retours à l'équipe de développement, et peut-être plus important encore, créer un corpus ouvert d'exemples d'entraînement d'agents de codage -- Share-OpenHands !
|
||||
|
||||
Fournir des commentaires est simple ! Lorsque vous utilisez OpenHands, vous pouvez appuyer sur le bouton de pouce vers le haut ou vers le bas à n'importe quel moment de votre interaction. Vous serez invité à fournir votre adresse email (par exemple, afin que nous puissions vous contacter si nous voulons poser des questions de suivi), et vous pouvez choisir si vous souhaitez fournir des commentaires publiquement ou en privé.
|
||||
## 📝 Comment fournir des commentaires
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/5rFx-StMVV0?si=svo7xzp6LhGK_GXr" title="Lecteur vidéo YouTube" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
Fournir des commentaires est facile ! Lorsque vous utilisez OpenHands, vous pouvez appuyer sur le bouton pouce vers le haut ou pouce vers le bas à tout moment pendant votre interaction. Vous serez invité à fournir votre adresse e-mail (par exemple, afin que nous puissions vous contacter si nous voulons poser des questions de suivi), et vous pouvez choisir si vous souhaitez fournir des commentaires publiquement ou en privé.
|
||||
|
||||
## 📜 Licence de Données et Confidentialité
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/5rFx-StMVV0?si=svo7xzp6LhGK_GXr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||
|
||||
* Les données **publiques** seront distribuées sous la licence MIT, comme OpenHands lui-même, et pourront être utilisées par la communauté pour former et tester des modèles. Évidemment, les commentaires que vous pouvez rendre publics seront plus précieux pour la communauté dans son ensemble, donc lorsque vous ne traitez pas d'informations sensibles, nous vous encourageons à choisir cette option !
|
||||
## 📜 Utilisation des données et confidentialité
|
||||
|
||||
### Paramètres de partage des données
|
||||
|
||||
Lorsque vous soumettez des données, vous pouvez les soumettre publiquement ou en privé.
|
||||
|
||||
* Les données **publiques** seront distribuées sous la licence MIT, comme OpenHands lui-même, et pourront être utilisées par la communauté pour entraîner et tester des modèles. Évidemment, les commentaires que vous pouvez rendre publics seront plus précieux pour la communauté dans son ensemble, donc lorsque vous ne traitez pas d'informations sensibles, nous vous encourageons à choisir cette option !
|
||||
* Les données **privées** ne seront partagées qu'avec l'équipe OpenHands dans le but d'améliorer OpenHands.
|
||||
|
||||
### Qui collecte et stocke les données ?
|
||||
|
||||
Les données sont collectées et stockées par [All Hands AI](https://all-hands.dev), une entreprise fondée par les mainteneurs d'OpenHands pour soutenir et améliorer OpenHands.
|
||||
|
||||
### Comment les données publiques seront-elles publiées ?
|
||||
|
||||
Les données publiques seront publiées lorsque nous atteindrons des jalons fixes, tels que 1 000 exemples publics, 10 000 exemples publics, etc.
|
||||
À ce moment-là, nous suivrons le processus de publication suivant :
|
||||
|
||||
1. Toutes les personnes qui ont contribué à des commentaires publics recevront un e-mail décrivant la publication des données et auront la possibilité de se retirer.
|
||||
2. La ou les personnes en charge de la publication des données effectueront un contrôle de la qualité des données, en supprimant les commentaires de mauvaise qualité, en supprimant les adresses e-mail des soumissionnaires et en essayant de supprimer toute information sensible.
|
||||
3. Les données seront publiées publiquement sous la licence MIT via des sites couramment utilisés tels que GitHub ou Hugging Face.
|
||||
|
||||
### Que faire si je veux que mes données soient supprimées ?
|
||||
|
||||
Pour les données sur les serveurs d'All Hands AI, nous sommes heureux de les supprimer sur demande :
|
||||
|
||||
**Une pièce de données :** Si vous souhaitez supprimer une pièce de données, nous ajouterons prochainement un mécanisme pour supprimer les pièces de données en utilisant le lien et le mot de passe qui s'affichent sur l'interface lorsque vous soumettez des données.
|
||||
|
||||
**Toutes les données :** Si vous souhaitez que toutes vos données soient supprimées, ou si vous n'avez pas l'ID et le mot de passe que vous avez reçus lors de la soumission des données, veuillez contacter `contact@all-hands.dev` à partir de l'adresse e-mail que vous avez enregistrée lorsque vous avez initialement soumis les données.
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
|
||||
# Démarrer avec OpenHands
|
||||
|
||||
Vous avez donc [installé OpenHands](./installation) et avez
|
||||
[configuré votre LLM](./installation#setup). Et maintenant ?
|
||||
|
||||
OpenHands peut vous aider à aborder une grande variété de tâches d'ingénierie. Mais la technologie
|
||||
est encore nouvelle, et nous sommes loin d'avoir des agents capables de prendre en charge des tâches
|
||||
d'ingénierie vastes et compliquées sans aucune aide. Il est donc important de se faire une idée de ce que l'agent
|
||||
fait bien, et où il pourrait avoir besoin d'un coup de main.
|
||||
|
||||
## Hello World
|
||||
|
||||
La première chose que vous voudrez peut-être essayer est un simple exemple "hello world".
|
||||
Cela peut être plus compliqué qu'il n'y paraît !
|
||||
|
||||
Essayez de demander à l'agent :
|
||||
> Veuillez écrire un script bash hello.sh qui affiche "hello world!"
|
||||
|
||||
Vous devriez constater que l'agent non seulement écrit le script, mais définit également les
|
||||
permissions correctes et exécute le script pour vérifier la sortie.
|
||||
|
||||
Vous pouvez continuer à demander à l'agent d'affiner votre code. C'est une excellente façon de
|
||||
travailler avec les agents. Commencez simplement, et itérez.
|
||||
|
||||
> Veuillez modifier hello.sh pour qu'il accepte un nom comme premier argument, mais par défaut "world"
|
||||
|
||||
Vous pouvez également travailler dans n'importe quel langage dont vous avez besoin, bien que l'agent puisse avoir besoin de passer du
|
||||
temps à configurer son environnement !
|
||||
|
||||
> Veuillez convertir hello.sh en un script Ruby, et l'exécuter
|
||||
|
||||
## Construire à partir de zéro
|
||||
|
||||
Les agents se débrouillent exceptionnellement bien pour les tâches "greenfield" (tâches où ils n'ont pas besoin
|
||||
de contexte sur une base de code existante) et ils peuvent simplement commencer à partir de zéro.
|
||||
|
||||
Il est préférable de commencer par une tâche simple, puis d'itérer. Il est également préférable d'être
|
||||
aussi précis que possible sur ce que vous voulez, sur la pile technologique à utiliser, etc.
|
||||
|
||||
Par exemple, nous pourrions construire une application TODO :
|
||||
|
||||
> Veuillez créer une application basique de liste de tâches en React. Elle devrait être uniquement frontend, et tout l'état
|
||||
> devrait être conservé dans localStorage.
|
||||
|
||||
Nous pouvons continuer à itérer sur l'application une fois le squelette en place :
|
||||
|
||||
> Veuillez permettre d'ajouter une date d'échéance optionnelle à chaque tâche
|
||||
|
||||
Tout comme avec le développement normal, il est bon de commiter et de pousser votre code fréquemment.
|
||||
De cette façon, vous pouvez toujours revenir à un ancien état si l'agent dévie.
|
||||
Vous pouvez demander à l'agent de commiter et de pousser pour vous :
|
||||
|
||||
> Veuillez commiter les changements et les pousser sur une nouvelle branche appelée "feature/due-dates"
|
||||
|
||||
|
||||
## Ajouter du nouveau code
|
||||
|
||||
OpenHands peut également faire un excellent travail en ajoutant du nouveau code à une base de code existante.
|
||||
|
||||
Par exemple, vous pouvez demander à OpenHands d'ajouter une nouvelle action GitHub à votre projet
|
||||
qui analyse votre code. OpenHands peut jeter un coup d'œil à votre base de code pour voir quel langage
|
||||
il doit utiliser, mais ensuite il peut simplement déposer un nouveau fichier dans `./github/workflows/lint.yml`
|
||||
|
||||
> Veuillez ajouter une action GitHub qui analyse le code dans ce dépôt
|
||||
|
||||
Certaines tâches peuvent nécessiter un peu plus de contexte. Bien qu'OpenHands puisse utiliser `ls` et `grep`
|
||||
pour rechercher dans votre base de code, fournir le contexte à l'avance lui permet d'aller plus vite,
|
||||
et plus précisément. Et cela vous coûtera moins de tokens !
|
||||
|
||||
> Veuillez modifier ./backend/api/routes.js pour ajouter une nouvelle route qui renvoie une liste de toutes les tâches
|
||||
|
||||
> Veuillez ajouter un nouveau composant React qui affiche une liste de Widgets dans le répertoire ./frontend/components.
|
||||
> Il devrait utiliser le composant Widget existant.
|
||||
|
||||
## Refactoring
|
||||
|
||||
OpenHands est excellent pour refactoriser du code existant, surtout par petits morceaux.
|
||||
Vous ne voulez probablement pas essayer de réarchitecturer toute votre base de code, mais diviser
|
||||
les longs fichiers et fonctions, renommer les variables, etc. ont tendance à très bien fonctionner.
|
||||
|
||||
> Veuillez renommer toutes les variables à une lettre dans ./app.go
|
||||
|
||||
> Veuillez diviser la fonction `build_and_deploy_widgets` en deux fonctions, `build_widgets` et `deploy_widgets` dans widget.php
|
||||
|
||||
> Veuillez diviser ./api/routes.js en fichiers séparés pour chaque route
|
||||
|
||||
## Corrections de bugs
|
||||
|
||||
OpenHands peut également vous aider à traquer et corriger des bugs dans votre code. Mais, comme tout
|
||||
développeur le sait, la correction de bugs peut être extrêmement délicate, et souvent OpenHands aura besoin de plus de contexte.
|
||||
Cela aide si vous avez diagnostiqué le bug, mais que vous voulez qu'OpenHands comprenne la logique.
|
||||
|
||||
> Actuellement, le champ email dans le point de terminaison `/subscribe` rejette les domaines .io. Veuillez corriger cela.
|
||||
|
||||
> La fonction `search_widgets` dans ./app.py effectue une recherche sensible à la casse. Veuillez la rendre insensible à la casse.
|
||||
|
||||
Il est souvent utile de faire du développement piloté par les tests lors de la correction de bugs avec un agent.
|
||||
Vous pouvez demander à l'agent d'écrire un nouveau test, puis d'itérer jusqu'à ce qu'il corrige le bug :
|
||||
|
||||
> La fonction `hello` plante sur la chaîne vide. Veuillez écrire un test qui reproduit ce bug, puis corrigez le code pour qu'il passe.
|
||||
|
||||
## Plus
|
||||
|
||||
OpenHands est capable d'aider sur à peu près n'importe quelle tâche de codage. Mais il faut de la pratique
|
||||
pour en tirer le meilleur parti. N'oubliez pas de :
|
||||
* Garder vos tâches petites
|
||||
* Être aussi précis que possible
|
||||
* Fournir autant de contexte que possible
|
||||
* Commiter et pousser fréquemment
|
||||
|
||||
Voir [Bonnes pratiques de prompting](./prompting/prompting-best-practices) pour plus de conseils sur la façon de tirer le meilleur parti d'OpenHands.
|
||||
@@ -0,0 +1,112 @@
|
||||
|
||||
|
||||
# Mode CLI
|
||||
|
||||
OpenHands peut être exécuté en mode CLI interactif, ce qui permet aux utilisateurs de démarrer une session interactive via la ligne de commande.
|
||||
|
||||
Ce mode est différent du [mode headless](headless-mode), qui est non interactif et mieux adapté aux scripts.
|
||||
|
||||
## Avec Python
|
||||
|
||||
Pour démarrer une session OpenHands interactive via la ligne de commande, suivez ces étapes :
|
||||
|
||||
1. Assurez-vous d'avoir suivi les [instructions de configuration de développement](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
|
||||
|
||||
2. Exécutez la commande suivante :
|
||||
|
||||
```bash
|
||||
poetry run python -m openhands.core.cli
|
||||
```
|
||||
|
||||
Cette commande démarrera une session interactive où vous pourrez saisir des tâches et recevoir des réponses d'OpenHands.
|
||||
|
||||
Vous devrez vous assurer de définir votre modèle, votre clé API et d'autres paramètres via des variables d'environnement
|
||||
[ou le fichier `config.toml`](https://github.com/All-Hands-AI/OpenHands/blob/main/config.template.toml).
|
||||
|
||||
|
||||
## Avec Docker
|
||||
|
||||
Pour exécuter OpenHands en mode CLI avec Docker, suivez ces étapes :
|
||||
|
||||
1. Définissez `WORKSPACE_BASE` sur le répertoire que vous voulez qu'OpenHands modifie :
|
||||
|
||||
```bash
|
||||
WORKSPACE_BASE=$(pwd)/workspace
|
||||
```
|
||||
|
||||
2. Définissez `LLM_MODEL` sur le modèle que vous voulez utiliser :
|
||||
|
||||
```bash
|
||||
LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"
|
||||
|
||||
```
|
||||
|
||||
3. Définissez `LLM_API_KEY` sur votre clé API :
|
||||
|
||||
```bash
|
||||
LLM_API_KEY="sk_test_12345"
|
||||
```
|
||||
|
||||
4. Exécutez la commande Docker suivante :
|
||||
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
-e LLM_MODEL=$LLM_MODEL \
|
||||
-v $WORKSPACE_BASE:/opt/workspace_base \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.cli
|
||||
```
|
||||
|
||||
Cette commande démarrera une session interactive dans Docker où vous pourrez saisir des tâches et recevoir des réponses d'OpenHands.
|
||||
|
||||
## Exemples de commandes CLI et de sorties attendues
|
||||
|
||||
Voici quelques exemples de commandes CLI et leurs sorties attendues :
|
||||
|
||||
### Exemple 1 : Tâche simple
|
||||
|
||||
```bash
|
||||
How can I help? >> Écrivez un script Python qui affiche "Hello, World!"
|
||||
```
|
||||
|
||||
Sortie attendue :
|
||||
|
||||
```bash
|
||||
🤖 Bien sûr ! Voici un script Python qui affiche "Hello, World!" :
|
||||
|
||||
❯ print("Hello, World!")
|
||||
```
|
||||
|
||||
### Exemple 2 : Commande Bash
|
||||
|
||||
```bash
|
||||
How can I help? >> Créez un répertoire nommé "test_dir"
|
||||
```
|
||||
|
||||
Sortie attendue :
|
||||
|
||||
```bash
|
||||
🤖 Création d'un répertoire nommé "test_dir" :
|
||||
|
||||
❯ mkdir test_dir
|
||||
```
|
||||
|
||||
### Exemple 3 : Gestion des erreurs
|
||||
|
||||
```bash
|
||||
How can I help? >> Supprimez un fichier inexistant
|
||||
```
|
||||
|
||||
Sortie attendue :
|
||||
|
||||
```bash
|
||||
🤖 Une erreur s'est produite. Veuillez réessayer.
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
|
||||
# Sandbox Personnalisé
|
||||
|
||||
Le sandbox est l'endroit où l'agent effectue ses tâches. Au lieu d'exécuter des commandes directement sur votre ordinateur (ce qui pourrait être risqué), l'agent les exécute à l'intérieur d'un conteneur Docker.
|
||||
|
||||
Le sandbox OpenHands par défaut (`python-nodejs:python3.12-nodejs22` de [nikolaik/python-nodejs](https://hub.docker.com/r/nikolaik/python-nodejs)) est livré avec certains paquets installés tels que Python et Node.js mais peut nécessiter l'installation d'autres logiciels par défaut.
|
||||
|
||||
Vous avez deux options pour la personnalisation :
|
||||
|
||||
1. Utiliser une image existante avec les logiciels requis.
|
||||
2. Créer votre propre image Docker personnalisée.
|
||||
|
||||
Si vous choisissez la première option, vous pouvez passer la section `Créer Votre Image Docker`.
|
||||
|
||||
## Créer Votre Image Docker
|
||||
|
||||
Pour créer une image Docker personnalisée, elle doit être basée sur Debian.
|
||||
|
||||
Par exemple, si vous voulez qu'OpenHands ait `ruby` installé, créez un `Dockerfile` avec le contenu suivant :
|
||||
|
||||
```dockerfile
|
||||
FROM debian:latest
|
||||
|
||||
# Installer les paquets requis
|
||||
RUN apt-get update && apt-get install -y ruby
|
||||
```
|
||||
|
||||
Enregistrez ce fichier dans un dossier. Ensuite, construisez votre image Docker (par exemple, nommée custom-image) en naviguant vers le dossier dans le terminal et en exécutant :
|
||||
|
||||
```bash
|
||||
docker build -t custom-image .
|
||||
```
|
||||
|
||||
Cela produira une nouvelle image appelée `custom-image`, qui sera disponible dans Docker.
|
||||
|
||||
> Notez que dans la configuration décrite dans ce document, OpenHands s'exécutera en tant qu'utilisateur "openhands" à l'intérieur du sandbox et donc tous les paquets installés via le docker file devraient être disponibles pour tous les utilisateurs du système, pas seulement root.
|
||||
|
||||
## Utilisation du Workflow de Développement
|
||||
|
||||
### Configuration
|
||||
|
||||
Tout d'abord, assurez-vous de pouvoir exécuter OpenHands en suivant les instructions dans [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
|
||||
|
||||
### Spécifier l'Image de Base du Sandbox
|
||||
|
||||
Dans le fichier `config.toml` dans le répertoire OpenHands, définissez `sandbox_base_container_image` sur l'image que vous souhaitez utiliser. Cela peut être une image que vous avez déjà extraite ou une que vous avez construite :
|
||||
|
||||
```bash
|
||||
[core]
|
||||
...
|
||||
sandbox_base_container_image="custom-image"
|
||||
```
|
||||
|
||||
### Exécution
|
||||
|
||||
Exécutez OpenHands en exécutant ```make run``` dans le répertoire de niveau supérieur.
|
||||
|
||||
## Explication Technique
|
||||
|
||||
Veuillez vous référer à la [section image docker personnalisée de la documentation d'exécution](https://docs.all-hands.dev/modules/usage/architecture/runtime#advanced-how-openhands-builds-and-maintains-od-runtime-images) pour plus de détails.
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
# Débogage
|
||||
|
||||
Ce qui suit est destiné à servir d'introduction au débogage d'OpenHands à des fins de développement.
|
||||
|
||||
## Serveur / VSCode
|
||||
|
||||
Le `launch.json` suivant permettra de déboguer les éléments agent, contrôleur et serveur, mais pas le bac à sable (qui s'exécute dans docker). Il ignorera toutes les modifications à l'intérieur du répertoire `workspace/` :
|
||||
|
||||
```
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "OpenHands CLI",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "openhands.core.cli",
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": "OpenHands WebApp",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "uvicorn",
|
||||
"args": [
|
||||
"openhands.server.listen:app",
|
||||
"--reload",
|
||||
"--reload-exclude",
|
||||
"${workspaceFolder}/workspace",
|
||||
"--port",
|
||||
"3000"
|
||||
],
|
||||
"justMyCode": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Des configurations de débogage plus spécifiques qui incluent plus de paramètres peuvent être spécifiées :
|
||||
|
||||
```
|
||||
...
|
||||
{
|
||||
"name": "Debug CodeAct",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "openhands.core.main",
|
||||
"args": [
|
||||
"-t",
|
||||
"Demandez-moi quelle est votre tâche.",
|
||||
"-d",
|
||||
"${workspaceFolder}/workspace",
|
||||
"-c",
|
||||
"CodeActAgent",
|
||||
"-l",
|
||||
"llm.o1",
|
||||
"-n",
|
||||
"prompts"
|
||||
],
|
||||
"justMyCode": false
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
Les valeurs dans l'extrait ci-dessus peuvent être mises à jour de telle sorte que :
|
||||
|
||||
* *t* : la tâche
|
||||
* *d* : le répertoire de l'espace de travail openhands
|
||||
* *c* : l'agent
|
||||
* *l* : la configuration LLM (prédéfinie dans config.toml)
|
||||
* *n* : le nom de la session (par exemple, le nom du flux d'événements)
|
||||
@@ -0,0 +1,280 @@
|
||||
|
||||
|
||||
# Évaluation
|
||||
|
||||
Ce guide fournit un aperçu de la façon d'intégrer votre propre benchmark d'évaluation dans le framework OpenHands.
|
||||
|
||||
## Configuration de l'environnement et de la configuration LLM
|
||||
|
||||
Veuillez suivre les instructions [ici](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md) pour configurer votre environnement de développement local.
|
||||
OpenHands en mode développement utilise `config.toml` pour garder une trace de la plupart des configurations.
|
||||
|
||||
Voici un exemple de fichier de configuration que vous pouvez utiliser pour définir et utiliser plusieurs LLMs :
|
||||
|
||||
```toml
|
||||
[llm]
|
||||
# IMPORTANT : ajoutez votre clé API ici et définissez le modèle que vous souhaitez évaluer
|
||||
model = "claude-3-5-sonnet-20241022"
|
||||
api_key = "sk-XXX"
|
||||
|
||||
[llm.eval_gpt4_1106_preview_llm]
|
||||
model = "gpt-4-1106-preview"
|
||||
api_key = "XXX"
|
||||
temperature = 0.0
|
||||
|
||||
[llm.eval_some_openai_compatible_model_llm]
|
||||
model = "openai/MODEL_NAME"
|
||||
base_url = "https://OPENAI_COMPATIBLE_URL/v1"
|
||||
api_key = "XXX"
|
||||
temperature = 0.0
|
||||
```
|
||||
|
||||
|
||||
## Comment utiliser OpenHands en ligne de commande
|
||||
|
||||
OpenHands peut être exécuté depuis la ligne de commande en utilisant le format suivant :
|
||||
|
||||
```bash
|
||||
poetry run python ./openhands/core/main.py \
|
||||
-i <max_iterations> \
|
||||
-t "<task_description>" \
|
||||
-c <agent_class> \
|
||||
-l <llm_config>
|
||||
```
|
||||
|
||||
Par exemple :
|
||||
|
||||
```bash
|
||||
poetry run python ./openhands/core/main.py \
|
||||
-i 10 \
|
||||
-t "Écrivez-moi un script bash qui affiche hello world." \
|
||||
-c CodeActAgent \
|
||||
-l llm
|
||||
```
|
||||
|
||||
Cette commande exécute OpenHands avec :
|
||||
- Un maximum de 10 itérations
|
||||
- La description de tâche spécifiée
|
||||
- En utilisant CodeActAgent
|
||||
- Avec la configuration LLM définie dans la section `llm` de votre fichier `config.toml`
|
||||
|
||||
## Comment fonctionne OpenHands
|
||||
|
||||
Le point d'entrée principal d'OpenHands se trouve dans `openhands/core/main.py`. Voici un flux simplifié de son fonctionnement :
|
||||
|
||||
1. Analyse des arguments de ligne de commande et chargement de la configuration
|
||||
2. Création d'un environnement d'exécution à l'aide de `create_runtime()`
|
||||
3. Initialisation de l'agent spécifié
|
||||
4. Exécution du contrôleur à l'aide de `run_controller()`, qui :
|
||||
- Attache l'environnement d'exécution à l'agent
|
||||
- Exécute la tâche de l'agent
|
||||
- Renvoie un état final une fois terminé
|
||||
|
||||
La fonction `run_controller()` est le cœur de l'exécution d'OpenHands. Elle gère l'interaction entre l'agent, l'environnement d'exécution et la tâche, en gérant des choses comme la simulation d'entrée utilisateur et le traitement des événements.
|
||||
|
||||
|
||||
## Le moyen le plus simple de commencer : Explorer les benchmarks existants
|
||||
|
||||
Nous vous encourageons à examiner les différents benchmarks d'évaluation disponibles dans le [répertoire `evaluation/benchmarks/`](https://github.com/All-Hands-AI/OpenHands/blob/main/evaluation/benchmarks) de notre dépôt.
|
||||
|
||||
Pour intégrer votre propre benchmark, nous vous suggérons de commencer par celui qui ressemble le plus à vos besoins. Cette approche peut considérablement rationaliser votre processus d'intégration, vous permettant de vous appuyer sur les structures existantes et de les adapter à vos exigences spécifiques.
|
||||
|
||||
## Comment créer un workflow d'évaluation
|
||||
|
||||
|
||||
Pour créer un workflow d'évaluation pour votre benchmark, suivez ces étapes :
|
||||
|
||||
1. Importez les utilitaires OpenHands pertinents :
|
||||
```python
|
||||
import openhands.agenthub
|
||||
from evaluation.utils.shared import (
|
||||
EvalMetadata,
|
||||
EvalOutput,
|
||||
make_metadata,
|
||||
prepare_dataset,
|
||||
reset_logger_for_multiprocessing,
|
||||
run_evaluation,
|
||||
)
|
||||
from openhands.controller.state.state import State
|
||||
from openhands.core.config import (
|
||||
AppConfig,
|
||||
SandboxConfig,
|
||||
get_llm_config_arg,
|
||||
parse_arguments,
|
||||
)
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.core.main import create_runtime, run_controller
|
||||
from openhands.events.action import CmdRunAction
|
||||
from openhands.events.observation import CmdOutputObservation, ErrorObservation
|
||||
from openhands.runtime.runtime import Runtime
|
||||
```
|
||||
|
||||
2. Créez une configuration :
|
||||
```python
|
||||
def get_config(instance: pd.Series, metadata: EvalMetadata) -> AppConfig:
|
||||
config = AppConfig(
|
||||
default_agent=metadata.agent_class,
|
||||
runtime='eventstream',
|
||||
max_iterations=metadata.max_iterations,
|
||||
sandbox=SandboxConfig(
|
||||
base_container_image='your_container_image',
|
||||
enable_auto_lint=True,
|
||||
timeout=300,
|
||||
),
|
||||
)
|
||||
config.set_llm_config(metadata.llm_config)
|
||||
return config
|
||||
```
|
||||
|
||||
3. Initialisez l'environnement d'exécution et configurez l'environnement d'évaluation :
|
||||
```python
|
||||
def initialize_runtime(runtime: Runtime, instance: pd.Series):
|
||||
# Configurez votre environnement d'évaluation ici
|
||||
# Par exemple, définir des variables d'environnement, préparer des fichiers, etc.
|
||||
pass
|
||||
```
|
||||
|
||||
4. Créez une fonction pour traiter chaque instance :
|
||||
```python
|
||||
from openhands.utils.async_utils import call_async_from_sync
|
||||
def process_instance(instance: pd.Series, metadata: EvalMetadata) -> EvalOutput:
|
||||
config = get_config(instance, metadata)
|
||||
runtime = create_runtime(config)
|
||||
call_async_from_sync(runtime.connect)
|
||||
initialize_runtime(runtime, instance)
|
||||
|
||||
instruction = get_instruction(instance, metadata)
|
||||
|
||||
state = run_controller(
|
||||
config=config,
|
||||
task_str=instruction,
|
||||
runtime=runtime,
|
||||
fake_user_response_fn=your_user_response_function,
|
||||
)
|
||||
|
||||
# Évaluez les actions de l'agent
|
||||
evaluation_result = await evaluate_agent_actions(runtime, instance)
|
||||
|
||||
return EvalOutput(
|
||||
instance_id=instance.instance_id,
|
||||
instruction=instruction,
|
||||
test_result=evaluation_result,
|
||||
metadata=metadata,
|
||||
history=compatibility_for_eval_history_pairs(state.history),
|
||||
metrics=state.metrics.get() if state.metrics else None,
|
||||
error=state.last_error if state and state.last_error else None,
|
||||
)
|
||||
```
|
||||
|
||||
5. Exécutez l'évaluation :
|
||||
```python
|
||||
metadata = make_metadata(llm_config, dataset_name, agent_class, max_iterations, eval_note, eval_output_dir)
|
||||
output_file = os.path.join(metadata.eval_output_dir, 'output.jsonl')
|
||||
instances = prepare_dataset(your_dataset, output_file, eval_n_limit)
|
||||
|
||||
await run_evaluation(
|
||||
instances,
|
||||
metadata,
|
||||
output_file,
|
||||
num_workers,
|
||||
process_instance
|
||||
)
|
||||
```
|
||||
|
||||
Ce workflow configure la configuration, initialise l'environnement d'exécution, traite chaque instance en exécutant l'agent et en évaluant ses actions, puis collecte les résultats dans un objet `EvalOutput`. La fonction `run_evaluation` gère la parallélisation et le suivi de la progression.
|
||||
|
||||
N'oubliez pas de personnaliser les fonctions `get_instruction`, `your_user_response_function` et `evaluate_agent_actions` en fonction des exigences spécifiques de votre benchmark.
|
||||
|
||||
En suivant cette structure, vous pouvez créer un workflow d'évaluation robuste pour votre benchmark dans le framework OpenHands.
|
||||
|
||||
|
||||
## Comprendre la `user_response_fn`
|
||||
|
||||
La `user_response_fn` est un composant crucial dans le workflow d'évaluation d'OpenHands. Elle simule l'interaction de l'utilisateur avec l'agent, permettant des réponses automatisées pendant le processus d'évaluation. Cette fonction est particulièrement utile lorsque vous voulez fournir des réponses cohérentes et prédéfinies aux requêtes ou actions de l'agent.
|
||||
|
||||
|
||||
### Workflow et interaction
|
||||
|
||||
Le workflow correct pour gérer les actions et la `user_response_fn` est le suivant :
|
||||
|
||||
1. L'agent reçoit une tâche et commence à la traiter
|
||||
2. L'agent émet une Action
|
||||
3. Si l'Action est exécutable (par exemple, CmdRunAction, IPythonRunCellAction) :
|
||||
- Le Runtime traite l'Action
|
||||
- Le Runtime renvoie une Observation
|
||||
4. Si l'Action n'est pas exécutable (généralement une MessageAction) :
|
||||
- La `user_response_fn` est appelée
|
||||
- Elle renvoie une réponse utilisateur simulée
|
||||
5. L'agent reçoit soit l'Observation, soit la réponse simulée
|
||||
6. Les étapes 2 à 5 se répètent jusqu'à ce que la tâche soit terminée ou que le nombre maximum d'itérations soit atteint
|
||||
|
||||
Voici une représentation visuelle plus précise :
|
||||
|
||||
```
|
||||
[Agent]
|
||||
|
|
||||
v
|
||||
[Émettre une Action]
|
||||
|
|
||||
v
|
||||
[L'Action est-elle exécutable ?]
|
||||
/ \
|
||||
Oui Non
|
||||
| |
|
||||
v v
|
||||
[Runtime] [user_response_fn]
|
||||
| |
|
||||
v v
|
||||
[Renvoyer une Observation] [Réponse simulée]
|
||||
\ /
|
||||
\ /
|
||||
v v
|
||||
[L'agent reçoit le feedback]
|
||||
|
|
||||
v
|
||||
[Continuer ou terminer la tâche]
|
||||
```
|
||||
|
||||
Dans ce workflow :
|
||||
|
||||
- Les actions exécutables (comme l'exécution de commandes ou de code) sont gérées directement par le Runtime
|
||||
- Les actions non exécutables (généralement lorsque l'agent veut communiquer ou demander des clarifications) sont gérées par la `user_response_fn`
|
||||
- L'agent traite ensuite le feedback, qu'il s'agisse d'une Observation du Runtime ou d'une réponse simulée de la `user_response_fn`
|
||||
|
||||
Cette approche permet une gestion automatisée des actions concrètes et des interactions utilisateur simulées, ce qui la rend adaptée aux scénarios d'évaluation où vous voulez tester la capacité de l'agent à accomplir des tâches avec une intervention humaine minimale.
|
||||
|
||||
### Exemple d'implémentation
|
||||
|
||||
Voici un exemple de `user_response_fn` utilisée dans l'évaluation SWE-Bench :
|
||||
|
||||
```python
|
||||
def codeact_user_response(state: State | None) -> str:
|
||||
msg = (
|
||||
'Veuillez continuer à travailler sur la tâche avec l\'approche que vous jugez appropriée.\n'
|
||||
'Si vous pensez avoir résolu la tâche, veuillez d\'abord envoyer votre réponse à l\'utilisateur via un message, puis <execute_bash> exit </execute_bash>.\n'
|
||||
'IMPORTANT : VOUS NE DEVEZ JAMAIS DEMANDER DE L\'AIDE HUMAINE.\n'
|
||||
)
|
||||
|
||||
if state and state.history:
|
||||
# vérifier si l'agent a essayé de parler à l'utilisateur 3 fois, si oui, faire savoir à l'agent qu'il peut abandonner
|
||||
user_msgs = [
|
||||
event
|
||||
for event in state.history
|
||||
if isinstance(event, MessageAction) and event.source == 'user'
|
||||
]
|
||||
if len(user_msgs) >= 2:
|
||||
# faire savoir à l'agent qu'il peut abandonner quand il a essayé 3 fois
|
||||
return (
|
||||
msg
|
||||
+ 'Si vous voulez abandonner, exécutez : <execute_bash> exit </execute_bash>.\n'
|
||||
)
|
||||
return msg
|
||||
```
|
||||
|
||||
Cette fonction fait ce qui suit :
|
||||
|
||||
1. Fournit un message standard encourageant l'agent à continuer à travailler
|
||||
2. Vérifie combien de fois l'agent a tenté de communiquer avec l'utilisateur
|
||||
3. Si l'agent a fait plusieurs tentatives, il lui donne la possibilité d'abandonner
|
||||
|
||||
En utilisant cette fonction, vous pouvez assurer un comportement cohérent sur plusieurs exécutions d'évaluation et empêcher l'agent de rester bloqué en attendant une entrée humaine.
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
# Utilisation de l'Action GitHub OpenHands
|
||||
|
||||
Ce guide explique comment utiliser l'Action GitHub OpenHands, à la fois dans le dépôt OpenHands et dans vos propres projets.
|
||||
|
||||
## Utilisation de l'Action dans le dépôt OpenHands
|
||||
|
||||
Pour utiliser l'Action GitHub OpenHands dans un dépôt, vous pouvez :
|
||||
|
||||
1. Créer un ticket dans le dépôt.
|
||||
2. Ajouter l'étiquette `fix-me` au ticket ou laisser un commentaire sur le ticket commençant par `@openhands-agent`.
|
||||
|
||||
L'action se déclenchera automatiquement et tentera de résoudre le ticket.
|
||||
|
||||
## Installation de l'Action dans un nouveau dépôt
|
||||
|
||||
Pour installer l'Action GitHub OpenHands dans votre propre dépôt, suivez le [README pour le Resolver OpenHands](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/resolver/README.md).
|
||||
|
||||
## Conseils d'utilisation
|
||||
|
||||
### Résolution itérative
|
||||
|
||||
1. Créez un ticket dans le dépôt.
|
||||
2. Ajoutez l'étiquette `fix-me` au ticket, ou laissez un commentaire commençant par `@openhands-agent`
|
||||
3. Examinez la tentative de résolution du ticket en vérifiant la pull request
|
||||
4. Faites un suivi avec des commentaires via des commentaires généraux, des commentaires de revue ou des commentaires de fil en ligne
|
||||
5. Ajoutez l'étiquette `fix-me` à la pull request, ou adressez un commentaire spécifique en commençant par `@openhands-agent`
|
||||
|
||||
### Étiquette versus Macro
|
||||
|
||||
- Étiquette (`fix-me`) : Demande à OpenHands de traiter le ticket ou la pull request dans son **intégralité**.
|
||||
- Macro (`@openhands-agent`) : Demande à OpenHands de ne considérer que la description du ticket/de la pull request et **le commentaire spécifique**.
|
||||
|
||||
## Paramètres avancés
|
||||
|
||||
### Ajouter des paramètres de dépôt personnalisés
|
||||
|
||||
Vous pouvez fournir des instructions personnalisées pour OpenHands en suivant le [README pour le resolver](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/resolver/README.md#providing-custom-instructions).
|
||||
|
||||
### Configurations personnalisées
|
||||
|
||||
Le resolver Github vérifiera automatiquement les [secrets de dépôt](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions?tool=webui#creating-secrets-for-a-repository) ou les [variables de dépôt](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#creating-configuration-variables-for-a-repository) valides pour personnaliser son comportement.
|
||||
Les options de personnalisation que vous pouvez définir sont :
|
||||
|
||||
| **Nom de l'attribut** | **Type** | **Objectif** | **Exemple** |
|
||||
|----------------------------------| -------- |-------------------------------------------------------------------------------------------------------------|------------------------------------------------------|
|
||||
| `LLM_MODEL` | Variable | Définir le LLM à utiliser avec OpenHands | `LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"` |
|
||||
| `OPENHANDS_MAX_ITER` | Variable | Définir la limite maximale pour les itérations de l'agent | `OPENHANDS_MAX_ITER=10` |
|
||||
| `OPENHANDS_MACRO` | Variable | Personnaliser la macro par défaut pour invoquer le resolver | `OPENHANDS_MACRO=@resolveit` |
|
||||
| `OPENHANDS_BASE_CONTAINER_IMAGE` | Variable | Sandbox personnalisé ([en savoir plus](https://docs.all-hands.dev/modules/usage/how-to/custom-sandbox-guide))| `OPENHANDS_BASE_CONTAINER_IMAGE="custom_image"` |
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
|
||||
# Mode Interface Graphique
|
||||
|
||||
## Introduction
|
||||
|
||||
OpenHands fournit un mode Interface Graphique (GUI) convivial pour interagir avec l'assistant IA. Ce mode offre une façon intuitive de configurer l'environnement, gérer les paramètres et communiquer avec l'IA.
|
||||
|
||||
## Installation et Configuration
|
||||
|
||||
1. Suivez les instructions du guide [Installation](../installation) pour installer OpenHands.
|
||||
|
||||
2. Après avoir exécuté la commande, accédez à OpenHands à l'adresse [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
## Interagir avec l'Interface Graphique
|
||||
|
||||
### Configuration Initiale
|
||||
|
||||
1. Lors du premier lancement, vous verrez une fenêtre modale de paramètres.
|
||||
2. Sélectionnez un `Fournisseur LLM` et un `Modèle LLM` dans les menus déroulants.
|
||||
3. Entrez la `Clé API` correspondante pour le fournisseur choisi.
|
||||
4. Cliquez sur "Enregistrer" pour appliquer les paramètres.
|
||||
|
||||
### Configuration du Jeton GitHub
|
||||
|
||||
OpenHands exporte automatiquement un `GITHUB_TOKEN` vers l'environnement shell s'il est disponible. Cela peut se produire de deux manières :
|
||||
|
||||
1. **Localement (OSS)** : L'utilisateur saisit directement son jeton GitHub
|
||||
2. **En ligne (SaaS)** : Le jeton est obtenu via l'authentification OAuth GitHub
|
||||
|
||||
#### Configuration d'un Jeton GitHub Local
|
||||
|
||||
1. **Générer un Personal Access Token (PAT)** :
|
||||
- Allez dans Paramètres GitHub > Paramètres développeur > Personal Access Tokens > Tokens (classique)
|
||||
- Cliquez sur "Générer un nouveau jeton (classique)"
|
||||
- Portées requises :
|
||||
- `repo` (Contrôle total des dépôts privés)
|
||||
- `workflow` (Mettre à jour les workflows GitHub Action)
|
||||
- `read:org` (Lire les données de l'organisation)
|
||||
|
||||
2. **Entrer le Jeton dans OpenHands** :
|
||||
- Cliquez sur le bouton Paramètres (icône d'engrenage) en haut à droite
|
||||
- Accédez à la section "GitHub"
|
||||
- Collez votre jeton dans le champ "Jeton GitHub"
|
||||
- Cliquez sur "Enregistrer" pour appliquer les modifications
|
||||
|
||||
#### Politiques de Jetons Organisationnels
|
||||
|
||||
Si vous travaillez avec des dépôts organisationnels, une configuration supplémentaire peut être nécessaire :
|
||||
|
||||
1. **Vérifier les Exigences de l'Organisation** :
|
||||
- Les administrateurs de l'organisation peuvent appliquer des politiques de jetons spécifiques
|
||||
- Certaines organisations exigent que les jetons soient créés avec SSO activé
|
||||
- Consultez les [paramètres de politique de jetons](https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization) de votre organisation
|
||||
|
||||
2. **Vérifier l'Accès à l'Organisation** :
|
||||
- Allez dans les paramètres de votre jeton sur GitHub
|
||||
- Recherchez l'organisation sous "Accès à l'organisation"
|
||||
- Si nécessaire, cliquez sur "Activer SSO" à côté de votre organisation
|
||||
- Terminez le processus d'autorisation SSO
|
||||
|
||||
#### Authentification OAuth (Mode En Ligne)
|
||||
|
||||
Lorsque vous utilisez OpenHands en mode en ligne, le flux OAuth GitHub :
|
||||
|
||||
1. Demande les autorisations suivantes :
|
||||
- Accès au dépôt (lecture/écriture)
|
||||
- Gestion des workflows
|
||||
- Accès en lecture à l'organisation
|
||||
|
||||
2. Étapes d'authentification :
|
||||
- Cliquez sur "Se connecter avec GitHub" lorsque vous y êtes invité
|
||||
- Examinez les autorisations demandées
|
||||
- Autorisez OpenHands à accéder à votre compte GitHub
|
||||
- Si vous utilisez une organisation, autorisez l'accès à l'organisation si vous y êtes invité
|
||||
|
||||
#### Dépannage
|
||||
|
||||
Problèmes courants et solutions :
|
||||
|
||||
1. **Jeton Non Reconnu** :
|
||||
- Assurez-vous que le jeton est correctement enregistré dans les paramètres
|
||||
- Vérifiez que le jeton n'a pas expiré
|
||||
- Vérifiez que le jeton a les portées requises
|
||||
- Essayez de régénérer le jeton
|
||||
|
||||
2. **Accès à l'Organisation Refusé** :
|
||||
- Vérifiez si SSO est requis mais non activé
|
||||
- Vérifiez l'appartenance à l'organisation
|
||||
- Contactez l'administrateur de l'organisation si les politiques de jetons bloquent l'accès
|
||||
|
||||
3. **Vérifier que le Jeton Fonctionne** :
|
||||
- L'application affichera une coche verte si le jeton est valide
|
||||
- Essayez d'accéder à un dépôt pour confirmer les autorisations
|
||||
- Vérifiez la console du navigateur pour tout message d'erreur
|
||||
- Utilisez le bouton "Tester la connexion" dans les paramètres s'il est disponible
|
||||
|
||||
### Paramètres Avancés
|
||||
|
||||
1. Basculez sur `Options Avancées` pour accéder aux paramètres supplémentaires.
|
||||
2. Utilisez la zone de texte `Modèle Personnalisé` pour saisir manuellement un modèle s'il ne figure pas dans la liste.
|
||||
3. Spécifiez une `URL de Base` si requis par votre fournisseur LLM.
|
||||
|
||||
### Interface Principale
|
||||
|
||||
L'interface principale se compose de plusieurs composants clés :
|
||||
|
||||
1. **Fenêtre de Chat** : La zone centrale où vous pouvez voir l'historique de conversation avec l'assistant IA.
|
||||
2. **Zone de Saisie** : Située en bas de l'écran, utilisez-la pour taper vos messages ou commandes à l'IA.
|
||||
3. **Bouton Envoyer** : Cliquez dessus pour envoyer votre message à l'IA.
|
||||
4. **Bouton Paramètres** : Une icône d'engrenage qui ouvre la fenêtre modale des paramètres, vous permettant d'ajuster votre configuration à tout moment.
|
||||
5. **Panneau Espace de Travail** : Affiche les fichiers et dossiers de votre espace de travail, vous permettant de naviguer et de visualiser les fichiers, ou les commandes passées de l'agent ou l'historique de navigation web.
|
||||
|
||||
### Interagir avec l'IA
|
||||
|
||||
1. Tapez votre question, demande ou description de tâche dans la zone de saisie.
|
||||
2. Cliquez sur le bouton d'envoi ou appuyez sur Entrée pour soumettre votre message.
|
||||
3. L'IA traitera votre saisie et fournira une réponse dans la fenêtre de chat.
|
||||
4. Vous pouvez poursuivre la conversation en posant des questions de suivi ou en fournissant des informations supplémentaires.
|
||||
|
||||
## Conseils pour une Utilisation Efficace
|
||||
|
||||
1. Soyez précis dans vos demandes pour obtenir les réponses les plus précises et utiles, comme décrit dans les [meilleures pratiques d'incitation](../prompting/prompting-best-practices).
|
||||
2. Utilisez le panneau d'espace de travail pour explorer la structure de votre projet.
|
||||
3. Utilisez l'un des modèles recommandés, comme décrit dans la section [LLMs](usage/llms/llms.md).
|
||||
|
||||
N'oubliez pas que le mode Interface Graphique d'OpenHands est conçu pour rendre votre interaction avec l'assistant IA aussi fluide et intuitive que possible. N'hésitez pas à explorer ses fonctionnalités pour maximiser votre productivité.
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
|
||||
# Mode sans interface
|
||||
|
||||
Vous pouvez exécuter OpenHands avec une seule commande, sans démarrer l'application web.
|
||||
Cela facilite l'écriture de scripts et l'automatisation des tâches avec OpenHands.
|
||||
|
||||
Ceci est différent du [Mode CLI](cli-mode), qui est interactif et mieux adapté au développement actif.
|
||||
|
||||
## Avec Python
|
||||
|
||||
Pour exécuter OpenHands en mode sans interface avec Python,
|
||||
[suivez les instructions de configuration de développement](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md),
|
||||
puis exécutez :
|
||||
|
||||
```bash
|
||||
poetry run python -m openhands.core.main -t "write a bash script that prints hi"
|
||||
```
|
||||
|
||||
Vous devrez vous assurer de définir votre modèle, votre clé API et d'autres paramètres via des variables d'environnement
|
||||
[ou le fichier `config.toml`](https://github.com/All-Hands-AI/OpenHands/blob/main/config.template.toml).
|
||||
|
||||
## Avec Docker
|
||||
|
||||
1. Définissez `WORKSPACE_BASE` sur le répertoire que vous voulez qu'OpenHands modifie :
|
||||
|
||||
```bash
|
||||
WORKSPACE_BASE=$(pwd)/workspace
|
||||
```
|
||||
|
||||
2. Définissez `LLM_MODEL` sur le modèle que vous voulez utiliser :
|
||||
|
||||
```bash
|
||||
LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"
|
||||
|
||||
```
|
||||
|
||||
3. Définissez `LLM_API_KEY` sur votre clé API :
|
||||
|
||||
```bash
|
||||
LLM_API_KEY="sk_test_12345"
|
||||
```
|
||||
|
||||
4. Exécutez la commande Docker suivante :
|
||||
|
||||
```bash
|
||||
docker run -it \
|
||||
--pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e SANDBOX_USER_ID=$(id -u) \
|
||||
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
|
||||
-e LLM_API_KEY=$LLM_API_KEY \
|
||||
-e LLM_MODEL=$LLM_MODEL \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v $WORKSPACE_BASE:/opt/workspace_base \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app-$(date +%Y%m%d%H%M%S) \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21 \
|
||||
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
|
||||
```
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
# Persistance des données de session
|
||||
|
||||
Avec l'installation standard, les données de session sont stockées en mémoire. Actuellement, si le service OpenHands est redémarré,
|
||||
les sessions précédentes deviennent invalides (un nouveau secret est généré) et ne sont donc pas récupérables.
|
||||
|
||||
## Comment persister les données de session
|
||||
|
||||
### Workflow de développement
|
||||
Dans le fichier `config.toml`, spécifiez ce qui suit :
|
||||
```
|
||||
[core]
|
||||
...
|
||||
file_store="local"
|
||||
file_store_path="/absolute/path/to/openhands/cache/directory"
|
||||
jwt_secret="secretpass"
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
## Configuration système requise
|
||||
|
||||
* Docker version 26.0.0+ ou Docker Desktop 4.31.0+.
|
||||
* Vous devez utiliser Linux ou Mac OS.
|
||||
* Si vous êtes sous Windows, vous devez utiliser [WSL](https://learn.microsoft.com/en-us/windows/wsl/install).
|
||||
|
||||
## Démarrer l'application
|
||||
|
||||
La façon la plus simple d'exécuter OpenHands est avec Docker.
|
||||
|
||||
```bash
|
||||
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik
|
||||
|
||||
docker run -it --rm --pull=always \
|
||||
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.21-nikolaik \
|
||||
-e LOG_ALL_EVENTS=true \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-p 3000:3000 \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
--name openhands-app \
|
||||
docker.all-hands.dev/all-hands-ai/openhands:0.21
|
||||
```
|
||||
|
||||
Vous pouvez également exécuter OpenHands en mode [headless scriptable](https://docs.all-hands.dev/modules/usage/how-to/headless-mode), en tant que [CLI interactive](https://docs.all-hands.dev/modules/usage/how-to/cli-mode), ou en utilisant l'[Action GitHub OpenHands](https://docs.all-hands.dev/modules/usage/how-to/github-action).
|
||||
|
||||
## Configuration
|
||||
|
||||
Après avoir exécuté la commande ci-dessus, vous trouverez OpenHands en cours d'exécution à l'adresse [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
Au lancement d'OpenHands, vous verrez une fenêtre modale de paramètres. Vous **devez** sélectionner un `Fournisseur LLM` et un `Modèle LLM`, et entrer une `Clé API` correspondante.
|
||||
Ces paramètres peuvent être modifiés à tout moment en sélectionnant le bouton `Paramètres` (icône d'engrenage) dans l'interface utilisateur.
|
||||
|
||||
Si le `Modèle LLM` requis n'existe pas dans la liste, vous pouvez activer les `Options avancées` et l'entrer manuellement avec le préfixe correct
|
||||
dans la zone de texte `Modèle personnalisé`.
|
||||
Les `Options avancées` vous permettent également de spécifier une `URL de base` si nécessaire.
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '20px' }}>
|
||||
<img src="/img/settings-screenshot.png" alt="settings-modal" width="340" />
|
||||
<img src="/img/settings-advanced.png" alt="settings-modal" width="335" />
|
||||
</div>
|
||||
|
||||
## Versions
|
||||
|
||||
La commande ci-dessus récupère la version stable la plus récente d'OpenHands. Vous avez également d'autres options :
|
||||
- Pour une version spécifique, utilisez `docker.all-hands.dev/all-hands-ai/openhands:$VERSION`, en remplaçant $VERSION par le numéro de version.
|
||||
- Nous utilisons semver et publions des tags majeurs, mineurs et de patch. Ainsi, `0.9` pointera automatiquement vers la dernière version `0.9.x`, et `0` pointera vers la dernière version `0.x.x`.
|
||||
- Pour la version de développement la plus à jour, vous pouvez utiliser `docker.all-hands.dev/all-hands-ai/openhands:main`. Cette version est instable et n'est recommandée qu'à des fins de test ou de développement.
|
||||
|
||||
Vous pouvez choisir le tag qui convient le mieux à vos besoins en fonction des exigences de stabilité et des fonctionnalités souhaitées.
|
||||
|
||||
Pour le workflow de développement, consultez [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md).
|
||||
|
||||
Vous rencontrez des problèmes ? Consultez notre [Guide de dépannage](https://docs.all-hands.dev/modules/usage/troubleshooting).
|
||||
@@ -42,7 +42,7 @@ Explorez le code source d'OpenHands sur [GitHub](https://github.com/All-Hands-AI
|
||||
/>
|
||||
</a>
|
||||
<br></br>
|
||||
<a href="https://join.slack.com/t/opendevin/shared_invite/zt-2oikve2hu-UDxHeo8nsE69y6T7yFX_BA">
|
||||
<a href="https://join.slack.com/t/openhands-ai/shared_invite/zt-2wkh4pklz-w~h_DVDtEe9H5kyQlcNxVw">
|
||||
<img
|
||||
src="https://img.shields.io/badge/Slack-Join%20Us-red?logo=slack&logoColor=white&style=for-the-badge"
|
||||
alt="Join our Slack community"
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
|
||||
# Azure
|
||||
|
||||
OpenHands utilise LiteLLM pour faire des appels aux modèles de chat d'Azure. Vous pouvez trouver leur documentation sur l'utilisation d'Azure comme fournisseur [ici](https://docs.litellm.ai/docs/providers/azure).
|
||||
|
||||
## Configuration d'Azure OpenAI
|
||||
|
||||
Lorsque vous exécutez OpenHands, vous devrez définir la variable d'environnement suivante en utilisant `-e` dans la
|
||||
[commande docker run](/modules/usage/installation#start-the-app) :
|
||||
|
||||
```
|
||||
LLM_API_VERSION="<api-version>" # par exemple "2023-05-15"
|
||||
```
|
||||
|
||||
Exemple :
|
||||
```bash
|
||||
docker run -it --pull=always \
|
||||
-e LLM_API_VERSION="2023-05-15"
|
||||
...
|
||||
```
|
||||
|
||||
Ensuite, définissez les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
|
||||
:::note
|
||||
Vous aurez besoin du nom de votre déploiement ChatGPT qui peut être trouvé sur la page des déploiements dans Azure. Il est référencé comme
|
||||
<deployment-name> ci-dessous.
|
||||
:::
|
||||
|
||||
* Activez `Advanced Options`
|
||||
* `Custom Model` à azure/<deployment-name>
|
||||
* `Base URL` à votre URL de base de l'API Azure (par exemple `https://example-endpoint.openai.azure.com`)
|
||||
* `API Key` à votre clé API Azure
|
||||
|
||||
## Embeddings
|
||||
|
||||
OpenHands utilise llama-index pour les embeddings. Vous pouvez trouver leur documentation sur Azure [ici](https://docs.llamaindex.ai/en/stable/api_reference/embeddings/azure_openai/).
|
||||
|
||||
### Configuration d'Azure OpenAI
|
||||
|
||||
Lorsque vous exécutez OpenHands, définissez les variables d'environnement suivantes en utilisant `-e` dans la
|
||||
[commande docker run](/modules/usage/installation#start-the-app) :
|
||||
|
||||
```
|
||||
LLM_EMBEDDING_MODEL="azureopenai"
|
||||
LLM_EMBEDDING_DEPLOYMENT_NAME="<your-embedding-deployment-name>" # par exemple "TextEmbedding...<etc>"
|
||||
LLM_API_VERSION="<api-version>" # par exemple "2024-02-15-preview"
|
||||
```
|
||||
@@ -0,0 +1,106 @@
|
||||
# Configurations LLM personnalisées
|
||||
|
||||
OpenHands permet de définir plusieurs configurations LLM nommées dans votre fichier `config.toml`. Cette fonctionnalité vous permet d'utiliser différentes configurations LLM pour différents usages, comme utiliser un modèle moins coûteux pour les tâches qui ne nécessitent pas de réponses de haute qualité, ou utiliser différents modèles avec différents paramètres pour des agents spécifiques.
|
||||
|
||||
## Comment ça fonctionne
|
||||
|
||||
Les configurations LLM nommées sont définies dans le fichier `config.toml` en utilisant des sections qui commencent par `llm.`. Par exemple :
|
||||
|
||||
```toml
|
||||
# Configuration LLM par défaut
|
||||
[llm]
|
||||
model = "gpt-4"
|
||||
api_key = "votre-clé-api"
|
||||
temperature = 0.0
|
||||
|
||||
# Configuration LLM personnalisée pour un modèle moins coûteux
|
||||
[llm.gpt3]
|
||||
model = "gpt-3.5-turbo"
|
||||
api_key = "votre-clé-api"
|
||||
temperature = 0.2
|
||||
|
||||
# Une autre configuration personnalisée avec des paramètres différents
|
||||
[llm.haute-creativite]
|
||||
model = "gpt-4"
|
||||
api_key = "votre-clé-api"
|
||||
temperature = 0.8
|
||||
top_p = 0.9
|
||||
```
|
||||
|
||||
Chaque configuration nommée hérite de tous les paramètres de la section `[llm]` par défaut et peut remplacer n'importe lequel de ces paramètres. Vous pouvez définir autant de configurations personnalisées que nécessaire.
|
||||
|
||||
## Utilisation des configurations personnalisées
|
||||
|
||||
### Avec les agents
|
||||
|
||||
Vous pouvez spécifier quelle configuration LLM un agent doit utiliser en définissant le paramètre `llm_config` dans la section de configuration de l'agent :
|
||||
|
||||
```toml
|
||||
[agent.RepoExplorerAgent]
|
||||
# Utiliser la configuration GPT-3 moins coûteuse pour cet agent
|
||||
llm_config = 'gpt3'
|
||||
|
||||
[agent.CodeWriterAgent]
|
||||
# Utiliser la configuration haute créativité pour cet agent
|
||||
llm_config = 'haute-creativite'
|
||||
```
|
||||
|
||||
### Options de configuration
|
||||
|
||||
Chaque configuration LLM nommée prend en charge toutes les mêmes options que la configuration LLM par défaut. Celles-ci incluent :
|
||||
|
||||
- Sélection du modèle (`model`)
|
||||
- Configuration de l'API (`api_key`, `base_url`, etc.)
|
||||
- Paramètres du modèle (`temperature`, `top_p`, etc.)
|
||||
- Paramètres de nouvelle tentative (`num_retries`, `retry_multiplier`, etc.)
|
||||
- Limites de jetons (`max_input_tokens`, `max_output_tokens`)
|
||||
- Et toutes les autres options de configuration LLM
|
||||
|
||||
Pour une liste complète des options disponibles, consultez la section Configuration LLM dans la documentation des [Options de configuration](../configuration-options).
|
||||
|
||||
## Cas d'utilisation
|
||||
|
||||
Les configurations LLM personnalisées sont particulièrement utiles dans plusieurs scénarios :
|
||||
|
||||
- **Optimisation des coûts** : Utiliser des modèles moins coûteux pour les tâches qui ne nécessitent pas de réponses de haute qualité, comme l'exploration de dépôt ou les opérations simples sur les fichiers.
|
||||
- **Réglage spécifique aux tâches** : Configurer différentes valeurs de température et de top_p pour les tâches qui nécessitent différents niveaux de créativité ou de déterminisme.
|
||||
- **Différents fournisseurs** : Utiliser différents fournisseurs LLM ou points d'accès API pour différentes tâches.
|
||||
- **Tests et développement** : Basculer facilement entre différentes configurations de modèles pendant le développement et les tests.
|
||||
|
||||
## Exemple : Optimisation des coûts
|
||||
|
||||
Un exemple pratique d'utilisation des configurations LLM personnalisées pour optimiser les coûts :
|
||||
|
||||
```toml
|
||||
# Configuration par défaut utilisant GPT-4 pour des réponses de haute qualité
|
||||
[llm]
|
||||
model = "gpt-4"
|
||||
api_key = "votre-clé-api"
|
||||
temperature = 0.0
|
||||
|
||||
# Configuration moins coûteuse pour l'exploration de dépôt
|
||||
[llm.repo-explorer]
|
||||
model = "gpt-3.5-turbo"
|
||||
temperature = 0.2
|
||||
|
||||
# Configuration pour la génération de code
|
||||
[llm.code-gen]
|
||||
model = "gpt-4"
|
||||
temperature = 0.0
|
||||
max_output_tokens = 2000
|
||||
|
||||
[agent.RepoExplorerAgent]
|
||||
llm_config = 'repo-explorer'
|
||||
|
||||
[agent.CodeWriterAgent]
|
||||
llm_config = 'code-gen'
|
||||
```
|
||||
|
||||
Dans cet exemple :
|
||||
- L'exploration de dépôt utilise un modèle moins coûteux car il s'agit principalement de comprendre et de naviguer dans le code
|
||||
- La génération de code utilise GPT-4 avec une limite de jetons plus élevée pour générer des blocs de code plus importants
|
||||
- La configuration par défaut reste disponible pour les autres tâches
|
||||
|
||||
:::note
|
||||
Les configurations LLM personnalisées ne sont disponibles que lors de l'utilisation d'OpenHands en mode développement, via `main.py` ou `cli.py`. Lors de l'exécution via `docker run`, veuillez utiliser les options de configuration standard.
|
||||
:::
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
# Google Gemini/Vertex
|
||||
|
||||
OpenHands utilise LiteLLM pour faire des appels aux modèles de chat de Google. Vous pouvez trouver leur documentation sur l'utilisation de Google comme fournisseur :
|
||||
|
||||
- [Gemini - Google AI Studio](https://docs.litellm.ai/docs/providers/gemini)
|
||||
- [VertexAI - Google Cloud Platform](https://docs.litellm.ai/docs/providers/vertex)
|
||||
|
||||
## Configurations de Gemini - Google AI Studio
|
||||
|
||||
Lors de l'exécution d'OpenHands, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* `LLM Provider` à `Gemini`
|
||||
* `LLM Model` au modèle que vous utiliserez.
|
||||
Si le modèle ne figure pas dans la liste, activez `Advanced Options` et entrez-le dans `Custom Model` (par exemple, gemini/<model-name> comme `gemini/gemini-1.5-pro`).
|
||||
* `API Key` à votre clé API Gemini
|
||||
|
||||
## Configurations de VertexAI - Google Cloud Platform
|
||||
|
||||
Pour utiliser Vertex AI via Google Cloud Platform lors de l'exécution d'OpenHands, vous devrez définir les variables d'environnement suivantes en utilisant `-e` dans la [commande docker run](/modules/usage/installation#start-the-app) :
|
||||
|
||||
```
|
||||
GOOGLE_APPLICATION_CREDENTIALS="<json-dump-of-gcp-service-account-json>"
|
||||
VERTEXAI_PROJECT="<your-gcp-project-id>"
|
||||
VERTEXAI_LOCATION="<your-gcp-location>"
|
||||
```
|
||||
|
||||
Ensuite, définissez les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* `LLM Provider` à `VertexAI`
|
||||
* `LLM Model` au modèle que vous utiliserez.
|
||||
Si le modèle ne figure pas dans la liste, activez `Advanced Options` et entrez-le dans `Custom Model` (par exemple, vertex_ai/<model-name>).
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
# Groq
|
||||
|
||||
OpenHands utilise LiteLLM pour faire des appels aux modèles de chat sur Groq. Vous pouvez trouver leur documentation sur l'utilisation de Groq comme fournisseur [ici](https://docs.litellm.ai/docs/providers/groq).
|
||||
|
||||
## Configuration
|
||||
|
||||
Lorsque vous exécutez OpenHands, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* `LLM Provider` à `Groq`
|
||||
* `LLM Model` au modèle que vous utiliserez. [Visitez ici pour voir la liste des modèles hébergés par Groq](https://console.groq.com/docs/models). Si le modèle n'est pas dans la liste, activez les `Advanced Options` et entrez-le dans `Custom Model` (par exemple, groq/<model-name> comme `groq/llama3-70b-8192`).
|
||||
* `API key` à votre clé API Groq. Pour trouver ou créer votre clé API Groq, [voir ici](https://console.groq.com/keys).
|
||||
|
||||
|
||||
|
||||
## Utilisation de Groq comme point de terminaison compatible OpenAI
|
||||
|
||||
Le point de terminaison Groq pour la complétion de chat est [principalement compatible OpenAI](https://console.groq.com/docs/openai). Par conséquent, vous pouvez accéder aux modèles Groq comme vous le feriez pour n'importe quel point de terminaison compatible OpenAI. Vous pouvez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* Activer les `Advanced Options`
|
||||
* `Custom Model` au préfixe `openai/` + le modèle que vous utiliserez (par exemple, `openai/llama3-70b-8192`)
|
||||
* `Base URL` à `https://api.groq.com/openai/v1`
|
||||
* `API Key` à votre clé API Groq
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
# Proxy LiteLLM
|
||||
|
||||
OpenHands prend en charge l'utilisation du [proxy LiteLLM](https://docs.litellm.ai/docs/proxy/quick_start) pour accéder à divers fournisseurs de LLM.
|
||||
|
||||
## Configuration
|
||||
|
||||
Pour utiliser le proxy LiteLLM avec OpenHands, vous devez :
|
||||
|
||||
1. Configurer un serveur proxy LiteLLM (voir la [documentation LiteLLM](https://docs.litellm.ai/docs/proxy/quick_start))
|
||||
2. Lors de l'exécution d'OpenHands, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* Activer les `Options avancées`
|
||||
* `Custom Model` au préfixe `litellm_proxy/` + le modèle que vous utiliserez (par exemple, `litellm_proxy/anthropic.claude-3-5-sonnet-20241022-v2:0`)
|
||||
* `Base URL` à l'URL de votre proxy LiteLLM (par exemple, `https://your-litellm-proxy.com`)
|
||||
* `API Key` à votre clé API du proxy LiteLLM
|
||||
|
||||
## Modèles pris en charge
|
||||
|
||||
Les modèles pris en charge dépendent de la configuration de votre proxy LiteLLM. OpenHands prend en charge tous les modèles que votre proxy LiteLLM est configuré pour gérer.
|
||||
|
||||
Reportez-vous à la configuration de votre proxy LiteLLM pour obtenir la liste des modèles disponibles et leurs noms.
|
||||
@@ -1,44 +1,84 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
|
||||
# 🤖 Backends LLM
|
||||
|
||||
OpenHands peut fonctionner avec n'importe quel backend LLM.
|
||||
Pour une liste complète des fournisseurs et des modèles LM disponibles, veuillez consulter la
|
||||
[documentation litellm](https://docs.litellm.ai/docs/providers).
|
||||
OpenHands peut se connecter à n'importe quel LLM supporté par LiteLLM. Cependant, il nécessite un modèle puissant pour fonctionner.
|
||||
|
||||
## Recommandations de modèles
|
||||
|
||||
Sur la base de nos évaluations des modèles de langage pour les tâches de codage (en utilisant le jeu de données SWE-bench), nous pouvons fournir quelques recommandations pour la sélection des modèles. Certaines analyses peuvent être trouvées dans [cet article de blog comparant les LLM](https://www.all-hands.dev/blog/evaluation-of-llms-as-coding-agents-on-swe-bench-at-30x-speed) et [cet article de blog avec des résultats plus récents](https://www.all-hands.dev/blog/openhands-codeact-21-an-open-state-of-the-art-software-development-agent).
|
||||
|
||||
Lors du choix d'un modèle, considérez à la fois la qualité des sorties et les coûts associés. Voici un résumé des résultats :
|
||||
|
||||
- Claude 3.5 Sonnet est le meilleur de loin, atteignant un taux de résolution de 53% sur SWE-Bench Verified avec l'agent par défaut dans OpenHands.
|
||||
- GPT-4o est à la traîne, et o1-mini a en fait obtenu des performances légèrement inférieures à celles de GPT-4o. Nous avons analysé les résultats un peu, et brièvement, il semblait que o1 "réfléchissait trop" parfois, effectuant des tâches de configuration d'environnement supplémentaires alors qu'il aurait pu simplement aller de l'avant et terminer la tâche.
|
||||
- Enfin, les modèles ouverts les plus puissants étaient Llama 3.1 405 B et deepseek-v2.5, et ils ont obtenu des performances raisonnables, surpassant même certains des modèles fermés.
|
||||
|
||||
Veuillez vous référer à [l'article complet](https://www.all-hands.dev/blog/evaluation-of-llms-as-coding-agents-on-swe-bench-at-30x-speed) pour plus de détails.
|
||||
|
||||
Sur la base de ces résultats et des commentaires de la communauté, il a été vérifié que les modèles suivants fonctionnent raisonnablement bien avec OpenHands :
|
||||
|
||||
- claude-3-5-sonnet (recommandé)
|
||||
- gpt-4 / gpt-4o
|
||||
- llama-3.1-405b
|
||||
- deepseek-v2.5
|
||||
|
||||
:::warning
|
||||
OpenHands émettra de nombreuses invitations au LLM que vous configurez. La plupart de ces LLM coûtent de l'argent -- assurez-vous de définir des limites de dépenses et de surveiller l'utilisation.
|
||||
OpenHands enverra de nombreuses invites au LLM que vous configurez. La plupart de ces LLM sont payants, alors assurez-vous de définir des limites de dépenses et de surveiller l'utilisation.
|
||||
:::
|
||||
|
||||
La variable d'environnement `LLM_MODEL` contrôle le modèle utilisé dans les interactions programmatiques.
|
||||
Mais en utilisant l'interface utilisateur OpenHands, vous devrez choisir votre modèle dans la fenêtre des paramètres (la roue dentée en bas à gauche).
|
||||
Si vous avez réussi à exécuter OpenHands avec des LLM spécifiques qui ne figurent pas dans la liste, veuillez les ajouter à la liste vérifiée. Nous vous encourageons également à ouvrir une PR pour partager votre processus de configuration afin d'aider les autres utilisant le même fournisseur et LLM !
|
||||
|
||||
Les variables d'environnement suivantes peuvent être nécessaires pour certains LLM :
|
||||
Pour une liste complète des fournisseurs et des modèles disponibles, veuillez consulter la [documentation litellm](https://docs.litellm.ai/docs/providers).
|
||||
|
||||
- `LLM_API_KEY`
|
||||
- `LLM_BASE_URL`
|
||||
:::note
|
||||
La plupart des modèles locaux et open source actuels ne sont pas aussi puissants. Lors de l'utilisation de tels modèles, vous pouvez constater de longs temps d'attente entre les messages, des réponses médiocres ou des erreurs concernant du JSON mal formé. OpenHands ne peut être aussi puissant que les modèles qui le pilotent. Cependant, si vous en trouvez qui fonctionnent, veuillez les ajouter à la liste vérifiée ci-dessus.
|
||||
:::
|
||||
|
||||
## Configuration LLM
|
||||
|
||||
Les éléments suivants peuvent être définis dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
|
||||
- `Fournisseur LLM`
|
||||
- `Modèle LLM`
|
||||
- `Clé API`
|
||||
- `URL de base` (via `Paramètres avancés`)
|
||||
|
||||
Il existe certains paramètres qui peuvent être nécessaires pour certains LLM/fournisseurs et qui ne peuvent pas être définis via l'interface utilisateur. Au lieu de cela, ils peuvent être définis via des variables d'environnement passées à la [commande docker run](/modules/usage/installation#start-the-app) en utilisant `-e` :
|
||||
|
||||
- `LLM_API_VERSION`
|
||||
- `LLM_EMBEDDING_MODEL`
|
||||
- `LLM_EMBEDDING_DEPLOYMENT_NAME`
|
||||
- `LLM_API_VERSION`
|
||||
- `LLM_DROP_PARAMS`
|
||||
- `LLM_DISABLE_VISION`
|
||||
- `LLM_CACHING_PROMPT`
|
||||
|
||||
Nous avons quelques guides pour exécuter OpenHands avec des fournisseurs de modèles spécifiques :
|
||||
|
||||
- [ollama](llms/local-llms)
|
||||
- [Azure](llms/azure-llms)
|
||||
- [Google](llms/google-llms)
|
||||
- [Groq](llms/groq)
|
||||
- [LiteLLM Proxy](llms/litellm-proxy)
|
||||
- [OpenAI](llms/openai-llms)
|
||||
- [OpenRouter](llms/openrouter)
|
||||
|
||||
Si vous utilisez un autre fournisseur, nous vous encourageons à ouvrir une PR pour partager votre configuration !
|
||||
### Nouvelles tentatives d'API et limites de débit
|
||||
|
||||
## Remarque sur les modèles alternatifs
|
||||
Les fournisseurs de LLM ont généralement des limites de débit, parfois très basses, et peuvent nécessiter de nouvelles tentatives. OpenHands réessaiera automatiquement les requêtes s'il reçoit une erreur de limite de débit (code d'erreur 429), une erreur de connexion API ou d'autres erreurs transitoires.
|
||||
|
||||
Les meilleurs modèles sont GPT-4 et Claude 3. Les modèles locaux et open source actuels ne sont pas aussi puissants.
|
||||
Lors de l'utilisation d'un modèle alternatif, vous pouvez constater des temps d'attente prolongés entre les messages,
|
||||
des réponses de mauvaise qualité ou des erreurs sur des JSON mal formés. OpenHands
|
||||
ne peut être aussi puissant que les modèles qui le pilotent -- heureusement, les membres de notre équipe travaillent activement à la construction de meilleurs modèles open source !
|
||||
Vous pouvez personnaliser ces options selon vos besoins pour le fournisseur que vous utilisez. Consultez leur documentation et définissez les variables d'environnement suivantes pour contrôler le nombre de nouvelles tentatives et le temps entre les tentatives :
|
||||
|
||||
## Réessais d'API et limites de taux
|
||||
- `LLM_NUM_RETRIES` (Par défaut 8)
|
||||
- `LLM_RETRY_MIN_WAIT` (Par défaut 15 secondes)
|
||||
- `LLM_RETRY_MAX_WAIT` (Par défaut 120 secondes)
|
||||
- `LLM_RETRY_MULTIPLIER` (Par défaut 2)
|
||||
|
||||
Certains LLM ont des limites de taux et peuvent nécessiter des réessais. OpenHands réessaiera automatiquement les demandes s'il reçoit une erreur 429 ou une erreur de connexion API.
|
||||
Vous pouvez définir les variables d'environnement `LLM_NUM_RETRIES`, `LLM_RETRY_MIN_WAIT`, `LLM_RETRY_MAX_WAIT` pour contrôler le nombre de réessais et le temps entre les réessais.
|
||||
Par défaut, `LLM_NUM_RETRIES` est 8 et `LLM_RETRY_MIN_WAIT`, `LLM_RETRY_MAX_WAIT` sont respectivement de 15 secondes et 120 secondes.
|
||||
Si vous exécutez OpenHands en mode développement, vous pouvez également définir ces options dans le fichier `config.toml` :
|
||||
|
||||
```toml
|
||||
[llm]
|
||||
num_retries = 8
|
||||
retry_min_wait = 15
|
||||
retry_max_wait = 120
|
||||
retry_multiplier = 2
|
||||
```
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
|
||||
# LLM local avec Ollama
|
||||
|
||||
:::warning
|
||||
Lors de l'utilisation d'un LLM local, OpenHands peut avoir des fonctionnalités limitées.
|
||||
:::
|
||||
|
||||
Assurez-vous que le serveur Ollama est opérationnel.
|
||||
Pour des instructions détaillées sur le démarrage, référez-vous à [ici](https://github.com/ollama/ollama).
|
||||
|
||||
Ce guide suppose que vous avez démarré ollama avec `ollama serve`. Si vous exécutez ollama différemment (par exemple dans docker), les instructions peuvent nécessiter des modifications. Veuillez noter que si vous utilisez WSL, la configuration par défaut d'ollama bloque les requêtes provenant des conteneurs docker. Voir [ici](#configuring-ollama-service-wsl-fr).
|
||||
|
||||
## Récupérer les modèles
|
||||
|
||||
Les noms des modèles Ollama peuvent être trouvés [ici](https://ollama.com/library). Pour un petit exemple, vous pouvez utiliser le modèle `codellama:7b`. Les modèles plus gros auront généralement de meilleures performances.
|
||||
|
||||
```bash
|
||||
ollama pull codellama:7b
|
||||
```
|
||||
|
||||
Vous pouvez vérifier quels modèles vous avez téléchargés comme ceci :
|
||||
|
||||
```bash
|
||||
~$ ollama list
|
||||
NAME ID SIZE MODIFIED
|
||||
codellama:7b 8fdf8f752f6e 3.8 GB 6 weeks ago
|
||||
mistral:7b-instruct-v0.2-q4_K_M eb14864c7427 4.4 GB 2 weeks ago
|
||||
starcoder2:latest f67ae0f64584 1.7 GB 19 hours ago
|
||||
```
|
||||
|
||||
## Exécuter OpenHands avec Docker
|
||||
|
||||
### Démarrer OpenHands
|
||||
Utilisez les instructions [ici](../getting-started) pour démarrer OpenHands en utilisant Docker.
|
||||
Mais lorsque vous exécutez `docker run`, vous devrez ajouter quelques arguments supplémentaires :
|
||||
|
||||
```bash
|
||||
docker run # ...
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
-e LLM_OLLAMA_BASE_URL="http://host.docker.internal:11434" \
|
||||
# ...
|
||||
```
|
||||
|
||||
LLM_OLLAMA_BASE_URL est optionnel. Si vous le définissez, il sera utilisé pour afficher
|
||||
les modèles installés disponibles dans l'interface utilisateur.
|
||||
|
||||
|
||||
### Configurer l'application Web
|
||||
|
||||
Lors de l'exécution d'`openhands`, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
- le modèle à "ollama/<nom-du-modèle>"
|
||||
- l'URL de base à `http://host.docker.internal:11434`
|
||||
- la clé API est optionnelle, vous pouvez utiliser n'importe quelle chaîne, comme `ollama`.
|
||||
|
||||
|
||||
## Exécuter OpenHands en mode développement
|
||||
|
||||
### Compiler à partir du code source
|
||||
|
||||
Utilisez les instructions dans [Development.md](https://github.com/All-Hands-AI/OpenHands/blob/main/Development.md) pour compiler OpenHands.
|
||||
Assurez-vous que `config.toml` est présent en exécutant `make setup-config` qui en créera un pour vous. Dans `config.toml`, entrez ce qui suit :
|
||||
|
||||
```
|
||||
[core]
|
||||
workspace_base="./workspace"
|
||||
|
||||
[llm]
|
||||
embedding_model="local"
|
||||
ollama_base_url="http://localhost:11434"
|
||||
|
||||
```
|
||||
|
||||
Terminé ! Vous pouvez maintenant démarrer OpenHands avec : `make run`. Vous devriez maintenant pouvoir vous connecter à `http://localhost:3000/`
|
||||
|
||||
### Configurer l'application Web
|
||||
|
||||
Dans l'interface utilisateur d'OpenHands, cliquez sur la roue des paramètres dans le coin inférieur gauche.
|
||||
Ensuite, dans le champ `Model`, entrez `ollama/codellama:7b`, ou le nom du modèle que vous avez récupéré précédemment.
|
||||
S'il n'apparaît pas dans la liste déroulante, activez `Advanced Settings` et tapez-le. Veuillez noter : vous avez besoin du nom du modèle tel qu'il est listé par `ollama list`, avec le préfixe `ollama/`.
|
||||
|
||||
Dans le champ API Key, entrez `ollama` ou n'importe quelle valeur, puisque vous n'avez pas besoin d'une clé particulière.
|
||||
|
||||
Dans le champ Base URL, entrez `http://localhost:11434`.
|
||||
|
||||
Et maintenant vous êtes prêt à démarrer !
|
||||
|
||||
## Configurer le service ollama (WSL) {#configuring-ollama-service-wsl-fr}
|
||||
|
||||
La configuration par défaut d'ollama dans WSL ne sert que localhost. Cela signifie que vous ne pouvez pas y accéder depuis un conteneur docker. Par ex. cela ne fonctionnera pas avec OpenHands. Testons d'abord qu'ollama fonctionne correctement.
|
||||
|
||||
```bash
|
||||
ollama list # obtenir la liste des modèles installés
|
||||
curl http://localhost:11434/api/generate -d '{"model":"[NOM]","prompt":"hi"}'
|
||||
#ex. curl http://localhost:11434/api/generate -d '{"model":"codellama:7b","prompt":"hi"}'
|
||||
#ex. curl http://localhost:11434/api/generate -d '{"model":"codellama","prompt":"hi"}' #le tag est optionnel s'il n'y en a qu'un
|
||||
```
|
||||
|
||||
Une fois cela fait, testez qu'il autorise les requêtes "extérieures", comme celles provenant d'un conteneur docker.
|
||||
|
||||
```bash
|
||||
docker ps # obtenir la liste des conteneurs docker en cours d'exécution, pour un test plus précis choisissez le conteneur sandbox OpenHands.
|
||||
docker exec [ID CONTENEUR] curl http://host.docker.internal:11434/api/generate -d '{"model":"[NOM]","prompt":"hi"}'
|
||||
#ex. docker exec cd9cc82f7a11 curl http://host.docker.internal:11434/api/generate -d '{"model":"codellama","prompt":"hi"}'
|
||||
```
|
||||
|
||||
## Résoudre le problème
|
||||
|
||||
Maintenant, faisons en sorte que cela fonctionne. Modifiez /etc/systemd/system/ollama.service avec des privilèges sudo. (Le chemin peut varier selon la distribution Linux)
|
||||
|
||||
```bash
|
||||
sudo vi /etc/systemd/system/ollama.service
|
||||
```
|
||||
|
||||
ou
|
||||
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/ollama.service
|
||||
```
|
||||
|
||||
Dans le bloc [Service], ajoutez ces lignes
|
||||
|
||||
```
|
||||
Environment="OLLAMA_HOST=0.0.0.0:11434"
|
||||
Environment="OLLAMA_ORIGINS=*"
|
||||
```
|
||||
|
||||
Ensuite, sauvegardez, rechargez la configuration et redémarrez le service.
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart ollama
|
||||
```
|
||||
|
||||
Enfin, testez qu'ollama est accessible depuis le conteneur
|
||||
|
||||
```bash
|
||||
ollama list # obtenir la liste des modèles installés
|
||||
docker ps # obtenir la liste des conteneurs docker en cours d'exécution, pour un test plus précis choisissez le conteneur sandbox OpenHands.
|
||||
docker exec [ID CONTENEUR] curl http://host.docker.internal:11434/api/generate -d '{"model":"[NOM]","prompt":"hi"}'
|
||||
```
|
||||
|
||||
|
||||
# LLM local avec LM Studio
|
||||
|
||||
Étapes pour configurer LM Studio :
|
||||
1. Ouvrez LM Studio
|
||||
2. Allez dans l'onglet Serveur local.
|
||||
3. Cliquez sur le bouton "Démarrer le serveur".
|
||||
4. Sélectionnez le modèle que vous souhaitez utiliser dans la liste déroulante.
|
||||
|
||||
|
||||
Définissez les configurations suivantes :
|
||||
```bash
|
||||
LLM_MODEL="openai/lmstudio"
|
||||
LLM_BASE_URL="http://localhost:1234/v1"
|
||||
CUSTOM_LLM_PROVIDER="openai"
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker run # ...
|
||||
-e LLM_MODEL="openai/lmstudio" \
|
||||
-e LLM_BASE_URL="http://host.docker.internal:1234/v1" \
|
||||
-e CUSTOM_LLM_PROVIDER="openai" \
|
||||
# ...
|
||||
```
|
||||
|
||||
Vous devriez maintenant pouvoir vous connecter à `http://localhost:3000/`
|
||||
|
||||
Dans l'environnement de développement, vous pouvez définir les configurations suivantes dans le fichier `config.toml` :
|
||||
|
||||
```
|
||||
[core]
|
||||
workspace_base="./workspace"
|
||||
|
||||
[llm]
|
||||
model="openai/lmstudio"
|
||||
base_url="http://localhost:1234/v1"
|
||||
custom_llm_provider="openai"
|
||||
```
|
||||
|
||||
Terminé ! Vous pouvez maintenant démarrer OpenHands avec : `make run` sans Docker. Vous devriez maintenant pouvoir vous connecter à `http://localhost:3000/`
|
||||
|
||||
# Note
|
||||
|
||||
Pour WSL, exécutez les commandes suivantes dans cmd pour configurer le mode réseau en miroir :
|
||||
|
||||
```
|
||||
python -c "print('[wsl2]\nnetworkingMode=mirrored',file=open(r'%UserProfile%\.wslconfig','w'))"
|
||||
wsl --shutdown
|
||||
```
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
# OpenAI
|
||||
|
||||
OpenHands utilise LiteLLM pour effectuer des appels aux modèles de chat d'OpenAI. Vous pouvez trouver leur documentation sur l'utilisation d'OpenAI en tant que fournisseur [ici](https://docs.litellm.ai/docs/providers/openai).
|
||||
|
||||
## Configuration
|
||||
|
||||
Lors de l'exécution d'OpenHands, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* `LLM Provider` à `OpenAI`
|
||||
* `LLM Model` au modèle que vous utiliserez.
|
||||
[Visitez ce lien pour voir une liste complète des modèles OpenAI pris en charge par LiteLLM.](https://docs.litellm.ai/docs/providers/openai#openai-chat-completion-models)
|
||||
Si le modèle ne figure pas dans la liste, activez les `Advanced Options` et entrez-le dans `Custom Model` (par exemple, openai/<model-name> comme `openai/gpt-4o`).
|
||||
* `API Key` à votre clé API OpenAI. Pour trouver ou créer votre clé API de projet OpenAI, [voir ici](https://platform.openai.com/api-keys).
|
||||
|
||||
## Utilisation des endpoints compatibles OpenAI
|
||||
|
||||
Tout comme pour les chat completions OpenAI, nous utilisons LiteLLM pour les endpoints compatibles OpenAI. Vous pouvez trouver leur documentation complète sur ce sujet [ici](https://docs.litellm.ai/docs/providers/openai_compatible).
|
||||
|
||||
## Utilisation d'un proxy OpenAI
|
||||
|
||||
Si vous utilisez un proxy OpenAI, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* Activer les `Advanced Options`
|
||||
* `Custom Model` à openai/<model-name> (par exemple, `openai/gpt-4o` ou openai/<proxy-prefix>/<model-name>)
|
||||
* `Base URL` à l'URL de votre proxy OpenAI
|
||||
* `API Key` à votre clé API OpenAI
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
# OpenRouter
|
||||
|
||||
OpenHands utilise LiteLLM pour effectuer des appels aux modèles de chat sur OpenRouter. Vous pouvez trouver leur documentation sur l'utilisation d'OpenRouter en tant que fournisseur [ici](https://docs.litellm.ai/docs/providers/openrouter).
|
||||
|
||||
## Configuration
|
||||
|
||||
Lors de l'exécution d'OpenHands, vous devrez définir les éléments suivants dans l'interface utilisateur d'OpenHands via les paramètres :
|
||||
* `LLM Provider` à `OpenRouter`
|
||||
* `LLM Model` au modèle que vous utiliserez.
|
||||
[Visitez ici pour voir une liste complète des modèles OpenRouter](https://openrouter.ai/models).
|
||||
Si le modèle ne figure pas dans la liste, activez `Advanced Options`, et entrez-le dans `Custom Model` (par exemple openrouter/<model-name> comme `openrouter/anthropic/claude-3.5-sonnet`).
|
||||
* `API Key` à votre clé API OpenRouter.
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
# Meilleures pratiques pour les prompts
|
||||
|
||||
Lorsque vous travaillez avec le développeur de logiciels OpenHands AI, il est crucial de fournir des prompts clairs et efficaces. Ce guide décrit les meilleures pratiques pour créer des prompts qui produiront les réponses les plus précises et utiles.
|
||||
|
||||
## Caractéristiques des bons prompts
|
||||
|
||||
Les bons prompts sont :
|
||||
|
||||
1. **Concrets** : Ils expliquent exactement quelle fonctionnalité doit être ajoutée ou quelle erreur doit être corrigée.
|
||||
2. **Spécifiques à l'emplacement** : Si connu, ils expliquent les emplacements dans la base de code qui doivent être modifiés.
|
||||
3. **Correctement dimensionnés** : Ils doivent avoir la taille d'une seule fonctionnalité, ne dépassant généralement pas 100 lignes de code.
|
||||
|
||||
## Exemples
|
||||
|
||||
### Exemples de bons prompts
|
||||
|
||||
1. "Ajoutez une fonction `calculate_average` dans `utils/math_operations.py` qui prend une liste de nombres en entrée et renvoie leur moyenne."
|
||||
|
||||
2. "Corrigez le TypeError dans `frontend/src/components/UserProfile.tsx` se produisant à la ligne 42. L'erreur suggère que nous essayons d'accéder à une propriété de undefined."
|
||||
|
||||
3. "Implémentez la validation des entrées pour le champ email dans le formulaire d'inscription. Mettez à jour `frontend/src/components/RegistrationForm.tsx` pour vérifier si l'email est dans un format valide avant la soumission."
|
||||
|
||||
### Exemples de mauvais prompts
|
||||
|
||||
1. "Améliorez le code." (Trop vague, pas concret)
|
||||
|
||||
2. "Réécrivez tout le backend pour utiliser un framework différent." (Pas correctement dimensionné)
|
||||
|
||||
3. "Il y a un bug quelque part dans l'authentification des utilisateurs. Pouvez-vous le trouver et le corriger ?" (Manque de spécificité et d'informations de localisation)
|
||||
|
||||
## Conseils pour des prompts efficaces
|
||||
|
||||
1. Soyez aussi précis que possible sur le résultat souhaité ou le problème à résoudre.
|
||||
2. Fournissez du contexte, y compris les chemins de fichiers et les numéros de ligne pertinents si disponibles.
|
||||
3. Décomposez les grandes tâches en prompts plus petits et gérables.
|
||||
4. Incluez tous les messages d'erreur ou logs pertinents.
|
||||
5. Spécifiez le langage de programmation ou le framework s'il n'est pas évident d'après le contexte.
|
||||
|
||||
N'oubliez pas, plus votre prompt est précis et informatif, mieux l'IA pourra vous aider à développer ou à modifier le logiciel OpenHands.
|
||||
|
||||
Voir [Getting Started with OpenHands](./getting-started) pour plus d'exemples de prompts utiles.
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
# Personnalisation du comportement de l'agent
|
||||
|
||||
OpenHands peut être personnalisé pour fonctionner plus efficacement avec des dépôts spécifiques en fournissant un contexte et des directives propres à chaque dépôt. Cette section explique comment optimiser OpenHands pour votre projet.
|
||||
|
||||
## Configuration du dépôt
|
||||
|
||||
Vous pouvez personnaliser le comportement d'OpenHands pour votre dépôt en créant un répertoire `.openhands` à la racine de votre dépôt. Au minimum, il doit contenir le fichier `.openhands/microagents/repo.md`, qui comprend les instructions qui seront données à l'agent chaque fois qu'il travaillera avec ce dépôt.
|
||||
|
||||
Nous vous suggérons d'inclure les informations suivantes :
|
||||
1. **Aperçu du dépôt** : Une brève description de l'objectif et de l'architecture de votre projet
|
||||
2. **Structure des répertoires** : Les répertoires clés et leurs objectifs
|
||||
3. **Directives de développement** : Les normes et pratiques de codage spécifiques au projet
|
||||
4. **Exigences de test** : Comment exécuter les tests et quels types de tests sont requis
|
||||
5. **Instructions de configuration** : Les étapes nécessaires pour construire et exécuter le projet
|
||||
|
||||
### Exemple de configuration de dépôt
|
||||
Exemple de fichier `.openhands/microagents/repo.md` :
|
||||
```
|
||||
Repository: MonProjet
|
||||
Description: Une application web pour la gestion des tâches
|
||||
|
||||
Structure des répertoires :
|
||||
- src/ : Code principal de l'application
|
||||
- tests/ : Fichiers de test
|
||||
- docs/ : Documentation
|
||||
|
||||
Configuration :
|
||||
- Exécutez `npm install` pour installer les dépendances
|
||||
- Utilisez `npm run dev` pour le développement
|
||||
- Exécutez `npm test` pour les tests
|
||||
|
||||
Directives :
|
||||
- Suivez la configuration ESLint
|
||||
- Écrivez des tests pour toutes les nouvelles fonctionnalités
|
||||
- Utilisez TypeScript pour le nouveau code
|
||||
```
|
||||
|
||||
### Personnalisation des prompts
|
||||
|
||||
Lorsque vous travaillez avec un dépôt personnalisé :
|
||||
|
||||
1. **Référencez les normes du projet** : Mentionnez les normes ou les modèles de codage spécifiques utilisés dans votre projet
|
||||
2. **Incluez le contexte** : Faites référence à la documentation pertinente ou aux implémentations existantes
|
||||
3. **Spécifiez les exigences de test** : Incluez les exigences de test spécifiques au projet dans vos prompts
|
||||
|
||||
Exemple de prompt personnalisé :
|
||||
```
|
||||
Ajoutez une nouvelle fonctionnalité d'achèvement des tâches à src/components/TaskList.tsx en suivant nos modèles de composants existants.
|
||||
Incluez des tests unitaires dans tests/components/ et mettez à jour la documentation dans docs/features/.
|
||||
Le composant doit utiliser notre style partagé de src/styles/components.
|
||||
```
|
||||
|
||||
### Meilleures pratiques pour la personnalisation du dépôt
|
||||
|
||||
1. **Gardez les instructions à jour** : Mettez régulièrement à jour votre répertoire `.openhands` au fur et à mesure de l'évolution de votre projet
|
||||
2. **Soyez spécifique** : Incluez des chemins, des modèles et des exigences spécifiques à votre projet
|
||||
3. **Documentez les dépendances** : Énumérez tous les outils et dépendances nécessaires au développement
|
||||
4. **Incluez des exemples** : Fournissez des exemples de bons modèles de code de votre projet
|
||||
5. **Spécifiez les conventions** : Documentez les conventions de nommage, l'organisation des fichiers et les préférences de style de code
|
||||
|
||||
En personnalisant OpenHands pour votre dépôt, vous obtiendrez des résultats plus précis et cohérents qui s'alignent sur les normes et les exigences de votre projet.
|
||||
|
||||
## Autres microagents
|
||||
Vous pouvez créer d'autres instructions dans le répertoire `.openhands/microagents/` qui seront envoyées à l'agent si un mot-clé particulier est trouvé, comme `test`, `frontend` ou `migration`. Voir [Microagents](microagents.md) pour plus d'informations.
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
|
||||
# Micro-Agents
|
||||
|
||||
OpenHands utilise des micro-agents spécialisés pour gérer efficacement des tâches et des contextes spécifiques. Ces micro-agents sont de petits composants ciblés qui fournissent un comportement et des connaissances spécialisés pour des scénarios particuliers.
|
||||
|
||||
## Aperçu
|
||||
|
||||
Les micro-agents sont définis dans des fichiers markdown sous le répertoire `openhands/agenthub/codeact_agent/micro/`. Chaque micro-agent est configuré avec :
|
||||
|
||||
- Un nom unique
|
||||
- Le type d'agent (généralement CodeActAgent)
|
||||
- Des mots-clés déclencheurs qui activent l'agent
|
||||
- Des instructions et des capacités spécifiques
|
||||
|
||||
## Micro-Agents Disponibles
|
||||
|
||||
### Agent GitHub
|
||||
**Fichier** : `github.md`
|
||||
**Déclencheurs** : `github`, `git`
|
||||
|
||||
L'agent GitHub se spécialise dans les interactions avec l'API GitHub et la gestion des dépôts. Il :
|
||||
- A accès à un `GITHUB_TOKEN` pour l'authentification API
|
||||
- Suit des directives strictes pour les interactions avec les dépôts
|
||||
- Gère les branches et les pull requests
|
||||
- Utilise l'API GitHub au lieu des interactions avec le navigateur web
|
||||
|
||||
Fonctionnalités clés :
|
||||
- Protection des branches (empêche les push directs vers main/master)
|
||||
- Création automatisée de PR
|
||||
- Gestion de la configuration Git
|
||||
- Approche API-first pour les opérations GitHub
|
||||
|
||||
### Agent NPM
|
||||
**Fichier** : `npm.md`
|
||||
**Déclencheurs** : `npm`
|
||||
|
||||
Se spécialise dans la gestion des packages npm avec un focus spécifique sur :
|
||||
- Les opérations shell non interactives
|
||||
- La gestion automatisée des confirmations en utilisant la commande Unix 'yes'
|
||||
- L'automatisation de l'installation des packages
|
||||
|
||||
### Micro-Agents Personnalisés
|
||||
|
||||
Vous pouvez créer vos propres micro-agents en ajoutant de nouveaux fichiers markdown dans le répertoire des micro-agents. Chaque fichier doit suivre cette structure :
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: nom_de_l_agent
|
||||
agent: CodeActAgent
|
||||
triggers:
|
||||
- mot_declencheur1
|
||||
- mot_declencheur2
|
||||
---
|
||||
|
||||
Instructions et capacités pour le micro-agent...
|
||||
```
|
||||
|
||||
## Bonnes Pratiques
|
||||
|
||||
Lorsque vous travaillez avec des micro-agents :
|
||||
|
||||
1. **Utilisez les déclencheurs appropriés** : Assurez-vous que vos commandes incluent les mots-clés déclencheurs pertinents pour activer le bon micro-agent
|
||||
2. **Suivez les directives de l'agent** : Chaque agent a des instructions et des limitations spécifiques - respectez-les pour des résultats optimaux
|
||||
3. **Approche API-First** : Lorsque c'est possible, utilisez les endpoints d'API plutôt que les interfaces web
|
||||
4. **Automatisation conviviale** : Concevez des commandes qui fonctionnent bien dans des environnements non interactifs
|
||||
|
||||
## Intégration
|
||||
|
||||
Les micro-agents sont automatiquement intégrés dans le workflow d'OpenHands. Ils :
|
||||
- Surveillent les commandes entrantes pour détecter leurs mots-clés déclencheurs
|
||||
- S'activent lorsque des déclencheurs pertinents sont détectés
|
||||
- Appliquent leurs connaissances et capacités spécialisées
|
||||
- Suivent leurs directives et restrictions spécifiques
|
||||
|
||||
## Exemple d'utilisation
|
||||
|
||||
```bash
|
||||
# Exemple d'agent GitHub
|
||||
git checkout -b feature-branch
|
||||
git commit -m "Add new feature"
|
||||
git push origin feature-branch
|
||||
|
||||
# Exemple d'agent NPM
|
||||
yes | npm install package-name
|
||||
```
|
||||
|
||||
Pour plus d'informations sur des agents spécifiques, reportez-vous à leurs fichiers de documentation individuels dans le répertoire des micro-agents.
|
||||
|
||||
## Contribuer un Micro-Agent
|
||||
|
||||
Pour contribuer un nouveau micro-agent à OpenHands, suivez ces directives :
|
||||
|
||||
### 1. Planification de votre Micro-Agent
|
||||
|
||||
Avant de créer un micro-agent, considérez :
|
||||
- Quel problème ou cas d'utilisation spécifique va-t-il adresser ?
|
||||
- Quelles capacités ou connaissances uniques devrait-il avoir ?
|
||||
- Quels mots-clés déclencheurs ont du sens pour l'activer ?
|
||||
- Quelles contraintes ou directives devrait-il suivre ?
|
||||
|
||||
### 2. Structure du fichier
|
||||
|
||||
Créez un nouveau fichier markdown dans `openhands/agenthub/codeact_agent/micro/` avec un nom descriptif (par ex., `docker.md` pour un agent axé sur Docker).
|
||||
|
||||
### 3. Composants requis
|
||||
|
||||
Votre fichier de micro-agent doit inclure :
|
||||
|
||||
1. **Front Matter** : Métadonnées YAML au début du fichier :
|
||||
```markdown
|
||||
---
|
||||
name: nom_de_votre_agent
|
||||
agent: CodeActAgent
|
||||
triggers:
|
||||
- mot_declencheur1
|
||||
- mot_declencheur2
|
||||
---
|
||||
```
|
||||
|
||||
2. **Instructions** : Directives claires et spécifiques pour le comportement de l'agent :
|
||||
```markdown
|
||||
Vous êtes responsable de [tâche/domaine spécifique].
|
||||
|
||||
Responsabilités clés :
|
||||
1. [Responsabilité 1]
|
||||
2. [Responsabilité 2]
|
||||
|
||||
Directives :
|
||||
- [Directive 1]
|
||||
- [Directive 2]
|
||||
|
||||
Exemples d'utilisation :
|
||||
[Exemple 1]
|
||||
[Exemple 2]
|
||||
```
|
||||
|
||||
### 4. Bonnes pratiques pour le développement de Micro-Agents
|
||||
|
||||
1. **Portée claire** : Gardez l'agent concentré sur un domaine ou une tâche spécifique
|
||||
2. **Instructions explicites** : Fournissez des directives claires et sans ambiguïté
|
||||
3. **Exemples utiles** : Incluez des exemples pratiques de cas d'utilisation courants
|
||||
4. **Sécurité d'abord** : Incluez les avertissements et contraintes nécessaires
|
||||
5. **Conscience de l'intégration** : Considérez comment l'agent interagit avec les autres composants
|
||||
|
||||
### 5. Tester votre Micro-Agent
|
||||
|
||||
Avant de soumettre :
|
||||
1. Testez l'agent avec divers prompts
|
||||
2. Vérifiez que les mots-clés déclencheurs activent correctement l'agent
|
||||
3. Assurez-vous que les instructions sont claires et complètes
|
||||
4. Vérifiez les conflits potentiels avec les agents existants
|
||||
|
||||
### 6. Exemple d'implémentation
|
||||
|
||||
Voici un modèle pour un nouveau micro-agent :
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: docker
|
||||
agent: CodeActAgent
|
||||
triggers:
|
||||
- docker
|
||||
- conteneur
|
||||
---
|
||||
|
||||
Vous êtes responsable de la gestion des conteneurs Docker et de la création de Dockerfiles.
|
||||
|
||||
Responsabilités clés :
|
||||
1. Créer et modifier des Dockerfiles
|
||||
2. Gérer le cycle de vie des conteneurs
|
||||
3. Gérer les configurations Docker Compose
|
||||
|
||||
Directives :
|
||||
- Utilisez toujours des images de base officielles lorsque possible
|
||||
- Incluez les considérations de sécurité nécessaires
|
||||
- Suivez les bonnes pratiques Docker pour l'optimisation des couches
|
||||
|
||||
Exemples :
|
||||
1. Créer un Dockerfile :
|
||||
```dockerfile
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
CMD ["npm", "start"]
|
||||
```
|
||||
|
||||
2. Utilisation de Docker Compose :
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
```
|
||||
|
||||
N'oubliez pas de :
|
||||
- Valider la syntaxe du Dockerfile
|
||||
- Vérifier les vulnérabilités de sécurité
|
||||
- Optimiser le temps de build et la taille de l'image
|
||||
```
|
||||
|
||||
### 7. Processus de soumission
|
||||
|
||||
1. Créez votre fichier de micro-agent dans le bon répertoire
|
||||
2. Testez minutieusement
|
||||
3. Soumettez une pull request avec :
|
||||
- Le nouveau fichier de micro-agent
|
||||
- La documentation mise à jour si nécessaire
|
||||
- La description du but et des capacités de l'agent
|
||||
|
||||
N'oubliez pas que les micro-agents sont un moyen puissant d'étendre les capacités d'OpenHands dans des domaines spécifiques. Des agents bien conçus peuvent améliorer significativement la capacité du système à gérer des tâches spécialisées.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user