PrivateAIProviderFeature Integration Flow in FluidVoice: Architecture & Implementation Guide

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. 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. 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.

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.

Stage 5: Integration Facade — PrivateAIIntegrationService.shared operates as an actor that serializes access to the integration layer at PrivateAIIntegrationService.swift:45-55. 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. 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.

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.

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, 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:

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.

Listing Available Models

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

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.

Preparing and Loading a Model

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

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.

Enhancing Dictation

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

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.

Removing Installed Models

Clean up disk space by removing downloaded model artifacts:

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.

Key Source Files

File Purpose
Sources/Fluid/Services/PrivateAIProvider.swift Feature bootstrap, registry management, and model registry helpers
Sources/Fluid/Services/PrivateAIIntegrationService.swift Actor façade forwarding UI calls to integration implementations
Sources/Fluid/Services/PrivateAIIntegrationProviding.swift Protocol defining contracts for runtime integration
Sources/Fluid/Services/PrivateAIProviderFeatureProviding.swift Protocol supplying static metadata and model lists
Sources/Fluid/Services/ModelRepository.swift Registration of Private AI provider among all AI providers
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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →