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.
- agentcompass/__init__.py +53 -0
- agentcompass/analyzers/__init__.py +24 -0
- agentcompass/analyzers/basic/__init__.py +19 -0
- agentcompass/analyzers/basic/basic_metric_analyzer.py +88 -0
- agentcompass/analyzers/basic/command_running_analyzer.py +256 -0
- agentcompass/analyzers/basic/completion_length_analyzer.py +92 -0
- agentcompass/analyzers/basic/empty_content_analyzer.py +80 -0
- agentcompass/analyzers/basic/exception_analyzer.py +167 -0
- agentcompass/analyzers/basic/json_error_analyzer.py +177 -0
- agentcompass/analyzers/basic/keyword_analyzer.py +156 -0
- agentcompass/analyzers/basic/latency_analyzer.py +265 -0
- agentcompass/analyzers/basic/mixed_lang_analyzer.py +175 -0
- agentcompass/analyzers/basic/network_op_analyzer.py +192 -0
- agentcompass/analyzers/basic/repetition_analyzer.py +340 -0
- agentcompass/analyzers/basic/tool_repetition_analyzer.py +409 -0
- agentcompass/analyzers/basic/truncation_analyzer.py +62 -0
- agentcompass/analyzers/hack_detection/__init__.py +14 -0
- agentcompass/analyzers/hack_detection/analyzer.py +419 -0
- agentcompass/analyzers/hack_detection/llm_verification.py +395 -0
- agentcompass/analyzers/hack_detection/rules.py +322 -0
- agentcompass/analyzers/qualitative/__init__.py +4 -0
- agentcompass/analyzers/qualitative/assets/codebook.md +342 -0
- agentcompass/analyzers/qualitative/assets/prompt.md +195 -0
- agentcompass/analyzers/qualitative/multi_qualitative_analyzer.py +640 -0
- agentcompass/analyzers/qualitative/qualitative_analyzer.py +1178 -0
- agentcompass/analyzers/qualitative/trajectory_parser.py +198 -0
- agentcompass/analyzers/trajectory_graph/__init__.py +40 -0
- agentcompass/analyzers/trajectory_graph/graph.py +484 -0
- agentcompass/analyzers/trajectory_graph/io.py +302 -0
- agentcompass/analyzers/trajectory_graph/render_html.py +1108 -0
- agentcompass/benchmarks/__init__.py +35 -0
- agentcompass/benchmarks/brainarena/__init__.py +5 -0
- agentcompass/benchmarks/brainarena/brainarena.py +326 -0
- agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_2B.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_3A.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_3C.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/genkin_2025/Fig_4B.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/legare_2025/Fig_2B.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/legare_2025/Fig_3A.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/tanaka_2026/Fig_3E.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/tanaka_2026/Fig_4A.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/tanaka_2026/Fig_4G.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/yu_2025/Fig_5C.md +1 -0
- agentcompass/benchmarks/brainarena/data/queries/yu_2025/Fig_5M.md +1 -0
- agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_2B.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_3A.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_3C.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/genkin_2025/Fig_4B.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/legare_2025/Fig_2B.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/legare_2025/Fig_3A.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/legare_2025/README.md +7 -0
- agentcompass/benchmarks/brainarena/data/references/tanaka_2026/Fig_3E.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/tanaka_2026/Fig_4A.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/tanaka_2026/Fig_4G.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/yu_2025/Fig_5C.png +0 -0
- agentcompass/benchmarks/brainarena/data/references/yu_2025/Fig_5M.png +0 -0
- agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_2B.json +44 -0
- agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_3A.json +53 -0
- agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_3C.json +41 -0
- agentcompass/benchmarks/brainarena/data/rubrics/genkin_2025/Fig_4B.json +40 -0
- agentcompass/benchmarks/brainarena/data/rubrics/legare_2025/Fig_2B.json +41 -0
- agentcompass/benchmarks/brainarena/data/rubrics/legare_2025/Fig_3A.json +43 -0
- agentcompass/benchmarks/brainarena/data/rubrics/tanaka_2026/Fig_3E.json +51 -0
- agentcompass/benchmarks/brainarena/data/rubrics/tanaka_2026/Fig_4A.json +44 -0
- agentcompass/benchmarks/brainarena/data/rubrics/tanaka_2026/Fig_4G.json +45 -0
- agentcompass/benchmarks/brainarena/data/rubrics/yu_2025/Fig_5C.json +39 -0
- agentcompass/benchmarks/brainarena/data/rubrics/yu_2025/Fig_5M.json +43 -0
- agentcompass/benchmarks/brainarena/data/tasks.json +13 -0
- agentcompass/benchmarks/brainarena/datasets.py +197 -0
- agentcompass/benchmarks/brainarena/scorer.py +202 -0
- agentcompass/benchmarks/browsecomp.py +127 -0
- agentcompass/benchmarks/browsecomp_zh.py +127 -0
- agentcompass/benchmarks/config.py +118 -0
- agentcompass/benchmarks/deepresearch_bench/__init__.py +5 -0
- agentcompass/benchmarks/deepresearch_bench/dataset.py +142 -0
- agentcompass/benchmarks/deepresearch_bench/deepresearch_bench.py +528 -0
- agentcompass/benchmarks/deepresearch_bench/fact.py +472 -0
- agentcompass/benchmarks/deepresearch_bench/parsing.py +178 -0
- agentcompass/benchmarks/deepresearch_bench/prompts.py +353 -0
- agentcompass/benchmarks/deepresearch_bench/race.py +429 -0
- agentcompass/benchmarks/deepresearch_bench/weighting.py +180 -0
- agentcompass/benchmarks/deepsearchqa.py +142 -0
- agentcompass/benchmarks/deepswe.py +532 -0
- agentcompass/benchmarks/frontier_engineering/__init__.py +5 -0
- agentcompass/benchmarks/frontier_engineering/data/medal_podium.csv +48 -0
- agentcompass/benchmarks/frontier_engineering/data/rank_baseline.csv +441 -0
- agentcompass/benchmarks/frontier_engineering/data/v1.yaml +264 -0
- agentcompass/benchmarks/frontier_engineering/data/v1_lite.yaml +83 -0
- agentcompass/benchmarks/frontier_engineering/evaluator.py +663 -0
- agentcompass/benchmarks/frontier_engineering/frontier_engineering.py +1995 -0
- agentcompass/benchmarks/frontier_engineering/openevolve_evaluator.py +38 -0
- agentcompass/benchmarks/frontier_swe.py +485 -0
- agentcompass/benchmarks/frontierscience.py +147 -0
- agentcompass/benchmarks/gaia.py +141 -0
- agentcompass/benchmarks/gdpval_ac/__init__.py +5 -0
- agentcompass/benchmarks/gdpval_ac/dataset.py +285 -0
- agentcompass/benchmarks/gdpval_ac/gdpval_ac.py +1211 -0
- agentcompass/benchmarks/gdpval_ac/pairwise_core.py +3111 -0
- agentcompass/benchmarks/gdpval_ac/stage_baseline.py +165 -0
- agentcompass/benchmarks/hle.py +320 -0
- agentcompass/benchmarks/osworld/__init__.py +10 -0
- agentcompass/benchmarks/osworld/agent_loop.py +118 -0
- agentcompass/benchmarks/osworld/agents/__init__.py +12 -0
- agentcompass/benchmarks/osworld/agents/base.py +30 -0
- agentcompass/benchmarks/osworld/agents/claude.py +639 -0
- agentcompass/benchmarks/osworld/agents/qwen35.py +420 -0
- agentcompass/benchmarks/osworld/osworld.py +435 -0
- agentcompass/benchmarks/osworld/runtime/__init__.py +23 -0
- agentcompass/benchmarks/osworld/runtime/controllers/__init__.py +0 -0
- agentcompass/benchmarks/osworld/runtime/controllers/python.py +496 -0
- agentcompass/benchmarks/osworld/runtime/controllers/setup.py +1068 -0
- agentcompass/benchmarks/osworld/runtime/docker_adapter.py +124 -0
- agentcompass/benchmarks/osworld/runtime/evaluator.py +161 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/__init__.py +5 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/__init__.py +21 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/calc.py +15 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/chrome.py +2714 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/file.py +156 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/general.py +44 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/gimp.py +38 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/impress.py +129 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/info.py +50 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/misc.py +495 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/replay.py +20 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/vlc.py +98 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/getters/vscode.py +43 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/__init__.py +45 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/basic_os.py +68 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/chrome.py +582 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/docs.py +1195 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/general.py +676 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/gimp.py +951 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/libreoffice.py +24 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/others.py +102 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/pdf.py +30 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/slides.py +1147 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/table.py +693 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/thunderbird.py +179 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/utils.py +820 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/vlc.py +556 -0
- agentcompass/benchmarks/osworld/runtime/evaluators/metrics/vscode.py +469 -0
- agentcompass/benchmarks/osworld/runtime/task_environment.py +154 -0
- agentcompass/benchmarks/osworld/runtime/types.py +49 -0
- agentcompass/benchmarks/pinchbench/__init__.py +3 -0
- agentcompass/benchmarks/pinchbench/grading_runner.py +118 -0
- agentcompass/benchmarks/pinchbench/pinchbench.py +908 -0
- agentcompass/benchmarks/pinchbench/pinchbench_grading_runtime.py +656 -0
- agentcompass/benchmarks/researchclawbench.py +545 -0
- agentcompass/benchmarks/scicode/__init__.py +8 -0
- agentcompass/benchmarks/scicode/data/13.6.txt +49 -0
- agentcompass/benchmarks/scicode/data/62.1.txt +29 -0
- agentcompass/benchmarks/scicode/data/76.3.txt +27 -0
- agentcompass/benchmarks/scicode/data/background_comment_template.txt +28 -0
- agentcompass/benchmarks/scicode/data/multistep_template.txt +23 -0
- agentcompass/benchmarks/scicode/data/problems_dev.jsonl +15 -0
- agentcompass/benchmarks/scicode/data/problems_test.jsonl +65 -0
- agentcompass/benchmarks/scicode/scicode.py +962 -0
- agentcompass/benchmarks/screenspot.py +213 -0
- agentcompass/benchmarks/sealqa.py +286 -0
- agentcompass/benchmarks/sgi_deep_research.py +124 -0
- agentcompass/benchmarks/skillsbench/__init__.py +10 -0
- agentcompass/benchmarks/skillsbench/benchmark.py +378 -0
- agentcompass/benchmarks/skillsbench/parser/__init__.py +12 -0
- agentcompass/benchmarks/skillsbench/parser/parsers.py +60 -0
- agentcompass/benchmarks/skillsbench/parser/v10.py +51 -0
- agentcompass/benchmarks/skillsbench/parser/v11.py +57 -0
- agentcompass/benchmarks/special_pattern.py +256 -0
- agentcompass/benchmarks/swe_marathon/__init__.py +9 -0
- agentcompass/benchmarks/swe_marathon/swe_marathon.py +632 -0
- agentcompass/benchmarks/swe_marathon/verifier.py +189 -0
- agentcompass/benchmarks/swe_marathon/workdir.py +48 -0
- agentcompass/benchmarks/swebench_multilingual.py +539 -0
- agentcompass/benchmarks/swebench_pro.py +641 -0
- agentcompass/benchmarks/swebench_pro_verified/__init__.py +13 -0
- agentcompass/benchmarks/swebench_pro_verified/network_policy.py +41 -0
- agentcompass/benchmarks/swebench_pro_verified/swebench_pro_verified.py +756 -0
- agentcompass/benchmarks/swebench_verified.py +579 -0
- agentcompass/benchmarks/taubench/__init__.py +5 -0
- agentcompass/benchmarks/taubench/_taubench_patch.py +536 -0
- agentcompass/benchmarks/taubench/agent_runner.py +261 -0
- agentcompass/benchmarks/taubench/remote_runner.py +443 -0
- agentcompass/benchmarks/taubench/taubench.py +786 -0
- agentcompass/benchmarks/taubench/utils/__init__.py +19 -0
- agentcompass/benchmarks/taubench/utils/embedding_cache.py +174 -0
- agentcompass/benchmarks/taubench/utils/model_backend.py +518 -0
- agentcompass/benchmarks/taubench/utils/utils.py +198 -0
- agentcompass/benchmarks/terminalbench2/__init__.py +0 -0
- agentcompass/benchmarks/terminalbench2/terminalbench2.py +344 -0
- agentcompass/benchmarks/terminalbench2/terminalbench2_1.py +369 -0
- agentcompass/benchmarks/terminalbench2/terminalbench2_verified.py +404 -0
- agentcompass/benchmarks/terminalbench2/verifier.py +185 -0
- agentcompass/benchmarks/terminalbench2/verifier_health.py +68 -0
- agentcompass/benchmarks/terminalbench2/workdir.py +53 -0
- agentcompass/benchmarks/utils/__init__.py +6 -0
- agentcompass/benchmarks/utils/data.py +118 -0
- agentcompass/benchmarks/utils/harbor.py +355 -0
- agentcompass/benchmarks/utils/scorers/__init__.py +25 -0
- agentcompass/benchmarks/utils/scorers/base.py +101 -0
- agentcompass/benchmarks/utils/scorers/deepsearchqa.py +239 -0
- agentcompass/benchmarks/utils/scorers/frontier_swe.py +257 -0
- agentcompass/benchmarks/utils/scorers/frontierscience.py +523 -0
- agentcompass/benchmarks/utils/scorers/llm.py +258 -0
- agentcompass/benchmarks/utils/scorers/researchclawbench.py +367 -0
- agentcompass/benchmarks/utils/scorers/sealqa.py +156 -0
- agentcompass/benchmarks/utils/scorers/xbench_deepsearch.py +94 -0
- agentcompass/benchmarks/widesearch/__init__.py +28 -0
- agentcompass/benchmarks/widesearch/benchmark.py +240 -0
- agentcompass/benchmarks/widesearch/dataset.py +90 -0
- agentcompass/benchmarks/widesearch/evaluator.py +724 -0
- agentcompass/benchmarks/widesearch/metrics.py +20 -0
- agentcompass/benchmarks/wildclawbench.py +998 -0
- agentcompass/benchmarks/wildclawbench_grading_runner.py +60 -0
- agentcompass/benchmarks/xbench_deepsearch.py +168 -0
- agentcompass/cli/__init__.py +7 -0
- agentcompass/cli/__main__.py +7 -0
- agentcompass/cli/main.py +1230 -0
- agentcompass/environments/__init__.py +10 -0
- agentcompass/environments/brainpp.py +531 -0
- agentcompass/environments/daytona.py +576 -0
- agentcompass/environments/docker.py +845 -0
- agentcompass/environments/docker_egress_proxy.py +398 -0
- agentcompass/environments/env_gateway.py +761 -0
- agentcompass/environments/host_process.py +202 -0
- agentcompass/environments/hsandbox.py +536 -0
- agentcompass/environments/modal.py +606 -0
- agentcompass/environments/opensandbox.py +384 -0
- agentcompass/environments/utils/__init__.py +1 -0
- agentcompass/environments/utils/exec.py +60 -0
- agentcompass/environments/utils/files.py +54 -0
- agentcompass/environments/utils/pvisor.py +208 -0
- agentcompass/environments/utils/shared_storage.py +181 -0
- agentcompass/errors.py +55 -0
- agentcompass/harnesses/__init__.py +14 -0
- agentcompass/harnesses/claude_code.py +576 -0
- agentcompass/harnesses/codex.py +639 -0
- agentcompass/harnesses/gui_agents/__init__.py +1 -0
- agentcompass/harnesses/gui_agents/qwen3vl.py +286 -0
- agentcompass/harnesses/mini_swe_agent/__init__.py +6 -0
- agentcompass/harnesses/mini_swe_agent/harness.py +503 -0
- agentcompass/harnesses/mini_swe_agent/interleaved_model.py +31 -0
- agentcompass/harnesses/mini_swe_agent/local_runner.py +684 -0
- agentcompass/harnesses/mini_swe_agent/plan.py +60 -0
- agentcompass/harnesses/mini_swe_agent/remote_runner.py +143 -0
- agentcompass/harnesses/mini_swe_agent/trajectory.py +160 -0
- agentcompass/harnesses/naive_search_agent/__init__.py +6 -0
- agentcompass/harnesses/naive_search_agent/engine/__init__.py +1 -0
- agentcompass/harnesses/naive_search_agent/engine/coordinator.py +148 -0
- agentcompass/harnesses/naive_search_agent/engine/fc_inferencer.py +711 -0
- agentcompass/harnesses/naive_search_agent/engine/runner.py +338 -0
- agentcompass/harnesses/naive_search_agent/engine/sub_agents.py +289 -0
- agentcompass/harnesses/naive_search_agent/engine/tools/__init__.py +1 -0
- agentcompass/harnesses/naive_search_agent/engine/tools/browse.py +577 -0
- agentcompass/harnesses/naive_search_agent/engine/tools/registry.py +198 -0
- agentcompass/harnesses/naive_search_agent/engine/tools/search.py +647 -0
- agentcompass/harnesses/naive_search_agent/engine/tools/web_visitor.py +491 -0
- agentcompass/harnesses/naive_search_agent/harness.py +204 -0
- agentcompass/harnesses/naive_search_agent/local_runner.py +115 -0
- agentcompass/harnesses/naive_search_agent/plan.py +80 -0
- agentcompass/harnesses/naive_search_agent/trajectory.py +211 -0
- agentcompass/harnesses/openai_chat.py +267 -0
- agentcompass/harnesses/openclaw.py +1413 -0
- agentcompass/harnesses/openevolve/__init__.py +13 -0
- agentcompass/harnesses/openevolve/harness.py +793 -0
- agentcompass/harnesses/openevolve/runner.py +557 -0
- agentcompass/harnesses/openhands/__init__.py +6 -0
- agentcompass/harnesses/openhands/harness.py +258 -0
- agentcompass/harnesses/openhands/plan.py +27 -0
- agentcompass/harnesses/openhands/prompt.py +34 -0
- agentcompass/harnesses/openhands/remote_entry.py +818 -0
- agentcompass/harnesses/openhands/remote_runner.py +432 -0
- agentcompass/harnesses/openhands/trajectory.py +453 -0
- agentcompass/harnesses/qwen3vl_gui.py +119 -0
- agentcompass/harnesses/researchharness.py +856 -0
- agentcompass/harnesses/scicode_tool_use.py +1038 -0
- agentcompass/harnesses/terminus2/__init__.py +0 -0
- agentcompass/harnesses/terminus2/llms/__init__.py +0 -0
- agentcompass/harnesses/terminus2/llms/chat.py +179 -0
- agentcompass/harnesses/terminus2/llms/llm.py +796 -0
- agentcompass/harnesses/terminus2/templates/terminus-json-plain.txt +54 -0
- agentcompass/harnesses/terminus2/templates/terminus-toolcall.txt +30 -0
- agentcompass/harnesses/terminus2/templates/terminus-xml-plain.txt +60 -0
- agentcompass/harnesses/terminus2/templates/timeout.txt +10 -0
- agentcompass/harnesses/terminus2/terminus_2.py +2496 -0
- agentcompass/harnesses/terminus2/tmux_session.py +801 -0
- agentcompass/harnesses/utils/__init__.py +23 -0
- agentcompass/harnesses/utils/glibc.py +139 -0
- agentcompass/harnesses/utils/model.py +24 -0
- agentcompass/harnesses/utils/result.py +24 -0
- agentcompass/harnesses/utils/sync.py +231 -0
- agentcompass/launcher.py +1394 -0
- agentcompass/recipes/__init__.py +37 -0
- agentcompass/recipes/brainarena/__init__.py +5 -0
- agentcompass/recipes/brainarena/docker.py +36 -0
- agentcompass/recipes/deepswe/__init__.py +5 -0
- agentcompass/recipes/deepswe/common.py +25 -0
- agentcompass/recipes/deepswe/daytona.py +24 -0
- agentcompass/recipes/deepswe/docker.py +24 -0
- agentcompass/recipes/deepswe/modal.py +24 -0
- agentcompass/recipes/frontier_engineering/__init__.py +3 -0
- agentcompass/recipes/frontier_engineering/docker.py +90 -0
- agentcompass/recipes/frontier_swe/__init__.py +5 -0
- agentcompass/recipes/frontier_swe/common.py +25 -0
- agentcompass/recipes/frontier_swe/daytona.py +21 -0
- agentcompass/recipes/frontier_swe/docker.py +23 -0
- agentcompass/recipes/frontier_swe/modal.py +26 -0
- agentcompass/recipes/gdpval_ac/__init__.py +5 -0
- agentcompass/recipes/gdpval_ac/common.py +19 -0
- agentcompass/recipes/gdpval_ac/daytona.py +25 -0
- agentcompass/recipes/gdpval_ac/docker.py +31 -0
- agentcompass/recipes/gdpval_ac/modal.py +25 -0
- agentcompass/recipes/osworld/__init__.py +3 -0
- agentcompass/recipes/osworld/docker.py +139 -0
- agentcompass/recipes/pinchbench/__init__.py +5 -0
- agentcompass/recipes/pinchbench/common.py +47 -0
- agentcompass/recipes/pinchbench/daytona.py +33 -0
- agentcompass/recipes/pinchbench/docker.py +34 -0
- agentcompass/recipes/pinchbench/modal.py +33 -0
- agentcompass/recipes/researchclawbench/__init__.py +11 -0
- agentcompass/recipes/researchclawbench/daytona.py +28 -0
- agentcompass/recipes/researchclawbench/docker.py +29 -0
- agentcompass/recipes/researchclawbench/modal.py +28 -0
- agentcompass/recipes/skillsbench/__init__.py +3 -0
- agentcompass/recipes/skillsbench/docker.py +54 -0
- agentcompass/recipes/swe_marathon/docker.py +30 -0
- agentcompass/recipes/swebench_multilingual/__init__.py +5 -0
- agentcompass/recipes/swebench_multilingual/daytona.py +47 -0
- agentcompass/recipes/swebench_multilingual/docker.py +45 -0
- agentcompass/recipes/swebench_multilingual/modal.py +47 -0
- agentcompass/recipes/swebench_pro/__init__.py +5 -0
- agentcompass/recipes/swebench_pro/daytona.py +42 -0
- agentcompass/recipes/swebench_pro/docker.py +50 -0
- agentcompass/recipes/swebench_pro/modal.py +42 -0
- agentcompass/recipes/swebench_pro_verified/__init__.py +3 -0
- agentcompass/recipes/swebench_pro_verified/docker.py +50 -0
- agentcompass/recipes/swebench_verified/__init__.py +6 -0
- agentcompass/recipes/swebench_verified/common.py +55 -0
- agentcompass/recipes/swebench_verified/daytona.py +58 -0
- agentcompass/recipes/swebench_verified/docker.py +44 -0
- agentcompass/recipes/swebench_verified/host_process.py +28 -0
- agentcompass/recipes/swebench_verified/modal.py +56 -0
- agentcompass/recipes/taubench/__init__.py +5 -0
- agentcompass/recipes/taubench/docker.py +34 -0
- agentcompass/recipes/terminalbench2/daytona.py +31 -0
- agentcompass/recipes/terminalbench2/docker.py +28 -0
- agentcompass/recipes/terminalbench2/docker_ac.py +62 -0
- agentcompass/recipes/terminalbench2/modal.py +29 -0
- agentcompass/recipes/terminalbench2_1/daytona.py +31 -0
- agentcompass/recipes/terminalbench2_1/docker.py +28 -0
- agentcompass/recipes/terminalbench2_1/docker_ac.py +62 -0
- agentcompass/recipes/terminalbench2_1/modal.py +29 -0
- agentcompass/recipes/terminalbench2_verified/daytona.py +34 -0
- agentcompass/recipes/terminalbench2_verified/docker.py +33 -0
- agentcompass/recipes/terminalbench2_verified/docker_ac.py +67 -0
- agentcompass/recipes/terminalbench2_verified/modal.py +32 -0
- agentcompass/recipes/wildclawbench/__init__.py +3 -0
- agentcompass/recipes/wildclawbench/common.py +55 -0
- agentcompass/recipes/wildclawbench/docker.py +34 -0
- agentcompass/runtime/__init__.py +147 -0
- agentcompass/runtime/analysis.py +161 -0
- agentcompass/runtime/artifacts.py +812 -0
- agentcompass/runtime/attempts/__init__.py +29 -0
- agentcompass/runtime/attempts/repository.py +374 -0
- agentcompass/runtime/attempts/scheduler.py +192 -0
- agentcompass/runtime/base.py +1034 -0
- agentcompass/runtime/capsule.py +507 -0
- agentcompass/runtime/checkpoint.py +256 -0
- agentcompass/runtime/config/__init__.py +73 -0
- agentcompass/runtime/config/component.py +203 -0
- agentcompass/runtime/config/environment.py +72 -0
- agentcompass/runtime/config/loader.py +418 -0
- agentcompass/runtime/config/settings.py +139 -0
- agentcompass/runtime/context.py +37 -0
- agentcompass/runtime/dependencies.py +218 -0
- agentcompass/runtime/issues.py +123 -0
- agentcompass/runtime/limits.py +309 -0
- agentcompass/runtime/llm/__init__.py +66 -0
- agentcompass/runtime/llm/anthropic.py +52 -0
- agentcompass/runtime/llm/errors.py +22 -0
- agentcompass/runtime/llm/generate.py +238 -0
- agentcompass/runtime/llm/openai.py +402 -0
- agentcompass/runtime/llm/protocol.py +123 -0
- agentcompass/runtime/logging.py +488 -0
- agentcompass/runtime/metrics/__init__.py +103 -0
- agentcompass/runtime/metrics/benchmark_aggregation.py +423 -0
- agentcompass/runtime/metrics/contract.py +250 -0
- agentcompass/runtime/metrics/mode.py +12 -0
- agentcompass/runtime/metrics/reducer.py +377 -0
- agentcompass/runtime/metrics/reducers.py +238 -0
- agentcompass/runtime/metrics/report.py +455 -0
- agentcompass/runtime/metrics/report_aggregation.py +418 -0
- agentcompass/runtime/metrics/strategies.py +214 -0
- agentcompass/runtime/metrics/types.py +26 -0
- agentcompass/runtime/models/__init__.py +104 -0
- agentcompass/runtime/models/analysis.py +28 -0
- agentcompass/runtime/models/model.py +75 -0
- agentcompass/runtime/models/orchestration.py +243 -0
- agentcompass/runtime/models/plan.py +97 -0
- agentcompass/runtime/models/request.py +658 -0
- agentcompass/runtime/models/result.py +117 -0
- agentcompass/runtime/models/task.py +320 -0
- agentcompass/runtime/models/trajectory.py +84 -0
- agentcompass/runtime/network.py +462 -0
- agentcompass/runtime/orchestration.py +1150 -0
- agentcompass/runtime/planner.py +384 -0
- agentcompass/runtime/progress.py +1002 -0
- agentcompass/runtime/recipes.py +128 -0
- agentcompass/runtime/recovery.py +108 -0
- agentcompass/runtime/registry.py +106 -0
- agentcompass/runtime/resources.py +203 -0
- agentcompass/runtime/results/__init__.py +15 -0
- agentcompass/runtime/results/detail.py +474 -0
- agentcompass/runtime/results/layout.py +308 -0
- agentcompass/runtime/results/legacy.py +44 -0
- agentcompass/runtime/results/render.py +185 -0
- agentcompass/runtime/results/store.py +1706 -0
- agentcompass/runtime/results/summary.py +101 -0
- agentcompass/runtime/runner.py +2385 -0
- agentcompass/runtime/setup.py +71 -0
- agentcompass/runtime/tasks.py +137 -0
- agentcompass/runtime/timeouts.py +59 -0
- agentcompass/utils/__init__.py +5 -0
- agentcompass/utils/api_key.py +40 -0
- agentcompass/utils/call.py +90 -0
- agentcompass/utils/command.py +108 -0
- agentcompass/utils/env.py +89 -0
- agentcompass/utils/exceptions.py +25 -0
- agentcompass/utils/redaction.py +29 -0
- agentcompass/utils/workspace.py +39 -0
- agentcompass/viewer/__init__.py +0 -0
- agentcompass/viewer/reader.py +838 -0
- agentcompass/viewer/server.py +160 -0
- agentcompass/viewer/static/assets/index-BywoR7aU.js +170 -0
- agentcompass/viewer/static/assets/index-CqGkz9_L.css +1 -0
- agentcompass/viewer/static/index.html +17 -0
- agentcompass-1.0.0.dist-info/METADATA +275 -0
- agentcompass-1.0.0.dist-info/RECORD +440 -0
- agentcompass-1.0.0.dist-info/WHEEL +5 -0
- agentcompass-1.0.0.dist-info/entry_points.txt +2 -0
- agentcompass-1.0.0.dist-info/licenses/LICENSE +203 -0
- agentcompass-1.0.0.dist-info/top_level.txt +1 -0
agentcompass/__init__.py
ADDED
|
@@ -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
|
+
)
|