# How to Add a New AI Provider (Ollama, Claude, Groq) for Meeting Summaries in Meetily

> Add new AI providers like Ollama Claude or Groq for meeting summaries in Meetily. Learn how to extend the LLMProvider enum and configure endpoints for seamless integration.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-14

---

**To add a new AI provider like Ollama, Claude, or Groq to Meetily, extend the `LLMProvider` enum in [`llm_client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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)](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](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/llm_client.rs#L37-L55)) 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](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/llm_client.rs#L200-L212)) 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/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`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/llm_client.rs):

```rust
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:

```rust
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:

```rust
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](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/llm_client.rs#L200-L212):

```rust
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:

```rust
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)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/settings.rs#L1-L9):

```rust
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/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:

```rust
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)](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src/app/page.tsx#L45-L48):

```tsx
<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 `LLMProvider` in [`llm_client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/llm_client.rs) and update `from_str` and `as_str` methods.
- **Define metadata**: Implement `requires_api_key`, `default_endpoint`, and `display_name` to configure authentication and routing.
- **Implement the handler**: Add a dispatch case in `LLMClient::call` and create the provider-specific async method (e.g., `call_ollama`).
- **Configure settings**: Add endpoint and token fields to `AppSettings` in [`settings.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/settings.rs) and read them in [`summary/service.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/summary/service.rs).
- **Update the UI**: Add the provider option to the dropdown in [`page.tsx`](https://github.com/Zackriya-Solutions/meetily/blob/main/page.tsx) to 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/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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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.