Skip to content

feat: transcribe on this computer, offline and free - #1

Merged
loan-talvat merged 4 commits into
mainfrom
feat/local-transcription
Nov 4, 2026
Merged

loan-talvat merged 4 commits into
mainfrom
feat/local-transcription

Conversation

@loan-talvat

Copy link
Copy Markdown
Contributor

What and why

Parler 0.1.8 adds a third way to dictate, On this computer, next to Parler Cloud and "own API key or server". A speech model runs on the computer itself: the voice never leaves it, it works offline and it's free. It stays as simple as the rest of Parler — one recommended model, a plain download with progress, and an "Advanced options" choice of a second model. No model manager.

  • Models (pinned Hugging Face files, SHA-256 checked, resumable downloads):
    • Parakeet Ultra — recommended for the 25 European languages it knows (740 MB).
    • Whisper Large v3 Turbo — every other language, uses the vocabulary (620 MB).
    • Parler picks one from the language settings; "Advanced options" switches.
  • Engine: transcribe.cpp 0.4.1 (MIT), the engine Handy uses.
    • Metal on Apple Silicon; the CPU on Intel Macs.
    • Vulkan on Windows and Linux GPUs (integrated ones too), otherwise the CPU module matching the processor (dynamic backends, no hard Vulkan dependency).
    • Falls back to the CPU when the GPU fails; a crash guard keeps a GPU that crashed Parler off until the next version.
  • Reliability: a local dictation is saved to the pending store before the engine runs, so an engine crash never loses it.
  • Honesty: onboarding and Settings say it uses the computer's power, can be slower when it's busy, and is a little less accurate than Parler Cloud. Settings shows quietly where the model runs (graphics chip / graphics card / processor).
  • Credits: in THIRD_PARTY_NOTICES.md and Settings → About (Parakeet Ultra is CC BY 4.0, NVIDIA and Moondream).

How it was tested

  • macOS (Apple M4), real:
    • Both models downloaded through the app's code, including a resumed download.

    • Transcribed French and English speech:

      Parakeet Ultra Whisper Large v3 Turbo
      11 s of speech, Metal 0.18 s 1.3 s
      59 s of speech, Metal 1.1 s 4 s
      11 s of speech, CPU 0.46 s 7.4 s
    • The Intel slice ran under Rosetta.

    • The universal release .app builds (31 MB vs 20 MB in 0.1.7).

  • Windows and Linux: only built in CI so far. They need a real run of the packaged app before release (download, transcription on Vulkan and on CPU, app starts without a Vulkan driver).
  • cargo test: 100 passed, 4 ignored. The ignored tests download real models (docs/contributing.md says how to run them).
  • Interface checked with the preview, light and dark, en and fr, at 780 px.

Screenshots

Onboarding download step, Settings model card, Home download banner: see the preview (pnpm dev → /preview.html?lang=fr&local=downloading, params in docs/contributing.md).

Checklist

  • The title follows Conventional Commits.
  • pnpm typecheck && pnpm build pass; pnpm test passes once the translations land (next commit).
  • cargo fmt --all -- --check && cargo clippy --all-targets -- -D warnings && cargo test pass on macOS (CI covers Windows and Linux).
  • New interface strings in all 12 locales — translations in progress.
  • No new setting where a better default would do (the model is picked from the language).
  • Docs updated (README.md, README.fr.md, docs/architecture.md, docs/contributing.md, THIRD_PARTY_NOTICES.md).

A third way to dictate, next to Parler Cloud and your own API key: a
speech model that runs on the computer itself, so the voice never leaves
it and Parler works offline. It stays as simple as the rest of Parler:

- One recommended model, picked from the language: Parakeet Ultra (25
  European languages, 740 MB), or Whisper Large v3 Turbo for every other
  language (620 MB, uses the vocabulary). "Advanced options" lets people
  pick the other one; nothing more to configure.
- Onboarding and Settings say plainly that it uses the computer's power,
  can be slower when it's busy, and is a little less accurate than Parler
  Cloud. The download shows its progress, can be cancelled and resumes
  where it stopped, from pinned Hugging Face files checked by SHA-256.
- It runs where each computer is fastest, through transcribe.cpp (MIT):
  Metal on Apple Silicon, the CPU on Intel Macs, Vulkan on Windows and
  Linux graphics cards and integrated chips, and otherwise the CPU build
  matching the processor. A GPU that fails falls back to the CPU, and one
  that crashed Parler isn't used again until the next version. Settings
  shows quietly where the model runs.
- A local dictation is saved before the engine runs, so even a crash of
  the engine never loses it. The model loads in the background and is
  let go after 15 idle minutes.
- On an M4, 11 s of speech takes 0.18 s with Parakeet on Metal.

Credits for transcribe.cpp, Parakeet Ultra (NVIDIA, Moondream, CC BY 4.0)
and Whisper are in THIRD_PARTY_NOTICES.md and Settings → About.
@loan-talvat
loan-talvat marked this pull request as ready for review November 4, 2026 10:39
@loan-talvat
loan-talvat merged commit 3c12bee into main Nov 4, 2026
8 checks passed
@loan-talvat
loan-talvat deleted the feat/local-transcription branch November 4, 2026 10:39
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T10:42:03.990024Z 09fb9c7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 09fb9c7c9e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if settings.provider != local::ID {
return None;
}
Some(pending.save(speech.wav.clone(), speech.duration_ms, local::interrupted()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep temporary crash backups outside the pending eviction quota

When the pending store already contains 20 unsent dictations, recording a local dictation now permanently deletes the oldest unsent recording even if the new dictation succeeds or is cancelled. Pending::save immediately invokes prune, enforcing both the 20-item and 50 MB limits, whereas this temporary entry is subsequently removed by forget_kept; removing it cannot restore the recording evicted to make room. Keep in-flight crash backups separately, or exclude them from eviction accounting until transcription actually fails.

Useful? React with 👍 / 👎.

Comment on lines 516 to +518
if session != self.session || !matches!(self.phase, Phase::Transcribing) {
return; // cancelled
// Cancelled: what was kept meanwhile goes with it.
self.forget_kept(kept);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Delete the local crash backup when cancellation is requested

If a user cancels a slow local transcription and then quits Parler before inference finishes, the cancelled recording remains on disk and reappears as an unsent dictation on the next launch. Cleanup here only runs when Event::Finished arrives, but cancel() merely invalidates the session and the blocking native transcription continues running. Track the backup ID in the controller and remove it during cancellation rather than relying exclusively on eventual completion.

Useful? React with 👍 / 👎.

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