repobench 0.1.0__tar.gz
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.
- repobench-0.1.0/.gitignore +1 -0
- repobench-0.1.0/Makefile +53 -0
- repobench-0.1.0/PKG-INFO +247 -0
- repobench-0.1.0/README.md +216 -0
- repobench-0.1.0/agentfit/__init__.py +3 -0
- repobench-0.1.0/agentfit/analysis/__init__.py +0 -0
- repobench-0.1.0/agentfit/analysis/metrics.py +58 -0
- repobench-0.1.0/agentfit/analysis/recommendation.py +104 -0
- repobench-0.1.0/agentfit/analysis/statistics.py +71 -0
- repobench-0.1.0/agentfit/benchmark/__init__.py +0 -0
- repobench-0.1.0/agentfit/benchmark/coverage.py +51 -0
- repobench-0.1.0/agentfit/benchmark/health.py +191 -0
- repobench-0.1.0/agentfit/benchmark/sampling.py +152 -0
- repobench-0.1.0/agentfit/cli/__init__.py +0 -0
- repobench-0.1.0/agentfit/cli/analyze.py +576 -0
- repobench-0.1.0/agentfit/cli/app.py +210 -0
- repobench-0.1.0/agentfit/cli/benchmark.py +295 -0
- repobench-0.1.0/agentfit/cli/candidates.py +119 -0
- repobench-0.1.0/agentfit/cli/config_cmd.py +106 -0
- repobench-0.1.0/agentfit/cli/doctor.py +240 -0
- repobench-0.1.0/agentfit/cli/init.py +284 -0
- repobench-0.1.0/agentfit/cli/report.py +224 -0
- repobench-0.1.0/agentfit/cli/run.py +358 -0
- repobench-0.1.0/agentfit/cli/task.py +169 -0
- repobench-0.1.0/agentfit/cli/telemetry.py +103 -0
- repobench-0.1.0/agentfit/cli/utils.py +46 -0
- repobench-0.1.0/agentfit/config.py +32 -0
- repobench-0.1.0/agentfit/harbor/__init__.py +0 -0
- repobench-0.1.0/agentfit/harbor/exporter.py +206 -0
- repobench-0.1.0/agentfit/harbor/parser.py +109 -0
- repobench-0.1.0/agentfit/harbor/runner.py +321 -0
- repobench-0.1.0/agentfit/logging.py +46 -0
- repobench-0.1.0/agentfit/mining/__init__.py +0 -0
- repobench-0.1.0/agentfit/mining/candidates.py +219 -0
- repobench-0.1.0/agentfit/models.py +375 -0
- repobench-0.1.0/agentfit/reporting/__init__.py +0 -0
- repobench-0.1.0/agentfit/reporting/json.py +61 -0
- repobench-0.1.0/agentfit/reporting/terminal.py +171 -0
- repobench-0.1.0/agentfit/repository/__init__.py +0 -0
- repobench-0.1.0/agentfit/repository/detection.py +424 -0
- repobench-0.1.0/agentfit/repository/git.py +309 -0
- repobench-0.1.0/agentfit/repository/github.py +261 -0
- repobench-0.1.0/agentfit/repository/workload.py +339 -0
- repobench-0.1.0/agentfit/storage/__init__.py +0 -0
- repobench-0.1.0/agentfit/storage/database.py +486 -0
- repobench-0.1.0/agentfit/storage/migrations/__init__.py +0 -0
- repobench-0.1.0/agentfit/tasks/__init__.py +0 -0
- repobench-0.1.0/agentfit/tasks/instruction.py +165 -0
- repobench-0.1.0/agentfit/tasks/leakage.py +139 -0
- repobench-0.1.0/agentfit/tasks/verifier.py +220 -0
- repobench-0.1.0/agentfit/utils.py +80 -0
- repobench-0.1.0/agentfit/validation/__init__.py +0 -0
- repobench-0.1.0/agentfit/validation/determinism.py +52 -0
- repobench-0.1.0/agentfit/validation/environment.py +160 -0
- repobench-0.1.0/agentfit/validation/noop.py +81 -0
- repobench-0.1.0/agentfit/validation/oracle.py +102 -0
- repobench-0.1.0/agentfit.yml +43 -0
- repobench-0.1.0/plans/go-java-support.md +121 -0
- repobench-0.1.0/plans/harbor-integration.md +188 -0
- repobench-0.1.0/pyproject.toml +64 -0
- repobench-0.1.0/tests/__init__.py +0 -0
- repobench-0.1.0/tests/test_benchmark_analysis.py +155 -0
- repobench-0.1.0/tests/test_instruction_mining.py +132 -0
- repobench-0.1.0/tests/test_workload_verifier.py +145 -0
- repobench-0.1.0/uv.lock +708 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.agentfit/
|
repobench-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.PHONY: install install-dev build clean publish test lint
|
|
2
|
+
|
|
3
|
+
# Install for users
|
|
4
|
+
install:
|
|
5
|
+
pip install .
|
|
6
|
+
|
|
7
|
+
# Install for development
|
|
8
|
+
install-dev:
|
|
9
|
+
pip install -e ".[dev]"
|
|
10
|
+
|
|
11
|
+
# Install with pipx (recommended for CLI tools)
|
|
12
|
+
install-pipx:
|
|
13
|
+
pipx install .
|
|
14
|
+
|
|
15
|
+
# Build package
|
|
16
|
+
build: clean
|
|
17
|
+
python -m build
|
|
18
|
+
|
|
19
|
+
# Clean build artifacts
|
|
20
|
+
clean:
|
|
21
|
+
rm -rf dist/ build/ *.egg-info agentfit/*.pyc
|
|
22
|
+
|
|
23
|
+
# Publish to PyPI (test)
|
|
24
|
+
publish-test: build
|
|
25
|
+
twine upload --repository testpypi dist/*
|
|
26
|
+
|
|
27
|
+
# Publish to PyPI (production)
|
|
28
|
+
publish: build
|
|
29
|
+
twine upload dist/*
|
|
30
|
+
|
|
31
|
+
# Run tests
|
|
32
|
+
test:
|
|
33
|
+
pytest tests/ -v
|
|
34
|
+
|
|
35
|
+
# Run linter
|
|
36
|
+
lint:
|
|
37
|
+
ruff check agentfit/
|
|
38
|
+
ruff format agentfit/ --check
|
|
39
|
+
|
|
40
|
+
# Format code
|
|
41
|
+
format:
|
|
42
|
+
ruff check agentfit/ --fix
|
|
43
|
+
ruff format agentfit/
|
|
44
|
+
|
|
45
|
+
# Type check
|
|
46
|
+
typecheck:
|
|
47
|
+
mypy agentfit/
|
|
48
|
+
|
|
49
|
+
# Show package info
|
|
50
|
+
info:
|
|
51
|
+
@echo "Package: agentfit"
|
|
52
|
+
@echo "Version: $$(python -c 'from agentfit import __version__; print(__version__)')"
|
|
53
|
+
@echo "Entry point: agentfit = agentfit.cli.app:app"
|
repobench-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: repobench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Living repository-native evals for coding agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/agentfit/agentfit
|
|
6
|
+
Project-URL: Repository, https://github.com/agentfit/agentfit
|
|
7
|
+
Project-URL: Issues, https://github.com/agentfit/agentfit/issues
|
|
8
|
+
Author: AgentFit Contributors
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Keywords: benchmark,coding-agent,eval,harbor,swe-bench
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: pydantic>=2.0.0
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: rich>=13.0.0
|
|
24
|
+
Requires-Dist: typer>=0.12.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# AgentFit
|
|
33
|
+
|
|
34
|
+
**Living repository-native evals for coding agents.**
|
|
35
|
+
|
|
36
|
+
Find which coding agent actually works for your codebase.
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Install
|
|
42
|
+
pipx install agentfit
|
|
43
|
+
|
|
44
|
+
# Initialize in your repo
|
|
45
|
+
cd my-project
|
|
46
|
+
agentfit init
|
|
47
|
+
|
|
48
|
+
# Analyze workload
|
|
49
|
+
agentfit analyze
|
|
50
|
+
|
|
51
|
+
# Build benchmark
|
|
52
|
+
agentfit benchmark build
|
|
53
|
+
|
|
54
|
+
# Run agents
|
|
55
|
+
agentfit run codex-local claude-local
|
|
56
|
+
|
|
57
|
+
# Get results
|
|
58
|
+
agentfit report
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## What is AgentFit?
|
|
62
|
+
|
|
63
|
+
AgentFit transforms your repository's real engineering history into a private, reproducible, representative benchmark for coding agents.
|
|
64
|
+
|
|
65
|
+
Instead of relying on public benchmarks that don't match your work, AgentFit mines your actual merged PRs to create evals that reflect what your team actually ships.
|
|
66
|
+
|
|
67
|
+
### The Pipeline
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Repository history
|
|
71
|
+
│
|
|
72
|
+
▼
|
|
73
|
+
DISCOVER What happened here?
|
|
74
|
+
│
|
|
75
|
+
▼
|
|
76
|
+
FILTER Can it become an eval?
|
|
77
|
+
│
|
|
78
|
+
▼
|
|
79
|
+
VALIDATE Can we prove correctness?
|
|
80
|
+
│
|
|
81
|
+
▼
|
|
82
|
+
REPRESENT Does the benchmark match
|
|
83
|
+
the actual workload?
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
RUN Execute agent configurations
|
|
87
|
+
│
|
|
88
|
+
▼
|
|
89
|
+
MEASURE Quality / cost / latency
|
|
90
|
+
│
|
|
91
|
+
▼
|
|
92
|
+
DECIDE What should we use?
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Commands
|
|
96
|
+
|
|
97
|
+
| Command | Description |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `agentfit doctor` | Check prerequisites |
|
|
100
|
+
| `agentfit init` | Initialize in current repo |
|
|
101
|
+
| `agentfit analyze` | Analyze repository workload |
|
|
102
|
+
| `agentfit candidates` | View mined candidate tasks |
|
|
103
|
+
| `agentfit task inspect <id>` | Inspect a candidate task |
|
|
104
|
+
| `agentfit benchmark build` | Build representative benchmark |
|
|
105
|
+
| `agentfit benchmark list` | List existing benchmarks |
|
|
106
|
+
| `agentfit run <configs...>` | Run agents via Harbor |
|
|
107
|
+
| `agentfit report` | Generate comparison report |
|
|
108
|
+
| `agentfit config show` | Show current configuration |
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
AgentFit creates `agentfit.yml` in your repo:
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
version: 1
|
|
116
|
+
|
|
117
|
+
repository:
|
|
118
|
+
provider: github
|
|
119
|
+
lookback_days: 180
|
|
120
|
+
|
|
121
|
+
project:
|
|
122
|
+
languages:
|
|
123
|
+
- python
|
|
124
|
+
- go
|
|
125
|
+
- java
|
|
126
|
+
install_command: pip install -e ".[dev]"
|
|
127
|
+
test_command: pytest
|
|
128
|
+
|
|
129
|
+
benchmark:
|
|
130
|
+
size: 24
|
|
131
|
+
dimensions:
|
|
132
|
+
task_type: 0.30
|
|
133
|
+
subsystem: 0.40
|
|
134
|
+
complexity: 0.30
|
|
135
|
+
|
|
136
|
+
execution:
|
|
137
|
+
environment: docker
|
|
138
|
+
concurrency: 4
|
|
139
|
+
|
|
140
|
+
agents:
|
|
141
|
+
codex-default:
|
|
142
|
+
agent: codex
|
|
143
|
+
model: openai/gpt-4o
|
|
144
|
+
|
|
145
|
+
claude-default:
|
|
146
|
+
agent: claude-code
|
|
147
|
+
model: anthropic/claude-opus-4
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Supported Languages
|
|
151
|
+
|
|
152
|
+
- Python
|
|
153
|
+
- JavaScript / TypeScript
|
|
154
|
+
- Go
|
|
155
|
+
- Java (Maven / Gradle)
|
|
156
|
+
|
|
157
|
+
## How it Works
|
|
158
|
+
|
|
159
|
+
### 1. Workload Analysis
|
|
160
|
+
|
|
161
|
+
AgentFit fetches your merged PRs via GitHub API and classifies each by:
|
|
162
|
+
- **Task type**: bugfix, feature, refactor
|
|
163
|
+
- **Subsystem**: payments, auth, frontend, etc.
|
|
164
|
+
- **Complexity**: small, medium, large
|
|
165
|
+
|
|
166
|
+
### 2. Candidate Mining
|
|
167
|
+
|
|
168
|
+
From the workload, AgentFit identifies PRs that can become reliable evals:
|
|
169
|
+
- Have linked issues or clear PR descriptions
|
|
170
|
+
- Include test changes (verifier evidence)
|
|
171
|
+
- Are within configurable size bounds
|
|
172
|
+
- Don't require unsupported environments
|
|
173
|
+
|
|
174
|
+
### 3. Validation Pipeline
|
|
175
|
+
|
|
176
|
+
Each candidate passes rigorous validation:
|
|
177
|
+
- **Base health**: tests pass before the change
|
|
178
|
+
- **No-op validation**: new tests fail without the fix
|
|
179
|
+
- **Oracle validation**: tests pass with the fix
|
|
180
|
+
- **Determinism**: tests produce consistent results
|
|
181
|
+
- **Leakage protection**: agent can't access gold solution
|
|
182
|
+
|
|
183
|
+
### 4. Representative Sampling
|
|
184
|
+
|
|
185
|
+
AgentFit selects benchmark tasks using stratified optimization to match your actual workload distribution across task type, subsystem, and complexity.
|
|
186
|
+
|
|
187
|
+
### 5. Execution via Harbor
|
|
188
|
+
|
|
189
|
+
Benchmarks are exported in Harbor format and executed via `harbor run`:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
agentfit run codex-default claude-default
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 6. Decision Report
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
agentfit report
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
AGENTFIT REPORT
|
|
203
|
+
────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
Repository: my-org/payments
|
|
206
|
+
Benchmark: af_b_20260825_a84f
|
|
207
|
+
Tasks: 24
|
|
208
|
+
Health: 87/100
|
|
209
|
+
|
|
210
|
+
Solve $/Solve
|
|
211
|
+
Codex 82% $0.71
|
|
212
|
+
Claude 86% $1.52
|
|
213
|
+
|
|
214
|
+
Quality
|
|
215
|
+
Claude +4pp vs Codex
|
|
216
|
+
95% CI: -7pp → +14pp
|
|
217
|
+
|
|
218
|
+
No conclusive quality difference.
|
|
219
|
+
|
|
220
|
+
Recommendation: Codex
|
|
221
|
+
Reason: lowest cost among statistically
|
|
222
|
+
indistinguishable configurations.
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Development
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# Clone
|
|
229
|
+
git clone https://github.com/agentfit/agentfit.git
|
|
230
|
+
cd agentfit
|
|
231
|
+
|
|
232
|
+
# Install dev dependencies
|
|
233
|
+
pip install -e ".[dev]"
|
|
234
|
+
|
|
235
|
+
# Run tests
|
|
236
|
+
pytest
|
|
237
|
+
|
|
238
|
+
# Build package
|
|
239
|
+
python -m build
|
|
240
|
+
|
|
241
|
+
# Publish to PyPI
|
|
242
|
+
twine upload dist/*
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
Apache 2.0
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# AgentFit
|
|
2
|
+
|
|
3
|
+
**Living repository-native evals for coding agents.**
|
|
4
|
+
|
|
5
|
+
Find which coding agent actually works for your codebase.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Install
|
|
11
|
+
pipx install agentfit
|
|
12
|
+
|
|
13
|
+
# Initialize in your repo
|
|
14
|
+
cd my-project
|
|
15
|
+
agentfit init
|
|
16
|
+
|
|
17
|
+
# Analyze workload
|
|
18
|
+
agentfit analyze
|
|
19
|
+
|
|
20
|
+
# Build benchmark
|
|
21
|
+
agentfit benchmark build
|
|
22
|
+
|
|
23
|
+
# Run agents
|
|
24
|
+
agentfit run codex-local claude-local
|
|
25
|
+
|
|
26
|
+
# Get results
|
|
27
|
+
agentfit report
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What is AgentFit?
|
|
31
|
+
|
|
32
|
+
AgentFit transforms your repository's real engineering history into a private, reproducible, representative benchmark for coding agents.
|
|
33
|
+
|
|
34
|
+
Instead of relying on public benchmarks that don't match your work, AgentFit mines your actual merged PRs to create evals that reflect what your team actually ships.
|
|
35
|
+
|
|
36
|
+
### The Pipeline
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Repository history
|
|
40
|
+
│
|
|
41
|
+
▼
|
|
42
|
+
DISCOVER What happened here?
|
|
43
|
+
│
|
|
44
|
+
▼
|
|
45
|
+
FILTER Can it become an eval?
|
|
46
|
+
│
|
|
47
|
+
▼
|
|
48
|
+
VALIDATE Can we prove correctness?
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
REPRESENT Does the benchmark match
|
|
52
|
+
the actual workload?
|
|
53
|
+
│
|
|
54
|
+
▼
|
|
55
|
+
RUN Execute agent configurations
|
|
56
|
+
│
|
|
57
|
+
▼
|
|
58
|
+
MEASURE Quality / cost / latency
|
|
59
|
+
│
|
|
60
|
+
▼
|
|
61
|
+
DECIDE What should we use?
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
| Command | Description |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `agentfit doctor` | Check prerequisites |
|
|
69
|
+
| `agentfit init` | Initialize in current repo |
|
|
70
|
+
| `agentfit analyze` | Analyze repository workload |
|
|
71
|
+
| `agentfit candidates` | View mined candidate tasks |
|
|
72
|
+
| `agentfit task inspect <id>` | Inspect a candidate task |
|
|
73
|
+
| `agentfit benchmark build` | Build representative benchmark |
|
|
74
|
+
| `agentfit benchmark list` | List existing benchmarks |
|
|
75
|
+
| `agentfit run <configs...>` | Run agents via Harbor |
|
|
76
|
+
| `agentfit report` | Generate comparison report |
|
|
77
|
+
| `agentfit config show` | Show current configuration |
|
|
78
|
+
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
AgentFit creates `agentfit.yml` in your repo:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
version: 1
|
|
85
|
+
|
|
86
|
+
repository:
|
|
87
|
+
provider: github
|
|
88
|
+
lookback_days: 180
|
|
89
|
+
|
|
90
|
+
project:
|
|
91
|
+
languages:
|
|
92
|
+
- python
|
|
93
|
+
- go
|
|
94
|
+
- java
|
|
95
|
+
install_command: pip install -e ".[dev]"
|
|
96
|
+
test_command: pytest
|
|
97
|
+
|
|
98
|
+
benchmark:
|
|
99
|
+
size: 24
|
|
100
|
+
dimensions:
|
|
101
|
+
task_type: 0.30
|
|
102
|
+
subsystem: 0.40
|
|
103
|
+
complexity: 0.30
|
|
104
|
+
|
|
105
|
+
execution:
|
|
106
|
+
environment: docker
|
|
107
|
+
concurrency: 4
|
|
108
|
+
|
|
109
|
+
agents:
|
|
110
|
+
codex-default:
|
|
111
|
+
agent: codex
|
|
112
|
+
model: openai/gpt-4o
|
|
113
|
+
|
|
114
|
+
claude-default:
|
|
115
|
+
agent: claude-code
|
|
116
|
+
model: anthropic/claude-opus-4
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Supported Languages
|
|
120
|
+
|
|
121
|
+
- Python
|
|
122
|
+
- JavaScript / TypeScript
|
|
123
|
+
- Go
|
|
124
|
+
- Java (Maven / Gradle)
|
|
125
|
+
|
|
126
|
+
## How it Works
|
|
127
|
+
|
|
128
|
+
### 1. Workload Analysis
|
|
129
|
+
|
|
130
|
+
AgentFit fetches your merged PRs via GitHub API and classifies each by:
|
|
131
|
+
- **Task type**: bugfix, feature, refactor
|
|
132
|
+
- **Subsystem**: payments, auth, frontend, etc.
|
|
133
|
+
- **Complexity**: small, medium, large
|
|
134
|
+
|
|
135
|
+
### 2. Candidate Mining
|
|
136
|
+
|
|
137
|
+
From the workload, AgentFit identifies PRs that can become reliable evals:
|
|
138
|
+
- Have linked issues or clear PR descriptions
|
|
139
|
+
- Include test changes (verifier evidence)
|
|
140
|
+
- Are within configurable size bounds
|
|
141
|
+
- Don't require unsupported environments
|
|
142
|
+
|
|
143
|
+
### 3. Validation Pipeline
|
|
144
|
+
|
|
145
|
+
Each candidate passes rigorous validation:
|
|
146
|
+
- **Base health**: tests pass before the change
|
|
147
|
+
- **No-op validation**: new tests fail without the fix
|
|
148
|
+
- **Oracle validation**: tests pass with the fix
|
|
149
|
+
- **Determinism**: tests produce consistent results
|
|
150
|
+
- **Leakage protection**: agent can't access gold solution
|
|
151
|
+
|
|
152
|
+
### 4. Representative Sampling
|
|
153
|
+
|
|
154
|
+
AgentFit selects benchmark tasks using stratified optimization to match your actual workload distribution across task type, subsystem, and complexity.
|
|
155
|
+
|
|
156
|
+
### 5. Execution via Harbor
|
|
157
|
+
|
|
158
|
+
Benchmarks are exported in Harbor format and executed via `harbor run`:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
agentfit run codex-default claude-default
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 6. Decision Report
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
agentfit report
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
AGENTFIT REPORT
|
|
172
|
+
────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
Repository: my-org/payments
|
|
175
|
+
Benchmark: af_b_20260825_a84f
|
|
176
|
+
Tasks: 24
|
|
177
|
+
Health: 87/100
|
|
178
|
+
|
|
179
|
+
Solve $/Solve
|
|
180
|
+
Codex 82% $0.71
|
|
181
|
+
Claude 86% $1.52
|
|
182
|
+
|
|
183
|
+
Quality
|
|
184
|
+
Claude +4pp vs Codex
|
|
185
|
+
95% CI: -7pp → +14pp
|
|
186
|
+
|
|
187
|
+
No conclusive quality difference.
|
|
188
|
+
|
|
189
|
+
Recommendation: Codex
|
|
190
|
+
Reason: lowest cost among statistically
|
|
191
|
+
indistinguishable configurations.
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Development
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Clone
|
|
198
|
+
git clone https://github.com/agentfit/agentfit.git
|
|
199
|
+
cd agentfit
|
|
200
|
+
|
|
201
|
+
# Install dev dependencies
|
|
202
|
+
pip install -e ".[dev]"
|
|
203
|
+
|
|
204
|
+
# Run tests
|
|
205
|
+
pytest
|
|
206
|
+
|
|
207
|
+
# Build package
|
|
208
|
+
python -m build
|
|
209
|
+
|
|
210
|
+
# Publish to PyPI
|
|
211
|
+
twine upload dist/*
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
Apache 2.0
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Configuration metrics aggregation from trials."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import statistics
|
|
6
|
+
from typing import Iterable
|
|
7
|
+
|
|
8
|
+
from agentfit.logging import get_logger
|
|
9
|
+
from agentfit.models import ConfigMetrics, Trial
|
|
10
|
+
from agentfit.analysis.statistics import wilson_ci
|
|
11
|
+
|
|
12
|
+
log = get_logger("analysis.metrics")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def compute_config_metrics(trials: Iterable[Trial]) -> ConfigMetrics:
|
|
16
|
+
"""Compute aggregated metrics for one agent configuration."""
|
|
17
|
+
trials = list(trials)
|
|
18
|
+
metrics = ConfigMetrics()
|
|
19
|
+
|
|
20
|
+
if not trials:
|
|
21
|
+
return metrics
|
|
22
|
+
|
|
23
|
+
solved = [t for t in trials if t.solved]
|
|
24
|
+
metrics.solved = len(solved)
|
|
25
|
+
metrics.total = len(trials)
|
|
26
|
+
metrics.pass_rate = len(solved) / len(trials)
|
|
27
|
+
|
|
28
|
+
# Wilson 95% CI
|
|
29
|
+
ci_lower, ci_upper = wilson_ci(len(solved), len(trials))
|
|
30
|
+
metrics.ci_lower = ci_lower
|
|
31
|
+
metrics.ci_upper = ci_upper
|
|
32
|
+
|
|
33
|
+
# Economics
|
|
34
|
+
costs = [t.cost_usd for t in trials if t.cost_usd is not None]
|
|
35
|
+
if costs:
|
|
36
|
+
metrics.total_cost = sum(costs)
|
|
37
|
+
metrics.mean_cost_task = metrics.total_cost / len(costs)
|
|
38
|
+
if metrics.solved > 0:
|
|
39
|
+
metrics.cost_per_solve = metrics.total_cost / metrics.solved
|
|
40
|
+
|
|
41
|
+
# Efficiency
|
|
42
|
+
prompt_tokens = [t.prompt_tokens for t in trials if t.prompt_tokens is not None]
|
|
43
|
+
completion_tokens = [t.completion_tokens for t in trials if t.completion_tokens is not None]
|
|
44
|
+
metrics.total_prompt_tokens = sum(prompt_tokens)
|
|
45
|
+
metrics.total_completion_tokens = sum(completion_tokens)
|
|
46
|
+
if metrics.solved > 0:
|
|
47
|
+
total_tokens = metrics.total_prompt_tokens + metrics.total_completion_tokens
|
|
48
|
+
metrics.tokens_per_solve = round(total_tokens / metrics.solved)
|
|
49
|
+
|
|
50
|
+
# Performance
|
|
51
|
+
durations = [t.duration_ms for t in trials if t.duration_ms is not None]
|
|
52
|
+
if durations:
|
|
53
|
+
metrics.p50_duration_ms = round(statistics.median(durations))
|
|
54
|
+
sorted_d = sorted(durations)
|
|
55
|
+
p90_idx = min(len(sorted_d) - 1, round(0.9 * (len(sorted_d) - 1)))
|
|
56
|
+
metrics.p90_duration_ms = sorted_d[p90_idx]
|
|
57
|
+
|
|
58
|
+
return metrics
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Cost-aware recommendation logic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
from agentfit.logging import get_logger
|
|
8
|
+
from agentfit.models import ConfigMetrics
|
|
9
|
+
|
|
10
|
+
log = get_logger("analysis.recommendation")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def recommend(
|
|
14
|
+
metrics_dict: dict[str, ConfigMetrics],
|
|
15
|
+
) -> tuple[str | None, str]:
|
|
16
|
+
"""Recommend a default agent configuration.
|
|
17
|
+
|
|
18
|
+
Policy (``cost_effective``):
|
|
19
|
+
1. Find the configuration with the highest observed pass rate.
|
|
20
|
+
2. Identify configurations whose quality difference is not
|
|
21
|
+
statistically conclusive (overlapping Wilson CIs).
|
|
22
|
+
3. Within that set, choose the lowest cost per verified solve.
|
|
23
|
+
|
|
24
|
+
Returns (config_name, reason).
|
|
25
|
+
"""
|
|
26
|
+
if not metrics_dict:
|
|
27
|
+
return None, "No configurations with results available."
|
|
28
|
+
|
|
29
|
+
# Consider only configs with trials
|
|
30
|
+
with_results = {name: m for name, m in metrics_dict.items() if m.total > 0}
|
|
31
|
+
if not with_results:
|
|
32
|
+
return None, "No configurations have completed trials."
|
|
33
|
+
|
|
34
|
+
# 1. Best observed pass rate
|
|
35
|
+
best_name = max(with_results, key=lambda n: with_results[n].pass_rate)
|
|
36
|
+
best = with_results[best_name]
|
|
37
|
+
|
|
38
|
+
# 2. Statistically indistinguishable set (overlapping Wilson CIs)
|
|
39
|
+
indistinguishable = []
|
|
40
|
+
for name, m in with_results.items():
|
|
41
|
+
if _cis_overlap(best, m):
|
|
42
|
+
indistinguishable.append(name)
|
|
43
|
+
|
|
44
|
+
# 3. Lowest cost per verified solve among indistinguishable
|
|
45
|
+
with_cost = [
|
|
46
|
+
(name, with_results[name])
|
|
47
|
+
for name in indistinguishable
|
|
48
|
+
if with_results[name].cost_per_solve is not None
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
if with_cost:
|
|
52
|
+
recommended = min(with_cost, key=lambda nm: nm[1].cost_per_solve)[0]
|
|
53
|
+
reason = (
|
|
54
|
+
f"lowest cost per verified solve among configurations "
|
|
55
|
+
f"statistically indistinguishable from observed best quality."
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
# No cost data: pick best observed pass rate
|
|
59
|
+
recommended = best_name
|
|
60
|
+
reason = "best observed pass rate (no cost data available)."
|
|
61
|
+
|
|
62
|
+
log.info("Recommendation: %s (%s)", recommended, reason)
|
|
63
|
+
return recommended, reason
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def pareto_frontier(
|
|
67
|
+
metrics_dict: dict[str, ConfigMetrics],
|
|
68
|
+
) -> list[str]:
|
|
69
|
+
"""Identify configurations on the Pareto frontier.
|
|
70
|
+
|
|
71
|
+
A configuration is dominated if another config has >= pass rate AND
|
|
72
|
+
<= cost per solve (with at least one strict inequality).
|
|
73
|
+
|
|
74
|
+
Returns config names on the frontier (highest quality first).
|
|
75
|
+
"""
|
|
76
|
+
with_cost = {
|
|
77
|
+
name: m for name, m in metrics_dict.items()
|
|
78
|
+
if m.total > 0 and m.cost_per_solve is not None
|
|
79
|
+
}
|
|
80
|
+
if not with_cost:
|
|
81
|
+
return list(metrics_dict.keys())
|
|
82
|
+
|
|
83
|
+
frontier: list[str] = []
|
|
84
|
+
for name, m in with_cost.items():
|
|
85
|
+
dominated = False
|
|
86
|
+
for other_name, other in with_cost.items():
|
|
87
|
+
if other_name == name:
|
|
88
|
+
continue
|
|
89
|
+
if (other.pass_rate >= m.pass_rate
|
|
90
|
+
and other.cost_per_solve <= m.cost_per_solve
|
|
91
|
+
and (other.pass_rate > m.pass_rate
|
|
92
|
+
or other.cost_per_solve < m.cost_per_solve)):
|
|
93
|
+
dominated = True
|
|
94
|
+
break
|
|
95
|
+
if not dominated:
|
|
96
|
+
frontier.append(name)
|
|
97
|
+
|
|
98
|
+
frontier.sort(key=lambda n: with_cost[n].pass_rate, reverse=True)
|
|
99
|
+
return frontier
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _cis_overlap(a: ConfigMetrics, b: ConfigMetrics) -> bool:
|
|
103
|
+
"""Check if two Wilson CIs overlap (statistically indistinguishable)."""
|
|
104
|
+
return not (a.ci_upper < b.ci_lower or b.ci_upper < a.ci_lower)
|