Skip to content

AI auto scripture: local transcription + AI reference detection (opt-in) - #3579

Open
josephomills wants to merge 252 commits into
ChurchApps:devfrom
josephomills:feat/ai-auto-scripture
Open

AI auto scripture: local transcription + AI reference detection (opt-in)#3579
josephomills wants to merge 252 commits into
ChurchApps:devfrom
josephomills:feat/ai-auto-scripture

Conversation

@josephomills

@josephomills josephomills commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Builds on #3578 (its commit is included here) — happy to rebase once that lands, or fold both together if preferred.

What

AI auto scripture (opt-in, off by default): FreeShow listens to the sermon, transcribes locally with whisper.cpp, detects scripture references — both spoken ("John chapter 3 verse 16", instant local matching) and quoted ("for God so loved the world…", via a bring-your-own-key LLM) — and either projects them automatically or offers one-click suggestion cards in the scripture drawer.

https://github.com/ChurchApps/FreeShow — demo video to follow.

Features

  • Live panel in the Scripture drawer: status, live transcript ticker (expandable), suggestion cards with confidence badges, restore-previous, manual-override auto-pause
  • Fully local option: a Local (Ollama) provider — Gemma / Llama / Qwen / Mistral / Phi on the user's own machine, no key, nothing leaves the device
  • Two-tier detection: instant local matching for explicit references (works with zero network/keys) + optional LLM detection of quoted/paraphrased verses (Anthropic / OpenAI / Gemini via plain REST, user's own key)
  • Quality gates: confidence-gated auto-projection (quoted verses default to suggestions), quoted matches verified against actual verse text across the user's selected translations, dedupe/cooldowns, music segments (♪) excluded from detection
  • Voice commands (separate opt-in): "go to the next verse", "give me verse five", "give me NIV", "give me another translation" — imperative-only, and only while a scripture is live
  • Interpretation mode: per-window language auto-detect for services with live interpreters, constrained to declared languages, with a "detect scriptures from" selector; multi-language transcript tagging
  • Multi-translation search with select-all, display-translation choice (drawer selection vs matched translation)
  • Session context: bare "look at verse twelve" resolves against the passage currently live
  • Dedicated AI Scripture settings tab, guided whisper install per platform, model downloads with progress/resume

Guarantees (please review these first)

  • Off by default: no UI, stores, audio capture, downloads or network activity unless explicitly enabled; mic starts only on the operator's click
  • Privacy: audio never leaves the device (transcription is fully local); only short transcript text windows go to the user's chosen LLM provider, only while listening with a key saved; keyless mode makes zero network calls; the transcript is memory-only
  • Keys: stored in the ACCESS store (non-portable, never cloud-synced — same as the Canva credentials precedent); raw keys never reach the renderer (booleans only); flagged for a possible safeStorage follow-up
  • Zero new npm dependencies (axios + yauzl already present; whisper.cpp is a runtime download/detection)
  • Supply chain: Windows binary auto-download is SHA-256-pinned against the official whisper.cpp release; macOS/Linux use system installs (brew/package manager) or a user-picked binary — no unofficial mirrors

Platform matrix

Platform Whisper
Windows x64 official release auto-download (checksum-verified)
macOS (Intel + Silicon) brew install whisper-cpp detection or custom path
Linux package manager detection or custom path

Testing

  • 141 unit tests (vitest, colocated) over detection, transcription windowing, providers, whisper management, voice commands
  • Field-tested live at a conference on macOS. Honest note: transcription accuracy with a laptop mic capturing room audio is limited — a direct feed from the sound desk (any input selectable in settings) is the intended setup and testing on that is ongoing. Detection precision is deliberately conservative (explicit references only auto-project; everything ambiguous becomes a suggestion).
  • Would love testers on Windows/Linux and with mixer feeds.

Happy to split, adjust scope, or rework anything — I kept every touched upstream file minimal and matched existing patterns (LTC worklet, ffmpegManager, ACCESS store, Material inputs, i18n) throughout.


Updates since opening

  • Local Ollama provider — fully offline quoted-verse detection (Gemma 3/4, Llama, Qwen, Mistral, Phi via ollama.com), same prompt/schema as the cloud providers, no API key; completes the everything-stays-local story
  • Auto-projection fixes from live field testing: the manual-override pause now only triggers when the operator overrides a scripture the AI itself projected (ordinary song/slide changes no longer silently pause auto mode) and self-resumes after a minute
  • Spoken-form coverage: "Matthew 12 4", "12, 4", "12. 4", "12-4", word numbers, and cued chapter-only ("turn to Matthew chapter five") all auto-project; a bare "Matthew 5" mid-sentence stays a suggestion
  • Music gating: whisper wraps singing in ♪ and hallucinates lyrics — music segments are shown faded in the transcript but never feed detection
  • Interpretation hardening: a declared "languages spoken" set constrains per-window language detection; out-of-set guesses are re-checked against the detection language
  • Capture/UX: system-default mic auto-selection, speech-tuned capture (AGC + noise suppression), sample-rate-aware resampling, resumable checksum-verified downloads with live progress

Test suite is now at 141.

@josephomills

Copy link
Copy Markdown
Contributor Author

Pushed a round of updates from live field testing at a conference, plus a fully-local AI option:

  • Local (Ollama) provider — quoted-verse detection with Gemma 3/4, Llama, Qwen, Mistral or Phi running on the user's own machine: no API key, free, offline, and nothing ever leaves the device. Cloud providers remain optional alternatives.
  • Auto mode fixes: the manual-override pause was too eager (any song/slide change paused auto-projection until manually resumed) — it now only triggers when the operator overrides a scripture the AI itself projected, and self-resumes after a minute.
  • Real spoken forms auto-project: preachers rarely say the words "chapter" and "verse" — "Matthew 12 4" (and every punctuation whisper produces for it: "12, 4", "12. 4", "12-4", word numbers) plus cued chapter-only ("turn to Matthew chapter five") now project automatically; a lone "Matthew 5" mid-sentence still asks first.
  • Music never triggers detection: whisper hallucinates lyrics for songs it hears (♪) — those segments show faded in the transcript but are excluded from detection and the LLM.
  • Interpretation mode: declared languages constrain whisper's per-window guess; out-of-set windows are double-checked against the detection language.
  • Plus capture-quality fixes (auto mic selection, speech-tuned gain/noise settings, sample-rate-aware resampling) and resumable, checksum-verified model downloads.

141 unit tests passing. The PR description's update section has the full list.

@josephomills
josephomills force-pushed the feat/ai-auto-scripture branch from 3aca772 to 0e03d7d Compare August 6, 2026 14:20
@josephomills
josephomills changed the base branch from main to dev August 6, 2026 14:21
@AlloDel

AlloDel commented Aug 6, 2026

Copy link
Copy Markdown

Left a longer note on #3547 about complementary fully local streaming work I have been testing (no API keys / no LLM for the core path). Happy to collaborate or wait for maintainer preference so we do not step on this review.

@josephomills

Copy link
Copy Markdown
Contributor Author

@AlloDel your driver is merged and wired up — engine selector in settings, resumable model download, packaging config. Thank you, the VAD choice and the Electron notes were spot on.

One change to your driver, after live testing. The decoder deafness you documented goes deeper than stream reuse: any chunked feeding (fresh, reused, or persistent stream) intermittently decodes short utterances like "next verse" to nothing. The same samples in one batch acceptWaveform on a fresh stream decode correctly every time. So the driver now buffers each utterance (1s pre-roll) and batch-decodes when the VAD closes, waiting 0.5s of real tail audio first so a soft last word isn't cut mid-word. VAD retuned: threshold 0.3, min-silence 0.8s, min-speech 0.15s. If your live-testing notes contradict any of this, I'd like to know.

What I'd like you to review: downloading the sherpa runtime on demand — finishing your zero-setup goal. Right now the npm natives ship in the installer (~16 MB/platform) for everyone. The plan, mirroring our ffmpeg/whisper downloaders:

Exact-pin sherpa-onnx-node@1.13.4; exclude the platform packages from electron-builder, keep the JS wrapper bundled
Self-hosted per-platform ZIPs with pinned SHA-256s (plus proper license files — the npm tarballs omit them)
Extract to userData/bin/sherpa/ with sherpa-onnx-node/ copied beside it, so addon.js's own ../sherpa-onnx- probe resolves — no patching
macOS: clear quarantine + ad-hoc codesign after extraction (darwin-x64 ships its .node unsigned)
One "Enable" gate chains runtime (~7 MB) + model (662 MB) downloads
Questions:

Does the sibling-layout require() hold across sherpa-onnx-node versions you've used?
Anything from your live testing this misses?
Any concern with self-hosting the natives vs keeping them npm-managed?
Whisper stays the default; settings, multilingual and detection are unchanged.

@vassbo
vassbo deleted the branch ChurchApps:dev August 7, 2026 11:42
@vassbo vassbo closed this Aug 7, 2026
@vassbo vassbo reopened this Aug 7, 2026
@josephomills
josephomills force-pushed the feat/ai-auto-scripture branch from 5c74a02 to 5204dc3 Compare August 7, 2026 13:16
@vassbo

vassbo commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

I will review this soon.

But just a couple things for now I would like changed:

  • Can all the new locale strings be placed under their own parent key(s) at the bottom in en.json
  • Can everything AI related have their own parent folders, so electron/ai/... & frontend/ai/...

@josephomills

Copy link
Copy Markdown
Contributor Author

Alright @vassbo
Will do

New AI_SCRIPTURE_* channels (typed in Main/ToMain), audio data filtered
from IPC logging like TIMECODE_AUDIO_DATA, and the orchestrator that
owns the transcriber + detection lifecycle. API keys live in the ACCESS
store (non-portable, never cloud-synced) and only reach the renderer as
booleans.
- whisperManager: binary/model lifecycle following the ffmpegManager
  pattern (Windows auto-download, system PATH probe, custom path,
  Hugging Face ggml model downloads with progress)
- transcriber: 16kHz PCM ring buffer, RMS silence gate, 7s windows with
  1s overlap, whisper-cli per window or persistent whisper-server,
  noise/low-confidence segment filtering
- detection: tier-1 local reference matching (spoken word-numbers,
  ordinal book prefixes, cue-gated confidence) and tier-2 LLM detection
  with dedupe, cooldowns and single-flight scheduling
- providers: Anthropic / OpenAI / Gemini over plain REST (axios) with
  structured JSON output, normalized error codes and key test calls
- 66 colocated vitest tests
AudioWorklet mirrors the LTC timecode listener (48k to 16k mono Int16,
1s chunks over IPC). The dispatcher validates detections against the
selected bibles (collection-aware), verifies quoted verses against
actual verse text across translations, clamps ranges, and projects via
activeScripture + playScripture so outLocked, history and templates all
apply. Auto mode is confidence-gated with a min-display queue, and a
manual override watcher pauses auto-projection whenever the operator
changes the output slide themselves.
Collapsible panel in the scripture drawer with live transcript view and
one-click suggestion cards; setup popup covering whisper install per
platform (with custom binary and model paths), provider API keys with
test, search-bible multi-select with select all, display translation
and behavior settings. Feature is off by default behind a toggle in
Settings > Other with a persistent privacy note.
- Anthropic: select the text content block instead of content[0] (thinking
  models lead with a thinking block) and raise detection max_tokens to 4096
- never abort an in-flight LLM call for a new segment; mark a rerun and
  refresh with the newest transcript once it settles (a >6s provider
  previously starved tier-2 completely during continuous speech)
- unify book numbering on the 66-book canon via a canonNumber book-table
  field so tier-1/tier-2 dedupe can't miss across domains
- pause tier-2 on permanent errors (model_not_found/invalid_request)
- redact API-key-shaped strings from provider error messages
- replace retired/nonexistent Gemini model ids with gemini-2.5-flash/pro
- 30s watchdog on whisper-cli runs (SIGTERM then SIGKILL)
- overlap dedupe trims segment text, not just timestamps
- no child can spawn after stop() (re-checked after every await)
- successful server respawn resets the failure counter
- server health check verifies the responder is whisper-server before
  any audio is posted; respawns on a fresh port otherwise
- temp dir 0700 / WAV files 0600
@vassbo vassbo reopened this Aug 26, 2026
@vassbo

vassbo commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

@josephomills The plan is to review this soon, and get it merged as a hidden/experimental feature before the next beta release in a few weeks.

@josephomills

Copy link
Copy Markdown
Contributor Author

Great! Looking forward to a lot of feedback from different speakers/preachers, different languages & different computers.

I'm also looking up ways to simplify this feature:
Eg a default multilingual speech model and improving STT quality. Already seeing massive improvements with the rework.

@josephomills

josephomills commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

🎉 Upstream win: all four sherpa-onnx PRs I sent for this feature are merged and released in sherpa-onnx-node@1.13.7:

One more still in review: k2-fsa/sherpa-onnx#3895 adds beam search + hotwords — correctly spells rare biblical names like Melchizedek and Zerubbabel in testing.

Will definitely implement these in the STT rework (1.13.6 → 1.13.7 is purely additive) and share the measured improvements after more testing. @vassbo @AlloDel

@josephomills

Copy link
Copy Markdown
Contributor Author

Hey @vassbo — the rework reads much cleaner, especially detection moving to the renderer and the generic suggestion model. A few things went out along the way and I'd rather ask than undo something deliberate.

Granularity — the one I'd most like to pin down. In August you asked for the AI code split into more files, so I broke scripture into ~10 modules; the recent cleanups go the other way (ten quote-match modules into one 1,660-line engine, whisper manager/transcriber/wrapper into one file). No strong preference either way — I'd just like a rule I can apply so I stop churning your files. Is it "one file per cohesive subsystem, split only by ownership"? And does that extend to the remaining scripture modules, or leave those as they are?

Vocabulary biasingbibleVocabulary.ts / prompt.ts are gone. Those were biasing whisper on book names before mishearings ("Philippines" for Philippians) ever reached the parser. Wrong layer, or not worth its keep? Happy to re-wire it renderer-side if it's the former.

LLM error codescodedError and the provider quirks came out, but DetectionCoordinator still branches on invalid_key / model_not_found / rate_limited. completeLLM now only emits invalid_key or server_error, so tier 2 never disables itself on a bad key and never backs off when a provider rate-limits us — it keeps calling every window. Slimmer codes back, or should I rewrite the coordinator around messages?

redactSecrets — went out with the same commit. It stripped sk-… / AIza… from provider errors before they hit the renderer and the logs (OpenAI 401s echo a partial key). Deliberate, or collateral? A few lines to restore.

Nemotron partials — the partial re-decode is gone, so text only leaves the driver when the utterance closes. Those partials emitted real segments, so detection saw words ~1.2s in; now mid-flow speech waits for a pause, up to the 12s VAD cap. Short utterances are fine — it's continuous preaching where it shows. Complexity, or did it misbehave? Can measure before/after if that helps.

Voice commands — happy to go LLM-first, but two things I can't see how they land. Today commands need no key and no setup; LLM-only means an API key, or installing Ollama and pulling a model. And tier 2 is gated on LLM_MIN_NEW_WORDS = 15, so a two-word "next verse" wouldn't trigger a call until another 13 words arrive — while giving commands their own call means a round-trip per utterance. With partials gone too, that's utterance-close plus gate plus round-trip before a slide moves. Is the thinking that commands ride the existing detection call, or something separate? Not arguing to bring the old matcher back, just want to understand the shape you have in mind.

Happy to pick up whichever of these you want and leave the rest.

@vassbo

vassbo commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

@josephomills I'm still not done. But taking a break.

  • Granularity: 10 files for "one thing" is too much. I merged it back to more easily trim it down, but I'm not done yet, 1500 lines in one file is still a bit much.
  • Vocabulary biasing: Not sure about this one as it's a lot of strings only for English, so I removed it. But I guess we can have the most common mishearings, this should apply to both Whisper and Nemotron.
  • LLM error codes: I want all error messages to be direct strings (English only), and not translatable codes, as it's much easier to maintain that way.
  • redactSecrets: I think it's not needed as the key is already partially redacted.
  • Nemotron partials: This can be re-added if it's kept simple, but it should work with the new system.
  • Voice commands: It's too much to maintain, and it would only work for English. LLMs are great at understanding any language.

@josephomills

josephomills commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@vassbo Thanks, that's helpful. No rush.

  • Error codes: agreed, I'll rewrite the coordinator around messages.
  • redactSecrets: fair enough, dropping it.
  • Vocabulary + partials: I'll take both on.
  • Voice commands: it wasn't English-only — a per-language table (en/es/de/fr/no/pt), ~17 short word lists each, matched against the spoken language plus English. Data rather than code, so it could live in the locale files and go through the translators instead of you.
  • Whisper could translate to English, so one English grammar would cover it. Nemotron can't — transducers are monotonic — so once 3.5 lands multilingual it'll emit e.g. Spanish text that still needs matching. That's the case a phrase table covers and translation can't.
  • STT rework: the sherpa 1.13.7 work I mentioned (multilingual 3.5, language pinning, utterance close, per-token confidence) is ready. Happy to rebase it onto your cleanups — good time to push it here, or would you rather finish the restructure first?

@vassbo

vassbo commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Voice commands: Right, I remember now. But still only accounts for some languages. I like the idea, but would still not be good to maintain. Let's get the LLM to work well with voice commands at least. Maybe another time...
STT rework: Great! I'll do some more restructuring first.

@vassbo vassbo closed this Sep 5, 2026
@josephomills

Copy link
Copy Markdown
Contributor Author

Voice commands: Alright, LLM will work to an extent. My only concern is the latency and dependence on network connection it'll introduce. Will think of how to mitigate that.

STT rework: Sure. Just let me know when to push

@vassbo vassbo reopened this Sep 5, 2026
@josephomills

josephomills commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Also wanted to ask if you'll like to maintain having a choice of STT model for scripture specifically (ie. Nemotron & whisper) as it is now, or you'd want to have one moving forward.

I started with the 2 because whisper was multilingual but not a streaming model as Nemotron. But I found Nemotron 3.5 ASR multilingual and that's what my STT rework uses. And the I think the 'interpretation mode' feature I had is a bit pre-mature, needs more work.

I can see whisper being useful in non-scripture aspects like on the Audio tab for audio files (where Nemotron won't be ideal). But that'll be an architectural call.

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.

7 participants