From c25d3ab150ed1928580d86acd357839c2ede6123 Mon Sep 17 00:00:00 2001 From: avi Date: Mon, 14 Sep 2026 21:05:41 -0500 Subject: [PATCH] Bundle the whisper base model + one-shot standalone provisioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - models/hf-cache/ vendors Systran/faster-whisper-base (142MB, gitignored); engine spawn pins HF_HUB_CACHE there, so transcription works with a cold ~/.cache and fully offline - verified: HOME=/tmp/fakehome HF_HUB_OFFLINE=1 model loads and transcribes via the bundled cache alone - setup-standalone.sh: idempotent fresh-clone provisioning (local venv, model copy-or-download, engine boot smoke on :8123) — runs green - backend/README rewritten for the desktop bundled-lite reality (was pointing at a nonexistent root README/docs and a Postgres quickstart) Kotlin suite still green (gradlew test, 0 failures) --- .gitignore | 1 + .../kotlin/com/shonar/desktop/DesktopState.kt | 3 + backend/README.md | 21 ++++--- setup-standalone.sh | 61 +++++++++++++++++++ 4 files changed, 77 insertions(+), 9 deletions(-) create mode 100755 setup-standalone.sh diff --git a/.gitignore b/.gitignore index cd74f3a..0977100 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ backend/__pycache__/ **/__pycache__/ *.pyc .dev-restart-flag +models/hf-cache/ diff --git a/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt b/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt index 418feec..ef63a8c 100644 --- a/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt +++ b/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt @@ -311,6 +311,9 @@ class DesktopState(private val appDir: File = defaultAppDir()) { "SHONAR_STORAGE_PATH" to File(appDir, "storage").absolutePath, "SHONAR_TRANSCRIPTION_PROVIDER" to "faster_whisper", "SHONAR_TRANSCRIPTION_MODEL" to "base", + // Bundled whisper model: shipped in models/hf-cache so + // transcription works with a cold ~/.cache or offline. + "HF_HUB_CACHE" to File(repo, "models/hf-cache").absolutePath, ) // Summarizer preference: LAN inference server (H200) when // its key is present in the environment, then local Ollama. diff --git a/backend/README.md b/backend/README.md index 0f73b7f..815d92f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,16 +1,19 @@ -# S.H.O.N.A.R. backend +# S.H.O.N.A.R. backend (desktop bundled-lite) -FastAPI + PostgreSQL backend for the S.H.O.N.A.R. Android app. +FastAPI engine for SHONAR Desktop, running standalone on this machine: +SQLite + in-process queue, self-migrating on boot. No Postgres/Redis/Docker. -See the repository root [README](../README.md) and `docs/` for full documentation. - -## Quick start (development) +## Setup (this repo is self-contained) ```bash cd backend -uv venv .venv && uv pip install -e ".[dev]" -cp ../.env.example .env # then edit SHONAR_SECRET_KEY etc. -uvicorn shonar.main:app --reload --port 8000 +uv venv .venv && uv pip install -p .venv/bin/python -e ".[dev,faster-whisper]" ``` -Tests: `pytest` · Lint: `ruff check .` · Migrations: `alembic upgrade head` +The app (DesktopState) creates this venv's env vars itself, including +`HF_HUB_CACHE=models/hf-cache` — the `base` faster-whisper model is vendored +under `models/hf-cache/` (gitignored; copy from any HF cache to reprovision). + +Tests: `.venv/bin/python -m pytest` (SQLite by default; point +`SHONAR_TEST_DATABASE_URL` at Postgres to test that path) · Lint: `ruff check .` +· Migrations: `alembic upgrade head` diff --git a/setup-standalone.sh b/setup-standalone.sh new file mode 100755 index 0000000..073c642 --- /dev/null +++ b/setup-standalone.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# One-shot provisioning for a fresh clone of SHONAR Desktop: +# local engine venv + vendored whisper base model. Idempotent. +set -uo pipefail +cd "$(dirname "$0")" + +echo "==> backend venv (local, editable install of THIS backend)" +if [ ! -x backend/.venv/bin/uvicorn ]; then + command -v uv >/dev/null || { echo "uv not found — install it: curl -LsSf https://astral.sh/uv/install.sh | sh"; exit 1; } + (cd backend && uv venv .venv --python 3.11 && uv pip install -p .venv/bin/python -e ".[dev,faster-whisper]") +else + echo " already present" +fi + +echo "==> whisper base model -> models/hf-cache" +MC="models/hf-cache/models--Systran--faster-whisper-base" +if [ -d "$MC/snapshots" ] && [ -n "$(ls -A "$MC/snapshots" 2>/dev/null)" ]; then + echo " already present" +else + # Prefer copying an existing local copy (works offline)... + src="" + for c in "${HF_HUB_CACHE:-$HOME/.cache/huggingface/hub}/models--Systran--faster-whisper-base" \ + "$HOME/.cache/huggingface/hub/models--Systran--faster-whisper-base"; do + [ -d "$c/snapshots" ] && { src="$c"; break; } + done + mkdir -p models/hf-cache + if [ -n "$src" ]; then + echo " copying from $src" + cp -r "$src" models/hf-cache/ + else + echo " no local copy — downloading from HuggingFace (needs internet once)" + backend/.venv/bin/python - <<'EOF' +from huggingface_hub import snapshot_download +snapshot_download("Systran/faster-whisper-base", cache_dir="models/hf-cache") +EOF + fi +fi + +echo "==> smoke: engine boots on :8123 with the bundled cache" +tmp=$(mktemp -d) +SHONAR_SECRET_KEY="bootstrap-verify-secret-0123456789abcdef" \ +SHONAR_DATABASE_URL="sqlite+aiosqlite:///$tmp/e.db" \ +SHONAR_QUEUE_BACKEND=inline SHONAR_AUTO_MIGRATE=1 \ +SHONAR_STORAGE_PATH="$tmp/storage" \ +HF_HUB_CACHE="$PWD/models/hf-cache" \ +backend/.venv/bin/uvicorn shonar.main:app --app-dir backend --port 8123 \ + >"$tmp/uvicorn.log" 2>&1 & +pid=$! +ok="" +for _ in $(seq 1 30); do + sleep 1 + curl -sf -m 2 http://127.0.0.1:8123/api/v1/readyz >/dev/null && { ok=1; break; } +done +kill $pid 2>/dev/null; wait $pid 2>/dev/null +rm -rf "$tmp" +if [ -n "$ok" ]; then + echo " engine ready — provisioning complete. Start the app: ./desktop-dev.sh" +else + echo " ENGINE FAILED TO START" >&2 + exit 1 +fi