Skip to content

feat(server): managed local whisper transcription — the zero-key default (MEET-3) - #370

Closed
eliotlim wants to merge 5 commits into
feat/meet-5-recorder-uifrom
feat/meet-3-local-whisper
Closed

eliotlim wants to merge 5 commits into
feat/meet-5-recorder-uifrom
feat/meet-3-local-whisper

Conversation

@eliotlim

@eliotlim eliotlim commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Problem

Transcription (MEET-2 / #367) had no local backend — the product default must work with zero cloud keys (owner directive: cloud supported, local default). Board: MEET-3.

Solution

LocalWhisper engine behind MEET-2's TranscriptionEngine seam: spawns host-installed whisper.cpp whisper-cli + ffmpeg subprocesses (shell-less, literal argv, private mkdtemp scratch, SIGKILL on abort, cleanup on all paths) — no native npm addon, no ABI coupling; CI needs neither binary. ffmpeg normalizes any browser audio to 16 kHz mono WAV; whisper JSON is strictly validated. Default model: multilingual Whisper base (~142 MiB) via the existing owner-gated download flow (now hardened: filename traversal guard, can't clobber the chat model). Concurrency clamp: engine-level semaphore (2 jobs, distinct busy error) + per-IP rate limit on the local path → 429 + Retry-After. Settings → AI shows runtime/model state + download affordance (i18n ×4). Env overrides: OPENBOOK_WHISPER_BIN, OPENBOOK_FFMPEG_BIN. Setup docs: docs/local-transcription.md. Stacked on #367 — merge after it.

Key files: packages/server/src/ai/{whisper.ts,service.ts,routes.ts}, packages/server/src/ai/whisper.test.ts, packages/server/src/transcription.test.ts, packages/ui/src/components/AiSettings.tsx.

Before / After

Behavior Before After
Transcribe with no cloud key actionable 400 works once whisper-cli/ffmpeg installed + model downloaded — opt-in native test (OPENBOOK_TEST_WHISPER=1) incl. real-speech smoke
Missing runtime/model n/a clear error pointing at Settings → AI; resolver discovers the model without restart
Parallel local jobs n/a max 2 concurrent + per-IP limit; overflow 429 + Retry-After — tested at engine and HTTP level
Abort mid-transcription n/a child SIGKILLed, scratch dir removed, semaphore slot released

No visual change beyond one new Settings section (text-only, i18n'd).

Test procedure

VITEST_MAX_WORKERS=1 pnpm verify (green, exit 0, no native deps). Native: install whisper.cpp + ffmpeg, then OPENBOOK_TEST_WHISPER=1 OPENBOOK_MODELS_DIR=... pnpm --filter @book.dev/server exec vitest run src/transcription.test.ts.

Operational notes

Hosts wanting local transcription install whisper-cli + ffmpeg (PATH or env overrides) and download the model in Settings → AI — see docs/local-transcription.md. No migration. Local path is reachable by anonymous readers of public pages (consistent with existing AI-route policy) — bounded by the new clamp. Accepted risk (documented): ffmpeg protocol whitelist retains file (requires writer privilege to matter). Binary packaging/distribution is follow-up work.


Verify: green serialized. Reviews cleared: security (Sasha — BLOCK→CLEAR after concurrency clamp), code (Quinn). Final HEAD 0a49bb1.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
app.book.pub Ready Ready Preview Oct 4, 2026 2:18pm UTC

Request Review

@eliotlim
eliotlim changed the base branch from main to feat/meet-5-recorder-ui October 4, 2026 14:08
@eliotlim
eliotlim deleted the branch feat/meet-5-recorder-ui October 4, 2026 14:09
@eliotlim eliotlim closed this Oct 4, 2026

This branch was successfully deployed

1 active deployment
Preview — 23370d4d Deployed Oct 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant