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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
_FRAME_RE = re.compile(r'^\s*File "([^"]+)", line \d+, in (\S+)', re.MULTILINE)
|
|
10
|
+
|
|
11
|
+
_EXC_LINE_RE = re.compile(r'^([A-Za-z_][\w.]*): (.*)$', re.MULTILINE)
|
|
12
|
+
|
|
13
|
+
_CHAIN_SEP_RE = re.compile(r'(?:The above exception was the direct cause of the following exception'
|
|
14
|
+
r'|During handling of the above exception, another exception occurred):')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _normalize_message(msg: str) -> str:
|
|
18
|
+
if not msg:
|
|
19
|
+
return ""
|
|
20
|
+
s = msg
|
|
21
|
+
s = re.sub(r'0x[0-9a-fA-F]+', '<hex>', s)
|
|
22
|
+
s = re.sub(r'\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b', '<uuid>', s)
|
|
23
|
+
s = re.sub(r'\b\d{1,3}(?:\.\d{1,3}){3}\b', '<ip>', s)
|
|
24
|
+
s = re.sub(r'(?:/[\w.\-]+)+', '<path>', s)
|
|
25
|
+
# s = re.sub(r'\b\d+(?:\.\d+)?\b', '<n>', s)
|
|
26
|
+
s = re.sub(r'"[^"]*"', '<s>', s)
|
|
27
|
+
s = re.sub(r"'[^']*'", '<s>', s)
|
|
28
|
+
return s.strip()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _traceback_fingerprint(error: str) -> dict:
|
|
32
|
+
"""Parse a Python traceback into a stable aggregation fingerprint.
|
|
33
|
+
"""
|
|
34
|
+
out = {
|
|
35
|
+
"error_type": "",
|
|
36
|
+
"error_location": "",
|
|
37
|
+
"error_message_normalized": "",
|
|
38
|
+
"error_fingerprint": "",
|
|
39
|
+
"error_cause_type": "",
|
|
40
|
+
"error_cause_location": "",
|
|
41
|
+
"error_cause": "",
|
|
42
|
+
}
|
|
43
|
+
if not error or not error.strip():
|
|
44
|
+
return out
|
|
45
|
+
|
|
46
|
+
exc_lines = _EXC_LINE_RE.findall(error)
|
|
47
|
+
frames = [(m.start(), m.group(1), m.group(2)) for m in _FRAME_RE.finditer(error)]
|
|
48
|
+
sep = _CHAIN_SEP_RE.search(error)
|
|
49
|
+
|
|
50
|
+
if not exc_lines:
|
|
51
|
+
out["error_type"] = error
|
|
52
|
+
out["error_fingerprint"] = _normalize_message(error.strip()) or "<error>"
|
|
53
|
+
return out
|
|
54
|
+
|
|
55
|
+
final_type, final_msg = exc_lines[-1]
|
|
56
|
+
chained = bool(sep) and len(exc_lines) >= 2
|
|
57
|
+
cause_type, cause_msg = exc_lines[0] if chained else ("", "")
|
|
58
|
+
|
|
59
|
+
final_loc = ""
|
|
60
|
+
cause_loc = ""
|
|
61
|
+
if frames:
|
|
62
|
+
_, ff_path, ff_func = frames[-1]
|
|
63
|
+
final_loc = f"{os.path.basename(ff_path)}:{ff_func}"
|
|
64
|
+
if chained:
|
|
65
|
+
before = [f for f in frames if f[0] < sep.start()]
|
|
66
|
+
if before:
|
|
67
|
+
_, cf_path, cf_func = before[-1]
|
|
68
|
+
cause_loc = f"{os.path.basename(cf_path)}:{cf_func}"
|
|
69
|
+
|
|
70
|
+
final_msg_n = _normalize_message(final_msg)
|
|
71
|
+
out["error_type"] = f"{final_type}: {final_msg_n}"
|
|
72
|
+
out["error_location"] = final_loc
|
|
73
|
+
|
|
74
|
+
if chained:
|
|
75
|
+
cause_msg_n = _normalize_message(cause_msg)
|
|
76
|
+
out["error_cause_type"] = cause_type
|
|
77
|
+
out["error_cause_location"] = cause_loc
|
|
78
|
+
out["error_cause"] = f"{cause_type}: {cause_msg}"
|
|
79
|
+
out["error_message_normalized"] = (f"{final_msg_n} (caused by: {cause_type}: {cause_msg_n})")
|
|
80
|
+
out["error_fingerprint"] = (f"{final_type} @ {final_loc or '<unknown>'} "
|
|
81
|
+
f"(caused by: {cause_type} @ {cause_loc or '<unknown>'})")
|
|
82
|
+
else:
|
|
83
|
+
out["error_message_normalized"] = final_msg_n
|
|
84
|
+
out["error_fingerprint"] = f"{final_type} @ {final_loc or '<unknown>'}"
|
|
85
|
+
return out
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@ANALYZERS.register()
|
|
89
|
+
class ExceptionAnalyzer(BaseAnalyzer):
|
|
90
|
+
id = "ExceptionAnalyzer"
|
|
91
|
+
description = "Detect execution issues and preserved diagnostic evidence, and parse tracebacks into stable, aggregable fingerprints (error type, location, cause)."
|
|
92
|
+
category = AnalyzerCategory.ENV_FRAMEWORK_ERROR
|
|
93
|
+
data_requirements = ["$.issues"]
|
|
94
|
+
datasets = []
|
|
95
|
+
conf = {"only_incorrect": False, "threshold": 0.0}
|
|
96
|
+
distribution_fields = {
|
|
97
|
+
"error_type": "value_counts",
|
|
98
|
+
"error_fingerprint": "value_counts",
|
|
99
|
+
"task_status": "value_counts",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
|
|
103
|
+
is_badcase = False
|
|
104
|
+
details = {}
|
|
105
|
+
|
|
106
|
+
if result is None:
|
|
107
|
+
return AnalysisResult(task_id=task.task_id, is_badcase=None, error="RunResult is None")
|
|
108
|
+
|
|
109
|
+
has_error_status = any(issue.severity in {"fatal", "error"} for issue in result.issues)
|
|
110
|
+
diagnostics = result.artifacts.get("execution_diagnostics") or "\n".join(issue.message
|
|
111
|
+
for issue in result.issues)
|
|
112
|
+
has_error_field = bool(diagnostics)
|
|
113
|
+
|
|
114
|
+
is_badcase = has_error_status or has_error_field
|
|
115
|
+
|
|
116
|
+
if has_error_status:
|
|
117
|
+
details["task_status"] = f"{result.status.value}"
|
|
118
|
+
if has_error_field:
|
|
119
|
+
details["error_snippet"] = diagnostics[:500]
|
|
120
|
+
details.update(_traceback_fingerprint(diagnostics))
|
|
121
|
+
|
|
122
|
+
return AnalysisResult(
|
|
123
|
+
task_id=task.task_id,
|
|
124
|
+
is_badcase=is_badcase,
|
|
125
|
+
score=1 if is_badcase else 0,
|
|
126
|
+
details=details,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@ANALYZERS.register()
|
|
131
|
+
class TerminalBench2ExceptionAnalyzer(ExceptionAnalyzer):
|
|
132
|
+
"""TerminalBench2-specific exception analysis.
|
|
133
|
+
|
|
134
|
+
Extends the generic ExceptionAnalyzer with environment error detection
|
|
135
|
+
from eval_raw_data.test_output.
|
|
136
|
+
"""
|
|
137
|
+
id = "TerminalBench2ExceptionAnalyzer"
|
|
138
|
+
description = "TerminalBench2 variant of ExceptionAnalyzer that also detects environment-level errors from eval test output (terminal_bench_2 only)."
|
|
139
|
+
datasets = ["terminal_bench_2", "terminal_bench_2_verified", "terminal_bench_2_1"]
|
|
140
|
+
base_analyzer = "ExceptionAnalyzer"
|
|
141
|
+
priority = 1
|
|
142
|
+
|
|
143
|
+
async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
|
|
144
|
+
base_result = await super().analysis(task, prepared, result, req, plan)
|
|
145
|
+
|
|
146
|
+
extra = result.extra or {}
|
|
147
|
+
eval_raw = extra.get("eval_raw_data") or {}
|
|
148
|
+
test_output = eval_raw.get("testcase_output")
|
|
149
|
+
if test_output and self._detect_env_error(test_output):
|
|
150
|
+
base_result.is_badcase = True
|
|
151
|
+
base_result.details["eval_environment_error"] = True
|
|
152
|
+
base_result.details["eval_environment_error_snippet"] = str(test_output)[:500]
|
|
153
|
+
if base_result.score is None or base_result.score < 1:
|
|
154
|
+
base_result.score = 1
|
|
155
|
+
|
|
156
|
+
return base_result
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _detect_env_error(test_output: str) -> bool:
|
|
160
|
+
"""Detect environment-level errors from test output."""
|
|
161
|
+
signals = [
|
|
162
|
+
"connection refused", "connection timed out", "name or service not known", "no such file or directory",
|
|
163
|
+
"permission denied", "address already in use", "environment error", "command not found",
|
|
164
|
+
"E: Could not get lock /var/lib/dpkg/lock-frontend", "No such file or directory"
|
|
165
|
+
]
|
|
166
|
+
lower = str(test_output).lower()
|
|
167
|
+
return any(s in lower for s in signals)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _extract_json_content(response: str) -> tuple[str, List[str]]:
|
|
11
|
+
"""Extract JSON content from response, handling extra text."""
|
|
12
|
+
warnings = []
|
|
13
|
+
|
|
14
|
+
# Try to find JSON object boundaries
|
|
15
|
+
json_start = -1
|
|
16
|
+
json_end = -1
|
|
17
|
+
brace_count = 0
|
|
18
|
+
in_string = False
|
|
19
|
+
escape_next = False
|
|
20
|
+
|
|
21
|
+
for i, char in enumerate(response):
|
|
22
|
+
if escape_next:
|
|
23
|
+
escape_next = False
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
if char == "\\":
|
|
27
|
+
escape_next = True
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
if char == '"' and not escape_next:
|
|
31
|
+
in_string = not in_string
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
if not in_string:
|
|
35
|
+
if char == "{":
|
|
36
|
+
if brace_count == 0:
|
|
37
|
+
json_start = i
|
|
38
|
+
brace_count += 1
|
|
39
|
+
elif char == "}":
|
|
40
|
+
brace_count -= 1
|
|
41
|
+
if brace_count == 0 and json_start != -1:
|
|
42
|
+
json_end = i + 1
|
|
43
|
+
break
|
|
44
|
+
|
|
45
|
+
if json_start == -1 or json_end == -1:
|
|
46
|
+
return "", ["No valid JSON object found"]
|
|
47
|
+
|
|
48
|
+
# Check for extra text
|
|
49
|
+
before_text = response[:json_start].strip()
|
|
50
|
+
after_text = response[json_end:].strip()
|
|
51
|
+
|
|
52
|
+
if before_text:
|
|
53
|
+
warnings.append("Extra text detected before JSON object")
|
|
54
|
+
if after_text:
|
|
55
|
+
warnings.append("Extra text detected after JSON object")
|
|
56
|
+
|
|
57
|
+
return response[json_start:json_end], warnings
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _check_json(text: str) -> str:
|
|
61
|
+
"""Classify a text string's JSON status.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
"clean_json" – text is valid JSON as-is
|
|
65
|
+
"json_with_extra_text" – valid JSON exists but is surrounded by other text
|
|
66
|
+
"no_json" – no valid JSON found
|
|
67
|
+
"""
|
|
68
|
+
if not text or not text.strip():
|
|
69
|
+
return "no_json"
|
|
70
|
+
|
|
71
|
+
stripped = text.strip()
|
|
72
|
+
|
|
73
|
+
json_content, extra_text_warnings = _extract_json_content(stripped)
|
|
74
|
+
if not json_content:
|
|
75
|
+
return "no_json"
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
_ = json.loads(json_content)
|
|
79
|
+
except (json.JSONDecodeError, ValueError):
|
|
80
|
+
return "no_json"
|
|
81
|
+
|
|
82
|
+
if extra_text_warnings:
|
|
83
|
+
return "json_with_extra_text"
|
|
84
|
+
|
|
85
|
+
return "clean_json"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@ANALYZERS.register()
|
|
89
|
+
class JSONErrorAnalyzer(BaseAnalyzer):
|
|
90
|
+
"""Detect JSON formatting issues in assistant content (terminal_bench_2 only)."""
|
|
91
|
+
|
|
92
|
+
id = "JSONErrorAnalyzer"
|
|
93
|
+
description = "Detect JSON formatting issues in assistant content — missing JSON or JSON wrapped in extra text (terminal_bench_2 only)."
|
|
94
|
+
category = AnalyzerCategory.ERROR
|
|
95
|
+
datasets = ["terminal_bench_2", "terminal_bench_2_verified", "terminal_bench_2_1"]
|
|
96
|
+
data_requirements = ["$.trajectory.steps[*].assistant_content"]
|
|
97
|
+
conf = {"only_incorrect": False, "threshold": 0.0}
|
|
98
|
+
distribution_fields = {
|
|
99
|
+
"json_error_types": "value_counts",
|
|
100
|
+
"no_json_steps": "numeric_stats",
|
|
101
|
+
"json_with_extra_text_steps": "numeric_stats",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
|
|
105
|
+
if result is None or result.trajectory is None:
|
|
106
|
+
return AnalysisResult(
|
|
107
|
+
task_id=task.task_id,
|
|
108
|
+
is_badcase=None,
|
|
109
|
+
error="no trajectory available",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
steps = result.trajectory.steps
|
|
113
|
+
if not steps:
|
|
114
|
+
return AnalysisResult(
|
|
115
|
+
task_id=task.task_id,
|
|
116
|
+
is_badcase=None,
|
|
117
|
+
error="empty trajectory",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
total_llm_steps = 0
|
|
121
|
+
no_json_count = 0
|
|
122
|
+
extra_text_count = 0
|
|
123
|
+
error_types: list[str] = []
|
|
124
|
+
error_steps_detail: list[dict] = []
|
|
125
|
+
|
|
126
|
+
for step in steps:
|
|
127
|
+
ac = step.assistant_content
|
|
128
|
+
if ac is None:
|
|
129
|
+
continue
|
|
130
|
+
total_llm_steps += 1
|
|
131
|
+
|
|
132
|
+
content = getattr(ac, "content", "") or ""
|
|
133
|
+
if isinstance(content, list):
|
|
134
|
+
content = " ".join(item.get("text", "") if isinstance(item, dict) else str(item) for item in content)
|
|
135
|
+
|
|
136
|
+
status = _check_json(content)
|
|
137
|
+
|
|
138
|
+
if status == "no_json":
|
|
139
|
+
no_json_count += 1
|
|
140
|
+
error_types.append("no_json")
|
|
141
|
+
error_steps_detail.append({
|
|
142
|
+
"step_id": step.step_id,
|
|
143
|
+
"error_type": "no_json",
|
|
144
|
+
"content_snippet": content[:300],
|
|
145
|
+
})
|
|
146
|
+
elif status == "json_with_extra_text":
|
|
147
|
+
extra_text_count += 1
|
|
148
|
+
error_types.append("json_with_extra_text")
|
|
149
|
+
error_steps_detail.append({
|
|
150
|
+
"step_id": step.step_id,
|
|
151
|
+
"error_type": "json_with_extra_text",
|
|
152
|
+
"content_snippet": content[:300],
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
# Task-level score: average across steps (no_json=1, json_with_extra_text=0.5, clean=0)
|
|
156
|
+
if total_llm_steps > 0:
|
|
157
|
+
score = round((no_json_count * 1.0 + extra_text_count * 0.5) / total_llm_steps, 4)
|
|
158
|
+
else:
|
|
159
|
+
score = 0.0
|
|
160
|
+
|
|
161
|
+
is_badcase = score > 0
|
|
162
|
+
|
|
163
|
+
details: dict = {
|
|
164
|
+
"total_llm_steps": total_llm_steps,
|
|
165
|
+
"no_json_steps": no_json_count,
|
|
166
|
+
"json_with_extra_text_steps": extra_text_count,
|
|
167
|
+
"json_error_types": error_types,
|
|
168
|
+
}
|
|
169
|
+
if error_steps_detail:
|
|
170
|
+
details["error_steps_detail"] = error_steps_detail
|
|
171
|
+
|
|
172
|
+
return AnalysisResult(
|
|
173
|
+
task_id=task.task_id,
|
|
174
|
+
is_badcase=is_badcase,
|
|
175
|
+
score=score,
|
|
176
|
+
details=details,
|
|
177
|
+
)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from agentcompass.runtime import ANALYZERS, AnalysisResult, AnalyzerCategory, BaseAnalyzer, RunResult
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _find_keywords(text: str, keywords: list[str]) -> list[str]:
|
|
9
|
+
"""Return the subset of *keywords* found in *text* (case-insensitive)."""
|
|
10
|
+
if not text or not keywords:
|
|
11
|
+
return []
|
|
12
|
+
lower = text.lower()
|
|
13
|
+
return [kw for kw in keywords if kw.lower() in lower]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class KeywordAnalyzerBase(BaseAnalyzer):
|
|
17
|
+
"""Base class for keyword detection.
|
|
18
|
+
|
|
19
|
+
Subclasses set ``check_fields`` to specify which ``assistant_content``
|
|
20
|
+
fields to inspect. Keywords come from ``conf["keywords"]``.
|
|
21
|
+
|
|
22
|
+
keywords – list of keyword strings to search for (required)
|
|
23
|
+
only_incorrect – only analyse incorrect samples
|
|
24
|
+
threshold – overall badcase ratio threshold
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
description = "Base class for forbidden/sensitive keyword detection across assistant_content fields; subclasses choose which fields to inspect and which keywords to search for."
|
|
28
|
+
check_fields: list[str] = ["content", "reasoning_content"]
|
|
29
|
+
|
|
30
|
+
def _find_in_field(self, text: str) -> list[str]:
|
|
31
|
+
keywords = self.conf.get("keywords") or []
|
|
32
|
+
return _find_keywords(text, keywords)
|
|
33
|
+
|
|
34
|
+
async def analysis(self, task, prepared, result: RunResult, req, plan) -> AnalysisResult:
|
|
35
|
+
if result is None or result.trajectory is None:
|
|
36
|
+
return AnalysisResult(
|
|
37
|
+
task_id=task.task_id,
|
|
38
|
+
is_badcase=None,
|
|
39
|
+
error="no trajectory available",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
steps = result.trajectory.steps
|
|
43
|
+
if not steps:
|
|
44
|
+
return AnalysisResult(
|
|
45
|
+
task_id=task.task_id,
|
|
46
|
+
is_badcase=None,
|
|
47
|
+
error="empty trajectory",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
keywords = self.conf.get("keywords") or []
|
|
51
|
+
if not keywords:
|
|
52
|
+
return AnalysisResult(
|
|
53
|
+
task_id=task.task_id,
|
|
54
|
+
is_badcase=None,
|
|
55
|
+
error="no keywords configured",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
total_llm_steps = 0
|
|
59
|
+
matched_steps = 0
|
|
60
|
+
all_matched_keywords: list[str] = []
|
|
61
|
+
matched_steps_detail: list[dict] = []
|
|
62
|
+
|
|
63
|
+
for step in steps:
|
|
64
|
+
ac = step.assistant_content
|
|
65
|
+
if ac is None:
|
|
66
|
+
continue
|
|
67
|
+
total_llm_steps += 1
|
|
68
|
+
|
|
69
|
+
step_matched = False
|
|
70
|
+
step_fields: dict = {}
|
|
71
|
+
|
|
72
|
+
for field_name in self.check_fields:
|
|
73
|
+
text = getattr(ac, field_name, "") or ""
|
|
74
|
+
if isinstance(text, list):
|
|
75
|
+
text = " ".join(item.get("text", "") if isinstance(item, dict) else str(item) for item in text)
|
|
76
|
+
found = self._find_in_field(text)
|
|
77
|
+
if found:
|
|
78
|
+
step_matched = True
|
|
79
|
+
all_matched_keywords.extend(found)
|
|
80
|
+
step_fields[field_name] = found
|
|
81
|
+
|
|
82
|
+
if step_matched:
|
|
83
|
+
matched_steps += 1
|
|
84
|
+
matched_steps_detail.append({
|
|
85
|
+
"step_id": step.step_id,
|
|
86
|
+
"matched_fields": step_fields,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
is_badcase = matched_steps > 0
|
|
90
|
+
|
|
91
|
+
details: dict = {
|
|
92
|
+
"total_llm_steps": total_llm_steps,
|
|
93
|
+
"matched_steps": matched_steps,
|
|
94
|
+
"matched_keywords": all_matched_keywords,
|
|
95
|
+
}
|
|
96
|
+
if matched_steps_detail:
|
|
97
|
+
details["matched_steps_detail"] = matched_steps_detail
|
|
98
|
+
|
|
99
|
+
return AnalysisResult(
|
|
100
|
+
task_id=task.task_id,
|
|
101
|
+
is_badcase=is_badcase,
|
|
102
|
+
score=1 if is_badcase else 0,
|
|
103
|
+
details=details,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@ANALYZERS.register()
|
|
108
|
+
class ContentKeywordAnalyzer(KeywordAnalyzerBase):
|
|
109
|
+
"""Detect forbidden/sensitive keywords in assistant content."""
|
|
110
|
+
|
|
111
|
+
id = "ContentKeywordAnalyzer"
|
|
112
|
+
description = "Detect forbidden/sensitive raw tokens (e.g. leaked special tokens like <think>, <tool_call>) in assistant content."
|
|
113
|
+
category = AnalyzerCategory.BEHAVIOR
|
|
114
|
+
datasets = []
|
|
115
|
+
data_requirements = ["$.trajectory.steps[*].assistant_content"]
|
|
116
|
+
check_fields = ["content"]
|
|
117
|
+
conf: dict = {
|
|
118
|
+
"threshold":
|
|
119
|
+
0.0,
|
|
120
|
+
"only_incorrect":
|
|
121
|
+
False,
|
|
122
|
+
"keywords": [
|
|
123
|
+
"<think>", "</think>", "<tool>", "</tool>", "<tool_call>", "</tool_call>", "<|end|>", "<|start|>",
|
|
124
|
+
"<|channe|>", "<|message|>"
|
|
125
|
+
],
|
|
126
|
+
}
|
|
127
|
+
distribution_fields = {
|
|
128
|
+
"matched_steps": "numeric_stats",
|
|
129
|
+
"matched_keywords": "value_counts",
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@ANALYZERS.register()
|
|
134
|
+
class ReasoningKeywordAnalyzer(KeywordAnalyzerBase):
|
|
135
|
+
"""Detect forbidden/sensitive keywords in assistant reasoning_content."""
|
|
136
|
+
|
|
137
|
+
id = "ReasoningKeywordAnalyzer"
|
|
138
|
+
description = "Detect forbidden/sensitive raw tokens (e.g. leaked special tokens like <think>, <tool_call>) in assistant reasoning_content."
|
|
139
|
+
category = AnalyzerCategory.BEHAVIOR
|
|
140
|
+
datasets = []
|
|
141
|
+
data_requirements = ["$.trajectory.steps[*].assistant_content"]
|
|
142
|
+
check_fields = ["reasoning_content"]
|
|
143
|
+
conf: dict = {
|
|
144
|
+
"threshold":
|
|
145
|
+
0.0,
|
|
146
|
+
"only_incorrect":
|
|
147
|
+
False,
|
|
148
|
+
"keywords": [
|
|
149
|
+
"<think>", "</think>", "<tool>", "</tool>", "<tool_call>", "</tool_call>", "<|end|>", "<|start|>",
|
|
150
|
+
"<|channe|>", "<|message|>"
|
|
151
|
+
],
|
|
152
|
+
}
|
|
153
|
+
distribution_fields = {
|
|
154
|
+
"matched_steps": "numeric_stats",
|
|
155
|
+
"matched_keywords": "value_counts",
|
|
156
|
+
}
|