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,53 @@
1
+ """AgentCompass public Python API."""
2
+
3
+ # isort: off
4
+ from agentcompass.launcher import ( # noqa: E402
5
+ async_launch, async_run_evaluation, async_run_evaluation_request, async_summary, build_run_request, launch,
6
+ run_evaluation, run_evaluation_request, summary)
7
+ from agentcompass.runtime import (
8
+ BaseRecipe,
9
+ ExecutionIssue,
10
+ IssuePhase,
11
+ IssueSeverity,
12
+ RunResult,
13
+ ExecutionPlan,
14
+ Orchestration,
15
+ OrchestrationDefaults,
16
+ OrchestrationResult,
17
+ OrchestrationSpec,
18
+ RequestOutcome,
19
+ RunRequest,
20
+ RunRequestSpec,
21
+ RunRuntimeSpec,
22
+ RuntimeOptions,
23
+ TaskSpec,
24
+ )
25
+ # isort: on
26
+
27
+ __all__ = [
28
+ "async_launch",
29
+ "async_run_evaluation",
30
+ "async_run_evaluation_request",
31
+ "async_summary",
32
+ "build_run_request",
33
+ "BaseRecipe",
34
+ "ExecutionIssue",
35
+ "IssuePhase",
36
+ "IssueSeverity",
37
+ "RunResult",
38
+ "ExecutionPlan",
39
+ "launch",
40
+ "Orchestration",
41
+ "OrchestrationDefaults",
42
+ "OrchestrationResult",
43
+ "OrchestrationSpec",
44
+ "RequestOutcome",
45
+ "run_evaluation",
46
+ "run_evaluation_request",
47
+ "RunRequest",
48
+ "RunRequestSpec",
49
+ "RunRuntimeSpec",
50
+ "RuntimeOptions",
51
+ "summary",
52
+ "TaskSpec",
53
+ ]
@@ -0,0 +1,24 @@
1
+ from agentcompass.analyzers.basic import (
2
+ BasicMetricAnalyzer,
3
+ ConsecutiveToolRepetitionAnalyzer,
4
+ ContentKeywordAnalyzer,
5
+ ContentMixedLangAnalyzer,
6
+ ContentRepetitionAnalyzer,
7
+ CrossStepToolRepetitionAnalyzer,
8
+ ExceptionAnalyzer,
9
+ JSONErrorAnalyzer,
10
+ LLMInferLatencyAnalyzer,
11
+ NetworkOperationAnalyzer,
12
+ ReasoningKeywordAnalyzer,
13
+ ReasoningMixedLangAnalyzer,
14
+ ReasoningRepetitionAnalyzer,
15
+ StepToolRepetitionAnalyzer,
16
+ TerminalBench2CommandRunningAnalyzer,
17
+ TerminalBench2ConsecutiveToolRepetitionAnalyzer,
18
+ TerminalBench2ExceptionAnalyzer,
19
+ ToolExecutionLatencyAnalyzer,
20
+ TrajectoryTimeCostAnalyzer,
21
+ TruncationAnalyzer,
22
+ )
23
+ from agentcompass.analyzers.hack_detection import HackDetectionAnalyzer # noqa: F401
24
+ from agentcompass.analyzers.qualitative import QualitativeAnalyzer # noqa: F401
@@ -0,0 +1,19 @@
1
+ from agentcompass.analyzers.basic.basic_metric_analyzer import BasicMetricAnalyzer
2
+ from agentcompass.analyzers.basic.command_running_analyzer import TerminalBench2CommandRunningAnalyzer
3
+ from agentcompass.analyzers.basic.completion_length_analyzer import CompletionLengthAnalyzer
4
+ from agentcompass.analyzers.basic.empty_content_analyzer import EmptyContentAnalyzer
5
+ from agentcompass.analyzers.basic.exception_analyzer import ExceptionAnalyzer, TerminalBench2ExceptionAnalyzer
6
+ from agentcompass.analyzers.basic.json_error_analyzer import JSONErrorAnalyzer
7
+ from agentcompass.analyzers.basic.keyword_analyzer import ContentKeywordAnalyzer, ReasoningKeywordAnalyzer
8
+ from agentcompass.analyzers.basic.latency_analyzer import (LLMInferLatencyAnalyzer, ToolExecutionLatencyAnalyzer,
9
+ TrajectoryTimeCostAnalyzer)
10
+ from agentcompass.analyzers.basic.mixed_lang_analyzer import ContentMixedLangAnalyzer, ReasoningMixedLangAnalyzer
11
+ from agentcompass.analyzers.basic.network_op_analyzer import NetworkOperationAnalyzer
12
+ from agentcompass.analyzers.basic.repetition_analyzer import ContentRepetitionAnalyzer, ReasoningRepetitionAnalyzer
13
+ from agentcompass.analyzers.basic.tool_repetition_analyzer import (
14
+ ConsecutiveToolRepetitionAnalyzer,
15
+ CrossStepToolRepetitionAnalyzer,
16
+ StepToolRepetitionAnalyzer,
17
+ TerminalBench2ConsecutiveToolRepetitionAnalyzer,
18
+ )
19
+ from agentcompass.analyzers.basic.truncation_analyzer import TruncationAnalyzer
@@ -0,0 +1,88 @@
1
+ import logging
2
+
3
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ def _extract_tool_name(tc) -> str | None:
9
+ """Extract function name from an OpenAI-format tool_call dict."""
10
+ if not isinstance(tc, dict):
11
+ return None
12
+ return tc.get("function", {}).get("name")
13
+
14
+
15
+ @ANALYZERS.register()
16
+ class BasicMetricAnalyzer(BaseAnalyzer):
17
+ """Collect basic trajectory statistics without badcase detection."""
18
+
19
+ id = "BasicMetricAnalyzer"
20
+ description = "Trajectory basic statistics: step count, tool call count, average tool calls per step, unique tool count, tool call distribution."
21
+ category = AnalyzerCategory.BEHAVIOR
22
+ datasets = []
23
+ data_requirements = ["$.trajectory"]
24
+ conf = {"only_incorrect": False, "threshold": 0.0}
25
+ distribution_fields = {
26
+ "total_steps": "numeric_stats",
27
+ "total_tool_calls": "numeric_stats",
28
+ "avg_tool_calls_per_step": "numeric_stats",
29
+ "unique_tool_count": "numeric_stats",
30
+ "tool_names": "value_counts",
31
+ "max_completion_token_len": "numeric_stats",
32
+ "min_completion_token_len": "numeric_stats",
33
+ "avg_completion_token_len": "numeric_stats",
34
+ }
35
+
36
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
37
+ if result is None or result.trajectory is None:
38
+ return AnalysisResult(
39
+ task_id=task.task_id,
40
+ is_badcase=None,
41
+ error="no trajectory available",
42
+ )
43
+
44
+ steps = result.trajectory.steps
45
+ total_steps = len(steps)
46
+
47
+ tool_names_all: list[str] = []
48
+ for step in steps:
49
+ if step.assistant_content is None:
50
+ continue
51
+ tool_calls = step.assistant_content.tool_calls
52
+ if not tool_calls:
53
+ continue
54
+ for tc in tool_calls:
55
+ fn = _extract_tool_name(tc)
56
+ if fn:
57
+ tool_names_all.append(fn)
58
+
59
+ total_tool_calls = len(tool_names_all)
60
+ avg_tool_calls = round(total_tool_calls / total_steps, 4) if total_steps > 0 else 0.0
61
+ unique_tool_count = len(set(tool_names_all))
62
+
63
+ completion_token_lens = [
64
+ step.metric.completion_tokens_len for step in steps
65
+ if step.metric and step.metric.completion_tokens_len is not None
66
+ ]
67
+ max_completion_token_len = max(completion_token_lens) if completion_token_lens else 0
68
+ min_completion_token_len = min(completion_token_lens) if completion_token_lens else 0
69
+ avg_completion_token_len = (round(sum(completion_token_lens) /
70
+ len(completion_token_lens), 4) if completion_token_lens else 0.0)
71
+
72
+ details = {
73
+ "total_steps": total_steps,
74
+ "total_tool_calls": total_tool_calls,
75
+ "avg_tool_calls_per_step": avg_tool_calls,
76
+ "unique_tool_count": unique_tool_count,
77
+ "tool_names": tool_names_all,
78
+ "max_completion_token_len": max_completion_token_len,
79
+ "min_completion_token_len": min_completion_token_len,
80
+ "avg_completion_token_len": avg_completion_token_len,
81
+ }
82
+
83
+ return AnalysisResult(
84
+ task_id=task.task_id,
85
+ is_badcase=None,
86
+ score=None,
87
+ details=details,
88
+ )
@@ -0,0 +1,256 @@
1
+ """Detect failures to recognize that a terminal command is still running.
2
+
3
+ Specific to ``terminal_bench_2`` / ``terminal_bench_2_verified``, whose shell
4
+ prompt looks like ``root@tb2vf-<task>-<id>:<path>#`` (ends with ``#``).
5
+
6
+ Rule
7
+ ----
8
+ A command has *finished* when the previous step's ``observation`` ends with a
9
+ shell prompt (a line that ends with ``#``/``$`` and has the ``user@host:path``
10
+ shape). Otherwise the command is *still running* (the observation shows
11
+ mid-output, e.g. an echoed command with no prompt).
12
+
13
+ If step ``i``'s command is still running but step ``i+1`` types a (substantive)
14
+ keystroke, the model failed to recognize the command was busy and typed into a
15
+ live terminal — a badcase.
16
+
17
+ Pure *wait/interrupt* keystrokes — an empty keystroke, a bare newline, or a
18
+ Ctrl key (``C-c`` / ``C-d`` / ``C-z``) — are treated as the model correctly
19
+ waiting/interrupting and do **not** count as violations (toggle with
20
+ ``ignore_wait_keystrokes``).
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import json
26
+ import logging
27
+ import re
28
+
29
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+ __all__ = ["TerminalBench2CommandRunningAnalyzer"]
34
+
35
+ # ANSI / terminal-control cleanup so a colored prompt or a progress-bar
36
+ # carriage-return overwrite does not confuse the prompt check.
37
+ _ANSI_RE = re.compile(r"\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
38
+
39
+ # keystrokes that mean "the model is waiting / interrupting", not "typing a new
40
+ # command into a busy terminal". Tmux-style escapes used by this harness.
41
+ _DEFAULT_WAIT_KEYSTROKES = {"", "C-c", "C-d", "\n"}
42
+
43
+ # default prompt suffixes: the observation (a finished shell prompt) ends with
44
+ # any one of these. Use a list so multi-char / alternative suffixes are easy.
45
+ _DEFAULT_PROMPT_CHARS = ["#", "#$"]
46
+
47
+ excluded_patterns = (
48
+ "Are you sure you want to mark the task as complete? This will trigger your solution to be graded and you won't be able to make any further corrections",
49
+ "WARNINGS: - No valid JSON object found\n\nPlease fix these issues and provide a proper JSON response.",
50
+ )
51
+
52
+
53
+ def _strip_ansi(text: str) -> str:
54
+ """Remove ANSI escape sequences and collapse ``\\r`` overwrites."""
55
+ if not text:
56
+ return ""
57
+ text = _ANSI_RE.sub("", text)
58
+ # progress bars rewrite a line with \r; keep the last fragment of each line
59
+ return "\n".join(line.split("\r")[-1] for line in text.split("\n"))
60
+
61
+
62
+ def _obs_text(step) -> str:
63
+ """Flatten a step's ``observation`` (a list of ``{content}`` dicts) to text."""
64
+ obs = getattr(step, "observation", None)
65
+ if not obs:
66
+ return ""
67
+ if isinstance(obs, list):
68
+ parts = []
69
+ for o in obs:
70
+ if isinstance(o, dict):
71
+ parts.append(str(o.get("content", "")))
72
+ else:
73
+ parts.append(str(o))
74
+ return "\n".join(parts)
75
+ return str(obs)
76
+
77
+
78
+ def _terminal_state(
79
+ text: str,
80
+ prompt_chars: list[str] | tuple[str, ...] | str = _DEFAULT_PROMPT_CHARS,
81
+ ignore_continuation: bool = True,
82
+ ) -> str:
83
+ """Classify a step's observation as the shell's state.
84
+
85
+ ``prompt_chars`` is a **list of suffix strings**: the command is finished if
86
+ the observation ends with any one of them (e.g. ``["#", "#$"]``).
87
+
88
+ Returns one of:
89
+
90
+ * ``"finished"`` — ends with a configured prompt suffix (command done);
91
+ * ``"continuation"`` — ends with a ``>`` PS2 prompt (composing a multi-line
92
+ command / heredoc; the shell is waiting for *input*, not executing);
93
+ * ``"busy"`` — mid-output with no prompt (command still executing);
94
+ * ``"unknown"`` — empty / cannot tell.
95
+ """
96
+ if not text:
97
+ return "unknown"
98
+ for item in excluded_patterns:
99
+ if item in text:
100
+ return "finished"
101
+ clean = _strip_ansi(text).rstrip()
102
+ if not clean:
103
+ return "unknown"
104
+ suffixes = (prompt_chars, ) if isinstance(prompt_chars, str) else tuple(prompt_chars)
105
+ # finished iff the observation ends with any configured prompt suffix
106
+ if clean.endswith(suffixes):
107
+ return "finished"
108
+ last = clean.rsplit("\n", 1)[-1].rstrip()
109
+ if ignore_continuation and last.endswith(">"):
110
+ return "continuation"
111
+ return "busy"
112
+
113
+
114
+ def _command_finished(
115
+ text: str,
116
+ prompt_chars: list[str] | tuple[str, ...] | str = _DEFAULT_PROMPT_CHARS,
117
+ ) -> bool:
118
+ """True iff ``text`` ends with a finished shell prompt."""
119
+ return _terminal_state(text, prompt_chars, ignore_continuation=False) == "finished"
120
+
121
+
122
+ def _keystrokes_of(step) -> list[str]:
123
+ """Return the ``keystrokes`` strings from every tool_call in a step."""
124
+ ac = getattr(step, "assistant_content", None)
125
+ tool_calls = getattr(ac, "tool_calls", None) or []
126
+ out: list[str] = []
127
+ tool_calls = tool_calls[:1] if tool_calls else []
128
+ for tc in tool_calls:
129
+ if not isinstance(tc, dict):
130
+ continue
131
+ fn = tc.get("function")
132
+ if not isinstance(fn, dict):
133
+ continue
134
+ args = fn.get("arguments", {})
135
+ if isinstance(args, str):
136
+ try:
137
+ args = json.loads(args)
138
+ except (json.JSONDecodeError, TypeError):
139
+ args = {}
140
+ if isinstance(args, dict) and "keystrokes" in args:
141
+ out.append(str(args.get("keystrokes") or ""))
142
+ return out
143
+
144
+
145
+ def _is_wait_keystroke(ks: str, conf: dict) -> bool:
146
+ """True if ``ks`` is a wait/interrupt (empty, whitespace, or a Ctrl key)."""
147
+ if ks is None:
148
+ return True
149
+ if conf.get("ignore_wait_keystrokes", True) and ks.strip() == "":
150
+ return True
151
+ if conf.get("ignore_wait_keystrokes", True):
152
+ waits = set(conf.get("wait_keystrokes", _DEFAULT_WAIT_KEYSTROKES))
153
+ if ks.strip() in waits:
154
+ return True
155
+ return False
156
+
157
+
158
+ @ANALYZERS.register()
159
+ class TerminalBench2CommandRunningAnalyzer(BaseAnalyzer):
160
+ """Flag attempts that type into a terminal while a command is still running.
161
+
162
+ For each consecutive step pair, if the previous step's observation does not
163
+ end with a finished shell prompt (command still running) but the current
164
+ step sends a substantive keystroke, the model failed to recognize the busy
165
+ terminal → badcase.
166
+
167
+ Conf keys:
168
+ prompt_chars – list of suffix strings marking a finished prompt;
169
+ the command is done if the observation ends with
170
+ any of them (default ``["#", "#$"]``)
171
+ ignore_wait_keystrokes – treat empty/whitespace/Ctrl keystrokes as waits,
172
+ not violations (default ``True``)
173
+ wait_keystrokes – extra keystroke strings treated as wait/interrupt
174
+ ignore_continuation_prompts – a ``>`` heredoc/multi-line continuation prompt
175
+ means the shell waits for input (not executing),
176
+ so typing there is fine (default ``True``)
177
+ max_reported – cap on the per-sample ``violations`` list (default 20)
178
+ """
179
+
180
+ id = "TerminalBench2CommandRunningAnalyzer"
181
+ description = "Detect typing into a terminal while a command is still running (no finished prompt before the next keystroke). Only for terminalbench*"
182
+ category = AnalyzerCategory.BEHAVIOR
183
+ datasets = ["terminal_bench_2", "terminal_bench_2_verified", "terminal_bench_2_1"]
184
+ data_requirements = ["$.trajectory"]
185
+ base_analyzer = None
186
+ priority = 0
187
+ conf: dict = {
188
+ "prompt_chars": list(_DEFAULT_PROMPT_CHARS),
189
+ "ignore_wait_keystrokes": True,
190
+ "wait_keystrokes": list(_DEFAULT_WAIT_KEYSTROKES),
191
+ "ignore_continuation_prompts": True,
192
+ "max_reported": 20,
193
+ }
194
+ distribution_fields = {
195
+ "command_running_violations": "numeric_stats",
196
+ }
197
+
198
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
199
+ if result is None or result.trajectory is None:
200
+ return AnalysisResult(
201
+ task_id=getattr(task, "task_id", None) if task else None,
202
+ is_badcase=None,
203
+ error="no trajectory available",
204
+ )
205
+
206
+ steps = result.trajectory.steps
207
+ if not steps:
208
+ return AnalysisResult(
209
+ task_id=getattr(task, "task_id", None) if task else None,
210
+ is_badcase=None,
211
+ error="empty trajectory",
212
+ )
213
+
214
+ prompt_chars = self.conf.get("prompt_chars", _DEFAULT_PROMPT_CHARS)
215
+ ignore_cont = bool(self.conf.get("ignore_continuation_prompts", True))
216
+ max_reported = int(self.conf.get("max_reported", 20))
217
+
218
+ violations: list[dict] = []
219
+ continuation_steps = 0
220
+ for i in range(len(steps) - 1):
221
+ prev, cur = steps[i], steps[i + 1]
222
+ prev_obs = _obs_text(prev)
223
+ state = _terminal_state(prev_obs, prompt_chars, ignore_continuation=ignore_cont)
224
+ # only a genuinely *busy* terminal (command still executing) can be a
225
+ # violation — finished and continuation (waiting-for-input) are fine.
226
+ if state == "continuation":
227
+ continuation_steps += 1
228
+ if state != "busy":
229
+ continue
230
+ # previous command still executing: did the model type into it?
231
+ ks_list = _keystrokes_of(cur)
232
+ if not ks_list:
233
+ continue
234
+ typed = [ks for ks in ks_list if not _is_wait_keystroke(ks, self.conf)]
235
+ if typed:
236
+ tail = _strip_ansi(prev_obs).rstrip()
237
+ violations.append({
238
+ "prev_step": getattr(prev, "step_id", i),
239
+ "step": getattr(cur, "step_id", i + 1),
240
+ "prev_observation_tail": tail[-200:],
241
+ "keystrokes": typed[0][:200],
242
+ })
243
+
244
+ is_badcase = len(violations) > 0
245
+ details: dict = {
246
+ "total_steps": len(steps),
247
+ "command_running_violations": len(violations),
248
+ "continuation_input_steps": continuation_steps,
249
+ "violations": violations[:max_reported],
250
+ }
251
+ return AnalysisResult(
252
+ task_id=getattr(task, "task_id", None) if task else None,
253
+ is_badcase=is_badcase,
254
+ details=details,
255
+ score=len(violations) / len(steps),
256
+ )
@@ -0,0 +1,92 @@
1
+ import logging
2
+
3
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ @ANALYZERS.register()
9
+ class CompletionLengthAnalyzer(BaseAnalyzer):
10
+ """Detect steps where LLM output (completion_tokens_len) exceeds a threshold."""
11
+
12
+ id = "CompletionLengthAnalyzer"
13
+ description = "Detect LLM steps whose completion token length exceeds the max_tokens threshold; reports mean/max completion tokens and the exceeded-step ratio."
14
+ category = AnalyzerCategory.EFFICIENCY
15
+ data_requirements = ["$.trajectory.steps[*].metric.completion_tokens_len"]
16
+ datasets = []
17
+ conf = {
18
+ "threshold": 0.0,
19
+ "only_incorrect": False,
20
+ "max_tokens": 131072,
21
+ }
22
+ distribution_fields = {
23
+ "mean_completion_tokens": "numeric_stats",
24
+ "max_completion_tokens": "numeric_stats",
25
+ "exceeded_ratio": "numeric_stats",
26
+ }
27
+
28
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
29
+ if result is None or result.trajectory is None:
30
+ return AnalysisResult(
31
+ task_id=task.task_id,
32
+ is_badcase=None,
33
+ error="no trajectory available",
34
+ )
35
+
36
+ steps = result.trajectory.steps
37
+ if not steps:
38
+ return AnalysisResult(
39
+ task_id=task.task_id,
40
+ is_badcase=None,
41
+ error="empty trajectory",
42
+ )
43
+
44
+ max_tokens = self.conf.get("max_tokens", 4096)
45
+ total_steps = 0
46
+ exceeded_steps = 0
47
+ exceeded_step_ids = []
48
+ token_values: list[int] = []
49
+
50
+ for step in steps:
51
+ if step.assistant_content is None:
52
+ continue
53
+ total_steps += 1
54
+ tokens = step.metric.completion_tokens_len if step.metric else None
55
+ if tokens is None:
56
+ continue
57
+ token_values.append(tokens)
58
+ if tokens > max_tokens:
59
+ exceeded_steps += 1
60
+ exceeded_step_ids.append(step.step_id)
61
+
62
+ if total_steps == 0:
63
+ return AnalysisResult(
64
+ task_id=task.task_id,
65
+ is_badcase=None,
66
+ error="no LLM steps in trajectory",
67
+ )
68
+
69
+ exceeded_ratio = exceeded_steps / total_steps
70
+ is_badcase = await self.is_threshold_badcase(exceeded_ratio) or (exceeded_steps > 0
71
+ and self.conf.get("threshold", 0.0) == 0.0)
72
+
73
+ mean_tokens = round(sum(token_values) / len(token_values), 1) if token_values else 0.0
74
+ max_tok = max(token_values) if token_values else 0
75
+
76
+ details = {
77
+ "total_llm_steps": total_steps,
78
+ "max_tokens_threshold": max_tokens,
79
+ "exceeded_steps": exceeded_steps,
80
+ "exceeded_ratio": round(exceeded_ratio, 4),
81
+ "mean_completion_tokens": mean_tokens,
82
+ "max_completion_tokens": max_tok,
83
+ }
84
+ if exceeded_step_ids:
85
+ details["exceeded_step_ids"] = exceeded_step_ids
86
+
87
+ return AnalysisResult(
88
+ task_id=task.task_id,
89
+ is_badcase=is_badcase,
90
+ details=details,
91
+ score=round(exceeded_ratio, 4),
92
+ )
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ @ANALYZERS.register()
11
+ class EmptyContentAnalyzer(BaseAnalyzer):
12
+ """Flag steps that have reasoning but no visible content and no tool calls.
13
+
14
+ This typically indicates the model produced internal reasoning only
15
+ without emitting any actionable output (no text content, no tool use).
16
+ """
17
+
18
+ id = "EmptyContentAnalyzer"
19
+ description = "Flag steps where the model emits reasoning but no visible content and no tool calls (reasoning-only dead steps)."
20
+ category = AnalyzerCategory.ERROR
21
+ datasets = []
22
+ data_requirements = ["$.trajectory"]
23
+ base_analyzer = None
24
+ priority = 0
25
+ conf: dict = {
26
+ "only_incorrect": False,
27
+ "threshold": 0.0,
28
+ }
29
+
30
+ async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
31
+ if result is None or result.trajectory is None:
32
+ return AnalysisResult(
33
+ task_id=task.task_id,
34
+ is_badcase=None,
35
+ error="no trajectory available",
36
+ )
37
+
38
+ steps = result.trajectory.steps
39
+ if not steps:
40
+ return AnalysisResult(
41
+ task_id=task.task_id,
42
+ is_badcase=None,
43
+ error="empty trajectory",
44
+ )
45
+
46
+ total_steps = len(steps)
47
+ bad_steps: list[dict] = []
48
+
49
+ for idx, step in enumerate(steps):
50
+ ac = step.assistant_content
51
+ if ac is None:
52
+ continue
53
+
54
+ reasoning = (getattr(ac, "reasoning_content", None) or "").strip()
55
+ content = (getattr(ac, "content", None) or "").strip()
56
+ tool_calls = getattr(ac, "tool_calls", None) or []
57
+
58
+ if not content and not tool_calls:
59
+ bad_steps.append({
60
+ "step_id": step.step_id,
61
+ "reasoning_preview": reasoning[:200],
62
+ })
63
+
64
+ bad_count = len(bad_steps)
65
+ score = round(bad_count / total_steps, 4) if total_steps else 0.0
66
+ is_badcase = bad_count > 0
67
+
68
+ details = {
69
+ "total_steps": total_steps,
70
+ "bad_steps": bad_count,
71
+ "score": score,
72
+ "bad_step_details": bad_steps,
73
+ }
74
+
75
+ return AnalysisResult(
76
+ task_id=task.task_id,
77
+ is_badcase=is_badcase,
78
+ details=details,
79
+ score=score,
80
+ )