agentcompass 1.0.0__py3-none-any.whl

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 (440) hide show
  1. agentcompass/__init__.py +53 -0
  2. agentcompass/analyzers/__init__.py +24 -0
  3. agentcompass/analyzers/basic/__init__.py +19 -0
  4. agentcompass/analyzers/basic/basic_metric_analyzer.py +88 -0
  5. agentcompass/analyzers/basic/command_running_analyzer.py +256 -0
  6. agentcompass/analyzers/basic/completion_length_analyzer.py +92 -0
  7. agentcompass/analyzers/basic/empty_content_analyzer.py +80 -0
  8. agentcompass/analyzers/basic/exception_analyzer.py +167 -0
  9. agentcompass/analyzers/basic/json_error_analyzer.py +177 -0
  10. agentcompass/analyzers/basic/keyword_analyzer.py +156 -0
  11. agentcompass/analyzers/basic/latency_analyzer.py +265 -0
  12. agentcompass/analyzers/basic/mixed_lang_analyzer.py +175 -0
  13. agentcompass/analyzers/basic/network_op_analyzer.py +192 -0
  14. agentcompass/analyzers/basic/repetition_analyzer.py +340 -0
  15. agentcompass/analyzers/basic/tool_repetition_analyzer.py +409 -0
  16. agentcompass/analyzers/basic/truncation_analyzer.py +62 -0
  17. agentcompass/analyzers/hack_detection/__init__.py +14 -0
  18. agentcompass/analyzers/hack_detection/analyzer.py +419 -0
  19. agentcompass/analyzers/hack_detection/llm_verification.py +395 -0
  20. agentcompass/analyzers/hack_detection/rules.py +322 -0
  21. agentcompass/analyzers/qualitative/__init__.py +4 -0
  22. agentcompass/analyzers/qualitative/assets/codebook.md +342 -0
  23. agentcompass/analyzers/qualitative/assets/prompt.md +195 -0
  24. agentcompass/analyzers/qualitative/multi_qualitative_analyzer.py +640 -0
  25. agentcompass/analyzers/qualitative/qualitative_analyzer.py +1178 -0
  26. agentcompass/analyzers/qualitative/trajectory_parser.py +198 -0
  27. agentcompass/analyzers/trajectory_graph/__init__.py +40 -0
  28. agentcompass/analyzers/trajectory_graph/graph.py +484 -0
  29. agentcompass/analyzers/trajectory_graph/io.py +302 -0
  30. agentcompass/analyzers/trajectory_graph/render_html.py +1108 -0
  31. agentcompass/benchmarks/__init__.py +35 -0
  32. agentcompass/benchmarks/brainarena/__init__.py +5 -0
  33. agentcompass/benchmarks/brainarena/brainarena.py +326 -0
  34. agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_2B.md +1 -0
  35. agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_3A.md +1 -0
  36. agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_3C.md +1 -0
  37. agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_4B.md +1 -0
  38. agentcompass/benchmarks/brainarena/data/queries/legare_2025/Fig_2B.md +1 -0
  39. agentcompass/benchmarks/brainarena/data/queries/legare_2025/Fig_3A.md +1 -0
  40. agentcompass/benchmarks/brainarena/data/queries/tanaka_2026/Fig_3E.md +1 -0
  41. agentcompass/benchmarks/brainarena/data/queries/tanaka_2026/Fig_4A.md +1 -0
  42. agentcompass/benchmarks/brainarena/data/queries/tanaka_2026/Fig_4G.md +1 -0
  43. agentcompass/benchmarks/brainarena/data/queries/yu_2025/Fig_5C.md +1 -0
  44. agentcompass/benchmarks/brainarena/data/queries/yu_2025/Fig_5M.md +1 -0
  45. agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_2B.png +0 -0
  46. agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_3A.png +0 -0
  47. agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_3C.png +0 -0
  48. agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_4B.png +0 -0
  49. agentcompass/benchmarks/brainarena/data/references/legare_2025/Fig_2B.png +0 -0
  50. agentcompass/benchmarks/brainarena/data/references/legare_2025/Fig_3A.png +0 -0
  51. agentcompass/benchmarks/brainarena/data/references/legare_2025/README.md +7 -0
  52. agentcompass/benchmarks/brainarena/data/references/tanaka_2026/Fig_3E.png +0 -0
  53. agentcompass/benchmarks/brainarena/data/references/tanaka_2026/Fig_4A.png +0 -0
  54. agentcompass/benchmarks/brainarena/data/references/tanaka_2026/Fig_4G.png +0 -0
  55. agentcompass/benchmarks/brainarena/data/references/yu_2025/Fig_5C.png +0 -0
  56. agentcompass/benchmarks/brainarena/data/references/yu_2025/Fig_5M.png +0 -0
  57. agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_2B.json +44 -0
  58. agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_3A.json +53 -0
  59. agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_3C.json +41 -0
  60. agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_4B.json +40 -0
  61. agentcompass/benchmarks/brainarena/data/rubrics/legare_2025/Fig_2B.json +41 -0
  62. agentcompass/benchmarks/brainarena/data/rubrics/legare_2025/Fig_3A.json +43 -0
  63. agentcompass/benchmarks/brainarena/data/rubrics/tanaka_2026/Fig_3E.json +51 -0
  64. agentcompass/benchmarks/brainarena/data/rubrics/tanaka_2026/Fig_4A.json +44 -0
  65. agentcompass/benchmarks/brainarena/data/rubrics/tanaka_2026/Fig_4G.json +45 -0
  66. agentcompass/benchmarks/brainarena/data/rubrics/yu_2025/Fig_5C.json +39 -0
  67. agentcompass/benchmarks/brainarena/data/rubrics/yu_2025/Fig_5M.json +43 -0
  68. agentcompass/benchmarks/brainarena/data/tasks.json +13 -0
  69. agentcompass/benchmarks/brainarena/datasets.py +197 -0
  70. agentcompass/benchmarks/brainarena/scorer.py +202 -0
  71. agentcompass/benchmarks/browsecomp.py +127 -0
  72. agentcompass/benchmarks/browsecomp_zh.py +127 -0
  73. agentcompass/benchmarks/config.py +118 -0
  74. agentcompass/benchmarks/deepresearch_bench/__init__.py +5 -0
  75. agentcompass/benchmarks/deepresearch_bench/dataset.py +142 -0
  76. agentcompass/benchmarks/deepresearch_bench/deepresearch_bench.py +528 -0
  77. agentcompass/benchmarks/deepresearch_bench/fact.py +472 -0
  78. agentcompass/benchmarks/deepresearch_bench/parsing.py +178 -0
  79. agentcompass/benchmarks/deepresearch_bench/prompts.py +353 -0
  80. agentcompass/benchmarks/deepresearch_bench/race.py +429 -0
  81. agentcompass/benchmarks/deepresearch_bench/weighting.py +180 -0
  82. agentcompass/benchmarks/deepsearchqa.py +142 -0
  83. agentcompass/benchmarks/deepswe.py +532 -0
  84. agentcompass/benchmarks/frontier_engineering/__init__.py +5 -0
  85. agentcompass/benchmarks/frontier_engineering/data/medal_podium.csv +48 -0
  86. agentcompass/benchmarks/frontier_engineering/data/rank_baseline.csv +441 -0
  87. agentcompass/benchmarks/frontier_engineering/data/v1.yaml +264 -0
  88. agentcompass/benchmarks/frontier_engineering/data/v1_lite.yaml +83 -0
  89. agentcompass/benchmarks/frontier_engineering/evaluator.py +663 -0
  90. agentcompass/benchmarks/frontier_engineering/frontier_engineering.py +1995 -0
  91. agentcompass/benchmarks/frontier_engineering/openevolve_evaluator.py +38 -0
  92. agentcompass/benchmarks/frontier_swe.py +485 -0
  93. agentcompass/benchmarks/frontierscience.py +147 -0
  94. agentcompass/benchmarks/gaia.py +141 -0
  95. agentcompass/benchmarks/gdpval_ac/__init__.py +5 -0
  96. agentcompass/benchmarks/gdpval_ac/dataset.py +285 -0
  97. agentcompass/benchmarks/gdpval_ac/gdpval_ac.py +1211 -0
  98. agentcompass/benchmarks/gdpval_ac/pairwise_core.py +3111 -0
  99. agentcompass/benchmarks/gdpval_ac/stage_baseline.py +165 -0
  100. agentcompass/benchmarks/hle.py +320 -0
  101. agentcompass/benchmarks/osworld/__init__.py +10 -0
  102. agentcompass/benchmarks/osworld/agent_loop.py +118 -0
  103. agentcompass/benchmarks/osworld/agents/__init__.py +12 -0
  104. agentcompass/benchmarks/osworld/agents/base.py +30 -0
  105. agentcompass/benchmarks/osworld/agents/claude.py +639 -0
  106. agentcompass/benchmarks/osworld/agents/qwen35.py +420 -0
  107. agentcompass/benchmarks/osworld/osworld.py +435 -0
  108. agentcompass/benchmarks/osworld/runtime/__init__.py +23 -0
  109. agentcompass/benchmarks/osworld/runtime/controllers/__init__.py +0 -0
  110. agentcompass/benchmarks/osworld/runtime/controllers/python.py +496 -0
  111. agentcompass/benchmarks/osworld/runtime/controllers/setup.py +1068 -0
  112. agentcompass/benchmarks/osworld/runtime/docker_adapter.py +124 -0
  113. agentcompass/benchmarks/osworld/runtime/evaluator.py +161 -0
  114. agentcompass/benchmarks/osworld/runtime/evaluators/__init__.py +5 -0
  115. agentcompass/benchmarks/osworld/runtime/evaluators/getters/__init__.py +21 -0
  116. agentcompass/benchmarks/osworld/runtime/evaluators/getters/calc.py +15 -0
  117. agentcompass/benchmarks/osworld/runtime/evaluators/getters/chrome.py +2714 -0
  118. agentcompass/benchmarks/osworld/runtime/evaluators/getters/file.py +156 -0
  119. agentcompass/benchmarks/osworld/runtime/evaluators/getters/general.py +44 -0
  120. agentcompass/benchmarks/osworld/runtime/evaluators/getters/gimp.py +38 -0
  121. agentcompass/benchmarks/osworld/runtime/evaluators/getters/impress.py +129 -0
  122. agentcompass/benchmarks/osworld/runtime/evaluators/getters/info.py +50 -0
  123. agentcompass/benchmarks/osworld/runtime/evaluators/getters/misc.py +495 -0
  124. agentcompass/benchmarks/osworld/runtime/evaluators/getters/replay.py +20 -0
  125. agentcompass/benchmarks/osworld/runtime/evaluators/getters/vlc.py +98 -0
  126. agentcompass/benchmarks/osworld/runtime/evaluators/getters/vscode.py +43 -0
  127. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/__init__.py +45 -0
  128. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/basic_os.py +68 -0
  129. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/chrome.py +582 -0
  130. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/docs.py +1195 -0
  131. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/general.py +676 -0
  132. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/gimp.py +951 -0
  133. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/libreoffice.py +24 -0
  134. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/others.py +102 -0
  135. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/pdf.py +30 -0
  136. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/slides.py +1147 -0
  137. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/table.py +693 -0
  138. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/thunderbird.py +179 -0
  139. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/utils.py +820 -0
  140. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/vlc.py +556 -0
  141. agentcompass/benchmarks/osworld/runtime/evaluators/metrics/vscode.py +469 -0
  142. agentcompass/benchmarks/osworld/runtime/task_environment.py +154 -0
  143. agentcompass/benchmarks/osworld/runtime/types.py +49 -0
  144. agentcompass/benchmarks/pinchbench/__init__.py +3 -0
  145. agentcompass/benchmarks/pinchbench/grading_runner.py +118 -0
  146. agentcompass/benchmarks/pinchbench/pinchbench.py +908 -0
  147. agentcompass/benchmarks/pinchbench/pinchbench_grading_runtime.py +656 -0
  148. agentcompass/benchmarks/researchclawbench.py +545 -0
  149. agentcompass/benchmarks/scicode/__init__.py +8 -0
  150. agentcompass/benchmarks/scicode/data/13.6.txt +49 -0
  151. agentcompass/benchmarks/scicode/data/62.1.txt +29 -0
  152. agentcompass/benchmarks/scicode/data/76.3.txt +27 -0
  153. agentcompass/benchmarks/scicode/data/background_comment_template.txt +28 -0
  154. agentcompass/benchmarks/scicode/data/multistep_template.txt +23 -0
  155. agentcompass/benchmarks/scicode/data/problems_dev.jsonl +15 -0
  156. agentcompass/benchmarks/scicode/data/problems_test.jsonl +65 -0
  157. agentcompass/benchmarks/scicode/scicode.py +962 -0
  158. agentcompass/benchmarks/screenspot.py +213 -0
  159. agentcompass/benchmarks/sealqa.py +286 -0
  160. agentcompass/benchmarks/sgi_deep_research.py +124 -0
  161. agentcompass/benchmarks/skillsbench/__init__.py +10 -0
  162. agentcompass/benchmarks/skillsbench/benchmark.py +378 -0
  163. agentcompass/benchmarks/skillsbench/parser/__init__.py +12 -0
  164. agentcompass/benchmarks/skillsbench/parser/parsers.py +60 -0
  165. agentcompass/benchmarks/skillsbench/parser/v10.py +51 -0
  166. agentcompass/benchmarks/skillsbench/parser/v11.py +57 -0
  167. agentcompass/benchmarks/special_pattern.py +256 -0
  168. agentcompass/benchmarks/swe_marathon/__init__.py +9 -0
  169. agentcompass/benchmarks/swe_marathon/swe_marathon.py +632 -0
  170. agentcompass/benchmarks/swe_marathon/verifier.py +189 -0
  171. agentcompass/benchmarks/swe_marathon/workdir.py +48 -0
  172. agentcompass/benchmarks/swebench_multilingual.py +539 -0
  173. agentcompass/benchmarks/swebench_pro.py +641 -0
  174. agentcompass/benchmarks/swebench_pro_verified/__init__.py +13 -0
  175. agentcompass/benchmarks/swebench_pro_verified/network_policy.py +41 -0
  176. agentcompass/benchmarks/swebench_pro_verified/swebench_pro_verified.py +756 -0
  177. agentcompass/benchmarks/swebench_verified.py +579 -0
  178. agentcompass/benchmarks/taubench/__init__.py +5 -0
  179. agentcompass/benchmarks/taubench/_taubench_patch.py +536 -0
  180. agentcompass/benchmarks/taubench/agent_runner.py +261 -0
  181. agentcompass/benchmarks/taubench/remote_runner.py +443 -0
  182. agentcompass/benchmarks/taubench/taubench.py +786 -0
  183. agentcompass/benchmarks/taubench/utils/__init__.py +19 -0
  184. agentcompass/benchmarks/taubench/utils/embedding_cache.py +174 -0
  185. agentcompass/benchmarks/taubench/utils/model_backend.py +518 -0
  186. agentcompass/benchmarks/taubench/utils/utils.py +198 -0
  187. agentcompass/benchmarks/terminalbench2/__init__.py +0 -0
  188. agentcompass/benchmarks/terminalbench2/terminalbench2.py +344 -0
  189. agentcompass/benchmarks/terminalbench2/terminalbench2_1.py +369 -0
  190. agentcompass/benchmarks/terminalbench2/terminalbench2_verified.py +404 -0
  191. agentcompass/benchmarks/terminalbench2/verifier.py +185 -0
  192. agentcompass/benchmarks/terminalbench2/verifier_health.py +68 -0
  193. agentcompass/benchmarks/terminalbench2/workdir.py +53 -0
  194. agentcompass/benchmarks/utils/__init__.py +6 -0
  195. agentcompass/benchmarks/utils/data.py +118 -0
  196. agentcompass/benchmarks/utils/harbor.py +355 -0
  197. agentcompass/benchmarks/utils/scorers/__init__.py +25 -0
  198. agentcompass/benchmarks/utils/scorers/base.py +101 -0
  199. agentcompass/benchmarks/utils/scorers/deepsearchqa.py +239 -0
  200. agentcompass/benchmarks/utils/scorers/frontier_swe.py +257 -0
  201. agentcompass/benchmarks/utils/scorers/frontierscience.py +523 -0
  202. agentcompass/benchmarks/utils/scorers/llm.py +258 -0
  203. agentcompass/benchmarks/utils/scorers/researchclawbench.py +367 -0
  204. agentcompass/benchmarks/utils/scorers/sealqa.py +156 -0
  205. agentcompass/benchmarks/utils/scorers/xbench_deepsearch.py +94 -0
  206. agentcompass/benchmarks/widesearch/__init__.py +28 -0
  207. agentcompass/benchmarks/widesearch/benchmark.py +240 -0
  208. agentcompass/benchmarks/widesearch/dataset.py +90 -0
  209. agentcompass/benchmarks/widesearch/evaluator.py +724 -0
  210. agentcompass/benchmarks/widesearch/metrics.py +20 -0
  211. agentcompass/benchmarks/wildclawbench.py +998 -0
  212. agentcompass/benchmarks/wildclawbench_grading_runner.py +60 -0
  213. agentcompass/benchmarks/xbench_deepsearch.py +168 -0
  214. agentcompass/cli/__init__.py +7 -0
  215. agentcompass/cli/__main__.py +7 -0
  216. agentcompass/cli/main.py +1230 -0
  217. agentcompass/environments/__init__.py +10 -0
  218. agentcompass/environments/brainpp.py +531 -0
  219. agentcompass/environments/daytona.py +576 -0
  220. agentcompass/environments/docker.py +845 -0
  221. agentcompass/environments/docker_egress_proxy.py +398 -0
  222. agentcompass/environments/env_gateway.py +761 -0
  223. agentcompass/environments/host_process.py +202 -0
  224. agentcompass/environments/hsandbox.py +536 -0
  225. agentcompass/environments/modal.py +606 -0
  226. agentcompass/environments/opensandbox.py +384 -0
  227. agentcompass/environments/utils/__init__.py +1 -0
  228. agentcompass/environments/utils/exec.py +60 -0
  229. agentcompass/environments/utils/files.py +54 -0
  230. agentcompass/environments/utils/pvisor.py +208 -0
  231. agentcompass/environments/utils/shared_storage.py +181 -0
  232. agentcompass/errors.py +55 -0
  233. agentcompass/harnesses/__init__.py +14 -0
  234. agentcompass/harnesses/claude_code.py +576 -0
  235. agentcompass/harnesses/codex.py +639 -0
  236. agentcompass/harnesses/gui_agents/__init__.py +1 -0
  237. agentcompass/harnesses/gui_agents/qwen3vl.py +286 -0
  238. agentcompass/harnesses/mini_swe_agent/__init__.py +6 -0
  239. agentcompass/harnesses/mini_swe_agent/harness.py +503 -0
  240. agentcompass/harnesses/mini_swe_agent/interleaved_model.py +31 -0
  241. agentcompass/harnesses/mini_swe_agent/local_runner.py +684 -0
  242. agentcompass/harnesses/mini_swe_agent/plan.py +60 -0
  243. agentcompass/harnesses/mini_swe_agent/remote_runner.py +143 -0
  244. agentcompass/harnesses/mini_swe_agent/trajectory.py +160 -0
  245. agentcompass/harnesses/naive_search_agent/__init__.py +6 -0
  246. agentcompass/harnesses/naive_search_agent/engine/__init__.py +1 -0
  247. agentcompass/harnesses/naive_search_agent/engine/coordinator.py +148 -0
  248. agentcompass/harnesses/naive_search_agent/engine/fc_inferencer.py +711 -0
  249. agentcompass/harnesses/naive_search_agent/engine/runner.py +338 -0
  250. agentcompass/harnesses/naive_search_agent/engine/sub_agents.py +289 -0
  251. agentcompass/harnesses/naive_search_agent/engine/tools/__init__.py +1 -0
  252. agentcompass/harnesses/naive_search_agent/engine/tools/browse.py +577 -0
  253. agentcompass/harnesses/naive_search_agent/engine/tools/registry.py +198 -0
  254. agentcompass/harnesses/naive_search_agent/engine/tools/search.py +647 -0
  255. agentcompass/harnesses/naive_search_agent/engine/tools/web_visitor.py +491 -0
  256. agentcompass/harnesses/naive_search_agent/harness.py +204 -0
  257. agentcompass/harnesses/naive_search_agent/local_runner.py +115 -0
  258. agentcompass/harnesses/naive_search_agent/plan.py +80 -0
  259. agentcompass/harnesses/naive_search_agent/trajectory.py +211 -0
  260. agentcompass/harnesses/openai_chat.py +267 -0
  261. agentcompass/harnesses/openclaw.py +1413 -0
  262. agentcompass/harnesses/openevolve/__init__.py +13 -0
  263. agentcompass/harnesses/openevolve/harness.py +793 -0
  264. agentcompass/harnesses/openevolve/runner.py +557 -0
  265. agentcompass/harnesses/openhands/__init__.py +6 -0
  266. agentcompass/harnesses/openhands/harness.py +258 -0
  267. agentcompass/harnesses/openhands/plan.py +27 -0
  268. agentcompass/harnesses/openhands/prompt.py +34 -0
  269. agentcompass/harnesses/openhands/remote_entry.py +818 -0
  270. agentcompass/harnesses/openhands/remote_runner.py +432 -0
  271. agentcompass/harnesses/openhands/trajectory.py +453 -0
  272. agentcompass/harnesses/qwen3vl_gui.py +119 -0
  273. agentcompass/harnesses/researchharness.py +856 -0
  274. agentcompass/harnesses/scicode_tool_use.py +1038 -0
  275. agentcompass/harnesses/terminus2/__init__.py +0 -0
  276. agentcompass/harnesses/terminus2/llms/__init__.py +0 -0
  277. agentcompass/harnesses/terminus2/llms/chat.py +179 -0
  278. agentcompass/harnesses/terminus2/llms/llm.py +796 -0
  279. agentcompass/harnesses/terminus2/templates/terminus-json-plain.txt +54 -0
  280. agentcompass/harnesses/terminus2/templates/terminus-toolcall.txt +30 -0
  281. agentcompass/harnesses/terminus2/templates/terminus-xml-plain.txt +60 -0
  282. agentcompass/harnesses/terminus2/templates/timeout.txt +10 -0
  283. agentcompass/harnesses/terminus2/terminus_2.py +2496 -0
  284. agentcompass/harnesses/terminus2/tmux_session.py +801 -0
  285. agentcompass/harnesses/utils/__init__.py +23 -0
  286. agentcompass/harnesses/utils/glibc.py +139 -0
  287. agentcompass/harnesses/utils/model.py +24 -0
  288. agentcompass/harnesses/utils/result.py +24 -0
  289. agentcompass/harnesses/utils/sync.py +231 -0
  290. agentcompass/launcher.py +1394 -0
  291. agentcompass/recipes/__init__.py +37 -0
  292. agentcompass/recipes/brainarena/__init__.py +5 -0
  293. agentcompass/recipes/brainarena/docker.py +36 -0
  294. agentcompass/recipes/deepswe/__init__.py +5 -0
  295. agentcompass/recipes/deepswe/common.py +25 -0
  296. agentcompass/recipes/deepswe/daytona.py +24 -0
  297. agentcompass/recipes/deepswe/docker.py +24 -0
  298. agentcompass/recipes/deepswe/modal.py +24 -0
  299. agentcompass/recipes/frontier_engineering/__init__.py +3 -0
  300. agentcompass/recipes/frontier_engineering/docker.py +90 -0
  301. agentcompass/recipes/frontier_swe/__init__.py +5 -0
  302. agentcompass/recipes/frontier_swe/common.py +25 -0
  303. agentcompass/recipes/frontier_swe/daytona.py +21 -0
  304. agentcompass/recipes/frontier_swe/docker.py +23 -0
  305. agentcompass/recipes/frontier_swe/modal.py +26 -0
  306. agentcompass/recipes/gdpval_ac/__init__.py +5 -0
  307. agentcompass/recipes/gdpval_ac/common.py +19 -0
  308. agentcompass/recipes/gdpval_ac/daytona.py +25 -0
  309. agentcompass/recipes/gdpval_ac/docker.py +31 -0
  310. agentcompass/recipes/gdpval_ac/modal.py +25 -0
  311. agentcompass/recipes/osworld/__init__.py +3 -0
  312. agentcompass/recipes/osworld/docker.py +139 -0
  313. agentcompass/recipes/pinchbench/__init__.py +5 -0
  314. agentcompass/recipes/pinchbench/common.py +47 -0
  315. agentcompass/recipes/pinchbench/daytona.py +33 -0
  316. agentcompass/recipes/pinchbench/docker.py +34 -0
  317. agentcompass/recipes/pinchbench/modal.py +33 -0
  318. agentcompass/recipes/researchclawbench/__init__.py +11 -0
  319. agentcompass/recipes/researchclawbench/daytona.py +28 -0
  320. agentcompass/recipes/researchclawbench/docker.py +29 -0
  321. agentcompass/recipes/researchclawbench/modal.py +28 -0
  322. agentcompass/recipes/skillsbench/__init__.py +3 -0
  323. agentcompass/recipes/skillsbench/docker.py +54 -0
  324. agentcompass/recipes/swe_marathon/docker.py +30 -0
  325. agentcompass/recipes/swebench_multilingual/__init__.py +5 -0
  326. agentcompass/recipes/swebench_multilingual/daytona.py +47 -0
  327. agentcompass/recipes/swebench_multilingual/docker.py +45 -0
  328. agentcompass/recipes/swebench_multilingual/modal.py +47 -0
  329. agentcompass/recipes/swebench_pro/__init__.py +5 -0
  330. agentcompass/recipes/swebench_pro/daytona.py +42 -0
  331. agentcompass/recipes/swebench_pro/docker.py +50 -0
  332. agentcompass/recipes/swebench_pro/modal.py +42 -0
  333. agentcompass/recipes/swebench_pro_verified/__init__.py +3 -0
  334. agentcompass/recipes/swebench_pro_verified/docker.py +50 -0
  335. agentcompass/recipes/swebench_verified/__init__.py +6 -0
  336. agentcompass/recipes/swebench_verified/common.py +55 -0
  337. agentcompass/recipes/swebench_verified/daytona.py +58 -0
  338. agentcompass/recipes/swebench_verified/docker.py +44 -0
  339. agentcompass/recipes/swebench_verified/host_process.py +28 -0
  340. agentcompass/recipes/swebench_verified/modal.py +56 -0
  341. agentcompass/recipes/taubench/__init__.py +5 -0
  342. agentcompass/recipes/taubench/docker.py +34 -0
  343. agentcompass/recipes/terminalbench2/daytona.py +31 -0
  344. agentcompass/recipes/terminalbench2/docker.py +28 -0
  345. agentcompass/recipes/terminalbench2/docker_ac.py +62 -0
  346. agentcompass/recipes/terminalbench2/modal.py +29 -0
  347. agentcompass/recipes/terminalbench2_1/daytona.py +31 -0
  348. agentcompass/recipes/terminalbench2_1/docker.py +28 -0
  349. agentcompass/recipes/terminalbench2_1/docker_ac.py +62 -0
  350. agentcompass/recipes/terminalbench2_1/modal.py +29 -0
  351. agentcompass/recipes/terminalbench2_verified/daytona.py +34 -0
  352. agentcompass/recipes/terminalbench2_verified/docker.py +33 -0
  353. agentcompass/recipes/terminalbench2_verified/docker_ac.py +67 -0
  354. agentcompass/recipes/terminalbench2_verified/modal.py +32 -0
  355. agentcompass/recipes/wildclawbench/__init__.py +3 -0
  356. agentcompass/recipes/wildclawbench/common.py +55 -0
  357. agentcompass/recipes/wildclawbench/docker.py +34 -0
  358. agentcompass/runtime/__init__.py +147 -0
  359. agentcompass/runtime/analysis.py +161 -0
  360. agentcompass/runtime/artifacts.py +812 -0
  361. agentcompass/runtime/attempts/__init__.py +29 -0
  362. agentcompass/runtime/attempts/repository.py +374 -0
  363. agentcompass/runtime/attempts/scheduler.py +192 -0
  364. agentcompass/runtime/base.py +1034 -0
  365. agentcompass/runtime/capsule.py +507 -0
  366. agentcompass/runtime/checkpoint.py +256 -0
  367. agentcompass/runtime/config/__init__.py +73 -0
  368. agentcompass/runtime/config/component.py +203 -0
  369. agentcompass/runtime/config/environment.py +72 -0
  370. agentcompass/runtime/config/loader.py +418 -0
  371. agentcompass/runtime/config/settings.py +139 -0
  372. agentcompass/runtime/context.py +37 -0
  373. agentcompass/runtime/dependencies.py +218 -0
  374. agentcompass/runtime/issues.py +123 -0
  375. agentcompass/runtime/limits.py +309 -0
  376. agentcompass/runtime/llm/__init__.py +66 -0
  377. agentcompass/runtime/llm/anthropic.py +52 -0
  378. agentcompass/runtime/llm/errors.py +22 -0
  379. agentcompass/runtime/llm/generate.py +238 -0
  380. agentcompass/runtime/llm/openai.py +402 -0
  381. agentcompass/runtime/llm/protocol.py +123 -0
  382. agentcompass/runtime/logging.py +488 -0
  383. agentcompass/runtime/metrics/__init__.py +103 -0
  384. agentcompass/runtime/metrics/benchmark_aggregation.py +423 -0
  385. agentcompass/runtime/metrics/contract.py +250 -0
  386. agentcompass/runtime/metrics/mode.py +12 -0
  387. agentcompass/runtime/metrics/reducer.py +377 -0
  388. agentcompass/runtime/metrics/reducers.py +238 -0
  389. agentcompass/runtime/metrics/report.py +455 -0
  390. agentcompass/runtime/metrics/report_aggregation.py +418 -0
  391. agentcompass/runtime/metrics/strategies.py +214 -0
  392. agentcompass/runtime/metrics/types.py +26 -0
  393. agentcompass/runtime/models/__init__.py +104 -0
  394. agentcompass/runtime/models/analysis.py +28 -0
  395. agentcompass/runtime/models/model.py +75 -0
  396. agentcompass/runtime/models/orchestration.py +243 -0
  397. agentcompass/runtime/models/plan.py +97 -0
  398. agentcompass/runtime/models/request.py +658 -0
  399. agentcompass/runtime/models/result.py +117 -0
  400. agentcompass/runtime/models/task.py +320 -0
  401. agentcompass/runtime/models/trajectory.py +84 -0
  402. agentcompass/runtime/network.py +462 -0
  403. agentcompass/runtime/orchestration.py +1150 -0
  404. agentcompass/runtime/planner.py +384 -0
  405. agentcompass/runtime/progress.py +1002 -0
  406. agentcompass/runtime/recipes.py +128 -0
  407. agentcompass/runtime/recovery.py +108 -0
  408. agentcompass/runtime/registry.py +106 -0
  409. agentcompass/runtime/resources.py +203 -0
  410. agentcompass/runtime/results/__init__.py +15 -0
  411. agentcompass/runtime/results/detail.py +474 -0
  412. agentcompass/runtime/results/layout.py +308 -0
  413. agentcompass/runtime/results/legacy.py +44 -0
  414. agentcompass/runtime/results/render.py +185 -0
  415. agentcompass/runtime/results/store.py +1706 -0
  416. agentcompass/runtime/results/summary.py +101 -0
  417. agentcompass/runtime/runner.py +2385 -0
  418. agentcompass/runtime/setup.py +71 -0
  419. agentcompass/runtime/tasks.py +137 -0
  420. agentcompass/runtime/timeouts.py +59 -0
  421. agentcompass/utils/__init__.py +5 -0
  422. agentcompass/utils/api_key.py +40 -0
  423. agentcompass/utils/call.py +90 -0
  424. agentcompass/utils/command.py +108 -0
  425. agentcompass/utils/env.py +89 -0
  426. agentcompass/utils/exceptions.py +25 -0
  427. agentcompass/utils/redaction.py +29 -0
  428. agentcompass/utils/workspace.py +39 -0
  429. agentcompass/viewer/__init__.py +0 -0
  430. agentcompass/viewer/reader.py +838 -0
  431. agentcompass/viewer/server.py +160 -0
  432. agentcompass/viewer/static/assets/index-BywoR7aU.js +170 -0
  433. agentcompass/viewer/static/assets/index-CqGkz9_L.css +1 -0
  434. agentcompass/viewer/static/index.html +17 -0
  435. agentcompass-1.0.0.dist-info/METADATA +275 -0
  436. agentcompass-1.0.0.dist-info/RECORD +440 -0
  437. agentcompass-1.0.0.dist-info/WHEEL +5 -0
  438. agentcompass-1.0.0.dist-info/entry_points.txt +2 -0
  439. agentcompass-1.0.0.dist-info/licenses/LICENSE +203 -0
  440. agentcompass-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,167 @@
1
+ import logging
2
+ import os
3
+ import re
4
+
5
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ _FRAME_RE = re.compile(r'^\s*File "([^"]+)", line \d+, in (\S+)', re.MULTILINE)
10
+
11
+ _EXC_LINE_RE = re.compile(r'^([A-Za-z_][\w.]*): (.*)$', re.MULTILINE)
12
+
13
+ _CHAIN_SEP_RE = re.compile(r'(?:The above exception was the direct cause of the following exception'
14
+ r'|During handling of the above exception, another exception occurred):')
15
+
16
+
17
+ def _normalize_message(msg: str) -> str:
18
+ if not msg:
19
+ return ""
20
+ s = msg
21
+ s = re.sub(r'0x[0-9a-fA-F]+', '<hex>', s)
22
+ s = re.sub(r'\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b', '<uuid>', s)
23
+ s = re.sub(r'\b\d{1,3}(?:\.\d{1,3}){3}\b', '<ip>', s)
24
+ s = re.sub(r'(?:/[\w.\-]+)+', '<path>', s)
25
+ # s = re.sub(r'\b\d+(?:\.\d+)?\b', '<n>', s)
26
+ s = re.sub(r'"[^"]*"', '<s>', s)
27
+ s = re.sub(r"'[^']*'", '<s>', s)
28
+ return s.strip()
29
+
30
+
31
+ def _traceback_fingerprint(error: str) -> dict:
32
+ """Parse a Python traceback into a stable aggregation fingerprint.
33
+ """
34
+ out = {
35
+ "error_type": "",
36
+ "error_location": "",
37
+ "error_message_normalized": "",
38
+ "error_fingerprint": "",
39
+ "error_cause_type": "",
40
+ "error_cause_location": "",
41
+ "error_cause": "",
42
+ }
43
+ if not error or not error.strip():
44
+ return out
45
+
46
+ exc_lines = _EXC_LINE_RE.findall(error)
47
+ frames = [(m.start(), m.group(1), m.group(2)) for m in _FRAME_RE.finditer(error)]
48
+ sep = _CHAIN_SEP_RE.search(error)
49
+
50
+ if not exc_lines:
51
+ out["error_type"] = error
52
+ out["error_fingerprint"] = _normalize_message(error.strip()) or "<error>"
53
+ return out
54
+
55
+ final_type, final_msg = exc_lines[-1]
56
+ chained = bool(sep) and len(exc_lines) >= 2
57
+ cause_type, cause_msg = exc_lines[0] if chained else ("", "")
58
+
59
+ final_loc = ""
60
+ cause_loc = ""
61
+ if frames:
62
+ _, ff_path, ff_func = frames[-1]
63
+ final_loc = f"{os.path.basename(ff_path)}:{ff_func}"
64
+ if chained:
65
+ before = [f for f in frames if f[0] < sep.start()]
66
+ if before:
67
+ _, cf_path, cf_func = before[-1]
68
+ cause_loc = f"{os.path.basename(cf_path)}:{cf_func}"
69
+
70
+ final_msg_n = _normalize_message(final_msg)
71
+ out["error_type"] = f"{final_type}: {final_msg_n}"
72
+ out["error_location"] = final_loc
73
+
74
+ if chained:
75
+ cause_msg_n = _normalize_message(cause_msg)
76
+ out["error_cause_type"] = cause_type
77
+ out["error_cause_location"] = cause_loc
78
+ out["error_cause"] = f"{cause_type}: {cause_msg}"
79
+ out["error_message_normalized"] = (f"{final_msg_n} (caused by: {cause_type}: {cause_msg_n})")
80
+ out["error_fingerprint"] = (f"{final_type} @ {final_loc or '<unknown>'} "
81
+ f"(caused by: {cause_type} @ {cause_loc or '<unknown>'})")
82
+ else:
83
+ out["error_message_normalized"] = final_msg_n
84
+ out["error_fingerprint"] = f"{final_type} @ {final_loc or '<unknown>'}"
85
+ return out
86
+
87
+
88
+ @ANALYZERS.register()
89
+ class ExceptionAnalyzer(BaseAnalyzer):
90
+ id = "ExceptionAnalyzer"
91
+ description = "Detect execution issues and preserved diagnostic evidence, and parse tracebacks into stable, aggregable fingerprints (error type, location, cause)."
92
+ category = AnalyzerCategory.ENV_FRAMEWORK_ERROR
93
+ data_requirements = ["$.issues"]
94
+ datasets = []
95
+ conf = {"only_incorrect": False, "threshold": 0.0}
96
+ distribution_fields = {
97
+ "error_type": "value_counts",
98
+ "error_fingerprint": "value_counts",
99
+ "task_status": "value_counts",
100
+ }
101
+
102
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
103
+ is_badcase = False
104
+ details = {}
105
+
106
+ if result is None:
107
+ return AnalysisResult(task_id=task.task_id, is_badcase=None, error="RunResult is None")
108
+
109
+ has_error_status = any(issue.severity in {"fatal", "error"} for issue in result.issues)
110
+ diagnostics = result.artifacts.get("execution_diagnostics") or "\n".join(issue.message
111
+ for issue in result.issues)
112
+ has_error_field = bool(diagnostics)
113
+
114
+ is_badcase = has_error_status or has_error_field
115
+
116
+ if has_error_status:
117
+ details["task_status"] = f"{result.status.value}"
118
+ if has_error_field:
119
+ details["error_snippet"] = diagnostics[:500]
120
+ details.update(_traceback_fingerprint(diagnostics))
121
+
122
+ return AnalysisResult(
123
+ task_id=task.task_id,
124
+ is_badcase=is_badcase,
125
+ score=1 if is_badcase else 0,
126
+ details=details,
127
+ )
128
+
129
+
130
+ @ANALYZERS.register()
131
+ class TerminalBench2ExceptionAnalyzer(ExceptionAnalyzer):
132
+ """TerminalBench2-specific exception analysis.
133
+
134
+ Extends the generic ExceptionAnalyzer with environment error detection
135
+ from eval_raw_data.test_output.
136
+ """
137
+ id = "TerminalBench2ExceptionAnalyzer"
138
+ description = "TerminalBench2 variant of ExceptionAnalyzer that also detects environment-level errors from eval test output (terminal_bench_2 only)."
139
+ datasets = ["terminal_bench_2", "terminal_bench_2_verified", "terminal_bench_2_1"]
140
+ base_analyzer = "ExceptionAnalyzer"
141
+ priority = 1
142
+
143
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
144
+ base_result = await super().analysis(task, prepared, result, req, plan)
145
+
146
+ extra = result.extra or {}
147
+ eval_raw = extra.get("eval_raw_data") or {}
148
+ test_output = eval_raw.get("testcase_output")
149
+ if test_output and self._detect_env_error(test_output):
150
+ base_result.is_badcase = True
151
+ base_result.details["eval_environment_error"] = True
152
+ base_result.details["eval_environment_error_snippet"] = str(test_output)[:500]
153
+ if base_result.score is None or base_result.score < 1:
154
+ base_result.score = 1
155
+
156
+ return base_result
157
+
158
+ @staticmethod
159
+ def _detect_env_error(test_output: str) -> bool:
160
+ """Detect environment-level errors from test output."""
161
+ signals = [
162
+ "connection refused", "connection timed out", "name or service not known", "no such file or directory",
163
+ "permission denied", "address already in use", "environment error", "command not found",
164
+ "E: Could not get lock /var/lib/dpkg/lock-frontend", "No such file or directory"
165
+ ]
166
+ lower = str(test_output).lower()
167
+ return any(s in lower for s in signals)
@@ -0,0 +1,177 @@
1
+ import json
2
+ import logging
3
+ from typing import List
4
+
5
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ def _extract_json_content(response: str) -> tuple[str, List[str]]:
11
+ """Extract JSON content from response, handling extra text."""
12
+ warnings = []
13
+
14
+ # Try to find JSON object boundaries
15
+ json_start = -1
16
+ json_end = -1
17
+ brace_count = 0
18
+ in_string = False
19
+ escape_next = False
20
+
21
+ for i, char in enumerate(response):
22
+ if escape_next:
23
+ escape_next = False
24
+ continue
25
+
26
+ if char == "\\":
27
+ escape_next = True
28
+ continue
29
+
30
+ if char == '"' and not escape_next:
31
+ in_string = not in_string
32
+ continue
33
+
34
+ if not in_string:
35
+ if char == "{":
36
+ if brace_count == 0:
37
+ json_start = i
38
+ brace_count += 1
39
+ elif char == "}":
40
+ brace_count -= 1
41
+ if brace_count == 0 and json_start != -1:
42
+ json_end = i + 1
43
+ break
44
+
45
+ if json_start == -1 or json_end == -1:
46
+ return "", ["No valid JSON object found"]
47
+
48
+ # Check for extra text
49
+ before_text = response[:json_start].strip()
50
+ after_text = response[json_end:].strip()
51
+
52
+ if before_text:
53
+ warnings.append("Extra text detected before JSON object")
54
+ if after_text:
55
+ warnings.append("Extra text detected after JSON object")
56
+
57
+ return response[json_start:json_end], warnings
58
+
59
+
60
+ def _check_json(text: str) -> str:
61
+ """Classify a text string's JSON status.
62
+
63
+ Returns:
64
+ "clean_json" – text is valid JSON as-is
65
+ "json_with_extra_text" – valid JSON exists but is surrounded by other text
66
+ "no_json" – no valid JSON found
67
+ """
68
+ if not text or not text.strip():
69
+ return "no_json"
70
+
71
+ stripped = text.strip()
72
+
73
+ json_content, extra_text_warnings = _extract_json_content(stripped)
74
+ if not json_content:
75
+ return "no_json"
76
+
77
+ try:
78
+ _ = json.loads(json_content)
79
+ except (json.JSONDecodeError, ValueError):
80
+ return "no_json"
81
+
82
+ if extra_text_warnings:
83
+ return "json_with_extra_text"
84
+
85
+ return "clean_json"
86
+
87
+
88
+ @ANALYZERS.register()
89
+ class JSONErrorAnalyzer(BaseAnalyzer):
90
+ """Detect JSON formatting issues in assistant content (terminal_bench_2 only)."""
91
+
92
+ id = "JSONErrorAnalyzer"
93
+ description = "Detect JSON formatting issues in assistant content — missing JSON or JSON wrapped in extra text (terminal_bench_2 only)."
94
+ category = AnalyzerCategory.ERROR
95
+ datasets = ["terminal_bench_2", "terminal_bench_2_verified", "terminal_bench_2_1"]
96
+ data_requirements = ["$.trajectory.steps[*].assistant_content"]
97
+ conf = {"only_incorrect": False, "threshold": 0.0}
98
+ distribution_fields = {
99
+ "json_error_types": "value_counts",
100
+ "no_json_steps": "numeric_stats",
101
+ "json_with_extra_text_steps": "numeric_stats",
102
+ }
103
+
104
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
105
+ if result is None or result.trajectory is None:
106
+ return AnalysisResult(
107
+ task_id=task.task_id,
108
+ is_badcase=None,
109
+ error="no trajectory available",
110
+ )
111
+
112
+ steps = result.trajectory.steps
113
+ if not steps:
114
+ return AnalysisResult(
115
+ task_id=task.task_id,
116
+ is_badcase=None,
117
+ error="empty trajectory",
118
+ )
119
+
120
+ total_llm_steps = 0
121
+ no_json_count = 0
122
+ extra_text_count = 0
123
+ error_types: list[str] = []
124
+ error_steps_detail: list[dict] = []
125
+
126
+ for step in steps:
127
+ ac = step.assistant_content
128
+ if ac is None:
129
+ continue
130
+ total_llm_steps += 1
131
+
132
+ content = getattr(ac, "content", "") or ""
133
+ if isinstance(content, list):
134
+ content = " ".join(item.get("text", "") if isinstance(item, dict) else str(item) for item in content)
135
+
136
+ status = _check_json(content)
137
+
138
+ if status == "no_json":
139
+ no_json_count += 1
140
+ error_types.append("no_json")
141
+ error_steps_detail.append({
142
+ "step_id": step.step_id,
143
+ "error_type": "no_json",
144
+ "content_snippet": content[:300],
145
+ })
146
+ elif status == "json_with_extra_text":
147
+ extra_text_count += 1
148
+ error_types.append("json_with_extra_text")
149
+ error_steps_detail.append({
150
+ "step_id": step.step_id,
151
+ "error_type": "json_with_extra_text",
152
+ "content_snippet": content[:300],
153
+ })
154
+
155
+ # Task-level score: average across steps (no_json=1, json_with_extra_text=0.5, clean=0)
156
+ if total_llm_steps > 0:
157
+ score = round((no_json_count * 1.0 + extra_text_count * 0.5) / total_llm_steps, 4)
158
+ else:
159
+ score = 0.0
160
+
161
+ is_badcase = score > 0
162
+
163
+ details: dict = {
164
+ "total_llm_steps": total_llm_steps,
165
+ "no_json_steps": no_json_count,
166
+ "json_with_extra_text_steps": extra_text_count,
167
+ "json_error_types": error_types,
168
+ }
169
+ if error_steps_detail:
170
+ details["error_steps_detail"] = error_steps_detail
171
+
172
+ return AnalysisResult(
173
+ task_id=task.task_id,
174
+ is_badcase=is_badcase,
175
+ score=score,
176
+ details=details,
177
+ )
@@ -0,0 +1,156 @@
1
+ import logging
2
+
3
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ def _find_keywords(text: str, keywords: list[str]) -> list[str]:
9
+ """Return the subset of *keywords* found in *text* (case-insensitive)."""
10
+ if not text or not keywords:
11
+ return []
12
+ lower = text.lower()
13
+ return [kw for kw in keywords if kw.lower() in lower]
14
+
15
+
16
+ class KeywordAnalyzerBase(BaseAnalyzer):
17
+ """Base class for keyword detection.
18
+
19
+ Subclasses set ``check_fields`` to specify which ``assistant_content``
20
+ fields to inspect. Keywords come from ``conf["keywords"]``.
21
+
22
+ keywords – list of keyword strings to search for (required)
23
+ only_incorrect – only analyse incorrect samples
24
+ threshold – overall badcase ratio threshold
25
+ """
26
+
27
+ description = "Base class for forbidden/sensitive keyword detection across assistant_content fields; subclasses choose which fields to inspect and which keywords to search for."
28
+ check_fields: list[str] = ["content", "reasoning_content"]
29
+
30
+ def _find_in_field(self, text: str) -> list[str]:
31
+ keywords = self.conf.get("keywords") or []
32
+ return _find_keywords(text, keywords)
33
+
34
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
35
+ if result is None or result.trajectory is None:
36
+ return AnalysisResult(
37
+ task_id=task.task_id,
38
+ is_badcase=None,
39
+ error="no trajectory available",
40
+ )
41
+
42
+ steps = result.trajectory.steps
43
+ if not steps:
44
+ return AnalysisResult(
45
+ task_id=task.task_id,
46
+ is_badcase=None,
47
+ error="empty trajectory",
48
+ )
49
+
50
+ keywords = self.conf.get("keywords") or []
51
+ if not keywords:
52
+ return AnalysisResult(
53
+ task_id=task.task_id,
54
+ is_badcase=None,
55
+ error="no keywords configured",
56
+ )
57
+
58
+ total_llm_steps = 0
59
+ matched_steps = 0
60
+ all_matched_keywords: list[str] = []
61
+ matched_steps_detail: list[dict] = []
62
+
63
+ for step in steps:
64
+ ac = step.assistant_content
65
+ if ac is None:
66
+ continue
67
+ total_llm_steps += 1
68
+
69
+ step_matched = False
70
+ step_fields: dict = {}
71
+
72
+ for field_name in self.check_fields:
73
+ text = getattr(ac, field_name, "") or ""
74
+ if isinstance(text, list):
75
+ text = " ".join(item.get("text", "") if isinstance(item, dict) else str(item) for item in text)
76
+ found = self._find_in_field(text)
77
+ if found:
78
+ step_matched = True
79
+ all_matched_keywords.extend(found)
80
+ step_fields[field_name] = found
81
+
82
+ if step_matched:
83
+ matched_steps += 1
84
+ matched_steps_detail.append({
85
+ "step_id": step.step_id,
86
+ "matched_fields": step_fields,
87
+ })
88
+
89
+ is_badcase = matched_steps > 0
90
+
91
+ details: dict = {
92
+ "total_llm_steps": total_llm_steps,
93
+ "matched_steps": matched_steps,
94
+ "matched_keywords": all_matched_keywords,
95
+ }
96
+ if matched_steps_detail:
97
+ details["matched_steps_detail"] = matched_steps_detail
98
+
99
+ return AnalysisResult(
100
+ task_id=task.task_id,
101
+ is_badcase=is_badcase,
102
+ score=1 if is_badcase else 0,
103
+ details=details,
104
+ )
105
+
106
+
107
+ @ANALYZERS.register()
108
+ class ContentKeywordAnalyzer(KeywordAnalyzerBase):
109
+ """Detect forbidden/sensitive keywords in assistant content."""
110
+
111
+ id = "ContentKeywordAnalyzer"
112
+ description = "Detect forbidden/sensitive raw tokens (e.g. leaked special tokens like <think>, <tool_call>) in assistant content."
113
+ category = AnalyzerCategory.BEHAVIOR
114
+ datasets = []
115
+ data_requirements = ["$.trajectory.steps[*].assistant_content"]
116
+ check_fields = ["content"]
117
+ conf: dict = {
118
+ "threshold":
119
+ 0.0,
120
+ "only_incorrect":
121
+ False,
122
+ "keywords": [
123
+ "<think>", "</think>", "<tool>", "</tool>", "<tool_call>", "</tool_call>", "<|end|>", "<|start|>",
124
+ "<|channe|>", "<|message|>"
125
+ ],
126
+ }
127
+ distribution_fields = {
128
+ "matched_steps": "numeric_stats",
129
+ "matched_keywords": "value_counts",
130
+ }
131
+
132
+
133
+ @ANALYZERS.register()
134
+ class ReasoningKeywordAnalyzer(KeywordAnalyzerBase):
135
+ """Detect forbidden/sensitive keywords in assistant reasoning_content."""
136
+
137
+ id = "ReasoningKeywordAnalyzer"
138
+ description = "Detect forbidden/sensitive raw tokens (e.g. leaked special tokens like <think>, <tool_call>) in assistant reasoning_content."
139
+ category = AnalyzerCategory.BEHAVIOR
140
+ datasets = []
141
+ data_requirements = ["$.trajectory.steps[*].assistant_content"]
142
+ check_fields = ["reasoning_content"]
143
+ conf: dict = {
144
+ "threshold":
145
+ 0.0,
146
+ "only_incorrect":
147
+ False,
148
+ "keywords": [
149
+ "<think>", "</think>", "<tool>", "</tool>", "<tool_call>", "</tool_call>", "<|end|>", "<|start|>",
150
+ "<|channe|>", "<|message|>"
151
+ ],
152
+ }
153
+ distribution_fields = {
154
+ "matched_steps": "numeric_stats",
155
+ "matched_keywords": "value_counts",
156
+ }