How to Add a New AI Provider (Ollama, Claude, Groq) for Meeting Summaries in Meetily
To add a new AI provider like Ollama, Claude, or Groq to Meetily, extend the LLMProvider enum in llm_client.rs, implement provider-specific metadata methods, add a dispatch case in LLMClient::call, configure any custom endpoints in AppSettings, and update the frontend dropdown in page.tsx.
Meetily is an open-source meeting assistant that generates AI-powered summaries from transcripts using a Rust-based Tauri backend. Adding support for a new LLM provider requires modifying a consistent set of components across the summary module to handle authentication, endpoint routing, and request formatting.
Meetily's LLM Provider Architecture
Meetily abstracts LLM interactions through a provider pattern implemented in the Rust backend. Understanding these four components is essential before adding a new service.
The Provider Enum defines every supported LLM. Located in [frontend/src-tauri/src/summary/llm_client.rs](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/llm_client.rs#L1-L9) (lines 1‑9), the LLMProvider enum currently supports OpenAI, Claude, Groq, Ollama, and others.
Provider Metadata Methods determine endpoint URLs, API key requirements, and display names. These methods on the LLMProvider enum (around lines 37‑55) tell the system whether to prompt for an API key and which base URL to use by default.
The LLM Client handles HTTP dispatch. The LLMClient::call method (lines 200‑212) uses pattern matching to route requests to provider-specific implementations like call_ollama or call_claude.
The Summary Service orchestrates the generation flow. In [service.rs](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/service.rs#L300-L311) (lines 300‑311), it injects provider-specific settings—such as custom endpoints or token thresholds—into the client before executing the request.
Step-by-Step: Adding a New AI Provider
1. Extend the LLMProvider Enum
Add your provider variant to the enum definition in frontend/src-tauri/src/summary/llm_client.rs:
pub enum LLMProvider {
OpenAI,
Claude,
Groq,
Ollama,
BuiltInAI,
OpenRouter,
CustomOpenAI,
NewProvider, // ← Add your variant here
}
Update the string conversion methods to support serialization. Modify from_str (around line 12) and as_str (around line 25) to include your new provider:
pub fn from_str(s: &str) -> Result<Self, &'static str> {
match s {
"openai" => Ok(Self::OpenAI),
"claude" => Ok(Self::Claude),
"groq" => Ok(Self::Groq),
"ollama" => Ok(Self::Ollama),
"newprovider" => Ok(Self::NewProvider), // ← Add this
_ => Err("Unsupported provider"),
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::OpenAI => "openai",
Self::Claude => "claude",
Self::Groq => "groq",
Self::Ollama => "ollama",
Self::NewProvider => "newprovider", // ← Add this
// ... other variants
}
}
2. Implement Provider Metadata
Define the endpoint, API key requirement, and display name. Update the match arms in the LLMProvider implementation block:
pub fn requires_api_key(&self) -> bool {
match self {
Self::OpenAI | Self::Claude | Self::Groq | Self::OpenRouter | Self::CustomOpenAI => true,
Self::Ollama | Self::BuiltInAI => false,
Self::NewProvider => true, // Set to true if API key required
}
}
pub fn default_endpoint(&self) -> Option<&'static str> {
match self {
Self::OpenAI => Some("https://api.openai.com/v1/chat/completions"),
Self::Claude => Some("https://api.anthropic.com/v1/messages"),
Self::Groq => Some("https://api.groq.com/openai/v1/chat/completions"),
Self::Ollama => Some("http://127.0.0.1:11434/api/chat"),
Self::NewProvider => Some("https://api.newprovider.com/v1/chat"), // ← Add endpoint
_ => None,
}
}
pub fn display_name(&self) -> &'static str {
match self {
Self::OpenAI => "OpenAI",
Self::Claude => "Claude",
Self::Groq => "Groq",
Self::Ollama => "Ollama",
Self::NewProvider => "New Provider", // ← Add display name
// ... other variants
}
}
3. Add the Provider-Specific HTTP Handler
Create an async method in LLMClient to handle the API specifics. First, add a match arm in the call method at lines 200‑212:
pub async fn call(&self, request: LLMRequest) -> Result<LLMResponse, String> {
match self.provider {
LLMProvider::OpenAI => self.call_openai(request).await,
LLMProvider::Claude => self.call_claude(request).await,
LLMProvider::Groq => self.call_groq(request).await,
LLMProvider::Ollama => self.call_ollama(request).await,
LLMProvider::NewProvider => self.call_newprovider(request).await, // ← Add this
_ => Err("Provider not implemented".to_string()),
}
}
Then implement the handler. Here is an Ollama implementation example you can adapt:
async fn call_ollama(&self, req: LLMRequest) -> Result<LLMResponse, String> {
let payload = serde_json::json!({
"model": req.model,
"messages": req.messages,
"stream": false,
});
let client = reqwest::Client::new();
let resp = client
.post(&self.endpoint)
.json(&payload)
.send()
.await
.map_err(|e| e.to_string())?
.json::<OllamaResponse>()
.await
.map_err(|e| e.to_string())?;
Ok(LLMResponse { content: resp.message.content })
}
4. Configure Provider-Specific Settings
For providers requiring custom endpoints (like Ollama's local server), extend AppSettings in [frontend/src-tauri/src/settings.rs](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/settings.rs#L1-L9):
pub struct AppSettings {
pub ollama_endpoint: Option<String>,
pub ollama_token_threshold: Option<u32>,
pub newprovider_endpoint: Option<String>, // ← Add custom endpoint field
pub newprovider_token_threshold: Option<u32>, // ← Optional: add token limit
pub model: String,
pub max_tokens: u16,
pub temperature: f32,
pub top_p: f32,
pub default_token_threshold: u32,
}
In [summary/service.rs](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/service.rs#L300-L311), read the setting and pass it to the client:
let custom_endpoint = if provider == LLMProvider::Ollama {
settings.ollama_endpoint.clone().unwrap_or_else(|| "http://127.0.0.1:11434".to_string())
} else if provider == LLMProvider::NewProvider {
settings.newprovider_endpoint.clone().unwrap_or_else(|| "https://api.newprovider.com".to_string())
} else {
String::new()
};
let token_threshold = if provider == LLMProvider::Ollama {
settings.ollama_token_threshold.unwrap_or(2048)
} else if provider == LLMProvider::NewProvider {
settings.newprovider_token_threshold.unwrap_or(4096)
} else {
settings.default_token_threshold
};
let client = LLMClient::new(provider.clone(), api_key.clone(), custom_endpoint);
5. Update the Frontend Dropdown
Expose the new provider in the React UI. Modify [frontend/src/app/page.tsx](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/app/page.tsx#L45-L48):
<select value={aiProvider} onChange={e => setAiProvider(e.target.value)}>
<option value="openai">OpenAI</option>
<option value="claude">Claude</option>
<option value="groq">Groq</option>
<option value="ollama">Ollama</option>
<option value="newprovider">New Provider</option> // ← Add this option
</select>
Summary
- Extend the enum: Add variants to
LLMProviderinllm_client.rsand updatefrom_strandas_strmethods. - Define metadata: Implement
requires_api_key,default_endpoint, anddisplay_nameto configure authentication and routing. - Implement the handler: Add a dispatch case in
LLMClient::calland create the provider-specific async method (e.g.,call_ollama). - Configure settings: Add endpoint and token fields to
AppSettingsinsettings.rsand read them insummary/service.rs. - Update the UI: Add the provider option to the dropdown in
page.tsxto expose it to users.
Frequently Asked Questions
Where is the provider selection stored between sessions?
Meetily persists the selected provider through the Tauri settings store. The value is saved as a string identifier (e.g., "ollama") via the settings management in [settings.rs](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/settings.rs), and reloaded on application startup to pre-populate the dropdown in page.tsx.
Do I need to handle authentication differently for local providers like Ollama?
No. The requires_api_key() method returns false for Ollama, so the UI will not prompt users for an API key. For providers requiring authentication, ensure this method returns true and that your call_* method includes the key in the Authorization header, following the pattern used in the existing OpenAI and Claude handlers.
Can I add a provider that uses a non-OpenAI-compatible API format?
Yes. The call_* methods are completely independent. While Groq uses an OpenAI-compatible format, Ollama uses a different JSON schema. Create custom request/response structs (like OllamaRequest and OllamaResponse) and handle the serialization within your provider-specific method without affecting other providers.
How do I test a new provider without breaking existing functionality?
Add the new variant without removing existing ones and use the debug logging enabled with RUST_LOG=debug. Select the new provider in the UI and verify in the logs that the correct endpoint is called and that responses are parsed correctly before deploying to production.
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 →