agentbridge-bench 0.1.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.
- agentbridge_bench/__init__.py +5 -0
- agentbridge_bench/cli.py +118 -0
- agentbridge_bench/data/configurations.yaml +65 -0
- agentbridge_bench/data/suites.yaml +19 -0
- agentbridge_bench/data/tasks/analysis-001/hidden_tests/test_hidden_analysis.py +33 -0
- agentbridge_bench/data/tasks/analysis-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/analysis-001/repo/shop/__init__.py +1 -0
- agentbridge_bench/data/tasks/analysis-001/repo/shop/cart.py +15 -0
- agentbridge_bench/data/tasks/analysis-001/repo/shop/cli.py +18 -0
- agentbridge_bench/data/tasks/analysis-001/repo/shop/pricing.py +9 -0
- agentbridge_bench/data/tasks/analysis-001/repo/shop/tax.py +7 -0
- agentbridge_bench/data/tasks/analysis-001/repo/tests/test_cart.py +5 -0
- agentbridge_bench/data/tasks/analysis-001/solution/ANALYSIS.md +20 -0
- agentbridge_bench/data/tasks/analysis-001/task.yaml +30 -0
- agentbridge_bench/data/tasks/bugfix-001/hidden_tests/test_hidden_slugify.py +17 -0
- agentbridge_bench/data/tasks/bugfix-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/bugfix-001/repo/tests/test_textutils.py +9 -0
- agentbridge_bench/data/tasks/bugfix-001/repo/textutils.py +9 -0
- agentbridge_bench/data/tasks/bugfix-001/solution/textutils.py +8 -0
- agentbridge_bench/data/tasks/bugfix-001/task.yaml +30 -0
- agentbridge_bench/data/tasks/bugfix-002/hidden_tests/test_hidden_median.py +24 -0
- agentbridge_bench/data/tasks/bugfix-002/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/bugfix-002/repo/stats.py +15 -0
- agentbridge_bench/data/tasks/bugfix-002/repo/tests/test_stats.py +9 -0
- agentbridge_bench/data/tasks/bugfix-002/solution/stats.py +20 -0
- agentbridge_bench/data/tasks/bugfix-002/task.yaml +30 -0
- agentbridge_bench/data/tasks/bugfix-003/hidden_tests/test_hidden_pagination.py +27 -0
- agentbridge_bench/data/tasks/bugfix-003/repo/pagination.py +12 -0
- agentbridge_bench/data/tasks/bugfix-003/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/bugfix-003/repo/tests/test_pagination.py +5 -0
- agentbridge_bench/data/tasks/bugfix-003/solution/pagination.py +14 -0
- agentbridge_bench/data/tasks/bugfix-003/task.yaml +31 -0
- agentbridge_bench/data/tasks/deps-001/hidden_tests/test_hidden_deps.py +31 -0
- agentbridge_bench/data/tasks/deps-001/repo/CHANGELOG.md +7 -0
- agentbridge_bench/data/tasks/deps-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/deps-001/repo/requirements.txt +3 -0
- agentbridge_bench/data/tasks/deps-001/solution/CHANGELOG.md +9 -0
- agentbridge_bench/data/tasks/deps-001/solution/requirements.txt +3 -0
- agentbridge_bench/data/tasks/deps-001/task.yaml +26 -0
- agentbridge_bench/data/tasks/docs-001/hidden_tests/test_hidden_docs.py +36 -0
- agentbridge_bench/data/tasks/docs-001/repo/README.md +3 -0
- agentbridge_bench/data/tasks/docs-001/repo/mathlib.py +18 -0
- agentbridge_bench/data/tasks/docs-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/docs-001/repo/tests/test_mathlib.py +13 -0
- agentbridge_bench/data/tasks/docs-001/solution/README.md +13 -0
- agentbridge_bench/data/tasks/docs-001/solution/mathlib.py +26 -0
- agentbridge_bench/data/tasks/docs-001/task.yaml +31 -0
- agentbridge_bench/data/tasks/feature-001/hidden_tests/test_hidden_email.py +24 -0
- agentbridge_bench/data/tasks/feature-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/feature-001/repo/tests/test_validators.py +9 -0
- agentbridge_bench/data/tasks/feature-001/repo/validators.py +10 -0
- agentbridge_bench/data/tasks/feature-001/solution/tests/test_email.py +9 -0
- agentbridge_bench/data/tasks/feature-001/solution/validators.py +26 -0
- agentbridge_bench/data/tasks/feature-001/task.yaml +32 -0
- agentbridge_bench/data/tasks/feature-002/hidden_tests/test_hidden_lru.py +43 -0
- agentbridge_bench/data/tasks/feature-002/repo/cache.py +17 -0
- agentbridge_bench/data/tasks/feature-002/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/feature-002/solution/cache.py +28 -0
- agentbridge_bench/data/tasks/feature-002/task.yaml +27 -0
- agentbridge_bench/data/tasks/feature-003/hidden_tests/test_hidden_durations.py +21 -0
- agentbridge_bench/data/tasks/feature-003/repo/durations.py +14 -0
- agentbridge_bench/data/tasks/feature-003/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/feature-003/repo/tests/test_durations.py +6 -0
- agentbridge_bench/data/tasks/feature-003/solution/durations.py +22 -0
- agentbridge_bench/data/tasks/feature-003/task.yaml +31 -0
- agentbridge_bench/data/tasks/refactor-001/hidden_tests/test_hidden_refactor.py +25 -0
- agentbridge_bench/data/tasks/refactor-001/repo/geometry.py +22 -0
- agentbridge_bench/data/tasks/refactor-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/refactor-001/repo/report.py +17 -0
- agentbridge_bench/data/tasks/refactor-001/repo/tests/test_report.py +7 -0
- agentbridge_bench/data/tasks/refactor-001/solution/geometry.py +13 -0
- agentbridge_bench/data/tasks/refactor-001/solution/report.py +11 -0
- agentbridge_bench/data/tasks/refactor-001/task.yaml +32 -0
- agentbridge_bench/data/tasks/review-001/hidden_tests/test_hidden_review.py +28 -0
- agentbridge_bench/data/tasks/review-001/repo/accounts.py +16 -0
- agentbridge_bench/data/tasks/review-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/review-001/solution/REVIEW.md +7 -0
- agentbridge_bench/data/tasks/review-001/task.yaml +25 -0
- agentbridge_bench/data/tasks/tests-001/hidden_tests/test_hidden_generated_tests.py +49 -0
- agentbridge_bench/data/tasks/tests-001/repo/calculator.py +19 -0
- agentbridge_bench/data/tasks/tests-001/repo/pytest.ini +3 -0
- agentbridge_bench/data/tasks/tests-001/repo/tests/.gitkeep +0 -0
- agentbridge_bench/data/tasks/tests-001/solution/tests/test_calculator.py +24 -0
- agentbridge_bench/data/tasks/tests-001/task.yaml +25 -0
- agentbridge_bench/dataset.py +158 -0
- agentbridge_bench/metrics.py +90 -0
- agentbridge_bench/models.py +93 -0
- agentbridge_bench/reports.py +217 -0
- agentbridge_bench/runner.py +386 -0
- agentbridge_bench-0.1.0.dist-info/METADATA +50 -0
- agentbridge_bench-0.1.0.dist-info/RECORD +94 -0
- agentbridge_bench-0.1.0.dist-info/WHEEL +4 -0
- agentbridge_bench-0.1.0.dist-info/entry_points.txt +2 -0
- agentbridge_bench-0.1.0.dist-info/licenses/LICENSE +21 -0
agentbridge_bench/cli.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""AgentBench command line: ``agentbridge-bench ...`` and ``agentbridge benchmark ...``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.markup import escape
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from agentbridge.cli.context import handle_errors
|
|
13
|
+
from agentbridge.cli.output import console
|
|
14
|
+
from agentbridge.errors import ConfigError, NotFoundError
|
|
15
|
+
from agentbridge_bench.dataset import (
|
|
16
|
+
default_benchmarks_dir,
|
|
17
|
+
load_configurations,
|
|
18
|
+
load_suites,
|
|
19
|
+
load_tasks,
|
|
20
|
+
)
|
|
21
|
+
from agentbridge_bench.reports import generate_reports
|
|
22
|
+
from agentbridge_bench.runner import run_benchmark
|
|
23
|
+
|
|
24
|
+
bench_app = typer.Typer(help="AgentBench: reproducible agent evaluation.", no_args_is_help=True)
|
|
25
|
+
|
|
26
|
+
DirOption = Annotated[
|
|
27
|
+
Path | None,
|
|
28
|
+
typer.Option("--benchmarks-dir", help="Directory with tasks/ and configurations.yaml"),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@bench_app.command("list")
|
|
33
|
+
@handle_errors
|
|
34
|
+
def bench_list(benchmarks_dir: DirOption = None) -> None:
|
|
35
|
+
"""List benchmark tasks, suites and configurations."""
|
|
36
|
+
root = benchmarks_dir or default_benchmarks_dir()
|
|
37
|
+
tasks = load_tasks(root)
|
|
38
|
+
table = Table(title=f"AgentBench tasks ({root})")
|
|
39
|
+
for column in ("Task", "Category", "Difficulty", "Timeout (s)"):
|
|
40
|
+
table.add_column(column)
|
|
41
|
+
for task in tasks.values():
|
|
42
|
+
table.add_row(task.task_id, task.task_category.value, task.difficulty, str(task.timeout))
|
|
43
|
+
console.print(table)
|
|
44
|
+
for name, ids in load_suites(root, tasks).items():
|
|
45
|
+
console.print(f"suite [bold]{name}[/bold]: {len(ids)} task(s)")
|
|
46
|
+
for name, config in load_configurations(root).items():
|
|
47
|
+
console.print(
|
|
48
|
+
f"configuration [bold]{name}[/bold] ({config.kind}): {escape(config.description)}"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@bench_app.command("run")
|
|
53
|
+
@handle_errors
|
|
54
|
+
def bench_run(
|
|
55
|
+
suite_name: Annotated[str, typer.Argument(help="Suite name, or a single task id.")],
|
|
56
|
+
config: Annotated[
|
|
57
|
+
list[str] | None,
|
|
58
|
+
typer.Option("--config", "-c", help="Configuration(s) to run (repeatable)."),
|
|
59
|
+
] = None,
|
|
60
|
+
repetitions: Annotated[int, typer.Option("--repetitions", "-n", min=1, max=20)] = 1,
|
|
61
|
+
output: Annotated[Path | None, typer.Option("--output", help="Experiments directory.")] = None,
|
|
62
|
+
benchmarks_dir: DirOption = None,
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Run a benchmark suite under one or more configurations."""
|
|
65
|
+
root = benchmarks_dir or default_benchmarks_dir()
|
|
66
|
+
tasks = load_tasks(root)
|
|
67
|
+
suites = load_suites(root, tasks)
|
|
68
|
+
if suite_name in suites:
|
|
69
|
+
selected = [tasks[i] for i in suites[suite_name]]
|
|
70
|
+
elif suite_name in tasks:
|
|
71
|
+
selected = [tasks[suite_name]]
|
|
72
|
+
else:
|
|
73
|
+
raise NotFoundError(
|
|
74
|
+
f"Unknown suite or task: {suite_name}", hint=f"Suites: {', '.join(suites)}"
|
|
75
|
+
)
|
|
76
|
+
configurations = load_configurations(root)
|
|
77
|
+
names = config or ["mock-oracle"]
|
|
78
|
+
unknown = [n for n in names if n not in configurations]
|
|
79
|
+
if unknown:
|
|
80
|
+
raise ConfigError(
|
|
81
|
+
f"Unknown configuration(s): {', '.join(unknown)}",
|
|
82
|
+
hint=f"Available: {', '.join(configurations)}",
|
|
83
|
+
)
|
|
84
|
+
exp_dir = run_benchmark(
|
|
85
|
+
root,
|
|
86
|
+
output or (root / "experiments"),
|
|
87
|
+
suite_name,
|
|
88
|
+
selected,
|
|
89
|
+
[configurations[n] for n in names],
|
|
90
|
+
repetitions,
|
|
91
|
+
progress=lambda msg: console.print(escape(msg)),
|
|
92
|
+
)
|
|
93
|
+
console.print(f"[green]Done.[/green] Report: {exp_dir / 'report.md'}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@bench_app.command("report")
|
|
97
|
+
@handle_errors
|
|
98
|
+
def bench_report(
|
|
99
|
+
experiment_dir: Annotated[Path, typer.Argument(help="Experiment directory.")],
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Regenerate CSV/Markdown/SVG reports from saved experiment records."""
|
|
102
|
+
if not (experiment_dir / "records").is_dir():
|
|
103
|
+
raise NotFoundError(f"No records/ directory in {experiment_dir}")
|
|
104
|
+
summary = generate_reports(experiment_dir)
|
|
105
|
+
for name, metrics in summary.items():
|
|
106
|
+
console.print(
|
|
107
|
+
f"{name}: success={metrics['task_success_rate']} executed={metrics['runs_executed']}"
|
|
108
|
+
)
|
|
109
|
+
console.print(f"Report: {experiment_dir / 'report.md'}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def main() -> None:
|
|
113
|
+
"""Entry point of the ``agentbridge-bench`` command."""
|
|
114
|
+
bench_app(prog_name="agentbridge-bench")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
main()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# AgentBench configurations.
|
|
2
|
+
#
|
|
3
|
+
# A configuration is only executed when every provider it uses is available and
|
|
4
|
+
# can run without a human. Others are recorded as "not_executable" (never as
|
|
5
|
+
# failures and never as fabricated results).
|
|
6
|
+
|
|
7
|
+
configurations:
|
|
8
|
+
mock-oracle:
|
|
9
|
+
description: >-
|
|
10
|
+
Harness control. The mock provider writes the task's reference solution, so
|
|
11
|
+
every task should pass. Validates fixtures and hidden tests; says nothing
|
|
12
|
+
about AI agents.
|
|
13
|
+
kind: control
|
|
14
|
+
planner_mode: single
|
|
15
|
+
oracle: true
|
|
16
|
+
providers:
|
|
17
|
+
mock: {type: mock}
|
|
18
|
+
routing: {planner: mock, default_worker: mock}
|
|
19
|
+
|
|
20
|
+
mock-noop:
|
|
21
|
+
description: >-
|
|
22
|
+
Negative control. The mock provider writes an unrelated file, so tasks
|
|
23
|
+
should fail. Confirms the hidden tests cannot be passed without a change.
|
|
24
|
+
kind: control
|
|
25
|
+
planner_mode: single
|
|
26
|
+
providers:
|
|
27
|
+
mock: {type: mock}
|
|
28
|
+
routing: {planner: mock, default_worker: mock}
|
|
29
|
+
|
|
30
|
+
claude-single:
|
|
31
|
+
description: "Configuration A: single Claude Code agent (Task -> Claude Code -> Result)."
|
|
32
|
+
kind: single_agent
|
|
33
|
+
planner_mode: single
|
|
34
|
+
providers:
|
|
35
|
+
claude: {type: claude_code, command: claude, permission_mode: acceptEdits}
|
|
36
|
+
routing: {planner: claude, default_worker: claude}
|
|
37
|
+
|
|
38
|
+
claude-multi:
|
|
39
|
+
description: >-
|
|
40
|
+
Multi-agent workflow using Claude Code in three roles: analyst -> developer ->
|
|
41
|
+
reviewer (template plan), with an additional agent review of the developer diff.
|
|
42
|
+
kind: multi_agent
|
|
43
|
+
planner_mode: standard
|
|
44
|
+
providers:
|
|
45
|
+
claude: {type: claude_code, command: claude, permission_mode: acceptEdits}
|
|
46
|
+
routing: {planner: claude, default_worker: claude, reviewer: claude}
|
|
47
|
+
|
|
48
|
+
antigravity-remote:
|
|
49
|
+
description: "Configuration B: single Antigravity remote agent (manual handoff)."
|
|
50
|
+
kind: remote
|
|
51
|
+
planner_mode: single
|
|
52
|
+
providers:
|
|
53
|
+
antigravity: {type: antigravity_remote, command: agy}
|
|
54
|
+
routing: {planner: antigravity, default_worker: antigravity}
|
|
55
|
+
|
|
56
|
+
claude-antigravity:
|
|
57
|
+
description: >-
|
|
58
|
+
Configuration C: Claude planner -> Antigravity remote worker -> independent
|
|
59
|
+
reviewer. Requires a manual remote handoff.
|
|
60
|
+
kind: remote
|
|
61
|
+
planner_mode: provider
|
|
62
|
+
providers:
|
|
63
|
+
claude: {type: claude_code, command: claude, permission_mode: acceptEdits}
|
|
64
|
+
antigravity: {type: antigravity_remote, command: agy}
|
|
65
|
+
routing: {planner: claude, default_worker: antigravity, reviewer: claude}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Named task suites. "all" (every task) is always available.
|
|
2
|
+
suites:
|
|
3
|
+
core:
|
|
4
|
+
- analysis-001
|
|
5
|
+
- bugfix-001
|
|
6
|
+
- bugfix-002
|
|
7
|
+
- bugfix-003
|
|
8
|
+
- deps-001
|
|
9
|
+
- docs-001
|
|
10
|
+
- feature-001
|
|
11
|
+
- feature-002
|
|
12
|
+
- feature-003
|
|
13
|
+
- refactor-001
|
|
14
|
+
- review-001
|
|
15
|
+
- tests-001
|
|
16
|
+
smoke:
|
|
17
|
+
- bugfix-001
|
|
18
|
+
- feature-001
|
|
19
|
+
- tests-001
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def text():
|
|
7
|
+
path = ROOT / "ANALYSIS.md"
|
|
8
|
+
assert path.exists(), "ANALYSIS.md was not written"
|
|
9
|
+
return path.read_text(encoding="utf-8")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_modules_listed():
|
|
13
|
+
content = text()
|
|
14
|
+
for module in ("cart", "pricing", "tax", "cli"):
|
|
15
|
+
assert module in content
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_functions_listed():
|
|
19
|
+
content = text()
|
|
20
|
+
for name in ("add_item", "remove_item", "item_count", "apply_discount", "round_price",
|
|
21
|
+
"tax_for"):
|
|
22
|
+
assert name in content
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_entry_point():
|
|
26
|
+
assert "main" in text()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_untested_modules():
|
|
30
|
+
lines = [line.lower() for line in text().splitlines() if "untested" in line.lower()]
|
|
31
|
+
for module in ("pricing", "tax", "cli"):
|
|
32
|
+
assert any(module in line for line in lines), module
|
|
33
|
+
assert not any("cart" in line for line in lines)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""A tiny shop."""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Shopping cart."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def add_item(cart, sku, quantity=1):
|
|
5
|
+
cart[sku] = cart.get(sku, 0) + quantity
|
|
6
|
+
return cart
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def remove_item(cart, sku):
|
|
10
|
+
cart.pop(sku, None)
|
|
11
|
+
return cart
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def item_count(cart):
|
|
15
|
+
return sum(cart.values())
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Command line interface."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from shop.cart import add_item, item_count
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main(argv=None):
|
|
9
|
+
args = sys.argv[1:] if argv is None else argv
|
|
10
|
+
cart = {}
|
|
11
|
+
for sku in args:
|
|
12
|
+
add_item(cart, sku)
|
|
13
|
+
print(item_count(cart))
|
|
14
|
+
return 0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Repository analysis
|
|
2
|
+
|
|
3
|
+
## Modules in `shop`
|
|
4
|
+
|
|
5
|
+
- `shop.cart`: add_item, remove_item, item_count
|
|
6
|
+
- `shop.pricing`: apply_discount, round_price
|
|
7
|
+
- `shop.tax`: tax_for (plus the TAX_RATES table)
|
|
8
|
+
- `shop.cli`: main
|
|
9
|
+
|
|
10
|
+
## Entry point
|
|
11
|
+
|
|
12
|
+
The command-line entry point is `shop.cli.main` (run `python -m shop.cli`).
|
|
13
|
+
|
|
14
|
+
## Test coverage
|
|
15
|
+
|
|
16
|
+
Only `shop.cart` has tests (tests/test_cart.py).
|
|
17
|
+
|
|
18
|
+
- pricing: untested
|
|
19
|
+
- tax: untested
|
|
20
|
+
- cli: untested
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# base_commit is null for fixture tasks: the base commit is synthesised
|
|
2
|
+
# deterministically from repo/ (see docs/benchmark-methodology.md).
|
|
3
|
+
task_id: analysis-001
|
|
4
|
+
repository: fixture
|
|
5
|
+
base_commit: null
|
|
6
|
+
task_category: repository_analysis
|
|
7
|
+
difficulty: easy
|
|
8
|
+
timeout: 900
|
|
9
|
+
issue_description: |
|
|
10
|
+
Write ANALYSIS.md describing this repository: list every module in the shop package together with its public functions, identify the command-line entry point, and state which modules have no tests (one line per untested module, containing the module name and the word "untested"). Do not modify source files.
|
|
11
|
+
acceptance_criteria:
|
|
12
|
+
- ANALYSIS.md lists all shop modules and their public functions
|
|
13
|
+
- ANALYSIS.md names the CLI entry point
|
|
14
|
+
- ANALYSIS.md lists the untested modules
|
|
15
|
+
expected_files:
|
|
16
|
+
- ANALYSIS.md
|
|
17
|
+
setup_commands: []
|
|
18
|
+
test_commands:
|
|
19
|
+
- - '{python}'
|
|
20
|
+
- -m
|
|
21
|
+
- pytest
|
|
22
|
+
- -q
|
|
23
|
+
- agentbench_hidden
|
|
24
|
+
regression_test_command:
|
|
25
|
+
- '{python}'
|
|
26
|
+
- -m
|
|
27
|
+
- pytest
|
|
28
|
+
- -q
|
|
29
|
+
- tests
|
|
30
|
+
license: MIT (authored for AgentBench)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from textutils import slugify
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_collapses_separators():
|
|
5
|
+
assert slugify("Hello, World!") == "hello-world"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_strips_edges():
|
|
9
|
+
assert slugify(" --Python 3.12-- ") == "python-3-12"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_empty():
|
|
13
|
+
assert slugify("") == ""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_plain():
|
|
17
|
+
assert slugify("abc") == "abc"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# base_commit is null for fixture tasks: the base commit is synthesised
|
|
2
|
+
# deterministically from repo/ (see docs/benchmark-methodology.md).
|
|
3
|
+
task_id: bugfix-001
|
|
4
|
+
repository: fixture
|
|
5
|
+
base_commit: null
|
|
6
|
+
task_category: bug_fixing
|
|
7
|
+
difficulty: easy
|
|
8
|
+
timeout: 900
|
|
9
|
+
issue_description: |
|
|
10
|
+
textutils.slugify() produces malformed slugs. slugify("Hello, World!") returns "hello---world-" but should return "hello-world". Runs of non-alphanumeric characters must collapse into a single dash, and the slug must not start or end with a dash.
|
|
11
|
+
acceptance_criteria:
|
|
12
|
+
- slugify collapses runs of separators into one dash
|
|
13
|
+
- slugify strips leading and trailing dashes
|
|
14
|
+
- Existing tests keep passing
|
|
15
|
+
expected_files:
|
|
16
|
+
- textutils.py
|
|
17
|
+
setup_commands: []
|
|
18
|
+
test_commands:
|
|
19
|
+
- - '{python}'
|
|
20
|
+
- -m
|
|
21
|
+
- pytest
|
|
22
|
+
- -q
|
|
23
|
+
- agentbench_hidden
|
|
24
|
+
regression_test_command:
|
|
25
|
+
- '{python}'
|
|
26
|
+
- -m
|
|
27
|
+
- pytest
|
|
28
|
+
- -q
|
|
29
|
+
- tests
|
|
30
|
+
license: MIT (authored for AgentBench)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from stats import mean, median
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_even():
|
|
7
|
+
assert median([1, 2, 3, 4]) == 2.5
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_even_unsorted():
|
|
11
|
+
assert median([4, 1, 3, 2]) == 2.5
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_single():
|
|
15
|
+
assert median([5]) == 5
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_empty_raises_value_error():
|
|
19
|
+
with pytest.raises(ValueError):
|
|
20
|
+
median([])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_mean_unchanged():
|
|
24
|
+
assert mean([2, 4]) == 3
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Basic statistics."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def mean(values):
|
|
5
|
+
"""Arithmetic mean of a non-empty sequence."""
|
|
6
|
+
values = list(values)
|
|
7
|
+
if not values:
|
|
8
|
+
raise ValueError("mean() of empty sequence")
|
|
9
|
+
return sum(values) / len(values)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def median(values):
|
|
13
|
+
"""Median of a non-empty sequence of numbers."""
|
|
14
|
+
ordered = sorted(values)
|
|
15
|
+
return ordered[len(ordered) // 2]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Basic statistics."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def mean(values):
|
|
5
|
+
"""Arithmetic mean of a non-empty sequence."""
|
|
6
|
+
values = list(values)
|
|
7
|
+
if not values:
|
|
8
|
+
raise ValueError("mean() of empty sequence")
|
|
9
|
+
return sum(values) / len(values)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def median(values):
|
|
13
|
+
"""Median of a non-empty sequence of numbers."""
|
|
14
|
+
ordered = sorted(values)
|
|
15
|
+
if not ordered:
|
|
16
|
+
raise ValueError("median() of empty sequence")
|
|
17
|
+
middle = len(ordered) // 2
|
|
18
|
+
if len(ordered) % 2:
|
|
19
|
+
return ordered[middle]
|
|
20
|
+
return (ordered[middle - 1] + ordered[middle]) / 2
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# base_commit is null for fixture tasks: the base commit is synthesised
|
|
2
|
+
# deterministically from repo/ (see docs/benchmark-methodology.md).
|
|
3
|
+
task_id: bugfix-002
|
|
4
|
+
repository: fixture
|
|
5
|
+
base_commit: null
|
|
6
|
+
task_category: bug_fixing
|
|
7
|
+
difficulty: easy
|
|
8
|
+
timeout: 900
|
|
9
|
+
issue_description: |
|
|
10
|
+
stats.median() returns the wrong value for sequences with an even number of elements: median([1, 2, 3, 4]) returns 3 instead of 2.5. It should return the mean of the two middle values. median([]) currently raises IndexError; it must raise ValueError instead.
|
|
11
|
+
acceptance_criteria:
|
|
12
|
+
- median returns the mean of the two middle values for even-length input
|
|
13
|
+
- median([]) raises ValueError
|
|
14
|
+
- Existing tests keep passing
|
|
15
|
+
expected_files:
|
|
16
|
+
- stats.py
|
|
17
|
+
setup_commands: []
|
|
18
|
+
test_commands:
|
|
19
|
+
- - '{python}'
|
|
20
|
+
- -m
|
|
21
|
+
- pytest
|
|
22
|
+
- -q
|
|
23
|
+
- agentbench_hidden
|
|
24
|
+
regression_test_command:
|
|
25
|
+
- '{python}'
|
|
26
|
+
- -m
|
|
27
|
+
- pytest
|
|
28
|
+
- -q
|
|
29
|
+
- tests
|
|
30
|
+
license: MIT (authored for AgentBench)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from pagination import page_count, paginate
|
|
4
|
+
|
|
5
|
+
ITEMS = list(range(25))
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_first_page():
|
|
9
|
+
assert paginate(ITEMS, 1, 10) == list(range(10))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_last_partial_page():
|
|
13
|
+
assert paginate(ITEMS, 3, 10) == [20, 21, 22, 23, 24]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_page_count_rounds_up():
|
|
17
|
+
assert page_count(25, 10) == 3
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_page_count_exact_and_zero():
|
|
21
|
+
assert page_count(20, 10) == 2
|
|
22
|
+
assert page_count(0, 10) == 0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_invalid_page():
|
|
26
|
+
with pytest.raises(ValueError):
|
|
27
|
+
paginate(ITEMS, 0)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""List pagination helpers."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def paginate(items, page, per_page=10):
|
|
5
|
+
"""Return the items on a 1-indexed page."""
|
|
6
|
+
start = page * per_page
|
|
7
|
+
return items[start : start + per_page]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def page_count(total, per_page=10):
|
|
11
|
+
"""Number of pages needed for ``total`` items."""
|
|
12
|
+
return total // per_page
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""List pagination helpers."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def paginate(items, page, per_page=10):
|
|
5
|
+
"""Return the items on a 1-indexed page."""
|
|
6
|
+
if page < 1:
|
|
7
|
+
raise ValueError("page numbers start at 1")
|
|
8
|
+
start = (page - 1) * per_page
|
|
9
|
+
return items[start : start + per_page]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def page_count(total, per_page=10):
|
|
13
|
+
"""Number of pages needed for ``total`` items."""
|
|
14
|
+
return -(-total // per_page)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# base_commit is null for fixture tasks: the base commit is synthesised
|
|
2
|
+
# deterministically from repo/ (see docs/benchmark-methodology.md).
|
|
3
|
+
task_id: bugfix-003
|
|
4
|
+
repository: fixture
|
|
5
|
+
base_commit: null
|
|
6
|
+
task_category: bug_fixing
|
|
7
|
+
difficulty: medium
|
|
8
|
+
timeout: 900
|
|
9
|
+
issue_description: |
|
|
10
|
+
pagination.paginate() is documented as 1-indexed but paginate(items, 1) returns the second page. page_count(25, 10) returns 2 but there are 3 pages. Fix both, and make paginate() raise ValueError for page numbers below 1.
|
|
11
|
+
acceptance_criteria:
|
|
12
|
+
- paginate(items, 1) returns the first page
|
|
13
|
+
- page_count rounds up partial pages
|
|
14
|
+
- paginate raises ValueError for page < 1
|
|
15
|
+
- Existing tests keep passing
|
|
16
|
+
expected_files:
|
|
17
|
+
- pagination.py
|
|
18
|
+
setup_commands: []
|
|
19
|
+
test_commands:
|
|
20
|
+
- - '{python}'
|
|
21
|
+
- -m
|
|
22
|
+
- pytest
|
|
23
|
+
- -q
|
|
24
|
+
- agentbench_hidden
|
|
25
|
+
regression_test_command:
|
|
26
|
+
- '{python}'
|
|
27
|
+
- -m
|
|
28
|
+
- pytest
|
|
29
|
+
- -q
|
|
30
|
+
- tests
|
|
31
|
+
license: MIT (authored for AgentBench)
|