# How FluidVoice Handles Parakeet and Nemotron Model Downloads and Caching

> FluidVoice efficiently downloads and caches Parakeet and Nemotron models on-demand, ensuring integrity with validation before loading them as Core ML providers.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: internals
- Published: 2026-06-29

---

**FluidVoice downloads Parakeet and Nemotron speech-recognition models on-demand to platform-specific cache directories, validating integrity through markup detection and atomic file writes before loading them as Core ML providers.**

FluidVoice, an open-source Swift framework by altic-dev, distributes its neural speech-recognition engines—Parakeet and Nemotron—as Core ML models hosted on Hugging Face. Rather than bundling these large binaries within the app bundle, the framework implements a self-healing download pipeline that fetches, validates, and caches model artifacts only when required.

## Cache Directory Structure

FluidVoice separates caching logic between its two primary speech-recognition providers, each using distinct filesystem locations appropriate for their data lifecycles.

### Parakeet Storage Location

Parakeet stores artifacts in an app-support subdirectory designed for model persistence. The `ParakeetRealtimeProvider` constructs this path via `cacheRootDirectory()`, resolving to `"FluidAudio/Models/parakeet-eou-streaming"` under the application support directory → [`Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L45-L56`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L45-L56).

### Nemotron Storage Location

Nemotron utilizes the user-domain caches folder, scoped by a dynamic `folderHint` parameter. The `NemotronProvider` exposes `cacheDirectory` which resolves to a subdirectory within `FileManager.SearchPathDirectory.cachesDirectory` → [`Sources/Fluid/Services/NemotronProvider.swift#L72-L75`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/NemotronProvider.swift#L72-L75).

## Validation and Integrity Checks

Before initiating any network requests, both providers verify whether cached artifacts exist and are uncorrupted through strict validation gates.

### Artifact Completeness Verification

The `HuggingFaceModelDownloader` class provides `artifactsAreComplete(root:items:)`, which checks that every required file in the model manifest exists and contains data. Parakeet wraps this call in `missingRequiredModelFiles()` → [`Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L21-L32`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L21-L32), while Nemotron exposes `artifactsAreComplete(at:)` → [`Sources/Fluid/Services/NemotronProvider.swift#L82-L92`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/NemotronProvider.swift#L82-L92).

### Corruption Detection via Markup Analysis

To detect proxy-block pages or corrupted downloads, `HuggingFaceModelDownloader.cachedPayloadContainsMarkup(root:relativePaths:)` scans cached files for HTML/XML signatures → [`Sources/Fluid/Networking/ModelDownloader.swift#L78-L90`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Networking/ModelDownloader.swift#L78-L90). If markup is detected, the provider deletes the corrupted files and triggers a fresh download.

## Download Pipeline and Atomic Writes

When validation fails or caches are missing, the providers invoke `ensureModelsPresent(at:onProgress:)`, which orchestrates the download through four strict stages:

1. **Repository Listing**: Queries the Hugging Face API to enumerate required files from the model repository.
2. **Streaming Download**: Downloads each file to a temporary location with per-file progress callbacks.
3. **Strict Validation**: `validateDownloadedFile` checks HTTP status codes, content-length headers, content-type consistency, and re-runs the markup heuristic → [`Sources/Fluid/Networking/ModelDownloader.swift#L113-L135`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Networking/ModelDownloader.swift#L113-L135).
4. **Atomic Write**: Validated files move from temporary storage to the cache directory via `FileManager.moveItem(at:to:)`. If sizes mismatch expectations, the operation fails and retries.

## Provider Initialization

Once artifacts pass validation, providers instantiate their respective Core ML managers. Parakeet initializes `StreamingEouAsrManager`, while Nemotron creates `NemotronStreamingAsrManager`. Only after successful instantiation does the provider set `isReady = true`, indicating the model is loaded into memory and available for transcription.

## Cache Invalidation

Both providers expose `clearCache()` to force eviction of downloaded models. This method deletes the entire cache directory tree, resets internal state flags, and requires a subsequent `prepare()` call to re-download artifacts.

For Parakeet: → [`Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L56-L64`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L56-L64)

For Nemotron: → [`Sources/Fluid/Services/NemotronProvider.swift#L125-L138`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/NemotronProvider.swift#L125-L138)

## Implementation Examples

### Triggering a Parakeet Download

```swift
let provider = ParakeetRealtimeProvider(chunkSize: .ms160)
Task {
    do {
        // Downloads missing Parakeet Flash artifacts if needed
        try await provider.prepare { progress in
            print("Download progress: \(progress * 100.0)%")
        }
        print("Parakeet ready:", provider.isReady)
    } catch {
        print("Failed to prepare Parakeet:", error)
    }
}

```

### Triggering a Nemotron Download

```swift
let nemotron = NemotronProvider(mode: .streaming)
Task {
    do {
        // Ensures Nemotron files exist in cache
        try await nemotron.prepare { progress, file in
            print("[\(file)] \(progress * 100.0)%")
        }
        print("Nemotron ready:", nemotron.isReady)
    } catch {
        print("Nemotron error:", error)
    }
}

```

### Clearing Cache Manually

```swift
Task {
    try await provider.clearCache()    // Parakeet
    try await nemotron.clearCache()    // Nemotron
}

```

### Checking Cache Existence Without Downloading

```swift
if provider.modelsExistOnDisk() {
    print("Parakeet models cached")
}
if nemotron.modelsExistOnDisk() {
    print("Nemotron models cached")
}

```

## Summary

- **FluidVoice** treats Parakeet and Nemotron as on-demand Core ML models fetched from Hugging Face.
- **Parakeet** caches to `"FluidAudio/Models/parakeet-eou-streaming"` under app support; **Nemotron** uses the user caches directory.
- **Integrity checks** include `artifactsAreComplete()` validation and `cachedPayloadContainsMarkup()` to detect corruption.
- **Atomic writes** ensure files are only committed to cache after passing HTTP, size, and content-type validation.
- **Clear cache** functionality forces re-download by deleting cache directories and resetting provider state.

## Frequently Asked Questions

### How does FluidVoice detect corrupted model downloads?

FluidVoice uses `HuggingFaceModelDownloader.cachedPayloadContainsMarkup()` to scan cached files for HTML or XML content, which typically indicates a proxy block or failed redirect rather than binary model data. If detected, the corrupted files are deleted immediately before triggering a fresh download → [`Sources/Fluid/Networking/ModelDownloader.swift#L78-L90`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Networking/ModelDownloader.swift#L78-L90).

### What happens if a download is interrupted mid-stream?

Files download to temporary URLs and only move to the final cache location via `FileManager.moveItem()` after `validateDownloadedFile()` confirms the content-length and content-type match expectations. Interrupted downloads leave incomplete temporary files that are discarded on the next `prepare()` invocation.

### Can I change the default cache location for Nemotron models?

The `NemotronProvider` cache directory derives from `FileManager.SearchPathDirectory.cachesDirectory` combined with a `folderHint` parameter. While the framework does not expose a public API to override the root path, you can manipulate the `folderHint` during provider initialization to create subdirectories within the standard caches folder.

### Where does Parakeet store its cached model files?

Parakeet stores models under the application support directory at `"FluidAudio/Models/parakeet-eou-streaming"`, as determined by `ParakeetRealtimeProvider.cacheRootDirectory()` in [`Sources/Fluid/Services/ParakeetRealtimeProvider.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ParakeetRealtimeProvider.swift) → [`L45-L56`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ParakeetRealtimeProvider.swift#L45-L56).