Compare commits

..

148 Commits

Author SHA1 Message Date
openhands
8d89fada9c fix: Use runtime to load microagents
- Update CodeActAgent to use runtime.get_microagents_from_selected_repo
- Add test to verify microagent loading from runtime
- Fix #6304
2025-01-16 14:01:43 +00:00
dependabot[bot]
6e089619e0 chore(deps-dev): bump chromadb from 0.6.2 to 0.6.3 in the chromadb group (#6289)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-16 00:37:42 +01:00
Xingyao Wang
179a89a211 Fix microagent loading with trailing slashes and nested directories (#6239)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-15 17:07:40 +00:00
tofarr
8795ee6c6e Fix closing sessions (#6114) 2025-01-15 10:04:22 -07:00
Engel Nyst
97e938d545 Fix French doc (#6283) 2025-01-15 04:25:47 +00:00
Engel Nyst
b9a70c8d5c Delegation fixes (#6165) 2025-01-15 03:24:39 +00:00
Ray Myers
082d0b25c5 Send status message on runtime restart (#6275) 2025-01-15 03:21:06 +01:00
Engel Nyst
c5797d1d5a Fix llm_config fallback (#4415)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-15 01:17:37 +00:00
Xingyao Wang
7ce1fb85ff chore: remove repo info from initial query for #6057 (#6279) 2025-01-15 00:40:54 +00:00
Robert Brennan
fa6792e5a6 Add GitHub repository information to system prompt (#6057)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-15 08:02:07 +08:00
dependabot[bot]
3d9b4c4af6 chore(deps): bump the version-all group with 4 updates (#6267)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 21:30:56 +01:00
tofarr
e21cbf67ee Feat: User id should be a str (Because it will probably be a UUID) (#6251) 2025-01-14 12:39:51 -07:00
Xingyao Wang
6b2e3f938f fix: prevent runtime size deselection (#6119)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: mamoodi <mamoodiha@gmail.com>
2025-01-14 17:53:51 +00:00
Rohit Malhotra
580d7b938c Fix: Don't refresh github token on local (#5880) 2025-01-14 17:48:33 +00:00
mamoodi
28178a2940 Remove extra optional for github token (#6270) 2025-01-14 17:44:28 +00:00
sp.wack
04382b2b19 hotfix(backend): Remove GH header token middleware (#6269) 2025-01-14 12:07:13 -05:00
Robert Brennan
4da812c781 Better handling of stack traces and exc_info (#6253) 2025-01-14 10:22:39 -05:00
mamoodi
37b7173481 Update landing page examples (#6254) 2025-01-14 15:09:30 +00:00
Graham Neubig
f0ebf3eba8 Improve i18n support and add missing translations (#6070)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-14 13:46:22 +00:00
Boxuan Li
92b8d55c2d Rename trajectories_path config to save_trajectory_path (#6216)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-14 04:32:45 +00:00
dependabot[bot]
a125b6cd43 chore(deps): bump the version-all group across 1 directory with 6 updates (#6248)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 23:33:09 +01:00
tofarr
01ac207b92 Fix remove dead code (#6249) 2025-01-13 14:15:13 -07:00
Ray Myers
6d015a5dca Don't start conversation runtime without valid API key (#6181) 2025-01-13 22:03:37 +01:00
dependabot[bot]
275512305d chore(deps): bump docker/setup-qemu-action from 3.2.0 to 3.3.0 (#6229)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 21:31:01 +01:00
mamoodi
3a4bc10b29 Release 0.20.0 (#6234)
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2025-01-13 14:58:20 -05:00
sp.wack
bbd31b32f3 chore: Move GH requests to the server (#6217) 2025-01-13 23:12:50 +04:00
Joseph O'Connor
295c6fd629 fix(issue_definitions, issue-success-check.jinja): pass git-patch to issue-success-check (#6243)
Co-authored-by: Graham Neubig <neubig@gmail.com>
2025-01-13 18:40:15 +00:00
tofarr
5a809c9b53 Feature: User id propagation (#6233) 2025-01-13 18:10:45 +00:00
sp.wack
0b74fd71d9 fix(frontend): Prevent from send a SET API key (#6235) 2025-01-13 17:50:37 +00:00
tofarr
4fa5c329d6 Fix : minor updates to log messages (#6232) 2025-01-13 17:19:51 +00:00
tofarr
5b1dcf83a6 Fix for issue where S3FileStore does not delete directory objects (#6231) 2025-01-13 16:50:58 +00:00
tofarr
b9884f7609 Fixes for minor cases where FDs were not closed (#6228) 2025-01-13 09:15:23 -07:00
dependabot[bot]
99eda0e571 chore(deps-dev): bump the eslint group in /frontend with 2 updates (#6227)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 19:44:10 +04:00
Ryan H. Tran
5832463088 Revert openhands-resolver.yml change in #5972 (#6222) 2025-01-13 15:39:54 +00:00
tofarr
045ec2b95d Fix: Timezone should be UTC (#6225) 2025-01-13 08:24:26 -07:00
tofarr
23473070b9 Revert "Config objects as Pydantic BaseModels (#6176)" (#6214) 2025-01-13 07:36:25 -07:00
mamoodi
63133c0ba9 Document changes for Micro-Agents and some formatting changes (#6155)
Co-authored-by: Robert Brennan <accounts@rbren.io>
2025-01-13 09:24:10 -05:00
dependabot[bot]
2023fb767f chore(deps): bump the version-all group in /frontend with 2 updates (#6192)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2025-01-13 11:09:11 +00:00
Ryan H. Tran
23f40a1c01 Enable runtime image build for resolver's experimental feature (#5972) 2025-01-12 17:21:34 -05:00
Calvin Smith
873dddb4e8 Config objects as Pydantic BaseModels (#6176)
Co-authored-by: Calvin Smith <calvin@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
2025-01-12 15:09:45 -05:00
Ryan H. Tran
fe50cd1f9f Upgrade openhands-aci to 0.1.8 (#6123) 2025-01-12 07:26:57 +01:00
Boxuan Li
516e2da520 Custom runtime builder: fix NoEmptyContinuation error (#6211) 2025-01-11 15:58:08 -08:00
jmtatsch
1dd6f544bc Fix #6056 (#6203) 2025-01-11 23:32:12 +01:00
Graham Neubig
40c52feb5b fix: Handle empty lines in patch parser (#6208)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-12 06:43:08 +09:00
Xingyao Wang
f31ccad48b feat: misc bash improvements, set max value for action-exec timeout, retry on requests.ConnectionError (#6175) 2025-01-11 04:36:12 +08:00
Xingyao Wang
828d169b82 refactor: consolidate runtime startup command into an util function (#6199) 2025-01-11 04:27:13 +08:00
dependabot[bot]
a622d27016 chore(deps-dev): bump llama-index-embeddings-huggingface from 0.4.0 to 0.5.0 in the llama group (#6194)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 19:59:54 +01:00
sp.wack
5507b131fe hotfix(frontend): Add beta tag to new app tab (#6198) 2025-01-10 17:31:42 +00:00
sp.wack
0f102e4c71 hotfix(frontend): Get bottom right conversation card details even when multi convo is disabled (#6197) 2025-01-10 17:19:37 +00:00
sp.wack
157a1a24f6 fix(frontend): Wait for fetched settings instead of loading default ones (#6193) 2025-01-10 16:54:31 +00:00
dependabot[bot]
fcfbcb64d4 chore(deps): bump the version-all group in /frontend with 5 updates (#6170)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
2025-01-10 13:51:03 +00:00
Xingyao Wang
931792e87a fix: UI terminal output incorrect newline (#6182) 2025-01-10 17:11:06 +04:00
Robert Brennan
ee701eacc2 fix: prevent race condition in session manager during disconnect (#6053)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-09 17:26:53 -07:00
Ray Myers
8907fed78e Provide a clearer error message when settings are missing midsession (#6158) 2025-01-09 19:09:34 +00:00
Robert Brennan
3cc20a2576 remove timeouts on remote runtime (#6171) 2025-01-09 12:39:40 -05:00
dependabot[bot]
01cf0d433c chore(deps): bump the version-all group with 5 updates (#6169)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-09 17:45:16 +01:00
sp.wack
f6bed82ae2 Add port mappings support (#5577)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: tofarr <tofarr@gmail.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Robert Brennan <contact@rbren.io>
2025-01-09 15:02:56 +00:00
sp.wack
3eae2e2aca chore(frontend): Optimize requests made to the backend (#6168) 2025-01-09 15:00:26 +00:00
sp.wack
b45fc522c7 feat(frontend): Display current conversation info in the bottom right (#6143) 2025-01-09 14:55:33 +00:00
sp.wack
0d409c8c24 fix(frontend): Prevent saving empty custom model (#6149) 2025-01-09 13:43:39 +00:00
Graham Neubig
5458ebbd7d Fix issue #6048: Update documentation of recommended models and add deepseek (#6050)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-09 02:39:53 +00:00
Robert Brennan
c411a29db4 Move GitHub Token export to backend (#6153)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-08 21:12:46 +00:00
ross
386e04a2ba Fix field deprecation in runloop runtime client (#6152) 2025-01-08 15:18:24 -05:00
sp.wack
62c4bab6ba hotfix(frontend): Prevent a redirect when clicking edit (#6151) 2025-01-08 19:53:24 +00:00
sp.wack
e308b6fb6f chore(backend): Update default conversation title logic (#6138) 2025-01-08 22:30:29 +04:00
Engel Nyst
27a660fb6b Make runtime logs optional (#6141) 2025-01-08 19:20:46 +01:00
sp.wack
27d761a1fe chore(frontend): Improve conversation card (#6121) 2025-01-08 21:57:57 +04:00
Robert Brennan
8028e2c2dd fix: handle binary data in GoogleCloudFileStore.write (#6145)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-08 17:36:34 +00:00
dependabot[bot]
ff9058e28a chore(deps): bump the version-all group with 6 updates (#6146)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 16:03:04 +00:00
dependabot[bot]
c45caaef1f chore(deps): bump the version-all group in /frontend with 8 updates (#6144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 15:40:43 +00:00
dependabot[bot]
a3c107daa4 chore(deps): bump the docusaurus group in /docs with 7 updates (#6142)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-08 19:27:37 +04:00
Boxuan Li
040839bdd1 Support custom base container image in openhands-app container (#6039) 2025-01-07 21:28:37 -08:00
Engel Nyst
aabbbb6c6a Fix duplicate state initialization (#6089) 2025-01-07 23:22:43 +01:00
mamoodi
9747c9e9f8 Some changes to microagents docs and new micro-agents section (#6020) 2025-01-07 16:21:12 -05:00
mamoodi
bb85542aca Release 0.19.0 (#6129) 2025-01-07 16:14:22 -05:00
Calvin Smith
6e4ff56934 feature: Condenser Interface and Defaults (#5306)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Calvin Smith <calvin@all-hands.dev>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-08 04:36:30 +08:00
Ray Myers
561f308401 Display connection rejection errors passed to client (#6101)
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2025-01-08 00:26:05 +04:00
Robert Brennan
3733c646af Fix file uploads (#6126) 2025-01-07 19:19:24 +00:00
sp.wack
cf0f6e5e38 Improve conversation panel (#6087) 2025-01-07 17:51:03 +00:00
Xingyao Wang
77aa843d53 feat: support running docker runtime stresstest in CI (#6100)
Co-authored-by: Boxuan Li <liboxuan@connect.hku.hk>
2025-01-07 16:55:21 +00:00
Robert Brennan
affbc49b08 fix for clone repo (#6116) 2025-01-07 16:42:41 +00:00
sp.wack
9016b9c434 chore(frontend): Fix "confirm delete conversation" modal button colors (#6118) 2025-01-07 16:42:06 +00:00
Robert Brennan
e3a96097ba Remove leaked exception (#6086)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-07 16:15:47 +00:00
dependabot[bot]
6a41a3cb4f chore(deps-dev): bump @tanstack/eslint-plugin-query from 5.62.15 to 5.62.16 in /frontend in the eslint group (#6112)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 20:06:22 +04:00
sp.wack
d1555e093c chore(frontend): Close conversation card context menu when clicking elsewhere (#6111) 2025-01-07 19:46:03 +04:00
dependabot[bot]
5469d5311d chore(deps): bump the version-all group across 1 directory with 11 updates (#6110)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:28:08 +01:00
tofarr
eaf4c610b2 Fix for delete conversation (#6097) 2025-01-07 08:25:45 -07:00
Mark Watson
aad7a612c1 fix(frontend): prevent repository name overflow in project menu card (#6091)
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2025-01-07 14:48:06 +00:00
OpenHands
23425c85aa Fix issue #6063: [Bug]: Build error on opencv-python (#6064) 2025-01-07 14:49:59 +09:00
Boxuan Li
fb53ae43c0 Add a stress test for eventstream runtime (#6038)
Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
2025-01-06 22:36:59 +00:00
Graham Neubig
1f8a0180d3 Add runtime size configuration feature (#5805)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
2025-01-07 06:22:58 +08:00
Robert Brennan
8cfcdd7ba3 Add close method to EventStream (#6093)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: tofarr <tofarr@gmail.com>
2025-01-06 21:59:42 +00:00
tofarr
9515ac5e62 Feat - browser client can now close sessions. (#6088) 2025-01-06 14:26:48 -07:00
Xingyao Wang
cebd391b7a fix: better handle bashlex error (#6090) 2025-01-06 20:45:59 +00:00
Robert Brennan
343b86429e Retrieve GitHub IDs more efficiently (#6074)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-06 19:22:52 +00:00
sp.wack
09734467c0 fix(frontend): Only render loading indicator if events are messages (#6082) 2025-01-06 13:03:44 -05:00
Dmitry Kozlov
17d722f3b3 Update README.md (#6076)
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
2025-01-06 17:31:19 +00:00
tofarr
e310f6b776 Feature - sort conversations by created at (#6079) 2025-01-06 09:07:53 -07:00
dependabot[bot]
5626a22e42 chore(deps-dev): bump @tanstack/eslint-plugin-query from 5.62.9 to 5.62.15 in /frontend in the eslint group (#6077)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 14:49:43 +00:00
tofarr
cde8aad47f Feat multi conversations wiring (#6011)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-06 07:43:11 -07:00
stefand678
efd0267919 docs: Fix inconsistent comments (#6051) 2025-01-06 13:43:53 +09:00
Xingyao Wang
f8735efadf chore: improve error logging for RuntimeError (#6055) 2025-01-05 23:02:42 +00:00
Boxuan Li
00d7395e09 Makefile: Fix poetry version detector (#6058) 2025-01-05 22:43:05 +00:00
மனோஜ்குமார் பழனிச்சாமி
150463e629 feat: Add GPU support (#6042) 2025-01-05 15:28:05 +09:00
Xingyao Wang
b7bbf0f5eb fix(agent controller): missing await (#6040) 2025-01-05 04:57:07 +00:00
f-diao
d2790c8b21 docs: Update the referenced py filename. (#6043) 2025-01-05 04:10:51 +00:00
Engel Nyst
3d2138d9ce Command line args fixes (#5990) 2025-01-05 02:58:26 +00:00
OpenHands
e4cf2eee2d Fix issue #4864: [Bug]: make start-backend results in NotImplementedError: Non-relative patterns are unsupported (#5332)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-05 11:44:23 +09:00
dependabot[bot]
79551e67f6 chore(deps): bump docker/setup-qemu-action from 3.0.0 to 3.2.0 (#5798)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-05 03:43:26 +01:00
Xingyao Wang
f5f988e552 fix(agent controller): state.metrics is missing on exception (#6036) 2025-01-05 01:08:47 +00:00
Talut Salako
0c58f469b4 fix: improve how llm models option (#6026) 2025-01-05 00:25:45 +00:00
Xingyao Wang
56d7dccec9 fix(runtime): replace send_request with _send_action_server_request (#6035) 2025-01-04 23:38:34 +00:00
Graham Neubig
411b63159f fix: Use _send_action_server_request in send_action_for_execution (#5951)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-05 08:13:18 +09:00
OpenHands
5ca0beadfb Fix issue #5995: [Resolver] Resolver's summary suggests UNRESOLVED due to "no human reviewer" (#5996)
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
2025-01-05 05:49:38 +09:00
Xingyao Wang
aaff3dd075 fix(llm): cost metrics calculation for unsupport litellm prefix (#6022) 2025-01-04 18:09:13 +00:00
sai krishna rohith k
ef2053011d feat: Added RateLimitError status on UI and Agent state (#5910) 2025-01-04 12:07:07 -05:00
siu
e6499a68f6 fix(frontend): Prevent message submission during IME composition (#6025) 2025-01-04 10:41:48 +00:00
Ryan H. Tran
33cb1d5f3c chore: upgrade openhands-aci to 0.1.6 (#6023) 2025-01-04 07:53:18 +00:00
Graham Neubig
5bdebac741 Add git patch info to guess_success prompt (#5950)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-04 10:56:50 +09:00
Robert Brennan
510c1644dd Add bytes support to FileStore write operations (#6019)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-03 16:30:25 -07:00
Xingyao Wang
ec70af9412 refactor: Replace pexpect with libtmux in BashSession (#4881)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2025-01-04 05:22:13 +08:00
Robert Brennan
761a574b09 Small style changes to repo picker (#6013)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2025-01-03 20:44:30 +00:00
Robert Brennan
825a9ba893 default to local fs (#6016) 2025-01-03 15:18:52 -05:00
tofarr
a6d392322a Fix conversation sorting and pagination (#6014)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-03 19:35:20 +00:00
Xingyao Wang
1ddf398a81 fix(microagent): remove extra unnecessary check (#6012) 2025-01-04 02:58:17 +08:00
mamoodi
4de6c782cc Add doc style guide and make docs adhere to it (#5983)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-03 12:14:14 -05:00
dependabot[bot]
9fef6f909a chore(deps): bump the version-all group across 1 directory with 5 updates (#6008)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
2025-01-03 16:54:48 +00:00
sp.wack
ff466d0f17 fix(frontend): Prevent rendering loading spinner in chat interface too frequently (#6009) 2025-01-03 16:34:06 +00:00
sp.wack
4c59cff2a3 fix(frontend): Memoize messages (#6006) 2025-01-03 16:12:28 +00:00
dependabot[bot]
fa44bdb390 chore(deps-dev): bump chromadb from 0.6.0 to 0.6.1 in the chromadb group (#6004)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-03 17:12:08 +01:00
Xingyao Wang
dd10f37f66 chore: remove extra debugging print (#6005) 2025-01-03 16:02:48 +00:00
Robert Brennan
3b26678a77 feat(frontend): enhance GitHub repo picker with search and sorting (#5783)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
2025-01-03 19:44:32 +04:00
Xingyao Wang
f14f75b064 feat: runtime improvements for rate-limit and 502/503/404 error (#5975) 2025-01-03 08:36:19 -07:00
Robert Brennan
ef8e04aee3 Update github microagent for draft PRs and not creating new branches/PRs (#5986)
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
2025-01-03 09:29:17 -05:00
Robert Brennan
23df4a09d2 Handle BadRequests in agent controller (#5991)
Co-authored-by: OpenHands Bot <openhands@all-hands.dev>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-03 09:29:01 -05:00
sp.wack
eb93113b7a feat(frontend): Add active status for ws (#5944) 2025-01-03 16:38:03 +04:00
Xingyao Wang
c40b0b9ae1 chore: remove extra debug print (#5994) 2025-01-03 02:57:24 +00:00
Xingyao Wang
61ebec9ff7 feat(eval): better visualization for comparing two swe-bench runs (#5993) 2025-01-03 02:36:51 +00:00
Engel Nyst
c567c11267 Enable/disable function calling by user configuration (#5992)
Co-authored-by: co <yc5@tju.edu.cn>
Co-authored-by: Cheng Yang <93481273+young010101@users.noreply.github.com>
2025-01-03 01:40:49 +01:00
Robert Brennan
e628615094 Revert "feat(config): enable/disable LLM model tools/funcs usage by config" (#5989)
Co-authored-by: tofarr <tofarr@gmail.com>
2025-01-03 00:28:07 +01:00
tofarr
50f821f9b9 Feat conversations CRUDS API (#5775)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-02 16:09:08 -07:00
Xingyao Wang
15e0a50ff4 chore: fix linter error for microagent re-structure (#5987) 2025-01-02 23:02:32 +00:00
dependabot[bot]
e52cdfd70a chore(deps): bump the version-all group with 6 updates (#5973)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-02 23:38:34 +01:00
Xingyao Wang
c1b514e9d3 refactor: restructure microagents system (#5886)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
2025-01-03 07:13:18 +09:00
Robert Brennan
8983d719bd Support microagents in CLI and Headless (#5971)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-02 16:52:45 -05:00
Xingyao Wang
9dd5463e06 Set default value of use_microagents to False to prevent breaking eval (#5976)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-01-03 05:39:17 +08:00
Cheng Yang
d5b2ce18cb Test/improve config loading tests (#5399)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-02 21:32:23 +00:00
Cheng Yang
8d627e52cb feat(config): enable/disable LLM model tools/funcs usage by config (#5576)
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-01-02 21:20:37 +00:00
380 changed files with 19382 additions and 9382 deletions

View File

@@ -36,6 +36,8 @@ jobs:
- 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

View File

@@ -29,6 +29,8 @@ jobs:
- 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

View File

@@ -56,7 +56,7 @@ jobs:
docker-images: false
swap-storage: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.3.0
with:
image: tonistiigi/binfmt:latest
- name: Login to GHCR
@@ -119,7 +119,7 @@ jobs:
docker-images: false
swap-storage: true
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.3.0
with:
image: tonistiigi/binfmt:latest
- name: Login to GHCR

View File

@@ -56,6 +56,7 @@ jobs:
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
@@ -70,7 +71,7 @@ jobs:
env:
SANDBOX_FORCE_REBUILD_RUNTIME: True
run: |
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD CodeActAgent '' $N_PROCESSES '' 'haiku_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)
@@ -88,6 +89,7 @@ jobs:
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
@@ -99,7 +101,7 @@ jobs:
env:
SANDBOX_FORCE_REBUILD_RUNTIME: True
run: |
poetry run ./evaluation/integration_tests/scripts/run_infer.sh llm.eval HEAD CodeActAgent '' $N_PROCESSES '' 'deepseek_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)
@@ -109,11 +111,75 @@ jobs:
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
- 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/* # Only include the actual result directories
tar -czvf ../../../integration_tests_${TIMESTAMP}.tar.gz integration_tests/CodeActAgent/* integration_tests/DelegatorAgent/* # Only include the actual result directories
- name: Upload evaluation results as artifact
uses: actions/upload-artifact@v4
@@ -154,5 +220,11 @@ jobs:
**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 }}
---
Download testing outputs (includes both Haiku and DeepSeek results): [Download](${{ steps.upload_results_artifact.outputs.artifact-url }})

View File

@@ -31,6 +31,8 @@ jobs:
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

View File

@@ -30,6 +30,8 @@ jobs:
- 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

View File

@@ -1,6 +1,7 @@
---
name: repo
agent: CodeAct
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).

View File

@@ -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

View File

@@ -11,11 +11,11 @@ To understand the codebase, please refer to the README in each module:
- [agenthub](./openhands/agenthub/README.md)
- [server](./openhands/server/README.md)
## Setting up your development environment
## 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?
## How Can I Contribute?
There are many ways that you can contribute:
@@ -23,7 +23,7 @@ There are many ways that you can contribute:
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](#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.
## What can I build?
## What Can I Build?
Here are a few ways you can help improve the codebase.
#### UI/UX
@@ -35,7 +35,7 @@ of the application, please open an issue first, or better, join the #frontend ch
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)
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
@@ -63,7 +63,7 @@ At the moment, we have two kinds of tests: [`unit`](./tests/unit) and [`integrat
## Sending Pull Requests to OpenHands
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)
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).
### 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:
@@ -103,7 +103,7 @@ Further, if you see an issue you like, please leave a "thumbs-up" or a comment,
### Making Pull Requests
We're generally happy to consider all [PRs](https://github.com/All-Hands-AI/OpenHands/pulls), with the evaluation process varying based on the type of change:
We're generally happy to consider all pull requests with the evaluation process varying based on the type of change:
#### For Small Improvements

View File

@@ -3,7 +3,7 @@ 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]
* [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!)
@@ -58,7 +58,7 @@ See [our documentation](https://docs.all-hands.dev/modules/usage/llms) for recom
### 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
```
@@ -75,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
```
@@ -93,14 +93,14 @@ poetry run pytest ./tests/unit/test_*.py
```
### 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.18-nikolaik`
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/all-hands-ai/runtime:0.20-nikolaik`
## Develop inside Docker container
@@ -110,7 +110,7 @@ TL;DR
make docker-dev
```
See more details [here](./containers/dev/README.md)
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.

View File

@@ -2,8 +2,8 @@
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 or affecting single user.
@@ -11,10 +11,10 @@ These are the procedures and guidelines on how issues are triaged in this repo b
* **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.

View File

@@ -106,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:"; \
@@ -190,7 +190,7 @@ build-frontend:
# Start backend
start-backend:
@echo "$(YELLOW)Starting backend...$(RESET)"
@poetry run uvicorn openhands.server.listen:app --host $(BACKEND_HOST) --port $(BACKEND_PORT) --reload --reload-exclude "$(shell pwd)/workspace"
@poetry run uvicorn openhands.server.listen:app --host $(BACKEND_HOST) --port $(BACKEND_PORT) --reload --reload-exclude "./workspace"
# Start frontend
start-frontend:

View File

@@ -43,17 +43,17 @@ See the [Installation](https://docs.all-hands.dev/modules/usage/installation) gu
system requirements and more information.
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-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 \
docker.all-hands.dev/all-hands-ai/openhands:0.18
docker.all-hands.dev/all-hands-ai/openhands:0.20
```
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!

View File

@@ -34,7 +34,7 @@ workspace_base = "./workspace"
# 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
#trajectories_path="./trajectories"
#save_trajectory_path="./trajectories"
# File store path
#file_store_path = "/tmp/file_store"
@@ -180,6 +180,12 @@ model = "gpt-4o"
# 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"
@@ -192,6 +198,16 @@ model = "gpt-4o"
# 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 = ""
@@ -204,6 +220,12 @@ model = "gpt-4o"
# LLM config group to use
#llm_config = 'your-llm-config-group'
# Whether to use microagents at all
#use_microagents = true
# List of microagents to disable
#disabled_microagents = []
[agent.RepoExplorerAgent]
# Example: use a cheaper model for RepoExplorerAgent to reduce cost, especially
# useful when an agent doesn't demand high quality but uses a lot of tokens

View File

@@ -71,6 +71,7 @@ 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 ./openhands/agenthub ./openhands/agenthub

View File

@@ -11,7 +11,7 @@ services:
- 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.18-nikolaik}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.20-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
ports:

View File

@@ -1,4 +1,4 @@
#
services:
openhands:
build:
@@ -7,8 +7,8 @@ services:
image: openhands:latest
container_name: openhands-app-${DATE:-}
environment:
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/all-hands-ai/runtime:0.18-nikolaik}
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.all-hands.dev/all-hands-ai/runtime:0.20-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"
@@ -16,6 +16,7 @@ services:
- "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

48
docs/DOC_STYLE_GUIDE.md Normal file
View 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
...
```

View File

@@ -1,5 +1,3 @@
# 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.
@@ -94,7 +92,7 @@ Les options de configuration de base sont définies dans la section `[core]` du
- Description : Désactiver la couleur dans la sortie du terminal
**Trajectoires**
- `trajectories_path`
- `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.
@@ -184,6 +182,10 @@ Les options de configuration LLM (Large Language Model) sont définies dans la s
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`
@@ -368,4 +370,26 @@ Les options de configuration de l'agent sont définies dans les sections `[agent
- `codeact_enable_llm_editor`
- Type : `bool`
- Valeur par défaut : `false`
- Description : Si l'éditeur LLM est activé dans l'espace d'action (foncti
- Description : Si l'éditeur LLM est activé dans l'espace d'action (fonctionne uniquement avec l'appel de fonction)
**Utilisation du micro-agent**
- `use_microagents`
- 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

View File

@@ -52,7 +52,7 @@ LLM_API_KEY="sk_test_12345"
```bash
docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -61,7 +61,7 @@ docker run -it \
-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.18 \
docker.all-hands.dev/all-hands-ai/openhands:0.20 \
python -m openhands.core.cli
```

View File

@@ -46,7 +46,7 @@ LLM_API_KEY="sk_test_12345"
```bash
docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -56,6 +56,6 @@ docker run -it \
-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.18 \
docker.all-hands.dev/all-hands-ai/openhands:0.20 \
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
```

View File

@@ -13,16 +13,16 @@
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.18-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-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.18
docker.all-hands.dev/all-hands-ai/openhands:0.20
```
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).

View File

@@ -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.
:::

View File

@@ -13,7 +13,7 @@ C'est le Runtime par défaut qui est utilisé lorsque vous démarrez OpenHands.
```
docker run # ...
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-v /var/run/docker.sock:/var/run/docker.sock \
# ...
```

View File

@@ -91,7 +91,7 @@
- 描述: 禁用终端输出中的颜色
**轨迹**
- `trajectories_path`
- `save_trajectory_path`
- 类型: `str`
- 默认值: `"./trajectories"`
- 描述: 存储轨迹的路径(可以是文件夹或文件)。如果是文件夹,轨迹将保存在该文件夹中以会话 ID 命名的 .json 文件中。

View File

@@ -50,7 +50,7 @@ LLM_API_KEY="sk_test_12345"
```bash
docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -59,7 +59,7 @@ docker run -it \
-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.18 \
docker.all-hands.dev/all-hands-ai/openhands:0.20 \
python -m openhands.core.cli
```

View File

@@ -47,7 +47,7 @@ LLM_API_KEY="sk_test_12345"
```bash
docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -57,6 +57,6 @@ docker run -it \
-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.18 \
docker.all-hands.dev/all-hands-ai/openhands:0.20 \
python -m openhands.core.main -t "write a bash script that prints hi" --no-auto-continue
```

View File

@@ -11,16 +11,16 @@
在 Docker 中运行 OpenHands 是最简单的方式。
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-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.18
docker.all-hands.dev/all-hands-ai/openhands:0.20
```
你也可以在可脚本化的[无头模式](https://docs.all-hands.dev/modules/usage/how-to/headless-mode)下运行 OpenHands作为[交互式 CLI](https://docs.all-hands.dev/modules/usage/how-to/cli-mode),或使用 [OpenHands GitHub Action](https://docs.all-hands.dev/modules/usage/how-to/github-action)。

View File

@@ -11,7 +11,7 @@
```
docker run # ...
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-v /var/run/docker.sock:/var/run/docker.sock \
# ...
```

View File

@@ -4,10 +4,9 @@
Achieving full replication of production-grade applications with LLMs is a complex endeavor. Our strategy involves:
1. **Core Technical Research:** Focusing on foundational research to understand and improve the technical aspects of code generation and handling
2. **Specialist Abilities:** Enhancing the effectiveness of core components through data curation, training methods, and more
3. **Task Planning:** Developing capabilities for bug detection, codebase management, and optimization
4. **Evaluation:** Establishing comprehensive evaluation metrics to better understand and improve our models
- **Core Technical Research:** Focusing on foundational research to understand and improve the technical aspects of code generation and handling.
- **Task Planning:** Developing capabilities for bug detection, codebase management, and optimization.
- **Evaluation:** Establishing comprehensive evaluation metrics to better understand and improve our agents.
## Default Agent
@@ -15,11 +14,14 @@ Our default Agent is currently the [CodeActAgent](agents), which is capable of g
## Built With
OpenHands is built using a combination of powerful frameworks and libraries, providing a robust foundation for its development. Here are the key technologies used in the project:
OpenHands is built using a combination of powerful frameworks and libraries, providing a robust foundation for its
development. Here are the key technologies used in the project:
![FastAPI](https://img.shields.io/badge/FastAPI-black?style=for-the-badge) ![uvicorn](https://img.shields.io/badge/uvicorn-black?style=for-the-badge) ![LiteLLM](https://img.shields.io/badge/LiteLLM-black?style=for-the-badge) ![Docker](https://img.shields.io/badge/Docker-black?style=for-the-badge) ![Ruff](https://img.shields.io/badge/Ruff-black?style=for-the-badge) ![MyPy](https://img.shields.io/badge/MyPy-black?style=for-the-badge) ![LlamaIndex](https://img.shields.io/badge/LlamaIndex-black?style=for-the-badge) ![React](https://img.shields.io/badge/React-black?style=for-the-badge)
Please note that the selection of these technologies is in progress, and additional technologies may be added or existing ones may be removed as the project evolves. We strive to adopt the most suitable and efficient tools to enhance the capabilities of OpenHands.
Please note that the selection of these technologies is in progress, and additional technologies may be added or
existing ones may be removed as the project evolves. We strive to adopt the most suitable and efficient tools to
enhance the capabilities of OpenHands.
## License

View File

@@ -7,53 +7,11 @@ If you are running in [GUI Mode](https://docs.all-hands.dev/modules/usage/how-to
take precedence.
:::
---
# Table of Contents
1. [Core Configuration](#core-configuration)
- [API Keys](#api-keys)
- [Workspace](#workspace)
- [Debugging and Logging](#debugging-and-logging)
- [Session Management](#session-management)
- [Trajectories](#trajectories)
- [File Store](#file-store)
- [Task Management](#task-management)
- [Sandbox Configuration](#sandbox-configuration)
- [Miscellaneous](#miscellaneous)
2. [LLM Configuration](#llm-configuration)
- [AWS Credentials](#aws-credentials)
- [API Configuration](#api-configuration)
- [Custom LLM Provider](#custom-llm-provider)
- [Embeddings](#embeddings)
- [Message Handling](#message-handling)
- [Model Selection](#model-selection)
- [Retrying](#retrying)
- [Advanced Options](#advanced-options)
3. [Agent Configuration](#agent-configuration)
- [Microagent Configuration](#microagent-configuration)
- [Memory Configuration](#memory-configuration)
- [LLM Configuration](#llm-configuration-2)
- [ActionSpace Configuration](#actionspace-configuration)
- [Microagent Usage](#microagent-usage)
4. [Sandbox Configuration](#sandbox-configuration-2)
- [Execution](#execution)
- [Container Image](#container-image)
- [Networking](#networking)
- [Linting and Plugins](#linting-and-plugins)
- [Dependencies and Environment](#dependencies-and-environment)
- [Evaluation](#evaluation)
5. [Security Configuration](#security-configuration)
- [Confirmation Mode](#confirmation-mode)
- [Security Analyzer](#security-analyzer)
---
## Core Configuration
The core configuration options are defined in the `[core]` section of the `config.toml` file.
**API Keys**
### API Keys
- `e2b_api_key`
- Type: `str`
- Default: `""`
@@ -69,7 +27,7 @@ The core configuration options are defined in the `[core]` section of the `confi
- Default: `""`
- Description: API token secret for Modal
**Workspace**
### Workspace
- `workspace_base`
- Type: `str`
- Default: `"./workspace"`
@@ -80,7 +38,7 @@ The core configuration options are defined in the `[core]` section of the `confi
- Default: `"/tmp/cache"`
- Description: Cache directory path
**Debugging and Logging**
### Debugging and Logging
- `debug`
- Type: `bool`
- Default: `false`
@@ -91,13 +49,13 @@ The core configuration options are defined in the `[core]` section of the `confi
- Default: `false`
- Description: Disable color in terminal output
**Trajectories**
- `trajectories_path`
### Trajectories
- `save_trajectory_path`
- Type: `str`
- Default: `"./trajectories"`
- Description: Path to store trajectories (can be a folder or a file). If it's a folder, the trajectories will be saved in a file named with the session id name and .json extension, in that folder.
**File Store**
### File Store
- `file_store_path`
- Type: `str`
- Default: `"/tmp/file_store"`
@@ -128,7 +86,7 @@ The core configuration options are defined in the `[core]` section of the `confi
- Default: `[".*"]`
- Description: List of allowed file extensions for uploads
**Task Management**
### Task Management
- `max_budget_per_task`
- Type: `float`
- Default: `0.0`
@@ -139,7 +97,7 @@ The core configuration options are defined in the `[core]` section of the `confi
- Default: `100`
- Description: Maximum number of iterations
**Sandbox Configuration**
### Sandbox Configuration
- `workspace_mount_path_in_sandbox`
- Type: `str`
- Default: `"/workspace"`
@@ -155,7 +113,7 @@ The core configuration options are defined in the `[core]` section of the `confi
- Default: `""`
- Description: Path to rewrite the workspace mount path to. You can usually ignore this, it refers to special cases of running inside another container.
**Miscellaneous**
### Miscellaneous
- `run_as_openhands`
- Type: `bool`
- Default: `true`
@@ -182,6 +140,10 @@ The LLM (Large Language Model) configuration options are defined in the `[llm]`
To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LLM_NUM_RETRIES`.
:::note
For development setups, you can also define custom named LLM configurations. See [Custom LLM Configurations](./llms/custom-llm-configs) for details.
:::
**AWS Credentials**
- `aws_access_key_id`
- Type: `str`
@@ -198,7 +160,7 @@ To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LL
- Default: `""`
- Description: AWS secret access key
**API Configuration**
### API Configuration
- `api_key`
- Type: `str`
- Default: `None`
@@ -224,13 +186,13 @@ To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LL
- Default: `0.0`
- Description: Cost per output token
**Custom LLM Provider**
### Custom LLM Provider
- `custom_llm_provider`
- Type: `str`
- Default: `""`
- Description: Custom LLM provider
**Embeddings**
### Embeddings
- `embedding_base_url`
- Type: `str`
- Default: `""`
@@ -246,7 +208,7 @@ To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LL
- Default: `"local"`
- Description: Embedding model to use
**Message Handling**
### Message Handling
- `max_message_chars`
- Type: `int`
- Default: `30000`
@@ -262,13 +224,13 @@ To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LL
- Default: `0`
- Description: Maximum number of output tokens
**Model Selection**
### Model Selection
- `model`
- Type: `str`
- Default: `"claude-3-5-sonnet-20241022"`
- Description: Model to use
**Retrying**
### Retrying
- `num_retries`
- Type: `int`
- Default: `8`
@@ -289,7 +251,7 @@ To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LL
- Default: `2.0`
- Description: Multiplier for exponential backoff calculation
**Advanced Options**
### Advanced Options
- `drop_params`
- Type: `bool`
- Default: `false`
@@ -329,13 +291,13 @@ To use these with the docker command, pass in `-e LLM_<option>`. Example: `-e LL
The agent configuration options are defined in the `[agent]` and `[agent.<agent_name>]` sections of the `config.toml` file.
**Microagent Configuration**
### Microagent Configuration
- `micro_agent_name`
- Type: `str`
- Default: `""`
- Description: Name of the micro agent to use for this agent
**Memory Configuration**
### Memory Configuration
- `memory_enabled`
- Type: `bool`
- Default: `false`
@@ -346,13 +308,13 @@ The agent configuration options are defined in the `[agent]` and `[agent.<agent_
- Default: `3`
- Description: The maximum number of threads indexing at the same time for embeddings
**LLM Configuration**
### LLM Configuration
- `llm_config`
- Type: `str`
- Default: `'your-llm-config-group'`
- Description: The name of the LLM config to use
**ActionSpace Configuration**
### ActionSpace Configuration
- `function_calling`
- Type: `bool`
- Default: `true`
@@ -373,7 +335,7 @@ The agent configuration options are defined in the `[agent]` and `[agent.<agent_
- Default: `false`
- Description: Whether Jupyter is enabled in the action space
**Microagent Usage**
### Microagent Usage
- `use_microagents`
- Type: `bool`
- Default: `true`
@@ -390,7 +352,7 @@ The sandbox configuration options are defined in the `[sandbox]` section of the
To use these with the docker command, pass in `-e SANDBOX_<option>`. Example: `-e SANDBOX_TIMEOUT`.
**Execution**
### Execution
- `timeout`
- Type: `int`
- Default: `120`
@@ -401,19 +363,19 @@ To use these with the docker command, pass in `-e SANDBOX_<option>`. Example: `-
- Default: `1000`
- Description: Sandbox user ID
**Container Image**
### Container Image
- `base_container_image`
- Type: `str`
- Default: `"nikolaik/python-nodejs:python3.12-nodejs22"`
- Description: Container image to use for the sandbox
**Networking**
### Networking
- `use_host_network`
- Type: `bool`
- Default: `false`
- Description: Use host network
**Linting and Plugins**
### Linting and Plugins
- `enable_auto_lint`
- Type: `bool`
- Default: `false`
@@ -424,7 +386,7 @@ To use these with the docker command, pass in `-e SANDBOX_<option>`. Example: `-
- Default: `true`
- Description: Whether to initialize plugins
**Dependencies and Environment**
### Dependencies and Environment
- `runtime_extra_deps`
- Type: `str`
- Default: `""`
@@ -435,7 +397,7 @@ To use these with the docker command, pass in `-e SANDBOX_<option>`. Example: `-
- Default: `{}`
- Description: Environment variables to set at the launch of the runtime
**Evaluation**
### Evaluation
- `browsergym_eval_env`
- Type: `str`
- Default: `""`
@@ -447,13 +409,13 @@ The security configuration options are defined in the `[security]` section of th
To use these with the docker command, pass in `-e SECURITY_<option>`. Example: `-e SECURITY_CONFIRMATION_MODE`.
**Confirmation Mode**
### Confirmation Mode
- `confirmation_mode`
- Type: `bool`
- Default: `false`
- Description: Enable confirmation mode
**Security Analyzer**
### Security Analyzer
- `security_analyzer`
- Type: `str`
- Default: `""`

View File

@@ -1,10 +1,14 @@
# ✅ Providing Feedback
When using OpenHands, you will encounter cases where things work well, and others where they don't. We encourage you to provide feedback when you use OpenHands to help give feedback to the development team, and perhaps more importantly, create an open corpus of coding agent training examples -- Share-OpenHands!
When using OpenHands, you will encounter cases where things work well, and others where they don't. We encourage you to
provide feedback when you use OpenHands to help give feedback to the development team, and perhaps more importantly,
create an open corpus of coding agent training examples -- Share-OpenHands!
## 📝 How to Provide Feedback
Providing feedback is easy! When you are using OpenHands, you can press the thumbs-up or thumbs-down button at any point during your interaction. You will be prompted to provide your email address (e.g. so we can contact you if we want to ask any follow-up questions), and you can choose whether you want to provide feedback publicly or privately.
Providing feedback is easy! When you are using OpenHands, you can press the thumbs-up or thumbs-down button at any point
during your interaction. You will be prompted to provide your email address
(e.g. so we can contact you if we want to ask any follow-up questions), and you can choose whether you want to provide feedback publicly or privately.
<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>
@@ -14,8 +18,11 @@ Providing feedback is easy! When you are using OpenHands, you can press the thum
When you submit data, you can submit it either publicly or privately.
* **Public** data will be distributed under the MIT License, like OpenHands itself, and can be used by the community to train and test models. Obviously, feedback that you can make public will be more valuable for the community as a whole, so when you are not dealing with sensitive information, we would encourage you to choose this option!
* **Private** data will only be shared with the OpenHands team for the purpose of improving OpenHands.
- **Public** data will be distributed under the MIT License, like OpenHands itself, and can be used by the community to
train and test models. Obviously, feedback that you can make public will be more valuable for the community as a whole,
so when you are not dealing with sensitive information, we would encourage you to choose this option!
- **Private** data will be made available to the OpenHands team for the purpose of improving OpenHands.
However, a link with a unique ID will still be created that you can share publicly with others.
### Who collects and stores the data?
@@ -27,13 +34,17 @@ The public data will be released when we hit fixed milestones, such as 1,000 pub
At this time, we will follow the following release process:
1. All people who contributed public feedback will receive an email describing the data release and being given an opportunity to opt out.
2. The person or people in charge of the data release will perform quality control of the data, removing low-quality feedback, removing email submitter email addresses, and attempting to remove any sensitive information.
2. The person or people in charge of the data release will perform quality control of the data, removing low-quality feedback,
removing email submitter email addresses, and attempting to remove any sensitive information.
3. The data will be released publicly under the MIT license through commonly used sites such as github or Hugging Face.
### What if I want my data deleted?
For data on the All Hands AI servers, we are happy to delete it at request:
**One Piece of Data:** If you want one piece of data deleted, we will shortly be adding a mechanism to delete pieces of data using the link and password that is displayed on the interface when you submit data.
**One Piece of Data:** If you want one piece of data deleted, we will shortly be adding a mechanism to delete pieces of
data using the link and password that is displayed on the interface when you submit data.
**All Data:** If you would like all pieces of your data deleted, or you do not have the ID and password that you received when submitting the data, please contact `contact@all-hands.dev` from the email address that you registered when you originally submitted the data.
**All Data:** If you would like all pieces of your data deleted, or you do not have the ID and password that you
received when submitting the data, please contact `contact@all-hands.dev` from the email address that you registered
when you originally submitted the data.

View File

@@ -44,7 +44,7 @@ For example, we might build a TODO app:
We can keep iterating on the app once the skeleton is there:
> Please allow adding an optional due date to every task
> Please allow adding an optional due date to every task.
Just like with normal development, it's good to commit and push your code frequently.
This way you can always revert back to an old state if the agent goes off track.
@@ -59,15 +59,15 @@ OpenHands can also do a great job adding new code to an existing code base.
For example, you can ask OpenHands to add a new GitHub action to your project
which lints your code. OpenHands may take a peek at your codebase to see what language
it should use, but then it can just drop a new file into `./github/workflows/lint.yml`
it should use and then drop a new file into `./github/workflows/lint.yml`.
> Please add a GitHub action that lints the code in this repository
> Please add a GitHub action that lints the code in this repository.
Some tasks might require a bit more context. While OpenHands can use `ls` and `grep`
to search through your codebase, providing context up front allows it to move faster,
and more accurately. And it'll cost you fewer tokens!
> Please modify ./backend/api/routes.js to add a new route that returns a list of all tasks
> Please modify ./backend/api/routes.js to add a new route that returns a list of all tasks.
> Please add a new React component that displays a list of Widgets to the ./frontend/components
> directory. It should use the existing Widget component.
@@ -78,15 +78,15 @@ OpenHands does great at refactoring existing code, especially in small chunks.
You probably don't want to try rearchitecting your whole codebase, but breaking up
long files and functions, renaming variables, etc. tend to work very well.
> Please rename all the single-letter variables in ./app.go
> Please rename all the single-letter variables in ./app.go.
> Please break the function `build_and_deploy_widgets` into two functions, `build_widgets` and `deploy_widgets` in widget.php
> Please break the function `build_and_deploy_widgets` into two functions, `build_widgets` and `deploy_widgets` in widget.php.
> Please break ./api/routes.js into separate files for each route
> Please break ./api/routes.js into separate files for each route.
## Bug Fixes
OpenHands can also help you track down and fix bugs in your code. But, as any
OpenHands can also help you track down and fix bugs in your code. But as any
developer knows, bug fixing can be extremely tricky, and often OpenHands will need more context.
It helps if you've diagnosed the bug, but want OpenHands to figure out the logic.
@@ -94,18 +94,18 @@ It helps if you've diagnosed the bug, but want OpenHands to figure out the logic
> The `search_widgets` function in ./app.py is doing a case-sensitive search. Please make it case-insensitive.
It often helps to do test-driven development when bugfixing with an agent.
It often helps to do test-driven development when bug fixing with an agent.
You can ask the agent to write a new test, and then iterate until it fixes the bug:
> The `hello` function crashes on the empty string. Please write a test that reproduces this bug, then fix the code so it passes.
## More
OpenHands is capable of helping out on just about any coding task. But it takes some practice
OpenHands is capable of helping out on just about any coding task but it takes some practice
to get the most out of it. Remember to:
* Keep your tasks small
* Be as specific as possible
* Provide as much context as possible
* Commit and push frequently
* Keep your tasks small.
* Be as specific as possible.
* Provide as much context as possible.
* Commit and push frequently.
See [Prompting Best Practices](./prompting/prompting-best-practices) for more tips on how to get the most out of OpenHands.

View File

@@ -26,16 +26,16 @@ To run OpenHands in CLI mode with Docker:
1. Set the following environmental variables in your terminal:
* `WORKSPACE_BASE` to the directory you want OpenHands to edit (Ex: `export WORKSPACE_BASE=$(pwd)/workspace`).
* `LLM_MODEL` to the model to use (Ex: `export LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"`).
* `LLM_API_KEY` to the API key (Ex: `export LLM_API_KEY="sk_test_12345"`).
- `WORKSPACE_BASE` to the directory you want OpenHands to edit (Ex: `export WORKSPACE_BASE=$(pwd)/workspace`).
- `LLM_MODEL` to the model to use (Ex: `export LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"`).
- `LLM_API_KEY` to the API key (Ex: `export LLM_API_KEY="sk_test_12345"`).
2. Run the following Docker command:
```bash
docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -45,7 +45,7 @@ docker run -it \
-v ~/.openhands-state:/.openhands-state \
--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.18 \
docker.all-hands.dev/all-hands-ai/openhands:0.20 \
python -m openhands.core.cli
```
@@ -58,7 +58,7 @@ Here are some examples of CLI commands and their expected outputs:
### Example 1: Simple Task
```bash
How can I help? >> Write a Python script that prints "Hello, World!"
>> Write a Python script that prints "Hello, World!"
```
Expected Output:
@@ -72,7 +72,7 @@ Expected Output:
### Example 2: Bash Command
```bash
How can I help? >> Create a directory named "test_dir"
>> Create a directory named "test_dir"
```
Expected Output:
@@ -86,7 +86,7 @@ Expected Output:
### Example 3: Error Handling
```bash
How can I help? >> Delete a non-existent file
>> Delete a non-existent file
```
Expected Output:

View File

@@ -9,8 +9,8 @@ as python and Node.js but may need other software installed by default.
You have two options for customization:
1. Use an existing image with the required software.
2. Create your own custom Docker image.
- Use an existing image with the required software.
- Create your own custom Docker image.
If you choose the first option, you can skip the `Create Your Docker Image` section.
@@ -58,7 +58,3 @@ sandbox_base_container_image="custom-image"
### Run
Run OpenHands by running ```make run``` in the top level directory.
## Technical Explanation
Please refer to [custom docker image section of the runtime documentation](https://docs.all-hands.dev/modules/usage/architecture/runtime#advanced-how-openhands-builds-and-maintains-od-runtime-images) for more details.

View File

@@ -21,10 +21,10 @@ the [README for the OpenHands Resolver](https://github.com/All-Hands-AI/OpenHand
### Iterative resolution
1. Create an issue in the repository.
2. Add the `fix-me` label to the issue, or leave a comment starting with `@openhands-agent`
3. Review the attempt to resolve the issue by checking the pull request
4. Follow up with feedback through general comments, review comments, or inline thread comments
5. Add the `fix-me` label to the pull request, or address a specific comment by starting with `@openhands-agent`
2. Add the `fix-me` label to the issue, or leave a comment starting with `@openhands-agent`.
3. Review the attempt to resolve the issue by checking the pull request.
4. Follow up with feedback through general comments, review comments, or inline thread comments.
5. Add the `fix-me` label to the pull request, or address a specific comment by starting with `@openhands-agent`.
### Label versus Macro

View File

@@ -2,12 +2,12 @@
## Introduction
OpenHands provides a user-friendly Graphical User Interface (GUI) mode for interacting with the AI assistant. This mode offers an intuitive way to set up the environment, manage settings, and communicate with the AI.
OpenHands provides a user-friendly Graphical User Interface (GUI) mode for interacting with the AI assistant.
This mode offers an intuitive way to set up the environment, manage settings, and communicate with the AI.
## Installation and Setup
1. Follow the instructions in the [Installation](../installation) guide to install OpenHands.
2. After running the command, access OpenHands at [http://localhost:3000](http://localhost:3000).
## Interacting with the GUI
@@ -23,39 +23,39 @@ OpenHands provides a user-friendly Graphical User Interface (GUI) mode for inter
OpenHands automatically exports a `GITHUB_TOKEN` to the shell environment if it is available. This can happen in two ways:
1. **Locally (OSS)**: The user directly inputs their GitHub token
2. **Online (SaaS)**: The token is obtained through GitHub OAuth authentication
- **Locally (OSS)**: The user directly inputs their GitHub token.
- **Online (SaaS)**: The token is obtained through GitHub OAuth authentication.
#### Setting Up a Local GitHub Token
1. **Generate a Personal Access Token (PAT)**:
- Go to GitHub Settings > Developer Settings > Personal Access Tokens > Tokens (classic)
- Click "Generate new token (classic)"
- Go to GitHub Settings > Developer Settings > Personal Access Tokens > Tokens (classic).
- Click "Generate new token (classic)".
- Required scopes:
- `repo` (Full control of private repositories)
- `workflow` (Update GitHub Action workflows)
- `read:org` (Read organization data)
2. **Enter Token in OpenHands**:
- Click the Settings button (gear icon) in the top right
- Navigate to the "GitHub" section
- Paste your token in the "GitHub Token" field
- Click "Save" to apply the changes
- Click the Settings button (gear icon) in the top right.
- Navigate to the "GitHub" section.
- Paste your token in the "GitHub Token" field.
- Click "Save" to apply the changes.
#### Organizational Token Policies
If you're working with organizational repositories, additional setup may be required:
1. **Check Organization Requirements**:
- Organization admins may enforce specific token policies
- Some organizations require tokens to be created with SSO enabled
- Review your organization's [token policy settings](https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization)
- Organization admins may enforce specific token policies.
- Some organizations require tokens to be created with SSO enabled.
- Review your organization's [token policy settings](https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization).
2. **Verify Organization Access**:
- Go to your token settings on GitHub
- Look for the organization under "Organization access"
- If required, click "Enable SSO" next to your organization
- Complete the SSO authorization process
- Go to your token settings on GitHub.
- Look for the organization under "Organization access".
- If required, click "Enable SSO" next to your organization.
- Complete the SSO authorization process.
#### OAuth Authentication (Online Mode)
@@ -67,31 +67,31 @@ When using OpenHands in online mode, the GitHub OAuth flow:
- Organization read access
2. Authentication steps:
- Click "Sign in with GitHub" when prompted
- Review the requested permissions
- Authorize OpenHands to access your GitHub account
- If using an organization, authorize organization access if prompted
- Click "Sign in with GitHub" when prompted.
- Review the requested permissions.
- Authorize OpenHands to access your GitHub account.
- If using an organization, authorize organization access if prompted.
#### Troubleshooting
Common issues and solutions:
1. **Token Not Recognized**:
- Ensure the token is properly saved in settings
- Check that the token hasn't expired
- Verify the token has the required scopes
- Try regenerating the token
- **Token Not Recognized**:
- Ensure the token is properly saved in settings.
- Check that the token hasn't expired.
- Verify the token has the required scopes.
- Try regenerating the token.
2. **Organization Access Denied**:
- Check if SSO is required but not enabled
- Verify organization membership
- Contact organization admin if token policies are blocking access
- **Organization Access Denied**:
- Check if SSO is required but not enabled.
- Verify organization membership.
- Contact organization admin if token policies are blocking access.
3. **Verifying Token Works**:
- The app will show a green checkmark if the token is valid
- Try accessing a repository to confirm permissions
- Check the browser console for any error messages
- Use the "Test Connection" button in settings if available
- **Verifying Token Works**:
- The app will show a green checkmark if the token is valid.
- Try accessing a repository to confirm permissions.
- Check the browser console for any error messages.
- Use the "Test Connection" button in settings if available.
### Advanced Settings
@@ -103,11 +103,11 @@ Common issues and solutions:
The main interface consists of several key components:
1. **Chat Window**: The central area where you can view the conversation history with the AI assistant.
2. **Input Box**: Located at the bottom of the screen, use this to type your messages or commands to the AI.
3. **Send Button**: Click this to send your message to the AI.
4. **Settings Button**: A gear icon that opens the settings modal, allowing you to adjust your configuration at any time.
5. **Workspace Panel**: Displays the files and folders in your workspace, allowing you to navigate and view files, or the agent's past commands or web browsing history.
- **Chat Window**: The central area where you can view the conversation history with the AI assistant.
- **Input Box**: Located at the bottom of the screen, use this to type your messages or commands to the AI.
- **Send Button**: Click this to send your message to the AI.
- **Settings Button**: A gear icon that opens the settings modal, allowing you to adjust your configuration at any time.
- **Workspace Panel**: Displays the files and folders in your workspace, allowing you to navigate and view files, or the agent's past commands or web browsing history.
### Interacting with the AI
@@ -118,8 +118,9 @@ The main interface consists of several key components:
## Tips for Effective Use
1. Be specific in your requests to get the most accurate and helpful responses, as described in the [prompting best practices](../prompting/prompting-best-practices).
2. Use the workspace panel to explore your project structure.
3. Use one of the recommended models, as described in the [LLMs section](usage/llms/llms.md).
- Be specific in your requests to get the most accurate and helpful responses, as described in the [prompting best practices](../prompting/prompting-best-practices).
- Use the workspace panel to explore your project structure.
- Use one of the recommended models, as described in the [LLMs section](usage/llms/llms.md).
Remember, the GUI mode of OpenHands is designed to make your interaction with the AI assistant as smooth and intuitive as possible. Don't hesitate to explore its features to maximize your productivity.
Remember, the GUI mode of OpenHands is designed to make your interaction with the AI assistant as smooth and intuitive
as possible. Don't hesitate to explore its features to maximize your productivity.

View File

@@ -23,16 +23,16 @@ To run OpenHands in Headless mode with Docker:
1. Set the following environmental variables in your terminal:
* `WORKSPACE_BASE` to the directory you want OpenHands to edit (Ex: `export WORKSPACE_BASE=$(pwd)/workspace`).
* `LLM_MODEL` to the model to use (Ex: `export LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"`).
* `LLM_API_KEY` to the API key (Ex: `export LLM_API_KEY="sk_test_12345"`).
- `WORKSPACE_BASE` to the directory you want OpenHands to edit (Ex: `export WORKSPACE_BASE=$(pwd)/workspace`).
- `LLM_MODEL` to the model to use (Ex: `export LLM_MODEL="anthropic/claude-3-5-sonnet-20241022"`).
- `LLM_API_KEY` to the API key (Ex: `export LLM_API_KEY="sk_test_12345"`).
2. Run the following Docker command:
```bash
docker run -it \
--pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-e LLM_API_KEY=$LLM_API_KEY \
@@ -43,7 +43,7 @@ docker run -it \
-v ~/.openhands-state:/.openhands-state \
--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.18 \
docker.all-hands.dev/all-hands-ai/openhands:0.20 \
python -m openhands.core.main -t "write a bash script that prints hi"
```
@@ -53,4 +53,4 @@ To view all available configuration options for headless mode, run the Python co
### Additional Logs
For the headless mode to log all the agent actions, in your terminal run: `export LOG_ALL_EVENTS=true`
For the headless mode to log all the agent actions, in the terminal run: `export LOG_ALL_EVENTS=true`

View File

@@ -1,16 +0,0 @@
# Persisting Session Data
Using the standard installation, the session data is stored in memory. Currently, if OpenHands' service is restarted,
previous sessions become invalid (a new secret is generated) and thus not recoverable.
## How to Persist Session Data
### Development Workflow
In the `config.toml` file, specify the following:
```
[core]
...
file_store="local"
file_store_path="/absolute/path/to/openhands/cache/directory"
jwt_secret="secretpass"
```

View File

@@ -2,26 +2,26 @@
## System Requirements
* Docker version 26.0.0+ or Docker Desktop 4.31.0+.
* You must be using Linux or Mac OS.
* If you are on Windows, you must use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install).
- Docker version 26.0.0+ or Docker Desktop 4.31.0+.
- You must be using Linux or Mac OS.
- If you are on Windows, you must use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install).
## Start the app
The easiest way to run OpenHands is in Docker.
```bash
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik
docker pull docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik
docker run -it --rm --pull=always \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-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 \
docker.all-hands.dev/all-hands-ai/openhands:0.18
docker.all-hands.dev/all-hands-ai/openhands:0.20
```
You'll find OpenHands running at http://localhost:3000!
@@ -33,8 +33,6 @@ or run it on tagged issues with [a github action](https://docs.all-hands.dev/mod
## Setup
After running the command above, you'll find OpenHands running at [http://localhost:3000](http://localhost:3000).
Upon launching OpenHands, you'll see a settings modal. You **must** select an `LLM Provider` and `LLM Model` and enter a corresponding `API Key`.
These can be changed at any time by selecting the `Settings` button (gear icon) in the UI.

View File

@@ -18,17 +18,18 @@ docker run -it --pull=always \
...
```
Then set the following in the OpenHands UI through the Settings:
Then in the OpenHands UI Settings:
:::note
You will need your ChatGPT deployment name which can be found on the deployments page in Azure. This is referenced as
&lt;deployment-name&gt; below.
:::
* Enable `Advanced Options`
* `Custom Model` to azure/&lt;deployment-name&gt;
* `Base URL` to your Azure API Base URL (e.g. `https://example-endpoint.openai.azure.com`)
* `API Key` to your Azure API key
1. Enable `Advanced Options`
2. Set the following:
- `Custom Model` to azure/&lt;deployment-name&gt;
- `Base URL` to your Azure API Base URL (e.g. `https://example-endpoint.openai.azure.com`)
- `API Key` to your Azure API key
## Embeddings

View File

@@ -0,0 +1,106 @@
# Custom LLM Configurations
OpenHands supports defining multiple named LLM configurations in your `config.toml` file. This feature allows you to use different LLM configurations for different purposes, such as using a cheaper model for tasks that don't require high-quality responses, or using different models with different parameters for specific agents.
## How It Works
Named LLM configurations are defined in the `config.toml` file using sections that start with `llm.`. For example:
```toml
# Default LLM configuration
[llm]
model = "gpt-4"
api_key = "your-api-key"
temperature = 0.0
# Custom LLM configuration for a cheaper model
[llm.gpt3]
model = "gpt-3.5-turbo"
api_key = "your-api-key"
temperature = 0.2
# Another custom configuration with different parameters
[llm.high-creativity]
model = "gpt-4"
api_key = "your-api-key"
temperature = 0.8
top_p = 0.9
```
Each named configuration inherits all settings from the default `[llm]` section and can override any of those settings. You can define as many custom configurations as needed.
## Using Custom Configurations
### With Agents
You can specify which LLM configuration an agent should use by setting the `llm_config` parameter in the agent's configuration section:
```toml
[agent.RepoExplorerAgent]
# Use the cheaper GPT-3 configuration for this agent
llm_config = 'gpt3'
[agent.CodeWriterAgent]
# Use the high creativity configuration for this agent
llm_config = 'high-creativity'
```
### Configuration Options
Each named LLM configuration supports all the same options as the default LLM configuration. These include:
- Model selection (`model`)
- API configuration (`api_key`, `base_url`, etc.)
- Model parameters (`temperature`, `top_p`, etc.)
- Retry settings (`num_retries`, `retry_multiplier`, etc.)
- Token limits (`max_input_tokens`, `max_output_tokens`)
- And all other LLM configuration options
For a complete list of available options, see the LLM Configuration section in the [Configuration Options](../configuration-options) documentation.
## Use Cases
Custom LLM configurations are particularly useful in several scenarios:
- **Cost Optimization**: Use cheaper models for tasks that don't require high-quality responses, like repository exploration or simple file operations.
- **Task-Specific Tuning**: Configure different temperature and top_p values for tasks that require different levels of creativity or determinism.
- **Different Providers**: Use different LLM providers or API endpoints for different tasks.
- **Testing and Development**: Easily switch between different model configurations during development and testing.
## Example: Cost Optimization
A practical example of using custom LLM configurations to optimize costs:
```toml
# Default configuration using GPT-4 for high-quality responses
[llm]
model = "gpt-4"
api_key = "your-api-key"
temperature = 0.0
# Cheaper configuration for repository exploration
[llm.repo-explorer]
model = "gpt-3.5-turbo"
temperature = 0.2
# Configuration for code generation
[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'
```
In this example:
- Repository exploration uses a cheaper model since it mainly involves understanding and navigating code
- Code generation uses GPT-4 with a higher token limit for generating larger code blocks
- The default configuration remains available for other tasks
:::note
Custom LLM configurations are only available when using OpenHands in development mode, via `main.py` or `cli.py`. When running via `docker run`, please use the standard configuration options.
:::

View File

@@ -8,10 +8,10 @@ OpenHands uses LiteLLM to make calls to Google's chat models. You can find their
## Gemini - Google AI Studio Configs
When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings:
* `LLM Provider` to `Gemini`
* `LLM Model` to the model you will be using.
- `LLM Provider` to `Gemini`
- `LLM Model` to the model you will be using.
If the model is not in the list, toggle `Advanced Options`, and enter it in `Custom Model` (e.g. gemini/&lt;model-name&gt; like `gemini/gemini-1.5-pro`).
* `API Key` to your Gemini API key
- `API Key` to your Gemini API key
## VertexAI - Google Cloud Platform Configs
@@ -25,6 +25,6 @@ VERTEXAI_LOCATION="<your-gcp-location>"
```
Then set the following in the OpenHands UI through the Settings:
* `LLM Provider` to `VertexAI`
* `LLM Model` to the model you will be using.
- `LLM Provider` to `VertexAI`
- `LLM Model` to the model you will be using.
If the model is not in the list, toggle `Advanced Options`, and enter it in `Custom Model` (e.g. vertex_ai/&lt;model-name&gt;).

View File

@@ -5,19 +5,20 @@ OpenHands uses LiteLLM to make calls to chat models on Groq. You can find their
## Configuration
When running OpenHands, you'll need to set the following in the OpenHands UI through the Settings:
* `LLM Provider` to `Groq`
* `LLM Model` to the model you will be using. [Visit here to see the list of
- `LLM Provider` to `Groq`
- `LLM Model` to the model you will be using. [Visit here to see the list of
models that Groq hosts](https://console.groq.com/docs/models). If the model is not in the list, toggle
`Advanced Options`, and enter it in `Custom Model` (e.g. groq/&lt;model-name&gt; like `groq/llama3-70b-8192`).
* `API key` to your Groq API key. To find or create your Groq API Key, [see here](https://console.groq.com/keys).
- `API key` to your Groq API key. To find or create your Groq API Key, [see here](https://console.groq.com/keys).
## Using Groq as an OpenAI-Compatible Endpoint
The Groq endpoint for chat completion is [mostly OpenAI-compatible](https://console.groq.com/docs/openai). Therefore, you can access Groq models as you
would access any OpenAI-compatible endpoint. You can set the following in the OpenHands UI through the Settings:
* Enable `Advanced Options`
* `Custom Model` to the prefix `openai/` + the model you will be using (e.g. `openai/llama3-70b-8192`)
* `Base URL` to `https://api.groq.com/openai/v1`
* `API Key` to your Groq API key
would access any OpenAI-compatible endpoint. In the OpenHands UI through the Settings:
1. Enable `Advanced Options`
2. Set the following:
- `Custom Model` to the prefix `openai/` + the model you will be using (e.g. `openai/llama3-70b-8192`)
- `Base URL` to `https://api.groq.com/openai/v1`
- `API Key` to your Groq API key

View File

@@ -4,22 +4,15 @@ OpenHands can connect to any LLM supported by LiteLLM. However, it requires a po
## Model Recommendations
Based on our evaluations of language models for coding tasks (using the SWE-bench dataset), we can provide some recommendations for model selection. Some analyses can be found in [this blog article comparing LLMs](https://www.all-hands.dev/blog/evaluation-of-llms-as-coding-agents-on-swe-bench-at-30x-speed) and [this blog article with some more recent results](https://www.all-hands.dev/blog/openhands-codeact-21-an-open-state-of-the-art-software-development-agent).
When choosing a model, consider both the quality of outputs and the associated costs. Here's a summary of the findings:
- Claude 3.5 Sonnet is the best by a fair amount, achieving a 53% resolve rate on SWE-Bench Verified with the default agent in OpenHands.
- GPT-4o lags behind, and o1-mini actually performed somewhat worse than GPT-4o. We went in and analyzed the results a little, and briefly it seemed like o1 was sometimes "overthinking" things, performing extra environment configuration tasks when it could just go ahead and finish the task.
- Finally, the strongest open models were Llama 3.1 405 B and deepseek-v2.5, and they performed reasonably, even besting some of the closed models.
Please refer to the [full article](https://www.all-hands.dev/blog/evaluation-of-llms-as-coding-agents-on-swe-bench-at-30x-speed) for more details.
Based on our evaluations of language models for coding tasks (using the SWE-bench dataset), we can provide some
recommendations for model selection. Our latest benchmarking results can be found in [this spreadsheet](https://docs.google.com/spreadsheets/d/1wOUdFCMyY6Nt0AIqF705KN4JKOWgeI4wUGUP60krXXs/edit?gid=0).
Based on these findings and community feedback, the following models have been verified to work reasonably well with OpenHands:
- claude-3-5-sonnet (recommended)
- gpt-4 / gpt-4o
- llama-3.1-405b
- deepseek-v2.5
- anthropic/claude-3-5-sonnet-20241022 (recommended)
- anthropic/claude-3-5-haiku-20241022
- deepseek/deepseek-chat
- gpt-4o
:::warning
OpenHands will issue many prompts to the LLM you configure. Most of these LLMs cost money, so be sure to set spending
@@ -69,9 +62,11 @@ We have a few guides for running OpenHands with specific model providers:
### API retries and rate limits
LLM providers typically have rate limits, sometimes very low, and may require retries. OpenHands will automatically retry requests if it receives a Rate Limit Error (429 error code), API connection error, or other transient errors.
LLM providers typically have rate limits, sometimes very low, and may require retries. OpenHands will automatically
retry requests if it receives a Rate Limit Error (429 error code), API connection error, or other transient errors.
You can customize these options as you need for the provider you're using. Check their documentation, and set the following environment variables to control the number of retries and the time between retries:
You can customize these options as you need for the provider you're using. Check their documentation, and set the
following environment variables to control the number of retries and the time between retries:
- `LLM_NUM_RETRIES` (Default of 8)
- `LLM_RETRY_MIN_WAIT` (Default of 15 seconds)

View File

@@ -17,8 +17,9 @@ Just as for OpenAI Chat completions, we use LiteLLM for OpenAI-compatible endpoi
## Using an OpenAI Proxy
If you're using an OpenAI proxy, you'll need to set the following in the OpenHands UI through the Settings:
* Enable `Advanced Options`
* `Custom Model` to openai/&lt;model-name&gt; (e.g. `openai/gpt-4o` or openai/&lt;proxy-prefix&gt;/&lt;model-name&gt;)
* `Base URL` to the URL of your OpenAI proxy
* `API Key` to your OpenAI API key
If you're using an OpenAI proxy, in the OpenHands UI through the Settings:
1. Enable `Advanced Options`
2. Set the following:
- `Custom Model` to openai/&lt;model-name&gt; (e.g. `openai/gpt-4o` or openai/&lt;proxy-prefix&gt;/&lt;model-name&gt;)
- `Base URL` to the URL of your OpenAI proxy
- `API Key` to your OpenAI API key

View File

@@ -1,67 +0,0 @@
# Customizing Agent Behavior
OpenHands can be customized to work more effectively with specific repositories by providing repository-specific context and guidelines. This section explains how to optimize OpenHands for your project.
## Repository Configuration
You can customize OpenHands' behavior for your repository by creating a `.openhands` directory in your repository's root. At minimum, it should contain the file
`.openhands/microagents/repo.md`, which includes instructions that will
be given to the agent every time it works with this repository.
We suggest including the following information:
1. **Repository Overview**: A brief description of your project's purpose and architecture
2. **Directory Structure**: Key directories and their purposes
3. **Development Guidelines**: Project-specific coding standards and practices
4. **Testing Requirements**: How to run tests and what types of tests are required
5. **Setup Instructions**: Steps needed to build and run the project
### Example Repository Configuration
Example `.openhands/microagents/repo.md` file:
```
Repository: MyProject
Description: A web application for task management
Directory Structure:
- src/: Main application code
- tests/: Test files
- docs/: Documentation
Setup:
- Run `npm install` to install dependencies
- Use `npm run dev` for development
- Run `npm test` for testing
Guidelines:
- Follow ESLint configuration
- Write tests for all new features
- Use TypeScript for new code
```
### Customizing Prompts
When working with a customized repository:
1. **Reference Project Standards**: Mention specific coding standards or patterns used in your project
2. **Include Context**: Reference relevant documentation or existing implementations
3. **Specify Testing Requirements**: Include project-specific testing requirements in your prompts
Example customized prompt:
```
Add a new task completion feature to src/components/TaskList.tsx following our existing component patterns.
Include unit tests in tests/components/ and update the documentation in docs/features/.
The component should use our shared styling from src/styles/components.
```
### Best Practices for Repository Customization
1. **Keep Instructions Updated**: Regularly update your `.openhands` directory as your project evolves
2. **Be Specific**: Include specific paths, patterns, and requirements unique to your project
3. **Document Dependencies**: List all tools and dependencies required for development
4. **Include Examples**: Provide examples of good code patterns from your project
5. **Specify Conventions**: Document naming conventions, file organization, and code style preferences
By customizing OpenHands for your repository, you'll get more accurate and consistent results that align with your project's standards and requirements.
## Other Microagents
You can create other instructions in the `.openhands/microagents/` directory
that will be sent to the agent if a particular keyword is found, like `test`, `frontend`, or `migration`. See [Microagents](microagents.md) for more information.

View File

@@ -0,0 +1,36 @@
# Microagents Overview
Microagents are specialized prompts that enhance OpenHands with domain-specific knowledge, repository-specific context
and task-specific workflows. They help by providing expert guidance, automating common tasks, and ensuring
consistent practices across projects.
## Microagent Types
Currently OpenHands supports the following types of microagents:
* [Repository Microagents](./microagents-repo): Repository-specific context and guidelines for OpenHands.
* [Public Microagents](./microagents-public): General guidelines triggered by keywords for all OpenHands users.
When OpenHands works with a repository, it:
1. Loads repository-specific instructions from `.openhands/microagents/` if present in the repository.
2. Loads general guidelines triggered by keywords in conversations.
See current [Public Microagents](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge).
## Microagent Format
All microagents use markdown files with YAML frontmatter that have special instructions to help OpenHands accomplish
tasks:
```
---
name: <Name of the microagent>
type: <MicroAgent type>
version: <MicroAgent version>
agent: <The agent type (Typically CodeActAgent)>
triggers:
- <Optional keywords triggering the microagent. If triggers are removed, it will always be included>
---
<Markdown with any special guidelines, instructions, and prompts that OpenHands should follow.
Check out the specific documentation for each microagent on best practices for more information.>
```

View File

@@ -0,0 +1,153 @@
# Public Microagents
## Overview
Public microagents are specialized guidelines triggered by keywords for all OpenHands users.
They are defined in markdown files under the
[`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge) directory.
Public microagents:
- Monitor incoming commands for their trigger words.
- Activate when relevant triggers are detected.
- Apply their specialized knowledge and capabilities.
- Follow their specific guidelines and restrictions.
## Current Public Microagents
For more information about specific microagents, refer to their individual documentation files in
the [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge/) directory.
### GitHub Agent
**File**: `github.md`
**Triggers**: `github`, `git`
The GitHub agent specializes in GitHub API interactions and repository management. It:
- Has access to a `GITHUB_TOKEN` for API authentication.
- Follows strict guidelines for repository interactions.
- Handles branch management and pull requests.
- Uses the GitHub API instead of web browser interactions.
Key features:
- Branch protection (prevents direct pushes to main/master)
- Automated PR creation
- Git configuration management
- API-first approach for GitHub operations
Usage Example:
```bash
git checkout -b feature-branch
git commit -m "Add new feature"
git push origin feature-branch
```
### NPM Agent
**File**: `npm.md`
**Triggers**: `npm`
Specializes in handling npm package management with specific focus on:
- Non-interactive shell operations.
- Automated confirmation handling using Unix 'yes' command.
- Package installation automation.
Usage Example:
```bash
yes | npm install package-name
```
## Contributing a Public Microagent
You can create your own public microagents by adding new markdown files to the
[`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge/) directory.
### Public Microagents Best Practices
- **Clear Scope**: Keep the microagent focused on a specific domain or task.
- **Explicit Instructions**: Provide clear, unambiguous guidelines.
- **Useful Examples**: Include practical examples of common use cases.
- **Safety First**: Include necessary warnings and constraints.
- **Integration Awareness**: Consider how the microagent interacts with other components.
### Steps to Contribute a Public Microagent
#### 1. Plan the Public Microagent
Before creating a public microagent, consider:
- What specific problem or use case will it address?
- What unique capabilities or knowledge should it have?
- What trigger words make sense for activating it?
- What constraints or guidelines should it follow?
#### 2. Create File
Create a new markdown file in [`microagents/knowledge/`](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge/)
with a descriptive name (e.g., `docker.md` for a Docker-focused agent).
Update the file with the required frontmatter [according to the required format](./microagents-overview#microagent-format)
and the required specialized guidelines while following the [best practices above](#public-microagents-best-practices).
#### 3. Testing the Public Microagent
- Test the agent with various prompts.
- Verify trigger words activate the agent correctly.
- Ensure instructions are clear and comprehensive.
- Check for potential conflicts with existing agents.
#### 4. Submission Process
Submit a pull request with:
- The new microagent file.
- Updated documentation if needed.
- Description of the agent's purpose and capabilities.
### Example Public Microagent Implementation
Here's a template for a new microagent:
```markdown
---
name: docker
agent: CodeActAgent
triggers:
- docker
- container
---
You are responsible for Docker container management and Dockerfile creation.
Key responsibilities:
1. Create and modify Dockerfiles
2. Manage container lifecycle
3. Handle Docker Compose configurations
Guidelines:
- Always use official base images when possible
- Include necessary security considerations
- Follow Docker best practices for layer optimization
Examples:
1. Creating a Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
2. Docker Compose usage:
version: '3'
services:
web:
build: .
ports:
- "3000:3000"
Remember to:
- Validate Dockerfile syntax
- Check for security vulnerabilities
- Optimize for build time and image size
```
See the [current public micro-agents](https://github.com/All-Hands-AI/OpenHands/tree/main/microagents/knowledge) for
more examples.

View File

@@ -0,0 +1,68 @@
# Repository Microagents
## Overview
OpenHands can be customized to work more effectively with specific repositories by providing repository-specific context
and guidelines. This section explains how to optimize OpenHands for your project.
## Creating a Repository Micro-Agent
You can customize OpenHands' behavior for your repository by creating a `.openhands/microagents/` directory in your repository's root.
At minimum it should contain the file
`.openhands/microagents/repo.md`, which includes instructions that will
be given to the agent every time it works with this repository.
### Repository Microagents Best Practices
- **Keep Instructions Updated**: Regularly update your `.openhands/microagents/` directory as your project evolves.
- **Be Specific**: Include specific paths, patterns, and requirements unique to your project.
- **Document Dependencies**: List all tools and dependencies required for development.
- **Include Examples**: Provide examples of good code patterns from your project.
- **Specify Conventions**: Document naming conventions, file organization, and code style preferences.
### Steps to Create a Repository Microagent
#### 1. Plan the Repository Microagent
When creating a repository-specific micro-agent, we suggest including the following information:
- **Repository Overview**: A brief description of your project's purpose and architecture.
- **Directory Structure**: Key directories and their purposes.
- **Development Guidelines**: Project-specific coding standards and practices.
- **Testing Requirements**: How to run tests and what types of tests are required.
- **Setup Instructions**: Steps needed to build and run the project.
#### 2. Create File
Create a file in your repository under `.openhands/microagents/` (Example: `.openhands/microagents/repo.md`)
Update the file with the required frontmatter [according to the required format](./microagents-overview#microagent-format)
and the required specialized guidelines for your repository.
### Example Repository Microagent
```
---
name: repo
type: repo
agent: CodeActAgent
---
Repository: MyProject
Description: A web application for task management
Directory Structure:
- src/: Main application code
- tests/: Test files
- docs/: Documentation
Setup:
- Run `npm install` to install dependencies
- Use `npm run dev` for development
- Run `npm test` for testing
Guidelines:
- Follow ESLint configuration
- Write tests for all new features
- Use TypeScript for new code
If adding a new component in src/components, always add appropriate unit tests in tests/components/.
```

View File

@@ -1,209 +0,0 @@
# Micro-Agents
OpenHands uses specialized micro-agents to handle specific tasks and contexts efficiently. These micro-agents are small, focused components that provide specialized behavior and knowledge for particular scenarios.
## Overview
Micro-agents are defined in markdown files under the `openhands/agenthub/codeact_agent/micro/` directory. Each micro-agent is configured with:
- A unique name
- The agent type (typically CodeActAgent)
- Trigger keywords that activate the agent
- Specific instructions and capabilities
## Available Micro-Agents
### GitHub Agent
**File**: `github.md`
**Triggers**: `github`, `git`
The GitHub agent specializes in GitHub API interactions and repository management. It:
- Has access to a `GITHUB_TOKEN` for API authentication
- Follows strict guidelines for repository interactions
- Handles branch management and pull requests
- Uses the GitHub API instead of web browser interactions
Key features:
- Branch protection (prevents direct pushes to main/master)
- Automated PR creation
- Git configuration management
- API-first approach for GitHub operations
### NPM Agent
**File**: `npm.md`
**Triggers**: `npm`
Specializes in handling npm package management with specific focus on:
- Non-interactive shell operations
- Automated confirmation handling using Unix 'yes' command
- Package installation automation
### Custom Micro-Agents
You can create your own micro-agents by adding new markdown files to the micro-agents directory. Each file should follow this structure:
```markdown
---
name: agent_name
agent: CodeActAgent
triggers:
- trigger_word1
- trigger_word2
---
Instructions and capabilities for the micro-agent...
```
## Best Practices
When working with micro-agents:
1. **Use Appropriate Triggers**: Ensure your commands include the relevant trigger words to activate the correct micro-agent
2. **Follow Agent Guidelines**: Each agent has specific instructions and limitations - respect these for optimal results
3. **API-First Approach**: When available, use API endpoints rather than web interfaces
4. **Automation Friendly**: Design commands that work well in non-interactive environments
## Integration
Micro-agents are automatically integrated into OpenHands' workflow. They:
- Monitor incoming commands for their trigger words
- Activate when relevant triggers are detected
- Apply their specialized knowledge and capabilities
- Follow their specific guidelines and restrictions
## Example Usage
```bash
# GitHub agent example
git checkout -b feature-branch
git commit -m "Add new feature"
git push origin feature-branch
# NPM agent example
yes | npm install package-name
```
For more information about specific agents, refer to their individual documentation files in the micro-agents directory.
## Contributing a Micro-Agent
To contribute a new micro-agent to OpenHands, follow these guidelines:
### 1. Planning Your Micro-Agent
Before creating a micro-agent, consider:
- What specific problem or use case will it address?
- What unique capabilities or knowledge should it have?
- What trigger words make sense for activating it?
- What constraints or guidelines should it follow?
### 2. File Structure
Create a new markdown file in `openhands/agenthub/codeact_agent/micro/` with a descriptive name (e.g., `docker.md` for a Docker-focused agent).
### 3. Required Components
Your micro-agent file must include:
1. **Front Matter**: YAML metadata at the start of the file:
```markdown
---
name: your_agent_name
agent: CodeActAgent
triggers:
- trigger_word1
- trigger_word2
---
```
2. **Instructions**: Clear, specific guidelines for the agent's behavior:
```markdown
You are responsible for [specific task/domain].
Key responsibilities:
1. [Responsibility 1]
2. [Responsibility 2]
Guidelines:
- [Guideline 1]
- [Guideline 2]
Examples of usage:
[Example 1]
[Example 2]
```
### 4. Best Practices for Micro-Agent Development
1. **Clear Scope**: Keep the agent focused on a specific domain or task
2. **Explicit Instructions**: Provide clear, unambiguous guidelines
3. **Useful Examples**: Include practical examples of common use cases
4. **Safety First**: Include necessary warnings and constraints
5. **Integration Awareness**: Consider how the agent interacts with other components
### 5. Testing Your Micro-Agent
Before submitting:
1. Test the agent with various prompts
2. Verify trigger words activate the agent correctly
3. Ensure instructions are clear and comprehensive
4. Check for potential conflicts with existing agents
### 6. Example Implementation
Here's a template for a new micro-agent:
```markdown
---
name: docker
agent: CodeActAgent
triggers:
- docker
- container
---
You are responsible for Docker container management and Dockerfile creation.
Key responsibilities:
1. Create and modify Dockerfiles
2. Manage container lifecycle
3. Handle Docker Compose configurations
Guidelines:
- Always use official base images when possible
- Include necessary security considerations
- Follow Docker best practices for layer optimization
Examples:
1. Creating a Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
2. Docker Compose usage:
version: '3'
services:
web:
build: .
ports:
- "3000:3000"
Remember to:
- Validate Dockerfile syntax
- Check for security vulnerabilities
- Optimize for build time and image size
```
### 7. Submission Process
1. Create your micro-agent file in the correct directory
2. Test thoroughly
3. Submit a pull request with:
- The new micro-agent file
- Updated documentation if needed
- Description of the agent's purpose and capabilities
Remember that micro-agents are a powerful way to extend OpenHands' capabilities in specific domains. Well-designed agents can significantly improve the system's ability to handle specialized tasks.

View File

@@ -6,35 +6,31 @@ When working with OpenHands AI software developer, it's crucial to provide clear
Good prompts are:
1. **Concrete**: They explain exactly what functionality should be added or what error needs to be fixed.
2. **Location-specific**: If known, they explain the locations in the code base that should be modified.
3. **Appropriately scoped**: They should be the size of a single feature, typically not exceeding 100 lines of code.
- **Concrete**: They explain exactly what functionality should be added or what error needs to be fixed.
- **Location-specific**: If known, they explain the locations in the code base that should be modified.
- **Appropriately scoped**: They should be the size of a single feature, typically not exceeding 100 lines of code.
## Examples
### Good Prompt Examples
1. "Add a function `calculate_average` in `utils/math_operations.py` that takes a list of numbers as input and returns their average."
2. "Fix the TypeError in `frontend/src/components/UserProfile.tsx` occurring on line 42. The error suggests we're trying to access a property of undefined."
3. "Implement input validation for the email field in the registration form. Update `frontend/src/components/RegistrationForm.tsx` to check if the email is in a valid format before submission."
- "Add a function `calculate_average` in `utils/math_operations.py` that takes a list of numbers as input and returns their average."
- "Fix the TypeError in `frontend/src/components/UserProfile.tsx` occurring on line 42. The error suggests we're trying to access a property of undefined."
- "Implement input validation for the email field in the registration form. Update `frontend/src/components/RegistrationForm.tsx` to check if the email is in a valid format before submission."
### Bad Prompt Examples
1. "Make the code better." (Too vague, not concrete)
2. "Rewrite the entire backend to use a different framework." (Not appropriately scoped)
3. "There's a bug somewhere in the user authentication. Can you find and fix it?" (Lacks specificity and location information)
- "Make the code better." (Too vague, not concrete)
- "Rewrite the entire backend to use a different framework." (Not appropriately scoped)
- "There's a bug somewhere in the user authentication. Can you find and fix it?" (Lacks specificity and location information)
## Tips for Effective Prompting
1. Be as specific as possible about the desired outcome or the problem to be solved.
2. Provide context, including relevant file paths and line numbers if available.
3. Break down large tasks into smaller, manageable prompts.
4. Include any relevant error messages or logs.
5. Specify the programming language or framework if it's not obvious from the context.
- Be as specific as possible about the desired outcome or the problem to be solved.
- Provide context, including relevant file paths and line numbers if available.
- Break down large tasks into smaller, manageable prompts.
- Include any relevant error messages or logs.
- Specify the programming language or framework if it's not obvious from the context.
Remember, the more precise and informative your prompt is, the better the AI can assist you in developing or modifying the OpenHands software.

View File

@@ -16,7 +16,7 @@ some flags being passed to `docker run` that make this possible:
```
docker run # ...
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.18-nikolaik \
-e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.20-nikolaik \
-v /var/run/docker.sock:/var/run/docker.sock \
# ...
```
@@ -26,30 +26,29 @@ that contains our Runtime server, as well as some basic utilities for Python and
You can also [build your own runtime image](how-to/custom-sandbox-guide).
### Connecting to Your filesystem
One useful feature here is the ability to connect to your local filesystem.
One useful feature here is the ability to connect to your local filesystem. To mount your filesystem into the runtime:
1. Set `WORKSPACE_BASE`:
To mount your filesystem into the runtime, first set WORKSPACE_BASE:
```bash
export WORKSPACE_BASE=/path/to/your/code
```bash
export WORKSPACE_BASE=/path/to/your/code
# Linux and Mac Example
# export WORKSPACE_BASE=$HOME/OpenHands
# Will set $WORKSPACE_BASE to /home/<username>/OpenHands
#
# WSL on Windows Example
# export WORKSPACE_BASE=/mnt/c/dev/OpenHands
# Will set $WORKSPACE_BASE to C:\dev\OpenHands
```
# Linux and Mac Example
# export WORKSPACE_BASE=$HOME/OpenHands
# Will set $WORKSPACE_BASE to /home/<username>/OpenHands
#
# WSL on Windows Example
# export WORKSPACE_BASE=/mnt/c/dev/OpenHands
# Will set $WORKSPACE_BASE to C:\dev\OpenHands
```
2. Add the following options to the `docker run` command:
then add the following options to the `docker run` command:
```bash
docker run # ...
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-v $WORKSPACE_BASE:/opt/workspace_base \
# ...
```
```bash
docker run # ...
-e SANDBOX_USER_ID=$(id -u) \
-e WORKSPACE_MOUNT_PATH=$WORKSPACE_BASE \
-v $WORKSPACE_BASE:/opt/workspace_base \
# ...
```
Be careful! There's nothing stopping the OpenHands agent from deleting or modifying
any files that are mounted into its workspace.
@@ -59,7 +58,7 @@ but seems to work well on most systems.
## All Hands Runtime
The All Hands Runtime is currently in beta. You can request access by joining
the #remote-runtime-limited-beta channel on Slack ([see the README](https://github.com/All-Hands-AI/OpenHands?tab=readme-ov-file#-join-our-community) for an invite).
the #remote-runtime-limited-beta channel on Slack ([see the README](https://github.com/All-Hands-AI/OpenHands?tab=readme-ov-file#-how-to-join-the-community) for an invite).
To use the All Hands Runtime, set the following environment variables when
starting OpenHands:

1322
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,10 +15,10 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.6.3",
"@docusaurus/plugin-content-pages": "^3.6.3",
"@docusaurus/preset-classic": "^3.6.3",
"@docusaurus/theme-mermaid": "^3.6.3",
"@docusaurus/core": "^3.7.0",
"@docusaurus/plugin-content-pages": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
"@docusaurus/theme-mermaid": "^3.7.0",
"@mdx-js/react": "^3.1.0",
"clsx": "^2.0.0",
"prism-react-renderer": "^2.4.1",
@@ -29,7 +29,7 @@
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.5.1",
"@docusaurus/tsconfig": "^3.6.3",
"@docusaurus/tsconfig": "^3.7.0",
"@docusaurus/types": "^3.5.1",
"typescript": "~5.7.2"
},

View File

@@ -23,15 +23,26 @@ const sidebars: SidebarsConfig = {
id: 'usage/prompting/prompting-best-practices',
},
{
type: 'doc',
label: 'Customization',
id: 'usage/prompting/customization',
},
{
type: 'doc',
type: 'category',
label: 'Microagents',
id: 'usage/prompting/microagents',
},
items: [
{
type: 'doc',
label: 'Overview',
id: 'usage/prompting/microagents-overview',
},
{
type: 'doc',
label: 'Repository',
id: 'usage/prompting/microagents-repo',
},
{
type: 'doc',
label: 'Public',
id: 'usage/prompting/microagents-public',
},
],
}
],
},
{
@@ -126,11 +137,6 @@ const sidebars: SidebarsConfig = {
label: 'Custom Sandbox',
id: 'usage/how-to/custom-sandbox-guide',
},
{
type: 'doc',
label: 'Persist Session Data',
id: 'usage/how-to/persist-session-data',
},
],
},
{

View File

@@ -123,7 +123,6 @@ class openhands.state.State {
updated_info: List[Tuple[Action, Observation]]
}
class openhands.observation.CmdOutputObservation {
command_id: int
command: str
exit_code: int
observation: str

View File

@@ -75,6 +75,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -59,6 +59,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config
@@ -135,7 +137,6 @@ def complete_runtime(
action = CmdRunAction(
command=f'chmod +x ./{script_name} && ./{script_name}',
keep_prompt=False,
)
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
@@ -162,8 +163,7 @@ def complete_runtime(
logger.info(f'Running get ground truth cmd: {script_name}')
action = CmdRunAction(
command=f'chmod +x ./{script_name} && ./{script_name}',
keep_prompt=False,
command=f'chmod +x ./{script_name} && ./{script_name}'
)
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)

View File

@@ -67,6 +67,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
# copy 'draft_editor' config if exists
config_copy = copy.deepcopy(config)
@@ -143,10 +145,7 @@ def complete_runtime(
)
logger.info(f'Running test file: {script_name}')
action = CmdRunAction(
command=f'python3 -m unittest {script_name}',
keep_prompt=False,
)
action = CmdRunAction(command=f'python3 -m unittest {script_name}')
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})

View File

@@ -73,6 +73,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config
@@ -197,7 +199,7 @@ def complete_runtime(
if obs.exit_code == 0:
test_result['metadata']['1_copy_change_success'] = True
action = CmdRunAction(command=f'cat {generated_path}', keep_prompt=False)
action = CmdRunAction(command=f'cat {generated_path}')
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
assert obs.exit_code == 0
@@ -221,9 +223,7 @@ def complete_runtime(
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert obs.exit_code == 0
action = CmdRunAction(
command='cat /testing_files/results_biocoder.json', keep_prompt=False
)
action = CmdRunAction(command='cat /testing_files/results_biocoder.json')
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
if obs.exit_code == 0:

View File

@@ -127,7 +127,6 @@ For each problem, OpenHands is given a set number of iterations to fix the faili
"observation": "run",
"content": "california_schools/california_schools.sqlite\r\n[(1.0,)]",
"extras": {
"command_id": -1,
"command": "python3 0.py",
"exit_code": 0
}

View File

@@ -86,6 +86,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config
@@ -266,10 +268,7 @@ def initialize_runtime(
runtime.copy_to(db_file, '/workspace')
# Check the database is copied
action = CmdRunAction(
command='cd /workspace && ls -l',
keep_prompt=False,
)
action = CmdRunAction(command='cd /workspace && ls -l')
obs = runtime.run_action(action)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert obs.exit_code == 0
@@ -298,10 +297,7 @@ def complete_runtime(
instance_id = instance.instance_id.replace('/', '__')
path = os.path.join('/workspace', f'{instance_id}.py')
action = CmdRunAction(
command=f'cat {path}',
keep_prompt=False,
)
action = CmdRunAction(command=f'cat {path}')
obs = runtime.run_action(action)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})

View File

@@ -50,6 +50,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -77,6 +77,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
agent_config = AgentConfig(
function_calling=False,
codeact_enable_jupyter=True,

View File

@@ -62,6 +62,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -55,6 +55,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -76,6 +76,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -71,7 +71,6 @@ For each problem, OpenHands is given a set number of iterations to fix the faili
"observation": "run",
"content": "[File: /workspace/Python__2.py (14 lines total)]\r\n1:def truncate_number(number: float) -> float:\r\n2: return number % 1.0 + 1.0\r\n3:\r\n4:\r\n5:\r\n6:\r\n7:\r\n8:\r\n9:def check(truncate_number):\r\n10: assert truncate_number(3.5) == 0.5\r\n11: assert abs(truncate_number(1.33) - 0.33) < 1e-6\r\n12: assert abs(truncate_number(123.456) - 0.456) < 1e-6\r\n13:\r\n14:check(truncate_number)",
"extras": {
"command_id": -1,
"command": "open Python__2.py",
"exit_code": 0
}
@@ -98,7 +97,6 @@ For each problem, OpenHands is given a set number of iterations to fix the faili
"observation": "run",
"content": "> > [File: /workspace/Python__2.py (14 lines total)]\r\n1:def truncate_number(number: float) -> float:\r\n2: return number % 1.0\r\n3:\r\n4:\r\n5:\r\n6:\r\n7:\r\n8:\r\n9:def check(truncate_number):\r\n10: assert truncate_number(3.5) == 0.5\r\n11: assert abs(truncate_number(1.33) - 0.33) < 1e-6\r\n12: assert abs(truncate_number(123.456) - 0.456) < 1e-6\r\n13:\r\n14:check(truncate_number)\r\nFile updated. Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.",
"extras": {
"command_id": -1,
"command": "edit 2:2 <<EOF\n return number % 1.0\nEOF",
"exit_code": 0
}
@@ -125,7 +123,6 @@ For each problem, OpenHands is given a set number of iterations to fix the faili
"observation": "run",
"content": "",
"extras": {
"command_id": -1,
"command": "python3 Python__2.py",
"exit_code": 0
}

View File

@@ -97,6 +97,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config
@@ -169,9 +171,7 @@ def complete_runtime(
num_workers = LANGUAGE_TO_NUM_WORKERS[language]
python_imports = '\n'.join(IMPORT_HELPER[language])
action = CmdRunAction(
command=f'cat /workspace/{_get_instance_id(instance)}.py', keep_prompt=False
)
action = CmdRunAction(command=f'cat /workspace/{_get_instance_id(instance)}.py')
obs = runtime.run_action(action)
assert obs.exit_code == 0

View File

@@ -61,6 +61,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -119,6 +119,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -92,6 +92,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config
@@ -161,7 +163,7 @@ def complete_runtime(
eval_script = os.path.join(task_path, 'run.sh')
logger.info(f'Running evaluation script: {eval_script}')
action = CmdRunAction(command=f'cat {eval_script}', keep_prompt=False)
action = CmdRunAction(command=f'cat {eval_script}')
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
if obs.exit_code == 0:

View File

@@ -121,10 +121,7 @@ def initialize_runtime(
runtime.copy_to(dataset_dir, '/workspace/benchmark/datasets', recursive=True)
# Check the dataset exists
action = CmdRunAction(
command='cd /workspace/benchmark/datasets && ls',
keep_prompt=False,
)
action = CmdRunAction(command='cd /workspace/benchmark/datasets && ls')
obs = runtime.run_action(action)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert obs.exit_code == 0
@@ -154,10 +151,7 @@ def complete_runtime(
assert obs.exit_code == 0
action = CmdRunAction(
command=f'cat pred_programs/{instance.pred_program_name}',
keep_prompt=False,
)
action = CmdRunAction(command=f'cat pred_programs/{instance.pred_program_name}')
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)

View File

@@ -204,7 +204,7 @@ Then, in a separate Python environment with `streamlit` library, you can run the
```bash
# Make sure you are inside the cloned `evaluation` repo
conda activate streamlit # if you follow the optional conda env setup above
streamlit app.py --server.port 8501 --server.address 0.0.0.0
streamlit run app.py --server.port 8501 --server.address 0.0.0.0
```
Then you can access the SWE-Bench trajectory visualizer at `localhost:8501`.

View File

@@ -98,6 +98,7 @@ def process_instance(
metadata: EvalMetadata,
reset_logger: bool = True,
log_dir: str | None = None,
runtime_failure_count: int = 0,
) -> EvalOutput:
"""
Evaluate agent performance on a SWE-bench problem instance.
@@ -146,6 +147,16 @@ def process_instance(
metadata=metadata,
)
# Increase resource_factor with increasing attempt_id
if runtime_failure_count > 0:
config.sandbox.remote_runtime_resource_factor = min(
config.sandbox.remote_runtime_resource_factor * (2**runtime_failure_count),
4, # hardcode maximum resource factor to 4
)
logger.warning(
f'This is the second attempt for instance {instance.instance_id}, setting resource factor to {config.sandbox.remote_runtime_resource_factor}'
)
runtime = create_runtime(config)
call_async_from_sync(runtime.connect)
# Get patch and save it to /tmp/patch.diff
@@ -177,7 +188,7 @@ def process_instance(
"(patch --batch --fuzz=5 -p1 -i /tmp/patch.diff && echo 'APPLY_PATCH_PASS' || "
"echo 'APPLY_PATCH_FAIL')))"
)
action = CmdRunAction(command=exec_command, keep_prompt=False)
action = CmdRunAction(command=exec_command)
action.timeout = 600
obs = runtime.run_action(action)
assert isinstance(obs, CmdOutputObservation)
@@ -200,9 +211,7 @@ def process_instance(
# Run eval script in background and save output to log file
log_file = '/tmp/eval_output.log'
action = CmdRunAction(
command=f'/tmp/eval.sh > {log_file} 2>&1 & echo $!', keep_prompt=False
)
action = CmdRunAction(command=f'/tmp/eval.sh > {log_file} 2>&1 & echo $!')
action.timeout = 60 # Short timeout just to get the process ID
obs = runtime.run_action(action)
@@ -224,7 +233,7 @@ def process_instance(
instance['test_result']['report']['test_timeout'] = True
break
check_action = CmdRunAction(
command=f'ps -p {pid} > /dev/null; echo $?', keep_prompt=False
command=f'ps -p {pid} > /dev/null; echo $?'
)
check_action.timeout = 60
check_obs = runtime.run_action(check_action)
@@ -242,7 +251,7 @@ def process_instance(
time.sleep(30) # Wait for 30 seconds before checking again
# Read the log file
cat_action = CmdRunAction(command=f'cat {log_file}', keep_prompt=False)
cat_action = CmdRunAction(command=f'cat {log_file}')
cat_action.timeout = 300
cat_obs = runtime.run_action(cat_action)

View File

@@ -15,6 +15,7 @@ from evaluation.utils.shared import (
EvalOutput,
assert_and_raise,
codeact_user_response,
get_metrics,
is_fatal_evaluation_error,
make_metadata,
prepare_dataset,
@@ -148,6 +149,7 @@ def get_config(
codeact_enable_jupyter=False,
codeact_enable_browsing=RUN_WITH_BROWSING,
codeact_enable_llm_editor=False,
condenser=metadata.condenser_config,
)
config.set_agent_config(agent_config)
return config
@@ -282,6 +284,16 @@ def initialize_runtime(
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert_and_raise(obs.exit_code == 0, f'Failed to remove git remotes: {str(obs)}')
action = CmdRunAction(command='which python')
action.timeout = 600
logger.info(action, extra={'msg_type': 'ACTION'})
obs = runtime.run_action(action)
logger.info(obs, extra={'msg_type': 'OBSERVATION'})
assert_and_raise(
obs.exit_code == 0 and 'testbed' in obs.content,
f'Expected to find python interpreter from testbed, but got: {str(obs)}',
)
logger.info('-' * 30)
logger.info('END Runtime Initialization Fn')
logger.info('-' * 30)
@@ -337,8 +349,7 @@ def complete_runtime(
git_patch = None
while n_retries < 5:
action = CmdRunAction(
command=f'git diff --no-color --cached {instance["base_commit"]}',
keep_prompt=False,
command=f'git diff --no-color --cached {instance["base_commit"]}'
)
action.timeout = 600 + 100 * n_retries
logger.info(action, extra={'msg_type': 'ACTION'})
@@ -385,7 +396,7 @@ def process_instance(
if runtime_failure_count > 0:
config.sandbox.remote_runtime_resource_factor = min(
config.sandbox.remote_runtime_resource_factor * (2**runtime_failure_count),
2, # hardcode maximum resource factor to 2
8,
)
logger.warning(
f'This is the second attempt for instance {instance.instance_id}, setting resource factor to {config.sandbox.remote_runtime_resource_factor}'
@@ -439,7 +450,7 @@ def process_instance(
# NOTE: this is NO LONGER the event stream, but an agent history that includes delegate agent's events
histories = [event_to_dict(event) for event in state.history]
metrics = state.metrics.get() if state.metrics else None
metrics = get_metrics(state)
# Save the output
output = EvalOutput(
@@ -535,4 +546,5 @@ if __name__ == '__main__':
args.eval_num_workers,
process_instance,
timeout_seconds=120 * 60, # 2 hour PER instance should be more than enough
max_retries=5,
)

View File

@@ -1,13 +1,20 @@
#!/usr/bin/env python3
import argparse
import os
import pandas as pd
from termcolor import colored
parser = argparse.ArgumentParser(
description='Compare two swe_bench output JSONL files and print the resolved diff'
)
parser.add_argument('input_file_1', type=str)
parser.add_argument('input_file_2', type=str)
parser.add_argument(
'--show-paths',
action='store_true',
help='Show visualization paths for failed instances',
)
args = parser.parse_args()
df1 = pd.read_json(args.input_file_1, orient='records', lines=True)
@@ -58,10 +65,60 @@ df_diff_y_only = df_diff[~df_diff['resolved_x'] & df_diff['resolved_y']].sort_va
print(f'# y resolved but x not={df_diff_y_only.shape[0]}')
print(df_diff_y_only[['instance_id', 'report_x', 'report_y']])
# get instance_id from df_diff_y_only
print('-' * 100)
print('Instances that x resolved but y not:')
print(df_diff_x_only['instance_id'].tolist())
x_only_by_repo = {}
for instance_id in df_diff_x_only['instance_id'].tolist():
repo = instance_id.split('__')[0]
x_only_by_repo.setdefault(repo, []).append(instance_id)
y_only_by_repo = {}
for instance_id in df_diff_y_only['instance_id'].tolist():
repo = instance_id.split('__')[0]
y_only_by_repo.setdefault(repo, []).append(instance_id)
print('-' * 100)
print('Instances that y resolved but x not:')
print(df_diff_y_only['instance_id'].tolist())
print(
colored('Repository comparison (x resolved vs y resolved):', 'cyan', attrs=['bold'])
)
all_repos = sorted(set(list(x_only_by_repo.keys()) + list(y_only_by_repo.keys())))
# Calculate diffs and sort repos by diff magnitude
repo_diffs = []
for repo in all_repos:
x_count = len(x_only_by_repo.get(repo, []))
y_count = len(y_only_by_repo.get(repo, []))
diff = abs(x_count - y_count)
repo_diffs.append((repo, diff))
# Sort by diff (descending) and then by repo name
repo_diffs.sort(key=lambda x: (-x[1], x[0]))
threshold = max(
3, sum(d[1] for d in repo_diffs) / len(repo_diffs) * 1.5 if repo_diffs else 0
)
x_input_file_folder = os.path.join(os.path.dirname(args.input_file_1), 'output.viz')
for repo, diff in repo_diffs:
x_instances = x_only_by_repo.get(repo, [])
y_instances = y_only_by_repo.get(repo, [])
# Determine if this repo has a significant diff
is_significant = diff >= threshold
repo_color = 'red' if is_significant else 'yellow'
print(f"\n{colored(repo, repo_color, attrs=['bold'])}:")
print(colored(f'Difference: {diff} instances!', repo_color, attrs=['bold']))
print(colored(f'X resolved but Y failed: ({len(x_instances)} instances)', 'green'))
if x_instances:
print(' ' + str(x_instances))
print(colored(f'Y resolved but X failed: ({len(y_instances)} instances)', 'red'))
if y_instances:
print(' ' + str(y_instances))
if args.show_paths:
print(
colored(' Visualization path for X failed:', 'cyan', attrs=['bold'])
)
for instance_id in y_instances:
instance_file = os.path.join(
x_input_file_folder, f'false.{instance_id}.md'
)
print(f' {instance_file}')

View File

@@ -20,6 +20,13 @@ output_md_folder = args.oh_output_file.replace('.jsonl', '.viz')
print(f'Converting {args.oh_output_file} to markdown files in {output_md_folder}')
oh_format = pd.read_json(args.oh_output_file, orient='records', lines=True)
swebench_eval_file = args.oh_output_file.replace('.jsonl', '.swebench_eval.jsonl')
if os.path.exists(swebench_eval_file):
eval_output_df = pd.read_json(swebench_eval_file, orient='records', lines=True)
else:
eval_output_df = None
# model name is the folder name of oh_output_file
model_name = os.path.basename(os.path.dirname(args.oh_output_file))
@@ -50,7 +57,7 @@ def convert_history_to_str(history):
return ret
def write_row_to_md_file(row):
def write_row_to_md_file(row, instance_id_to_test_result):
if 'git_patch' in row:
model_patch = row['git_patch']
elif 'test_result' in row and 'git_patch' in row['test_result']:
@@ -58,8 +65,21 @@ def write_row_to_md_file(row):
else:
raise ValueError(f'Row {row} does not have a git_patch')
if 'report' in row:
resolved = row['report'].get('resolved', False)
test_output = None
if row['instance_id'] in instance_id_to_test_result:
report = instance_id_to_test_result[row['instance_id']].get('report', {})
resolved = report.get('resolved', False)
test_output = instance_id_to_test_result[row['instance_id']].get(
'test_output', None
)
elif 'report' in row and row['report'] is not None:
if not isinstance(row['report'], dict):
resolved = None
print(
f'ERROR: Report is not a dict, but a {type(row["report"])}. Row: {row}'
)
else:
resolved = row['report'].get('resolved', False)
else:
resolved = None
@@ -84,5 +104,18 @@ def write_row_to_md_file(row):
f.write('## Model Patch\n')
f.write(f'{process_git_patch(model_patch)}\n')
f.write('## Test Output\n')
f.write(str(test_output))
oh_format.progress_apply(write_row_to_md_file, axis=1)
instance_id_to_test_result = {}
if eval_output_df is not None:
instance_id_to_test_result = (
eval_output_df[['instance_id', 'test_result']]
.set_index('instance_id')['test_result']
.to_dict()
)
oh_format.progress_apply(
write_row_to_md_file, axis=1, instance_id_to_test_result=instance_id_to_test_result
)

View File

@@ -111,6 +111,11 @@ elif os.path.exists(openhands_remote_report_jsonl):
instance_id_to_status[row['instance_id']] = row['test_result']['report']
df['report'] = df.apply(apply_report, axis=1)
report_is_dict = df['report'].apply(lambda x: isinstance(x, dict))
if not report_is_dict.all():
print(df[~report_is_dict])
raise ValueError(f'Report is not a dict, but a {type(row["report"])}')
_n_instances = len(df)
_n_resolved = len(df[df['report'].apply(lambda x: x.get('resolved', False))])
_n_unresolved = _n_instances - _n_resolved

View File

@@ -39,7 +39,7 @@ def get_config(
run_as_openhands=False,
max_budget_per_task=4,
max_iterations=100,
trajectories_path=os.path.join(
save_trajectory_path=os.path.join(
mount_path_on_host, f'traj_{task_short_name}.json'
),
sandbox=SandboxConfig(

View File

@@ -56,6 +56,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -77,6 +77,8 @@ def get_config(
workspace_mount_path=None,
)
config.set_llm_config(metadata.llm_config)
agent_config = config.get_agent_config(metadata.agent_class)
agent_config.use_microagents = False
return config

View File

@@ -8,13 +8,15 @@ from evaluation.integration_tests.tests.base import BaseIntegrationTest, TestRes
from evaluation.utils.shared import (
EvalMetadata,
EvalOutput,
codeact_user_response,
make_metadata,
prepare_dataset,
reset_logger_for_multiprocessing,
run_evaluation,
update_llm_config_for_completions_logging,
)
from evaluation.utils.shared import (
codeact_user_response as fake_user_response,
)
from openhands.controller.state.state import State
from openhands.core.config import (
AgentConfig,
@@ -31,7 +33,8 @@ from openhands.runtime.base import Runtime
from openhands.utils.async_utils import call_async_from_sync
FAKE_RESPONSES = {
'CodeActAgent': codeact_user_response,
'CodeActAgent': fake_user_response,
'DelegatorAgent': fake_user_response,
}
@@ -219,7 +222,7 @@ if __name__ == '__main__':
df = pd.read_json(output_file, lines=True, orient='records')
# record success and reason for failure for the final report
# record success and reason
df['success'] = df['test_result'].apply(lambda x: x['success'])
df['reason'] = df['test_result'].apply(lambda x: x['reason'])
logger.info('-' * 100)
@@ -234,15 +237,27 @@ if __name__ == '__main__':
logger.info('-' * 100)
# record cost for each instance, with 3 decimal places
df['cost'] = df['metrics'].apply(lambda x: round(x['accumulated_cost'], 3))
# we sum up all the "costs" from the metrics array
df['cost'] = df['metrics'].apply(
lambda m: round(sum(c['cost'] for c in m['costs']), 3)
if m and 'costs' in m
else 0.0
)
# capture the top-level error if present, per instance
df['error_message'] = df.get('error', None)
logger.info(f'Total cost: USD {df["cost"].sum():.2f}')
report_file = os.path.join(metadata.eval_output_dir, 'report.md')
with open(report_file, 'w') as f:
f.write(
f'Success rate: {df["success"].mean():.2%} ({df["success"].sum()}/{len(df)})\n'
f'Success rate: {df["success"].mean():.2%}'
f' ({df["success"].sum()}/{len(df)})\n'
)
f.write(f'\nTotal cost: USD {df["cost"].sum():.2f}\n')
f.write(
df[['instance_id', 'success', 'reason', 'cost']].to_markdown(index=False)
df[
['instance_id', 'success', 'reason', 'cost', 'error_message']
].to_markdown(index=False)
)

View File

@@ -7,8 +7,9 @@ MODEL_CONFIG=$1
COMMIT_HASH=$2
AGENT=$3
EVAL_LIMIT=$4
NUM_WORKERS=$5
EVAL_IDS=$6
MAX_ITERATIONS=$5
NUM_WORKERS=$6
EVAL_IDS=$7
if [ -z "$NUM_WORKERS" ]; then
NUM_WORKERS=1
@@ -43,7 +44,7 @@ fi
COMMAND="poetry run python evaluation/integration_tests/run_infer.py \
--agent-cls $AGENT \
--llm-config $MODEL_CONFIG \
--max-iterations 10 \
--max-iterations ${MAX_ITERATIONS:-10} \
--eval-num-workers $NUM_WORKERS \
--eval-note $EVAL_NOTE"

View File

@@ -24,7 +24,7 @@ class Test(BaseIntegrationTest):
@classmethod
def verify_result(cls, runtime: Runtime, histories: list[Event]) -> TestResult:
# check if the file /workspace/bad.txt has been fixed
action = CmdRunAction(command='cat /workspace/bad.txt', keep_prompt=False)
action = CmdRunAction(command='cat /workspace/bad.txt')
obs = runtime.run_action(action)
if obs.exit_code != 0:
return TestResult(

View File

@@ -10,14 +10,14 @@ class Test(BaseIntegrationTest):
@classmethod
def initialize_runtime(cls, runtime: Runtime) -> None:
action = CmdRunAction(command='mkdir -p /workspace', keep_prompt=False)
action = CmdRunAction(command='mkdir -p /workspace')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
@classmethod
def verify_result(cls, runtime: Runtime, histories: list[Event]) -> TestResult:
# check if the file /workspace/hello.sh exists
action = CmdRunAction(command='cat /workspace/hello.sh', keep_prompt=False)
action = CmdRunAction(command='cat /workspace/hello.sh')
obs = runtime.run_action(action)
if obs.exit_code != 0:
return TestResult(
@@ -26,7 +26,7 @@ class Test(BaseIntegrationTest):
)
# execute the script
action = CmdRunAction(command='bash /workspace/hello.sh', keep_prompt=False)
action = CmdRunAction(command='bash /workspace/hello.sh')
obs = runtime.run_action(action)
if obs.exit_code != 0:
return TestResult(

View File

@@ -10,14 +10,14 @@ class Test(BaseIntegrationTest):
@classmethod
def initialize_runtime(cls, runtime: Runtime) -> None:
action = CmdRunAction(command='mkdir -p /workspace', keep_prompt=False)
action = CmdRunAction(command='mkdir -p /workspace')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
@classmethod
def verify_result(cls, runtime: Runtime, histories: list[Event]) -> TestResult:
# check if the file /workspace/hello.sh exists
action = CmdRunAction(command='cat /workspace/test.txt', keep_prompt=False)
action = CmdRunAction(command='cat /workspace/test.txt')
obs = runtime.run_action(action)
if obs.exit_code != 0:
return TestResult(
@@ -26,7 +26,7 @@ class Test(BaseIntegrationTest):
)
# execute the script
action = CmdRunAction(command='cat /workspace/test.txt', keep_prompt=False)
action = CmdRunAction(command='cat /workspace/test.txt')
obs = runtime.run_action(action)
if obs.exit_code != 0:

View File

@@ -10,31 +10,29 @@ class Test(BaseIntegrationTest):
@classmethod
def initialize_runtime(cls, runtime: Runtime) -> None:
action = CmdRunAction(command='mkdir -p /workspace', keep_prompt=False)
action = CmdRunAction(command='mkdir -p /workspace')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
# git init
action = CmdRunAction(command='git init', keep_prompt=False)
action = CmdRunAction(command='git init')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
# create README.md
action = CmdRunAction(
command='echo \'print("hello world")\' > hello.py', keep_prompt=False
)
action = CmdRunAction(command='echo \'print("hello world")\' > hello.py')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
# git add README.md
action = CmdRunAction(command='git add hello.py', keep_prompt=False)
action = CmdRunAction(command='git add hello.py')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
@classmethod
def verify_result(cls, runtime: Runtime, histories: list[Event]) -> TestResult:
# check if the file /workspace/hello.py exists
action = CmdRunAction(command='cat /workspace/hello.py', keep_prompt=False)
action = CmdRunAction(command='cat /workspace/hello.py')
obs = runtime.run_action(action)
if obs.exit_code != 0:
return TestResult(
@@ -43,7 +41,7 @@ class Test(BaseIntegrationTest):
)
# check if the staging area is empty
action = CmdRunAction(command='git status', keep_prompt=False)
action = CmdRunAction(command='git status')
obs = runtime.run_action(action)
if obs.exit_code != 0:
return TestResult(

View File

@@ -83,11 +83,11 @@ class Test(BaseIntegrationTest):
@classmethod
def initialize_runtime(cls, runtime: Runtime) -> None:
action = CmdRunAction(command='mkdir -p /workspace', keep_prompt=False)
action = CmdRunAction(command='mkdir -p /workspace')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
action = CmdRunAction(command='mkdir -p /tmp/server', keep_prompt=False)
action = CmdRunAction(command='mkdir -p /tmp/server')
obs = runtime.run_action(action)
assert_and_raise(obs.exit_code == 0, f'Failed to run command: {obs.content}')
@@ -101,8 +101,7 @@ class Test(BaseIntegrationTest):
# create README.md
action = CmdRunAction(
command='cd /tmp/server && nohup python3 -m http.server 8000 &',
keep_prompt=False,
command='cd /tmp/server && nohup python3 -m http.server 8000 &'
)
obs = runtime.run_action(action)

View File

@@ -17,6 +17,10 @@ from tqdm import tqdm
from openhands.controller.state.state import State
from openhands.core.config import LLMConfig
from openhands.core.config.condenser_config import (
CondenserConfig,
NoOpCondenserConfig,
)
from openhands.core.exceptions import (
AgentRuntimeBuildError,
AgentRuntimeDisconnectedError,
@@ -33,6 +37,7 @@ from openhands.events.action.message import MessageAction
from openhands.events.event import Event
from openhands.events.serialization.event import event_to_dict
from openhands.events.utils import get_pairs_from_events
from openhands.memory.condenser import get_condensation_metadata
class EvalMetadata(BaseModel):
@@ -45,11 +50,17 @@ class EvalMetadata(BaseModel):
dataset: str | None = None
data_split: str | None = None
details: dict[str, Any] | None = None
condenser_config: CondenserConfig | None = None
def model_dump(self, *args, **kwargs):
dumped_dict = super().model_dump(*args, **kwargs)
# avoid leaking sensitive information
dumped_dict['llm_config'] = self.llm_config.to_safe_dict()
if hasattr(self.condenser_config, 'llm_config'):
dumped_dict['condenser_config']['llm_config'] = (
self.condenser_config.llm_config.to_safe_dict()
)
return dumped_dict
def model_dump_json(self, *args, **kwargs):
@@ -57,6 +68,11 @@ class EvalMetadata(BaseModel):
dumped_dict = json.loads(dumped)
# avoid leaking sensitive information
dumped_dict['llm_config'] = self.llm_config.to_safe_dict()
if hasattr(self.condenser_config, 'llm_config'):
dumped_dict['condenser_config']['llm_config'] = (
self.condenser_config.llm_config.to_safe_dict()
)
logger.debug(f'Dumped metadata: {dumped_dict}')
return json.dumps(dumped_dict)
@@ -192,6 +208,7 @@ def make_metadata(
eval_output_dir: str,
data_split: str | None = None,
details: dict[str, Any] | None = None,
condenser_config: CondenserConfig | None = None,
) -> EvalMetadata:
model_name = llm_config.model.split('/')[-1]
model_path = model_name.replace(':', '_').replace('@', '-')
@@ -222,6 +239,9 @@ def make_metadata(
dataset=dataset_name,
data_split=data_split,
details=details,
condenser_config=condenser_config
if condenser_config
else NoOpCondenserConfig(),
)
metadata_json = metadata.model_dump_json()
logger.info(f'Metadata: {metadata_json}')
@@ -376,7 +396,12 @@ def _process_instance_wrapper(
+ '\n'
)
if isinstance(
e, (AgentRuntimeDisconnectedError, AgentRuntimeUnavailableError)
e,
(
AgentRuntimeDisconnectedError,
AgentRuntimeUnavailableError,
AgentRuntimeNotFoundError,
),
):
runtime_failure_count += 1
msg += f'Runtime disconnected error detected for instance {instance.instance_id}, runtime failure count: {runtime_failure_count}'
@@ -546,3 +571,10 @@ def is_fatal_evaluation_error(error: str | None) -> bool:
return True
return False
def get_metrics(state: State) -> dict[str, Any]:
"""Extract metrics from the state."""
metrics = state.metrics.get() if state.metrics else {}
metrics['condenser'] = get_condensation_metadata(state)
return metrics

View File

@@ -73,6 +73,7 @@
}
}
],
"react/prop-types": "off",
"react/no-array-index-key": "off",
"react-hooks/exhaustive-deps": "off",
"import/no-extraneous-dependencies": "off",

View File

@@ -1,47 +0,0 @@
import { describe, expect, it, vi } from "vitest";
import { retrieveLatestGitHubCommit } from "../../src/api/github";
describe("retrieveLatestGitHubCommit", () => {
const { githubGetMock } = vi.hoisted(() => ({
githubGetMock: vi.fn(),
}));
vi.mock("../../src/api/github-axios-instance", () => ({
github: {
get: githubGetMock,
},
}));
it("should return the latest commit when repository has commits", async () => {
const mockCommit = {
sha: "123abc",
commit: {
message: "Initial commit",
},
};
githubGetMock.mockResolvedValueOnce({
data: [mockCommit],
});
const result = await retrieveLatestGitHubCommit("user/repo");
expect(result).toEqual(mockCommit);
});
it("should return null when repository is empty", async () => {
const error = new Error("Repository is empty");
(error as any).response = { status: 409 };
githubGetMock.mockRejectedValueOnce(error);
const result = await retrieveLatestGitHubCommit("user/empty-repo");
expect(result).toBeNull();
});
it("should throw error for other error cases", async () => {
const error = new Error("Network error");
(error as any).response = { status: 500 };
githubGetMock.mockRejectedValueOnce(error);
await expect(retrieveLatestGitHubCommit("user/repo")).rejects.toThrow();
});
});

View File

@@ -1,11 +1,10 @@
import { describe, it, expect, afterEach, vi } from "vitest";
import * as router from "react-router";
// Mock useParams before importing components
vi.mock("react-router", async () => {
const actual = await vi.importActual("react-router");
return {
...actual as object,
...(actual as object),
useParams: () => ({ conversationId: "test-conversation-id" }),
};
});
@@ -14,7 +13,7 @@ vi.mock("react-router", async () => {
vi.mock("react-i18next", async () => {
const actual = await vi.importActual("react-i18next");
return {
...actual as object,
...(actual as object),
useTranslation: () => ({
t: (key: string) => key,
i18n: {
@@ -28,7 +27,6 @@ import { screen } from "@testing-library/react";
import { renderWithProviders } from "../../test-utils";
import { BrowserPanel } from "#/components/features/browser/browser";
describe("Browser", () => {
afterEach(() => {
vi.clearAllMocks();
@@ -45,7 +43,7 @@ describe("Browser", () => {
});
// i18n empty message key
expect(screen.getByText("BROWSER$EMPTY_MESSAGE")).toBeInTheDocument();
expect(screen.getByText("BROWSER$NO_PAGE_LOADED")).toBeInTheDocument();
});
it("renders the url and a screenshot", () => {

View File

@@ -84,12 +84,10 @@ describe("ChatInput", () => {
expect(onSubmitMock).not.toHaveBeenCalled();
});
it("should render a placeholder", () => {
render(
<ChatInput placeholder="Enter your message" onSubmit={onSubmitMock} />,
);
it("should render a placeholder with translation key", () => {
render(<ChatInput onSubmit={onSubmitMock} />);
const textarea = screen.getByPlaceholderText("Enter your message");
const textarea = screen.getByPlaceholderText("SUGGESTIONS$WHAT_TO_BUILD");
expect(textarea).toBeInTheDocument();
});
@@ -218,4 +216,30 @@ describe("ChatInput", () => {
// Verify image paste was handled
expect(onImagePaste).toHaveBeenCalledWith([file]);
});
it("should not submit when Enter is pressed during IME composition", async () => {
const user = userEvent.setup();
render(<ChatInput onSubmit={onSubmitMock} />);
const textarea = screen.getByRole("textbox");
await user.type(textarea, "こんにちは");
// Simulate Enter during IME composition
fireEvent.keyDown(textarea, {
key: "Enter",
isComposing: true,
nativeEvent: { isComposing: true },
});
expect(onSubmitMock).not.toHaveBeenCalled();
// Simulate normal Enter after composition is done
fireEvent.keyDown(textarea, {
key: "Enter",
isComposing: false,
nativeEvent: { isComposing: false },
});
expect(onSubmitMock).toHaveBeenCalledWith("こんにちは");
});
});

View File

@@ -2,36 +2,42 @@ import { describe, expect, it } from "vitest";
import { screen } from "@testing-library/react";
import { renderWithProviders } from "test-utils";
import { ExpandableMessage } from "#/components/features/chat/expandable-message";
import { vi } from 'vitest';
import { vi } from "vitest";
vi.mock('react-i18next', async () => {
const actual = await vi.importActual('react-i18next');
vi.mock("react-i18next", async () => {
const actual = await vi.importActual("react-i18next");
return {
...actual,
useTranslation: () => ({
t: (key:string) => key,
t: (key: string) => key,
i18n: {
changeLanguage: () => new Promise(() => {}),
language: 'en',
language: "en",
exists: () => true,
},
}),
}
};
});
describe("ExpandableMessage", () => {
it("should render with neutral border for non-action messages", () => {
renderWithProviders(<ExpandableMessage message="Hello" type="thought" />);
const element = screen.getByText("Hello");
const container = element.closest("div.flex.gap-2.items-center.justify-start");
const container = element.closest(
"div.flex.gap-2.items-center.justify-start",
);
expect(container).toHaveClass("border-neutral-300");
expect(screen.queryByTestId("status-icon")).not.toBeInTheDocument();
});
it("should render with neutral border for error messages", () => {
renderWithProviders(<ExpandableMessage message="Error occurred" type="error" />);
renderWithProviders(
<ExpandableMessage message="Error occurred" type="error" />,
);
const element = screen.getByText("Error occurred");
const container = element.closest("div.flex.gap-2.items-center.justify-start");
const container = element.closest(
"div.flex.gap-2.items-center.justify-start",
);
expect(container).toHaveClass("border-danger");
expect(screen.queryByTestId("status-icon")).not.toBeInTheDocument();
});
@@ -43,10 +49,12 @@ describe("ExpandableMessage", () => {
message="Command executed successfully"
type="action"
success={true}
/>
/>,
);
const element = screen.getByText("OBSERVATION_MESSAGE$RUN");
const container = element.closest("div.flex.gap-2.items-center.justify-start");
const container = element.closest(
"div.flex.gap-2.items-center.justify-start",
);
expect(container).toHaveClass("border-neutral-300");
const icon = screen.getByTestId("status-icon");
expect(icon).toHaveClass("fill-success");
@@ -59,10 +67,12 @@ describe("ExpandableMessage", () => {
message="Command failed"
type="action"
success={false}
/>
/>,
);
const element = screen.getByText("OBSERVATION_MESSAGE$RUN");
const container = element.closest("div.flex.gap-2.items-center.justify-start");
const container = element.closest(
"div.flex.gap-2.items-center.justify-start",
);
expect(container).toHaveClass("border-neutral-300");
const icon = screen.getByTestId("status-icon");
expect(icon).toHaveClass("fill-danger");
@@ -74,10 +84,12 @@ describe("ExpandableMessage", () => {
id="OBSERVATION_MESSAGE$RUN"
message="Running command"
type="action"
/>
/>,
);
const element = screen.getByText("OBSERVATION_MESSAGE$RUN");
const container = element.closest("div.flex.gap-2.items-center.justify-start");
const container = element.closest(
"div.flex.gap-2.items-center.justify-start",
);
expect(container).toHaveClass("border-neutral-300");
expect(screen.queryByTestId("status-icon")).not.toBeInTheDocument();
});

View File

@@ -3,11 +3,13 @@ import { afterEach, describe, expect, it, test, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { formatTimeDelta } from "#/utils/format-time-delta";
import { ConversationCard } from "#/components/features/conversation-panel/conversation-card";
import { clickOnEditButton } from "./utils";
describe("ConversationCard", () => {
const onClick = vi.fn();
const onDelete = vi.fn();
const onChangeTitle = vi.fn();
const onDownloadWorkspace = vi.fn();
afterEach(() => {
vi.clearAllMocks();
@@ -17,11 +19,11 @@ describe("ConversationCard", () => {
render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
const expectedDate = `${formatTimeDelta(new Date("2021-10-01T12:00:00Z"))} ago`;
@@ -33,53 +35,34 @@ describe("ConversationCard", () => {
within(card).getByText(expectedDate);
});
it("should render the repo if available", () => {
it("should render the selectedRepository if available", () => {
const { rerender } = render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
expect(
screen.queryByTestId("conversation-card-repo"),
screen.queryByTestId("conversation-card-selected-repository"),
).not.toBeInTheDocument();
rerender(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo="org/repo"
lastUpdated="2021-10-01T12:00:00Z"
isActive
title="Conversation 1"
selectedRepository="org/selectedRepository"
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
screen.getByTestId("conversation-card-repo");
});
it("should call onClick when the card is clicked", async () => {
const user = userEvent.setup();
render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
/>,
);
const card = screen.getByTestId("conversation-card");
await user.click(card);
expect(onClick).toHaveBeenCalled();
screen.getByTestId("conversation-card-selected-repository");
});
it("should toggle a context menu when clicking the ellipsis button", async () => {
@@ -87,11 +70,11 @@ describe("ConversationCard", () => {
render(
<ConversationCard
onDelete={onDelete}
onClick={onClick}
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
@@ -111,12 +94,12 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
@@ -131,21 +114,23 @@ describe("ConversationCard", () => {
expect(onDelete).toHaveBeenCalled();
});
test("clicking the repo should not trigger the onClick handler", async () => {
test("clicking the selectedRepository should not trigger the onClick handler", async () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo="org/repo"
lastUpdated="2021-10-01T12:00:00Z"
title="Conversation 1"
selectedRepository="org/selectedRepository"
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
const repo = screen.getByTestId("conversation-card-repo");
await user.click(repo);
const selectedRepository = screen.getByTestId(
"conversation-card-selected-repository",
);
await user.click(selectedRepository);
expect(onClick).not.toHaveBeenCalled();
});
@@ -154,16 +139,24 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
isActive
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
onChangeTitle={onChangeTitle}
/>,
);
const title = screen.getByTestId("conversation-card-title");
expect(title).toBeDisabled();
await clickOnEditButton(user);
expect(title).toBeEnabled();
expect(screen.queryByTestId("context-menu")).not.toBeInTheDocument();
// expect to be focused
expect(document.activeElement).toBe(title);
await user.clear(title);
await user.type(title, "New Conversation Name ");
@@ -171,21 +164,24 @@ describe("ConversationCard", () => {
expect(onChangeTitle).toHaveBeenCalledWith("New Conversation Name");
expect(title).toHaveValue("New Conversation Name");
expect(title).toBeDisabled();
});
it("should reset title and not call onChangeTitle when the title is empty", async () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
await clickOnEditButton(user);
const title = screen.getByTestId("conversation-card-title");
await user.clear(title);
@@ -199,12 +195,12 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
@@ -218,12 +214,12 @@ describe("ConversationCard", () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
@@ -238,37 +234,151 @@ describe("ConversationCard", () => {
expect(onClick).not.toHaveBeenCalled();
});
it("should call onDownloadWorkspace when the download button is clicked", async () => {
const user = userEvent.setup();
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
onChangeTitle={onChangeTitle}
onDownloadWorkspace={onDownloadWorkspace}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
const ellipsisButton = screen.getByTestId("ellipsis-button");
await user.click(ellipsisButton);
const menu = screen.getByTestId("context-menu");
const downloadButton = within(menu).getByTestId("download-button");
await user.click(downloadButton);
expect(onDownloadWorkspace).toHaveBeenCalled();
});
it("should not display the edit or delete options if the handler is not provided", async () => {
const user = userEvent.setup();
const { rerender } = render(
<ConversationCard
onClick={onClick}
onChangeTitle={onChangeTitle}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
const ellipsisButton = screen.getByTestId("ellipsis-button");
await user.click(ellipsisButton);
expect(screen.queryByTestId("edit-button")).toBeInTheDocument();
expect(screen.queryByTestId("delete-button")).not.toBeInTheDocument();
// toggle to hide the context menu
await user.click(ellipsisButton);
rerender(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
await user.click(ellipsisButton);
expect(screen.queryByTestId("edit-button")).not.toBeInTheDocument();
expect(screen.queryByTestId("delete-button")).toBeInTheDocument();
});
it("should not render the ellipsis button if there are no actions", () => {
const { rerender } = render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
onChangeTitle={onChangeTitle}
onDownloadWorkspace={onDownloadWorkspace}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
expect(screen.getByTestId("ellipsis-button")).toBeInTheDocument();
rerender(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
onDownloadWorkspace={onDownloadWorkspace}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
expect(screen.getByTestId("ellipsis-button")).toBeInTheDocument();
rerender(
<ConversationCard
onClick={onClick}
onDownloadWorkspace={onDownloadWorkspace}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
expect(screen.queryByTestId("ellipsis-button")).toBeInTheDocument();
rerender(
<ConversationCard
onClick={onClick}
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
expect(screen.queryByTestId("ellipsis-button")).not.toBeInTheDocument();
});
describe("state indicator", () => {
it("should render the 'cold' indicator by default", () => {
it("should render the 'STOPPED' indicator by default", () => {
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
/>,
);
screen.getByTestId("cold-indicator");
screen.getByTestId("STOPPED-indicator");
});
it("should render the other indicators when provided", () => {
render(
<ConversationCard
onClick={onClick}
onDelete={onDelete}
isActive
onChangeTitle={onChangeTitle}
name="Conversation 1"
repo={null}
lastUpdated="2021-10-01T12:00:00Z"
state="warm"
title="Conversation 1"
selectedRepository={null}
lastUpdatedAt="2021-10-01T12:00:00Z"
status="RUNNING"
/>,
);
expect(screen.queryByTestId("cold-indicator")).not.toBeInTheDocument();
screen.getByTestId("warm-indicator");
expect(screen.queryByTestId("STOPPED-indicator")).not.toBeInTheDocument();
screen.getByTestId("RUNNING-indicator");
});
});
});

View File

@@ -6,15 +6,23 @@ import {
QueryClientConfig,
} from "@tanstack/react-query";
import userEvent from "@testing-library/user-event";
import { createRoutesStub } from "react-router";
import { ConversationPanel } from "#/components/features/conversation-panel/conversation-panel";
import OpenHands from "#/api/open-hands";
import { AuthProvider } from "#/context/auth-context";
import { clickOnEditButton } from "./utils";
describe("ConversationPanel", () => {
const onCloseMock = vi.fn();
const RouterStub = createRoutesStub([
{
Component: () => <ConversationPanel onClose={onCloseMock} />,
path: "/",
},
]);
const renderConversationPanel = (config?: QueryClientConfig) =>
render(<ConversationPanel onClose={onCloseMock} />, {
render(<RouterStub />, {
wrapper: ({ children }) => (
<AuthProvider>
<QueryClientProvider client={new QueryClient(config)}>
@@ -52,6 +60,8 @@ describe("ConversationPanel", () => {
renderConversationPanel();
const cards = await screen.findAllByTestId("conversation-card");
// NOTE that we filter out conversations that don't have a created_at property
// (mock data has 4 conversations, but only 3 have a created_at property)
expect(cards).toHaveLength(3);
});
@@ -61,7 +71,7 @@ describe("ConversationPanel", () => {
renderConversationPanel();
const emptyState = await screen.findByText("No conversations found");
const emptyState = await screen.findByText("CONVERSATION$NO_CONVERSATIONS");
expect(emptyState).toBeInTheDocument();
});
@@ -169,13 +179,15 @@ describe("ConversationPanel", () => {
const cards = await screen.findAllByTestId("conversation-card");
const title = within(cards[0]).getByTestId("conversation-card-title");
await clickOnEditButton(user);
await user.clear(title);
await user.type(title, "Conversation 1 Renamed");
await user.tab();
// Ensure the conversation is renamed
expect(updateUserConversationSpy).toHaveBeenCalledWith("3", {
name: "Conversation 1 Renamed",
title: "Conversation 1 Renamed",
});
});
@@ -196,6 +208,8 @@ describe("ConversationPanel", () => {
// Ensure the conversation is not renamed
expect(updateUserConversationSpy).not.toHaveBeenCalled();
await clickOnEditButton(user);
await user.type(title, "Conversation 1");
await user.click(title);
await user.tab();
@@ -217,51 +231,4 @@ describe("ConversationPanel", () => {
expect(onCloseMock).toHaveBeenCalledOnce();
});
describe("New Conversation Button", () => {
it("should display a confirmation modal when clicking", async () => {
const user = userEvent.setup();
renderConversationPanel();
expect(
screen.queryByTestId("confirm-new-conversation-modal"),
).not.toBeInTheDocument();
const newProjectButton = screen.getByTestId("new-conversation-button");
await user.click(newProjectButton);
const modal = screen.getByTestId("confirm-new-conversation-modal");
expect(modal).toBeInTheDocument();
});
it("should call endSession and close panel after confirming", async () => {
const user = userEvent.setup();
renderConversationPanel();
const newProjectButton = screen.getByTestId("new-conversation-button");
await user.click(newProjectButton);
const confirmButton = screen.getByText("Confirm");
await user.click(confirmButton);
expect(endSessionMock).toHaveBeenCalledOnce();
expect(onCloseMock).toHaveBeenCalledOnce();
});
it("should close the modal when cancelling", async () => {
const user = userEvent.setup();
renderConversationPanel();
const newProjectButton = screen.getByTestId("new-conversation-button");
await user.click(newProjectButton);
const cancelButton = screen.getByText("Cancel");
await user.click(cancelButton);
expect(endSessionMock).not.toHaveBeenCalled();
expect(
screen.queryByTestId("confirm-new-conversation-modal"),
).not.toBeInTheDocument();
});
});
});

View File

@@ -0,0 +1,12 @@
import { screen, within } from "@testing-library/react";
import { UserEvent } from "@testing-library/user-event";
export const clickOnEditButton = async (user: UserEvent) => {
const ellipsisButton = screen.getByTestId("ellipsis-button");
await user.click(ellipsisButton);
const menu = screen.getByTestId("context-menu");
const editButton = within(menu).getByTestId("edit-button");
await user.click(editButton);
};

View File

@@ -0,0 +1,78 @@
import { screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "test-utils";
import { GitHubRepositorySelector } from "#/components/features/github/github-repo-selector";
import OpenHands from "#/api/open-hands";
describe("GitHubRepositorySelector", () => {
const onInputChangeMock = vi.fn();
const onSelectMock = vi.fn();
it("should render the search input", () => {
renderWithProviders(
<GitHubRepositorySelector
onInputChange={onInputChangeMock}
onSelect={onSelectMock}
publicRepositories={[]}
userRepositories={[]}
/>,
);
expect(
screen.getByPlaceholderText("LANDING$SELECT_REPO"),
).toBeInTheDocument();
});
it("should show the GitHub login button in OSS mode", () => {
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
getConfigSpy.mockResolvedValue({
APP_MODE: "oss",
APP_SLUG: "openhands",
GITHUB_CLIENT_ID: "test-client-id",
POSTHOG_CLIENT_KEY: "test-posthog-key",
});
renderWithProviders(
<GitHubRepositorySelector
onInputChange={onInputChangeMock}
onSelect={onSelectMock}
publicRepositories={[]}
userRepositories={[]}
/>,
);
expect(screen.getByTestId("github-repo-selector")).toBeInTheDocument();
});
it("should show the search results", () => {
const mockSearchedRepos = [
{
id: 1,
full_name: "test/repo1",
stargazers_count: 100,
},
{
id: 2,
full_name: "test/repo2",
stargazers_count: 200,
},
];
const searchPublicRepositoriesSpy = vi.spyOn(
OpenHands,
"searchGitHubRepositories",
);
searchPublicRepositoriesSpy.mockResolvedValue(mockSearchedRepos);
renderWithProviders(
<GitHubRepositorySelector
onInputChange={onInputChangeMock}
onSelect={onSelectMock}
publicRepositories={[]}
userRepositories={[]}
/>,
);
expect(screen.getByTestId("github-repo-selector")).toBeInTheDocument();
});
});

Some files were not shown because too many files have changed in this diff Show More