Home / use case

OpenNVR benchmarks every model its camera agent uses on a 4-core CPU

What they built

Varun Pratap Singh maintains OpenNVR, an open source video recorder with a camera agent built in: you can ask it what's happening on a camera, arm an alarm or search old footage, and it will soon look into events on its own.

Most people who run it have a small 4-core PC and no GPU, so the team tested every model the agent depends on (tool-calling language model, vision, speech to text, text to speech and image search) on a 4 vCPU machine, instead of picking models by reputation. The test scripts and raw numbers are in the repo so anyone can rerun them.

Four defaults changed, and several results were not what they expected, starting with a 4B language model that scored worse than a 1.7B one.

Setup

  • A 4 vCPU machine with no GPU; all timings on 4 CPU threads
  • Language models served through Ollama (granite4:micro, granite4:tiny-h, qwen3:1.7b, LFM2-1.2B-Tool, qwen3:4b)
  • Vision: qwen3-vl:2b-instruct, gemma3:4b, Moondream 0.5B
  • Speech: Parakeet TDT 0.6B v3 via onnx-asr and faster-whisper; TTS: Piper and Kokoro-82M
  • Search: SigLIP 2 B/16 vs CLIP ViT-B/32; detector YOLO26n vs YOLOv8n

How it works

  1. Language model: 20 investigation cases such as 'person at the loading bay at 01:14', each run 3 times; the model must check the camera, history and site rules before deciding.
  2. Vision: 128 labelled images with 3 questions each, including one about something that isn't there, where a 'yes' counts as a hallucination, plus a people count.
  3. Speech to text measured word error rate on the phrases people actually say to the agent; TTS checked whether it runs faster than real time.
  4. Search tested finding the right image from a text description like 'red truck at the dock'.

Results & numbers

  • granite4:micro passed 44 of 60 cases; qwen3:4b passed 9 of 60 because it thought for a long time and almost never called a tool. Granite 4 micro is now the default, at roughly twice the time per turn.
  • qwen3-vl:2b-instruct gave 0 false 'yes' answers out of 126 but needed 18 s per frame on CPU, so Moondream 0.5B (7.6 s, 22 false yeses) stays the CPU default and Qwen3-VL is used when a GPU is present.
  • Parakeet TDT 0.6B v3 hit 1.4% word error rate at 0.31 s per sentence, versus 3.3% and 1.42 s for faster-whisper base.en.
  • Kokoro-82M ran at 1.9x real time on 4 cores, so Piper (0.23x) was kept.
  • SigLIP 2 B/16 scored 0.672 mAP against CLIP's 0.571 for footage search; the detector moved from YOLOv8n (75 ms) to YOLO26n (62 ms).

Gotchas

  • In Ollama, the plain qwen3-vl:2b tag is the thinking version and ignores think: false; use the -instruct tag.
  • Telling the model to always look before describing made Granite check a camera when a user said 'thanks, that's all'. Small talk now goes to the model without tools, which helped every model.
  • Parakeet can't translate and doesn't do Hindi, so Whisper stays for sites that need that.
  • Rankings flip on a GPU, so test on the hardware your users actually have.
“Whether the model checks with its tools before deciding mattered more than parameter count.”— Varun Pratap Singh

Do it yourself

  1. List every model your local agent depends on (LLM, vision, speech, TTS, search).
  2. Write one small test per job with known answers, including a 'thing that isn't there' question to catch vision hallucinations.
  3. Run every candidate on the hardware your users have, with the thread count fixed, and repeat each case a few times.
  4. Read the traces, not just scores: check whether the agent actually calls tools before deciding.
  5. Pick defaults per hardware class, for example a fast small vision model on CPU and a more accurate one when a GPU is present.
  6. Reuse or compare against OpenNVR's published harness in examples/camera-agent/MODELS_AND_LATENCY.md.
Source: Varun Pratap Singh (DEV Community / opennvr.org) · 2026-10-19 · Added 2026-10-21. This write-up summarizes the original source; see it for full details.

Related

Siesta: a local overnight agent that reads fridge photos and receipts, then sleeps

Md Faijal Eaqbal built Siesta, a folder-watching agent on 3B open-weight models in Ollama that turns fridge photos, receipts and chat exports into one morning page, and spends zero tokens when nothing has changed.

Md Faijal Eaqbal (DEV Community) · 2026-10-21

Running a 14.4 GB Gemma 4 26B MoE on an 8 GB RTX 3050 with llama.cpp

A step-by-step llama.cpp setup that serves Gemma 4 26B A4B from an 8 GB RTX 3050 by keeping most expert layers in system RAM, reaching 131,072 tokens of context and scores level with the 12B that fits the card.

xbill (DEV Community) · 2026-10-19

A 4B local model on a 4 GB laptop GPU as a PASS/FAIL test evaluator

Qwen3 4B Instruct in Ollama (Docker, WSL2) grades a Spring Boot app's root-cause answers against expected ones, returning a JSON verdict, with every speed and memory number measured on a 4 GB Quadro T1000.

Vishnu Vardhan · 2026-10-15

Hardening a two-GPU Ollama server with systemd: 9.2 UNSAFE to 1.4

A homelab write-up that adds measured memory and CPU limits, restart policy and sandboxing to Ollama through systemd drop-ins on a server with two RTX 5060 Ti cards, testing after every round that the GPUs still work.

Gancho Stoychev (gshell.dev) · 2026-10-15