# PrivateAIProviderFeature Integration Flow in FluidVoice: Architecture & Implementation Guide

> Explore the PrivateAIProviderFeature integration flow in FluidVoice. Learn about its lazy bootstrap, runtime registration, and how PrivateAIIntegrationService manages model lifecycles for enhanced dictation.

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

---

**The PrivateAIProviderFeature initializes through a lazy bootstrap mechanism where `PrivateAIProviderBootstrap.installIfAvailable()` conditionally registers a bridge implementation at runtime, exposing capabilities via `PrivateAIProviderFeature.shared` while `PrivateAIIntegrationService` acts as a serialized actor façade that handles model lifecycle operations and dictation enhancement.**

The PrivateAIProviderFeature is a plugin-style subsystem in **altic-dev/FluidVoice** that enables locally-hosted LLM integration for dictation enhancement. This modular architecture uses compile-time flags and runtime bridge registration to keep the codebase clean while supporting optional private AI functionality. Understanding the integration flow reveals how FluidVoice maintains strict separation between UI components and the underlying private runtime infrastructure.

## Integration Flow Overview

The integration follows an eight-stage pipeline that moves from compile-time conditional compilation through runtime model management. Each stage decouples the UI from implementation details while ensuring thread-safe access to potentially expensive local LLM operations.

**Stage 1: Feature Bootstrap** — On first access to `PrivateAIProviderFeature.shared`, the system invokes `PrivateAIProviderBootstrap.installIfAvailable()` at [`PrivateAIProvider.swift:44-53`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L44-L53). This checks the `PRIVATE_AI_PROVIDER` compile-time flag and conditionally installs the `PrivateAIProviderBridge` if present.

**Stage 2: Feature Registry** — `PrivateAIProviderRegistry` maintains references to both feature metadata and integration implementation at [`PrivateAIProvider.swift:39-43`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L39-L43). By default, these point to "unavailable" shims that return `isAvailable = false`.

**Stage 3: Public Accessor** — The `PrivateAIProviderFeature.shared` property lazily triggers bootstrap and returns the concrete `PrivateAIProviderFeatureProviding` implementation or the unavailable shim at [`PrivateAIProvider.swift:56-61`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L56-L61).

**Stage 4: Model Registry** — `PrivateAIModelRegistry` forwards static calls (model lookup, default model ID, URL construction) to the active feature implementation at [`PrivateAIProvider.swift:78-104`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L78-L104).

**Stage 5: Integration Facade** — `PrivateAIIntegrationService.shared` operates as an **actor** that serializes access to the integration layer at [`PrivateAIIntegrationService.swift:45-55`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L45-L55). It resolves the concrete `PrivateAIIntegrationProviding` implementation stored in `PrivateAIProviderRegistry.integration`.

**Stage 6: Runtime Configuration** — Before enhancement, the UI constructs a `RuntimeConfiguration` struct containing provider ID, API key, optional local model path, and KV-cache settings at [`PrivateAIIntegrationService.swift:31-35`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L31-L35). This configuration determines whether to use the private runtime or fall back to cloud providers.

**Stage 7: Model Lifecycle** — The integration handles `prepareModel` (download and SHA-256 verification), `loadModel` (runtime launch), `prewarmDictation` (latency reduction), and `unloadCachedRuntime` (resource cleanup) at [`PrivateAIIntegrationService.swift:16-25`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L16-L25).

**Stage 8: UI Wiring** — Settings screens and dictation interfaces read `PrivateAIProviderFeature.shared` for identifiers and display names, invoking operations through the static façade as shown in [`RewriteModeView.swift:337-340`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Views/RewriteModeView.swift#L337-L340).

## Conditional Bootstrap Mechanism

The bootstrap process relies on compile-time conditional compilation to keep the private AI code path optional. When any component first accesses `PrivateAIProviderFeature.shared`, the lazy initialization triggers `PrivateAIProviderBootstrap.installIfAvailable()`.

If the **`PRIVATE_AI_PROVIDER`** flag is enabled during compilation, the method calls `PrivateAIProviderBridge.install()`, registering the concrete bridge implementation that connects to the local LLM runtime. If the flag is disabled, the registry retains the shim implementations, and `isAvailable` returns false without attempting to load external binaries.

This design ensures that builds without the private AI entitlement remain lightweight and free from unused dependencies, while allowing a single codebase to support both configurations.

## Registry Pattern and Feature Access

FluidVoice uses a dual-registry pattern to separate feature metadata from integration logic. `PrivateAIProviderRegistry` holds two critical references:

- **feature**: An implementation of `PrivateAIProviderFeatureProviding` supplying static metadata like `providerID`, `providerName`, and `promptSelectionID`
- **integration**: An implementation of `PrivateAIIntegrationProviding` handling runtime operations

The `PrivateAIModelRegistry` provides static helper methods (`modelIDs()`, `model(id:)`, `defaultModelID`, `localModelURL(for:directoryURL:)`) that forward to the active feature implementation. This abstraction allows UI code to list available models and construct file paths without direct coupling to the underlying LLM runtime.

## The Integration Service Actor

`PrivateAIIntegrationService` is implemented as a Swift **actor** to ensure thread-safe access to the private runtime. As a single-point façade, it serializes all operations including model preparation, loading, and dictation enhancement.

The service resolves the concrete integration implementation from `PrivateAIProviderRegistry.integration` for each operation. When the private AI provider is unavailable, the shim throws `PrivateAIUnavailableError` and reports `.unavailable` status, allowing the UI to gracefully degrade to cloud-based alternatives.

All dictation enhancement flows through `enhanceDictation(_:runtime:context:)` at [`PrivateAIIntegrationService.swift:55-61`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L55-L61), which forwards to the integration provider after validating the runtime configuration.

## Model Lifecycle Operations

The private AI integration manages four distinct lifecycle phases to balance performance and resource usage:

**Preparation** — `prepareModel(_:progressHandler:)` downloads the model artifact if a `downloadURL` is present, then validates the SHA-256 hash. Progress updates flow through the `PrivateAIModelDownloadProgressHandler` closure.

**Loading** — `loadModel(_:)` initiates the local runtime, potentially spawning a bridge process or initializing an embedded inference engine. This operation is heavyweight and typically occurs when the user explicitly selects a private model or when the app prewarms the runtime.

**Prewarming** — `prewarmDictation()` primes the model KV-cache and warms up inference threads, reducing latency for the first dictation request. This is typically called once at app startup if private AI is the preferred provider.

**Shutdown** — `unloadCachedRuntime(reason:)` and `shutdownForTermination()` cleanly stop the bridge process, release GPU/Neural Engine resources, and purge temporary caches when the app terminates or the user switches providers.

## Implementation Examples

### Checking Feature Availability

Use the `PrivateFeatures` gate to determine if the private AI provider was compiled into the current build:

```swift
if PrivateFeatures.privateAIProvider {
    print("✅ Private AI Provider is enabled")
} else {
    print("❌ Feature not compiled in")
}

```

*Reference:* `PrivateFeatures.privateAIProvider` evaluates `PrivateAIProviderFeature.shared.isAvailable` at [`PrivateAIProvider.swift:72-75`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L72-L75).

### Listing Available Models

Query the model registry to populate UI pickers without accessing the underlying integration:

```swift
let models = PrivateAIModelRegistry.modelIDs()
models.forEach { id in
    if let model = PrivateAIModelRegistry.model(id: id) {
        print("\(model.displayName) – \(model.detail)")
    }
}

```

*Reference:* `PrivateAIModelRegistry.modelIDs()` forwards to the feature implementation at [`PrivateAIProvider.swift:98-102`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L98-L102).

### Preparing and Loading a Model

Download, verify, and load a model into the runtime using the integration service actor:

```swift
let model = PrivateAIModelRegistry.defaultModel
Task {
    do {
        // Download / verify the model if needed
        let localURL = try await PrivateAIIntegrationService.prepareModel(model) { progress in
            print("Download: \(progress.fractionCompleted ?? 0)")
        }
        // Load the model into the local runtime
        let status = try await PrivateAIIntegrationService.shared.loadModel(model)
        print("Load status: \(status.state)")
    } catch {
        print("❗️Failed: \(error)")
    }
}

```

*Reference:* `prepareModel` and `loadModel` implementations at [`PrivateAIIntegrationService.swift:16-25`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L16-L25).

### Enhancing Dictation

Build a runtime configuration and context to process text through the local LLM:

```swift
let runtime = PrivateAIIntegrationService.RuntimeConfiguration(
    selectedProviderID: PrivateAIProviderFeature.shared.providerID,
    providerKey: "my-api-key",
    baseURL: "http://localhost:8080",
    model: PrivateAIModelRegistry.defaultModelID,
    apiKey: "my-api-key",
    localModelPath: nil,
    usesStablePromptPrefixKVCache: true
)

let context = PrivateAIIntegrationService.AppContext(
    appName: "FluidVoice",
    bundleID: Bundle.main.bundleIdentifier ?? "",
    windowTitle: "Untitled",
    appVersion: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
)

Task {
    do {
        let result = try await PrivateAIIntegrationService.shared.enhanceDictation(
            "turn on the lights",
            runtime: runtime,
            context: context
        )
        print("Enhanced: \(result.outputText)")
    } catch {
        print("Enhancement failed: \(error)")
    }
}

```

*Reference:* `enhanceDictation` path through integration provider at [`PrivateAIIntegrationService.swift:55-61`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L55-L61).

### Removing Installed Models

Clean up disk space by removing downloaded model artifacts:

```swift
let model = PrivateAIModelRegistry.model(id: "my-custom-model")!
if PrivateAIIntegrationService.canRemoveInstalledModel(model) {
    try PrivateAIIntegrationService.removeInstalledModel(model)
    print("Model removed")
}

```

*Reference:* `canRemoveInstalledModel` and `removeInstalledModel` logic at [`PrivateAIIntegrationService.swift:79-94`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift#L79-L94).

## Key Source Files

| File | Purpose |
|------|---------|
| [`Sources/Fluid/Services/PrivateAIProvider.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift) | Feature bootstrap, registry management, and model registry helpers |
| [`Sources/Fluid/Services/PrivateAIIntegrationService.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationService.swift) | Actor façade forwarding UI calls to integration implementations |
| [`Sources/Fluid/Services/PrivateAIIntegrationProviding.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIIntegrationProviding.swift) | Protocol defining contracts for runtime integration |
| [`Sources/Fluid/Services/PrivateAIProviderFeatureProviding.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProviderFeatureProviding.swift) | Protocol supplying static metadata and model lists |
| [`Sources/Fluid/Services/ModelRepository.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/ModelRepository.swift) | Registration of Private AI provider among all AI providers |
| [`Sources/Fluid/Views/RewriteModeView.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Views/RewriteModeView.swift) | Example UI consumption of feature IDs and model lists |

## Summary

- **Lazy Bootstrap**: `PrivateAIProviderBootstrap.installIfAvailable()` triggers only on first access to `PrivateAIProviderFeature.shared`, respecting the `PRIVATE_AI_PROVIDER` compile-time flag
- **Dual Registry**: `PrivateAIProviderRegistry` separates feature metadata from integration implementation, allowing shim fallbacks when the feature is unavailable
- **Actor Safety**: `PrivateAIIntegrationService` serializes all runtime operations through Swift actor isolation, preventing race conditions during model loading and inference
- **Lifecycle Management**: Explicit phases for preparation (download/verify), loading (runtime start), prewarming (cache optimization), and shutdown (resource cleanup)
- **UI Decoupling**: Views interact exclusively through static registry helpers and the integration service façade, remaining agnostic to whether the underlying runtime is local or cloud-based

## Frequently Asked Questions

### What triggers the PrivateAIProviderFeature bootstrap in FluidVoice?

The bootstrap triggers lazily when any code accesses `PrivateAIProviderFeature.shared` for the first time. This invokes `PrivateAIProviderBootstrap.installIfAvailable()` at [`PrivateAIProvider.swift:44-53`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Services/PrivateAIProvider.swift#L44-L53), which checks if the `PRIVATE_AI_PROVIDER` compile-time flag is set before attempting to register the bridge implementation.

### How does FluidVoice handle model downloads and verification?

The `prepareModel(_:progressHandler:)` method in `PrivateAIIntegrationService` manages the entire process. It downloads the model artifact from the provided URL if necessary, then validates the SHA-256 hash against the manifest before marking the model ready for loading. Progress updates stream through the `PrivateAIModelDownloadProgressHandler` closure.

### What is the role of PrivateAIIntegrationService in the architecture?

`PrivateAIIntegrationService` acts as a thread-safe actor façade that serializes access to the private AI runtime. It serves as the single integration point for UI components, forwarding model lifecycle operations and dictation enhancement requests to the concrete `PrivateAIIntegrationProviding` implementation while handling unavailable states gracefully.

### Can the PrivateAIProviderFeature work without the PRIVATE_AI_PROVIDER flag?

No. Without the `PRIVATE_AI_PROVIDER` compile-time flag, the bootstrap skips bridge installation and the registries retain their default shim implementations. These shims return `isAvailable = false` and throw `PrivateAIUnavailableError` for all operations, effectively disabling the feature while maintaining API compatibility.