Publish README.md from marimo
Browse files
README.md
CHANGED
|
@@ -24,7 +24,9 @@ Python standard library.
|
|
| 24 |
|
| 25 |
> **Status 0.3.0: it drives a real desktop — on Linux and on Windows.** Screen capture,
|
| 26 |
> an OCR element index with real coordinates, a real mouse click, and a *verified*
|
| 27 |
-
> outcome.
|
|
|
|
|
|
|
| 28 |
> 2022 guest; what is not solved is model reliability — see
|
| 29 |
> [measured results](#measured-results).
|
| 30 |
|
|
@@ -84,7 +86,7 @@ X server. First the suite, headless:
|
|
| 84 |
```bash
|
| 85 |
python -m pip install -e ".[desktop,ocr,dev]"
|
| 86 |
pytest tests -q
|
| 87 |
-
#
|
| 88 |
```
|
| 89 |
|
| 90 |
The two skips are the live-desktop tests. Give them a display and they run for real:
|
|
@@ -102,7 +104,8 @@ Then the same desktop, driven through the CLI with the mouse actually live:
|
|
| 102 |
|
| 103 |
```bash
|
| 104 |
export TINY_COMPUTER_AGENT_ALLOW_REAL_INPUT=1
|
| 105 |
-
python -m tiny_computer_agent --live --real-input --verify-text Saved
|
|
|
|
| 106 |
```
|
| 107 |
|
| 108 |
```
|
|
@@ -153,6 +156,37 @@ named the right target:
|
|
| 153 |
step 0 | click label='Save' reason="the reply names 'Save'" -> pixel (295, 284)
|
| 154 |
```
|
| 155 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
### Which brain drove which run
|
| 157 |
|
| 158 |
The harness and the brain are separable, and it matters which one a result came from.
|
|
@@ -184,7 +218,7 @@ open problem: it is reliable on a clean screenshot and unreliable on a busy live
|
|
| 184 |
`warning: no text was found on screen, so the index is 96 numbered grid cells and the
|
| 185 |
agent is choosing blind` — instead of silently guessing.
|
| 186 |
|
| 187 |
-
**
|
| 188 |
that click a real button.
|
| 189 |
|
| 190 |
## Verified on real Windows
|
|
@@ -210,7 +244,7 @@ desktop was up a few minutes after that.
|
|
| 210 |
**What was installed inside it.**
|
| 211 |
|
| 212 |
```
|
| 213 |
-
Python 3.12.8 (embeddable distribution, unpacked to C:
|
| 214 |
pytest, pillow 13.0.0, mss 10.2.0
|
| 215 |
numpy 2.5.4, onnxruntime 1.31.1, opencv-python 5.0.0.93, rapidocr-onnxruntime 1.4.4
|
| 216 |
the harness itself, installed into site-packages
|
|
@@ -226,7 +260,7 @@ the harness itself, installed into site-packages
|
|
| 226 |
|
| 227 |
None of the three failures in the first row is a harness defect: two are
|
| 228 |
`ModuleNotFoundError: numpy` and the third is the guard bug documented below. With the
|
| 229 |
-
OCR engine installed, the same suite exits **0**.
|
| 230 |
|
| 231 |
**The emulated-OCR caveat.** onnxruntime requires AVX2, and QEMU's software emulation of
|
| 232 |
AVX2 is roughly two orders of magnitude slower than native. A suite that finishes in 17
|
|
@@ -252,7 +286,7 @@ exactly the kind of thing that only surfaces on a machine without an OCR engine.
|
|
| 252 |
|
| 253 |
## The test suite, in full
|
| 254 |
|
| 255 |
-
`pytest tests` collects **
|
| 256 |
weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
| 257 |
`TINY_COMPUTER_AGENT_LIVE_TEST=1`.
|
| 258 |
|
|
@@ -269,11 +303,12 @@ weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
|
| 269 |
| `tests/test_grid_fallback.py` | 11 | the fallback index announces itself instead of guessing |
|
| 270 |
| `tests/test_desktop.py` | 10 | display detection, the preflight checks, `doctor` |
|
| 271 |
| `tests/test_cli.py` | 10 | the command line: `--doctor`, `--image`, `--live`, argument handling |
|
|
|
|
| 272 |
| `tests/test_live_desktop.py` | 2 | **needs a display, opt-in**: OCR finds a real button at the pixels it was drawn at, and a real click makes the app react |
|
| 273 |
|
| 274 |
```bash
|
| 275 |
-
pytest tests -q #
|
| 276 |
-
TINY_COMPUTER_AGENT_LIVE_TEST=1 pytest tests -q #
|
| 277 |
```
|
| 278 |
|
| 279 |
## Install
|
|
@@ -282,7 +317,7 @@ TINY_COMPUTER_AGENT_LIVE_TEST=1 pytest tests -q # 125 passed, on a desktop
|
|
| 282 |
pip install -e ".[ocr]" # labels from pixels (RapidOCR: no system binary)
|
| 283 |
pip install -e ".[desktop,ocr]" # + real capture and real input
|
| 284 |
pip install -e ".[vision,ocr]" # + the tiny model brains
|
| 285 |
-
pytest #
|
| 286 |
```
|
| 287 |
|
| 288 |
## Run it on your machine
|
|
@@ -346,11 +381,20 @@ demands:
|
|
| 346 |
```bash
|
| 347 |
# 1. install media, plus an answer file on removable media
|
| 348 |
qemu-img create -f qcow2 win.qcow2 64G
|
| 349 |
-
qemu-system-x86_64 -accel tcg,thread=multi -m 8G -smp 8 -machine pc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
|
| 351 |
# 2. inside the guest, after autologon
|
| 352 |
# vc_redist.x64.exe is required before onnxruntime will load
|
| 353 |
-
curl.exe -s -o C:
|
| 354 |
```
|
| 355 |
|
| 356 |
Serve files to the guest over QEMU's user-mode networking (the host is `10.0.2.2`), which
|
|
@@ -374,6 +418,7 @@ this: an answer file with non-canonical XML namespaces is **silently ignored** b
|
|
| 374 |
| `loop.py` | `AgentLoop`, `Episode`, `Step`, the no-progress guard and the verifier |
|
| 375 |
| `desktop.py` | Preflight checks, `doctor`, and `build_agent` in one call |
|
| 376 |
| `demo.py` | The synthetic desktop used by tests and `--image example` |
|
|
|
|
| 377 |
|
| 378 |
## Why a tiny model can do this at all
|
| 379 |
|
|
@@ -406,7 +451,7 @@ success.
|
|
| 406 |
|
| 407 |
**Works:** real capture, a real OCR index with coordinates that land on the right pixel,
|
| 408 |
a real click an app reacts to, verified success, the closed vocabulary, the policy gates,
|
| 409 |
-
the no-progress guard, echo detection, the doctor, the CLI,
|
| 410 |
reads a button label from pixels on a clean screenshot — and the same suite passing inside
|
| 411 |
a real Windows Server 2022 guest with no KVM (109 passed, exit 0, with the OCR-inference
|
| 412 |
tests excluded as impractically slow under emulation).
|
|
@@ -427,7 +472,7 @@ when no verifier is given.
|
|
| 427 |
- [ ] Make the OCR guard in `test_cli.py` skip, rather than fail, when no engine is installed
|
| 428 |
- [ ] Make the VLM reliable on a live screen (it echoes the prompt instead of choosing)
|
| 429 |
- [ ] Accessibility-tree detector (AT-SPI on Linux, UI Automation on Windows)
|
| 430 |
-
- [
|
| 431 |
- [ ] marimo console: live screen preview, action log, pause / resume / step
|
| 432 |
|
| 433 |
## License
|
|
|
|
| 24 |
|
| 25 |
> **Status 0.3.0: it drives a real desktop — on Linux and on Windows.** Screen capture,
|
| 26 |
> an OCR element index with real coordinates, a real mouse click, and a *verified*
|
| 27 |
+
> outcome. A run can be saved as a portable *trace*, replayed offline and **scored**, so
|
| 28 |
+
> "which brain is right, and how often?" is a number in a table.
|
| 29 |
+
> The suite passes on a live Linux desktop and inside a real Windows Server
|
| 30 |
> 2022 guest; what is not solved is model reliability — see
|
| 31 |
> [measured results](#measured-results).
|
| 32 |
|
|
|
|
| 86 |
```bash
|
| 87 |
python -m pip install -e ".[desktop,ocr,dev]"
|
| 88 |
pytest tests -q
|
| 89 |
+
# 151 passed, 2 skipped in 18.7s
|
| 90 |
```
|
| 91 |
|
| 92 |
The two skips are the live-desktop tests. Give them a display and they run for real:
|
|
|
|
| 104 |
|
| 105 |
```bash
|
| 106 |
export TINY_COMPUTER_AGENT_ALLOW_REAL_INPUT=1
|
| 107 |
+
python -m tiny_computer_agent --live --real-input --verify-text Saved \
|
| 108 |
+
"click 'Save' to save the document"
|
| 109 |
```
|
| 110 |
|
| 111 |
```
|
|
|
|
| 156 |
step 0 | click label='Save' reason="the reply names 'Save'" -> pixel (295, 284)
|
| 157 |
```
|
| 158 |
|
| 159 |
+
### Scoring a brain, offline
|
| 160 |
+
|
| 161 |
+
"Which brain is right, and how often?" needs no desktop, no model and no network,
|
| 162 |
+
because a **trace** keeps every frame's element index and throws the pixels away. Record a
|
| 163 |
+
run once; replay it against any brain, as often as you like.
|
| 164 |
+
|
| 165 |
+
```
|
| 166 |
+
$ python -m tiny_computer_agent --image example --trace runs/save.json --expected Save
|
| 167 |
+
...
|
| 168 |
+
trace: wrote runs/save.json (2 frames)
|
| 169 |
+
score: correct click 'Save' to save the document: expected 'Save', chose 'Save'
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
A trace is JSON, so it diffs in review, commits next to the tests, and replays on a machine
|
| 173 |
+
with nothing installed. Scoring a whole suite is a table:
|
| 174 |
+
|
| 175 |
+
```
|
| 176 |
+
scoreboard: 7/9 correct (78%)
|
| 177 |
+
planner (rule-based): 100%
|
| 178 |
+
first-item (baseline): 33%
|
| 179 |
+
oracle (always right): 100%
|
| 180 |
+
correct save the document: expected 'Save', chose 'Save'
|
| 181 |
+
incorrect save the document: expected 'Save', chose 'Files' (chose 'Files', not 'Save')
|
| 182 |
+
...
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
The number is the brain's **decision** - what it chose when it had to commit. Whether the
|
| 186 |
+
harness *allowed* that decision to run is a separate axis: a click the policy refused is
|
| 187 |
+
still the choice it was, and it is flagged `(harness refused it)` rather than passed off as
|
| 188 |
+
a clean success. Keeping those two apart is the point.
|
| 189 |
+
|
| 190 |
### Which brain drove which run
|
| 191 |
|
| 192 |
The harness and the brain are separable, and it matters which one a result came from.
|
|
|
|
| 218 |
`warning: no text was found on screen, so the index is 96 numbered grid cells and the
|
| 219 |
agent is choosing blind` — instead of silently guessing.
|
| 220 |
|
| 221 |
+
**153 tests pass**, including real OCR over a real image and (opt-in) live-desktop tests
|
| 222 |
that click a real button.
|
| 223 |
|
| 224 |
## Verified on real Windows
|
|
|
|
| 244 |
**What was installed inside it.**
|
| 245 |
|
| 246 |
```
|
| 247 |
+
Python 3.12.8 (embeddable distribution, unpacked to C:\tiny\py)
|
| 248 |
pytest, pillow 13.0.0, mss 10.2.0
|
| 249 |
numpy 2.5.4, onnxruntime 1.31.1, opencv-python 5.0.0.93, rapidocr-onnxruntime 1.4.4
|
| 250 |
the harness itself, installed into site-packages
|
|
|
|
| 260 |
|
| 261 |
None of the three failures in the first row is a harness defect: two are
|
| 262 |
`ModuleNotFoundError: numpy` and the third is the guard bug documented below. With the
|
| 263 |
+
OCR engine installed, the same suite exits **0**. (Those Windows counts predate `trace.py`; the harness suite has since grown to 153 tests.)
|
| 264 |
|
| 265 |
**The emulated-OCR caveat.** onnxruntime requires AVX2, and QEMU's software emulation of
|
| 266 |
AVX2 is roughly two orders of magnitude slower than native. A suite that finishes in 17
|
|
|
|
| 286 |
|
| 287 |
## The test suite, in full
|
| 288 |
|
| 289 |
+
`pytest tests` collects **153 tests**. 151 run headless — no display, no network, no
|
| 290 |
weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
| 291 |
`TINY_COMPUTER_AGENT_LIVE_TEST=1`.
|
| 292 |
|
|
|
|
| 303 |
| `tests/test_grid_fallback.py` | 11 | the fallback index announces itself instead of guessing |
|
| 304 |
| `tests/test_desktop.py` | 10 | display detection, the preflight checks, `doctor` |
|
| 305 |
| `tests/test_cli.py` | 10 | the command line: `--doctor`, `--image`, `--live`, argument handling |
|
| 306 |
+
| `tests/test_trace.py` | 28 | traces: a run recorded with no pixels, replayed against another brain, and scored against ground truth |
|
| 307 |
| `tests/test_live_desktop.py` | 2 | **needs a display, opt-in**: OCR finds a real button at the pixels it was drawn at, and a real click makes the app react |
|
| 308 |
|
| 309 |
```bash
|
| 310 |
+
pytest tests -q # 151 passed, 2 skipped
|
| 311 |
+
TINY_COMPUTER_AGENT_LIVE_TEST=1 pytest tests -q # 153 passed, on a desktop
|
| 312 |
```
|
| 313 |
|
| 314 |
## Install
|
|
|
|
| 317 |
pip install -e ".[ocr]" # labels from pixels (RapidOCR: no system binary)
|
| 318 |
pip install -e ".[desktop,ocr]" # + real capture and real input
|
| 319 |
pip install -e ".[vision,ocr]" # + the tiny model brains
|
| 320 |
+
pytest # 153 tests, no display or network needed
|
| 321 |
```
|
| 322 |
|
| 323 |
## Run it on your machine
|
|
|
|
| 381 |
```bash
|
| 382 |
# 1. install media, plus an answer file on removable media
|
| 383 |
qemu-img create -f qcow2 win.qcow2 64G
|
| 384 |
+
qemu-system-x86_64 -accel tcg,thread=multi -m 8G -smp 8 -machine pc \
|
| 385 |
+
-cpu Skylake-Client \
|
| 386 |
+
-drive file=win.qcow2,if=none,id=hd0,format=qcow2,cache=writeback \
|
| 387 |
+
-device ide-hd,drive=hd0,bus=ide.0 \
|
| 388 |
+
-drive file=win2022.iso,if=none,id=cd0,media=cdrom,readonly=on \
|
| 389 |
+
-device ide-cd,drive=cd0,bus=ide.1 \
|
| 390 |
+
-drive if=none,id=usb,format=raw,file=unattend.img \
|
| 391 |
+
-device usb-ehci,id=ehci -device usb-storage,bus=ehci.0,drive=usb \
|
| 392 |
+
-netdev user,id=n0 -device e1000,netdev=n0 \
|
| 393 |
+
-vga std -display none -vnc :1 -boot order=d,menu=off
|
| 394 |
|
| 395 |
# 2. inside the guest, after autologon
|
| 396 |
# vc_redist.x64.exe is required before onnxruntime will load
|
| 397 |
+
curl.exe -s -o C:\tiny\vc_redist.x64.exe <vc_redist-url> && C:\tiny\vc_redist.x64.exe /install /quiet /norestart
|
| 398 |
```
|
| 399 |
|
| 400 |
Serve files to the guest over QEMU's user-mode networking (the host is `10.0.2.2`), which
|
|
|
|
| 418 |
| `loop.py` | `AgentLoop`, `Episode`, `Step`, the no-progress guard and the verifier |
|
| 419 |
| `desktop.py` | Preflight checks, `doctor`, and `build_agent` in one call |
|
| 420 |
| `demo.py` | The synthetic desktop used by tests and `--image example` |
|
| 421 |
+
| `trace.py` | Portable traces: record a run with no pixels, replay it offline, score the choice against ground truth |
|
| 422 |
|
| 423 |
## Why a tiny model can do this at all
|
| 424 |
|
|
|
|
| 451 |
|
| 452 |
**Works:** real capture, a real OCR index with coordinates that land on the right pixel,
|
| 453 |
a real click an app reacts to, verified success, the closed vocabulary, the policy gates,
|
| 454 |
+
the no-progress guard, echo detection, the doctor, the CLI, portable traces that score a brain's choices offline, 153 tests, a 256M VLM that
|
| 455 |
reads a button label from pixels on a clean screenshot — and the same suite passing inside
|
| 456 |
a real Windows Server 2022 guest with no KVM (109 passed, exit 0, with the OCR-inference
|
| 457 |
tests excluded as impractically slow under emulation).
|
|
|
|
| 472 |
- [ ] Make the OCR guard in `test_cli.py` skip, rather than fail, when no engine is installed
|
| 473 |
- [ ] Make the VLM reliable on a live screen (it echoes the prompt instead of choosing)
|
| 474 |
- [ ] Accessibility-tree detector (AT-SPI on Linux, UI Automation on Windows)
|
| 475 |
+
- [x] Score episodes: replay a trace and count how often the choice was right - see [scoring a brain, offline](#scoring-a-brain-offline)
|
| 476 |
- [ ] marimo console: live screen preview, action log, pause / resume / step
|
| 477 |
|
| 478 |
## License
|