from __future__ import annotations
from dataclasses import dataclass, replace
from agentcompass.benchmarks.config import RuntimeBenchmarkConfig
from agentcompass.runtime import (
BENCHMARKS,
BaseBenchmark,
BenchmarkPlan,
EnvironmentSession,
EnvironmentSpec,
ExecutionPlan,
PreparedTask,
RunRequest,
RunResult,
TaskInput,
TaskOutput,
TaskSpec,
TaskStatus,
)
from agentcompass.runtime.config import config_field, parse_bool
from agentcompass.runtime.metrics import make_metric_contract
@dataclass(slots=True)
class ExampleExactMatchConfig(RuntimeBenchmarkConfig):
case_sensitive: bool = config_field(
default=False,
description="Compare answers with case sensitivity.",
)
def __post_init__(self) -> None:
RuntimeBenchmarkConfig.__post_init__(self)
self.case_sensitive = parse_bool(self.case_sensitive, "case_sensitive")
@dataclass(slots=True)
class ExampleExactMatchPlan(BenchmarkPlan):
expected: str = ""
case_sensitive: bool = False
@BENCHMARKS.register()
class ExampleExactMatchBenchmark(BaseBenchmark):
id = "example_exact_match"
description = "One-task exact-match Benchmark used by the developer tutorial."
config_class = ExampleExactMatchConfig
evaluation_environment_mode = "none"
metric_contract = make_metric_contract(
primary="correct",
binary=("correct",),
labels={"correct": "Accuracy"},
)
def load_tasks(self, req: RunRequest) -> list[TaskSpec]:
_ = req
return [
TaskSpec(
task_id="capital-france",
question=(
"What is the capital of France? "
"Answer with only the city name."
),
category="geography",
ground_truth="Paris",
metadata={"dataset_revision": "tutorial-v1"},
)
]
def build_plan(
self,
task: TaskSpec,
req: RunRequest,
environment: EnvironmentSpec,
) -> ExampleExactMatchPlan:
_ = environment
config = self.build_config(req)
if not isinstance(config, ExampleExactMatchConfig):
raise TypeError(
"example_exact_match requires ExampleExactMatchConfig"
)
return ExampleExactMatchPlan(
expected=str(task.ground_truth),
case_sensitive=config.case_sensitive,
)
async def prepare_task(
self,
task: TaskSpec,
env: EnvironmentSession,
req: RunRequest,
plan: BenchmarkPlan,
) -> PreparedTask:
_ = env, req
self._require_plan(plan)
return PreparedTask(
task_id=task.task_id,
category=task.category,
ground_truth=None,
input=TaskInput(prompt=task.question),
output=TaskOutput(answer="Return only the city name."),
metadata={
"dataset_revision": task.metadata["dataset_revision"],
},
)
async def evaluate(
self,
task: TaskSpec,
prepared: PreparedTask,
result: RunResult,
req: RunRequest,
plan: ExecutionPlan,
env: EnvironmentSession | None = None,
) -> RunResult:
_ = req, env
benchmark_plan = self._require_plan(plan.benchmark_plan)
candidate = str(result.final_answer or "").strip()
expected = benchmark_plan.expected.strip()
if not benchmark_plan.case_sensitive:
candidate = candidate.casefold()
expected = expected.casefold()
evaluated = (
result.status == TaskStatus.COMPLETED and not result.error
)
correct = evaluated and candidate == expected
return replace(
result,
task_id=prepared.task_id,
category=prepared.category,
metrics={"correct": correct},
)
@staticmethod
def _require_plan(plan: BenchmarkPlan) -> ExampleExactMatchPlan:
if not isinstance(plan, ExampleExactMatchPlan):
raise TypeError(
"example_exact_match requires ExampleExactMatchPlan"
)
return plan