featurebench 0.2.0__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.0 → featurebench-0.2.2}/PKG-INFO +8 -4
  2. {featurebench-0.2.0 → featurebench-0.2.2}/README.md +6 -2
  3. {featurebench-0.2.0 → featurebench-0.2.2}/config_example.toml +12 -2
  4. {featurebench-0.2.0 → featurebench-0.2.2}/docs/config.md +15 -2
  5. {featurebench-0.2.0 → featurebench-0.2.2}/docs/harness_cli_arg.md +5 -0
  6. {featurebench-0.2.0 → featurebench-0.2.2}/docs/infer_cli_arg.md +19 -4
  7. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/cli.py +3 -1
  8. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/docker/image_manager.py +176 -14
  9. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/container.py +4 -2
  10. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/review_codes.py +5 -1
  11. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/run_evaluation.py +460 -29
  12. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/mini_swe_agent.py +16 -0
  13. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/openhands.py +358 -36
  14. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/config.py +90 -11
  15. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/container.py +11 -6
  16. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/models.py +18 -1
  17. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/run_infer.py +394 -21
  18. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/pipeline.py +4 -1
  19. {featurebench-0.2.0 → featurebench-0.2.2}/pyproject.toml +1 -1
  20. featurebench-0.2.2/tests/test_dataset_version.py +212 -0
  21. featurebench-0.2.2/tests/test_eval_data_container_cleanup.py +339 -0
  22. featurebench-0.2.2/tests/test_infer_container_cleanup.py +116 -0
  23. featurebench-0.2.2/tests/test_openhands_session_cache.py +97 -0
  24. {featurebench-0.2.0 → featurebench-0.2.2}/uv.lock +1 -1
  25. {featurebench-0.2.0 → featurebench-0.2.2}/.gitignore +0 -0
  26. {featurebench-0.2.0 → featurebench-0.2.2}/.python-version +0 -0
  27. {featurebench-0.2.0 → featurebench-0.2.2}/LICENSE +0 -0
  28. {featurebench-0.2.0 → featurebench-0.2.2}/docs/pics/logo.png +0 -0
  29. {featurebench-0.2.0 → featurebench-0.2.2}/docs/pics/workflow.png +0 -0
  30. {featurebench-0.2.0 → featurebench-0.2.2}/docs/pipeline.md +0 -0
  31. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/__init__.py +0 -0
  32. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/classification/__init__.py +0 -0
  33. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/classification/code_classifier.py +0 -0
  34. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/classification/file_analyzer.py +0 -0
  35. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/classification/llm_top_classifier.py +0 -0
  36. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/classification/p2p_chooser.py +0 -0
  37. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/conversion/__init__.py +0 -0
  38. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/conversion/case_converter.py +0 -0
  39. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/conversion/eval_code.py +0 -0
  40. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/conversion/llm_prompt_generator.py +0 -0
  41. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/conversion/llm_testfile_adjuster.py +0 -0
  42. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/conversion/render_task.py +0 -0
  43. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/docker/__init__.py +0 -0
  44. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/docker/dynamic_tracer.py +0 -0
  45. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/docker/test_runner.py +0 -0
  46. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/docker/test_scanner.py +0 -0
  47. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/__init__.py +0 -0
  48. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/constants.py +0 -0
  49. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/grading.py +0 -0
  50. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/report.py +0 -0
  51. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/runtime.py +0 -0
  52. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/test_parsers.py +0 -0
  53. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/harness/utils.py +0 -0
  54. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/__init__.py +0 -0
  55. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/__init__.py +0 -0
  56. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/base.py +0 -0
  57. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/claude_code.py +0 -0
  58. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/codex.py +0 -0
  59. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/agents/gemini_cli.py +0 -0
  60. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/gpu_scheduler.py +0 -0
  61. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/output.py +0 -0
  62. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/render_infer_log.py +0 -0
  63. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/infer/runtime.py +0 -0
  64. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/llm/__init__.py +0 -0
  65. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/llm/llm_caller.py +0 -0
  66. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/llm/llm_exceptions.py +0 -0
  67. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/mask/__init__.py +0 -0
  68. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/mask/mask_generator.py +0 -0
  69. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/mask/mask_validator.py +0 -0
  70. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/mask/signature_extractor.py +0 -0
  71. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/post_verify/__init__.py +0 -0
  72. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/post_verify/f2p_validator.py +0 -0
  73. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/post_verify/level2_validator.py +0 -0
  74. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/post_verify/p2p_validator.py +0 -0
  75. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/constants/fast_images.txt +0 -0
  76. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/constants/full_images.txt +0 -0
  77. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/constants/lite_images.txt +0 -0
  78. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/constants/python_example.py +0 -0
  79. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310.py +0 -0
  80. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310_cu118_torch212.py +0 -0
  81. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310_cu121_torch251.py +0 -0
  82. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python310_cu121_torch28.py +0 -0
  83. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python311.py +0 -0
  84. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python311_cu121_torch28.py +0 -0
  85. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312.py +0 -0
  86. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_cu121_torch251.py +0 -0
  87. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_cu121_torch26.py +0 -0
  88. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_cu121_torch28.py +0 -0
  89. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python312_torch251.py +0 -0
  90. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/dockerfiles/python39.py +0 -0
  91. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/autograd_backward_patch.py +0 -0
  92. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/dynamic_script.py +0 -0
  93. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/import_hook_patch.py +0 -0
  94. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/joblib_patch.py +0 -0
  95. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/scripts/dynamic_script/sitecustomize.py +0 -0
  96. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/scripts/scanner_script.py +0 -0
  97. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/templates/appendix.md.j2 +0 -0
  98. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/templates/black_links.txt.j2 +0 -0
  99. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/templates/config_template.yaml +0 -0
  100. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/templates/level_1.j2 +0 -0
  101. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/templates/level_2.j2 +0 -0
  102. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/resources/templates/llm_prompt_templates.yaml +0 -0
  103. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/scripts/cal_eval_outputs.py +0 -0
  104. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/scripts/post_filter_cases.py +0 -0
  105. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/scripts/pull_images.py +0 -0
  106. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/__init__.py +0 -0
  107. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/command_utils.py +0 -0
  108. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/config.py +0 -0
  109. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/docker_images.py +0 -0
  110. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/logger.py +0 -0
  111. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/parser/__init__.py +0 -0
  112. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/parser/pytest_parser.py +0 -0
  113. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/repo_manager.py +0 -0
  114. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/storage.py +0 -0
  115. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/utils/utils.py +0 -0
  116. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/__init__.py +0 -0
  117. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/post_additions.py +0 -0
  118. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/prompts/add_prompt_lv1.txt +0 -0
  119. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/prompts/add_prompt_lv2.txt +0 -0
  120. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/prompts/prompt_template_lv1.txt +0 -0
  121. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/prompts/prompt_template_lv2.txt +0 -0
  122. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/run_verification.py +0 -0
  123. {featurebench-0.2.0 → featurebench-0.2.2}/featurebench/verified/runner.py +0 -0
  124. {featurebench-0.2.0 → 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.0
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**.
@@ -75,7 +77,8 @@ It provides a unified CLI to run inference, evaluation, and dataset generation.
75
77
  | 16 | GLM 4.7 | Mini-SWE-Agent | 41.2 | 6.7 |
76
78
  | 17 | Qwen3-Coder-480B-A35B-Instruct | OpenHands | 38.3 | 6.7 |
77
79
  | 18 | DeepSeek V3.2 | OpenHands | 35.9 | 6.7 |
78
- | 19 | Qwen3-Coder-30B-A3B-Instruct | OpenHands | 23 | 3.3 |
80
+ | 19 | Qwen3.5-27B | OpenHands | 34.8 | 10.0 |
81
+ | 20 | Qwen3-Coder-30B-A3B-Instruct | OpenHands | 23 | 3.3 |
79
82
 
80
83
  </details>
81
84
  <!-- LEADERBOARD_END -->
@@ -120,6 +123,7 @@ fb pull --mode /path/to/images.txt # one image name per line
120
123
  ```bash
121
124
  fb infer \
122
125
  --config-path config.toml \
126
+ --data-version v1.1 \
123
127
  --agent mini_swe_agent \
124
128
  --model openai/qwen3-coder-480b-a35b-instruct \
125
129
  --split fast
@@ -155,4 +159,4 @@ If you found FeatureBench useful, please cite us as:
155
159
 
156
160
  ## 📧 Contact
157
161
 
158
- 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**.
@@ -51,7 +53,8 @@ It provides a unified CLI to run inference, evaluation, and dataset generation.
51
53
  | 16 | GLM 4.7 | Mini-SWE-Agent | 41.2 | 6.7 |
52
54
  | 17 | Qwen3-Coder-480B-A35B-Instruct | OpenHands | 38.3 | 6.7 |
53
55
  | 18 | DeepSeek V3.2 | OpenHands | 35.9 | 6.7 |
54
- | 19 | Qwen3-Coder-30B-A3B-Instruct | OpenHands | 23 | 3.3 |
56
+ | 19 | Qwen3.5-27B | OpenHands | 34.8 | 10.0 |
57
+ | 20 | Qwen3-Coder-30B-A3B-Instruct | OpenHands | 23 | 3.3 |
55
58
 
56
59
  </details>
57
60
  <!-- LEADERBOARD_END -->
@@ -96,6 +99,7 @@ fb pull --mode /path/to/images.txt # one image name per line
96
99
  ```bash
97
100
  fb infer \
98
101
  --config-path config.toml \
102
+ --data-version v1.1 \
99
103
  --agent mini_swe_agent \
100
104
  --model openai/qwen3-coder-480b-a35b-instruct \
101
105
  --split fast
@@ -131,4 +135,4 @@ If you found FeatureBench useful, please cite us as:
131
135
 
132
136
  ## 📧 Contact
133
137
 
134
- 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
  # ==============================================================================
@@ -53,13 +61,15 @@ LLM_API_KEY = ""
53
61
  # Optional: LLM API Base URL
54
62
  LLM_BASE_URL = ""
55
63
  # Optional: Lock OpenHands version (leave empty to use latest)
56
- OPENHANDS_VERSION = "0.62.0"
64
+ OPENHANDS_VERSION = ""
57
65
  # Optional: Reasoning effort for OpenAI o-series models
58
66
  LLM_REASONING_EFFORT = ""
59
67
  # Optional: Force OpenHands native tool calling ("true" or "false"; empty uses OpenHands default)
60
68
  LLM_NATIVE_TOOL_CALLING = ""
61
69
  # Optional: Send prior assistant reasoning_content back to OpenHands requests
62
70
  LLM_SEND_REASONING_CONTENT = ""
71
+ # Optional: JSON object passed to LiteLLM extra_body, e.g. {"enable_thinking": true}
72
+ LLM_LITELLM_EXTRA_BODY = ""
63
73
  # Azure API Version (required for Azure models only)
64
74
  LLM_API_VERSION = ""
65
75
  # Optional: OpenHands agent max iterations (step limit). Upstream default is 500.
@@ -107,4 +117,4 @@ MSWEA_BASE_URL = ""
107
117
  # Optional: Cost tracking mode (default behavior in FB adapter is ignore_errors)
108
118
  MSWEA_COST_TRACKING = ""
109
119
  # Optional: Lock mini_swe_agent version (leave empty to use latest)
110
- 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
 
@@ -77,6 +89,7 @@ INFER_LOG_RENDER_MODE = "compact" # Optional: compact|full for infer.log renderi
77
89
  LLM_REASONING_EFFORT = "" # Optional: Reasoning effort for OpenAI o-series models
78
90
  LLM_NATIVE_TOOL_CALLING = "" # Optional: true/false to force OpenHands native tool calling; empty uses OpenHands default
79
91
  LLM_SEND_REASONING_CONTENT = "" # Optional: true to send prior assistant reasoning_content in history
92
+ LLM_LITELLM_EXTRA_BODY = "" # Optional: JSON object passed to LiteLLM extra_body, e.g. {"enable_thinking": true}
80
93
  OPENHANDS_MAX_ITERATIONS = "" # Optional: OpenHands agent max iterations (step limit). Upstream default is 500.
81
94
 
82
95
  ```
@@ -207,4 +220,4 @@ Example (local vLLM):
207
220
  [llm.local]
208
221
  backend = "vllm"
209
222
  base_url = "http://localhost:8080/v1"
210
- ```
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.
@@ -135,9 +142,17 @@ flags can override metadata (see the argument list below).
135
142
  Force native tool calling off (`LLM_NATIVE_TOOL_CALLING=false`).
136
143
  Resume mode: ignored (uses metadata).
137
144
 
138
- - `--send-reasoning-content`
139
- Send prior assistant `reasoning_content` back to the model in subsequent OpenHands requests.
140
- Useful for thinking models whose chat template supports reasoning history.
145
+ - `--send-reasoning-content`
146
+ Send prior assistant reasoning back to the model in subsequent OpenHands requests using both `reasoning_content` and `reasoning` fields.
147
+ Useful for thinking models whose chat template supports reasoning history.
148
+ Resume mode: ignored (uses metadata).
149
+
150
+ - `--litellm-extra-body`
151
+ Pass a JSON object to OpenHands `LLM.litellm_extra_body`, for example `--litellm-extra-body '{"enable_thinking": true}'`.
152
+ Resume mode: ignored (uses metadata).
153
+
154
+ - `--session-cache`
155
+ Enable backend KV-cache session reuse for OpenHands by sending a unique `X-Session-Id` header for each task attempt and releasing it after the run with `DELETE /session_release?session_id=...`.
141
156
  Resume mode: ignored (uses metadata).
142
157
 
143
158
  - `--max-iters`
@@ -158,4 +173,4 @@ runs/{timestamp}/
158
173
  ├── infer.log # Agent execution log
159
174
  ├── run.log # Runtime log
160
175
  └── patch.diff # Generated patch
161
- ```
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"
@@ -56,6 +56,8 @@ def _run_with_patched_argv(
56
56
  if result is None:
57
57
  return 0
58
58
  return int(result)
59
+ except KeyboardInterrupt:
60
+ return 130
59
61
  except SystemExit as exc:
60
62
  code = exc.code
61
63
  if code is None:
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import atexit
2
3
  import subprocess
3
4
  import tempfile
4
5
  import os
@@ -6,6 +7,8 @@ import hashlib
6
7
  import platform
7
8
  import importlib.util
8
9
  import shutil
10
+ import signal
11
+ import threading
9
12
  from concurrent.futures import ThreadPoolExecutor, as_completed
10
13
  from typing import Dict, Optional, List, Tuple
11
14
  from pathlib import Path
@@ -24,6 +27,11 @@ from featurebench.utils.logger import (
24
27
  from featurebench.utils.utils import select_candidate_pool_and_allocate_gpu, release_gpu
25
28
 
26
29
 
30
+ FEATUREBENCH_RUN_LABEL = "featurebench.run"
31
+ FEATUREBENCH_KIND_LABEL = "featurebench.kind"
32
+ FEATUREBENCH_TASK_LABEL = "featurebench.task"
33
+
34
+
27
35
  class ImageManager:
28
36
  """Docker image manager."""
29
37
 
@@ -52,6 +60,159 @@ class ImageManager:
52
60
 
53
61
  # Track GPUs per container: {container_id: [gpu_id, ...]}
54
62
  self._container_gpu_map: Dict[str, List[int]] = {}
63
+
64
+ # Track containers created by fb data so Ctrl+C/process-exit cleanup can
65
+ # remove them even if worker threads do not reach their own finally block.
66
+ self._cleanup_run_id = f"data-{int(time.time() * 1000)}-{os.getpid()}"
67
+ self._active_container_ids_lock = threading.RLock()
68
+ self._active_container_ids: set[str] = set()
69
+ self._cleanup_lock = threading.RLock()
70
+ self._cleanup_in_progress = False
71
+ self._cleanup_interrupt_notice_printed = False
72
+ self._previous_sigint = None
73
+ self._signal_cleanup_installed = False
74
+ if threading.current_thread() is threading.main_thread():
75
+ self._previous_sigint = signal.getsignal(signal.SIGINT)
76
+ signal.signal(signal.SIGINT, self._handle_interrupt)
77
+ self._signal_cleanup_installed = True
78
+ self._atexit_cleanup = self._cleanup_active_containers_at_exit
79
+ atexit.register(self._atexit_cleanup)
80
+
81
+ def _container_labels(self, specs_name: str, purpose: str = "data") -> Dict[str, str]:
82
+ return {
83
+ FEATUREBENCH_RUN_LABEL: self._cleanup_run_id,
84
+ FEATUREBENCH_KIND_LABEL: "data",
85
+ FEATUREBENCH_TASK_LABEL: str(specs_name),
86
+ "featurebench.purpose": purpose,
87
+ }
88
+
89
+ def _add_container_label_args(self, docker_command: List[str], specs_name: str) -> None:
90
+ for key, value in self._container_labels(specs_name).items():
91
+ docker_command.extend(["--label", f"{key}={value}"])
92
+
93
+ def _register_container_id(self, container_id: str) -> None:
94
+ if not container_id:
95
+ return
96
+ with self._active_container_ids_lock:
97
+ self._active_container_ids.add(container_id)
98
+
99
+ def _unregister_container_id(self, container_id: str) -> None:
100
+ if not container_id:
101
+ return
102
+ with self._active_container_ids_lock:
103
+ self._active_container_ids.discard(container_id)
104
+
105
+ def _ignore_interrupt_during_cleanup(self, signum, frame) -> None:
106
+ if not self._cleanup_interrupt_notice_printed:
107
+ self._cleanup_interrupt_notice_printed = True
108
+ try:
109
+ self.logger.warning("Cleanup already in progress; ignoring additional Ctrl+C.")
110
+ except Exception:
111
+ pass
112
+
113
+ def _handle_interrupt(self, signum, frame) -> None:
114
+ try:
115
+ self.logger.warning("Interrupted; cleaning FeatureBench data containers...")
116
+ except Exception:
117
+ pass
118
+ self.cleanup_active_containers("keyboard interrupt")
119
+
120
+ previous = self._previous_sigint
121
+ if callable(previous):
122
+ previous(signum, frame)
123
+ elif previous == signal.SIG_DFL:
124
+ raise KeyboardInterrupt
125
+
126
+ def _release_container_gpu(self, container_id: str) -> None:
127
+ gpu_ids = self._container_gpu_map.pop(container_id, None)
128
+ if gpu_ids:
129
+ release_gpu(gpu_ids, self.logger)
130
+ self.logger.debug(f"Container {container_id[:12]} released GPUs {gpu_ids}")
131
+
132
+ def _remove_container_id_best_effort(self, container_id: str) -> bool:
133
+ if not container_id:
134
+ return False
135
+ try:
136
+ self._release_container_gpu(container_id)
137
+ subprocess.run(["docker", "kill", container_id], capture_output=True)
138
+ subprocess.run(["docker", "rm", "-f", container_id], capture_output=True)
139
+ return True
140
+ finally:
141
+ self._unregister_container_id(container_id)
142
+
143
+ def _cleanup_labeled_containers(self) -> int:
144
+ try:
145
+ result = subprocess.run(
146
+ [
147
+ "docker",
148
+ "ps",
149
+ "-aq",
150
+ "--filter",
151
+ f"label={FEATUREBENCH_RUN_LABEL}={self._cleanup_run_id}",
152
+ "--filter",
153
+ f"label={FEATUREBENCH_KIND_LABEL}=data",
154
+ ],
155
+ capture_output=True,
156
+ text=True,
157
+ check=False,
158
+ )
159
+ except Exception as exc:
160
+ self.logger.warning(f"Failed to scan FeatureBench data containers by label: {exc}")
161
+ return 0
162
+
163
+ if result.returncode != 0:
164
+ stderr = (result.stderr or "").strip()
165
+ if stderr:
166
+ self.logger.warning(f"Failed to scan FeatureBench data containers by label: {stderr}")
167
+ return 0
168
+
169
+ removed = 0
170
+ for container_id in result.stdout.splitlines():
171
+ if self._remove_container_id_best_effort(container_id.strip()):
172
+ removed += 1
173
+ return removed
174
+
175
+ def cleanup_active_containers(self, reason: str) -> int:
176
+ with self._cleanup_lock:
177
+ if self._cleanup_in_progress:
178
+ return 0
179
+ self._cleanup_in_progress = True
180
+
181
+ previous_sigint = None
182
+ signal_replaced = False
183
+ try:
184
+ if threading.current_thread() is threading.main_thread():
185
+ previous_sigint = signal.getsignal(signal.SIGINT)
186
+ signal.signal(signal.SIGINT, self._ignore_interrupt_during_cleanup)
187
+ signal_replaced = True
188
+
189
+ with self._active_container_ids_lock:
190
+ container_ids = list(self._active_container_ids)
191
+
192
+ removed = 0
193
+ if container_ids:
194
+ self.logger.warning(
195
+ f"Cleaning {len(container_ids)} active FeatureBench data container(s) after {reason}..."
196
+ )
197
+ for container_id in container_ids:
198
+ if self._remove_container_id_best_effort(container_id):
199
+ removed += 1
200
+
201
+ removed += self._cleanup_labeled_containers()
202
+ if removed:
203
+ self.logger.warning(f"Removed {removed} FeatureBench data container(s).")
204
+ return removed
205
+ finally:
206
+ if signal_replaced:
207
+ try:
208
+ signal.signal(signal.SIGINT, previous_sigint)
209
+ except Exception:
210
+ pass
211
+ with self._cleanup_lock:
212
+ self._cleanup_in_progress = False
213
+
214
+ def _cleanup_active_containers_at_exit(self) -> None:
215
+ self.cleanup_active_containers("process exit")
55
216
 
56
217
  def prepare_images(self, repo_manager: RepoManager) -> None:
57
218
  """
@@ -664,6 +825,7 @@ RUN echo "source /opt/miniconda3/etc/profile.d/conda.sh && conda activate testbe
664
825
 
665
826
  # Add runtime config to docker_command and compute env_setup + GPU IDs
666
827
  env_setup_cmd, selected_gpu_ids = self._add_docker_runtime_config(docker_command, specs)
828
+ self._add_container_label_args(docker_command, specs_name)
667
829
 
668
830
  # Add image
669
831
  docker_command.append(instance_image)
@@ -695,6 +857,7 @@ RUN echo "source /opt/miniconda3/etc/profile.d/conda.sh && conda activate testbe
695
857
  )
696
858
 
697
859
  container_id = result.stdout.strip()
860
+ self._register_container_id(container_id)
698
861
 
699
862
  # Record GPUs used by container (if any)
700
863
  if selected_gpu_ids:
@@ -1097,17 +1260,16 @@ RUN echo "source /opt/miniconda3/etc/profile.d/conda.sh && conda activate testbe
1097
1260
  container_id: Container ID
1098
1261
  force: Force kill without graceful stop (use on timeout)
1099
1262
  """
1100
- # Release GPUs used by container before stop
1101
- gpu_ids = self._container_gpu_map.get(container_id)
1102
- if gpu_ids:
1103
- release_gpu(gpu_ids, self.logger)
1104
- del self._container_gpu_map[container_id]
1105
- self.logger.debug(f"Container {container_id[:12]} released GPUs {gpu_ids}")
1106
-
1107
- if force:
1108
- # Force kill container (no wait)
1109
- subprocess.run(['docker', 'kill', container_id], capture_output=True)
1110
- else:
1111
- # Graceful stop
1112
- subprocess.run(['docker', 'stop', container_id], capture_output=True)
1113
- subprocess.run(['docker', 'rm', container_id], capture_output=True)
1263
+ try:
1264
+ # Release GPUs used by container before stop
1265
+ self._release_container_gpu(container_id)
1266
+
1267
+ if force:
1268
+ # Force kill container (no wait)
1269
+ subprocess.run(['docker', 'kill', container_id], capture_output=True)
1270
+ else:
1271
+ # Graceful stop
1272
+ subprocess.run(['docker', 'stop', container_id], capture_output=True)
1273
+ subprocess.run(['docker', 'rm', container_id], capture_output=True)
1274
+ finally:
1275
+ self._unregister_container_id(container_id)
@@ -190,6 +190,7 @@ class EvalContainerManager:
190
190
  gpu_ids: str | None = None,
191
191
  proxy_port: int | None = None,
192
192
  docker_runtime_config: dict | None = None,
193
+ labels: dict[str, str] | None = None,
193
194
  ) -> Container:
194
195
  """
195
196
  Create and start a Docker container for evaluation.
@@ -201,6 +202,7 @@ class EvalContainerManager:
201
202
  gpu_ids: Comma-separated GPU IDs to use
202
203
  proxy_port: Proxy port for network access (uses bridge network with Docker host gateway)
203
204
  docker_runtime_config: Runtime config from repo_settings (need_gpu, shm_size, env_vars, env_exports, number_once)
205
+ labels: Docker labels to attach to the container
204
206
 
205
207
  Returns:
206
208
  Docker container object
@@ -297,6 +299,7 @@ class EvalContainerManager:
297
299
  environment=environment,
298
300
  network_mode=network_mode,
299
301
  shm_size=shm_size,
302
+ labels=labels,
300
303
  )
301
304
 
302
305
  # Check if GPU is available
@@ -365,5 +368,4 @@ class EvalContainerManager:
365
368
  container.remove()
366
369
  self.logger.info("Container removed successfully")
367
370
  except Exception as e:
368
- self.logger.warning(f"Failed to cleanup container: {e}")
369
-
371
+ self.logger.warning(f"Failed to cleanup container: {e}")
@@ -135,6 +135,7 @@ def save_review_codes_level1(
135
135
  log_dir: Path,
136
136
  docker_image: str,
137
137
  logger: logging.Logger,
138
+ labels: dict[str, str] | None = None,
138
139
  ) -> None:
139
140
  """
140
141
  Save review codes for Level 1 instance.
@@ -176,6 +177,7 @@ def save_review_codes_level1(
176
177
  name=container_name,
177
178
  detach=True,
178
179
  remove=False,
180
+ labels=labels,
179
181
  )
180
182
 
181
183
  # Step 1: Restore project
@@ -290,6 +292,7 @@ def save_review_codes_level2(
290
292
  log_dir: Path,
291
293
  docker_image: str,
292
294
  logger: logging.Logger,
295
+ labels: dict[str, str] | None = None,
293
296
  ) -> None:
294
297
  """
295
298
  Save review codes for Level 2 instance.
@@ -332,6 +335,7 @@ def save_review_codes_level2(
332
335
  name=container_name,
333
336
  detach=True,
334
337
  remove=False,
338
+ labels=labels,
335
339
  )
336
340
 
337
341
  # Step 1: Clean /testbed/ and create README.md
@@ -402,4 +406,4 @@ def save_review_codes_level2(
402
406
  container.stop(timeout=10)
403
407
  container.remove()
404
408
  except Exception as e:
405
- logger.warning(f"Failed to cleanup review container: {e}")
409
+ logger.warning(f"Failed to cleanup review container: {e}")