File size: 3,372 Bytes
2edb151 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | # keys-automatic-receipt-doc-scanner
Local receipt/document scanner for **Autonomous OS + Autonomous Lamp**.
Lamp camera captures; a GPU box runs the model. Nothing cloud.
## Hardware split (do not blur this)
| Where | Fits | Does not fit |
|---|---|---|
| **Lamp** (ARM64, **6 GB RAM**) | This skill (`SKILL.md` + `scripts/`), HAL snapshot, SQLite | Gemma 4 12B Unified, Qwen3.8-27B, Nemotron 3.5 Lightning |
| **GPU box** (DGX Spark / Omen) | Gemma 4 12B Unified (omni: vision + embed) or Qwen3.8-27B VLM + Embed-1B | β |
Gemma 4 12B Unified is ~12B dense (`hidden_size` **3840**, `Gemma4UnifiedForConditionalGeneration`). Weights alone exceed the Lamp's 6 GB even in NVFP4. Treat the Lamp as eyes/hands; treat the GPU box as the brain.
## Default brain: Gemma 4 12B Unified (omni)
Encoder-free VLM. One OpenAI-compatible server does:
1. **Vision extract** β `POST /v1/chat/completions` with `image_url` data URI (receipt/doc JPEG)
2. **Embed** β `POST /v1/embeddings` against the **same** server (mean-pool / convert-embed). Dim **3840**.
Never mix 3840 (Gemma) and 2048 (Nemotron-3-Embed-1B) in one sqlite-vec index.
## Fallback brains (same skill, env only)
- **Qwen3.8-27B ADay777** VLM at `:8078` (`qwen38-nvfp4`) for extract; Nemotron-3-Embed-1B 2048-d for embed
- **Lightning** is text-only. Never send images to it.
Fleet GB10: `--gpu-memory-utilization` **0.85** hard cap.
## Pipeline
```
voice / phone / drop β Lamp camera or inbox/
β optional OCR assist
β Gemma4 (or Qwen) vision JSON extract + category
β omni embed (or Nemotron-3-Embed-1B)
β sqlite-vec vendor/SKU/category match
β review / speak summary
```
## Rules
- No cloud APIs. Backends behind `OCRBackend` / `LLMBackend` / `EmbedBackend`.
- Lightning: `accepts_images=False`. Never attach image parts.
- Python 3.12, typed, pytest. No notebooks. Don't vendor weights.
- Idle-batch inbox 30s. Dedup sha256.
## Extract JSON
```json
{
"doc_kind": "receipt|invoice|document",
"category": "groceries|dining|transport|household|health|entertainment|utilities|office|travel|other",
"vendor": "string|null",
"date": "YYYY-MM-DD|null",
"tax": "number|null",
"total": "number|null",
"currency": "string|null",
"line_items": [
{"description": "string", "qty": "number|null", "unit_price": "number|null",
"amount": "number|null", "sku": "string|null"}
]
}
```
Money stored as integer cents.
## Match (cosine similarity = 1 - sqlite-vec distance)
| | Auto | Review | Unmatched |
|---|---|---|---|
| SKU / line | β₯ 0.88 | 0.72β0.88 | < 0.72 |
| Vendor | β₯ 0.82 | 0.65β0.82 | < 0.65 |
Exact catalog SKU wins first.
## Layout
```
app/ config, schemas, media, camera, extract, embed, db, match,
pipeline, watcher, cli, ui
backends/ base, openai_compat, gemma, nvidia, ollama, apple, cpu
skills/keys-receipt-scanner/ Autonomous OS built-in skill (Lamp)
inbox/ processing/ processed/ failed/ exports/
```
## Autonomous OS skill
`skills/keys-receipt-scanner/` is a **built-in skill** in Autonomous OS format:
- `SKILL.md` + `skill.json` (`capabilities: ["vision"]`)
- Installs on any body that declares vision (Lamp, Reachy Mini β not Intern)
- Acts via HAL `GET :5001/camera/snapshot` then `python -m app.cli scan --image PATH`
- Does not load 12B weights on the robot
|