agent-mini 0.2.0__tar.gz → 0.3.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.
- {agent_mini-0.2.0 → agent_mini-0.3.0}/.gitignore +1 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/PKG-INFO +27 -5
- {agent_mini-0.2.0 → agent_mini-0.3.0}/README.md +26 -4
- agent_mini-0.3.0/evals/README.md +154 -0
- agent_mini-0.3.0/evals/fixtures/buggy_stack/pytest.ini +2 -0
- agent_mini-0.3.0/evals/fixtures/buggy_stack/src/stack.py +22 -0
- agent_mini-0.3.0/evals/fixtures/buggy_stack/tests/test_stack.py +24 -0
- agent_mini-0.3.0/evals/fixtures/py_project/pytest.ini +2 -0
- agent_mini-0.3.0/evals/fixtures/py_project/src/mymath.py +11 -0
- agent_mini-0.3.0/evals/fixtures/py_project/tests/test_mymath.py +16 -0
- agent_mini-0.3.0/evals/fixtures/small_repo/README.md +5 -0
- agent_mini-0.3.0/evals/fixtures/small_repo/src/config.py +2 -0
- agent_mini-0.3.0/evals/fixtures/small_repo/src/server.py +8 -0
- agent_mini-0.3.0/evals/run.py +512 -0
- agent_mini-0.3.0/evals/tasks/codebase_qa.yaml +14 -0
- agent_mini-0.3.0/evals/tasks/error_recovery.yaml +13 -0
- agent_mini-0.3.0/evals/tasks/fix_failing_test.yaml +14 -0
- agent_mini-0.3.0/evals/tasks/list_directory_basic.yaml +14 -0
- agent_mini-0.3.0/evals/tasks/multi_file_edit.yaml +16 -0
- agent_mini-0.3.0/evals/tasks/refactor_rename.yaml +15 -0
- agent_mini-0.3.0/evals/tasks/search_and_summarize.yaml +14 -0
- agent_mini-0.3.0/evals/tasks/shell_pipeline.yaml +14 -0
- agent_mini-0.3.0/evals/tasks/write_file_from_scratch.yaml +15 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/pyproject.toml +1 -1
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/__init__.py +1 -1
- agent_mini-0.3.0/src/agent_mini/agent/context.py +112 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/agent/loop.py +35 -31
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/agent/token_estimator.py +11 -4
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/agent/tools.py +66 -3
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/channels/telegram.py +10 -9
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/cli.py +22 -1
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/providers/local.py +4 -1
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/providers/ollama.py +34 -3
- agent_mini-0.3.0/tests/test_context.py +120 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_loop.py +24 -0
- agent_mini-0.3.0/tests/test_providers.py +86 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_token_estimator.py +4 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_tools.py +41 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/uv.lock +1 -1
- agent_mini-0.2.0/src/agent_mini/agent/context.py +0 -48
- agent_mini-0.2.0/tests/test_context.py +0 -66
- {agent_mini-0.2.0 → agent_mini-0.3.0}/.github/workflows/ci.yml +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/.github/workflows/publish.yml +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/CONTRIBUTING.md +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/LICENSE +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/SECURITY.md +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/config.example.json +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/__main__.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/agent/__init__.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/agent/memory.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/agent/vision.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/bus.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/channels/__init__.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/channels/base.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/config.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/providers/__init__.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/providers/base.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/providers/openai.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/src/agent_mini/sessions.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/__init__.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_config.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_json_repair.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_memory.py +0 -0
- {agent_mini-0.2.0 → agent_mini-0.3.0}/tests/test_phases.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-mini
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Ultra-lightweight personal AI agent — shell, files, web, memory — powered by local or cloud LLMs
|
|
5
5
|
Project-URL: Homepage, https://github.com/mohsinkaleem/agent-mini
|
|
6
6
|
Project-URL: Repository, https://github.com/mohsinkaleem/agent-mini
|
|
@@ -46,8 +46,9 @@ A minimal, local-first AI agent you can actually understand and extend.
|
|
|
46
46
|
- **Local-first** — Ollama as default, OpenAI if you want cloud, or any OpenAI-compatible server
|
|
47
47
|
- **Zero frameworks** — pure `httpx` + `asyncio`, no LangChain, no LiteLLM
|
|
48
48
|
- **Built-in tools** — shell, files, web search, persistent memory
|
|
49
|
+
- **Vision** — drop an image path or URL into any message; works on Ollama, OpenAI, and OpenAI-compatible providers
|
|
49
50
|
- **Extensible** — drop a Python file in `~/.agent-mini/plugins/` and it's a tool
|
|
50
|
-
- **Small-model optimized** — token-aware context pruning, tool
|
|
51
|
+
- **Small-model optimized** — tier-scaled system prompt, token-aware context pruning, tool-call repair, and a [task-eval harness](evals/) to measure whether the tuning actually pays off
|
|
51
52
|
|
|
52
53
|
## Quick Start
|
|
53
54
|
|
|
@@ -133,7 +134,7 @@ Available out of the box — no API keys needed:
|
|
|
133
134
|
| `list_directory` | Browse filesystem |
|
|
134
135
|
| `search_files` | Grep / ripgrep across files |
|
|
135
136
|
| `web_search` | DuckDuckGo search (free, no key) |
|
|
136
|
-
| `web_fetch` | Fetch
|
|
137
|
+
| `web_fetch` | Fetch a public URL as plain text (private/loopback hosts blocked) |
|
|
137
138
|
| `memory_store` | Save to persistent memory |
|
|
138
139
|
| `memory_recall` | Fuzzy search memory (TF-IDF) |
|
|
139
140
|
|
|
@@ -214,7 +215,9 @@ Control tool access:
|
|
|
214
215
|
{ "tools": { "sandboxLevel": "readonly" } }
|
|
215
216
|
```
|
|
216
217
|
|
|
217
|
-
Dangerous shell commands (`rm -rf`, `sudo`, `mkfs`, etc.) are blocked by default.
|
|
218
|
+
Dangerous shell commands (`rm -rf`, `sudo`, `mkfs`, etc.) are blocked by default. Treat the shell blocklist as **friction, not a boundary** — a determined caller can bypass it, so don't run the agent in an untrusted context and expect the blocklist to save you.
|
|
219
|
+
|
|
220
|
+
`web_fetch` is guarded against basic SSRF: requests to `localhost`, loopback (`::1`), RFC-1918 private ranges, and link-local (including the cloud metadata endpoint `169.254.169.254`) are refused, and the final URL is re-checked after every redirect. DNS rebinding can still bypass — use `sandboxLevel: readonly` if you need a stronger guarantee.
|
|
218
221
|
|
|
219
222
|
---
|
|
220
223
|
|
|
@@ -236,11 +239,14 @@ Agent Mini is a **ReAct loop** — the LLM reasons, picks a tool, observes the r
|
|
|
236
239
|
|
|
237
240
|
Key design choices for small/local models:
|
|
238
241
|
|
|
242
|
+
- **Tier-scaled system prompt** — tiny models get a compact rules block and no memory recall; larger tiers get the full descriptive prompt and recent context
|
|
243
|
+
- **Inline tool list** — `<available_tools>` block in the system prompt so small models can see tool names at a glance without inferring from the JSON schema
|
|
239
244
|
- **Token-aware context** — estimates token usage and prunes old tool results when approaching the model's effective context window
|
|
240
|
-
- **Model tier classification** — auto-detects tiny/small/medium/cloud
|
|
245
|
+
- **Model tier classification** — auto-detects tiny/small/medium/cloud (including large open-weight sizes like `:32b`, `:70b`, `8x7b`) and adjusts context budgets, iteration limits, and output caps
|
|
241
246
|
- **Tool call repair** — fixes malformed JSON from small models (trailing commas, single quotes, unquoted keys)
|
|
242
247
|
- **Loop detection** — catches repeated identical tool calls and nudges the LLM to try a different approach
|
|
243
248
|
- **History summarization** — compresses long conversations to stay within context
|
|
249
|
+
- **[Task-eval harness](evals/)** — measures success rate, iterations, tokens, and JSON-repair fire-rate across model tiers so the tuning above is testable, not hand-waved
|
|
244
250
|
|
|
245
251
|
---
|
|
246
252
|
|
|
@@ -284,6 +290,7 @@ Key paths:
|
|
|
284
290
|
| `agent-mini init` | Interactive setup wizard |
|
|
285
291
|
| `agent-mini chat` | Interactive chat |
|
|
286
292
|
| `agent-mini chat -m "..."` | Single message |
|
|
293
|
+
| `agent-mini chat --workspace <dir>` | Override the workspace for a single run (also honours `AGENT_MINI_WORKSPACE`) |
|
|
287
294
|
| `agent-mini gateway` | Start Telegram bot |
|
|
288
295
|
| `agent-mini status` | Show config status |
|
|
289
296
|
|
|
@@ -324,6 +331,21 @@ uv run pytest tests/ -v
|
|
|
324
331
|
uv run ruff check src/ tests/
|
|
325
332
|
```
|
|
326
333
|
|
|
334
|
+
### Task evals
|
|
335
|
+
|
|
336
|
+
The [`evals/`](evals/) directory contains a small, framework-free task-eval
|
|
337
|
+
harness that runs the agent end-to-end against real fixtures (refactor a
|
|
338
|
+
codebase, fix a failing test, answer a codebase question, etc.). Use it to
|
|
339
|
+
measure whether the small-model optimizations actually pay off:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
python evals/run.py # run all tasks against config model
|
|
343
|
+
python evals/run.py --task refactor_rename # single task
|
|
344
|
+
python evals/run.py --compare results/*.json # cross-tier comparison table
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
See [evals/README.md](evals/README.md) for the full workflow.
|
|
348
|
+
|
|
327
349
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
328
350
|
|
|
329
351
|
## License
|
|
@@ -11,8 +11,9 @@ A minimal, local-first AI agent you can actually understand and extend.
|
|
|
11
11
|
- **Local-first** — Ollama as default, OpenAI if you want cloud, or any OpenAI-compatible server
|
|
12
12
|
- **Zero frameworks** — pure `httpx` + `asyncio`, no LangChain, no LiteLLM
|
|
13
13
|
- **Built-in tools** — shell, files, web search, persistent memory
|
|
14
|
+
- **Vision** — drop an image path or URL into any message; works on Ollama, OpenAI, and OpenAI-compatible providers
|
|
14
15
|
- **Extensible** — drop a Python file in `~/.agent-mini/plugins/` and it's a tool
|
|
15
|
-
- **Small-model optimized** — token-aware context pruning, tool
|
|
16
|
+
- **Small-model optimized** — tier-scaled system prompt, token-aware context pruning, tool-call repair, and a [task-eval harness](evals/) to measure whether the tuning actually pays off
|
|
16
17
|
|
|
17
18
|
## Quick Start
|
|
18
19
|
|
|
@@ -98,7 +99,7 @@ Available out of the box — no API keys needed:
|
|
|
98
99
|
| `list_directory` | Browse filesystem |
|
|
99
100
|
| `search_files` | Grep / ripgrep across files |
|
|
100
101
|
| `web_search` | DuckDuckGo search (free, no key) |
|
|
101
|
-
| `web_fetch` | Fetch
|
|
102
|
+
| `web_fetch` | Fetch a public URL as plain text (private/loopback hosts blocked) |
|
|
102
103
|
| `memory_store` | Save to persistent memory |
|
|
103
104
|
| `memory_recall` | Fuzzy search memory (TF-IDF) |
|
|
104
105
|
|
|
@@ -179,7 +180,9 @@ Control tool access:
|
|
|
179
180
|
{ "tools": { "sandboxLevel": "readonly" } }
|
|
180
181
|
```
|
|
181
182
|
|
|
182
|
-
Dangerous shell commands (`rm -rf`, `sudo`, `mkfs`, etc.) are blocked by default.
|
|
183
|
+
Dangerous shell commands (`rm -rf`, `sudo`, `mkfs`, etc.) are blocked by default. Treat the shell blocklist as **friction, not a boundary** — a determined caller can bypass it, so don't run the agent in an untrusted context and expect the blocklist to save you.
|
|
184
|
+
|
|
185
|
+
`web_fetch` is guarded against basic SSRF: requests to `localhost`, loopback (`::1`), RFC-1918 private ranges, and link-local (including the cloud metadata endpoint `169.254.169.254`) are refused, and the final URL is re-checked after every redirect. DNS rebinding can still bypass — use `sandboxLevel: readonly` if you need a stronger guarantee.
|
|
183
186
|
|
|
184
187
|
---
|
|
185
188
|
|
|
@@ -201,11 +204,14 @@ Agent Mini is a **ReAct loop** — the LLM reasons, picks a tool, observes the r
|
|
|
201
204
|
|
|
202
205
|
Key design choices for small/local models:
|
|
203
206
|
|
|
207
|
+
- **Tier-scaled system prompt** — tiny models get a compact rules block and no memory recall; larger tiers get the full descriptive prompt and recent context
|
|
208
|
+
- **Inline tool list** — `<available_tools>` block in the system prompt so small models can see tool names at a glance without inferring from the JSON schema
|
|
204
209
|
- **Token-aware context** — estimates token usage and prunes old tool results when approaching the model's effective context window
|
|
205
|
-
- **Model tier classification** — auto-detects tiny/small/medium/cloud
|
|
210
|
+
- **Model tier classification** — auto-detects tiny/small/medium/cloud (including large open-weight sizes like `:32b`, `:70b`, `8x7b`) and adjusts context budgets, iteration limits, and output caps
|
|
206
211
|
- **Tool call repair** — fixes malformed JSON from small models (trailing commas, single quotes, unquoted keys)
|
|
207
212
|
- **Loop detection** — catches repeated identical tool calls and nudges the LLM to try a different approach
|
|
208
213
|
- **History summarization** — compresses long conversations to stay within context
|
|
214
|
+
- **[Task-eval harness](evals/)** — measures success rate, iterations, tokens, and JSON-repair fire-rate across model tiers so the tuning above is testable, not hand-waved
|
|
209
215
|
|
|
210
216
|
---
|
|
211
217
|
|
|
@@ -249,6 +255,7 @@ Key paths:
|
|
|
249
255
|
| `agent-mini init` | Interactive setup wizard |
|
|
250
256
|
| `agent-mini chat` | Interactive chat |
|
|
251
257
|
| `agent-mini chat -m "..."` | Single message |
|
|
258
|
+
| `agent-mini chat --workspace <dir>` | Override the workspace for a single run (also honours `AGENT_MINI_WORKSPACE`) |
|
|
252
259
|
| `agent-mini gateway` | Start Telegram bot |
|
|
253
260
|
| `agent-mini status` | Show config status |
|
|
254
261
|
|
|
@@ -289,6 +296,21 @@ uv run pytest tests/ -v
|
|
|
289
296
|
uv run ruff check src/ tests/
|
|
290
297
|
```
|
|
291
298
|
|
|
299
|
+
### Task evals
|
|
300
|
+
|
|
301
|
+
The [`evals/`](evals/) directory contains a small, framework-free task-eval
|
|
302
|
+
harness that runs the agent end-to-end against real fixtures (refactor a
|
|
303
|
+
codebase, fix a failing test, answer a codebase question, etc.). Use it to
|
|
304
|
+
measure whether the small-model optimizations actually pay off:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
python evals/run.py # run all tasks against config model
|
|
308
|
+
python evals/run.py --task refactor_rename # single task
|
|
309
|
+
python evals/run.py --compare results/*.json # cross-tier comparison table
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
See [evals/README.md](evals/README.md) for the full workflow.
|
|
313
|
+
|
|
292
314
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
293
315
|
|
|
294
316
|
## License
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Agent Mini — Task Evals
|
|
2
|
+
|
|
3
|
+
A tiny, framework-free harness that measures whether the agent can complete
|
|
4
|
+
**real-world tasks** end-to-end, not just pass unit tests. This is where the
|
|
5
|
+
"small-model optimized" claim gets tested for real.
|
|
6
|
+
|
|
7
|
+
Zero new dependencies. Uses only the standard library + `agent-mini` itself.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# 1. Have Ollama (or any provider) already running with a model
|
|
15
|
+
ollama pull llama3.1:8b
|
|
16
|
+
|
|
17
|
+
# 2. Point agent-mini at it (only needed once)
|
|
18
|
+
agent-mini init
|
|
19
|
+
|
|
20
|
+
# 3. Run the whole suite
|
|
21
|
+
python evals/run.py
|
|
22
|
+
|
|
23
|
+
# 4. Run against a specific model
|
|
24
|
+
python evals/run.py --model llama3.2:3b
|
|
25
|
+
|
|
26
|
+
# 5. Run a single task
|
|
27
|
+
python evals/run.py --task refactor_rename
|
|
28
|
+
|
|
29
|
+
# 6. Compare tiers (the thesis test)
|
|
30
|
+
python evals/run.py --model llama3.2:3b --out results/tiny.json
|
|
31
|
+
python evals/run.py --model llama3.1:8b --out results/small.json
|
|
32
|
+
python evals/run.py --model qwen2.5:14b --out results/medium.json
|
|
33
|
+
python evals/run.py --compare results/*.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Results are printed as a table and written to `results/<model>_<timestamp>.json`
|
|
37
|
+
(one line per task, so you can `jq` / `grep` them).
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Latest results — qwen3.5 tiers (Ollama, 2026-07-28)
|
|
42
|
+
|
|
43
|
+
Local M-series Mac, `think: false`, default agent config (`maxIterations: 20`).
|
|
44
|
+
|
|
45
|
+
| Task | qwen3.5:4b | qwen3.5:9b | qwen3.5:27b |
|
|
46
|
+
|----------------------------|--------------|--------------|---------------|
|
|
47
|
+
| codebase_qa | PASS · 18.0s | PASS · 23.8s | PASS · 81.5s |
|
|
48
|
+
| error_recovery | PASS · 8.7s | PASS · 19.5s | PASS · 50.3s |
|
|
49
|
+
| fix_failing_test | PASS · 17.9s | PASS · 24.9s | PASS · 85.3s |
|
|
50
|
+
| list_directory_basic | FAIL · 8.3s | PASS · 14.0s | PASS · 47.5s |
|
|
51
|
+
| multi_file_edit | PASS · 9.5s | PASS · 18.3s | PASS · 78.4s |
|
|
52
|
+
| refactor_rename | FAIL · 10.1s | FAIL · 61.0s | PASS · 184.9s |
|
|
53
|
+
| search_and_summarize | PASS · 6.2s | PASS · 23.2s | PASS · 32.0s |
|
|
54
|
+
| shell_pipeline | FAIL · 12.6s | PASS · 32.2s | FAIL · 120.0s (timeout) |
|
|
55
|
+
| write_file_from_scratch | FAIL · 11.1s | FAIL · 15.7s | PASS · 34.7s |
|
|
56
|
+
| **Pass rate** | **5/9** | **7/9** | **8/9** |
|
|
57
|
+
|
|
58
|
+
Notes:
|
|
59
|
+
|
|
60
|
+
- `qwen3.5:9b` is the sweet spot on this suite
|
|
61
|
+
`shell_pipeline` and finishing every task well under the timeout.
|
|
62
|
+
- `write_file_from_scratch` also fails everywhere: models emit a `print`
|
|
63
|
+
/ `__main__` block despite the prompt forbidding it. Good candidate for
|
|
64
|
+
a small-model instruction-tuning improvement.
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Layout
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
evals/
|
|
71
|
+
├── README.md # this file
|
|
72
|
+
├── run.py # ~200 lines: copy fixture → run agent headless → score
|
|
73
|
+
├── tasks/*.yaml # task fixtures — prompt + programmatic check
|
|
74
|
+
└── fixtures/ # tiny sample repos used by tasks
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Each task is a self-contained YAML file:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
id: refactor_rename
|
|
81
|
+
category: refactor
|
|
82
|
+
setup: fixtures/py_project
|
|
83
|
+
prompt: |
|
|
84
|
+
Rename the function `calc` to `compute` across all .py files in this
|
|
85
|
+
workspace and make sure the tests still pass.
|
|
86
|
+
timeout_seconds: 180
|
|
87
|
+
checks:
|
|
88
|
+
- shell: "! grep -rq 'def calc(' ."
|
|
89
|
+
description: "No `def calc` remains"
|
|
90
|
+
- shell: "grep -rq 'def compute(' ."
|
|
91
|
+
description: "`def compute` appears somewhere"
|
|
92
|
+
- shell: "python -m pytest -q"
|
|
93
|
+
description: "Tests still pass"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Add a task: drop a YAML file in `tasks/` and (optionally) a matching
|
|
97
|
+
fixture directory in `fixtures/`. The runner picks it up on next run.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Task categories (the surface we actually care about)
|
|
102
|
+
|
|
103
|
+
| Category | Exercises | Task ID |
|
|
104
|
+
|----------|-----------|---------|
|
|
105
|
+
| File ops | `write_file`, `code_edit`, multi-file rename | `refactor_rename` |
|
|
106
|
+
| Codebase Q&A | `search_files` → `read_file` → summarize | `codebase_qa` |
|
|
107
|
+
| Multi-step | write, run, verify a fix | `fix_failing_test` |
|
|
108
|
+
| Error recovery | retry a different way after failure | `error_recovery` |
|
|
109
|
+
| Memory | store fact turn 1, recall turn 3 (uses `--multi-turn`) | `memory_recall` |
|
|
110
|
+
| Search | grep + read | `search_and_summarize` |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Metrics (per model)
|
|
115
|
+
|
|
116
|
+
The runner tabulates:
|
|
117
|
+
|
|
118
|
+
| Metric | Why it matters |
|
|
119
|
+
|--------|----------------|
|
|
120
|
+
| Success rate | Core efficacy. |
|
|
121
|
+
| Median iterations | Loop efficiency. Wandering = bad. |
|
|
122
|
+
| JSON-repair fire rate | Is small-model tool-call repair earning its keep? |
|
|
123
|
+
| Summarize trigger rate | Is context management engaging when needed? |
|
|
124
|
+
| Loop-detection hits | Are small models stalling? |
|
|
125
|
+
| Total tokens / wall time | Cost. |
|
|
126
|
+
|
|
127
|
+
The **thesis test**: run the same suite across `tiny` / `small` / `medium` /
|
|
128
|
+
`cloud` tiers. If tuned `3B` ≈ untuned `8B`, the optimization is proven. If
|
|
129
|
+
not, it's theater — and this harness is exactly what surfaces that.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Design constraints
|
|
134
|
+
|
|
135
|
+
- **No new dependencies.** stdlib only (`subprocess`, `shutil`, `tempfile`,
|
|
136
|
+
`json`, `re`, `argparse`). YAML is parsed by a tiny in-file loader — the
|
|
137
|
+
task schema is intentionally flat enough not to need PyYAML.
|
|
138
|
+
- **No mocking.** Real provider, real tools, real filesystem. Fixtures are
|
|
139
|
+
copied to a fresh `tempfile.mkdtemp()` per task, so runs don't leak.
|
|
140
|
+
- **Zero framework bloat.** ~200 LOC in `run.py`. Read it in five minutes.
|
|
141
|
+
- **CI-friendly.** Gate as nightly against a live model; keep unit tests as
|
|
142
|
+
the fast job. Model latency should not block PRs.
|
|
143
|
+
|
|
144
|
+
## Adding a task
|
|
145
|
+
|
|
146
|
+
1. Create `evals/fixtures/my_fixture/` with a minimal set of files.
|
|
147
|
+
2. Create `evals/tasks/my_task.yaml`:
|
|
148
|
+
- `id`: matches the file stem.
|
|
149
|
+
- `setup`: path to the fixture (relative to `evals/`).
|
|
150
|
+
- `prompt`: what to ask the agent.
|
|
151
|
+
- `checks`: shell one-liners that must exit `0`.
|
|
152
|
+
3. Verify: `python evals/run.py --task my_task`.
|
|
153
|
+
|
|
154
|
+
Each bug we fix should get a new task. The corpus grows organically.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""A buggy stack implementation. The `pop` method is off-by-one — the eval
|
|
2
|
+
asks the agent to find and fix the bug so the tests pass."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Stack:
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self._items = []
|
|
8
|
+
|
|
9
|
+
def push(self, x):
|
|
10
|
+
self._items.append(x)
|
|
11
|
+
|
|
12
|
+
def pop(self):
|
|
13
|
+
if not self._items:
|
|
14
|
+
raise IndexError("pop from empty stack")
|
|
15
|
+
# BUG: returns the wrong end of the list
|
|
16
|
+
return self._items.pop(0)
|
|
17
|
+
|
|
18
|
+
def peek(self):
|
|
19
|
+
return self._items[-1]
|
|
20
|
+
|
|
21
|
+
def __len__(self):
|
|
22
|
+
return len(self._items)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
5
|
+
|
|
6
|
+
from stack import Stack # noqa: E402
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_push_pop_is_lifo():
|
|
10
|
+
s = Stack()
|
|
11
|
+
s.push(1)
|
|
12
|
+
s.push(2)
|
|
13
|
+
s.push(3)
|
|
14
|
+
assert s.pop() == 3
|
|
15
|
+
assert s.pop() == 2
|
|
16
|
+
assert s.pop() == 1
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_peek_does_not_mutate():
|
|
20
|
+
s = Stack()
|
|
21
|
+
s.push("a")
|
|
22
|
+
s.push("b")
|
|
23
|
+
assert s.peek() == "b"
|
|
24
|
+
assert len(s) == 2
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""A tiny arithmetic helper — used by the eval fixture."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def calc(a: int, b: int) -> int:
|
|
5
|
+
"""Add two ints. The eval will ask the agent to rename this."""
|
|
6
|
+
return a + b
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def double_calc(x: int) -> int:
|
|
10
|
+
"""Uses `calc` — the rename must update the call site too."""
|
|
11
|
+
return calc(x, x)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Tests for mymath — must still pass after the rename."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
7
|
+
|
|
8
|
+
from mymath import calc, double_calc # noqa: E402
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_calc_adds():
|
|
12
|
+
assert calc(2, 3) == 5
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_double_uses_calc():
|
|
16
|
+
assert double_calc(4) == 8
|