@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,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_run_cli_no_arg_error():
|
|
7
|
+
args = [
|
|
8
|
+
"sweagent",
|
|
9
|
+
]
|
|
10
|
+
output = subprocess.run(args, check=False, capture_output=True)
|
|
11
|
+
print(output.stdout.decode())
|
|
12
|
+
print(output.stderr.decode())
|
|
13
|
+
assert output.returncode in [1, 2]
|
|
14
|
+
assert "run-batch" in output.stdout.decode()
|
|
15
|
+
assert "run-replay" in output.stdout.decode()
|
|
16
|
+
assert "run" in output.stdout.decode()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_run_cli_main_help():
|
|
20
|
+
args = [
|
|
21
|
+
"sweagent",
|
|
22
|
+
"--help",
|
|
23
|
+
]
|
|
24
|
+
output = subprocess.run(args, check=True, capture_output=True)
|
|
25
|
+
assert "run-batch" in output.stdout.decode()
|
|
26
|
+
assert "run-replay" in output.stdout.decode()
|
|
27
|
+
assert "run" in output.stdout.decode()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_run_cli_subcommand_help():
|
|
31
|
+
args = [
|
|
32
|
+
"sweagent",
|
|
33
|
+
"run",
|
|
34
|
+
"--help",
|
|
35
|
+
]
|
|
36
|
+
output = subprocess.run(args, check=True, capture_output=True)
|
|
37
|
+
assert "--config" in output.stdout.decode()
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from sweagent.run.run import main
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.slow
|
|
9
|
+
def test_expert_instances(test_data_sources_path: Path, tmp_path: Path):
|
|
10
|
+
ds_path = test_data_sources_path / "expert_instances.yaml"
|
|
11
|
+
assert ds_path.exists()
|
|
12
|
+
cmd = [
|
|
13
|
+
"run-batch",
|
|
14
|
+
"--agent.model.name",
|
|
15
|
+
"instant_empty_submit",
|
|
16
|
+
"--instances.type",
|
|
17
|
+
"expert_file",
|
|
18
|
+
"--instances.path",
|
|
19
|
+
str(ds_path),
|
|
20
|
+
"--output_dir",
|
|
21
|
+
str(tmp_path),
|
|
22
|
+
"--raise_exceptions",
|
|
23
|
+
"True",
|
|
24
|
+
]
|
|
25
|
+
main(cmd)
|
|
26
|
+
for _id in ["simple_test_problem", "simple_test_problem_2"]:
|
|
27
|
+
assert (tmp_path / f"{_id}" / f"{_id}.traj").exists(), list(tmp_path.iterdir())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.mark.slow
|
|
31
|
+
def test_simple_instances(test_data_sources_path: Path, tmp_path: Path):
|
|
32
|
+
ds_path = test_data_sources_path / "simple_instances.yaml"
|
|
33
|
+
assert ds_path.exists()
|
|
34
|
+
cmd = [
|
|
35
|
+
"run-batch",
|
|
36
|
+
"--agent.model.name",
|
|
37
|
+
"instant_empty_submit",
|
|
38
|
+
"--instances.path",
|
|
39
|
+
str(ds_path),
|
|
40
|
+
"--output_dir",
|
|
41
|
+
str(tmp_path),
|
|
42
|
+
"--raise_exceptions",
|
|
43
|
+
"True",
|
|
44
|
+
]
|
|
45
|
+
main(cmd)
|
|
46
|
+
assert (tmp_path / "simple_test_problem" / "simple_test_problem.traj").exists(), list(tmp_path.iterdir())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_empty_instances_simple(test_data_sources_path: Path, tmp_path: Path):
|
|
50
|
+
ds_path = test_data_sources_path / "simple_instances.yaml"
|
|
51
|
+
assert ds_path.exists()
|
|
52
|
+
cmd = [
|
|
53
|
+
"run-batch",
|
|
54
|
+
"--agent.model.name",
|
|
55
|
+
"instant_empty_submit",
|
|
56
|
+
"--instances.path",
|
|
57
|
+
str(ds_path),
|
|
58
|
+
"--output_dir",
|
|
59
|
+
str(tmp_path),
|
|
60
|
+
"--raise_exceptions",
|
|
61
|
+
"True",
|
|
62
|
+
"--instances.filter",
|
|
63
|
+
"doesnotmatch",
|
|
64
|
+
]
|
|
65
|
+
with pytest.raises(ValueError, match="No instances to run"):
|
|
66
|
+
main(cmd)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_empty_instances_expert(test_data_sources_path: Path, tmp_path: Path):
|
|
70
|
+
ds_path = test_data_sources_path / "expert_instances.yaml"
|
|
71
|
+
assert ds_path.exists()
|
|
72
|
+
cmd = [
|
|
73
|
+
"run-batch",
|
|
74
|
+
"--agent.model.name",
|
|
75
|
+
"instant_empty_submit",
|
|
76
|
+
"--instances.path",
|
|
77
|
+
str(ds_path),
|
|
78
|
+
"--instances.type",
|
|
79
|
+
"expert_file",
|
|
80
|
+
"--output_dir",
|
|
81
|
+
str(tmp_path),
|
|
82
|
+
"--raise_exceptions",
|
|
83
|
+
"True",
|
|
84
|
+
"--instances.filter",
|
|
85
|
+
"doesnotmatch",
|
|
86
|
+
]
|
|
87
|
+
with pytest.raises(ValueError, match="No instances to run"):
|
|
88
|
+
main(cmd)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# This doesn't work because we need to retrieve environment variables from the environment
|
|
92
|
+
# in order to format our templates.
|
|
93
|
+
# def test_run_batch_swe_bench_instances(tmp_path: Path):
|
|
94
|
+
# cmd = [
|
|
95
|
+
# "run-batch",
|
|
96
|
+
# "--agent.model.name",
|
|
97
|
+
# "instant_empty_submit",
|
|
98
|
+
# "--instances.subset",
|
|
99
|
+
# "lite",
|
|
100
|
+
# "--instances.split",
|
|
101
|
+
# "test",
|
|
102
|
+
# "--instances.slice",
|
|
103
|
+
# "0:1",
|
|
104
|
+
# "--output_dir",
|
|
105
|
+
# str(tmp_path),
|
|
106
|
+
# "--raise_exceptions",
|
|
107
|
+
# "--instances.deployment.type",
|
|
108
|
+
# "dummy",
|
|
109
|
+
# ]
|
|
110
|
+
# main(cmd)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from sweagent.agent.problem_statement import GithubIssue
|
|
6
|
+
from sweagent.run.hooks.open_pr import OpenPRConfig, OpenPRHook
|
|
7
|
+
from sweagent.types import AgentRunResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _fake_issue_from_url(url: str):
|
|
11
|
+
# Minimal object shape used by OpenPRHook.should_open_pr
|
|
12
|
+
issue_number = url.rstrip("/").split("/")[-1]
|
|
13
|
+
|
|
14
|
+
class Issue:
|
|
15
|
+
def __init__(self, state: str, assignee, locked: bool):
|
|
16
|
+
self.state = state
|
|
17
|
+
self.assignee = assignee
|
|
18
|
+
self.locked = locked
|
|
19
|
+
|
|
20
|
+
if issue_number == "16":
|
|
21
|
+
return Issue(state="closed", assignee=None, locked=False)
|
|
22
|
+
if issue_number == "17":
|
|
23
|
+
return Issue(state="open", assignee="someone", locked=False)
|
|
24
|
+
if issue_number == "18":
|
|
25
|
+
return Issue(state="open", assignee=None, locked=True)
|
|
26
|
+
if issue_number == "19":
|
|
27
|
+
return Issue(state="open", assignee=None, locked=False)
|
|
28
|
+
return Issue(state="open", assignee=None, locked=False)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fake_associated_commit_urls(_org: str, _repo: str, issue_number: str, *, token: str = ""):
|
|
32
|
+
if issue_number == "19":
|
|
33
|
+
return ["https://github.com/swe-agent/test-repo/commit/abc123"]
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.fixture
|
|
38
|
+
def open_pr_hook_init_for_sop(monkeypatch: pytest.MonkeyPatch):
|
|
39
|
+
# Patch network calls in OpenPRHook
|
|
40
|
+
import sweagent.run.hooks.open_pr as open_pr
|
|
41
|
+
|
|
42
|
+
def fake_get_issue(url: str, token: str = ""):
|
|
43
|
+
if "github.com" not in url or "/issues/" not in url:
|
|
44
|
+
msg = f"Invalid GitHub issue URL: {url}"
|
|
45
|
+
raise open_pr.InvalidGithubURL(msg)
|
|
46
|
+
return _fake_issue_from_url(url)
|
|
47
|
+
|
|
48
|
+
monkeypatch.setattr(open_pr, "_get_gh_issue_data", fake_get_issue)
|
|
49
|
+
monkeypatch.setattr(open_pr, "_get_associated_commit_urls", _fake_associated_commit_urls)
|
|
50
|
+
|
|
51
|
+
hook = OpenPRHook(config=OpenPRConfig(skip_if_commits_reference_issue=True))
|
|
52
|
+
hook._token = os.environ.get("GITHUB_TOKEN", "")
|
|
53
|
+
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/1")
|
|
54
|
+
return hook
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.fixture
|
|
58
|
+
def agent_run_result():
|
|
59
|
+
return AgentRunResult(
|
|
60
|
+
info={
|
|
61
|
+
"submission": "asdf",
|
|
62
|
+
"exit_status": "submitted",
|
|
63
|
+
},
|
|
64
|
+
trajectory=[],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_should_open_pr_fail_submission(open_pr_hook_init_for_sop, agent_run_result):
|
|
69
|
+
hook = open_pr_hook_init_for_sop
|
|
70
|
+
agent_run_result.info["submission"] = None
|
|
71
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_should_open_pr_fail_exit(open_pr_hook_init_for_sop, agent_run_result):
|
|
75
|
+
hook = open_pr_hook_init_for_sop
|
|
76
|
+
agent_run_result.info["exit_status"] = "fail"
|
|
77
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_should_open_pr_fail_invalid_url(open_pr_hook_init_for_sop, agent_run_result):
|
|
81
|
+
hook = open_pr_hook_init_for_sop
|
|
82
|
+
hook._problem_statement = type("PS", (), {"github_url": "asdf"})()
|
|
83
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_should_open_pr_fail_closed(open_pr_hook_init_for_sop, agent_run_result):
|
|
87
|
+
hook = open_pr_hook_init_for_sop
|
|
88
|
+
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/16")
|
|
89
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_should_open_pr_fail_assigned(open_pr_hook_init_for_sop, agent_run_result):
|
|
93
|
+
hook = open_pr_hook_init_for_sop
|
|
94
|
+
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/17")
|
|
95
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_should_open_pr_fail_locked(open_pr_hook_init_for_sop, agent_run_result):
|
|
99
|
+
hook = open_pr_hook_init_for_sop
|
|
100
|
+
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/18")
|
|
101
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_should_open_pr_fail_has_pr(open_pr_hook_init_for_sop, agent_run_result):
|
|
105
|
+
hook = open_pr_hook_init_for_sop
|
|
106
|
+
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/19")
|
|
107
|
+
assert not hook.should_open_pr(agent_run_result)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_should_open_pr_success_has_pr_override(open_pr_hook_init_for_sop, agent_run_result):
|
|
111
|
+
hook = open_pr_hook_init_for_sop
|
|
112
|
+
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/19")
|
|
113
|
+
hook._config.skip_if_commits_reference_issue = False
|
|
114
|
+
assert hook.should_open_pr(agent_run_result)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from swerex.deployment.config import DockerDeploymentConfig
|
|
7
|
+
|
|
8
|
+
from sweagent.run.run_replay import RunReplay, RunReplayConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def rr_config(swe_agent_test_repo_traj, tmp_path, swe_agent_test_repo_clone):
|
|
13
|
+
return RunReplayConfig(
|
|
14
|
+
traj_path=swe_agent_test_repo_traj,
|
|
15
|
+
deployment=DockerDeploymentConfig(image="python:3.11"),
|
|
16
|
+
output_dir=tmp_path,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_replay(rr_config):
|
|
21
|
+
rr = RunReplay.from_config(rr_config, _catch_errors=False, _require_zero_exit_code=True)
|
|
22
|
+
rr.main()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_run_cli_help():
|
|
26
|
+
args = [
|
|
27
|
+
"sweagent",
|
|
28
|
+
"run-replay",
|
|
29
|
+
"--help",
|
|
30
|
+
]
|
|
31
|
+
output = subprocess.run(args, capture_output=True)
|
|
32
|
+
assert output.returncode == 0
|
|
33
|
+
assert "Replay a trajectory file" in output.stdout.decode()
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from sweagent import CONFIG_DIR, TOOLS_DIR
|
|
8
|
+
from sweagent.agent.agents import DefaultAgentConfig
|
|
9
|
+
from sweagent.agent.models import InstantEmptySubmitModelConfig
|
|
10
|
+
from sweagent.environment.swe_env import EnvironmentConfig
|
|
11
|
+
from sweagent.run.common import BasicCLI
|
|
12
|
+
from sweagent.run.hooks.abstract import RunHook
|
|
13
|
+
from sweagent.run.run_single import RunSingle, RunSingleConfig
|
|
14
|
+
from sweagent.tools.bundle import Bundle
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RaisesExceptionHook(RunHook):
|
|
18
|
+
def on_instance_start(self, *args, **kwargs):
|
|
19
|
+
msg = "test exception"
|
|
20
|
+
raise ValueError(msg)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.slow
|
|
24
|
+
def test_run_single_raises_exception():
|
|
25
|
+
rsc = RunSingleConfig(agent=DefaultAgentConfig(model=InstantEmptySubmitModelConfig()))
|
|
26
|
+
rs = RunSingle.from_config(rsc)
|
|
27
|
+
rs.add_hook(RaisesExceptionHook())
|
|
28
|
+
with pytest.raises(ValueError, match="test exception"):
|
|
29
|
+
rs.run()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture
|
|
33
|
+
def agent_config_with_commands():
|
|
34
|
+
ac = DefaultAgentConfig(model=InstantEmptySubmitModelConfig())
|
|
35
|
+
ac.tools.bundles = [
|
|
36
|
+
Bundle(path=TOOLS_DIR / "registry"),
|
|
37
|
+
Bundle(path=TOOLS_DIR / "windowed"),
|
|
38
|
+
Bundle(path=TOOLS_DIR / "submit"),
|
|
39
|
+
]
|
|
40
|
+
ac.tools.env_variables = {"WINDOW": 100}
|
|
41
|
+
assert (TOOLS_DIR / "submit").exists()
|
|
42
|
+
# Make sure dependent properties are set
|
|
43
|
+
ac.tools.model_post_init(None)
|
|
44
|
+
return ac
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.mark.slow
|
|
48
|
+
def test_hidden_tools(tmpdir):
|
|
49
|
+
ac = DefaultAgentConfig(model=InstantEmptySubmitModelConfig())
|
|
50
|
+
ac.tools.env_variables = {"WINDOW": 100}
|
|
51
|
+
ac.tools.bundles = [
|
|
52
|
+
Bundle(path=TOOLS_DIR / "registry"),
|
|
53
|
+
Bundle(path=TOOLS_DIR / "windowed", hidden_tools=["scroll_up"]),
|
|
54
|
+
Bundle(path=TOOLS_DIR / "submit"),
|
|
55
|
+
]
|
|
56
|
+
ac.model.name = "instant_empty_submit"
|
|
57
|
+
rsc = RunSingleConfig(
|
|
58
|
+
env=EnvironmentConfig(),
|
|
59
|
+
agent=ac,
|
|
60
|
+
output_dir=Path(tmpdir),
|
|
61
|
+
)
|
|
62
|
+
rs = RunSingle.from_config(rsc)
|
|
63
|
+
rs.run()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.mark.slow
|
|
67
|
+
def test_run_ies(tmpdir, agent_config_with_commands):
|
|
68
|
+
# ies = instant empty submit
|
|
69
|
+
ac = agent_config_with_commands
|
|
70
|
+
ac.model.name = "instant_empty_submit"
|
|
71
|
+
rsc = RunSingleConfig(
|
|
72
|
+
env=EnvironmentConfig(),
|
|
73
|
+
agent=ac,
|
|
74
|
+
output_dir=Path(tmpdir),
|
|
75
|
+
)
|
|
76
|
+
rs = RunSingle.from_config(rsc)
|
|
77
|
+
rs.agent.tools.mock_state = {"open_file": "asdf123", "working_dir": "/root"}
|
|
78
|
+
rs.run()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pytest.mark.slow
|
|
82
|
+
@pytest.mark.parametrize("repo", ["local", "github"])
|
|
83
|
+
@pytest.mark.parametrize("problem_statement_source", ["github", "local", "text"])
|
|
84
|
+
def test_run_ies_repo_ps_matrix(
|
|
85
|
+
tmpdir,
|
|
86
|
+
swe_agent_test_repo_clone,
|
|
87
|
+
repo,
|
|
88
|
+
problem_statement_source,
|
|
89
|
+
):
|
|
90
|
+
output_formats = ["traj", "pred", "patch"]
|
|
91
|
+
for fmt in output_formats:
|
|
92
|
+
assert not list(Path(tmpdir).glob(f"*.{fmt}"))
|
|
93
|
+
if problem_statement_source == "github":
|
|
94
|
+
ps_args = ["--problem_statement.github_url", "https://github.com/swe-agent/test-repo/issues/1"]
|
|
95
|
+
elif problem_statement_source == "local":
|
|
96
|
+
ps_args = ["--problem_statement.path", str(swe_agent_test_repo_clone / "problem_statements" / "1.md")]
|
|
97
|
+
elif problem_statement_source == "text":
|
|
98
|
+
ps_args = ["--problem_statement.text='this is a test'"]
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError(problem_statement_source)
|
|
101
|
+
if repo == "local":
|
|
102
|
+
repo_args = ["--env.repo.path", str(swe_agent_test_repo_clone)]
|
|
103
|
+
elif repo == "github":
|
|
104
|
+
repo_args = ["--env.repo.github_url", "https://github.com/swe-agent/test-repo"]
|
|
105
|
+
else:
|
|
106
|
+
raise ValueError(repo)
|
|
107
|
+
args = [
|
|
108
|
+
"--agent.model.name=instant_empty_submit",
|
|
109
|
+
"--output_dir",
|
|
110
|
+
str(tmpdir),
|
|
111
|
+
*ps_args,
|
|
112
|
+
*repo_args,
|
|
113
|
+
"--config",
|
|
114
|
+
str(CONFIG_DIR / "default_backticks.yaml"),
|
|
115
|
+
]
|
|
116
|
+
print(args)
|
|
117
|
+
rs_config = BasicCLI(RunSingleConfig).get_config(args)
|
|
118
|
+
print(rs_config)
|
|
119
|
+
rs = RunSingle.from_config(rs_config) # type: ignore
|
|
120
|
+
with tmpdir.as_cwd():
|
|
121
|
+
# Test that we can run run.py also independently from repo dir
|
|
122
|
+
rs.run()
|
|
123
|
+
for fmt in output_formats:
|
|
124
|
+
print(fmt, list(Path(tmpdir).iterdir()))
|
|
125
|
+
assert len(list(Path(tmpdir).rglob(f"*.{fmt}"))) == 1
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from sweagent.tools.commands import Argument, Command
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_command_parsing_formats():
|
|
7
|
+
"""Test various signature formats and default parsing."""
|
|
8
|
+
# Default format (no signature)
|
|
9
|
+
command = Command(
|
|
10
|
+
name="test_cmd",
|
|
11
|
+
docstring="A test command",
|
|
12
|
+
arguments=[
|
|
13
|
+
Argument(name="arg1", type="string", description="First argument", required=True),
|
|
14
|
+
Argument(name="arg2", type="integer", description="Second argument", required=False),
|
|
15
|
+
],
|
|
16
|
+
)
|
|
17
|
+
assert command.invoke_format == "test_cmd {arg1} {arg2} "
|
|
18
|
+
|
|
19
|
+
# Angle brackets
|
|
20
|
+
command = Command(
|
|
21
|
+
name="goto",
|
|
22
|
+
signature="goto <line_number>",
|
|
23
|
+
docstring="moves the window to show line_number",
|
|
24
|
+
arguments=[Argument(name="line_number", type="integer", description="line number", required=True)],
|
|
25
|
+
)
|
|
26
|
+
assert command.invoke_format == "goto {line_number}"
|
|
27
|
+
|
|
28
|
+
# Optional brackets (stripped in invoke_format)
|
|
29
|
+
command = Command(
|
|
30
|
+
name="open",
|
|
31
|
+
signature='open "<path>" [<line_number>]',
|
|
32
|
+
docstring="opens file at path",
|
|
33
|
+
arguments=[
|
|
34
|
+
Argument(name="path", type="string", description="file path", required=True),
|
|
35
|
+
Argument(name="line_number", type="integer", description="line number", required=False),
|
|
36
|
+
],
|
|
37
|
+
)
|
|
38
|
+
assert command.invoke_format == 'open "{path}" {line_number}'
|
|
39
|
+
|
|
40
|
+
# Flag-style arguments
|
|
41
|
+
command = Command(
|
|
42
|
+
name="grep",
|
|
43
|
+
signature="grep --pattern <pattern> --file <file>",
|
|
44
|
+
docstring="search for pattern in file",
|
|
45
|
+
arguments=[
|
|
46
|
+
Argument(name="pattern", type="string", description="search pattern", required=True),
|
|
47
|
+
Argument(name="file", type="string", description="file to search", required=True),
|
|
48
|
+
],
|
|
49
|
+
)
|
|
50
|
+
assert command.invoke_format == "grep --pattern {pattern} --file {file}"
|
|
51
|
+
|
|
52
|
+
# No arguments
|
|
53
|
+
command = Command(name="scroll_up", signature="scroll_up", docstring="scrolls up", arguments=[])
|
|
54
|
+
assert command.invoke_format == "scroll_up"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_argument_validation():
|
|
58
|
+
"""Test argument validation rules."""
|
|
59
|
+
# Required arguments must come before optional ones
|
|
60
|
+
with pytest.raises(ValueError, match="Required argument.*cannot come after optional arguments"):
|
|
61
|
+
Command(
|
|
62
|
+
name="bad_order",
|
|
63
|
+
docstring="bad argument order",
|
|
64
|
+
arguments=[
|
|
65
|
+
Argument(name="optional", type="string", description="optional", required=False),
|
|
66
|
+
Argument(name="required", type="string", description="required", required=True),
|
|
67
|
+
],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Duplicate argument names
|
|
71
|
+
with pytest.raises(ValueError, match="Duplicate argument names"):
|
|
72
|
+
Command(
|
|
73
|
+
name="duplicate",
|
|
74
|
+
docstring="duplicate args",
|
|
75
|
+
arguments=[
|
|
76
|
+
Argument(name="arg1", type="string", description="first", required=True),
|
|
77
|
+
Argument(name="arg1", type="string", description="duplicate", required=True),
|
|
78
|
+
],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_argument_name_patterns():
|
|
83
|
+
"""Test valid and invalid argument name patterns."""
|
|
84
|
+
# Valid names including single characters
|
|
85
|
+
valid_names = ["a", "x", "n", "simple", "with_underscore", "with-dash", "with123numbers", "_starts_with_underscore"]
|
|
86
|
+
|
|
87
|
+
for name in valid_names:
|
|
88
|
+
command = Command(
|
|
89
|
+
name="test",
|
|
90
|
+
docstring="test",
|
|
91
|
+
arguments=[Argument(name=name, type="string", description="test", required=True)],
|
|
92
|
+
)
|
|
93
|
+
assert command.arguments[0].name == name
|
|
94
|
+
|
|
95
|
+
# Invalid names
|
|
96
|
+
invalid_names = ["123starts_with_number", ""]
|
|
97
|
+
|
|
98
|
+
for name in invalid_names:
|
|
99
|
+
with pytest.raises(ValueError, match="Invalid argument name"):
|
|
100
|
+
Command(
|
|
101
|
+
name="test",
|
|
102
|
+
docstring="test",
|
|
103
|
+
arguments=[Argument(name=name, type="string", description="test", required=True)],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_signature_argument_consistency():
|
|
108
|
+
"""Test that signatures and arguments must be consistent."""
|
|
109
|
+
# Missing argument in signature
|
|
110
|
+
with pytest.raises(ValueError, match="Missing argument.*in signature"):
|
|
111
|
+
Command(
|
|
112
|
+
name="missing_arg",
|
|
113
|
+
signature="missing_arg <existing_arg>",
|
|
114
|
+
docstring="missing argument in signature",
|
|
115
|
+
arguments=[
|
|
116
|
+
Argument(name="existing_arg", type="string", description="exists", required=True),
|
|
117
|
+
Argument(name="missing_arg", type="string", description="not in signature", required=True),
|
|
118
|
+
],
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Extra argument in signature
|
|
122
|
+
with pytest.raises(ValueError, match="Argument names.*do not match"):
|
|
123
|
+
Command(
|
|
124
|
+
name="extra_arg",
|
|
125
|
+
signature="extra_arg <arg1> <extra>",
|
|
126
|
+
docstring="extra argument in signature",
|
|
127
|
+
arguments=[Argument(name="arg1", type="string", description="exists", required=True)],
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_function_calling_tool_generation():
|
|
132
|
+
"""Test OpenAI function calling tool generation."""
|
|
133
|
+
command = Command(
|
|
134
|
+
name="test_function",
|
|
135
|
+
docstring="A test function for OpenAI",
|
|
136
|
+
arguments=[
|
|
137
|
+
Argument(name="required_arg", type="string", description="Required string argument", required=True),
|
|
138
|
+
Argument(
|
|
139
|
+
name="enum_arg", type="string", description="Enum argument", required=True, enum=["option1", "option2"]
|
|
140
|
+
),
|
|
141
|
+
Argument(name="optional_arg", type="integer", description="Optional integer argument", required=False),
|
|
142
|
+
],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
tool = command.get_function_calling_tool()
|
|
146
|
+
|
|
147
|
+
assert tool["type"] == "function"
|
|
148
|
+
assert tool["function"]["name"] == "test_function"
|
|
149
|
+
assert tool["function"]["description"] == "A test function for OpenAI"
|
|
150
|
+
|
|
151
|
+
properties = tool["function"]["parameters"]["properties"]
|
|
152
|
+
assert properties["required_arg"]["type"] == "string"
|
|
153
|
+
assert properties["optional_arg"]["type"] == "integer"
|
|
154
|
+
assert properties["enum_arg"]["enum"] == ["option1", "option2"]
|
|
155
|
+
|
|
156
|
+
required = tool["function"]["parameters"]["required"]
|
|
157
|
+
assert "required_arg" in required
|
|
158
|
+
assert "enum_arg" in required
|
|
159
|
+
assert "optional_arg" not in required
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_multiline_command():
|
|
163
|
+
"""Test multi-line commands with end markers."""
|
|
164
|
+
command = Command(
|
|
165
|
+
name="edit",
|
|
166
|
+
signature="edit <filename>",
|
|
167
|
+
docstring="Edit a file with multi-line content",
|
|
168
|
+
end_name="EOF",
|
|
169
|
+
arguments=[Argument(name="filename", type="string", description="file to edit", required=True)],
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
assert command.invoke_format == "edit {filename}"
|
|
173
|
+
assert command.end_name == "EOF"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_custom_argument_format():
|
|
177
|
+
"""Test custom argument formatting."""
|
|
178
|
+
command = Command(
|
|
179
|
+
name="custom_format",
|
|
180
|
+
docstring="Test custom argument formatting",
|
|
181
|
+
arguments=[
|
|
182
|
+
Argument(
|
|
183
|
+
name="arg1",
|
|
184
|
+
type="string",
|
|
185
|
+
description="Custom formatted argument",
|
|
186
|
+
required=True,
|
|
187
|
+
argument_format="--{value}",
|
|
188
|
+
)
|
|
189
|
+
],
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
assert command.arguments[0].argument_format == "--{value}"
|
|
193
|
+
assert command.invoke_format == "custom_format {arg1} "
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from sweagent import REPO_ROOT
|
|
6
|
+
from sweagent.utils.config import _convert_path_to_abspath, _convert_paths_to_abspath
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_convert_path_to_abspath():
|
|
10
|
+
assert _convert_path_to_abspath("sadf") == REPO_ROOT / "sadf"
|
|
11
|
+
assert _convert_path_to_abspath("/sadf") == Path("/sadf")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_convert_paths_to_abspath():
|
|
15
|
+
assert _convert_paths_to_abspath([Path("sadf"), Path("/sadf")]) == [REPO_ROOT / "sadf", Path("/sadf")]
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from unittest import mock
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture
|
|
7
|
+
def with_tmp_env_file(tmp_path):
|
|
8
|
+
env_file = tmp_path / ".swe-agent-env"
|
|
9
|
+
env_file.write_text("{}")
|
|
10
|
+
with mock.patch.dict("os.environ", {"SWE_AGENT_ENV_FILE": str(env_file)}, clear=True):
|
|
11
|
+
yield env_file
|
|
12
|
+
env_file.unlink()
|