# Meetily Summary Generation Workflow: How Ollama, Claude, Groq & OpenRouter Process Meeting Transcripts

> Explore the Meetily summary generation workflow using Ollama, Claude, Groq, and OpenRouter. Learn how it chunks transcripts, summarizes, and creates markdown reports.

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

---

**Meetily orchestrates a multi-pass LLM pipeline that chunks transcripts, generates partial summaries, combines them into a unified narrative, and renders final markdown reports using Ollama, Claude, Groq, or OpenRouter as backend providers.**

The Meetily meeting assistant handles summarization entirely within its Tauri desktop application, supporting both local and cloud-based language models. This article examines the complete summary generation workflow as implemented in the open-source Zackriya-Solutions/meetily repository, detailing how the Rust backend processes transcripts through multiple LLM providers.

## Architecture Overview: From Click to Completion

The summary generation workflow initiates when a user clicks **"Generate summary"** in the frontend. The `api_process_transcript` function serves as the API entry point, queuing a background job that bridges the frontend JavaScript to the Rust backend.

The system immediately persists the raw transcript data using `TranscriptChunksRepository::save_transcript_data`, storing the content in the local SQLite database. This ensures data durability before any LLM processing begins.

A background worker spawned via `tauri::async_runtime::spawn` executes `SummaryService::process_transcript_background`, which orchestrates the entire pipeline asynchronously without blocking the UI.

## Provider Selection and Authentication

The incoming request contains a `model_provider` string (e.g., `"ollama"`, `"claude"`, `"groq"`, `"openrouter"`). The `LLMProvider::from_str` implementation 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)** maps this string to a strongly-typed enum variant.

**API key handling** varies by provider type:

- **Cloud providers** (`openai`, `groq`, `openrouter`): The system retrieves the API key from the `settings` table in the SQLite database
- **Local providers** (Ollama, Built-in AI, Custom-OpenAI): No API key required; the service connects directly to the local endpoint or built-in model

## Dynamic Context Sizing and Text Chunking

Before sending text to the LLM, the system calculates optimal context windows. For **Ollama** specifically, the service queries the `ModelMetadataCache` to retrieve the model's specific context size, then applies a **300-token safety margin** to determine the token threshold for chunking.

When transcripts exceed this threshold and the provider has limited context (Ollama or Built-in AI), the `chunk_text` function in **[`frontend/src-tauri/src/summary/processor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/processor.rs)** splits the transcript into overlapping windows. This sliding window approach ensures no semantic context is lost at chunk boundaries.

## The Three-Pass Summarization Pipeline

Meetily implements a sophisticated three-pass approach to handle transcripts of arbitrary length while maintaining coherence.

### Pass 1: Chunk-Level Summarization

For every text chunk, the system builds a user prompt using `build_chunk_summary_user_prompt` that forces English-only output. Each chunk is processed independently through `generate_summary` (defined 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)**), producing partial summaries that capture local context.

### Pass 2: Summary Combination

If the transcript required multiple chunks, the individual chunk summaries are concatenated and sent back to the LLM using `build_combine_summary_user_prompt`. This pass synthesizes the partial summaries into a **single coherent English summary** of the entire meeting, eliminating redundancy and resolving cross-chunk references.

### Pass 3: Final Report Generation

The final pass transforms the combined summary into a structured markdown report. The system:

1. Selects the appropriate template (e.g., `"daily_standup"`)
2. Renders the template structure using `template.to_markdown_structure()`
3. Generates section instructions via `template.to_section_instructions()`
4. Constructs a final system prompt with `build_final_report_system_prompt`
5. Embeds the combined summary plus any user-provided custom prompt

This produces the final markdown report formatted according to the selected template.

## Language Handling and Translation

The `resolve_final_language_action` function determines the final output language through three possible paths:

- **Return as English**: When the transcript is already English and the target is English
- **Normalize to English**: When the transcript language is unknown or non-English but the target language is English
- **Translate**: When the target language differs from the source, triggering a second LLM call using `translation_system_prompt` and re-invoking `generate_summary` with dedicated translation prompts

## Output Cleaning and Delivery

Before returning the result to the frontend, `clean_llm_markdown_output` processes the LLM response. This function strips thinking tags and other metadata artifacts that cloud providers sometimes include in their outputs, ensuring clean markdown delivery to the user interface.

## Summary

- Meetily's summary generation workflow runs entirely within the Tauri desktop application using Rust-based background workers
- The system supports **Ollama, Claude, Groq, and OpenRouter** through a unified `LLMProvider` abstraction in [`llm_client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/llm_client.rs)
- A **three-pass pipeline** (chunk summarization → combination → final report) handles arbitrarily long transcripts while maintaining narrative coherence
- **Dynamic chunking** in [`processor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/processor.rs) adapts to model-specific context limits, applying a 300-token safety buffer for Ollama deployments
- **Language resolution** supports both normalization and active translation through secondary LLM calls when target languages differ from source content

## Frequently Asked Questions

### How does Meetily handle transcripts that exceed the LLM's context window?

Meetily implements intelligent chunking via the `chunk_text` function in [`processor.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/processor.rs). For Ollama and Built-in AI providers, the system queries the `ModelMetadataCache` to determine the specific model's context size, subtracts a 300-token safety margin, and splits the transcript into overlapping chunks. Each chunk is summarized individually in Pass 1, then combined into a coherent narrative in Pass 2.

### What is the difference between Pass 1 and Pass 2 in the summarization pipeline?

Pass 1 processes individual text chunks independently using `build_chunk_summary_user_prompt`, generating partial summaries that capture local context. Pass 2 activates only when multiple chunks exist, concatenating the partial summaries and sending them through `build_combine_summary_user_prompt` to create a unified, coherent summary that resolves cross-chunk dependencies and eliminates redundancy.

### Which LLM providers require API keys in Meetily?

According to the source code in [`llm_client.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/llm_client.rs), cloud providers including **Groq**, **OpenRouter**, and OpenAI require API keys fetched from the `settings` table. **Ollama**, Built-in AI, and Custom-OpenAI endpoints do not require API keys, as they connect to local or self-hosted infrastructure.

### How does Meetily determine the final output language for meeting summaries?

The `resolve_final_language_action` function evaluates the source transcript language against the target language preference. If they match English, it returns the content directly. If the source is unknown or non-English but the target is English, it normalizes to English. For non-English target languages, the system executes a secondary translation pass using `translation_system_prompt` and re-invokes `generate_summary` to produce the localized output.