featurebench 0.2.1__tar.gz → 0.2.3__tar.gz

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.
Files changed (136) hide show
  1. {featurebench-0.2.1 → featurebench-0.2.3}/PKG-INFO +7 -4
  2. {featurebench-0.2.1 → featurebench-0.2.3}/README.md +5 -2
  3. {featurebench-0.2.1 → featurebench-0.2.3}/config_example.toml +11 -2
  4. {featurebench-0.2.1 → featurebench-0.2.3}/docs/config.md +17 -4
  5. {featurebench-0.2.1 → featurebench-0.2.3}/docs/harness_cli_arg.md +5 -0
  6. {featurebench-0.2.1 → featurebench-0.2.3}/docs/infer_cli_arg.md +15 -1
  7. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/cli.py +2 -2
  8. featurebench-0.2.3/featurebench/environment_fixes/__init__.py +13 -0
  9. featurebench-0.2.3/featurebench/environment_fixes/base.py +24 -0
  10. featurebench-0.2.3/featurebench/environment_fixes/mlflow.py +41 -0
  11. featurebench-0.2.3/featurebench/environment_fixes/registry.py +43 -0
  12. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/run_evaluation.py +81 -9
  13. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/runtime.py +26 -12
  14. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/base.py +14 -2
  15. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/mini_swe_agent.py +110 -12
  16. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/config.py +90 -11
  17. featurebench-0.2.3/featurebench/infer/environments/__init__.py +1 -0
  18. featurebench-0.2.3/featurebench/infer/environments/mini_swe.py +185 -0
  19. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/models.py +16 -2
  20. featurebench-0.2.3/featurebench/infer/network.py +473 -0
  21. featurebench-0.2.3/featurebench/infer/proxy_bridge.py +57 -0
  22. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/run_infer.py +195 -10
  23. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/runtime.py +138 -5
  24. {featurebench-0.2.1 → featurebench-0.2.3}/pyproject.toml +1 -1
  25. featurebench-0.2.3/tests/test_dataset_version.py +215 -0
  26. featurebench-0.2.3/tests/test_environment_fixes.py +52 -0
  27. featurebench-0.2.3/tests/test_infer_network.py +150 -0
  28. featurebench-0.2.3/tests/test_infer_runtime.py +163 -0
  29. featurebench-0.2.3/tests/test_mini_swe_agent.py +213 -0
  30. {featurebench-0.2.1 → featurebench-0.2.3}/uv.lock +43 -43
  31. {featurebench-0.2.1 → featurebench-0.2.3}/.gitignore +0 -0
  32. {featurebench-0.2.1 → featurebench-0.2.3}/.python-version +0 -0
  33. {featurebench-0.2.1 → featurebench-0.2.3}/LICENSE +0 -0
  34. {featurebench-0.2.1 → featurebench-0.2.3}/docs/pics/logo.png +0 -0
  35. {featurebench-0.2.1 → featurebench-0.2.3}/docs/pics/workflow.png +0 -0
  36. {featurebench-0.2.1 → featurebench-0.2.3}/docs/pipeline.md +0 -0
  37. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/__init__.py +0 -0
  38. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/classification/__init__.py +0 -0
  39. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/classification/code_classifier.py +0 -0
  40. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/classification/file_analyzer.py +0 -0
  41. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/classification/llm_top_classifier.py +0 -0
  42. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/classification/p2p_chooser.py +0 -0
  43. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/conversion/__init__.py +0 -0
  44. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/conversion/case_converter.py +0 -0
  45. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/conversion/eval_code.py +0 -0
  46. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/conversion/llm_prompt_generator.py +0 -0
  47. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/conversion/llm_testfile_adjuster.py +0 -0
  48. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/conversion/render_task.py +0 -0
  49. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/docker/__init__.py +0 -0
  50. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/docker/dynamic_tracer.py +0 -0
  51. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/docker/image_manager.py +0 -0
  52. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/docker/test_runner.py +0 -0
  53. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/docker/test_scanner.py +0 -0
  54. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/__init__.py +0 -0
  55. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/constants.py +0 -0
  56. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/container.py +0 -0
  57. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/grading.py +0 -0
  58. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/report.py +0 -0
  59. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/review_codes.py +0 -0
  60. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/test_parsers.py +0 -0
  61. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/harness/utils.py +0 -0
  62. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/__init__.py +0 -0
  63. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/__init__.py +0 -0
  64. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/claude_code.py +0 -0
  65. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/codex.py +0 -0
  66. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/gemini_cli.py +0 -0
  67. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/agents/openhands.py +0 -0
  68. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/container.py +0 -0
  69. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/gpu_scheduler.py +0 -0
  70. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/output.py +0 -0
  71. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/infer/render_infer_log.py +0 -0
  72. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/llm/__init__.py +0 -0
  73. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/llm/llm_caller.py +0 -0
  74. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/llm/llm_exceptions.py +0 -0
  75. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/mask/__init__.py +0 -0
  76. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/mask/mask_generator.py +0 -0
  77. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/mask/mask_validator.py +0 -0
  78. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/mask/signature_extractor.py +0 -0
  79. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/pipeline.py +0 -0
  80. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/post_verify/__init__.py +0 -0
  81. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/post_verify/f2p_validator.py +0 -0
  82. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/post_verify/level2_validator.py +0 -0
  83. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/post_verify/p2p_validator.py +0 -0
  84. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/constants/fast_images.txt +0 -0
  85. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/constants/full_images.txt +0 -0
  86. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/constants/lite_images.txt +0 -0
  87. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/constants/python_example.py +0 -0
  88. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python310.py +0 -0
  89. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python310_cu118_torch212.py +0 -0
  90. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python310_cu121_torch251.py +0 -0
  91. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python310_cu121_torch28.py +0 -0
  92. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python311.py +0 -0
  93. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python311_cu121_torch28.py +0 -0
  94. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python312.py +0 -0
  95. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python312_cu121_torch251.py +0 -0
  96. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python312_cu121_torch26.py +0 -0
  97. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python312_cu121_torch28.py +0 -0
  98. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python312_torch251.py +0 -0
  99. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/dockerfiles/python39.py +0 -0
  100. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/scripts/dynamic_script/autograd_backward_patch.py +0 -0
  101. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/scripts/dynamic_script/dynamic_script.py +0 -0
  102. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/scripts/dynamic_script/import_hook_patch.py +0 -0
  103. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/scripts/dynamic_script/joblib_patch.py +0 -0
  104. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/scripts/dynamic_script/sitecustomize.py +0 -0
  105. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/scripts/scanner_script.py +0 -0
  106. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/templates/appendix.md.j2 +0 -0
  107. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/templates/black_links.txt.j2 +0 -0
  108. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/templates/config_template.yaml +0 -0
  109. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/templates/level_1.j2 +0 -0
  110. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/templates/level_2.j2 +0 -0
  111. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/resources/templates/llm_prompt_templates.yaml +0 -0
  112. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/scripts/cal_eval_outputs.py +0 -0
  113. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/scripts/post_filter_cases.py +0 -0
  114. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/scripts/pull_images.py +0 -0
  115. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/__init__.py +0 -0
  116. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/command_utils.py +0 -0
  117. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/config.py +0 -0
  118. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/docker_images.py +0 -0
  119. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/logger.py +0 -0
  120. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/parser/__init__.py +0 -0
  121. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/parser/pytest_parser.py +0 -0
  122. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/repo_manager.py +0 -0
  123. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/storage.py +0 -0
  124. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/utils/utils.py +0 -0
  125. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/__init__.py +0 -0
  126. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/post_additions.py +0 -0
  127. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/prompts/add_prompt_lv1.txt +0 -0
  128. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/prompts/add_prompt_lv2.txt +0 -0
  129. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/prompts/prompt_template_lv1.txt +0 -0
  130. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/prompts/prompt_template_lv2.txt +0 -0
  131. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/run_verification.py +0 -0
  132. {featurebench-0.2.1 → featurebench-0.2.3}/featurebench/verified/runner.py +0 -0
  133. {featurebench-0.2.1 → featurebench-0.2.3}/tests/test_eval_data_container_cleanup.py +0 -0
  134. {featurebench-0.2.1 → featurebench-0.2.3}/tests/test_infer_container_cleanup.py +0 -0
  135. {featurebench-0.2.1 → featurebench-0.2.3}/tests/test_openhands_session_cache.py +0 -0
  136. {featurebench-0.2.1 → featurebench-0.2.3}/tests/test_pull_images.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: featurebench
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: FeatureBench Pipeline - A test-driven data generation pipeline for building and evaluating feature-level coding benchmarks
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -41,6 +41,8 @@ It provides a unified CLI to run inference, evaluation, and dataset generation.
41
41
 
42
42
  ## 📰 News
43
43
 
44
+ 📦 **2026.08.24**: We released **FeatureBench Dataset V1.1**, primarily correcting inconsistencies and omissions in task problem statements.
45
+
44
46
  📊 **2026.05.18**: We added **lite split** evaluation results for frontier models including **GPT-5.5, Claude Opus 4.7, DeepSeek-V4, GLM-5.1, Kimi-2.6, Mimo-V2.5-Pro**, and more to the leaderboard.
45
47
 
46
48
  🚀 **2026.03.27**: We released the **fast split** containing 100 instances (a subset of full split). These instances require no GPU and are optimized for rapid evaluation. On an Intel Xeon Platinum 8457C with 944GB RAM, the average evaluation time per instance using gold patches is **57.2 seconds**.
@@ -50,7 +52,7 @@ It provides a unified CLI to run inference, evaluation, and dataset generation.
50
52
  <!-- LEADERBOARD_START -->
51
53
  ## 🏆 Leaderboard
52
54
 
53
- [Full interactive leaderboard](https://libercoders.github.io/FeatureBench/leaderboard/) with tabs, filters, and sorting.
55
+ [Full interactive leaderboard](https://libercoders.github.io/FeatureBench/leaderboard/) with tabs, filters, and sorting (Dataset V1.0).
54
56
 
55
57
  <details open>
56
58
  <summary><b>Lite split results, ranked by %PASSED</b></summary>
@@ -121,6 +123,7 @@ fb pull --mode /path/to/images.txt # one image name per line
121
123
  ```bash
122
124
  fb infer \
123
125
  --config-path config.toml \
126
+ --data-version v1.1 \
124
127
  --agent mini_swe_agent \
125
128
  --model openai/qwen3-coder-480b-a35b-instruct \
126
129
  --split fast
@@ -156,4 +159,4 @@ If you found FeatureBench useful, please cite us as:
156
159
 
157
160
  ## 📧 Contact
158
161
 
159
- If you have any questions, feel free to contact [qixingzhou1125@gmail.com](mailto:qixingzhou1125@gmail.com) or [zjcheng2022@gmail.com](mailto:zjcheng2022@gmail.com).
162
+ If you have any questions, feel free to contact [qixing.zhou@smail.nju.edu.cn](mailto:qixing.zhou@smail.nju.edu.cn) or [zjcheng2022@gmail.com](mailto:zjcheng2022@gmail.com).
@@ -17,6 +17,8 @@ It provides a unified CLI to run inference, evaluation, and dataset generation.
17
17
 
18
18
  ## 📰 News
19
19
 
20
+ 📦 **2026.08.24**: We released **FeatureBench Dataset V1.1**, primarily correcting inconsistencies and omissions in task problem statements.
21
+
20
22
  📊 **2026.05.18**: We added **lite split** evaluation results for frontier models including **GPT-5.5, Claude Opus 4.7, DeepSeek-V4, GLM-5.1, Kimi-2.6, Mimo-V2.5-Pro**, and more to the leaderboard.
21
23
 
22
24
  🚀 **2026.03.27**: We released the **fast split** containing 100 instances (a subset of full split). These instances require no GPU and are optimized for rapid evaluation. On an Intel Xeon Platinum 8457C with 944GB RAM, the average evaluation time per instance using gold patches is **57.2 seconds**.
@@ -26,7 +28,7 @@ It provides a unified CLI to run inference, evaluation, and dataset generation.
26
28
  <!-- LEADERBOARD_START -->
27
29
  ## 🏆 Leaderboard
28
30
 
29
- [Full interactive leaderboard](https://libercoders.github.io/FeatureBench/leaderboard/) with tabs, filters, and sorting.
31
+ [Full interactive leaderboard](https://libercoders.github.io/FeatureBench/leaderboard/) with tabs, filters, and sorting (Dataset V1.0).
30
32
 
31
33
  <details open>
32
34
  <summary><b>Lite split results, ranked by %PASSED</b></summary>
@@ -97,6 +99,7 @@ fb pull --mode /path/to/images.txt # one image name per line
97
99
  ```bash
98
100
  fb infer \
99
101
  --config-path config.toml \
102
+ --data-version v1.1 \
100
103
  --agent mini_swe_agent \
101
104
  --model openai/qwen3-coder-480b-a35b-instruct \
102
105
  --split fast
@@ -132,4 +135,4 @@ If you found FeatureBench useful, please cite us as:
132
135
 
133
136
  ## 📧 Contact
134
137
 
135
- If you have any questions, feel free to contact [qixingzhou1125@gmail.com](mailto:qixingzhou1125@gmail.com) or [zjcheng2022@gmail.com](mailto:zjcheng2022@gmail.com).
138
+ If you have any questions, feel free to contact [qixing.zhou@smail.nju.edu.cn](mailto:qixing.zhou@smail.nju.edu.cn) or [zjcheng2022@gmail.com](mailto:zjcheng2022@gmail.com).
@@ -6,6 +6,14 @@ HF_ENDPOINT=""
6
6
  HF_TOKEN=""
7
7
  GITHUB_TOKEN=""
8
8
 
9
+ # ==============================================================================
10
+ # Hugging Face Dataset
11
+ # ==============================================================================
12
+ [dataset]
13
+ # Dataset tag, branch, or commit SHA used by both `fb infer` and `fb eval`.
14
+ # The CLI flag `--data-version` overrides this value.
15
+ revision="v1.1"
16
+
9
17
  # ==============================================================================
10
18
  # DataPipeline Configuration
11
19
  # ==============================================================================
@@ -44,7 +52,8 @@ base_url = "http://localhost:8080/v1" # vLLM server URL
44
52
  # Optional: Host cache directory for agent downloads; will be mounted to /download inside containers
45
53
  # Set to empty to disable cache mount (each container will re-download/install)
46
54
  # Use an absolute path here
47
- download_cache_dir="/path/to/FeatureBench/download_cache"
55
+ # Optional shared install cache. Leave unset for leakage-resistant evaluations.
56
+ # download_cache_dir="/path/to/FeatureBench/download_cache"
48
57
 
49
58
  # OpenHands Agent Configuration
50
59
  [infer_config.openhands]
@@ -109,4 +118,4 @@ MSWEA_BASE_URL = ""
109
118
  # Optional: Cost tracking mode (default behavior in FB adapter is ignore_errors)
110
119
  MSWEA_COST_TRACKING = ""
111
120
  # Optional: Lock mini_swe_agent version (leave empty to use latest)
112
- MINI_SWE_AGENT_VERSION = ""
121
+ MINI_SWE_AGENT_VERSION = ""
@@ -31,13 +31,25 @@ HF_TOKEN = ""
31
31
  GITHUB_TOKEN = ""
32
32
  ```
33
33
 
34
+ ### [dataset] (dataset version used by harness / infer)
35
+
36
+ ```toml
37
+ [dataset]
38
+ revision = "v1.1"
39
+ ```
40
+
41
+ - `revision`: Hugging Face dataset tag, branch, or commit SHA used by both
42
+ `fb infer` and `fb eval`. The default is `v1.1`.
43
+ - `--data-version` overrides this value for a single command.
44
+
34
45
  ---
35
46
 
36
47
  ## A. Harness
37
48
 
38
49
  The core idea of harness is: read the `output.jsonl` produced by infer, apply each patch to the corresponding repository image, run tests, and generate evaluation results.
39
50
 
40
- When harness loads the HuggingFace dataset during evaluation, it uses `HF_ENDPOINT` from `[env_vars]`.
51
+ When harness loads the HuggingFace dataset during evaluation, it uses `HF_ENDPOINT`
52
+ from `[env_vars]` and the pinned revision from `[dataset]`.
41
53
 
42
54
  ---
43
55
 
@@ -56,10 +68,11 @@ How infer reads `config.toml`:
56
68
 
57
69
  ```toml
58
70
  [infer]
59
- download_cache_dir = "/abs/path/to/FeatureBench/download_cache"
71
+ # Optional shared install cache. Leave unset for leakage-resistant evaluations.
72
+ # download_cache_dir = "/abs/path/to/FeatureBench/download_cache"
60
73
  ```
61
74
 
62
- - `download_cache_dir`: host-side cache directory that will be mounted into the container at `/download`.
75
+ - `download_cache_dir`: optional host-side cache directory mounted into the container at `/download`. It is disabled by default because the agent can read shared cache contents during inference.
63
76
  - Purpose: speed up agent installs/downloads (e.g. npm cache).
64
77
  - Empty means no mount (each container downloads on its own; usually slower).
65
78
 
@@ -208,4 +221,4 @@ Example (local vLLM):
208
221
  [llm.local]
209
222
  backend = "vllm"
210
223
  base_url = "http://localhost:8080/v1"
211
- ```
224
+ ```
@@ -51,6 +51,11 @@ fb eval \
51
51
  HuggingFace dataset repo name (e.g., `LiberCoders/FeatureBench`).
52
52
  Default: `LiberCoders/FeatureBench`.
53
53
 
54
+ - `--data-version`
55
+ Hugging Face dataset tag, branch, or commit SHA.
56
+ Normally inferred from the inference run's `run_metadata.json`.
57
+ If unavailable, falls back to `[dataset].revision` or `v1.1`.
58
+
54
59
  - `--split`
55
60
  Dataset split name (e.g., `lite`, `full`).
56
61
  Default: `full`.
@@ -6,6 +6,7 @@ This document describes all CLI arguments supported by `fb infer`.
6
6
 
7
7
  ```bash
8
8
  fb infer \
9
+ --data-version v1.1 \
9
10
  --agent gemini_cli \
10
11
  --model gemini-3-pro-preview
11
12
  ```
@@ -53,6 +54,12 @@ flags can override metadata (see the argument list below).
53
54
  HuggingFace dataset repo name (e.g., `LiberCoders/FeatureBench`).
54
55
  Default: `LiberCoders/FeatureBench` in non-resume mode.
55
56
 
57
+ - `--data-version`
58
+ Hugging Face dataset tag, branch, or commit SHA.
59
+ Overrides `[dataset].revision` from `config.toml`; default: `v1.1`.
60
+ Saved into `run_metadata.json`; resume mode keeps the recorded dataset version.
61
+ This is separate from `--version`, which controls the agent software version.
62
+
56
63
  - `--split`
57
64
  Dataset split name (e.g., `lite`, `full`).
58
65
  Default: `full` in non-resume mode. In resume mode, uses metadata.
@@ -153,6 +160,13 @@ flags can override metadata (see the argument list below).
153
160
  Default: no override (OpenHands default applies).
154
161
  Resume mode: ignored (uses metadata).
155
162
 
163
+ ### mini-swe-agent Only
164
+
165
+ - `--cost-limit`
166
+ Per-task cost limit in USD, passed to mini-swe-agent.
167
+ Set to `0` to disable the limit. If omitted, mini-swe-agent's upstream default applies.
168
+ Resume mode: ignored (uses metadata).
169
+
156
170
  ## Section 4: Output Directory Structure
157
171
 
158
172
  ```
@@ -166,4 +180,4 @@ runs/{timestamp}/
166
180
  ├── infer.log # Agent execution log
167
181
  ├── run.log # Runtime log
168
182
  └── patch.diff # Generated patch
169
- ```
183
+ ```
@@ -31,7 +31,7 @@ def _print_help(stream=None) -> None:
31
31
  " data Run data pipeline (supports featurebench-pipeline args)\n"
32
32
  "\n"
33
33
  "Examples:\n"
34
- " fb infer --agent mini_swe_agent --model openai/gpt-4o --split fast\n"
34
+ " fb infer --data-version v1.1 --agent mini_swe_agent --model openai/gpt-4o --split fast\n"
35
35
  " fb eval --predictions-path runs/xxx/output.jsonl --split fast\n"
36
36
  " fb pull --mode fast\n"
37
37
  " fb data --config-path constants/python_new.py --output-dir runs/data\n"
@@ -128,4 +128,4 @@ def main(argv: Sequence[str] | None = None) -> int:
128
128
 
129
129
 
130
130
  if __name__ == "__main__":
131
- raise SystemExit(main())
131
+ raise SystemExit(main())
@@ -0,0 +1,13 @@
1
+ """Repository-specific fixes for leaked or duplicated task-environment artifacts.
2
+
3
+ Add a fix by implementing :class:`EnvironmentFix`, decorating it with
4
+ ``register_environment_fix``, and importing its module below. Each plugin is
5
+ matched by repository and is shared by inference and Oracle evaluation.
6
+ """
7
+
8
+ from featurebench.environment_fixes.registry import apply_environment_fixes
9
+
10
+ # Import built-in plugins so they register themselves.
11
+ from featurebench.environment_fixes import mlflow as _mlflow # noqa: F401, E402
12
+
13
+ __all__ = ["apply_environment_fixes"]
@@ -0,0 +1,24 @@
1
+ """Interfaces for repository-specific task-environment fixes."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from collections.abc import Callable
5
+ import logging
6
+
7
+
8
+ CommandRunner = Callable[[str], tuple[int, str | bytes]]
9
+
10
+
11
+ class EnvironmentFix(ABC):
12
+ """A targeted cleanup applied before the task repository becomes the Git baseline."""
13
+
14
+ name: str
15
+ repositories: frozenset[str]
16
+
17
+ def applies_to(self, repository: str) -> bool:
18
+ """Return whether this fix applies to a canonical ``owner/repo`` name."""
19
+ normalized = repository.strip().casefold()
20
+ return normalized in {repo.casefold() for repo in self.repositories}
21
+
22
+ @abstractmethod
23
+ def apply(self, run_command: CommandRunner, logger: logging.Logger) -> bool:
24
+ """Apply and verify the fix, returning ``False`` on failure."""
@@ -0,0 +1,41 @@
1
+ """MLflow-specific task-environment cleanup."""
2
+
3
+ import logging
4
+
5
+ from featurebench.environment_fixes.base import CommandRunner, EnvironmentFix
6
+ from featurebench.environment_fixes.registry import register_environment_fix
7
+
8
+
9
+ @register_environment_fix
10
+ class MlflowParallelSourcesFix(EnvironmentFix):
11
+ """Remove unmasked MLflow package copies shipped in the monorepo's ``libs`` tree."""
12
+
13
+ name = "mlflow_parallel_sources"
14
+ repositories = frozenset({"mlflow/mlflow"})
15
+ paths = (
16
+ "/testbed/libs/skinny/mlflow",
17
+ "/testbed/libs/tracing/mlflow",
18
+ )
19
+
20
+ def apply(self, run_command: CommandRunner, logger: logging.Logger) -> bool:
21
+ paths = " ".join(self.paths)
22
+ command = f"""
23
+ paths=({paths})
24
+ for path in "${{paths[@]}}"; do
25
+ if [ -e "$path" ] || [ -L "$path" ]; then
26
+ rm -rf -- "$path" || exit 1
27
+ echo "Removed MLflow parallel source: $path"
28
+ fi
29
+ if [ -e "$path" ] || [ -L "$path" ]; then
30
+ echo "MLflow parallel source still exists: $path" >&2
31
+ exit 1
32
+ fi
33
+ done
34
+ """
35
+ exit_code, output = run_command(command)
36
+ if exit_code != 0:
37
+ if isinstance(output, bytes):
38
+ output = output.decode("utf-8", errors="replace")
39
+ logger.error("Failed to remove MLflow parallel sources: %s", output)
40
+ return False
41
+ return True
@@ -0,0 +1,43 @@
1
+ """Registry and execution helpers for task-environment fixes."""
2
+
3
+ import logging
4
+ from typing import TypeVar
5
+
6
+ from featurebench.environment_fixes.base import CommandRunner, EnvironmentFix
7
+
8
+
9
+ _FIXES: list[EnvironmentFix] = []
10
+ _EnvironmentFixType = TypeVar("_EnvironmentFixType", bound=type[EnvironmentFix])
11
+
12
+
13
+ def register_environment_fix(fix_type: _EnvironmentFixType) -> _EnvironmentFixType:
14
+ """Register an environment-fix class at import time."""
15
+ fix = fix_type()
16
+ if any(existing.name == fix.name for existing in _FIXES):
17
+ raise ValueError(f"Duplicate environment fix name: {fix.name}")
18
+ _FIXES.append(fix)
19
+ return fix_type
20
+
21
+
22
+ def apply_environment_fixes(
23
+ repository: str,
24
+ run_command: CommandRunner,
25
+ logger: logging.Logger,
26
+ ) -> bool:
27
+ """Apply every registered fix matching ``repository`` in registration order."""
28
+ matching_fixes = [fix for fix in _FIXES if fix.applies_to(repository)]
29
+ if not matching_fixes:
30
+ return True
31
+
32
+ for fix in matching_fixes:
33
+ logger.info("Applying environment fix %s for %s", fix.name, repository)
34
+ try:
35
+ if not fix.apply(run_command, logger):
36
+ logger.error("Environment fix %s failed for %s", fix.name, repository)
37
+ return False
38
+ except Exception:
39
+ logger.exception("Environment fix %s raised for %s", fix.name, repository)
40
+ return False
41
+ logger.info("Environment fix %s completed for %s", fix.name, repository)
42
+
43
+ return True
@@ -74,7 +74,13 @@ from featurebench.harness.utils import (
74
74
  parse_repo_settings,
75
75
  )
76
76
 
77
- from featurebench.infer.config import InferConfigLoader
77
+ from featurebench.infer.config import (
78
+ DEFAULT_DATASET_REVISION,
79
+ InferConfigLoader,
80
+ configure_hf_access,
81
+ resolve_hf_dataset_revision,
82
+ verify_loaded_dataset_revision,
83
+ )
78
84
  from featurebench.infer.gpu_scheduler import GpuLease, GpuScheduler, detect_host_gpu_ids, parse_gpu_id_list
79
85
 
80
86
  # Global console for rich output
@@ -747,6 +753,7 @@ def load_dataset_from_hf(
747
753
  split: str,
748
754
  dataset: str,
749
755
  config_path: str | None = None,
756
+ dataset_revision: str | None = None,
750
757
  ) -> pd.DataFrame:
751
758
  """
752
759
  Load dataset from HuggingFace using config.toml settings.
@@ -766,21 +773,39 @@ def load_dataset_from_hf(
766
773
  resolved_path = Path(config_path).expanduser() if config_path else None
767
774
  config_loader = InferConfigLoader(resolved_path)
768
775
  env_vars = config_loader.env_vars
769
- hf_token = env_vars.get("HF_TOKEN")
776
+ hf_token, hf_endpoint = configure_hf_access(env_vars)
777
+ configured_revision = config_loader.get_dataset_revision()
770
778
  except FileNotFoundError:
771
779
  console.print("[yellow]Warning: config.toml not found, using defaults[/]")
772
- hf_token = os.environ.get('HF_TOKEN', None)
780
+ hf_token, hf_endpoint = configure_hf_access({})
781
+ configured_revision = DEFAULT_DATASET_REVISION
782
+
783
+ requested_revision = str(dataset_revision or "").strip()
784
+ effective_revision = requested_revision or configured_revision
773
785
 
774
786
  console.print(f"[bold blue]Loading dataset from HuggingFace...[/]")
775
787
  console.print(f"[dim]Repository: {dataset}[/]")
776
788
  console.print(f"[dim]Split: {split}[/]")
789
+ console.print(f"[dim]Version: {effective_revision}[/]")
777
790
 
778
791
  if hf_token:
779
792
  console.print("[dim]Using HuggingFace token from config[/]")
780
793
 
781
794
  try:
795
+ resolved_revision = resolve_hf_dataset_revision(
796
+ dataset,
797
+ effective_revision,
798
+ hf_token,
799
+ hf_endpoint,
800
+ )
782
801
  # Load the specified split
783
- hf_dataset = load_dataset(dataset, split=split, token=hf_token)
802
+ hf_dataset = load_dataset(
803
+ dataset,
804
+ split=split,
805
+ token=hf_token,
806
+ revision=resolved_revision,
807
+ )
808
+ verify_loaded_dataset_revision(hf_dataset, resolved_revision)
784
809
  df = pd.DataFrame(hf_dataset)
785
810
 
786
811
  # Determine level from instance_id's last segment (e.g., "xxx.lv1" -> level 1)
@@ -809,6 +834,28 @@ def load_dataset_from_hf(
809
834
  raise
810
835
 
811
836
 
837
+ def resolve_eval_dataset_revision(
838
+ cli_revision: str | None,
839
+ run_metadata: dict | None,
840
+ ) -> str | None:
841
+ """Resolve the dataset revision for evaluation.
842
+
843
+ An explicit CLI value wins. Otherwise, evaluation follows the revision
844
+ recorded by inference. Runs created before revision tracking belong to the
845
+ original v1.0 dataset. When no run metadata is available, the dataset
846
+ loader falls back to the configured/default revision.
847
+ """
848
+ requested_revision = str(cli_revision or "").strip()
849
+ if requested_revision:
850
+ return requested_revision
851
+
852
+ if run_metadata:
853
+ recorded_revision = str(run_metadata.get("dataset_revision") or "").strip()
854
+ return recorded_revision or "v1.0"
855
+
856
+ return None
857
+
858
+
812
859
  def parse_args() -> argparse.Namespace:
813
860
  """Parse command line arguments."""
814
861
  parser = argparse.ArgumentParser(
@@ -874,6 +921,17 @@ def parse_args() -> argparse.Namespace:
874
921
  help="HuggingFace dataset repo name (e.g., 'LiberCoders/FeatureBench')",
875
922
  )
876
923
 
924
+ parser.add_argument(
925
+ "--data-version",
926
+ dest="dataset_version",
927
+ type=str,
928
+ default=None,
929
+ help=(
930
+ "Hugging Face dataset version (tag, branch, or commit SHA). "
931
+ "Default: infer from run_metadata.json, then [dataset].revision, then v1.1"
932
+ ),
933
+ )
934
+
877
935
  parser.add_argument(
878
936
  "--split",
879
937
  type=str,
@@ -956,15 +1014,23 @@ def main():
956
1014
  # we keep FAIL_TO_PASS test files visible during Level 1 patch application to avoid
957
1015
  # "patch touches deleted test file" apply failures.
958
1016
  white_enabled = False
1017
+ run_metadata = None
959
1018
  try:
960
1019
  metadata_path = output_dir / "run_metadata.json"
961
- if metadata_path.exists():
1020
+ if not use_gold_patch and metadata_path.exists():
962
1021
  with open(metadata_path, "r", encoding="utf-8") as f:
963
1022
  meta = json.load(f)
964
- if isinstance(meta, dict) and meta.get("white_box") is True:
965
- white_enabled = True
1023
+ if isinstance(meta, dict):
1024
+ run_metadata = meta
1025
+ white_enabled = meta.get("white_box") is True
966
1026
  except Exception:
967
1027
  white_enabled = False
1028
+ run_metadata = None
1029
+
1030
+ dataset_revision = resolve_eval_dataset_revision(
1031
+ args.dataset_version,
1032
+ run_metadata,
1033
+ )
968
1034
 
969
1035
  # Print startup banner
970
1036
  console.print()
@@ -996,7 +1062,13 @@ def main():
996
1062
  console.print()
997
1063
 
998
1064
  # Load dataset
999
- dataset = load_dataset_from_hf(console, args.split, args.dataset, args.config_path)
1065
+ dataset = load_dataset_from_hf(
1066
+ console,
1067
+ args.split,
1068
+ args.dataset,
1069
+ args.config_path,
1070
+ dataset_revision,
1071
+ )
1000
1072
 
1001
1073
  # Load predictions
1002
1074
  console.print(f"[bold blue]Loading predictions...[/]")
@@ -1221,4 +1293,4 @@ def main():
1221
1293
 
1222
1294
 
1223
1295
  if __name__ == "__main__":
1224
- raise SystemExit(main())
1296
+ raise SystemExit(main())
@@ -13,6 +13,7 @@ from typing import Any
13
13
  import pandas as pd
14
14
  from docker.models.containers import Container
15
15
 
16
+ from featurebench.environment_fixes import apply_environment_fixes
16
17
  from featurebench.harness.constants import (
17
18
  APPLY_PATCH_FAIL,
18
19
  APPLY_PATCH_PASS,
@@ -55,9 +56,11 @@ def run_instance_level1(
55
56
  1. Activate conda environment
56
57
  2. Restore project from /root/my_repo/
57
58
  3. Apply patch (for masking) and directly delete F2P test files
58
- 4. Reinitialize git
59
- 5. Apply agent's patch
60
- 6. Delete any generated F2P files, restore F2P test, and run tests
59
+ 4. Apply repository-specific environment fixes
60
+ 5. Reinitialize git
61
+ 6. Apply agent's patch
62
+ 7. Delete any generated F2P files and restore the F2P test
63
+ 8. Run tests
61
64
 
62
65
  Args:
63
66
  instance: Instance data (from HuggingFace)
@@ -177,8 +180,20 @@ def run_instance_level1(
177
180
  else:
178
181
  logger.warning("No FAIL_TO_PASS tests found in instance, skipping test file deletion")
179
182
 
180
- # Step 3: Reinitialize git repository
181
- logger.info("Step 3: Reinitializing git repository")
183
+ # Step 3: Apply the same repository-specific cleanup used during
184
+ # inference. Running it before Git initialization keeps the cleanup out
185
+ # of the candidate patch while making Oracle validation representative.
186
+ logger.info("Step 3: Applying repository-specific environment fixes")
187
+ if not apply_environment_fixes(
188
+ str(repo_name),
189
+ lambda command: exec_run_with_timeout(container, command, timeout=120),
190
+ logger,
191
+ ):
192
+ results["error"] = "Repository-specific environment fix failed"
193
+ return results
194
+
195
+ # Step 4: Reinitialize git repository
196
+ logger.info("Step 4: Reinitializing git repository")
182
197
  git_cmds = [
183
198
  "cd /testbed && rm -rf .git",
184
199
  "cd /testbed && git init",
@@ -194,8 +209,8 @@ def run_instance_level1(
194
209
  logger.warning(f"Git command failed: {cmd}")
195
210
  logger.warning(f"Output: {output.decode(UTF8, errors='replace')}")
196
211
 
197
- # Step 4: Apply agent's patch
198
- logger.info("Step 4: Applying agent patch")
212
+ # Step 5: Apply agent's patch
213
+ logger.info("Step 5: Applying agent patch")
199
214
  patch_content = pred[KEY_PREDICTION]
200
215
 
201
216
  if not patch_content or patch_content.strip() == "":
@@ -231,8 +246,8 @@ def run_instance_level1(
231
246
  logger.info(f"{APPLY_PATCH_PASS}")
232
247
  results["patch_applied"] = True
233
248
 
234
- # Step 5: Prepare for testing - delete generated F2P files and restore F2P test
235
- logger.info("Step 5: Preparing for testing - cleaning up and restoring F2P test")
249
+ # Step 6: Prepare for testing - delete generated F2P files and restore F2P test
250
+ logger.info("Step 6: Preparing for testing - cleaning up and restoring F2P test")
236
251
 
237
252
  fail_to_pass = instance.get('FAIL_TO_PASS', [])
238
253
  if not fail_to_pass:
@@ -268,8 +283,8 @@ def run_instance_level1(
268
283
  else:
269
284
  logger.warning("No test_patch available to restore F2P test file")
270
285
 
271
- # Step 6: Run tests
272
- logger.info("Step 6: Running tests")
286
+ # Step 7: Run tests
287
+ logger.info("Step 7: Running tests")
273
288
 
274
289
  # Get test configuration from repo_settings
275
290
  repo_settings = parse_repo_settings(instance)
@@ -589,4 +604,3 @@ def run_instance_level2(
589
604
  logger.error(traceback.format_exc())
590
605
  results["error"] = str(e)
591
606
  return results
592
-
@@ -41,6 +41,10 @@ class BaseAgent(ABC):
41
41
  self.env_vars = env_vars or {}
42
42
  self.logger = logger or logging.getLogger(__name__)
43
43
  self._kwargs = kwargs
44
+ # Agent adapters may populate this with their native terminal status.
45
+ # It deliberately remains optional because not every supported agent
46
+ # exposes a structured status artifact.
47
+ self.agent_exit_status: Optional[str] = None
44
48
 
45
49
  @property
46
50
  @abstractmethod
@@ -354,6 +358,7 @@ class BaseAgent(ABC):
354
358
  Returns:
355
359
  True if agent completed successfully
356
360
  """
361
+ self.agent_exit_status = None
357
362
  self.logger.info(f"Running {self.name} agent...")
358
363
 
359
364
  with open(log_file, "a", encoding="utf-8") as f:
@@ -385,16 +390,23 @@ class BaseAgent(ABC):
385
390
  log_file=log_file,
386
391
  timeout=timeout
387
392
  )
388
-
393
+
389
394
  success_run = exit_code == 0
395
+ timed_out = not success_run and self._infer_log_has_timeout_marker(log_file)
390
396
  success_post_run = self.post_run_hook(container, log_file)
391
397
 
398
+ # A host-side timeout may kill the agent before it can persist a
399
+ # native terminal status. Preserve the actual reason for stopping
400
+ # instead of reporting an unknown/generic failure.
401
+ if timed_out:
402
+ self.agent_exit_status = "TimeExceeded"
403
+
392
404
  if success_run and success_post_run:
393
405
  self.logger.info(f"{self.name} agent completed successfully")
394
406
  return True
395
407
 
396
408
  if not success_run:
397
- if self._force_timeout_enabled() and self._infer_log_has_timeout_marker(log_file):
409
+ if self._force_timeout_enabled() and timed_out:
398
410
  post_run_state = "passed" if success_post_run else "failed"
399
411
  self.logger.warning(
400
412
  f"{self.name} run hit timeout marker (post-run hook {post_run_state}); "