# How Meetily Implements Template-Based Meeting Summarization with Multiple AI Providers

> Explore how Meetily's summary processor achieves template-based meeting summarization by abstracting LLM interactions through a unified provider trait, supporting Qwen, Ollama, Groq, and OpenRouter.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: internals
- Published: 2026-07-26

---

**Meetily's summary processor uses a template-driven architecture that abstracts LLM interactions through a unified provider trait, enabling seamless switching between built-in Qwen models, Ollama, Groq, and OpenRouter without changing the core summarization logic.**

Meetily is an open-source meeting assistant that ships with a Rust-based backend utilizing Tauri's native capabilities. The application implements **template-based meeting summarization** through a modular pipeline defined in `src-tauri/src/summary/`, separating prompt templates from provider-specific implementations to support both local and cloud AI backends.

## Template System Architecture

The summarization engine centers on the **Template** struct defined in [`src-tauri/src/summary/templates/types.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/templates/types.rs). Each template contains:

- An `instruction` field describing the summarization task
- A `sections` vector defining ordered placeholders such as `{{meeting_name}}`, `{{transcript}}`, and `{{language}}`

Template definitions reside in [`src-tauri/src/summary/templates/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/templates/mod.rs), where static templates like "concise" or "detailed" are registered. At runtime, the processor selects templates by name, allowing users to switch between summary styles without code changes.

## LLM Provider Abstraction

All AI backends implement the **`LLMProvider`** trait defined in [`src-tauri/src/summary/llm_client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/llm_client.rs). This contract requires a single method:

```rust
fn generate(prompt: &str) -> Result<String, Error>

```

Concrete provider implementations live in `src-tauri/src/summary/summary_engine/`:

- **builtin_ai.rs**: Local Qwen and Gemma model execution
- **ollama.rs**: Integration with Ollama local servers
- **groq.rs**: Groq API client
- **openrouter.rs**: OpenRouter API access

The provider registry in [`src-tauri/src/summary/summary_engine/client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/summary_engine/client.rs) manages instantiation based on user settings, decoupling the summarization logic from transport and authentication details.

## Multi-Provider Selection Logic

Provider selection occurs through `builtin_ai_get_recommended_summary_model` in [`src-tauri/src/summary/summary_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/summary_engine/commands.rs). The recommendation engine inspects system resources, preferring higher-capacity Qwen models (4B parameters) on systems with ≥16GB RAM, while falling back to smaller Qwen 2B or Gemma variants on constrained hardware.

This automatic selection integrates with the `recommend_summary_model` function, which evaluates available providers and returns the optimal configuration without user intervention.

## The Summarization Pipeline

The end-to-end flow executes in [`src-tauri/src/summary/processor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/processor.rs) through the `generate_meeting_summary` function:

1. **Language Detection**: The system calls `detect_summary_language` from [`src-tauri/src/summary/language_detection.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/language_detection.rs) to normalize language codes (e.g., "en", "es") for cache keys and prompts.

2. **Prompt Construction**: The processor builds the final LLM input by injecting transcript data and metadata into the selected template's placeholders.

3. **Provider Execution**: The `LLMProvider::generate` method sends the constructed prompt to the chosen backend.

4. **Caching Strategy**: Results are stored using cache keys generated by `build_summary_cache_source`, combining `meeting_id + template_id + language` to prevent redundant AI calls.

5. **Post-Processing**: The `build_summary_result_json` function wraps raw LLM output, extracts meeting names from markdown, and emits `summary-update` events to the frontend via [`src-tauri/src/summary/service.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/service.rs).

## Code Implementation Examples

Frontend applications invoke the pipeline through Tauri commands:

```typescript
// Request a French summary using the concise template
await invoke('generate_meeting_summary', {
  meeting_id: 'abcd-1234',
  language: 'fr',
  template_name: 'concise'
});

```

The Rust service layer coordinates the process:

```rust
pub async fn generate_meeting_summary(
    pool: &sqlx::Pool<Sqlite>,
    meeting_id: &str,
    summary_language: Option<&str>,
    detected_summary_language: Option<&str>,
) -> Result<serde_json::Value, String> {
    // Build prompt from template
    let prompt = build_prompt(meeting_id, summary_language, detected_summary_language).await?;
    
    // Initialize provider from settings
    let provider = LLMProvider::new_from_settings(pool).await?;
    
    // Generate summary
    let raw_summary = provider.generate(&prompt).await?;
    
    // Return structured JSON
    Ok(build_summary_result_json(raw_summary, summary_language))
}

```

Template structures follow this schema:

```rust
pub struct Template {
    pub name: String,
    pub instruction: String,
    pub sections: Vec<TemplateSection>,
}

pub struct TemplateSection {
    pub placeholder: String,   // e.g., "{{transcript}}"
    pub description: String,
}

```

## Summary

- **Template-driven architecture**: Meetily separates prompt engineering from code through configurable templates in `src-tauri/src/summary/templates/`.
- **Provider abstraction**: The `LLMProvider` trait in [`src-tauri/src/summary/llm_client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/llm_client.rs) unifies local and cloud AI backends behind a single interface.
- **Intelligent selection**: Resource-aware model recommendation in [`summary_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/summary_engine/commands.rs) optimizes for available RAM and desired latency.
- **Comprehensive caching**: Cache keys combining meeting ID, template ID, and language prevent redundant LLM calls.
- **Event-driven updates**: The service layer emits frontend events through `summary-update` signals upon completion.

## Frequently Asked Questions

### How does Meetily select which AI provider to use?

The system evaluates hardware constraints through `builtin_ai_get_recommended_summary_model` in [`src-tauri/src/summary/summary_engine/commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/summary_engine/commands.rs), automatically selecting higher-capacity Qwen models on systems with ≥16GB RAM while falling back to smaller variants or alternative providers when resources are limited. Users can override this via settings to force specific backends like Ollama or Groq.

### Can I customize the summarization templates?

Yes. Templates are defined in [`src-tauri/src/summary/templates/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/templates/mod.rs) and structured in [`types.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/types.rs), allowing modification of instructions and placeholder sections. The system supports runtime template selection through the `template_name` parameter, enabling distinct styles (concise, detailed, technical) without redeploying the application.

### What languages are supported for meeting summaries?

Meetily supports any language detectable by the `detect_summary_language` function in [`src-tauri/src/summary/language_detection.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/src-tauri/src/summary/language_detection.rs), which normalizes codes to canonical forms (e.g., "en", "es") for the LLM prompt and caching layer. The template-based approach allows language-specific instructions to be embedded directly in template definitions.

### How does the caching mechanism prevent redundant AI calls?

The processor generates cache keys via `build_summary_cache_source` using the concatenation of `meeting_id`, `template_id`, and target `language`. These keys reference the `summary_processes` table, allowing identical transcript-template-language combinations to retrieve stored results instantly rather than invoking the LLM provider again.