featurebench 0.2.1__tar.gz → 0.2.2__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 (124) hide show
  1. {featurebench-0.2.1 → featurebench-0.2.2}/PKG-INFO +6 -3
  2. {featurebench-0.2.1 → featurebench-0.2.2}/README.md +4 -1
  3. {featurebench-0.2.1 → featurebench-0.2.2}/config_example.toml +9 -1
  4. {featurebench-0.2.1 → featurebench-0.2.2}/docs/config.md +14 -2
  5. {featurebench-0.2.1 → featurebench-0.2.2}/docs/harness_cli_arg.md +5 -0
  6. {featurebench-0.2.1 → featurebench-0.2.2}/docs/infer_cli_arg.md +8 -1
  7. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/cli.py +2 -2
  8. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/run_evaluation.py +81 -9
  9. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/mini_swe_agent.py +16 -0
  10. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/config.py +90 -11
  11. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/models.py +8 -1
  12. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/run_infer.py +56 -6
  13. {featurebench-0.2.1 → featurebench-0.2.2}/pyproject.toml +1 -1
  14. featurebench-0.2.2/tests/test_dataset_version.py +212 -0
  15. {featurebench-0.2.1 → featurebench-0.2.2}/uv.lock +1 -1
  16. {featurebench-0.2.1 → featurebench-0.2.2}/.gitignore +0 -0
  17. {featurebench-0.2.1 → featurebench-0.2.2}/.python-version +0 -0
  18. {featurebench-0.2.1 → featurebench-0.2.2}/LICENSE +0 -0
  19. {featurebench-0.2.1 → featurebench-0.2.2}/docs/pics/logo.png +0 -0
  20. {featurebench-0.2.1 → featurebench-0.2.2}/docs/pics/workflow.png +0 -0
  21. {featurebench-0.2.1 → featurebench-0.2.2}/docs/pipeline.md +0 -0
  22. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/__init__.py +0 -0
  23. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/classification/__init__.py +0 -0
  24. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/classification/code_classifier.py +0 -0
  25. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/classification/file_analyzer.py +0 -0
  26. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/classification/llm_top_classifier.py +0 -0
  27. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/classification/p2p_chooser.py +0 -0
  28. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/conversion/__init__.py +0 -0
  29. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/conversion/case_converter.py +0 -0
  30. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/conversion/eval_code.py +0 -0
  31. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/conversion/llm_prompt_generator.py +0 -0
  32. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/conversion/llm_testfile_adjuster.py +0 -0
  33. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/conversion/render_task.py +0 -0
  34. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/docker/__init__.py +0 -0
  35. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/docker/dynamic_tracer.py +0 -0
  36. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/docker/image_manager.py +0 -0
  37. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/docker/test_runner.py +0 -0
  38. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/docker/test_scanner.py +0 -0
  39. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/__init__.py +0 -0
  40. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/constants.py +0 -0
  41. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/container.py +0 -0
  42. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/grading.py +0 -0
  43. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/report.py +0 -0
  44. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/review_codes.py +0 -0
  45. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/runtime.py +0 -0
  46. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/test_parsers.py +0 -0
  47. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/harness/utils.py +0 -0
  48. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/__init__.py +0 -0
  49. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/__init__.py +0 -0
  50. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/base.py +0 -0
  51. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/claude_code.py +0 -0
  52. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/codex.py +0 -0
  53. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/gemini_cli.py +0 -0
  54. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/agents/openhands.py +0 -0
  55. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/container.py +0 -0
  56. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/gpu_scheduler.py +0 -0
  57. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/output.py +0 -0
  58. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/render_infer_log.py +0 -0
  59. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/infer/runtime.py +0 -0
  60. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/llm/__init__.py +0 -0
  61. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/llm/llm_caller.py +0 -0
  62. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/llm/llm_exceptions.py +0 -0
  63. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/mask/__init__.py +0 -0
  64. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/mask/mask_generator.py +0 -0
  65. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/mask/mask_validator.py +0 -0
  66. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/mask/signature_extractor.py +0 -0
  67. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/pipeline.py +0 -0
  68. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/post_verify/__init__.py +0 -0
  69. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/post_verify/f2p_validator.py +0 -0
  70. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/post_verify/level2_validator.py +0 -0
  71. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/post_verify/p2p_validator.py +0 -0
  72. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/constants/fast_images.txt +0 -0
  73. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/constants/full_images.txt +0 -0
  74. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/constants/lite_images.txt +0 -0
  75. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/constants/python_example.py +0 -0
  76. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310.py +0 -0
  77. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310_cu118_torch212.py +0 -0
  78. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310_cu121_torch251.py +0 -0
  79. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310_cu121_torch28.py +0 -0
  80. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python311.py +0 -0
  81. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python311_cu121_torch28.py +0 -0
  82. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312.py +0 -0
  83. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_cu121_torch251.py +0 -0
  84. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_cu121_torch26.py +0 -0
  85. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_cu121_torch28.py +0 -0
  86. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_torch251.py +0 -0
  87. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python39.py +0 -0
  88. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/autograd_backward_patch.py +0 -0
  89. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/dynamic_script.py +0 -0
  90. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/import_hook_patch.py +0 -0
  91. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/joblib_patch.py +0 -0
  92. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/sitecustomize.py +0 -0
  93. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/scripts/scanner_script.py +0 -0
  94. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/templates/appendix.md.j2 +0 -0
  95. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/templates/black_links.txt.j2 +0 -0
  96. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/templates/config_template.yaml +0 -0
  97. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/templates/level_1.j2 +0 -0
  98. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/templates/level_2.j2 +0 -0
  99. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/resources/templates/llm_prompt_templates.yaml +0 -0
  100. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/scripts/cal_eval_outputs.py +0 -0
  101. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/scripts/post_filter_cases.py +0 -0
  102. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/scripts/pull_images.py +0 -0
  103. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/__init__.py +0 -0
  104. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/command_utils.py +0 -0
  105. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/config.py +0 -0
  106. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/docker_images.py +0 -0
  107. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/logger.py +0 -0
  108. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/parser/__init__.py +0 -0
  109. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/parser/pytest_parser.py +0 -0
  110. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/repo_manager.py +0 -0
  111. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/storage.py +0 -0
  112. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/utils/utils.py +0 -0
  113. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/__init__.py +0 -0
  114. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/post_additions.py +0 -0
  115. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/prompts/add_prompt_lv1.txt +0 -0
  116. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/prompts/add_prompt_lv2.txt +0 -0
  117. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/prompts/prompt_template_lv1.txt +0 -0
  118. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/prompts/prompt_template_lv2.txt +0 -0
  119. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/run_verification.py +0 -0
  120. {featurebench-0.2.1 → featurebench-0.2.2}/featurebench/verified/runner.py +0 -0
  121. {featurebench-0.2.1 → featurebench-0.2.2}/tests/test_eval_data_container_cleanup.py +0 -0
  122. {featurebench-0.2.1 → featurebench-0.2.2}/tests/test_infer_container_cleanup.py +0 -0
  123. {featurebench-0.2.1 → featurebench-0.2.2}/tests/test_openhands_session_cache.py +0 -0
  124. {featurebench-0.2.1 → featurebench-0.2.2}/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.2
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**.
@@ -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 [zhouqixing@stu.cqu.edu.cn](mailto:zhouqixing@stu.cqu.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**.
@@ -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 [zhouqixing@stu.cqu.edu.cn](mailto:zhouqixing@stu.cqu.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
  # ==============================================================================
@@ -109,4 +117,4 @@ MSWEA_BASE_URL = ""
109
117
  # Optional: Cost tracking mode (default behavior in FB adapter is ignore_errors)
110
118
  MSWEA_COST_TRACKING = ""
111
119
  # Optional: Lock mini_swe_agent version (leave empty to use latest)
112
- MINI_SWE_AGENT_VERSION = ""
120
+ 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
 
@@ -208,4 +220,4 @@ Example (local vLLM):
208
220
  [llm.local]
209
221
  backend = "vllm"
210
222
  base_url = "http://localhost:8080/v1"
211
- ```
223
+ ```
@@ -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.
@@ -166,4 +173,4 @@ runs/{timestamp}/
166
173
  ├── infer.log # Agent execution log
167
174
  ├── run.log # Runtime log
168
175
  └── patch.diff # Generated patch
169
- ```
176
+ ```
@@ -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())
@@ -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())
@@ -109,6 +109,14 @@ echo "mini-swe-agent installation complete"
109
109
  )
110
110
  except Exception:
111
111
  pass
112
+ try:
113
+ self.cm.copy_from_container(
114
+ container,
115
+ "/root/.config/mini-swe-agent/last_mini_run.traj.json",
116
+ log_dir / "traj.json",
117
+ )
118
+ except Exception:
119
+ pass
112
120
  return True
113
121
 
114
122
  def failure_hook(self, container, log_file: Path) -> None:
@@ -122,6 +130,14 @@ echo "mini-swe-agent installation complete"
122
130
  )
123
131
  except Exception:
124
132
  pass
133
+ try:
134
+ self.cm.copy_from_container(
135
+ container,
136
+ "/root/.config/mini-swe-agent/last_mini_run.traj.json",
137
+ log_dir / "traj.json",
138
+ )
139
+ except Exception:
140
+ pass
125
141
 
126
142
  def get_run_command(self, instruction: str) -> str:
127
143
  """Get the command to run mini-swe-agent."""
@@ -5,8 +5,75 @@ Configuration loader for the inference module.
5
5
  import logging
6
6
  import os
7
7
  from pathlib import Path
8
- from typing import Any, Dict, List, Optional
8
+ from typing import Any, Dict, List, Optional, Tuple, Union
9
+
9
10
  import toml
11
+ from huggingface_hub import HfApi
12
+
13
+
14
+ DEFAULT_DATASET_REVISION = "v1.1"
15
+
16
+
17
+ def configure_hf_access(env_vars: Dict[str, Any]) -> Tuple[Union[str, bool, None], Optional[str]]:
18
+ """Apply configured Hub access settings and return normalized values."""
19
+ configured_endpoint = str(env_vars.get("HF_ENDPOINT") or "").strip()
20
+ endpoint = configured_endpoint or str(os.environ.get("HF_ENDPOINT") or "").strip()
21
+ if endpoint:
22
+ os.environ["HF_ENDPOINT"] = endpoint
23
+ # ``datasets.config.HF_ENDPOINT`` is initialized at import time, so update
24
+ # it as well when callers load their config after importing datasets.
25
+ import datasets
26
+
27
+ datasets.config.HF_ENDPOINT = endpoint
28
+
29
+ if "HF_TOKEN" in env_vars:
30
+ configured_token = str(env_vars.get("HF_TOKEN") or "").strip()
31
+ token: Union[str, bool, None] = configured_token or False
32
+ else:
33
+ token = str(os.environ.get("HF_TOKEN") or "").strip() or None
34
+
35
+ return token, endpoint or None
36
+
37
+
38
+ def resolve_hf_dataset_revision(
39
+ dataset: str,
40
+ revision: str,
41
+ token: Union[str, bool, None],
42
+ endpoint: Optional[str],
43
+ ) -> str:
44
+ """Resolve a Hub dataset revision to its immutable commit SHA."""
45
+ try:
46
+ info = HfApi(endpoint=endpoint, token=token).dataset_info(
47
+ repo_id=dataset,
48
+ revision=revision,
49
+ )
50
+ except Exception as exc:
51
+ raise RuntimeError(
52
+ f"Unable to resolve dataset revision '{revision}' for '{dataset}'. "
53
+ "Refusing to use an unverified cached dataset."
54
+ ) from exc
55
+
56
+ resolved_revision = str(info.sha or "").strip()
57
+ if not resolved_revision:
58
+ raise RuntimeError(
59
+ f"Hugging Face returned no commit SHA for '{dataset}' at '{revision}'"
60
+ )
61
+ return resolved_revision
62
+
63
+
64
+ def verify_loaded_dataset_revision(dataset: Any, resolved_revision: str) -> None:
65
+ """Reject Hugging Face's fallback to a cache from another revision."""
66
+ cache_files = getattr(dataset, "cache_files", None) or []
67
+ cache_paths = [
68
+ Path(item["filename"])
69
+ for item in cache_files
70
+ if isinstance(item, dict) and item.get("filename")
71
+ ]
72
+ if cache_paths and any(resolved_revision not in path.parts for path in cache_paths):
73
+ raise RuntimeError(
74
+ f"Loaded dataset cache does not match resolved commit {resolved_revision}. "
75
+ "Refusing to use a different dataset version."
76
+ )
10
77
 
11
78
 
12
79
  class InferConfigLoader:
@@ -75,6 +142,12 @@ class InferConfigLoader:
75
142
  return Path(path).expanduser()
76
143
  return None
77
144
 
145
+ def get_dataset_revision(self) -> str:
146
+ """Get the pinned Hugging Face dataset revision."""
147
+ dataset_cfg = self._config.get("dataset", {}) or {}
148
+ revision = str(dataset_cfg.get("revision") or "").strip()
149
+ return revision or DEFAULT_DATASET_REVISION
150
+
78
151
  def get_agent_env_vars(self, agent_name: str) -> Dict[str, str]:
79
152
  """
80
153
  Get environment variables for a specific agent.
@@ -153,20 +226,15 @@ class DatasetLoader:
153
226
 
154
227
  def _setup_hf_env(self) -> None:
155
228
  """Set up HuggingFace environment variables."""
156
- env_vars = self.config.env_vars
157
-
158
- if env_vars.get("HF_ENDPOINT"):
159
- os.environ["HF_ENDPOINT"] = env_vars["HF_ENDPOINT"]
160
-
161
- if env_vars.get("HF_TOKEN"):
162
- os.environ["HF_TOKEN"] = env_vars["HF_TOKEN"]
229
+ configure_hf_access(self.config.env_vars)
163
230
 
164
231
  def load_dataset(
165
232
  self,
166
233
  dataset: str,
167
234
  split: Optional[str] = None,
168
235
  levels: Optional[List[int]] = None,
169
- task_ids: Optional[List[str]] = None
236
+ task_ids: Optional[List[str]] = None,
237
+ revision: Optional[str] = None,
170
238
  ) -> List[Dict[str, Any]]:
171
239
  """
172
240
  Load dataset from HuggingFace.
@@ -177,6 +245,8 @@ class DatasetLoader:
177
245
  levels: List of levels to filter (1, 2). If None, loads all levels.
178
246
  Level is determined by the last segment of instance_id (e.g., "lv1", "lv2").
179
247
  task_ids: Specific task IDs to filter. If None, loads all tasks.
248
+ revision: Hugging Face dataset revision (tag, branch, or commit SHA).
249
+ If omitted, uses ``[dataset].revision`` or ``v1.1``.
180
250
 
181
251
  Returns:
182
252
  List of task instances as dictionaries
@@ -193,7 +263,8 @@ class DatasetLoader:
193
263
  from datasets import load_dataset
194
264
 
195
265
  env_vars = self.config.env_vars
196
- hf_token = env_vars.get("HF_TOKEN")
266
+ hf_token, hf_endpoint = configure_hf_access(env_vars)
267
+ dataset_revision = str(revision or "").strip() or self.config.get_dataset_revision()
197
268
 
198
269
  dataset_name = str(dataset or "").strip()
199
270
  if not dataset_name:
@@ -205,11 +276,19 @@ class DatasetLoader:
205
276
  all_instances = []
206
277
 
207
278
  try:
279
+ resolved_revision = resolve_hf_dataset_revision(
280
+ dataset_name,
281
+ dataset_revision,
282
+ hf_token,
283
+ hf_endpoint,
284
+ )
208
285
  dataset = load_dataset(
209
286
  dataset_name,
210
287
  split=split,
211
- token=hf_token
288
+ token=hf_token,
289
+ revision=resolved_revision,
212
290
  )
291
+ verify_loaded_dataset_revision(dataset, resolved_revision)
213
292
 
214
293
  for item in dataset:
215
294
  item_dict = dict(item)
@@ -152,6 +152,8 @@ class InferConfig:
152
152
  model: str
153
153
  # HuggingFace dataset repo name (e.g., "LiberCoders/FeatureBench")
154
154
  dataset: str = "LiberCoders/FeatureBench"
155
+ # Hugging Face dataset tag, branch, or commit SHA. None defers to config.toml/default.
156
+ dataset_revision: Optional[str] = None
155
157
  n_concurrent: int = 1
156
158
  n_attempts: int = 1
157
159
  task_ids: Optional[List[str]] = None
@@ -191,6 +193,7 @@ class InferConfig:
191
193
  "agent": self.agent,
192
194
  "model": self.model,
193
195
  "dataset": self.dataset,
196
+ "dataset_revision": self.dataset_revision,
194
197
  "n_concurrent": self.n_concurrent,
195
198
  "n_attempts": self.n_attempts,
196
199
  "task_ids": self.task_ids,
@@ -257,6 +260,7 @@ class RunMetadata:
257
260
  task_ids: List[str]
258
261
  output_path: str
259
262
  start_time: str
263
+ dataset_revision: str = "v1.1"
260
264
  timeout: int = 7200
261
265
  proxy_port: Optional[int] = None
262
266
  runtime_proxy: Optional[bool] = None
@@ -284,6 +288,7 @@ class RunMetadata:
284
288
  "agent": self.agent,
285
289
  "model": self.model,
286
290
  "dataset": self.dataset,
291
+ "dataset_revision": self.dataset_revision,
287
292
  "n_concurrent": self.n_concurrent,
288
293
  "n_attempts": self.n_attempts,
289
294
  "task_ids": self.task_ids,
@@ -321,6 +326,8 @@ class RunMetadata:
321
326
  """Load metadata from a JSON file."""
322
327
  with open(path, "r", encoding="utf-8") as f:
323
328
  data = json.load(f)
329
+ # Runs created before dataset versioning used the original Hub release.
330
+ data.setdefault("dataset_revision", "v1.0")
324
331
  return cls(**data)
325
332
 
326
333
 
@@ -358,4 +365,4 @@ class TaskPaths:
358
365
 
359
366
  def ensure_dirs(self) -> None:
360
367
  """Create all necessary directories."""
361
- self.attempt_dir.mkdir(parents=True, exist_ok=True)
368
+ self.attempt_dir.mkdir(parents=True, exist_ok=True)