# How to Switch Between Whisper.cpp and Parakeet Transcription Models in Meetily

> Easily switch between Whisper.cpp and Parakeet transcription models in Meetily at runtime. The set_transcription_provider command re-initializes the engine without restarts, persisting your choice.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-12

---

**You can switch between Whisper.cpp and Parakeet transcription models in Meetily at runtime using the `set_transcription_provider` Tauri command, which persists the selection to the settings database and re-initializes the appropriate engine without requiring an application restart.**

Meetily, an open-source meeting transcription application from [Zackriya-Solutions/meetily](https://github.com/Zackriya-Solutions/meetily), ships with dual speech-to-text back-ends to accommodate different hardware configurations. The codebase provides a unified abstraction over **Whisper.cpp** for CPU-friendly inference and **Parakeet** (NVIDIA NeMo) for GPU-accelerated real-time transcription, allowing users to toggle between them dynamically based on their system's capabilities.

## Understanding the Two Transcription Back-Ends

Meetily implements two distinct transcription engines that expose an identical API surface:

- **Whisper.cpp** ([`frontend/src-tauri/src/whisper_engine/whisper_engine.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/whisper_engine/whisper_engine.rs)): The original Whisper implementation optimized for CPU inference. This engine provides higher accuracy but requires more processing time, making it suitable for post-meeting transcription or systems without NVIDIA GPUs.

- **Parakeet** ([`frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs)): A real-time transcription engine built on NVIDIA NeMo. This option requires modern NVIDIA hardware but delivers significantly faster processing speeds, ideal for live captioning during meetings.

Both engines implement the same three core methods: `load_model`, `transcribe_audio`, and `unload_model`, enabling seamless swapping through a common interface.

## How Provider Selection Works in Meetily's Settings

The transcription provider is not hard-coded but stored in the application's settings database, allowing for persistent configuration across sessions.

### Where the Provider Configuration is Stored

During initial onboarding, the selected provider is persisted in the settings table. As seen in [`frontend/src-tauri/src/onboarding.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/onboarding.rs), the application saves the configuration at line 200:

```rust
info!("Saved transcription model config: provider=parakeet, model={}", DEFAULT_PARAKEET_MODEL);

```

This settings value is read by the audio pipeline when transcription begins, determining which engine receives the audio stream.

### The Runtime Engine Selection Logic

The audio pipeline ([`frontend/src-tauri/src/audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/pipeline.rs)) does not directly instantiate engines. Instead, it queries the current provider from the settings repository and routes audio chunks to the corresponding global engine instance:

```rust
let provider = settings.get_transcription_provider().await?;
match provider.as_str() {
    "parakeet" => {
        let engine = PARAKEET_ENGINE.read().await;
        engine.transcribe_audio(samples, language).await
    }
    "whisper" => {
        let engine = WHISPER_ENGINE.read().await;
        engine.transcribe_audio(samples, language).await
    }
    _ => Err(anyhow!("Unknown transcription provider")),
}

```

The global engine handles are stored as `Arc<RwLock<Option<...>>>` in [`frontend/src-tauri/src/parakeet_engine/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/parakeet_engine/mod.rs) and [`frontend/src-tauri/src/whisper_engine/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/whisper_engine/mod.rs), ensuring thread-safe access from any async task.

## Switching Models Programmatically

Meetily exposes two primary Tauri commands that the frontend invokes to manage transcription providers:

### Using set_transcription_provider Command

The `set_transcription_provider` command, implemented in both [`frontend/src-tauri/src/parakeet_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/parakeet_engine/commands.rs) and [`frontend/src-tauri/src/whisper_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/whisper_engine/commands.rs), handles the runtime switch:

```rust
#[tauri::command]
pub async fn set_transcription_provider(app: AppHandle, provider: String) -> Result<(), String> {
    // Persist the provider value
    let mut settings = SettingsRepository::new(app.clone()).await?;
    settings.set_transcription_provider(&provider).await?;
    
    // Re-initialise the appropriate engine
    match provider.as_str() {
        "parakeet" => ParakeetEngine::initialize(app).await?,
        "whisper"  => WhisperEngine::initialize(app).await?,
        _ => return Err(format!("Unsupported provider: {}", provider)),
    }
    Ok(())
}

```

To switch to Parakeet from the frontend JavaScript/TypeScript:

```typescript
await invoke('set_transcription_provider', { provider: 'parakeet' });

```

To switch back to Whisper.cpp:

```typescript
await invoke('set_transcription_provider', { provider: 'whisper' });

```

### Loading Specific Models with load_transcription_model

After setting the provider, you must load a specific model variant using the `load_transcription_model` command. Each engine implements this command in its respective commands file.

For Whisper.cpp ([`frontend/src-tauri/src/whisper_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/whisper_engine/commands.rs)):

```rust
#[tauri::command]
pub async fn load_transcription_model(app: AppHandle, model_name: String) -> Result<(), String> {
    let mut guard = WHISPER_ENGINE.write().await;
    let engine = WhisperEngine::new().map_err(|e| e.to_string())?;
    engine.load_model(&model_name).await.map_err(|e| e.to_string())?;
    *guard = Some(engine);
    Ok(())
}

```

For Parakeet ([`frontend/src-tauri/src/parakeet_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/parakeet_engine/commands.rs)):

```rust
#[tauri::command]
pub async fn load_transcription_model(app: AppHandle, model_name: String) -> Result<(), String> {
    let mut guard = PARAKEET_ENGINE.write().await;
    let engine = ParakeetEngine::new().map_err(|e| e.to_string())?;
    engine.load_model(&model_name).await.map_err(|e| e.to_string())?;
    *guard = Some(engine);
    Ok(())
}

```

Example usage from the frontend:

```typescript
// Load Parakeet large_v2 model
await invoke('set_transcription_provider', { provider: 'parakeet' });
await invoke('load_transcription_model', { model_name: 'large_v2' });

// Or load Whisper base model
await invoke('set_transcription_provider', { provider: 'whisper' });
await invoke('load_transcription_model', { model_name: 'base' });

```

## Technical Implementation in the Audio Pipeline

When recording begins, the audio pipeline reads the provider from settings and routes audio chunks accordingly. This architecture ensures that:

1. **No restart is required**: The engine switch happens immediately upon invoking `set_transcription_provider`
2. **Thread safety**: Global engine handles use `Arc<RwLock<Option<Engine>>>` to allow safe concurrent access
3. **Clean state transitions**: Each engine's `initialize` method properly sets up the global instance before transcription begins

## UI Feedback and Model Availability

The system tray UI ([`frontend/src-tauri/src/tray.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/tray.rs)) monitors model availability for the selected provider. When switching providers, the tray refreshes its status to reflect whether the required model files are downloaded:

```rust
match crate::parakeet_engine::commands::parakeet_has_available_models().await {
    true => menu_item.enable(),
    false => menu_item.disable(),
}

```

If the chosen provider lacks a downloaded model, the recording option is temporarily disabled and the menu displays "⏳ Downloading transcription model…" until the files are available.

## Summary

- Meetily supports **Whisper.cpp** (CPU/GPU, higher accuracy) and **Parakeet** (GPU-only, real-time speed) through a unified engine interface.
- Use **`set_transcription_provider`** to switch engines at runtime, which updates the settings database and re-initializes the appropriate global engine instance.
- Call **`load_transcription_model`** after switching to download and initialize specific model variants (e.g., "base" for Whisper or "large_v2" for Parakeet).
- The audio pipeline ([`audio/pipeline.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/audio/pipeline.rs)) routes audio to the correct engine based on the stored provider setting, using thread-safe `Arc<RwLock>` handles defined in each engine's [`mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/mod.rs).
- The system tray ([`tray.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/tray.rs)) provides visual feedback by enabling or disabling recording based on model download status for the active provider.

## Frequently Asked Questions

### Can I switch between Whisper and Parakeet without restarting Meetily?

Yes. The `set_transcription_provider` command handles the switch immediately by writing the new provider to the settings database and calling the appropriate `initialize` method. The audio pipeline reads this setting on the next transcription request, routing audio to the newly selected engine without requiring an application restart.

### What happens if I select Parakeet but don't have an NVIDIA GPU?

The Parakeet engine requires NVIDIA hardware and will fail to initialize if CUDA-compatible devices are unavailable. You should check GPU availability before switching or handle the error returned by `set_transcription_provider`. The codebase stores engine instances as `Option<Engine>` in global locks, so initialization failures won't crash the application, but transcription will be unavailable until you switch to Whisper.cpp.

### Where does Meetily store which transcription model is currently active?

The active provider is stored in the settings repository, persisted to a local database. During onboarding ([`frontend/src-tauri/src/onboarding.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/onboarding.rs)), the selection is saved via `settings.set_transcription_provider()`. The audio pipeline and system tray read this value at runtime using `settings.get_transcription_provider()` to determine which engine to use and which availability checks to perform.

### How do I know when the transcription model has finished downloading?

The system tray menu ([`frontend/src-tauri/src/tray.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/tray.rs)) monitors model availability through provider-specific checks like `parakeet_has_available_models()`. When a model is downloading, the tray disables the recording menu item and shows "⏳ Downloading transcription model…". Once the model files are present, the menu item automatically enables, allowing recording to proceed.