How to Add Custom API Providers Like OpenAI and Groq for AI Enhancement in FluidVoice

FluidVoice supports OpenAI-compatible endpoints out of the box, allowing you to plug in Groq, Azure OpenAI, or self-hosted services by simply configuring the base URL and API key in the Settings UI.

FluidVoice is an open-source macOS dictation application that leverages large language models to post-process and enhance transcription accuracy. Adding custom API providers like OpenAI and Groq for AI enhancement in FluidVoice requires no code changes for OpenAI-compatible services, while custom protocols need only a minimal provider implementation. The architecture abstracts all HTTP networking behind a generic AIProvider protocol, making it trivial to swap between cloud and self-hosted AI backends.

Understanding the Provider-Agnostic Architecture

FluidVoice’s AI enhancement layer is built on a flexible, provider-agnostic design centered around the OpenAICompatibleProvider class. Any service implementing the OpenAI HTTP API specification—including Groq, Azure OpenAI, or private endpoints—works immediately without modification.

The system relies on three core components:

  • UI Configuration: Captures base URLs and API keys securely
  • Networking Layer: Handles request construction and authentication
  • Model Repository: Parses available models from provider endpoints

Because the networking code in Sources/Fluid/Networking/AIProvider.swift is deliberately generic, FluidVoice treats any OpenAI-compatible endpoint identically, whether it is OpenAI’s official API or a custom self-hosted instance.

Configuring OpenAI and Groq in the Settings UI

Provider Selection and Enum

The provider enum resides in Sources/Fluid/UI/AISettingsView.swift at line 75, defining the built-in choices including OpenAI and Groq:

// Sources/Fluid/UI/AISettingsView.swift
case openai = "OpenAI"

The interactive picker that renders this list is implemented in Sources/Fluid/UI/SearchableProviderPicker.swift at line 175. When users select a provider, the app stores the identifier (e.g., "openai" or "groq") for later use by the networking layer.

API Key and Base URL Configuration

Once a provider is selected, Sources/Fluid/UI/AISettingsView+AIConfiguration.swift (lines 1526-2688) displays the configuration fields:

  • OpenAI-compatible base URL: The endpoint root (e.g., https://api.groq.com/openai/v1)
  • API Key: Stored securely in the macOS Keychain via SettingsStore.swift
  • Model Selection: Populated dynamically from the provider’s model list

For Groq specifically, users simply select "Groq" from the picker and enter their API key. The default base URL points to Groq’s OpenAI-compatible endpoint, requiring no additional configuration.

The Networking Layer: OpenAICompatibleProvider

All OpenAI-style services are wrapped by the OpenAICompatibleProvider class found in Sources/Fluid/Networking/AIProvider.swift (lines 7-83). This class constructs requests by appending /chat/completions to the user-provided base URL and injects the API key as a Bearer token:

// Sources/Fluid/Networking/AIProvider.swift
final class OpenAICompatibleProvider: AIProvider {
    // Appends /chat/completions for OpenAI-compatible endpoints
    // Line 83: Constructs the full URL path
}

The provider handles streaming responses and function-calling payloads using the same logic for any OpenAI-compatible service. This means Groq, Azure, or custom endpoints receive identical request formatting without code changes.

Model Discovery and Provider Mapping

When FluidVoice queries a provider for available models, Sources/Fluid/Services/ModelRepository.swift handles the parsing. The file maps provider identifiers to display names at line 94:

// Sources/Fluid/Services/ModelRepository.swift
case "openai": return "OpenAI"

At line 306, the repository parses the OpenAI-style response format:

{ "data": [{ "id": "model-name" }, ...] }

If you add a new provider that returns models in this format, no changes are needed. For custom response formats, you extend the parser in this file to handle the new JSON structure.

Adding a Fully Custom API Provider

For services that deviate from the OpenAI specification—different endpoint paths, custom authentication headers, or unique payload formats—you must implement a custom provider class:

  1. Create a new provider class in Sources/Fluid/Networking/AIProvider.swift (or a new file) implementing the AIProvider protocol
  2. Override requestHeaders() to inject custom authentication (e.g., X-API-Key instead of Authorization: Bearer)
  3. Override endpointPath() to return custom paths (e.g., /v2/generate instead of /chat/completions)
  4. Register the provider in Sources/Fluid/Services/ModelRepository.swift by adding a new case in the provider identifier switch
  5. Expose UI fields in Sources/Fluid/UI/AISettingsView+AIConfiguration.swift for any additional parameters

Here is a minimal skeleton for a custom provider:

// Sources/Fluid/Networking/CustomProvider.swift
final class CustomProvider: AIProvider {
    private let baseURL: URL
    private let apiKey: String
    
    init(baseURL: URL, apiKey: String) {
        self.baseURL = baseURL
        self.apiKey = apiKey
    }
    
    override func requestHeaders() -> [String: String] {
        [
            "Authorization": "Bearer \(apiKey)",
            "X-Custom-Auth": "my-custom-value"
        ]
    }
    
    override func endpointPath(for request: AIRequest) -> String {
        // Custom endpoint path
        return "/v2/completions"
    }
}

After implementation, LLMClient automatically uses your new class for dictation enhancement without requiring changes to the transcription pipeline.

Quick Setup Examples

Connecting to Groq

  1. Open Settings → AI Enhancement
  2. Select "Groq" from the searchable provider picker
  3. Enter your Groq API key (stored in macOS Keychain)
  4. Adjust the base URL if needed (defaults to https://api.groq.com/openai/v1)
  5. Select your preferred model from the populated dropdown

Connecting to a Self-Hosted OpenAI-Compatible Service

  1. Open Settings → AI Enhancement
  2. Select "OpenAI" (the UI treats any OpenAI-compatible endpoint the same)
  3. Set Base URL to your server (e.g., https://my-llm.local/v1)
  4. Paste your server’s API key
  5. Choose the model name your server advertises

Summary

  • OpenAI-compatible services (Groq, Azure, self-hosted) require only UI configuration in AISettingsView+AIConfiguration.swift with no code changes
  • Core networking is handled by OpenAICompatibleProvider in AIProvider.swift, which appends /chat/completions and injects Bearer tokens
  • Model lists are parsed by ModelRepository.swift, supporting standard OpenAI response formats out of the box
  • Custom protocols require implementing the AIProvider protocol with custom requestHeaders() and endpointPath() methods
  • Security is managed through SettingsStore.swift, which persists API keys in the macOS Keychain

Frequently Asked Questions

What is the default base URL for Groq in FluidVoice?

The default base URL for Groq points to https://api.groq.com/openai/v1, which Groq provides as an OpenAI-compatible endpoint. You can override this in the Settings UI if you are using a proxy or custom routing.

Can I use Azure OpenAI with FluidVoice?

Yes. Select "OpenAI" from the provider picker and set the base URL to your Azure OpenAI endpoint (e.g., https://your-resource.openai.azure.com/openai/deployments/your-deployment-name). Enter your Azure API key in the API key field. The OpenAICompatibleProvider class handles the standard Azure OpenAI request format.

Where are API keys stored in FluidVoice?

API keys are stored securely in the macOS Keychain via SettingsStore.swift. They are never written to plaintext preferences and are retrieved only when constructing network requests in AIProvider.swift.

Do I need to rebuild FluidVoice to add a new provider?

Only if the provider uses a non-OpenAI-compatible API. For OpenAI-compatible endpoints (including Groq), you only need to enter the base URL and API key in the Settings UI. For custom protocols, you must add a new provider class to AIProvider.swift and register it in ModelRepository.swift, then rebuild the application.

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 →