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:
- Create a new provider class in
Sources/Fluid/Networking/AIProvider.swift(or a new file) implementing theAIProviderprotocol - Override
requestHeaders()to inject custom authentication (e.g.,X-API-Keyinstead ofAuthorization: Bearer) - Override
endpointPath()to return custom paths (e.g.,/v2/generateinstead of/chat/completions) - Register the provider in
Sources/Fluid/Services/ModelRepository.swiftby adding a new case in the provider identifier switch - Expose UI fields in
Sources/Fluid/UI/AISettingsView+AIConfiguration.swiftfor 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
- Open Settings → AI Enhancement
- Select "Groq" from the searchable provider picker
- Enter your Groq API key (stored in macOS Keychain)
- Adjust the base URL if needed (defaults to
https://api.groq.com/openai/v1) - Select your preferred model from the populated dropdown
Connecting to a Self-Hosted OpenAI-Compatible Service
- Open Settings → AI Enhancement
- Select "OpenAI" (the UI treats any OpenAI-compatible endpoint the same)
- Set Base URL to your server (e.g.,
https://my-llm.local/v1) - Paste your server’s API key
- Choose the model name your server advertises
Summary
- OpenAI-compatible services (Groq, Azure, self-hosted) require only UI configuration in
AISettingsView+AIConfiguration.swiftwith no code changes - Core networking is handled by
OpenAICompatibleProviderinAIProvider.swift, which appends/chat/completionsand injects Bearer tokens - Model lists are parsed by
ModelRepository.swift, supporting standard OpenAI response formats out of the box - Custom protocols require implementing the
AIProviderprotocol with customrequestHeaders()andendpointPath()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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →