vco 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.
- vco-0.1.0/PKG-INFO +387 -0
- vco-0.1.0/README.md +363 -0
- vco-0.1.0/pyproject.toml +36 -0
- vco-0.1.0/setup.cfg +4 -0
- vco-0.1.0/tests/test_core.py +1253 -0
- vco-0.1.0/tests/test_web_ui_matrix.py +43 -0
- vco-0.1.0/tests/test_webagent.py +85 -0
- vco-0.1.0/tests/test_webdebug.py +69 -0
- vco-0.1.0/vco/__init__.py +35 -0
- vco-0.1.0/vco/adaptive_zoom.py +245 -0
- vco-0.1.0/vco/backends/apple_vision_ocr.swift +73 -0
- vco-0.1.0/vco/browser.py +341 -0
- vco-0.1.0/vco/capture.py +31 -0
- vco-0.1.0/vco/cli.py +1187 -0
- vco-0.1.0/vco/executor.py +81 -0
- vco-0.1.0/vco/geometry.py +106 -0
- vco-0.1.0/vco/grid.py +108 -0
- vco-0.1.0/vco/loop.py +132 -0
- vco-0.1.0/vco/mcp_server.py +618 -0
- vco-0.1.0/vco/models.py +181 -0
- vco-0.1.0/vco/ocr.py +417 -0
- vco-0.1.0/vco/ocr_assist.py +193 -0
- vco-0.1.0/vco/ocr_server.py +111 -0
- vco-0.1.0/vco/providers.py +1411 -0
- vco-0.1.0/vco/webagent.py +420 -0
- vco-0.1.0/vco/webdebug.py +180 -0
- vco-0.1.0/vco/zoom.py +189 -0
- vco-0.1.0/vco.egg-info/PKG-INFO +387 -0
- vco-0.1.0/vco.egg-info/SOURCES.txt +31 -0
- vco-0.1.0/vco.egg-info/dependency_links.txt +1 -0
- vco-0.1.0/vco.egg-info/entry_points.txt +2 -0
- vco-0.1.0/vco.egg-info/requires.txt +22 -0
- vco-0.1.0/vco.egg-info/top_level.txt +1 -0
vco-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vco
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A clicker for LLMs: operate web pages and desktop screens via shell commands.
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: Pillow>=10.0
|
|
8
|
+
Requires-Dist: pydantic>=2.7
|
|
9
|
+
Provides-Extra: openai
|
|
10
|
+
Requires-Dist: openai>=2.0; extra == "openai"
|
|
11
|
+
Provides-Extra: control
|
|
12
|
+
Requires-Dist: pyautogui>=0.9.54; extra == "control"
|
|
13
|
+
Provides-Extra: ocr
|
|
14
|
+
Requires-Dist: rapidocr<4,>=3.9; extra == "ocr"
|
|
15
|
+
Requires-Dist: onnxruntime>=1.17; extra == "ocr"
|
|
16
|
+
Provides-Extra: browser
|
|
17
|
+
Requires-Dist: playwright>=1.40; extra == "browser"
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: openai>=2.0; extra == "all"
|
|
20
|
+
Requires-Dist: pyautogui>=0.9.54; extra == "all"
|
|
21
|
+
Requires-Dist: rapidocr<4,>=3.9; extra == "all"
|
|
22
|
+
Requires-Dist: onnxruntime>=1.17; extra == "all"
|
|
23
|
+
Requires-Dist: playwright>=1.40; extra == "all"
|
|
24
|
+
|
|
25
|
+
# vco — visual computer operate for LLMs
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/vco/)
|
|
28
|
+
[中文文档](README_zh.md)
|
|
29
|
+
|
|
30
|
+
`vco` is a clicker for LLMs: it lets any agent that can run shell commands and read JSON operate web pages and desktop screens.
|
|
31
|
+
|
|
32
|
+
Two channels, deterministic-first, vision models only as fallback:
|
|
33
|
+
|
|
34
|
+
- **Web**: headless Chromium over the DOM — accessibility-tree perception (readable by text-only models), click by text/selector, form filling, error capture, video recording. No vision model involved at all.
|
|
35
|
+
- **Desktop**: screenshot + OCR locating (a unique text hit needs zero model calls); only text-free targets fall back to a vision model picking cells on a numbered grid, while local code handles coordinate math and the real mouse.
|
|
36
|
+
|
|
37
|
+
## Commands
|
|
38
|
+
|
|
39
|
+
stdout is always JSON. Artifacts go to `.screenshot/` under the **current working directory** (whichever project you run it from).
|
|
40
|
+
|
|
41
|
+
Web (headless, never touches the real mouse):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
vco webshot https://example.com # render a page and screenshot it; --full-page for the whole page
|
|
45
|
+
# output includes console_errors/page_errors/failed_requests
|
|
46
|
+
vco webtext https://example.com # dump the accessibility tree (roles + text as YAML);
|
|
47
|
+
# this is how a text-only model "sees" a page
|
|
48
|
+
vco webclick http://127.0.0.1:9005 --fill 输入消息=你好 --target 发送
|
|
49
|
+
# fill inputs (repeatable --fill) then click; ambiguous targets are
|
|
50
|
+
# refused and listed; before/after screenshots are saved
|
|
51
|
+
# --expect "text" waits for post-click content (verified field)
|
|
52
|
+
# --profile <dir> persists login/cookies; --selector clicks any CSS target
|
|
53
|
+
# --headed --hold 3 shows a live demo (visible typing, orange halo on target)
|
|
54
|
+
# --record saves a .webm video of the whole session (works headless)
|
|
55
|
+
vco webrun http://127.0.0.1:9005 --task 'type xxx and click send' --provider ollama --model qwen3:8b
|
|
56
|
+
# text-model agent loop: snapshot → decision → DOM action → repeat until done
|
|
57
|
+
vco webdebug http://127.0.0.1:9005 --task 'I scrolled up to read, sent a message, and it jumped to the top'
|
|
58
|
+
# front-end debugger: reproduces the complaint, records why, reports; never edits
|
|
59
|
+
# findings are built from what was recorded, so an unproven cause cannot appear
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Desktop (OCR first; the real mouse moves only via `click`):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
vco shot # screenshot; --region x,y,w,h for a sub-region;
|
|
66
|
+
# --display 2 selects a secondary display (default: main);
|
|
67
|
+
# coordinates are always global logical pixels, ready for click
|
|
68
|
+
vco find --target "CODEX" # locate (dry-run): OCR first, draws a translucent red circle on hit
|
|
69
|
+
vco click --at 1164,92 # real mouse click; click --target "..." locates first
|
|
70
|
+
vco ask [image] --provider ollama --model minicpm-v4.6:latest
|
|
71
|
+
# ask a vision model "what's in this image"; captures the screen if no image
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Recommended desktop workflow (every step verifiable):
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
shot see the current state
|
|
78
|
+
find --target locate + circle confirmation image (marked_image); never moves the mouse
|
|
79
|
+
│ ├─ unique OCR hit → coordinates computed locally, zero model calls
|
|
80
|
+
│ ├─ multiple hits → refuses to click; refine the target or add --provider
|
|
81
|
+
│ └─ zero hits → add --provider for vision-model grid/zoom locating
|
|
82
|
+
click --at real click after the circle looks right
|
|
83
|
+
shot screenshot again to verify the UI actually changed
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Web is simpler: `webtext` to perceive → `webclick` to act (`--expect` verifies inline); or hand the whole task to `webrun` and let a text model drive. For a bug rather than a task, `webdebug` — see below.
|
|
87
|
+
|
|
88
|
+
Key `find`/`click` JSON fields: `found` (exit code 2 when false — a normal outcome, not a failure), `x`/`y`, `method` (`ocr` / `model` / `model+ocr-hints`), `marked_image`, `metadata.elapsed_seconds`.
|
|
89
|
+
|
|
90
|
+
`ask` supports four providers: `ollama` (local, default `127.0.0.1:11434`), `minimax` and `glm` (need `--minimax-settings` / `--glm-settings` pointing at a JSON file with an api_key), and `openai`. Use `--question` to customize.
|
|
91
|
+
|
|
92
|
+
Full usage instructions (including safety rules and failure handling) are packaged as a tool-agnostic agent skill: [`skills/operate-screen/SKILL.md`](skills/operate-screen/SKILL.md) — drop it into any agent's skills directory. This repo's `.kimi-code/skills/operate-screen` is a symlink to it, and `plugins/vco/` is the Codex MCP packaging.
|
|
93
|
+
|
|
94
|
+
## Debugging a front-end bug
|
|
95
|
+
|
|
96
|
+
`webdebug` takes a complaint in the words someone actually used and comes back with the line that caused it. It reproduces and reports; it never edits. Fixing needs the whole codebase in your head and is where the risk lives, while locating is where the hours go — by the time you know which line moved the value, the edit is usually obvious.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
vco webdebug http://127.0.0.1:8772/ \
|
|
100
|
+
--task "I scrolled down to read something, hit Refresh, and the list jumped back to the top" \
|
|
101
|
+
--provider ollama --model qwen3.6:latest
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"reproduced": true,
|
|
107
|
+
"culprit": "HTMLButtonElement.rebuildList (http://127.0.0.1:8772/:25:19)",
|
|
108
|
+
"how": "assigned",
|
|
109
|
+
"assignments": [{"from": 1820, "to": 0, "source": "HTMLButtonElement.rebuildList (…:25:19)"}],
|
|
110
|
+
"user_actions": ["click Refresh list"]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The method is fixed rather than improvised: **measure** the value the complaint is about, **arm** the recorders before touching anything, **act** the way the user did, **collect**, then **report**. Arming late sees nothing, so the order is the point.
|
|
115
|
+
|
|
116
|
+
`arm` installs two recorders at once, because the two failure modes look identical from outside and only one of them leaves a trail:
|
|
117
|
+
|
|
118
|
+
- assignments, each with the call stack that made it — this is what names the culprit;
|
|
119
|
+
- a sampler for changes **nobody assigned** — a value also resets when a node is re-attached or re-laid-out, and no stack exists for that.
|
|
120
|
+
|
|
121
|
+
It drives the page like a person: real clicks, a real wheel, and text typed one character at a time (11 characters means 11 `keydown` events, not one `input`). Debounce, input handlers and autocomplete all behave differently for a value that appears all at once, so a bug that only shows up under real typing still shows up here. Since the accessibility tree carries roles and text but no selectors, every step is also handed the page's scrollable elements and what each is called.
|
|
122
|
+
|
|
123
|
+
**Findings come from the evidence, not from the model's closing statement.** A stack that was never captured cannot appear in `culprit`, and "cannot reproduce" is reported as itself rather than dressed up as an answer. Exit code is 0 when the bug was reproduced, 2 when it was not — nothing was learned in that case, which is the outcome worth failing on.
|
|
124
|
+
|
|
125
|
+
`examples/scroll_bug_fixture.html` is a target to try it against: three ways of losing a scroll position, two of which are indistinguishable from outside and are caught by different halves of `arm`.
|
|
126
|
+
|
|
127
|
+
## MCP Server
|
|
128
|
+
|
|
129
|
+
Zero-dependency stdio MCP server: `python3 -m vco.mcp_server` (plugin config in `plugins/vco/.mcp.json`). Exposes 8 tools:
|
|
130
|
+
|
|
131
|
+
- `web_snapshot` / `web_screenshot` / `web_click` / `web_run`: the headless web channel (aria-tree perception, screenshots, DOM clicks, agent loop) — no model required, or a text model for the loop;
|
|
132
|
+
- `screen_probe` / `screen_run`: the desktop channel (GLM/MiniMax vision models + grid zoom); requires `VCO_GLM_SETTINGS` / `VCO_MINIMAX_SETTINGS` env vars pointing at settings files;
|
|
133
|
+
- `cache_status` / `cache_clear`: cache management.
|
|
134
|
+
|
|
135
|
+
Any MCP-capable agent (Codex, Claude Code, Kimi, ...) can call these directly.
|
|
136
|
+
|
|
137
|
+
## Installation
|
|
138
|
+
|
|
139
|
+
Requires Python 3.9+.
|
|
140
|
+
|
|
141
|
+
Install from PyPI:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pip install 'vco[control,ocr,browser]' # control=real mouse (pyautogui), ocr=RapidOCR, browser=Playwright
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Or install from source for development:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
python3 -m venv .venv
|
|
151
|
+
source .venv/bin/activate
|
|
152
|
+
pip install -e '.[control,ocr,browser]'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Minimal installs: `pip install vco` is core-only; `[openai]` adds the OpenAI provider; `[browser]` also needs `playwright install chromium` (skip if a local Chromium cache already exists). On first macOS run, grant **Screen Recording** and **Accessibility** to your terminal in System Settings → Privacy & Security.
|
|
156
|
+
|
|
157
|
+
## Provider settings
|
|
158
|
+
|
|
159
|
+
Cloud providers (`minimax`, `glm`) read API keys from JSON files so keys never enter the repo or shell history. Copy the examples and fill in your keys:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
cp examples/provider_settings.minimax.json.example provider_settings.minimax.json
|
|
163
|
+
cp examples/provider_settings.glm.json.example provider_settings.glm.json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`provider_settings.minimax.json`:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"api_key": "YOUR_MINIMAX_API_KEY_HERE"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`provider_settings.glm.json`:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"api_key": "YOUR_GLM_API_KEY_HERE",
|
|
179
|
+
"base_url": "https://open.bigmodel.cn/api/paas/v4/"
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The benchmark harness (`debug/run_web_ui_matrix.py`) also looks for `provider_settings.deepseek.json` in the settings directory; see `examples/provider_settings.deepseek.json.example` for that format. All `provider_settings.*.json` files are ignored by `.gitignore` so real keys cannot be committed by accident.
|
|
184
|
+
|
|
185
|
+
## How the desktop grid works
|
|
186
|
+
|
|
187
|
+
Grid strategy depends on model capability: small models use a two-level sparse grid; strong vision models read a single dense grid directly.
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
screenshot of the target region
|
|
191
|
+
│
|
|
192
|
+
v
|
|
193
|
+
full-view 4×4 numbered grid ──> model picks a coarse cell
|
|
194
|
+
│
|
|
195
|
+
v
|
|
196
|
+
crop a 2×2-cell window around the coarse cell
|
|
197
|
+
│
|
|
198
|
+
v
|
|
199
|
+
re-slice the crop into 4×4 ──> model picks a fine cell
|
|
200
|
+
│
|
|
201
|
+
v
|
|
202
|
+
locally take the fine-cell center and map to real screen coordinates
|
|
203
|
+
│
|
|
204
|
+
v
|
|
205
|
+
click ──> wait for UI change ──> screenshot again
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The model receives images and a task description, never numeric screen coordinates; it may only output strict JSON grid actions. Real screen coordinates are generated locally.
|
|
209
|
+
|
|
210
|
+
`MiniMax-M3` and `glm-4.6v` use adaptive zoom: the `model` strategy lets the model click early when the target is clear; the `fixed` strategy forces a cell pick and zoom at every non-final level and only accepts a click at the last level. On real desktops with tiny icons, model-chosen zoom cells can drift — prefer `fixed` for small or similar-looking controls.
|
|
211
|
+
|
|
212
|
+
Near screen edges the crop window shifts inward instead of shrinking. The second level defaults to the fine-cell center rather than the small model's unreliable `0–1` fractional offsets.
|
|
213
|
+
|
|
214
|
+
## Benchmarks
|
|
215
|
+
|
|
216
|
+
### Capability threshold
|
|
217
|
+
|
|
218
|
+
Success requires: a model that truly accepts image input; a coarse-to-fine grid so small models never read dense numbering; local code doing screenshots, coordinate math, and mouse actions; and bounded regions, steps, and action types.
|
|
219
|
+
|
|
220
|
+
| Model | Size | Image input | Result |
|
|
221
|
+
|---|---:|---:|---|
|
|
222
|
+
| `minicpm-v4.6:latest` | 752M LM + 548M vision projector, 1.6GB file | yes | 6/10 strict hits on 10 images, 2.67 s/image |
|
|
223
|
+
| `gemma4:latest` | 8B, 9.6GB file | yes (image & audio) | 6/10 strict hits, 16.27 s/image |
|
|
224
|
+
| `qwen3.6:latest` | 36B MoE, 23GB file | yes | not benchmarked yet |
|
|
225
|
+
| `RogerBen/HY-MT2-1.8B:latest` | 1.8B | no, completion-only | unusable for screen grounding |
|
|
226
|
+
| MiniMax `MiniMax-M2.5` API | paid cloud model | no; M2.x is text+tools only | failed the single-image capability gate |
|
|
227
|
+
| MiniMax `MiniMax-M3` API | paid cloud natively multimodal | yes | single-pass 16×16, 10/10 strict hits, 10.035 s/image |
|
|
228
|
+
| Zhipu `glm-4.6v` API | cloud vision model | yes | real-desktop "拼" icon hit `(1601,19)` with fixed 3-level zoom, 44.667 s |
|
|
229
|
+
|
|
230
|
+
The accurate conclusion is not "any 1.8B can operate a screen" but: **a capable small vision model — even below 1.8B — can perform limited screen clicks with grid zoom and a local controller.**
|
|
231
|
+
|
|
232
|
+
### 10-image benchmark
|
|
233
|
+
|
|
234
|
+
`debug/` contains 10 synthetic UI screenshots with ground-truth button bounds, covering different positions, colors, labels, and distractor buttons. All runs use `grid-only + two-level zoom`:
|
|
235
|
+
|
|
236
|
+
| Model | Strict hits | Mean latency | 20px expanded-box diagnostic |
|
|
237
|
+
|---|---:|---:|---:|
|
|
238
|
+
| `minicpm-v4.6:latest` | **6/10 (60%)** | **2.67 s** | 10/10 |
|
|
239
|
+
| `gemma4:latest` | **6/10 (60%)** | 16.27 s | 9/10 |
|
|
240
|
+
| MiniMax `MiniMax-M3` (single 16×16) | **10/10 (100%)** | 10.035 s | 10/10 |
|
|
241
|
+
|
|
242
|
+
Misses are all near-misses 8–28px from the button edge; the expanded-box score only indicates the model found the target area. Bigger models did not improve strict accuracy. MiniMax-M3's 10/10 comes from the synthetic set and does not guarantee the same rate on complex real desktops; cloud latency is still unsuitable for real-time control.
|
|
243
|
+
|
|
244
|
+
See [debug/README.md](debug/README.md) and the per-model `debug/results*/SUMMARY.md` for case overviews, trajectories, and machine-readable results. Full ablation notes: [docs/local-model-findings.md](docs/local-model-findings.md). `cache/web-ui-matrix/` has a separate 10-page 2560×1440 real web-UI comparison across models and strategies (including OCR-first).
|
|
245
|
+
|
|
246
|
+
### OCR-first is the best path for text targets
|
|
247
|
+
|
|
248
|
+
When the target has text, a unique OCR hit gives the center point locally: zero model calls, ~2–3 s, and no thumbnail misestimation. In the web-ui-matrix scoring, OCR-first groups reached 100% (9/9). The only scenario where pure-vision grid zoom is irreplaceable is a target with no text at all (bare icons, color blocks, custom-drawn controls).
|
|
249
|
+
|
|
250
|
+
## Full loop: run & probe
|
|
251
|
+
|
|
252
|
+
Static-image validation (no capture, no mouse):
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
python3 examples/generate_probe_fixture.py --output /tmp/vco-clean.png
|
|
256
|
+
vco probe \
|
|
257
|
+
--task 'Click the blue Save button.' \
|
|
258
|
+
--image /tmp/vco-clean.png \
|
|
259
|
+
--provider ollama --model minicpm-v4.6:latest \
|
|
260
|
+
--ollama-image-mode grid --zoom
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Live loop (dry-run by default; add `--execute` for real clicks once coordinates look stable):
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
vco run \
|
|
267
|
+
--task '点击保存按钮,看到成功状态后结束' \
|
|
268
|
+
--region 100,100,1200,800 \
|
|
269
|
+
--provider ollama --model minicpm-v4.6:latest \
|
|
270
|
+
--ollama-image-mode grid --zoom \
|
|
271
|
+
--max-steps 5 # add --execute once verified
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
MiniMax-M3 / GLM-4.6V usage (adaptive 16×16 zoom; do NOT pass `--zoom`):
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
vco run --task '...' --region 100,100,1000,600 \
|
|
278
|
+
--provider minimax --minimax-settings /path/to/provider_settings.minimax.json \
|
|
279
|
+
--max-steps 1
|
|
280
|
+
# verify cache/runs/<timestamp>/step-001-action.json, then:
|
|
281
|
+
# --max-steps 20 --settle 1.0 --execute
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Recommended GLM flags for tiny icons: `--model glm-4.6v --glm-thinking disabled --glm-image-mode both --adaptive-zoom-strategy fixed --adaptive-zoom-levels 3`.
|
|
285
|
+
|
|
286
|
+
Common zoom/model flags: `--adaptive-zoom-levels 3`, `--adaptive-zoom-span 4`, `--adaptive-zoom-strategy model|fixed`, `--no-adaptive-zoom`, `--minimax-service-tier standard|priority`, `--minimax-image-detail low|default|high`, `--zoom-use-model-offset`, `--zoom-center-delta`.
|
|
287
|
+
|
|
288
|
+
API keys are read only from settings JSON files and are never copied into the repo or run artifacts.
|
|
289
|
+
|
|
290
|
+
## Action protocol
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{"type": "click", "target": {"cell": 11, "offset_x": 0.5, "offset_y": 0.5}}
|
|
294
|
+
{"type": "drag", "start": {"cell": 6, "offset_x": 0.2, "offset_y": 0.5},
|
|
295
|
+
"end": {"cell": 10, "offset_x": 0.8, "offset_y": 0.5}}
|
|
296
|
+
{"type": "done", "reason": "已看到保存成功状态"}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Cells are one-based, row-major. `offset_x`/`offset_y` must be in `[0,1]`. Extra fields and raw screen coordinates are rejected by the schema.
|
|
300
|
+
|
|
301
|
+
## OCR interface
|
|
302
|
+
|
|
303
|
+
OCR is decoupled from vision models and the default backend is cross-platform. `rapidocr` (cross-platform ONNX, offline) is preferred; `apple-vision` is a macOS-only optional backend; `http` talks to any OCR service implementing the VCO JSON contract; `auto` tries RapidOCR first, then platform backends.
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
vco ocr screen.png --backend rapidocr --mode accurate \
|
|
307
|
+
--language zh-Hans --language en-US --output result.json
|
|
308
|
+
vco ocr-serve --backend rapidocr --host 127.0.0.1 --port 8765 # built-in /v1/ocr service
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Python interface:
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
from PIL import Image
|
|
315
|
+
from vco.ocr import OCRRequest, create_ocr_backend
|
|
316
|
+
|
|
317
|
+
backend = create_ocr_backend("auto")
|
|
318
|
+
result = backend.recognize(
|
|
319
|
+
Image.open("screen.png"),
|
|
320
|
+
OCRRequest(languages=("zh-Hans", "en-US"), mode="accurate"),
|
|
321
|
+
)
|
|
322
|
+
for box in result.boxes:
|
|
323
|
+
print(box.id, box.text, box.confidence, box.bbox, box.center)
|
|
324
|
+
# result.find_text("拼") matches text locally and returns the click center
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Third-party backends can register via `register_ocr_backend("my-ocr", MyOCRBackend)` without touching the core. An OCR service bound to a non-loopback address must set a Bearer token via `--api-key-env`.
|
|
328
|
+
|
|
329
|
+
OCR + vision-model composition (shared by `probe`/`run`/`find`/`click`): a unique match locates locally (`model_calls=0`); zero or multiple matches pass OCR text and grid positions to the model as hints; OCR failure falls back to the plain vision model. `--ocr-match contains` relaxes matching; `--no-ocr-direct-click` forbids OCR-decided clicks.
|
|
330
|
+
|
|
331
|
+
## Other commands
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
vco overlay screenshot.png --output grid.png --rows 10 --cols 10 # draw a grid
|
|
335
|
+
vco overlay screenshot.png --output grid.png --mapping mapping.json \
|
|
336
|
+
--region 100,200,1200,800 # coordinate mapping table
|
|
337
|
+
vco convert --region 100,200,1200,800 --rows 10 --cols 10 \
|
|
338
|
+
--action '{"type":"click","target":{"cell":45,"offset_x":0.5,"offset_y":0.5}}'
|
|
339
|
+
# offline action resolution
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Also supports manual input (`--provider manual`), JSONL replay, and the OpenAI provider — see `vco --help`.
|
|
343
|
+
|
|
344
|
+
## Capability boundaries
|
|
345
|
+
|
|
346
|
+
Good at: clicking clear buttons, cards, and icons; coarse selection inside bounded regions; simple straight-line drags; low-cost local Computer-Use proofs of concept; acting as a "visual probe" for agents (screenshot, locate, verify).
|
|
347
|
+
|
|
348
|
+
Not yet reliable or unsupported: text-dense UIs, very small or visually similar controls; fast-disappearing menus, heavy animation, low-latency reactions; keyboard input, scrolling, multi-point curves, complex drawing; precise dragging; autonomously understanding long tasks (small models may misfire `done` — always set max steps); safely executing payments, deletions, message sending, and other high-risk operations.
|
|
349
|
+
|
|
350
|
+
## Safety design
|
|
351
|
+
|
|
352
|
+
- `run` defaults to dry-run; real mouse input requires explicit `--execute`. `find` never moves the mouse; only `click` does.
|
|
353
|
+
- Ambiguous OCR targets refuse to click and fall back to model judgment.
|
|
354
|
+
- All actions are confined to the user-specified region.
|
|
355
|
+
- Model JSON is validated against a strict schema; illegal offsets are never silently corrected.
|
|
356
|
+
- `--max-steps` prevents infinite loops.
|
|
357
|
+
- The action set contains no shell, file operations, keyboard input, or arbitrary tool calls.
|
|
358
|
+
- Every step saves the clean image, grid image, zoom images, model action, and local resolution.
|
|
359
|
+
- Ollama defaults to `127.0.0.1:11434`; images never leave the machine with that provider.
|
|
360
|
+
- Real execution uses pyautogui with the top-left-corner fail-safe enabled: slam the mouse into the main screen's top-left corner to emergency-stop.
|
|
361
|
+
|
|
362
|
+
## Artifacts
|
|
363
|
+
|
|
364
|
+
- `shot`/`find`/`click`/`ask` artifacts go to `.screenshot/` under the current directory (already in `.gitignore`).
|
|
365
|
+
- `run` writes full trajectories to `cache/runs/<timestamp>/`: task, mapping table, per-step screenshots, zoom images, actions, and resolved coordinates.
|
|
366
|
+
|
|
367
|
+
These files may contain private screen content; both directories can be deleted wholesale:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
rm -rf ./cache/ ./.screenshot/
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Tests
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
python3 -m unittest discover -v
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Covers grid boundaries, forward/reverse coordinate mapping, strict JSON schema, Ollama/OpenAI/MiniMax transports, edge cropping, two-level zoom, and loop artifacts; plus the web agent's decision parsing (refusing precisely matters as much as accepting — a rejected decision is handed back for the model to correct) and the report builder behind `webdebug`, including one test pinning down the rule that a cause the model merely asserted, with nothing recorded behind it, never reaches the findings.
|
|
380
|
+
|
|
381
|
+
## Next steps
|
|
382
|
+
|
|
383
|
+
1. Image diffing before/after actions for success detection, reducing "clicked but nothing happened" misjudgments (today's stopgap is a manual/model `shot` comparison after `click`).
|
|
384
|
+
2. Forbidden-region policies and pre-execution confirmation for high-risk areas.
|
|
385
|
+
3. Separate local zoom for drag start and end points.
|
|
386
|
+
4. An automated grounding evaluation set with varied positions, sizes, and themes.
|
|
387
|
+
5. A window picker for live runs to avoid hand-writing `--region`.
|