2026 · Built on the job, in active use
Surplus AI
Private, offline AI system for ASU Surplus
Problem
Surplus staff were typing hardware specs into a spreadsheet by hand, which introduced typos into the inventory. Writing product listings was also manual.
What I built
One connected pipeline: collect specs, build a master spreadsheet, and have AI write the listings, all on a private network that never touches the internet.
USB spec collector
system_profiler on each working Mac
Camera scanner
Vision OCR on the bottom label
Lookup table
Product, year, screen size, resale lane
Master inventory
Keyed on serial number
LLM
Strict prompt: never invent specs
Listings
Results saved
Node 1
10.0.0.1
Head
Node 2
10.0.0.2
14B model files still copying
Node 3
10.0.0.3
14B model files still copying
Node 4
10.0.0.4
14B model files still copying
Node 5
10.0.0.5
14B model files still copying
No uplink on the switch
Wi-Fi off
HF_HUB_OFFLINE=1, so the model software can't download anything
Model server only answers on localhost
How it works
- Five-node cluster
- Five M1 Mac minis on a gigabit switch with no internet uplink, fixed IPs, and passwordless SSH from the head node. Apple's MLX framework splits one model across several machines with tensor parallelism, so a 14B model can run across Macs that can't hold it individually.
- USB spec collector
- A Bash script on a USB stick runs system_profiler on each Mac, reads model, chip, RAM, storage, battery health, and macOS version, looks up year and screen size from a reference table, and de-duplicates by serial number.
- Camera inventory scanner
- For Macs that won't boot, a webcam photographs the bottom label and Apple's Vision OCR reads the model number, EMC, and serial. These are matched against the lookup table to get product, year, screen size, and resale lane (Retail, Auction, or Parts).
- Merge logic
- Both sources land in one master inventory keyed on serial number, so a camera scan never overwrites real data from a working machine.
- Surplus AI app
- A Flask server runs two model engines: Qwen 14B across four nodes, and Llama 3B on one node for quick responses. Replies stream word by word. A Listings tab builds a strict prompt from each row's real specs, and a Cluster tab polls each node over SSH every 5 seconds.
- Launchers
- One double-click starts everything, waits for the engines to respond, and opens the app. Closing it shuts down every node.
Status
Everything works except the 14B cluster engine, which is waiting on model files being copied to nodes 2 through 4.
Stack
- MLX
- Flask
- Bash
- Apple Vision OCR
- Qwen 14B
- Llama 3B