@elizaos/sweagent-root 2.0.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +270 -0
- package/package.json +71 -0
- package/python/LICENSE +21 -0
- package/python/config/README.md +15 -0
- package/python/config/bash_only.yaml +222 -0
- package/python/config/benchmarks/250212_sweagent_heavy_sbl.yaml +188 -0
- package/python/config/benchmarks/250225_anthropic_filemap_simple_review.yaml +75 -0
- package/python/config/benchmarks/250522_anthropic_filemap_simple_review.yaml +92 -0
- package/python/config/benchmarks/250526_anthropic_filemap_simple_review_sbl.yaml +93 -0
- package/python/config/benchmarks/anthropic_filemap_multilingual.yaml +66 -0
- package/python/config/coding_challenge.yaml +104 -0
- package/python/config/default.yaml +69 -0
- package/python/config/default_backticks.yaml +69 -0
- package/python/config/default_mm_no_images.yaml +82 -0
- package/python/config/default_mm_with_images.yaml +83 -0
- package/python/config/demo/default.yaml +80 -0
- package/python/config/demo/no_instructions.yaml +69 -0
- package/python/config/demo/only_bash.yaml +60 -0
- package/python/config/exotic/default_shell.yaml +52 -0
- package/python/config/exotic/windowed_replace.yaml +125 -0
- package/python/config/exotic/windowed_replace_late_repro.yaml +127 -0
- package/python/config/human/human.yaml +24 -0
- package/python/config/human/human_demo.yaml +52 -0
- package/python/config/sweagent_0_7/07.yaml +101 -0
- package/python/config/sweagent_0_7/07_fcalling.yaml +100 -0
- package/python/config/sweagent_0_7/07_from_url.yaml +114 -0
- package/python/config/sweagent_0_7/07_thought_action.yaml +102 -0
- package/python/config/sweagent_0_7/07_thought_action_xml.yaml +96 -0
- package/python/mlc_config.json +44 -0
- package/python/pyproject.toml +262 -0
- package/python/sweagent/__init__.py +114 -0
- package/python/sweagent/__main__.py +4 -0
- package/python/sweagent/agent/__init__.py +0 -0
- package/python/sweagent/agent/action_sampler.py +317 -0
- package/python/sweagent/agent/agents.py +1294 -0
- package/python/sweagent/agent/extra/shell_agent.py +106 -0
- package/python/sweagent/agent/history_processors.py +399 -0
- package/python/sweagent/agent/hooks/__init__.py +0 -0
- package/python/sweagent/agent/hooks/abstract.py +139 -0
- package/python/sweagent/agent/hooks/status.py +34 -0
- package/python/sweagent/agent/models.py +896 -0
- package/python/sweagent/agent/problem_statement.py +312 -0
- package/python/sweagent/agent/reviewer.py +664 -0
- package/python/sweagent/environment/__init__.py +0 -0
- package/python/sweagent/environment/hooks/__init__.py +0 -0
- package/python/sweagent/environment/hooks/abstract.py +60 -0
- package/python/sweagent/environment/hooks/status.py +28 -0
- package/python/sweagent/environment/repo.py +219 -0
- package/python/sweagent/environment/swe_env.py +276 -0
- package/python/sweagent/exceptions.py +54 -0
- package/python/sweagent/inspector/README.md +6 -0
- package/python/sweagent/inspector/__init__.py +0 -0
- package/python/sweagent/inspector/favicon.ico +0 -0
- package/python/sweagent/inspector/fileViewer.js +354 -0
- package/python/sweagent/inspector/icons/computer.png +0 -0
- package/python/sweagent/inspector/icons/edit_icon.svg +11 -0
- package/python/sweagent/inspector/icons/swe-agent-logo-50.png +0 -0
- package/python/sweagent/inspector/icons/swellama_blue.png +0 -0
- package/python/sweagent/inspector/icons/swellama_brown.png +0 -0
- package/python/sweagent/inspector/icons/swellama_grey.png +0 -0
- package/python/sweagent/inspector/icons/swellama_tan.png +0 -0
- package/python/sweagent/inspector/index.html +25 -0
- package/python/sweagent/inspector/server.py +354 -0
- package/python/sweagent/inspector/static.py +169 -0
- package/python/sweagent/inspector/style.css +454 -0
- package/python/sweagent/run/__init__.py +0 -0
- package/python/sweagent/run/_progress.py +158 -0
- package/python/sweagent/run/batch_instances.py +419 -0
- package/python/sweagent/run/common.py +387 -0
- package/python/sweagent/run/compare_runs.py +123 -0
- package/python/sweagent/run/extract_pred.py +19 -0
- package/python/sweagent/run/hooks/__init__.py +0 -0
- package/python/sweagent/run/hooks/abstract.py +67 -0
- package/python/sweagent/run/hooks/apply_patch.py +106 -0
- package/python/sweagent/run/hooks/open_pr.py +244 -0
- package/python/sweagent/run/hooks/swe_bench_evaluate.py +113 -0
- package/python/sweagent/run/inspector_cli.py +493 -0
- package/python/sweagent/run/merge_predictions.py +64 -0
- package/python/sweagent/run/quick_stats.py +96 -0
- package/python/sweagent/run/remove_unfinished.py +63 -0
- package/python/sweagent/run/rich_test.py +91 -0
- package/python/sweagent/run/run.py +147 -0
- package/python/sweagent/run/run_batch.py +442 -0
- package/python/sweagent/run/run_replay.py +219 -0
- package/python/sweagent/run/run_shell.py +155 -0
- package/python/sweagent/run/run_single.py +225 -0
- package/python/sweagent/run/run_traj_to_demo.py +85 -0
- package/python/sweagent/tools/__init__.py +0 -0
- package/python/sweagent/tools/bundle.py +57 -0
- package/python/sweagent/tools/commands.py +220 -0
- package/python/sweagent/tools/parsing.py +619 -0
- package/python/sweagent/tools/tools.py +430 -0
- package/python/sweagent/tools/utils.py +108 -0
- package/python/sweagent/types.py +102 -0
- package/python/sweagent/utils/__init__.py +0 -0
- package/python/sweagent/utils/config.py +80 -0
- package/python/sweagent/utils/files.py +27 -0
- package/python/sweagent/utils/github.py +118 -0
- package/python/sweagent/utils/jinja_warnings.py +14 -0
- package/python/sweagent/utils/log.py +175 -0
- package/python/sweagent/utils/patch_formatter.py +152 -0
- package/python/sweagent/utils/serialization.py +45 -0
- package/python/tests/__init__.py +0 -0
- package/python/tests/conftest.py +191 -0
- package/python/tests/test_agent.py +258 -0
- package/python/tests/test_batch_instance.py +43 -0
- package/python/tests/test_commands/_interactive_dummy.py +35 -0
- package/python/tests/test_commands/interactive_dummy_wrapper.sh +29 -0
- package/python/tests/test_data/config_files/dummy_interactive.yaml +62 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/Dockerfile +20 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/README.md +13 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/challenge.json +12 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/customrandom.c +50 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/docker-compose.yml +14 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/release +0 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/server +0 -0
- package/python/tests/test_data/data_sources/ctf/crypto/Katy/solver.py +12 -0
- package/python/tests/test_data/data_sources/ctf/forensics/flash/README.md +16 -0
- package/python/tests/test_data/data_sources/ctf/forensics/flash/challenge.json +9 -0
- package/python/tests/test_data/data_sources/ctf/forensics/flash/flash_c8429a430278283c0e571baebca3d139.zip +0 -0
- package/python/tests/test_data/data_sources/ctf/misc/networking_1/README.md +15 -0
- package/python/tests/test_data/data_sources/ctf/misc/networking_1/challenge.json +10 -0
- package/python/tests/test_data/data_sources/ctf/misc/networking_1/networking.pcap +0 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/Dockerfile +28 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/README.md +14 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/challenge.json +14 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/docker-compose.yml +14 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/flag.txt +1 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/warmup +0 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/warmup.c +26 -0
- package/python/tests/test_data/data_sources/ctf/pwn/warmup/warmup.py +9 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/README.md +14 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/challenge.json +8 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/rock +0 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/rock.cpp +167 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/solution.cpp +24 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/test_solver/solution.py +6 -0
- package/python/tests/test_data/data_sources/ctf/rev/rock/test_solver/test.sh +10 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/000-default.conf +18 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/Dockerfile +20 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/cgi/file.pl +38 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/cgi/forms.pl +40 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/cgi/hello.pl +11 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/challenge.json +12 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/docker-compose.yml +14 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/flag +1 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/index.html +11 -0
- package/python/tests/test_data/data_sources/ctf/web/i_got_id_demo/solution.txt +1 -0
- package/python/tests/test_data/data_sources/debug_20240322.json +1 -0
- package/python/tests/test_data/data_sources/expert_instances.yaml +16 -0
- package/python/tests/test_data/data_sources/human_eval.json +1 -0
- package/python/tests/test_data/data_sources/simple_instances.yaml +3 -0
- package/python/tests/test_data/data_sources/simple_instances_long.yaml +30 -0
- package/python/tests/test_data/data_sources/swe-bench-dev-easy.json +1 -0
- package/python/tests/test_data/data_sources/swe-bench-dev-easy_first_only.json +1 -0
- package/python/tests/test_data/data_sources/swe-bench-lite-test.json +1 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent-test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/6e44b9__sweagenttestrepo-1c2844.traj +342 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent-test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/solution_missing_colon.py +15 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent__test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/args.yaml +518 -0
- package/python/tests/test_data/trajectories/gpt4__swe-agent__test-repo__default_from_url__t-0.00__p-0.95__c-3.00__install-1/swe-agent__test-repo-i1.traj +124 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/all_preds.jsonl +1 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/args.yaml +520 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/patches/pydicom__pydicom-1458.patch +18 -0
- package/python/tests/test_data/trajectories/gpt4__swe-bench-dev-easy_first_only__default__t-0.00__p-0.95__c-3.00__install-1/pydicom__pydicom-1458.traj +257 -0
- package/python/tests/test_env.py +66 -0
- package/python/tests/test_env_utils.py +129 -0
- package/python/tests/test_history_processors.py +40 -0
- package/python/tests/test_models.py +23 -0
- package/python/tests/test_openai_live.py +164 -0
- package/python/tests/test_packaging.py +7 -0
- package/python/tests/test_parsing.py +131 -0
- package/python/tests/test_problem_statement_multimodal.py +111 -0
- package/python/tests/test_quick_stats.py +42 -0
- package/python/tests/test_run.py +37 -0
- package/python/tests/test_run_batch.py +110 -0
- package/python/tests/test_run_hooks.py +114 -0
- package/python/tests/test_run_replay.py +33 -0
- package/python/tests/test_run_single.py +125 -0
- package/python/tests/test_tools_command_parsing.py +193 -0
- package/python/tests/test_utils.py +15 -0
- package/python/tests/tools/__init__.py +0 -0
- package/python/tests/tools/conftest.py +12 -0
- package/python/tests/tools/test_default_utils.py +153 -0
- package/python/tests/tools/test_edit_replace.py +0 -0
- package/python/tests/tools/test_split_string.py +82 -0
- package/python/tests/utils.py +29 -0
- package/python/tools/diff_state/bin/_state_diff_state +52 -0
- package/python/tools/diff_state/config.yaml +2 -0
- package/python/tools/edit_anthropic/bin/_state_anthropic +21 -0
- package/python/tools/edit_anthropic/bin/str_replace_editor +710 -0
- package/python/tools/edit_anthropic/config.yaml +56 -0
- package/python/tools/edit_anthropic/install.sh +3 -0
- package/python/tools/filemap/bin/filemap +45 -0
- package/python/tools/filemap/config.yaml +9 -0
- package/python/tools/filemap/install.sh +2 -0
- package/python/tools/forfeit/bin/exit_forfeit +5 -0
- package/python/tools/forfeit/config.yaml +5 -0
- package/python/tools/image_tools/bin/view_image +36 -0
- package/python/tools/image_tools/config.yaml +9 -0
- package/python/tools/multilingual_setup/bin/do_nothing +2 -0
- package/python/tools/multilingual_setup/config.yaml +1 -0
- package/python/tools/multilingual_setup/install.sh +45 -0
- package/python/tools/registry/bin/_read_env +10 -0
- package/python/tools/registry/bin/_write_env +10 -0
- package/python/tools/registry/config.yaml +1 -0
- package/python/tools/registry/install.sh +6 -0
- package/python/tools/registry/lib/__init__.py +0 -0
- package/python/tools/registry/lib/registry.py +56 -0
- package/python/tools/review_on_submit_m/README.md +6 -0
- package/python/tools/review_on_submit_m/bin/submit +54 -0
- package/python/tools/review_on_submit_m/config.yaml +6 -0
- package/python/tools/review_on_submit_m/install.sh +0 -0
- package/python/tools/search/bin/find_file +31 -0
- package/python/tools/search/bin/search_dir +39 -0
- package/python/tools/search/bin/search_file +55 -0
- package/python/tools/search/config.yaml +37 -0
- package/python/tools/search/install.sh +3 -0
- package/python/tools/submit/bin/submit +17 -0
- package/python/tools/submit/config.yaml +5 -0
- package/python/tools/web_browser/bin/click_mouse +41 -0
- package/python/tools/web_browser/bin/close_site +28 -0
- package/python/tools/web_browser/bin/double_click_mouse +37 -0
- package/python/tools/web_browser/bin/drag_mouse +46 -0
- package/python/tools/web_browser/bin/execute_script_on_page +39 -0
- package/python/tools/web_browser/bin/get_console_output +48 -0
- package/python/tools/web_browser/bin/move_mouse +35 -0
- package/python/tools/web_browser/bin/navigate_back +33 -0
- package/python/tools/web_browser/bin/navigate_forward +33 -0
- package/python/tools/web_browser/bin/open_site +36 -0
- package/python/tools/web_browser/bin/press_keys_on_page +51 -0
- package/python/tools/web_browser/bin/reload_page +33 -0
- package/python/tools/web_browser/bin/run_web_browser_server +394 -0
- package/python/tools/web_browser/bin/screenshot_site +38 -0
- package/python/tools/web_browser/bin/scroll_on_page +40 -0
- package/python/tools/web_browser/bin/set_browser_window_size +40 -0
- package/python/tools/web_browser/bin/type_text +34 -0
- package/python/tools/web_browser/bin/wait_time +39 -0
- package/python/tools/web_browser/config.yaml +155 -0
- package/python/tools/web_browser/install.sh +22 -0
- package/python/tools/web_browser/lib/browser_manager.py +404 -0
- package/python/tools/web_browser/lib/web_browser_config.py +33 -0
- package/python/tools/web_browser/lib/web_browser_utils.py +126 -0
- package/python/tools/web_browser/test_console.html +1 -0
- package/python/tools/windowed/bin/_state +25 -0
- package/python/tools/windowed/bin/create +29 -0
- package/python/tools/windowed/bin/goto +37 -0
- package/python/tools/windowed/bin/open +49 -0
- package/python/tools/windowed/bin/scroll_down +12 -0
- package/python/tools/windowed/bin/scroll_up +13 -0
- package/python/tools/windowed/config.yaml +38 -0
- package/python/tools/windowed/install.sh +15 -0
- package/python/tools/windowed/lib/__init__.py +0 -0
- package/python/tools/windowed/lib/flake8_utils.py +147 -0
- package/python/tools/windowed/lib/windowed_file.py +312 -0
- package/python/tools/windowed_edit_linting/bin/edit +128 -0
- package/python/tools/windowed_edit_linting/config.yaml +31 -0
- package/python/tools/windowed_edit_linting/install.sh +5 -0
- package/python/tools/windowed_edit_replace/bin/edit +172 -0
- package/python/tools/windowed_edit_replace/bin/insert +77 -0
- package/python/tools/windowed_edit_replace/config.yaml +60 -0
- package/python/tools/windowed_edit_replace/install.sh +5 -0
- package/python/tools/windowed_edit_rewrite/bin/edit +78 -0
- package/python/tools/windowed_edit_rewrite/config.yaml +11 -0
- package/python/tools/windowed_edit_rewrite/install.sh +5 -0
- package/python/trajectories/demonstrations/ctf/crypto/BabyEncryption.traj +318 -0
- package/python/trajectories/demonstrations/ctf/crypto/BabyTimeCapsule.traj +197 -0
- package/python/trajectories/demonstrations/ctf/crypto/eps.traj +289 -0
- package/python/trajectories/demonstrations/ctf/crypto/katy.traj +368 -0
- package/python/trajectories/demonstrations/ctf/forensics/flash.traj +102 -0
- package/python/trajectories/demonstrations/ctf/misc/networking_1.traj +102 -0
- package/python/trajectories/demonstrations/ctf/pwn/warmup.traj +159 -0
- package/python/trajectories/demonstrations/ctf/rev/rock.traj +251 -0
- package/python/trajectories/demonstrations/ctf/web/i_got_id_demo.traj +422 -0
- package/python/trajectories/demonstrations/function_calling_simple.traj +151 -0
- package/python/trajectories/demonstrations/human_thought__swe-bench-HumanEvalFix-python__lcb__t-0.00__p-0.95__c-4.00__install-0/humanevalfix-python-0.traj +129 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default__t-0.20__p-0.95__c-2.00__install-1___install_from_source/marshmallow-code__marshmallow-1867.traj +318 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default_sys-env_cursors_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +251 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__default_sys-env_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +399 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__function_calling__install-1/marshmallow-code__marshmallow-1867.traj +594 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__function_calling_replace__install-1/marshmallow-code__marshmallow-1867.traj +592 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__function_calling_replace_from_source/marshmallow-code__marshmallow-1867.traj +3316 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__xml_sys-env_cursors_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +251 -0
- package/python/trajectories/demonstrations/replay__marshmallow-code__marshmallow-1867__xml_sys-env_window100__t-0.20__p-0.95__c-2.00__install-1/marshmallow-code__marshmallow-1867.traj +399 -0
- package/python/trajectories/demonstrations/str_replace_anthropic_demo.yaml +432 -0
- package/rust/Cargo.toml +100 -0
- package/rust/README.md +49 -0
- package/rust/src/agent/action_sampler.rs +130 -0
- package/rust/src/agent/agents.rs +1029 -0
- package/rust/src/agent/history_processors.rs +277 -0
- package/rust/src/agent/hooks/mod.rs +208 -0
- package/rust/src/agent/mod.rs +24 -0
- package/rust/src/agent/models.rs +837 -0
- package/rust/src/agent/problem_statement.rs +355 -0
- package/rust/src/agent/reviewer.rs +505 -0
- package/rust/src/bin/sweagent.rs +784 -0
- package/rust/src/environment/deployment.rs +631 -0
- package/rust/src/environment/hooks/mod.rs +114 -0
- package/rust/src/environment/mod.rs +16 -0
- package/rust/src/environment/repo.rs +265 -0
- package/rust/src/environment/runtime.rs +237 -0
- package/rust/src/environment/swe_env.rs +248 -0
- package/rust/src/exceptions.rs +228 -0
- package/rust/src/lib.rs +68 -0
- package/rust/src/monitoring.rs +482 -0
- package/rust/src/run/hooks/mod.rs +134 -0
- package/rust/src/run/mod.rs +12 -0
- package/rust/src/run/run_batch.rs +563 -0
- package/rust/src/run/run_single.rs +196 -0
- package/rust/src/tools/bundle.rs +224 -0
- package/rust/src/tools/commands.rs +173 -0
- package/rust/src/tools/mod.rs +295 -0
- package/rust/src/tools/parsing.rs +354 -0
- package/rust/src/tools/registry.rs +143 -0
- package/rust/src/types.rs +554 -0
- package/rust/src/utils/config.rs +105 -0
- package/rust/src/utils/files.rs +137 -0
- package/rust/src/utils/github.rs +171 -0
- package/rust/src/utils/log.rs +65 -0
- package/rust/src/utils/mod.rs +17 -0
- package/rust/src/utils/serialization.rs +181 -0
- package/rust/src/utils/template.rs +173 -0
- package/typescript/README.md +335 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from sweagent.environment.repo import Repo, RepoConfig
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EnvHook:
|
|
5
|
+
"""Hook to be used in `SWEEnv`.
|
|
6
|
+
|
|
7
|
+
Subclass this class, add functionality and add it with `SWEEEnv.add_hook(hook)`.
|
|
8
|
+
This allows to inject custom functionality at different stages of the environment
|
|
9
|
+
lifecycle, in particular to connect SWE-agent to a new interface (like a GUI).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def on_init(self, *, env) -> None:
|
|
13
|
+
"""Gets called when the hook is added"""
|
|
14
|
+
|
|
15
|
+
def on_copy_repo_started(self, repo: RepoConfig | Repo) -> None:
|
|
16
|
+
"""Gets called when the repository is being cloned to the container"""
|
|
17
|
+
|
|
18
|
+
def on_start_deployment(self) -> None:
|
|
19
|
+
"""Gets called when the deployment is being started"""
|
|
20
|
+
|
|
21
|
+
def on_install_env_started(self) -> None:
|
|
22
|
+
"""Called when we start installing the environment"""
|
|
23
|
+
|
|
24
|
+
def on_close(self):
|
|
25
|
+
"""Called when the environment is closed"""
|
|
26
|
+
|
|
27
|
+
def on_environment_startup(self) -> None:
|
|
28
|
+
"""Called when the environment is started"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CombinedEnvHooks(EnvHook):
|
|
32
|
+
def __init__(self):
|
|
33
|
+
self._hooks = []
|
|
34
|
+
|
|
35
|
+
def add_hook(self, hook: EnvHook) -> None:
|
|
36
|
+
self._hooks.append(hook)
|
|
37
|
+
|
|
38
|
+
def on_init(self, *, env) -> None:
|
|
39
|
+
for hook in self._hooks:
|
|
40
|
+
hook.on_init(env=env)
|
|
41
|
+
|
|
42
|
+
def on_copy_repo_started(self, repo: RepoConfig | Repo) -> None:
|
|
43
|
+
for hook in self._hooks:
|
|
44
|
+
hook.on_copy_repo_started(repo=repo)
|
|
45
|
+
|
|
46
|
+
def on_start_deployment(self) -> None:
|
|
47
|
+
for hook in self._hooks:
|
|
48
|
+
hook.on_start_deployment()
|
|
49
|
+
|
|
50
|
+
def on_install_env_started(self) -> None:
|
|
51
|
+
for hook in self._hooks:
|
|
52
|
+
hook.on_install_env_started()
|
|
53
|
+
|
|
54
|
+
def on_close(self):
|
|
55
|
+
for hook in self._hooks:
|
|
56
|
+
hook.on_close()
|
|
57
|
+
|
|
58
|
+
def on_environment_startup(self) -> None:
|
|
59
|
+
for hook in self._hooks:
|
|
60
|
+
hook.on_environment_startup()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
from sweagent.environment.hooks.abstract import EnvHook
|
|
4
|
+
from sweagent.environment.repo import Repo, RepoConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SetStatusEnvironmentHook(EnvHook):
|
|
8
|
+
def __init__(self, id: str, callable: Callable[[str, str], None]):
|
|
9
|
+
self._callable = callable
|
|
10
|
+
self._id = id
|
|
11
|
+
|
|
12
|
+
def _update(self, message: str):
|
|
13
|
+
self._callable(self._id, message)
|
|
14
|
+
|
|
15
|
+
def on_copy_repo_started(self, repo: RepoConfig | Repo):
|
|
16
|
+
self._update(f"Copying repo {repo.repo_name}")
|
|
17
|
+
|
|
18
|
+
def on_start_deployment(self):
|
|
19
|
+
self._update("Starting deployment")
|
|
20
|
+
|
|
21
|
+
def on_install_env_started(self):
|
|
22
|
+
self._update("Installing environment")
|
|
23
|
+
|
|
24
|
+
def on_environment_startup(self):
|
|
25
|
+
self._update("Starting environment")
|
|
26
|
+
|
|
27
|
+
def on_close(self):
|
|
28
|
+
self._update("Closing environment")
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import shlex
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Literal, Protocol
|
|
6
|
+
|
|
7
|
+
from git import InvalidGitRepositoryError
|
|
8
|
+
from git import Repo as GitRepo
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
from swerex.deployment.abstract import AbstractDeployment
|
|
11
|
+
from swerex.runtime.abstract import Command, UploadRequest
|
|
12
|
+
from typing_extensions import Self
|
|
13
|
+
|
|
14
|
+
from sweagent.utils.github import _parse_gh_repo_url
|
|
15
|
+
from sweagent.utils.log import get_logger
|
|
16
|
+
|
|
17
|
+
logger = get_logger("swea-config", emoji="🔧")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Repo(Protocol):
|
|
21
|
+
"""Protocol for repository configurations."""
|
|
22
|
+
|
|
23
|
+
base_commit: str
|
|
24
|
+
repo_name: str
|
|
25
|
+
|
|
26
|
+
def copy(self, deployment: AbstractDeployment): ...
|
|
27
|
+
|
|
28
|
+
def get_reset_commands(self) -> list[str]: ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _get_git_reset_commands(base_commit: str) -> list[str]:
|
|
32
|
+
return [
|
|
33
|
+
"git fetch",
|
|
34
|
+
"git status",
|
|
35
|
+
"git restore .",
|
|
36
|
+
"git reset --hard",
|
|
37
|
+
f"git checkout {shlex.quote(base_commit)}",
|
|
38
|
+
"git clean -fdq",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PreExistingRepoConfig(BaseModel):
|
|
43
|
+
"""Use this to specify a repository that already exists on the deployment.
|
|
44
|
+
This is important because we need to cd to the repo before running the agent.
|
|
45
|
+
|
|
46
|
+
Note: The repository must be at the root of the deployment.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
repo_name: str
|
|
50
|
+
"""The repo name (the repository must be located at the root of the deployment)."""
|
|
51
|
+
base_commit: str = Field(default="HEAD")
|
|
52
|
+
"""The commit to reset the repository to. The default is HEAD,
|
|
53
|
+
i.e., the latest commit. You can also set this to a branch name (e.g., `dev`),
|
|
54
|
+
a tag (e.g., `v0.1.0`), or a commit hash (e.g., `a4464baca1f`).
|
|
55
|
+
SWE-agent will then start from this commit when trying to solve the problem.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
type: Literal["preexisting"] = "preexisting"
|
|
59
|
+
"""Discriminator for (de)serialization/CLI. Do not change."""
|
|
60
|
+
|
|
61
|
+
reset: bool = True
|
|
62
|
+
"""If True, reset the repository to the base commit after the copy operation."""
|
|
63
|
+
|
|
64
|
+
model_config = ConfigDict(extra="forbid")
|
|
65
|
+
|
|
66
|
+
def copy(self, deployment: AbstractDeployment):
|
|
67
|
+
"""Does nothing."""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def get_reset_commands(self) -> list[str]:
|
|
71
|
+
"""Issued after the copy operation or when the environment is reset."""
|
|
72
|
+
if self.reset:
|
|
73
|
+
return _get_git_reset_commands(self.base_commit)
|
|
74
|
+
return []
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class LocalRepoConfig(BaseModel):
|
|
78
|
+
path: Path
|
|
79
|
+
base_commit: str = Field(default="HEAD")
|
|
80
|
+
"""The commit to reset the repository to. The default is HEAD,
|
|
81
|
+
i.e., the latest commit. You can also set this to a branch name (e.g., `dev`),
|
|
82
|
+
a tag (e.g., `v0.1.0`), or a commit hash (e.g., `a4464baca1f`).
|
|
83
|
+
SWE-agent will then start from this commit when trying to solve the problem.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
type: Literal["local"] = "local"
|
|
87
|
+
"""Discriminator for (de)serialization/CLI. Do not change."""
|
|
88
|
+
|
|
89
|
+
model_config = ConfigDict(extra="forbid")
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def repo_name(self) -> str:
|
|
93
|
+
"""Set automatically based on the repository name. Cannot be set."""
|
|
94
|
+
return Path(self.path).resolve().name.replace(" ", "-").replace("'", "")
|
|
95
|
+
|
|
96
|
+
# Let's not make this a model validator, because it leads to cryptic errors.
|
|
97
|
+
# Let's just check during copy instead.
|
|
98
|
+
def check_valid_repo(self) -> Self:
|
|
99
|
+
try:
|
|
100
|
+
repo = GitRepo(self.path, search_parent_directories=True)
|
|
101
|
+
except InvalidGitRepositoryError as e:
|
|
102
|
+
msg = f"Could not find git repository at {self.path=}."
|
|
103
|
+
raise ValueError(msg) from e
|
|
104
|
+
if repo.is_dirty() and "PYTEST_CURRENT_TEST" not in os.environ:
|
|
105
|
+
msg = f"Local git repository {self.path} is dirty. Please commit or stash changes."
|
|
106
|
+
raise ValueError(msg)
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
def copy(self, deployment: AbstractDeployment):
|
|
110
|
+
self.check_valid_repo()
|
|
111
|
+
asyncio.run(
|
|
112
|
+
deployment.runtime.upload(UploadRequest(source_path=str(self.path), target_path=f"/{self.repo_name}"))
|
|
113
|
+
)
|
|
114
|
+
r = asyncio.run(
|
|
115
|
+
deployment.runtime.execute(Command(command=f"chown -R root:root /{self.repo_name}", shell=True))
|
|
116
|
+
)
|
|
117
|
+
if r.exit_code != 0:
|
|
118
|
+
msg = f"Failed to change permissions on copied repository (exit code: {r.exit_code}, stdout: {r.stdout}, stderr: {r.stderr})"
|
|
119
|
+
raise RuntimeError(msg)
|
|
120
|
+
|
|
121
|
+
def get_reset_commands(self) -> list[str]:
|
|
122
|
+
"""Issued after the copy operation or when the environment is reset."""
|
|
123
|
+
return _get_git_reset_commands(self.base_commit)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class GithubRepoConfig(BaseModel):
|
|
127
|
+
github_url: str
|
|
128
|
+
|
|
129
|
+
base_commit: str = Field(default="HEAD")
|
|
130
|
+
"""The commit to reset the repository to. The default is HEAD,
|
|
131
|
+
i.e., the latest commit. You can also set this to a branch name (e.g., `dev`),
|
|
132
|
+
a tag (e.g., `v0.1.0`), or a commit hash (e.g., `a4464baca1f`).
|
|
133
|
+
SWE-agent will then start from this commit when trying to solve the problem.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
clone_timeout: float = 500
|
|
137
|
+
"""Timeout for git clone operation."""
|
|
138
|
+
|
|
139
|
+
type: Literal["github"] = "github"
|
|
140
|
+
"""Discriminator for (de)serialization/CLI. Do not change."""
|
|
141
|
+
|
|
142
|
+
model_config = ConfigDict(extra="forbid")
|
|
143
|
+
|
|
144
|
+
def model_post_init(self, __context: Any) -> None:
|
|
145
|
+
if self.github_url.count("/") == 1:
|
|
146
|
+
self.github_url = f"https://github.com/{self.github_url}"
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def repo_name(self) -> str:
|
|
150
|
+
org, repo = _parse_gh_repo_url(self.github_url)
|
|
151
|
+
return f"{org}__{repo}"
|
|
152
|
+
|
|
153
|
+
def _get_url_with_token(self, token: str) -> str:
|
|
154
|
+
"""Prepend github token to URL"""
|
|
155
|
+
if not token:
|
|
156
|
+
return self.github_url
|
|
157
|
+
if "@" in self.github_url:
|
|
158
|
+
logger.warning("Cannot prepend token to URL. '@' found in URL")
|
|
159
|
+
return self.github_url
|
|
160
|
+
_, _, url_no_protocol = self.github_url.partition("://")
|
|
161
|
+
return f"https://{token}@{url_no_protocol}"
|
|
162
|
+
|
|
163
|
+
def copy(self, deployment: AbstractDeployment):
|
|
164
|
+
"""Clones the repository to the sandbox."""
|
|
165
|
+
base_commit = self.base_commit
|
|
166
|
+
github_token = os.getenv("GITHUB_TOKEN", "")
|
|
167
|
+
url = self._get_url_with_token(github_token)
|
|
168
|
+
asyncio.run(
|
|
169
|
+
deployment.runtime.execute(
|
|
170
|
+
Command(
|
|
171
|
+
command=" && ".join(
|
|
172
|
+
(
|
|
173
|
+
f"mkdir /{self.repo_name}",
|
|
174
|
+
f"cd /{self.repo_name}",
|
|
175
|
+
"git init",
|
|
176
|
+
f"git remote add origin {shlex.quote(url)}",
|
|
177
|
+
f"git fetch --depth 1 origin {shlex.quote(base_commit)}",
|
|
178
|
+
"git checkout FETCH_HEAD",
|
|
179
|
+
"cd ..",
|
|
180
|
+
)
|
|
181
|
+
),
|
|
182
|
+
timeout=self.clone_timeout,
|
|
183
|
+
shell=True,
|
|
184
|
+
check=True,
|
|
185
|
+
)
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def get_reset_commands(self) -> list[str]:
|
|
190
|
+
"""Issued after the copy operation or when the environment is reset."""
|
|
191
|
+
return _get_git_reset_commands(self.base_commit)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
RepoConfig = LocalRepoConfig | GithubRepoConfig | PreExistingRepoConfig
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def repo_from_simplified_input(
|
|
198
|
+
*, input: str, base_commit: str = "HEAD", type: Literal["local", "github", "preexisting", "auto"] = "auto"
|
|
199
|
+
) -> RepoConfig:
|
|
200
|
+
"""Get repo config from a simplified input.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
input: Local path or GitHub URL
|
|
204
|
+
type: The type of repo. Set to "auto" to automatically detect the type
|
|
205
|
+
(does not work for preexisting repos).
|
|
206
|
+
"""
|
|
207
|
+
if type == "local":
|
|
208
|
+
return LocalRepoConfig(path=Path(input), base_commit=base_commit)
|
|
209
|
+
if type == "github":
|
|
210
|
+
return GithubRepoConfig(github_url=input, base_commit=base_commit)
|
|
211
|
+
if type == "preexisting":
|
|
212
|
+
return PreExistingRepoConfig(repo_name=input, base_commit=base_commit)
|
|
213
|
+
if type == "auto":
|
|
214
|
+
if input.startswith("https://github.com/"):
|
|
215
|
+
return GithubRepoConfig(github_url=input, base_commit=base_commit)
|
|
216
|
+
else:
|
|
217
|
+
return LocalRepoConfig(path=Path(input), base_commit=base_commit)
|
|
218
|
+
msg = f"Unknown repo type: {type}"
|
|
219
|
+
raise ValueError(msg)
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import shlex
|
|
4
|
+
from pathlib import PurePath
|
|
5
|
+
from typing import Literal, Self
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
from swerex.deployment.abstract import AbstractDeployment
|
|
9
|
+
from swerex.deployment.config import DeploymentConfig, DockerDeploymentConfig, get_deployment
|
|
10
|
+
from swerex.runtime.abstract import (
|
|
11
|
+
BashAction,
|
|
12
|
+
BashInterruptAction,
|
|
13
|
+
CreateBashSessionRequest,
|
|
14
|
+
ReadFileRequest,
|
|
15
|
+
WriteFileRequest,
|
|
16
|
+
)
|
|
17
|
+
from swerex.runtime.abstract import Command as RexCommand
|
|
18
|
+
|
|
19
|
+
from sweagent.environment.hooks.abstract import CombinedEnvHooks, EnvHook
|
|
20
|
+
from sweagent.environment.repo import Repo, RepoConfig
|
|
21
|
+
from sweagent.utils.log import get_logger
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EnvironmentConfig(BaseModel):
|
|
25
|
+
"""Configure data sources and setup instructions for the environment in which we solve the tasks."""
|
|
26
|
+
|
|
27
|
+
deployment: DeploymentConfig = Field(
|
|
28
|
+
default_factory=lambda: DockerDeploymentConfig(image="python:3.11", python_standalone_dir="/root"),
|
|
29
|
+
description="Deployment options.",
|
|
30
|
+
)
|
|
31
|
+
repo: RepoConfig | None = Field(
|
|
32
|
+
default=None,
|
|
33
|
+
description="Repository options.",
|
|
34
|
+
)
|
|
35
|
+
post_startup_commands: list[str] = []
|
|
36
|
+
"""Execute these commands before starting to run the agent but after all other setup steps.
|
|
37
|
+
They will be executed in the same shell as the agent.
|
|
38
|
+
Note: Every command is passed as a string, not a list of arguments.
|
|
39
|
+
"""
|
|
40
|
+
post_startup_command_timeout: int = 500
|
|
41
|
+
"""Timeout for the post-startup commands.
|
|
42
|
+
NOTE: The timeout applies to every command in `post_startup_commands` separately.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
# pydantic config
|
|
46
|
+
model_config = ConfigDict(extra="forbid")
|
|
47
|
+
|
|
48
|
+
name: str = "main"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SWEEnv:
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
*,
|
|
55
|
+
deployment: AbstractDeployment,
|
|
56
|
+
repo: Repo | RepoConfig | None,
|
|
57
|
+
post_startup_commands: list[str],
|
|
58
|
+
post_startup_command_timeout: int = 500,
|
|
59
|
+
hooks: list[EnvHook] | None = None,
|
|
60
|
+
name: str = "main",
|
|
61
|
+
):
|
|
62
|
+
"""This class represents the environment in which we solve the tasks.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
deployment: SWE-ReX deployment instance
|
|
66
|
+
repo: Repository configuration object, or anything following the `Repo` protocol
|
|
67
|
+
post_startup_commands: Commands to execute before starting the agent
|
|
68
|
+
hooks: Environment hooks (used to inject custom functionality)
|
|
69
|
+
Equivalent to calling `add_hook` for each hook after initialization.
|
|
70
|
+
name: Name of the environment
|
|
71
|
+
"""
|
|
72
|
+
super().__init__()
|
|
73
|
+
self.deployment = deployment
|
|
74
|
+
self.repo = repo
|
|
75
|
+
self._post_startup_commands = post_startup_commands
|
|
76
|
+
self.post_startup_command_timeout = post_startup_command_timeout
|
|
77
|
+
self.logger = get_logger("swea-env", emoji="🪴")
|
|
78
|
+
self.name = name
|
|
79
|
+
self.clean_multi_line_functions = lambda x: x
|
|
80
|
+
self._chook = CombinedEnvHooks()
|
|
81
|
+
for hook in hooks or []:
|
|
82
|
+
self.add_hook(hook)
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def from_config(cls, config: EnvironmentConfig) -> Self:
|
|
86
|
+
"""Create an environment instance from a configuration object.
|
|
87
|
+
This is the recommended way to create an environment instance, unless you need
|
|
88
|
+
more flexibility.
|
|
89
|
+
"""
|
|
90
|
+
# Always copy config to avoid shared state between different instances
|
|
91
|
+
config = config.model_copy(deep=True)
|
|
92
|
+
return cls(
|
|
93
|
+
deployment=get_deployment(config.deployment),
|
|
94
|
+
repo=config.repo,
|
|
95
|
+
post_startup_commands=config.post_startup_commands,
|
|
96
|
+
post_startup_command_timeout=config.post_startup_command_timeout,
|
|
97
|
+
name=config.name,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def add_hook(self, hook: EnvHook) -> None:
|
|
101
|
+
"""Add `EnvHook` to the environment.
|
|
102
|
+
|
|
103
|
+
This allows to inject custom functionality at different stages of the environment
|
|
104
|
+
lifecycle, in particular to connect SWE-agent to a new interface (like a GUI).
|
|
105
|
+
"""
|
|
106
|
+
hook.on_init(env=self)
|
|
107
|
+
self._chook.add_hook(hook)
|
|
108
|
+
|
|
109
|
+
def start(self) -> None:
|
|
110
|
+
"""Start the environment and reset it to a clean state."""
|
|
111
|
+
self._init_deployment()
|
|
112
|
+
self.reset()
|
|
113
|
+
for command in self._post_startup_commands:
|
|
114
|
+
self.communicate(command, check="raise", timeout=self.post_startup_command_timeout)
|
|
115
|
+
|
|
116
|
+
def _copy_repo(self) -> None:
|
|
117
|
+
"""Clone/copy repository/codebase in container"""
|
|
118
|
+
if self.repo is None:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
folders = self.communicate(input="ls", check="raise").split("\n")
|
|
122
|
+
if self.repo.repo_name in folders:
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
self._chook.on_copy_repo_started(repo=self.repo)
|
|
126
|
+
self.repo.copy(self.deployment)
|
|
127
|
+
|
|
128
|
+
def hard_reset(self):
|
|
129
|
+
"""Resets the environment and deployment, i.e., completely restarts the
|
|
130
|
+
deployment.
|
|
131
|
+
"""
|
|
132
|
+
self.close()
|
|
133
|
+
self.start()
|
|
134
|
+
|
|
135
|
+
def reset(self):
|
|
136
|
+
"""Reset the environment to a clean state.
|
|
137
|
+
Gets called by `start`, but can also be called independently to reset the
|
|
138
|
+
environment to a clean state before a new attempt.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
observation: output from container
|
|
142
|
+
info: additional information (e.g. debugging information)
|
|
143
|
+
"""
|
|
144
|
+
self.communicate(input="cd /", check="raise")
|
|
145
|
+
self._copy_repo()
|
|
146
|
+
self._reset_repository()
|
|
147
|
+
self._chook.on_environment_startup()
|
|
148
|
+
|
|
149
|
+
def _reset_repository(self) -> None:
|
|
150
|
+
"""Clean repository of any modifications + Checkout base commit"""
|
|
151
|
+
if self.repo is not None:
|
|
152
|
+
self.logger.debug("Resetting repository %s to commit %s", self.repo.repo_name, self.repo.base_commit)
|
|
153
|
+
# todo: Currently has swe-ft specific change: The original repo.copy isn't called, because the repo is already
|
|
154
|
+
# present. However, reset --hard <BRANCH> also doesn't work. So modified it here to do a checkout instead.
|
|
155
|
+
startup_commands = [
|
|
156
|
+
f"cd /{self.repo.repo_name}",
|
|
157
|
+
"export ROOT=$(pwd -P)",
|
|
158
|
+
*self.repo.get_reset_commands(),
|
|
159
|
+
]
|
|
160
|
+
self.communicate(
|
|
161
|
+
input=" && ".join(startup_commands),
|
|
162
|
+
check="raise",
|
|
163
|
+
error_msg="Failed to clean repository",
|
|
164
|
+
# Sometimes this is slow because it rebuilds some index
|
|
165
|
+
timeout=120,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def close(self) -> None:
|
|
169
|
+
"""Shutdown SWE-ReX deployment etc."""
|
|
170
|
+
self.logger.info("Beginning environment shutdown...")
|
|
171
|
+
asyncio.run(self.deployment.stop())
|
|
172
|
+
self._chook.on_close()
|
|
173
|
+
|
|
174
|
+
# MARK: Helper functions #
|
|
175
|
+
|
|
176
|
+
def _init_deployment(
|
|
177
|
+
self,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Handles container initialization. Defines container name and creates it.
|
|
180
|
+
If cached_image is provided, it will use that image name instead of the default.
|
|
181
|
+
"""
|
|
182
|
+
self._chook.on_start_deployment()
|
|
183
|
+
asyncio.run(self.deployment.start())
|
|
184
|
+
asyncio.run(
|
|
185
|
+
self.deployment.runtime.create_session(
|
|
186
|
+
CreateBashSessionRequest(startup_source=["/root/.bashrc"], startup_timeout=10)
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
self.set_env_variables({"LANG": "C.UTF-8", "LC_ALL": "C.UTF-8", "PIP_PROGRESS_BAR": "off", "PAGER": "cat"})
|
|
190
|
+
self.logger.info("Environment Initialized")
|
|
191
|
+
|
|
192
|
+
def interrupt_session(self):
|
|
193
|
+
self.logger.info("Interrupting session")
|
|
194
|
+
asyncio.run(self.deployment.runtime.run_in_session(BashInterruptAction()))
|
|
195
|
+
|
|
196
|
+
# todo: return exit code?
|
|
197
|
+
def communicate(
|
|
198
|
+
self,
|
|
199
|
+
input: str,
|
|
200
|
+
timeout: int | float = 25,
|
|
201
|
+
*,
|
|
202
|
+
check: Literal["warn", "ignore", "raise"] = "ignore",
|
|
203
|
+
error_msg: str = "Command failed",
|
|
204
|
+
) -> str:
|
|
205
|
+
"""Executes a command in the running shell. The details of this are handled by
|
|
206
|
+
the SWE-ReX deployment/runtime.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
input: input to send to container
|
|
210
|
+
timeout_duration: duration to wait for output
|
|
211
|
+
check: `ignore`: do not extract exit code (more stable), `warn`: extract exit code and log error if
|
|
212
|
+
exit code is non-zero, `raise`: raise error if exit code is non-zero
|
|
213
|
+
error_msg: error message to raise if the command fails
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
output: output from container
|
|
217
|
+
"""
|
|
218
|
+
self.logger.log(logging.TRACE, "Input:\n%s", input) # type: ignore
|
|
219
|
+
rex_check = "silent" if check else "ignore"
|
|
220
|
+
r = asyncio.run(
|
|
221
|
+
self.deployment.runtime.run_in_session(BashAction(command=input, timeout=timeout, check=rex_check))
|
|
222
|
+
)
|
|
223
|
+
output = r.output
|
|
224
|
+
self.logger.log(logging.TRACE, "Output:\n%s", output) # type: ignore
|
|
225
|
+
if check != "ignore" and r.exit_code != 0:
|
|
226
|
+
self.logger.error(f"{error_msg}:\n{output}")
|
|
227
|
+
msg = f"Command {input!r} failed ({r.exit_code=}): {error_msg}"
|
|
228
|
+
self.logger.error(msg)
|
|
229
|
+
if check == "raise":
|
|
230
|
+
self.close()
|
|
231
|
+
raise RuntimeError(msg)
|
|
232
|
+
return output
|
|
233
|
+
|
|
234
|
+
def read_file(self, path: str | PurePath, encoding: str | None = None, errors: str | None = None) -> str:
|
|
235
|
+
"""Read file contents from container
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
path: Absolute path to file
|
|
239
|
+
encoding: Encoding to use when reading the file. None means default encoding.
|
|
240
|
+
This is the same as the `encoding` argument of `Path.read_text()`
|
|
241
|
+
errors: Error handling to use when reading the file. None means default error handling.
|
|
242
|
+
This is the same as the `errors` argument of `Path.read_text()`
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
file_contents: Contents of file as string
|
|
246
|
+
"""
|
|
247
|
+
r = asyncio.run(
|
|
248
|
+
self.deployment.runtime.read_file(ReadFileRequest(path=str(path), encoding=encoding, errors=errors))
|
|
249
|
+
)
|
|
250
|
+
return r.content
|
|
251
|
+
|
|
252
|
+
def write_file(self, path: str | PurePath, content: str) -> None:
|
|
253
|
+
"""Write content to file in container"""
|
|
254
|
+
asyncio.run(self.deployment.runtime.write_file(WriteFileRequest(path=str(path), content=content)))
|
|
255
|
+
|
|
256
|
+
def set_env_variables(self, env_variables: dict[str, str]) -> None:
|
|
257
|
+
"""Set environment variables in the environment."""
|
|
258
|
+
if not env_variables:
|
|
259
|
+
self.logger.debug("No environment variables to set")
|
|
260
|
+
return
|
|
261
|
+
_env_setters = [f"export {k}={shlex.quote(str(v))}" for k, v in env_variables.items()]
|
|
262
|
+
command = " && ".join(_env_setters)
|
|
263
|
+
self.communicate(command, check="raise")
|
|
264
|
+
|
|
265
|
+
def execute_command(
|
|
266
|
+
self,
|
|
267
|
+
command: str,
|
|
268
|
+
shell: bool = True,
|
|
269
|
+
check: bool = False,
|
|
270
|
+
env: dict[str, str] | None = None,
|
|
271
|
+
cwd: str | None = None,
|
|
272
|
+
) -> None:
|
|
273
|
+
"""Execute a command in the environment independent of the session (i.e., as a subprocess)"""
|
|
274
|
+
asyncio.run(
|
|
275
|
+
self.deployment.runtime.execute(RexCommand(command=command, shell=shell, check=check, env=env, cwd=cwd))
|
|
276
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
|
|
3
|
+
"""This module contains all custom exceptions used by the SWE-agent."""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FormatError(Exception):
|
|
7
|
+
"""Raised when the model response cannot properly be parsed into thought and actions."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FunctionCallingFormatError(FormatError):
|
|
11
|
+
"""Format error exception used by the function
|
|
12
|
+
calling parser."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
message: str,
|
|
17
|
+
error_code: Literal[
|
|
18
|
+
"missing", "multiple", "incorrect_args", "invalid_json", "invalid_command", "missing_arg", "unexpected_arg"
|
|
19
|
+
],
|
|
20
|
+
**extra_info: Any,
|
|
21
|
+
):
|
|
22
|
+
super().__init__(message + f" [error_code={error_code}]")
|
|
23
|
+
self.message = message
|
|
24
|
+
self.extra_info = {"error_code": error_code, **extra_info}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ContextWindowExceededError(Exception):
|
|
28
|
+
"""Raised when the context window of a LM is exceeded"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CostLimitExceededError(Exception):
|
|
32
|
+
"""Raised when we exceed a cost limit"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class InstanceCostLimitExceededError(CostLimitExceededError):
|
|
36
|
+
"""Raised when we exceed the cost limit set for one task instance"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TotalCostLimitExceededError(CostLimitExceededError):
|
|
40
|
+
"""Raised when we exceed the total cost limit"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class InstanceCallLimitExceededError(CostLimitExceededError):
|
|
44
|
+
"""Raised when we exceed the per instance call limit"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ContentPolicyViolationError(Exception):
|
|
48
|
+
"""Raised when the model response violates a content policy"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ModelConfigurationError(Exception):
|
|
52
|
+
"""Raised when the model configuration is invalid/no further retries
|
|
53
|
+
should be made.
|
|
54
|
+
"""
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
🔗 For more information on the trajectory inspector, visit [our documentation website][docs].
|
|
2
|
+
|
|
3
|
+
You can also find the corresponding markdown files in the [`docs/` folder][source].
|
|
4
|
+
|
|
5
|
+
[docs]: https://swe-agent.com/latest/usage/inspector/
|
|
6
|
+
[source]: https://github.com/SWE-agent/SWE-agent/tree/main/docs
|
|
File without changes
|
|
Binary file
|