Skip to content

Smart AI STT Auto Scripture & LLM APIs - #3579

Merged
vassbo merged 292 commits into
ChurchApps:devfrom
josephomills:feat/ai-auto-scripture
Sep 9, 2026
Merged

Smart AI STT Auto Scripture & LLM APIs#3579
vassbo merged 292 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

Speakers just say "next chapter" - the grammar required an order like
"go to the next chapter", so the plain form never matched and the
command appeared broken in live use.

Without an imperative the phrase must END the utterance, which is what
separates an instruction from narration that contains the same words:
"in the next verse paul says something amazing" keeps talking, so it
still never fires, and a preposition right before the phrase ("we will
see that in the next chapter") is still read as narration. Applies to
relative movement, translation cycling and named translations. Verse
and chapter jumps stay imperative-only: a bare "verse 5" is already
resolved against the live passage by detection, and a bare "chapter 4"
is usually the tail of a spoken reference.
… word

The VAD's confidence decays before a word actually ends, and audio was
only fed to the recognizer while it flagged speech - the tail of an
utterance's last word arrived after the flag dropped and went nowhere,
so "next verse" came out as "next". While an utterance is open, keep
feeding it through the dip; the VAD still decides where the utterance
closes, so this only adds trailing audio.
Live testing kept losing short utterances ("next verse" transcribed as
"Next", or nothing at all) even after the audio was proven to reach the
recognizer. Measured against every feed pattern the binding supports,
the NeMo transducer decodes reliably in exactly one shape: a fresh
stream given the whole utterance in a single acceptWaveform call. Small
incremental chunks - on a fresh stream, a reused stream or one
persistent session stream - intermittently decode short utterances to
nothing, which is the deafness the original driver worked around but
could not eliminate.

The driver now buffers the utterance's audio (starting with 1s of
pre-roll so the encoder has lead-in before the first word) and batch
decodes it when the VAD closes. The close waits for half a second of
real tail audio first: a soft last word makes the VAD's silence
countdown run during the word, so the close can land mid-word - and if
speech resumes in that window it was a pause, so the utterance simply
continues. VAD tuning (threshold 0.5 -> 0.3, min silence 0.6s -> 0.8s,
min speech 0.25s -> 0.15s) keeps quiet word endings counted as speech
in the first place.

Validated against multi-utterance session audio: every utterance now
produces a segment, split and soft-tailed commands included.
The streaming engine emits one segment per utterance, so a breath in
the middle of a command splits it ("next" / "verse") and per-segment
matching can never see the whole phrase. CommandStream joins the
segments of the last few seconds and runs detection over the joined
tail; a command only fires when its matched phrase reaches into the
newest segment, so text that already had its chance never re-fires
from a later join, while genuinely repeating the command does.

Also from review: the start path now rejects the streaming engine
cleanly when the native addon is unavailable ("nemotron_unsupported"
instead of an unsanitized MODULE_NOT_FOUND require stack - the model
being downloaded does not imply the addon loads), and a start failure
message is sanitized like every other surfaced error.
The unsupported warning replaced the whole engine section, so a 660 MB
model downloaded before the addon stopped loading had no way to be
reclaimed from the UI.
The model URLs pointed at the repo's mutable "main" ref with only a
size check on arrival, so what landed was whatever the CDN served. The
URLs are now pinned to a specific revision and every file is verified
against its SHA-256 (the LFS checksums Hugging Face publishes for that
revision, plus the VAD gate's hash) before it counts as downloaded - a
corrupt, truncated or substituted file is deleted and reported instead
of landing. Files left by an earlier interrupted download are verified
the same way before being skipped.

Mid-transfer drops still retry with a ranged request inside one
download call; a new call starts the file clean.
The feature's 108 strings were spread through the existing "settings" and
"scripture" sections, which every translator and most feature branches
touch. They now live under one "ai_scripture" parent at the bottom of
en.json, and each key drops the redundant "ai_" prefix the parent already
carries ("ai_whisper_model_tiny" -> "whisper_model_tiny").

settings.ai_scripture deliberately stays where it is: the settings tab
label is resolved from the tab id as settings.{tabId}, so moving it would
leave the tab and the profile permission list unnamed.

Two strings collapsed onto the same name once the prefix was dropped, so
the long notice shown when the feature is off is now "privacy_notice",
leaving "privacy" to the section heading that owns the _local/_llm/_keys
strings under it.
The feature was spread across src/electron/aiScripture, src/frontend/audio
and two components sitting in unrelated component folders. It now lives in
one place per process:

  src/electron/ai/                 (was electron/aiScripture)
  src/frontend/ai/                 (was the aiScripture files in audio/)
  src/frontend/components/ai/      (was settings/tabs + drawer/bible)

The settings tab component is renamed AiScriptureSettings.svelte, since
sitting beside AiScripturePanel.svelte it needed to say which one it is.

The shared types stay in src/types/AiScripture.ts, which is where every
type crossing the electron/renderer boundary lives.

Both components moved a directory level up, so their relative imports lost
one "../"; the panel's same-folder import of the bible helper is now an
explicit path, and the whisper/audio helper imports follow their modules.
@josephomills
josephomills force-pushed the feat/ai-auto-scripture branch from 5204dc3 to 082dce5 Compare August 10, 2026 18:00
@josephomills

Copy link
Copy Markdown
Contributor Author

Hi @vassbo
I pushed the changes you requested yesterday for your review, whenever you can. Thanks

@vassbo

vassbo commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

I made some changes.

  • Can the providers/models setup be general and not scripture specific, because the same models might be useful for other things. - That includes all the IPC messages should not contain SCRIPTURE, and most of the locale keys also does not need scripture in the key name.
  • Can we organize the code into more folders/files.

@josephomills

Copy link
Copy Markdown
Contributor Author

I'll leave the bubble work for now.
Pushed a few more updates.

@vassbo

vassbo commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

@josephomills Do you have any more plans on this specific PR, or what's the status?
I'm going to do a final cleanup, and get it merged tomorrow I guess.
Of course after this is merged you can make a new PR if there's more.

@josephomills

Copy link
Copy Markdown
Contributor Author

Okay sounds great.
For now, I may probably work on a few minor improvements. But feel free to go ahead with the merge and any beta release. Like you said, I'll just create a new PR for any more enhancements after you merge. eg. I'm waiting on my PR on sherpa to add hotwords/contextual-biasing.

Ultimately, my plan is basically to get STT, detections and quote-matching as accurate and fast as possible. I'm coming from a background of scripture-heavy sermons, no notes before service, switching between translations fluently and we already have a skilled projection team (not to boast, lol). We've tried other dedicated AI scripture tools which haven't worked (in terms of accuracy, speed, limit on available translations & other features). So if we can get this to work well, then I can say confidently that it'll work fine for all types of congregations. And I'm personally not for building another AI scripture software, I'd rather contribute to have the feature on FreeShow cos it does much more

@josephomills

josephomills commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

I think cross-translation quote-matching or switching translations via voice would be my next in line.
But I'd want to know how you'd want it done, in a general sense.

Next will be handling paraphrasing.

- Dynamic model loading
- Renamed some files
- Error messages
- UI cleanup
@josephomills

Copy link
Copy Markdown
Contributor Author

I think the whisper path check has a bug.

@vassbo

vassbo commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

I think the whisper path check has a bug.

I know, it has not worked for a while.

@josephomills

Copy link
Copy Markdown
Contributor Author

For the cross-translation quote-matching, if any user has many translations, the app will freeze while the bibles are being indexed. That's why I had run it on a worker initially. Open to indexing in a web worker? or should I index in sequence one at a time in the background (opened translation first then favourites then the rest). Do you have any idea the average number of translations users have? people in my circles will have 15+

@vassbo

vassbo commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

I'm pretty certain that worker you had only worked in the dev build, and would not work in a built version.
I made it load more asynchronously.

I added searching in multiple scriptures.
Currently it will search for content matches in all local scripture files, but it's not optimized to detect the differences. So you can optimize the detection confidence.

Do you have any idea the average number of translations users have? people in my circles will have 15+

I guess most churches have just one or two translations that they commonly use.

@vassbo vassbo changed the title AI auto scripture: local transcription + AI reference detection (opt-in) Smart AI STT Auto Scripture & LLM APIs Sep 9, 2026
@vassbo
vassbo merged commit 8dfc62d into ChurchApps:dev Sep 9, 2026
@vassbo

vassbo commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Merged! Thanks for helping me with this. :)

It's at least working as a basic level, but now you can more easily experiment with the system and improve as much as you want on a seperate branch, then send a PR at any time.

Got the floating color style improved as well:
image

@josephomills

josephomills commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

I'm pretty certain that worker you had only worked in the dev build, and would not work in a built version.
I made it load more asynchronously.

Great!

I added searching in multiple scriptures.

Nice! Will check it out

I guess most churches have just one or two translations that they commonly use.

👍🏽

Merged! Thanks for helping me with this. :)

Exciting!

It's at least working as a basic level, but now you can more easily experiment with the system and improve as much as you want on a seperate branch, then send a PR at any time.

Yhup yhup. Thank you! Beta release coming soon?

@gladsonsam

Copy link
Copy Markdown
Contributor

Thanks for all the work on this. Can't wait to try it out!

vassbo added a commit that referenced this pull request Sep 9, 2026
* Check textFit when detecting items that need auto size (#3660)

* Planning Center item type categories

* Updated Hungarian language

* Updated languages

* Fixed action output update custom activations triggering too early #3667

* Wider title search spelling mistake range  #3586

* Update

* Fixed scripture split in two not updating properly #3573

* Fixed cutting scripture show text not updating properly #3574

* Fixed removing scripture show verse numbers

* Scripture show cut/paste fix

* Fix

* Canva download cache resets if updated

* Fixed active scripture book not visible in grid mode

* Updated Chinese language

* Stripped bundled dist file of ~10MB

* Highlight to be deleted slides from context menu

* Added audio settings to profiles #3671

* Fixed camera cropping

* Fix minor keyboard shortcut issues (#3678)

* Updated language

* Item cutout shape #3669

* Custom vertial alignment with Cutout shape

* Updated Polish language

* Project section notes dynamic value #3675

* Tweak

* Video duration tweak

* Fix PDF text export layout for multi-page songs (#3684)

* Improved text parsing

* PCO Item Details import

* Open show action sets the correct project index
- Fixed clear actions triggering "after" output update when run from "Start show" action

* Fixed FFmpeg not working on some macOS #3676

* Don't delete cloud folder before backup

* Rendering overhaul: GPU-native output capture for multi-output / 4K performance (#3681)

* Cleanup

* Fixed PDF randomly not outputting #3686

* Sync B1 plan item assignees into section notes (#3675) (#3685)

* Tweak

* Fixed fade out pausing videos with no audio

* Per-item Text edit #3617

* Tweak

* PPT tweaks

* Profiles settings tabs are now linked to global tabs

* Updated language

* Fixed style overwrite font size issue #3694

* Fixed Vimeo URL parse issue

* Fixing start_scripture API verse selection issue #3700

* Fixed audio sometimes not going through Main channel #3688

* Audio rounting should not be synced

* Custom fonts manager #3650

* Jesus words stay red across bracketed phrases (#3709)

* Scripture undertitles wrap on the output instead of being clipped (#3708)

* Bracketed text keeps its template style inside red-letter verses (#3712)

* Added OMT Support (#3693)

* Cleanup OMT

* Set group template the moment the group is added

* Fixed multi item move

* Better shift move

* Scripture templates can style undertitles with {scripture_undertitle} (#3718)

* Revert segment

* Fixed incorrect stage resolution

* Fixed YouTube video not muted in stage output #3717

* Tweaks

* Per output encoder option

* Settings UI Tweak

* Fixed new stage output not focused

* Fixed main window saved position issues #3719

* Fixed chord auto break #3670

* Project collapsed mode #3703

* Tweak

* Smart AI STT Auto Scripture & LLM APIs (#3579)

* Gradient button tweak

* Auto update STT when download finishes

* Cleanup

* Increased suggestion log limit

* Don't include unneded files in build

* Auto output position tweak

* Scripture helper location update

* Fixed overlays preview not paused

* Longer camera preview load delay

* Fix

* Fixed PowerPoint custom crop format #3720

* Tweaks

* Version update

---------

Co-authored-by: Joseph Mills <96480787+josephomills@users.noreply.github.com>
Co-authored-by: hansli112 <31886587+hansli112@users.noreply.github.com>
Co-authored-by: Pineapple <34526243+Pineapple-Pineapple@users.noreply.github.com>
Co-authored-by: David Myers <david.d.myers@gmail.com>
Co-authored-by: Jeremy Zongker <jeremy@zongker.net>
@belos117

belos117 commented Sep 9, 2026

Copy link
Copy Markdown

Installed the beta and it works fantastically on my home laptop. I'm running it live this weekend during Sunday school with a Ghanaian congregation here in Italy, off a mixer feed, and I'll report precision and recall on detected references, accented English is a case I haven't seen covered here.

One thing from setup: the only way to stop the listener mid-service is the settings tab. During a service the operator needs that in one click, a toggle and a listening indicator in the main interface would help. Is there already a status element in the drawer panel that could be promoted, or did that go in the rewrite?

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