Publish tiny_computer_agent_app.py from marimo
Browse files- tiny_computer_agent_app.py +62 -11
tiny_computer_agent_app.py
CHANGED
|
@@ -132,7 +132,7 @@ def readme():
|
|
| 132 |
```bash
|
| 133 |
python -m pip install -e ".[desktop,ocr,dev]"
|
| 134 |
pytest tests -q
|
| 135 |
-
#
|
| 136 |
```
|
| 137 |
|
| 138 |
The two skips are the live-desktop tests. Give them a display and they run for real:
|
|
@@ -304,7 +304,7 @@ def readme():
|
|
| 304 |
`warning: no text was found on screen, so the index is 96 numbered grid cells and the
|
| 305 |
agent is choosing blind` — instead of silently guessing.
|
| 306 |
|
| 307 |
-
**
|
| 308 |
that click a real button.
|
| 309 |
|
| 310 |
## Verified on real Windows
|
|
@@ -346,7 +346,7 @@ def readme():
|
|
| 346 |
|
| 347 |
None of the three failures in the first row is a harness defect: two are
|
| 348 |
`ModuleNotFoundError: numpy` and the third is the guard bug documented below. With the
|
| 349 |
-
OCR engine installed, the same suite exits **0**. (Those Windows counts predate `trace.py`; the harness suite has since grown to
|
| 350 |
|
| 351 |
**The emulated-OCR caveat.** onnxruntime requires AVX2, and QEMU's software emulation of
|
| 352 |
AVX2 is roughly two orders of magnitude slower than native. A suite that finishes in 17
|
|
@@ -372,7 +372,7 @@ def readme():
|
|
| 372 |
|
| 373 |
## The test suite, in full
|
| 374 |
|
| 375 |
-
`pytest tests` collects **
|
| 376 |
weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
| 377 |
`TINY_COMPUTER_AGENT_LIVE_TEST=1`.
|
| 378 |
|
|
@@ -399,8 +399,8 @@ def readme():
|
|
| 399 |
| `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 |
|
| 400 |
|
| 401 |
```bash
|
| 402 |
-
pytest tests -q #
|
| 403 |
-
TINY_COMPUTER_AGENT_LIVE_TEST=1 pytest tests -q #
|
| 404 |
```
|
| 405 |
|
| 406 |
## Install
|
|
@@ -409,7 +409,7 @@ def readme():
|
|
| 409 |
pip install -e ".[ocr]" # labels from pixels (RapidOCR: no system binary)
|
| 410 |
pip install -e ".[desktop,ocr]" # + real capture and real input
|
| 411 |
pip install -e ".[vision,ocr]" # + the tiny model brains
|
| 412 |
-
pytest #
|
| 413 |
```
|
| 414 |
|
| 415 |
The accessibility detector needs system packages rather than an extra:
|
|
@@ -523,6 +523,7 @@ def readme():
|
|
| 523 |
| `accessibility.py` | The accessibility tree: `AtspiReader` and `AtspiDetector`, roles and real extents |
|
| 524 |
| `showcase.py` | The capability gallery: one real screenshot per capability, checked against the public API |
|
| 525 |
| `measure_vlm.py` | Measures the four ways to ask a model, on a real screen, and prints the table |
|
|
|
|
| 526 |
|
| 527 |
## The marimo console
|
| 528 |
|
|
@@ -575,6 +576,53 @@ def readme():
|
|
| 575 |
on Windows keeps using OCR. Tk barely publishes a tree and a canvas publishes none,
|
| 576 |
which is why this is a detector and not a replacement.
|
| 577 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
## Capability gallery
|
| 579 |
|
| 580 |
**68 capabilities, one screenshot each - and every one of the 141 public callables in the package (functions, methods, classes and protocols) appears in at least one of them**, together with 6 of its public constants.
|
|
@@ -1227,9 +1275,12 @@ def readme():
|
|
| 1227 |
[asking a tiny model in four ways](#asking-a-tiny-model-in-four-ways-measured)
|
| 1228 |
- [x] The marimo console: live screen preview, action log, pause / resume / step
|
| 1229 |
- [x] An accessibility-tree detector on Linux (AT-SPI), driving a real click with no OCR
|
| 1230 |
-
- [
|
| 1231 |
-
|
| 1232 |
-
|
|
|
|
|
|
|
|
|
|
| 1233 |
- [ ] **UI Automation** (Windows) for the accessibility detector. The interface is here and
|
| 1234 |
`accessibility_available("uia")` reports it as not implemented rather than pretending.
|
| 1235 |
- [ ] Windows run of the *live desktop* test: the workflow is committed, but it needs a
|
|
@@ -1378,7 +1429,7 @@ def publish_status(
|
|
| 1378 |
}
|
| 1379 |
# the tool that builds the capability gallery, shipped with the card it fills
|
| 1380 |
for _tool in ("showcase.py", "showcase_core.py", "showcase_catalog.py",
|
| 1381 |
-
"measure_vlm.py"):
|
| 1382 |
if (_root / _tool).is_file():
|
| 1383 |
_files[_tool] = (_root / _tool).read_text(encoding="utf-8")
|
| 1384 |
_folders = [f for f in ("tiny_computer_agent", "tests", "gallery",
|
|
|
|
| 132 |
```bash
|
| 133 |
python -m pip install -e ".[desktop,ocr,dev]"
|
| 134 |
pytest tests -q
|
| 135 |
+
# 227 passed, 2 skipped in 18.7s
|
| 136 |
```
|
| 137 |
|
| 138 |
The two skips are the live-desktop tests. Give them a display and they run for real:
|
|
|
|
| 304 |
`warning: no text was found on screen, so the index is 96 numbered grid cells and the
|
| 305 |
agent is choosing blind` — instead of silently guessing.
|
| 306 |
|
| 307 |
+
**229 tests pass**, including real OCR over a real image and (opt-in) live-desktop tests
|
| 308 |
that click a real button.
|
| 309 |
|
| 310 |
## Verified on real Windows
|
|
|
|
| 346 |
|
| 347 |
None of the three failures in the first row is a harness defect: two are
|
| 348 |
`ModuleNotFoundError: numpy` and the third is the guard bug documented below. With the
|
| 349 |
+
OCR engine installed, the same suite exits **0**. (Those Windows counts predate `trace.py`; the harness suite has since grown to 229 tests.)
|
| 350 |
|
| 351 |
**The emulated-OCR caveat.** onnxruntime requires AVX2, and QEMU's software emulation of
|
| 352 |
AVX2 is roughly two orders of magnitude slower than native. A suite that finishes in 17
|
|
|
|
| 372 |
|
| 373 |
## The test suite, in full
|
| 374 |
|
| 375 |
+
`pytest tests` collects **229 tests**. 227 run headless — no display, no network, no
|
| 376 |
weights. The last 2 are the opt-in live-desktop tests, skipped unless
|
| 377 |
`TINY_COMPUTER_AGENT_LIVE_TEST=1`.
|
| 378 |
|
|
|
|
| 399 |
| `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 |
|
| 400 |
|
| 401 |
```bash
|
| 402 |
+
pytest tests -q # 227 passed, 2 skipped
|
| 403 |
+
TINY_COMPUTER_AGENT_LIVE_TEST=1 pytest tests -q # 229 passed, on a desktop
|
| 404 |
```
|
| 405 |
|
| 406 |
## Install
|
|
|
|
| 409 |
pip install -e ".[ocr]" # labels from pixels (RapidOCR: no system binary)
|
| 410 |
pip install -e ".[desktop,ocr]" # + real capture and real input
|
| 411 |
pip install -e ".[vision,ocr]" # + the tiny model brains
|
| 412 |
+
pytest # 229 tests, no display or network needed
|
| 413 |
```
|
| 414 |
|
| 415 |
The accessibility detector needs system packages rather than an extra:
|
|
|
|
| 523 |
| `accessibility.py` | The accessibility tree: `AtspiReader` and `AtspiDetector`, roles and real extents |
|
| 524 |
| `showcase.py` | The capability gallery: one real screenshot per capability, checked against the public API |
|
| 525 |
| `measure_vlm.py` | Measures the four ways to ask a model, on a real screen, and prints the table |
|
| 526 |
+
| `evaluate_daily.py` | A day's tasks against a real app, verified by the app's own log |
|
| 527 |
|
| 528 |
## The marimo console
|
| 529 |
|
|
|
|
| 576 |
on Windows keeps using OCR. Tk barely publishes a tree and a canvas publishes none,
|
| 577 |
which is why this is a detector and not a replacement.
|
| 578 |
|
| 579 |
+
## Can it do a day's work?
|
| 580 |
+
|
| 581 |
+
The honest way to answer "could this replace a person at a PC?" is to give it a
|
| 582 |
+
person's tasks and check the result against **the application's own record**, not the
|
| 583 |
+
agent's opinion of itself. Eleven of them, on a real GTK desktop with the real mouse:
|
| 584 |
+
|
| 585 |
+
```
|
| 586 |
+
button yes 0.7 Click Save
|
| 587 |
+
right-button yes 0.4 Click Cancel, not the button next to it
|
| 588 |
+
type yes 1.8 Type a new file name and save
|
| 589 |
+
shortcut yes 0.7 Press Ctrl+S
|
| 590 |
+
menu yes 0.7 Open the File menu and choose Save As
|
| 591 |
+
scroll yes 1.5 Scroll the list and pick a row
|
| 592 |
+
second-window yes 0.7 Open Settings and press Apply there
|
| 593 |
+
see yes 0.1 Read what the window says
|
| 594 |
+
verify yes 0.7 Check the save actually happened
|
| 595 |
+
refuse yes 0.1 Do not press the Terminal button
|
| 596 |
+
kill-switch yes 0.1 Stop a live run mid-plan
|
| 597 |
+
```
|
| 598 |
+
|
| 599 |
+
Run it yourself with `python evaluate_daily.py` (`--only menu` for one task).
|
| 600 |
+
|
| 601 |
+
**What that number does and does not mean.** The first run of this suite scored
|
| 602 |
+
**5/11**, and the failures were not the model. A coordinate bug in the detector
|
| 603 |
+
*chain* was shifting every accessibility click by the capture's scale factor:
|
| 604 |
+
`--detector best` wraps the tree detector inside `AutoDetector`, which reported
|
| 605 |
+
"frame coordinates" for elements that were already screen pixels, so `IndexedScreen`
|
| 606 |
+
re-mapped them through the downscale and the click landed beside the button. Two more
|
| 607 |
+
real gaps surfaced with it - an unnamed GTK list row was invisible to the index (so
|
| 608 |
+
nothing in a list could be clicked at all), and two GTK instances sharing an
|
| 609 |
+
application id made the second launch silently do nothing. All three are fixed and
|
| 610 |
+
pinned by tests.
|
| 611 |
+
|
| 612 |
+
So the machine half works: it can see, decide, act and verify a day's operations, and
|
| 613 |
+
it refuses the dangerous ones. What this suite deliberately does **not** measure is
|
| 614 |
+
*choosing* the actions - nine of the eleven tasks were driven by a scripted action
|
| 615 |
+
list, because that is what isolates the harness from the brain. Choosing is measured
|
| 616 |
+
in [asking a tiny model in four ways](#asking-a-tiny-model-in-four-ways-measured):
|
| 617 |
+
4/4 goals right for a 0.5B text planner over the accessibility index in `rank` mode,
|
| 618 |
+
and 1-2/4 for a 256M VLM.
|
| 619 |
+
|
| 620 |
+
Caveats, stated plainly: this is a Linux GTK desktop under Xvfb with **no window
|
| 621 |
+
manager** (so the keyboard tasks had to move the pointer into the window first,
|
| 622 |
+
because X without a manager focuses by pointer); Windows was not exercised; the tasks
|
| 623 |
+
are single-application with no overlapping windows, no animations and no network; and
|
| 624 |
+
eleven tasks are a sample, not a survey.
|
| 625 |
+
|
| 626 |
## Capability gallery
|
| 627 |
|
| 628 |
**68 capabilities, one screenshot each - and every one of the 141 public callables in the package (functions, methods, classes and protocols) appears in at least one of them**, together with 6 of its public constants.
|
|
|
|
| 1275 |
[asking a tiny model in four ways](#asking-a-tiny-model-in-four-ways-measured)
|
| 1276 |
- [x] The marimo console: live screen preview, action log, pause / resume / step
|
| 1277 |
- [x] An accessibility-tree detector on Linux (AT-SPI), driving a real click with no OCR
|
| 1278 |
+
- [x] Make a tiny model choose correctly: a 0.5B text planner over the accessibility index in
|
| 1279 |
+
`rank` mode gets **4/4** goals right in 0.9s. A 256M **vision** model does not (1-2/4) -
|
| 1280 |
+
it is below the threshold, which is why the rule-free planner is still the default
|
| 1281 |
+
- [x] A daily-usage evaluation: 11 tasks (menu, scroll, typing, shortcut, second window,
|
| 1282 |
+
verify, refuse, kill switch) against a real GTK app, checked against the application's
|
| 1283 |
+
own log - see [can it do a day's work](#can-it-do-a-days-work)
|
| 1284 |
- [ ] **UI Automation** (Windows) for the accessibility detector. The interface is here and
|
| 1285 |
`accessibility_available("uia")` reports it as not implemented rather than pretending.
|
| 1286 |
- [ ] Windows run of the *live desktop* test: the workflow is committed, but it needs a
|
|
|
|
| 1429 |
}
|
| 1430 |
# the tool that builds the capability gallery, shipped with the card it fills
|
| 1431 |
for _tool in ("showcase.py", "showcase_core.py", "showcase_catalog.py",
|
| 1432 |
+
"measure_vlm.py", "evaluate_daily.py"):
|
| 1433 |
if (_root / _tool).is_file():
|
| 1434 |
_files[_tool] = (_root / _tool).read_text(encoding="utf-8")
|
| 1435 |
_folders = [f for f in ("tiny_computer_agent", "tests", "gallery",
|