Keep K3 suite selection and report-schema scoring in bash, merge K3/vision dataset_args into dpv4 yamls, and pin EvalScope at 735d920ee911 with local patches. Co-authored-by: Cursor <cursoragent@cursor.com>
1189 lines
48 KiB
Python
1189 lines
48 KiB
Python
# Copyright (c) Alibaba, Inc. and its affiliates.
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
import zipfile
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
from unittest.mock import Mock, patch
|
|
|
|
from dotenv import dotenv_values, load_dotenv
|
|
|
|
load_dotenv('.env')
|
|
|
|
env = dotenv_values('.env')
|
|
|
|
import unittest
|
|
|
|
import numpy as np
|
|
|
|
from evalscope.api.agent import NativeAgentConfig
|
|
from evalscope.api.agent.mcp import MCPServerConfigStdio
|
|
from evalscope.api.benchmark.adapters.browsergym_adapter import BrowserGymStep
|
|
from evalscope.api.messages import ChatMessageAssistant
|
|
from evalscope.api.metric import SampleScore, Score
|
|
from evalscope.api.model import ModelOutput
|
|
from evalscope.api.model.model_output import ChatCompletionChoice
|
|
from evalscope.api.registry import get_benchmark
|
|
from evalscope.api.tool import ToolCall, ToolFunction
|
|
from evalscope.benchmarks.automation_bench.utils import (
|
|
_create_automation_bench_env,
|
|
_normalize_result,
|
|
ensure_automation_bench_runtime,
|
|
)
|
|
from evalscope.benchmarks.claw_eval.claw_eval_adapter import ClawEvalAdapter
|
|
from evalscope.benchmarks.claw_eval.utils import (
|
|
DEFAULT_CLAW_EVAL_SANDBOX_IMAGE,
|
|
ClawEvalAssets,
|
|
_prepare_official_repo,
|
|
ensure_claw_eval_sandbox_image,
|
|
load_claw_eval_trace,
|
|
materialize_task_root,
|
|
run_claw_eval_task,
|
|
validate_claw_eval_private_api,
|
|
)
|
|
from evalscope.benchmarks.toolathlon.toolathlon_adapter import ToolathlonAdapter
|
|
from evalscope.config import SandboxTaskConfig, TaskConfig
|
|
from evalscope.constants import EvalType, JudgeStrategy, OutputType
|
|
from evalscope.models.mockllm import MockLLM
|
|
from evalscope.run import run_task
|
|
from evalscope.utils.data_utils import get_model_prediction
|
|
from evalscope.utils.logger import get_logger
|
|
from tests.common import TestBenchmark
|
|
|
|
logger = get_logger()
|
|
|
|
|
|
def _fake_claw_eval_scoring_modules() -> dict[str, ModuleType]:
|
|
claw_eval = ModuleType('claw_eval')
|
|
claw_eval.__path__ = []
|
|
models = ModuleType('claw_eval.models')
|
|
scoring = ModuleType('claw_eval.models.scoring')
|
|
|
|
def compute_pass_at_k(scores, k):
|
|
return 1.0 if scores else 0.0
|
|
|
|
def compute_pass_hat_k(scores, k):
|
|
return 1.0 if all(score >= 0.8 for score in scores) else 0.25
|
|
|
|
scoring.compute_pass_at_k = compute_pass_at_k
|
|
scoring.compute_pass_hat_k = compute_pass_hat_k
|
|
return {
|
|
'claw_eval': claw_eval,
|
|
'claw_eval.models': models,
|
|
'claw_eval.models.scoring': scoring,
|
|
}
|
|
|
|
|
|
def _fake_claw_eval_cli_modules(run_single_task) -> dict[str, ModuleType]:
|
|
claw_eval = ModuleType('claw_eval')
|
|
claw_eval.__path__ = []
|
|
cli = ModuleType('claw_eval.cli')
|
|
cli._run_single_task = run_single_task
|
|
return {
|
|
'claw_eval': claw_eval,
|
|
'claw_eval.cli': cli,
|
|
}
|
|
|
|
|
|
class TestAgentBenchmark(TestBenchmark):
|
|
"""Agentic benchmark evaluation test cases."""
|
|
|
|
def setUp(self):
|
|
"""Setup common test configuration."""
|
|
self.base_config = {
|
|
'model': 'qwen3-max',
|
|
'api_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
'api_key': env.get('DASHSCOPE_API_KEY'),
|
|
'eval_type': EvalType.OPENAI_API,
|
|
'eval_batch_size': 5,
|
|
'limit': 5,
|
|
'generation_config': {
|
|
'temperature': 0.7,
|
|
'parallel_tool_calls': True,
|
|
'retries': 3,
|
|
'extra_body': {
|
|
'enable_thinking': True
|
|
},
|
|
'stream': True
|
|
},
|
|
'judge': {'strategy': JudgeStrategy.AUTO, 'models': {
|
|
'model_id': 'qwen3-max',
|
|
'api_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
'api_key': env.get('DASHSCOPE_API_KEY'),
|
|
'generation_config': {
|
|
'temperature': 0.0,
|
|
'extra_body': {
|
|
'enable_thinking': False
|
|
}
|
|
}
|
|
}},
|
|
'debug': True,
|
|
}
|
|
|
|
def test_browsecomp(self):
|
|
"""Test BrowseComp benchmark end-to-end."""
|
|
config_overrides = {
|
|
'collect_perf': False,
|
|
'debug': False,
|
|
'eval_batch_size': 1,
|
|
'limit': 1,
|
|
'no_timestamp': True,
|
|
'work_dir': 'outputs/test_agent_browsecomp',
|
|
}
|
|
if not env.get('DASHSCOPE_API_KEY'):
|
|
config_overrides['judge'] = {'strategy': JudgeStrategy.RULE}
|
|
|
|
self._run_dataset_test('browsecomp', **config_overrides)
|
|
|
|
review_files = list(Path('outputs/test_agent_browsecomp').glob('reviews/*/browsecomp_default.jsonl'))
|
|
self.assertEqual(len(review_files), 1)
|
|
review = json.loads(review_files[0].read_text(encoding='utf-8').strip())
|
|
self.assertNotIn('canary', review['sample_score']['sample_metadata'])
|
|
|
|
def test_deepsearchqa(self):
|
|
"""Test DeepSearchQA benchmark end-to-end."""
|
|
config_overrides = {
|
|
'collect_perf': False,
|
|
'debug': False,
|
|
'eval_batch_size': 1,
|
|
'judge': {'strategy': JudgeStrategy.RULE},
|
|
'limit': 1,
|
|
'no_timestamp': True,
|
|
'work_dir': 'outputs/test_agent_deepsearchqa',
|
|
}
|
|
|
|
self._run_dataset_test('deepsearchqa', use_mock=True, **config_overrides)
|
|
|
|
review_files = list(Path('outputs/test_agent_deepsearchqa').glob('reviews/*/deepsearchqa_default.jsonl'))
|
|
self.assertEqual(len(review_files), 1)
|
|
review = json.loads(review_files[0].read_text(encoding='utf-8').strip())
|
|
self.assertIn('answer_type', review['sample_score']['sample_metadata'])
|
|
|
|
def test_miniwob(self):
|
|
"""Run the BrowserGym agent, reward and reporting flow end to end."""
|
|
|
|
class FakeSession:
|
|
|
|
async def reset(self):
|
|
return BrowserGymStep(
|
|
observation={
|
|
'goal': 'Click OK',
|
|
'url': 'http://miniwob/click-dialog.html',
|
|
'axtree_txt': '[1] button "OK"',
|
|
'last_action_error': False,
|
|
'screenshot': np.zeros((8, 8, 3), dtype=np.uint8),
|
|
},
|
|
)
|
|
|
|
async def step(self, action):
|
|
assert action == 'click("1")'
|
|
return BrowserGymStep(
|
|
observation={
|
|
'goal': 'Click OK',
|
|
'url': 'http://miniwob/click-dialog.html',
|
|
'axtree_txt': '[1] button "OK"',
|
|
'last_action_error': False,
|
|
'screenshot': np.zeros((8, 8, 3), dtype=np.uint8),
|
|
},
|
|
reward=1.0,
|
|
done=True,
|
|
)
|
|
|
|
async def close(self):
|
|
return None
|
|
|
|
record = {
|
|
'task_name': 'miniwob.click-dialog',
|
|
'miniwob_category': 'test',
|
|
'comment': '',
|
|
'webgum_subset': 'False',
|
|
'similarity_group': '0',
|
|
'browsergym_split': 'test',
|
|
'task_id': 'miniwob.click-dialog',
|
|
'_episode_seeds': [28, 29, 30, 31, 32],
|
|
}
|
|
call = ToolCall(
|
|
id='browser-1',
|
|
function=ToolFunction(name='browser_action', arguments={'action': 'click("1")'}),
|
|
)
|
|
output = ModelOutput(
|
|
model='mock_llm',
|
|
choices=[
|
|
ChatCompletionChoice(
|
|
message=ChatMessageAssistant(content='', tool_calls=[call]),
|
|
stop_reason='tool_calls',
|
|
)
|
|
],
|
|
)
|
|
original_init = MockLLM.__init__
|
|
|
|
def patched_init(model_self, *args, **kwargs):
|
|
kwargs['custom_outputs'] = [output.model_copy(deep=True) for _ in range(5)]
|
|
original_init(model_self, *args, **kwargs)
|
|
|
|
with tempfile.TemporaryDirectory() as work_dir, \
|
|
patch('evalscope.api.benchmark.adapters.browsergym_adapter.check_import'), \
|
|
patch('evalscope.benchmarks.miniwob.miniwob_adapter.load_miniwob_records',
|
|
return_value=([record], Path('/cache/miniwob.csv'))), \
|
|
patch('evalscope.benchmarks.miniwob.miniwob_adapter.MiniWobAdapter.create_browsergym_session',
|
|
side_effect=lambda sample: FakeSession()), \
|
|
patch.object(MockLLM, '__init__', patched_init):
|
|
reports = run_task(
|
|
TaskConfig(
|
|
model='mock_llm',
|
|
datasets=['miniwob'],
|
|
eval_type='mock_llm',
|
|
repeats=5,
|
|
eval_batch_size=1,
|
|
work_dir=work_dir,
|
|
no_timestamp=True,
|
|
analysis_report=False,
|
|
agent_config=NativeAgentConfig(
|
|
strategy='function_calling',
|
|
max_steps=10,
|
|
),
|
|
)
|
|
)
|
|
|
|
self.assertEqual(reports['miniwob'].score, 1.0)
|
|
reviews = list(Path(work_dir).glob('reviews/**/*.jsonl'))
|
|
rows = [json.loads(line) for line in reviews[0].read_text(encoding='utf-8').splitlines()]
|
|
self.assertEqual(len(rows), 5)
|
|
self.assertEqual([row['sample_score']['sample_metadata']['seed'] for row in rows], [28, 29, 30, 31, 32])
|
|
self.assertTrue(all(row['agent_trace']['events'][0]['payload']['backend'] == 'browsergym' for row in rows))
|
|
|
|
prediction_rows = get_model_prediction(work_dir, 'mock_llm', 'miniwob', 'default')
|
|
browser_trace = prediction_rows.iloc[0]['AgentTrace']
|
|
self.assertEqual(browser_trace['environment'], 'browsergym')
|
|
self.assertEqual(browser_trace['events'][0]['type'], 'env_reset')
|
|
self.assertIn(
|
|
browser_trace['events'][0]['message_id'],
|
|
{message['id']
|
|
for message in prediction_rows.iloc[0]['Messages']}
|
|
)
|
|
submit_event = next(event for event in browser_trace['events'] if event['type'] == 'submit')
|
|
terminal_observation = next(
|
|
message for message in prediction_rows.iloc[0]['Messages']
|
|
if message['id'] == submit_event['message_id']
|
|
)
|
|
self.assertEqual(terminal_observation['tool_call_id'], ['browser-1'])
|
|
self.assertTrue(terminal_observation['metadata']['done'])
|
|
|
|
def test_swe_bench_verified_agentic(self):
|
|
"""Test SWE-bench-verified agentic dataset using docker environment."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'build_docker_images': True,
|
|
'pull_remote_images_if_available': True,
|
|
'force_arch': 'arm64',
|
|
}
|
|
}
|
|
self._run_dataset_test('swe_bench_verified_agentic', dataset_args, limit=1)
|
|
|
|
def test_swe_bench_verified_mini_agentic(self):
|
|
"""Test SWE-bench-verified-mini agentic dataset using docker environment."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'build_docker_images': True,
|
|
'pull_remote_images_if_available': True,
|
|
'force_arch': 'arm64',
|
|
}
|
|
}
|
|
self._run_dataset_test('swe_bench_verified_mini_agentic', dataset_args, limit=3)
|
|
|
|
def test_swe_bench_lite_agentic(self):
|
|
"""Test SWE-bench-lite agentic dataset using docker environment."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'build_docker_images': True,
|
|
'pull_remote_images_if_available': True,
|
|
'force_arch': 'arm64',
|
|
}
|
|
}
|
|
self._run_dataset_test('swe_bench_lite_agentic', dataset_args, limit=1)
|
|
|
|
def test_swe_bench_multilingual_agentic(self):
|
|
"""Test SWE-bench-multilingual agentic dataset using docker environment."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'build_docker_images': False,
|
|
'pull_remote_images_if_available': True,
|
|
}
|
|
}
|
|
self._run_dataset_test(
|
|
'swe_bench_multilingual_agentic',
|
|
dataset_args,
|
|
limit=1,
|
|
generation_config={
|
|
'temperature': 0.0,
|
|
'parallel_tool_calls': False,
|
|
'retries': 3,
|
|
'extra_body': {
|
|
'enable_thinking': True
|
|
},
|
|
'stream': True
|
|
},
|
|
)
|
|
|
|
def test_swe_bench_pro(self):
|
|
"""Test SWE-bench_Pro agentic dataset using docker environment."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'eval_timeout': 1800,
|
|
}
|
|
}
|
|
self._run_dataset_test(
|
|
'swe_bench_pro',
|
|
dataset_args,
|
|
limit=5,
|
|
use_cache='outputs/20260519_155200',
|
|
rerun_review=True,
|
|
sandbox=SandboxTaskConfig(
|
|
default_config={
|
|
'platform': 'linux/amd64',
|
|
'memory_limit': '12g',
|
|
'cpu_limit': 4.0
|
|
},
|
|
),
|
|
)
|
|
|
|
def test_gaia(self):
|
|
"""Test GAIA benchmark using docker environment with react + bash."""
|
|
dataset_args = {
|
|
'subset_list': ['2023_level1', '2023_level2', '2023_level3'],
|
|
}
|
|
self._run_dataset_test(
|
|
'gaia',
|
|
dataset_args,
|
|
limit=1,
|
|
sandbox=SandboxTaskConfig(default_config={
|
|
'image': 'python:3.11',
|
|
'network_enabled': True
|
|
}),
|
|
)
|
|
|
|
def test_gaia_with_mcp(self):
|
|
"""GAIA + MCP fetch server, exercising the host-side MCP plumbing.
|
|
|
|
Requires ``pip install mcp-server-fetch`` in the eval environment.
|
|
Using ``python -m mcp_server_fetch`` (rather than ``uvx``) keeps the
|
|
test deterministic — no per-run package fetch / venv creation.
|
|
"""
|
|
dataset_args = {
|
|
'subset_list': ['2023_level1'],
|
|
}
|
|
agent_config = NativeAgentConfig(
|
|
max_steps=30,
|
|
mcp_servers=[
|
|
MCPServerConfigStdio(
|
|
command=sys.executable,
|
|
# ``--ignore-robots-txt`` lets the server fetch sites whose
|
|
# robots.txt is unreachable (transient network failures /
|
|
# CDN-blocked UAs commonly seen during offline-ish CI runs).
|
|
args=['-m', 'mcp_server_fetch', '--ignore-robots-txt'],
|
|
name='fetch',
|
|
),
|
|
],
|
|
)
|
|
self._run_dataset_test(
|
|
'gaia',
|
|
dataset_args,
|
|
limit=1,
|
|
agent_config=agent_config,
|
|
sandbox=SandboxTaskConfig(default_config={
|
|
'image': 'python:3.11',
|
|
'network_enabled': True
|
|
}),
|
|
)
|
|
|
|
def test_researchrubrics(self):
|
|
"""Test ResearchRubrics with a real agent API and binary LLM judge."""
|
|
if not env.get('DASHSCOPE_API_KEY'):
|
|
self.skipTest('DASHSCOPE_API_KEY is required for the ResearchRubrics real-API smoke test.')
|
|
|
|
self._run_dataset_test(
|
|
'researchrubrics',
|
|
limit=5,
|
|
eval_batch_size=5,
|
|
collect_perf=False,
|
|
debug=False,
|
|
)
|
|
|
|
def test_job_bench(self):
|
|
"""Test JobBench end-to-end with Docker artifacts and LLM judging."""
|
|
if not env.get('DASHSCOPE_API_KEY'):
|
|
self.skipTest('DASHSCOPE_API_KEY is required for the JobBench end-to-end test.')
|
|
|
|
self._run_dataset_test(
|
|
'job_bench',
|
|
limit=5,
|
|
eval_batch_size=5,
|
|
collect_perf=False,
|
|
debug=False,
|
|
agent_config=NativeAgentConfig(environment='docker', max_steps=80),
|
|
sandbox=SandboxTaskConfig(default_config={
|
|
'image': 'python:3.11-slim-bookworm',
|
|
'network_enabled': True,
|
|
}),
|
|
)
|
|
|
|
def test_wide_search(self):
|
|
"""Test WideSearch with real qwen-plus, bash, Fetch MCP, and LLM judging."""
|
|
if not env.get('DASHSCOPE_API_KEY'):
|
|
self.skipTest('DASHSCOPE_API_KEY is required for the WideSearch real-API smoke test.')
|
|
|
|
self._run_dataset_test(
|
|
'wide_search',
|
|
model='qwen-plus',
|
|
limit=1,
|
|
eval_batch_size=1,
|
|
collect_perf=False,
|
|
debug=False,
|
|
judge={'models': {
|
|
'model_id': 'qwen-plus',
|
|
'api_url': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
'api_key': env.get('DASHSCOPE_API_KEY'),
|
|
'generation_config': {
|
|
'temperature': 0.0,
|
|
'extra_body': {
|
|
'enable_thinking': False
|
|
}
|
|
}
|
|
}},
|
|
agent_config=NativeAgentConfig(
|
|
mcp_servers=[
|
|
MCPServerConfigStdio(
|
|
command=sys.executable,
|
|
args=['-m', 'mcp_server_fetch', '--ignore-robots-txt'],
|
|
name='fetch',
|
|
)
|
|
],
|
|
),
|
|
)
|
|
|
|
def test_terminal_bench_v2_1(self):
|
|
"""Test Terminal-Bench v2.1 dataset."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'timeout_multiplier': 3,
|
|
'environment_kwargs': {
|
|
'override_cpus': 2
|
|
},
|
|
},
|
|
}
|
|
self._run_dataset_test('terminal_bench_v2_1', dataset_args, limit=3, eval_batch_size=3)
|
|
|
|
def test_toolathlon(self):
|
|
"""Test Toolathlon official-service wrapper with a mocked service client."""
|
|
|
|
class FakeToolathlonClient:
|
|
last_config = None
|
|
|
|
def __init__(self, config):
|
|
FakeToolathlonClient.last_config = config
|
|
|
|
def run_private(self):
|
|
return {
|
|
'job_id': self.last_config.job_id or 'fake-toolathlon-job',
|
|
'output_dir': str(self.last_config.output_dir),
|
|
'acc': 1.0,
|
|
'eval_stats': {
|
|
'passed': 1,
|
|
'total': 1
|
|
},
|
|
'task_results': [{
|
|
'task': 'find-alita-paper',
|
|
'pass': True
|
|
}],
|
|
}
|
|
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'task_list': ['find-alita-paper'],
|
|
'workers': 1,
|
|
'skip_container_restart': True,
|
|
'model_params': {
|
|
'temperature': 0.0
|
|
},
|
|
}
|
|
}
|
|
|
|
with patch.object(ToolathlonAdapter, 'client_cls', FakeToolathlonClient):
|
|
self._run_dataset_test(
|
|
'toolathlon',
|
|
dataset_args,
|
|
use_mock=True,
|
|
limit=1,
|
|
eval_batch_size=1,
|
|
no_timestamp=True,
|
|
work_dir='outputs/test_agent_toolathlon',
|
|
api_url='http://localhost:8000/v1',
|
|
api_key='local-key',
|
|
)
|
|
|
|
self.assertIsNotNone(FakeToolathlonClient.last_config)
|
|
self.assertEqual(FakeToolathlonClient.last_config.base_url, 'http://localhost:8000/v1')
|
|
self.assertEqual(FakeToolathlonClient.last_config.api_key, 'local-key')
|
|
self.assertEqual(FakeToolathlonClient.last_config.task_list, ['find-alita-paper'])
|
|
self.assertEqual(FakeToolathlonClient.last_config.model_params['temperature'], 0.0)
|
|
self.assertNotIn('retries', FakeToolathlonClient.last_config.model_params)
|
|
self.assertNotIn('batch_size', FakeToolathlonClient.last_config.model_params)
|
|
|
|
def test_claw_eval(self):
|
|
"""Test Claw-Eval task-level wrapper with mocked assets and private API runner."""
|
|
run_calls = []
|
|
|
|
def fake_run_claw_eval_task(**kwargs):
|
|
run_calls.append(kwargs)
|
|
self.assertTrue((kwargs['task_dir'] / 'task.yaml').is_file())
|
|
self.assertEqual(kwargs['task_dir'].name, 'T002_task')
|
|
self.assertTrue(kwargs['config_path'].is_file())
|
|
return {
|
|
'task_id': 'T002_task',
|
|
'task_name': 'Task 2',
|
|
'difficulty': 'easy',
|
|
'trace_root': str(kwargs['trace_root']),
|
|
'trace_path': str(kwargs['trace_root'] / f'T002_task_{len(run_calls)}.jsonl'),
|
|
'raw_result': {
|
|
'task_id': 'T002_task'
|
|
},
|
|
'trials': [],
|
|
'metrics': {
|
|
'avg_score': 0.8,
|
|
'task_score': 0.8,
|
|
'passed': 1.0,
|
|
'error_rate': 0.0,
|
|
'tokens': 10,
|
|
'model_input_tokens': 8,
|
|
'model_output_tokens': 2,
|
|
'wall_time_s': 1.5,
|
|
'model_time_s': 1.0,
|
|
'tool_time_s': 0.2,
|
|
'completion': 0.8,
|
|
'robustness': 0.8,
|
|
'communication': 0.0,
|
|
'safety': 1.0,
|
|
},
|
|
}
|
|
|
|
def fake_manifest_loader(dataset_id, data_source, splits, force_redownload=False):
|
|
self.assertEqual(dataset_id, 'claw-eval/Claw-Eval')
|
|
self.assertEqual(splits, ['general'])
|
|
return [
|
|
{
|
|
'task_id': 'T001_task',
|
|
'split': 'general'
|
|
},
|
|
{
|
|
'task_id': 'T002_task',
|
|
'split': 'general'
|
|
},
|
|
]
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
repo_root = Path(tmp) / 'repo'
|
|
tasks_dir = repo_root / 'tasks'
|
|
(tasks_dir / 'T001_task').mkdir(parents=True)
|
|
(tasks_dir / 'T002_task').mkdir(parents=True)
|
|
(tasks_dir / 'T001_task' / 'task.yaml').write_text('id: T001_task\n', encoding='utf-8')
|
|
(tasks_dir / 'T002_task' / 'task.yaml').write_text('id: T002_task\n', encoding='utf-8')
|
|
fixtures_archive = Path(tmp) / 'fixtures.tar.gz'
|
|
fixtures_archive.write_bytes(b'fixture')
|
|
fixtures_dir = Path(tmp) / 'fixtures'
|
|
fixtures_dir.mkdir()
|
|
|
|
def fake_assets_preparer(**kwargs):
|
|
self.assertNotIn('download_fixtures', kwargs)
|
|
return ClawEvalAssets(
|
|
repo_root=repo_root,
|
|
tasks_dir=tasks_dir,
|
|
fixtures_archive=fixtures_archive,
|
|
fixtures_dir=fixtures_dir,
|
|
)
|
|
|
|
prepared_images = []
|
|
|
|
def fake_image_preparer(repo_root_arg):
|
|
prepared_images.append(repo_root_arg)
|
|
return DEFAULT_CLAW_EVAL_SANDBOX_IMAGE
|
|
|
|
dataset_args = {
|
|
'subset_list': ['general'],
|
|
'extra_params': {
|
|
'task_ids': ['T002_task'],
|
|
}
|
|
}
|
|
|
|
with patch('evalscope.benchmarks.claw_eval.claw_eval_adapter.run_claw_eval_task',
|
|
side_effect=fake_run_claw_eval_task), \
|
|
patch('evalscope.benchmarks.claw_eval.claw_eval_adapter.load_task_manifest',
|
|
side_effect=fake_manifest_loader), \
|
|
patch('evalscope.benchmarks.claw_eval.claw_eval_adapter.prepare_claw_eval_assets',
|
|
side_effect=fake_assets_preparer), \
|
|
patch('evalscope.benchmarks.claw_eval.claw_eval_adapter.ensure_claw_eval_sandbox_image',
|
|
side_effect=fake_image_preparer), \
|
|
patch.dict(sys.modules, _fake_claw_eval_scoring_modules()), \
|
|
patch.object(ClawEvalAdapter, '_check_runtime', return_value=None):
|
|
self._run_dataset_test(
|
|
'claw_eval',
|
|
dataset_args,
|
|
use_mock=True,
|
|
limit=1,
|
|
eval_batch_size=1,
|
|
no_timestamp=True,
|
|
work_dir='outputs/test_agent_claw_eval',
|
|
api_url='http://localhost:8000/v1',
|
|
api_key='local-key',
|
|
repeats=3,
|
|
)
|
|
|
|
self.assertEqual(len(run_calls), 3)
|
|
self.assertEqual(run_calls[0]['model_id'], 'qwen3-max')
|
|
self.assertEqual(run_calls[0]['api_key'], 'local-key')
|
|
self.assertEqual(run_calls[0]['base_url'], 'http://localhost:8000/v1')
|
|
self.assertEqual([call['port_offset'] for call in run_calls], [0, 0, 0])
|
|
self.assertTrue(all(str(call['repo_root']).endswith('/repo') for call in run_calls))
|
|
self.assertEqual(prepared_images, [repo_root])
|
|
review_files = list(Path('outputs/test_agent_claw_eval').glob('reviews/*/claw_eval_general.jsonl'))
|
|
self.assertEqual(len(review_files), 1)
|
|
reviews = [json.loads(line) for line in review_files[0].read_text(encoding='utf-8').splitlines() if line]
|
|
self.assertEqual(len(reviews), 3)
|
|
review = reviews[0]
|
|
metadata = review['sample_score']['sample_metadata']
|
|
self.assertEqual(metadata['split'], 'general')
|
|
self.assertEqual(metadata['task_id'], 'T002_task')
|
|
self.assertEqual({row['sample_score']['group_id'] for row in reviews}, {0})
|
|
self.assertNotIn('selected_task_ids', metadata)
|
|
self.assertNotIn('selected_records', metadata)
|
|
|
|
def test_automation_bench(self):
|
|
"""Test AutomationBench's official task wrapper with a mocked in-process runner."""
|
|
run_calls = []
|
|
|
|
def fake_record_loader(domains):
|
|
self.assertEqual(domains, ['sales'])
|
|
return {
|
|
'sales': [{
|
|
'example_id': 501,
|
|
'task': 'sales.update_contact',
|
|
'prompt': [
|
|
{
|
|
'role': 'system',
|
|
'content': 'Use the available business APIs.'
|
|
},
|
|
{
|
|
'role': 'user',
|
|
'content': 'Update the contact phone number.'
|
|
},
|
|
],
|
|
'answer': '',
|
|
'info': '{}',
|
|
}]
|
|
}
|
|
|
|
def fake_task_runner(**kwargs):
|
|
run_calls.append(kwargs)
|
|
return {
|
|
'task': 'sales.update_contact',
|
|
'reward': 0.5,
|
|
'metrics': {
|
|
'partial_credit': 0.5,
|
|
'task_completed_correctly': 0.0,
|
|
},
|
|
'messages': [
|
|
{
|
|
'role': 'system',
|
|
'content': 'Use the available business APIs.'
|
|
},
|
|
{
|
|
'role': 'user',
|
|
'content': 'Update the contact phone number.'
|
|
},
|
|
{
|
|
'role': 'assistant',
|
|
'content': None,
|
|
'tool_calls': [
|
|
'{"id":"call-1","name":"salesforce_update_contact",'
|
|
'"arguments":"{\\"contact_id\\":\\"123\\"}"}'
|
|
],
|
|
},
|
|
{
|
|
'role': 'tool',
|
|
'tool_call_id': 'call-1',
|
|
'content': '{"ok": true}'
|
|
},
|
|
{
|
|
'role': 'assistant',
|
|
'content': 'Done.'
|
|
},
|
|
],
|
|
'usage': {
|
|
'input_tokens': 20,
|
|
'output_tokens': 5
|
|
},
|
|
'debug': {},
|
|
'assertion_results': [{
|
|
'type': 'contact_phone_equals',
|
|
'passed': True,
|
|
'excluded': False
|
|
}],
|
|
'end_state': {
|
|
'salesforce': {}
|
|
},
|
|
'perf': {
|
|
'tool_calls': 1
|
|
},
|
|
'error': None,
|
|
}
|
|
|
|
dataset_args = {
|
|
'subset_list': ['sales'],
|
|
'extra_params': {
|
|
'toolset': 'api',
|
|
},
|
|
}
|
|
with patch('evalscope.benchmarks.automation_bench.automation_bench_adapter.ensure_automation_bench_runtime'), \
|
|
patch('evalscope.benchmarks.automation_bench.automation_bench_adapter.load_automation_bench_records',
|
|
side_effect=fake_record_loader), \
|
|
patch('evalscope.benchmarks.automation_bench.automation_bench_adapter.run_automation_bench_task',
|
|
side_effect=fake_task_runner):
|
|
self._run_dataset_test(
|
|
'automation_bench',
|
|
dataset_args,
|
|
use_mock=True,
|
|
limit=1,
|
|
repeats=2,
|
|
eval_batch_size=1,
|
|
no_timestamp=True,
|
|
work_dir='outputs/test_agent_automation_bench',
|
|
api_url='http://localhost:8000/v1',
|
|
api_key='local-key',
|
|
agent_config=NativeAgentConfig(max_steps=7),
|
|
generation_config={
|
|
'temperature': 0.2,
|
|
'reasoning_effort': 'low',
|
|
'extra_body': {
|
|
'enable_thinking': True
|
|
},
|
|
'extra_headers': {
|
|
'X-Test': 'value'
|
|
},
|
|
},
|
|
)
|
|
|
|
self.assertEqual(len(run_calls), 2)
|
|
self.assertTrue(all(call['model_name'] == 'qwen3-max' for call in run_calls))
|
|
self.assertTrue(all(call['api'] == 'chat_completions' for call in run_calls))
|
|
self.assertTrue(all(call['toolset'] == 'api' for call in run_calls))
|
|
self.assertTrue(all(call['max_turns'] == 7 for call in run_calls))
|
|
self.assertTrue(all(call['extra_headers'] == {'X-Test': 'value'} for call in run_calls))
|
|
self.assertTrue(
|
|
all(
|
|
call['sampling_args'] == {
|
|
'temperature': 0.2,
|
|
'reasoning_effort': 'low',
|
|
'extra_body': {
|
|
'enable_thinking': True
|
|
},
|
|
} for call in run_calls
|
|
)
|
|
)
|
|
|
|
review_files = list(Path('outputs/test_agent_automation_bench').glob('reviews/*/automation_bench_sales.jsonl'))
|
|
self.assertEqual(len(review_files), 1)
|
|
reviews = [json.loads(line) for line in review_files[0].read_text().splitlines() if line]
|
|
self.assertEqual(len(reviews), 2)
|
|
review = reviews[0]
|
|
self.assertEqual(review['sample_score']['score']['main_score_name'], 'pass_rate')
|
|
self.assertEqual(review['sample_score']['score']['value']['pass_rate'], 0.0)
|
|
self.assertEqual(review['sample_score']['score']['value']['partial_credit'], 0.5)
|
|
self.assertEqual(review['sample_score']['sample_metadata']['domain'], 'sales')
|
|
self.assertNotIn('automation_bench_result', review['sample_score']['sample_metadata'])
|
|
prediction_file = next(
|
|
Path('outputs/test_agent_automation_bench').glob('predictions/*/automation_bench_sales.jsonl')
|
|
)
|
|
prediction = json.loads(prediction_file.read_text().splitlines()[-1])
|
|
self.assertNotIn('automation_bench_result', prediction['metadata'])
|
|
self.assertNotIn('messages', prediction['model_output']['metadata'])
|
|
assistant = next(message for message in prediction['messages'] if message['role'] == 'assistant')
|
|
self.assertEqual(assistant['tool_calls'][0]['function']['name'], 'salesforce_update_contact')
|
|
|
|
def test_automation_bench_requires_python_313(self):
|
|
"""Test AutomationBench rejects unsupported Python interpreters before import."""
|
|
with patch('evalscope.benchmarks.automation_bench.utils.sys.version_info', (3, 12, 0)):
|
|
with self.assertRaisesRegex(RuntimeError, 'Python 3.13'):
|
|
ensure_automation_bench_runtime()
|
|
|
|
def test_automation_bench_missing_package_has_install_hint(self):
|
|
"""Test a missing official package reports the pinned installation command."""
|
|
with patch('evalscope.benchmarks.automation_bench.utils.sys.version_info', (3, 13, 0)), \
|
|
patch('evalscope.benchmarks.automation_bench.utils.importlib.import_module',
|
|
side_effect=ImportError('missing')):
|
|
with self.assertRaisesRegex(ImportError, 'python -m pip install'):
|
|
ensure_automation_bench_runtime()
|
|
|
|
def test_automation_bench_simple_aggregate_is_separate(self):
|
|
"""Test the simple baseline does not contribute to the public pass-rate metric."""
|
|
with patch('evalscope.benchmarks.automation_bench.automation_bench_adapter.ensure_automation_bench_runtime'):
|
|
adapter = get_benchmark(
|
|
'automation_bench',
|
|
TaskConfig(
|
|
datasets=['automation_bench'],
|
|
dataset_args={'automation_bench': {
|
|
'subset_list': ['simple']
|
|
}},
|
|
),
|
|
)
|
|
|
|
def simple_score(completed, partial_credit, error=None):
|
|
task_state = Mock(
|
|
metadata={'domain': 'simple'},
|
|
output=Mock(
|
|
metadata={
|
|
'metrics': {
|
|
'task_completed_correctly': completed,
|
|
'partial_credit': partial_credit,
|
|
},
|
|
'error': error,
|
|
}
|
|
),
|
|
)
|
|
return adapter.match_score('', '', '', task_state)
|
|
|
|
scores = [
|
|
SampleScore(
|
|
score=simple_score(1.0, 1.0),
|
|
sample_id=0,
|
|
sample_metadata={'domain': 'simple'},
|
|
),
|
|
SampleScore(
|
|
score=simple_score(0.0, 0.5, 'failed'),
|
|
sample_id=1,
|
|
sample_metadata={'domain': 'simple'},
|
|
),
|
|
]
|
|
|
|
aggregated = {score.metric_name: score.score for score in adapter.aggregate_scores(scores)}
|
|
self.assertEqual(aggregated['simple_pass_rate'], 0.5)
|
|
self.assertEqual(aggregated['simple_partial_credit'], 0.75)
|
|
self.assertEqual(aggregated['simple_error_rate'], 0.5)
|
|
self.assertNotIn('pass_rate', aggregated)
|
|
|
|
def test_automation_bench_environment_init_has_no_process_hooks(self):
|
|
"""Test the EvalScope environment discovers handlers without registering process hooks."""
|
|
|
|
class FakeOfficialEnvironment:
|
|
|
|
def __init__(self, **kwargs):
|
|
self.kwargs = kwargs
|
|
self.__post_init__()
|
|
|
|
handlers = {
|
|
'stop': ['stop'],
|
|
'cleanup': ['cleanup'],
|
|
'teardown': ['teardown'],
|
|
}
|
|
discover_decorated = Mock(side_effect=lambda _, kind: handlers[kind])
|
|
with patch('signal.signal') as signal_register, patch('atexit.register') as atexit_register:
|
|
env = _create_automation_bench_env(
|
|
environment_cls=FakeOfficialEnvironment,
|
|
discover_decorated=discover_decorated,
|
|
dataset='dataset',
|
|
)
|
|
|
|
self.assertEqual(env._stop_conditions, ['stop'])
|
|
self.assertEqual(env._cleanup_handlers, ['cleanup'])
|
|
self.assertEqual(env._teardown_handlers, ['teardown'])
|
|
self.assertEqual(env.kwargs, {'dataset': 'dataset'})
|
|
signal_register.assert_not_called()
|
|
atexit_register.assert_not_called()
|
|
|
|
def test_automation_bench_normalization_uses_official_result_fields(self):
|
|
"""Test reward and debug diagnostics do not override official metrics or error state."""
|
|
result = _normalize_result({
|
|
'task': 'sales.update_contact',
|
|
'reward': 1.0,
|
|
'metrics': {
|
|
'partial_credit': 0.25,
|
|
},
|
|
'_debug': {
|
|
'errors': ['diagnostic only']
|
|
},
|
|
})
|
|
|
|
self.assertEqual(result['metrics']['partial_credit'], 0.25)
|
|
self.assertEqual(result['metrics']['task_completed_correctly'], 0.0)
|
|
self.assertIsNone(result['error'])
|
|
self.assertEqual(result['debug']['errors'], ['diagnostic only'])
|
|
|
|
def test_claw_eval_init_checks_runtime(self):
|
|
"""Test Claw-Eval fails early when the official package is unavailable."""
|
|
with patch.object(ClawEvalAdapter, '_check_runtime') as check:
|
|
adapter = get_benchmark('claw_eval', TaskConfig(datasets=['claw_eval']))
|
|
|
|
self.assertIsInstance(adapter, ClawEvalAdapter)
|
|
check.assert_called_once_with()
|
|
|
|
def test_claw_eval_force_redownload_replaces_cached_archive(self):
|
|
"""Test force_redownload reaches the shared downloader."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
cache_root = Path(tmp)
|
|
archive_path = cache_root / 'claw_eval_official.zip'
|
|
archive_path.write_bytes(b'stale archive')
|
|
|
|
def fake_download(url, save_path, **kwargs):
|
|
with zipfile.ZipFile(save_path, 'w') as archive:
|
|
archive.writestr('claw-eval/tasks/T001_task/task.yaml', 'id: T001_task\n')
|
|
|
|
with patch('evalscope.benchmarks.claw_eval.utils.download_url', side_effect=fake_download) as download:
|
|
repo_root = _prepare_official_repo(
|
|
cache_root=cache_root,
|
|
force_redownload=True,
|
|
official_repo_path=None,
|
|
)
|
|
|
|
self.assertTrue((repo_root / 'tasks' / 'T001_task' / 'task.yaml').is_file())
|
|
self.assertTrue(download.call_args.kwargs['force'])
|
|
|
|
def test_claw_eval_runner_uses_official_sandbox(self):
|
|
"""Test the runner always invokes the official private API sandbox path."""
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
trace_root = root / 'traces'
|
|
config_path = root / 'config.yaml'
|
|
config_path.write_text('sandbox:\n enabled: true\n', encoding='utf-8')
|
|
task_dir = root / 'tasks' / 'T001_task'
|
|
task_dir.mkdir(parents=True)
|
|
(task_dir / 'task.yaml').write_text('id: T001_task\n', encoding='utf-8')
|
|
run = Mock(
|
|
return_value={
|
|
'task_id': 'T001_task',
|
|
'task_name': 'Task 1',
|
|
'difficulty': 'easy',
|
|
'trials': [{
|
|
'trace': str(trace_root / 'T001_task_abc.jsonl'),
|
|
'task_score': 1.0,
|
|
'passed': True,
|
|
}],
|
|
'error': None,
|
|
}
|
|
)
|
|
with patch('evalscope.benchmarks.claw_eval.utils.validate_claw_eval_private_api', return_value=None), \
|
|
patch.dict(sys.modules, _fake_claw_eval_cli_modules(run)):
|
|
parsed = run_claw_eval_task(
|
|
task_dir=task_dir,
|
|
trace_root=trace_root,
|
|
config_path=config_path,
|
|
repo_root=root,
|
|
model_id='qwen3-max',
|
|
api_key='local-key',
|
|
base_url='http://localhost:8000/v1',
|
|
port_offset=50,
|
|
)
|
|
|
|
kwargs = run.call_args.kwargs
|
|
self.assertEqual(kwargs['task_dir'], str(task_dir.resolve()))
|
|
self.assertEqual(kwargs['trials'], 1)
|
|
self.assertEqual(kwargs['port_offset'], 50)
|
|
self.assertTrue(kwargs['sandbox'])
|
|
self.assertFalse(kwargs['sandbox_tools'])
|
|
self.assertIsNone(kwargs['sandbox_image'])
|
|
self.assertEqual(kwargs['api_key'], 'local-key')
|
|
self.assertEqual(parsed['metrics']['avg_score'], 1.0)
|
|
|
|
def test_claw_eval_private_api_signature_guard(self):
|
|
"""Test incompatible Claw-Eval private API signatures fail fast."""
|
|
|
|
def incompatible_run_single_task(task_dir):
|
|
return {}
|
|
|
|
with patch.dict(sys.modules, _fake_claw_eval_cli_modules(incompatible_run_single_task)):
|
|
with self.assertRaisesRegex(RuntimeError, 'private API is incompatible'):
|
|
validate_claw_eval_private_api()
|
|
|
|
def test_claw_eval_aggregate_scores_groups_repeats(self):
|
|
"""Test grouped Claw-Eval repeat aggregation."""
|
|
adapter = object.__new__(ClawEvalAdapter)
|
|
scores = [
|
|
SampleScore(score=Score(value={'judge_score': 1.0}, metadata={}), sample_id=0, group_id=0),
|
|
SampleScore(score=Score(value={'judge_score': 0.0}, metadata={}), sample_id=1, group_id=0),
|
|
SampleScore(score=Score(value={'judge_score': 0.5}, metadata={'error': 'failed'}), sample_id=2, group_id=1),
|
|
SampleScore(score=Score(value={'judge_score': 1.0}, metadata={}), sample_id=3, group_id=1),
|
|
]
|
|
|
|
with patch.dict(sys.modules, _fake_claw_eval_scoring_modules()):
|
|
aggregated = {(score.metric_name, score.aggregation): score.score
|
|
for score in adapter.aggregate_scores(scores)}
|
|
|
|
self.assertAlmostEqual(aggregated[('judge_score', 'mean')], 0.625)
|
|
self.assertAlmostEqual(aggregated[('pass_at_k', 'mean')], 1.0)
|
|
self.assertAlmostEqual(aggregated[('pass_hat_k', 'mean')], 0.25)
|
|
self.assertAlmostEqual(aggregated[('error_rate', 'mean')], 0.25)
|
|
|
|
def test_claw_eval_trace_converts_to_agent_trace(self):
|
|
"""Test official Claw-Eval JSONL traces are exposed as EvalScope agent traces."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
trace_path = Path(tmp) / 'trace.jsonl'
|
|
rows = [
|
|
{
|
|
'type': 'trace_start',
|
|
'trace_id': 'trace-1',
|
|
'timestamp': '2026-07-14T00:00:00+00:00',
|
|
},
|
|
{
|
|
'type': 'message',
|
|
'message': {
|
|
'role': 'user',
|
|
'content': [{
|
|
'type': 'text',
|
|
'text': 'Sort my inbox.'
|
|
}],
|
|
},
|
|
'usage': {
|
|
'input_tokens': 0,
|
|
'output_tokens': 0
|
|
},
|
|
'timestamp': '2026-07-14T00:00:00+00:00',
|
|
},
|
|
{
|
|
'type': 'message',
|
|
'message': {
|
|
'role': 'assistant',
|
|
'content': [
|
|
{
|
|
'type': 'text',
|
|
'text': 'Checking email.'
|
|
},
|
|
{
|
|
'type': 'tool_use',
|
|
'id': 'call-1',
|
|
'name': 'gmail_list_messages',
|
|
'input': {
|
|
'max_results': 5
|
|
},
|
|
},
|
|
],
|
|
},
|
|
'usage': {
|
|
'input_tokens': 10,
|
|
'output_tokens': 3
|
|
},
|
|
'timestamp': '2026-07-14T00:00:01+00:00',
|
|
},
|
|
{
|
|
'type': 'tool_dispatch',
|
|
'tool_use_id': 'call-1',
|
|
'tool_name': 'gmail_list_messages',
|
|
'request_body': {
|
|
'max_results': 5
|
|
},
|
|
'response_status': 200,
|
|
'latency_ms': 2.5,
|
|
'timestamp': '2026-07-14T00:00:01+00:00',
|
|
},
|
|
{
|
|
'type': 'message',
|
|
'message': {
|
|
'role': 'user',
|
|
'content': [{
|
|
'type': 'tool_result',
|
|
'tool_use_id': 'call-1',
|
|
'content': [{
|
|
'type': 'text',
|
|
'text': '{"messages": []}'
|
|
}],
|
|
'is_error': False,
|
|
}],
|
|
},
|
|
'timestamp': '2026-07-14T00:00:02+00:00',
|
|
},
|
|
{
|
|
'type': 'trace_end',
|
|
'trace_id': 'trace-1',
|
|
'input_tokens': 10,
|
|
'output_tokens': 3,
|
|
'total_tokens': 13,
|
|
'wall_time_s': 2.0,
|
|
'timestamp': '2026-07-14T00:00:03+00:00',
|
|
},
|
|
]
|
|
trace_path.write_text('\n'.join(json.dumps(row) for row in rows), encoding='utf-8')
|
|
|
|
trace, messages = load_claw_eval_trace(str(trace_path))
|
|
|
|
self.assertIsNotNone(trace)
|
|
self.assertIsNotNone(messages)
|
|
self.assertEqual(trace.framework, 'claw-eval')
|
|
self.assertEqual(trace.trial_id, 'trace-1')
|
|
self.assertEqual(trace.total_usage.total_tokens, 13)
|
|
self.assertEqual([message.role for message in messages], ['user', 'assistant', 'tool'])
|
|
self.assertEqual(messages[1].tool_calls[0].function.name, 'gmail_list_messages')
|
|
event_types = {event.type.value for event in trace.events}
|
|
self.assertIn('env_exec', event_types)
|
|
self.assertIn('tool_result', event_types)
|
|
|
|
def test_claw_eval_image_auto_build(self):
|
|
"""Test missing official sandbox image is built from the official repo."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
repo_root = Path(tmp)
|
|
(repo_root / 'Dockerfile.agent').write_text('FROM scratch\n', encoding='utf-8')
|
|
with patch('evalscope.benchmarks.claw_eval.utils.should_build_docker_image', return_value=True), \
|
|
patch('evalscope.benchmarks.claw_eval.utils.build_docker_image') as build:
|
|
image = ensure_claw_eval_sandbox_image(repo_root)
|
|
|
|
self.assertEqual(image, DEFAULT_CLAW_EVAL_SANDBOX_IMAGE)
|
|
build.assert_called_once_with(
|
|
image=DEFAULT_CLAW_EVAL_SANDBOX_IMAGE,
|
|
path=str(repo_root),
|
|
dockerfile='Dockerfile.agent',
|
|
)
|
|
|
|
def test_claw_eval_task_root_keeps_fixture_cross_refs(self):
|
|
"""Test selected task roots keep non-selected fixture-only cross refs."""
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
source_tasks = Path(tmp) / 'source' / 'tasks'
|
|
source_mock_services = source_tasks.parent / 'mock_services'
|
|
selected_task = source_tasks / 'T001_task'
|
|
fixture_only_task = source_tasks / 'T999_fixture_source'
|
|
selected_task.mkdir(parents=True)
|
|
source_mock_services.mkdir(parents=True)
|
|
(selected_task / 'task.yaml').write_text('id: T001_task\n', encoding='utf-8')
|
|
(source_mock_services / 'server.py').write_text('print("ok")\n', encoding='utf-8')
|
|
(fixture_only_task / 'fixtures').mkdir(parents=True)
|
|
(fixture_only_task / 'fixtures' / 'data.json').write_text('{}\n', encoding='utf-8')
|
|
|
|
runtime_tasks = materialize_task_root(
|
|
source_tasks_dir=source_tasks,
|
|
selected_task_ids=['T001_task'],
|
|
output_root=Path(tmp) / 'runtime',
|
|
)
|
|
|
|
self.assertTrue((runtime_tasks / 'T001_task' / 'task.yaml').is_file())
|
|
self.assertTrue((Path(tmp) / 'runtime' / 'mock_services' / 'server.py').is_file())
|
|
self.assertTrue((runtime_tasks / 'T999_fixture_source' / 'fixtures' / 'data.json').is_file())
|
|
self.assertFalse((runtime_tasks / 'T999_fixture_source' / 'task.yaml').exists())
|
|
|
|
def test_swe_bench_verified_agentic_backticks(self):
|
|
"""Test SWE-bench-verified agentic dataset with backticks protocol."""
|
|
dataset_args = {
|
|
'extra_params': {
|
|
'build_docker_images': True,
|
|
'pull_remote_images_if_available': True,
|
|
'force_arch': 'arm64',
|
|
}
|
|
}
|
|
self._run_dataset_test(
|
|
'swe_bench_verified_agentic',
|
|
dataset_args,
|
|
limit=1,
|
|
agent_config=NativeAgentConfig(strategy='swe_bench_backticks'),
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Run specific test: python -m unittest test_agent.TestAgentBenchmark.test_swe_bench_verified_agentic
|
|
# Run all tests: python -m unittest test_agent.TestAgentBenchmark
|
|
unittest.main()
|