Files
Behavision/behavision.spec
Suriyakumarvijayanayagam dad04e8cda Behavision: face recognition for retail, edge to head office
Five components that ship as one product:

- behavision/  the recognition engine. RTSP ingest, YuNet detection, IoU
               tracking, ArcFace embeddings, a FAISS/SQLite gallery, and a
               FastAPI dashboard. Identity is decided once per TRACK from an
               average of at least three embeddings, never per frame.
- agent/       the Go edge agent: supervises the engine, holds a durable
               spool, and drains it to MQTT. Nothing is acked before the
               broker confirms.
- desktop/     the shop PC application (Wails + React + tray).
- server/      the cloud API, MQTT consumer, reports and assistant.
- web/         platform.loyaly.ai, the head-office app, embedded in the
               server binary.

The gallery stores 512-float embeddings and timestamps - no images unless
`app.store_faces` is switched on. Those embeddings are biometric personal
data under GDPR and India's DPDP: template inversion reconstructs a
recognisable face from an ArcFace vector, so data/behavision.db is treated
as a biometric database and DELETE /api/visitors/{id} is a real erasure.

CLAUDE.md carries the reasoning behind every non-obvious decision here,
including the ones that were measured and the ones that were wrong first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HViLj9gYNRtSr7YVZmW5sn
2026-09-04 11:14:18 +05:30

103 lines
3.0 KiB
Python

# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller spec for the Behavision engine.
One-folder, not one-file. A onefile build of this is ~200 MB and extracts the
whole thing to a temp directory on **every** start, which on a store PC means a
multi-second delay and an antivirus scan each time the service restarts.
Models are NOT bundled. They are ~200 MB on their own and `setup-models`
already downloads them with a resumable `.part`-then-rename; bundling them
would triple the installer and force a re-sign for a model change. They land in
the writable state root (see behavision/paths.py), not next to the code.
Build:
pyinstaller behavision.spec --noconfirm
Output:
dist/behavision/behavision.exe
"""
import sys
from PyInstaller.utils.hooks import collect_dynamic_libs, collect_submodules
block_cipher = None
# The dashboard and the default config are read from disk at runtime, so they
# have to travel with the code. They go to the *install* root; anything the app
# writes goes to the state root instead.
datas = [
("behavision/static", "behavision/static"),
("config/default.yaml", "config"),
]
# onnxruntime and cv2 load native libraries that PyInstaller's static analysis
# cannot see through. Missing these is the classic "works in the venv, dies in
# the bundle" failure.
binaries = []
for pkg in ("onnxruntime", "cv2"):
try:
binaries += collect_dynamic_libs(pkg)
except Exception:
pass
hiddenimports = [
# Imported lazily inside functions, so the graph never sees them.
"dotenv",
"uvicorn.logging",
"uvicorn.loops.auto",
"uvicorn.protocols.http.auto",
"uvicorn.protocols.websockets.auto",
"uvicorn.lifespan.on",
]
for pkg in ("onnxruntime", "faiss"):
try:
hiddenimports += collect_submodules(pkg)
except Exception:
# faiss is optional - the numpy fallback is exact and identical, just
# slower, so its absence must not fail the build.
pass
a = Analysis(
["behavision/__main__.py"],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
runtime_hooks=[],
# Nothing here draws a window; matplotlib/tkinter would add ~40 MB of
# payload that no code path can reach.
excludes=["tkinter", "matplotlib", "PyQt5", "PySide2", "IPython",
"notebook", "pytest"],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="behavision",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # UPX-packed binaries are a common AV false positive
console=True, # the tray app is the GUI; this is the engine
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
name="behavision",
)