How Meetily Implements Template-Based Meeting Summarization with Multiple AI Providers
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. Each template contains:
- An
instructionfield describing the summarization task - A
sectionsvector defining ordered placeholders such as{{meeting_name}},{{transcript}}, and{{language}}
Template definitions reside in 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. This contract requires a single method:
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 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. 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 through the generate_meeting_summary function:
-
Language Detection: The system calls
detect_summary_languagefromsrc-tauri/src/summary/language_detection.rsto normalize language codes (e.g., "en", "es") for cache keys and prompts. -
Prompt Construction: The processor builds the final LLM input by injecting transcript data and metadata into the selected template's placeholders.
-
Provider Execution: The
LLMProvider::generatemethod sends the constructed prompt to the chosen backend. -
Caching Strategy: Results are stored using cache keys generated by
build_summary_cache_source, combiningmeeting_id + template_id + languageto prevent redundant AI calls. -
Post-Processing: The
build_summary_result_jsonfunction wraps raw LLM output, extracts meeting names from markdown, and emitssummary-updateevents to the frontend viasrc-tauri/src/summary/service.rs.
Code Implementation Examples
Frontend applications invoke the pipeline through Tauri commands:
// 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:
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:
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
LLMProvidertrait insrc-tauri/src/summary/llm_client.rsunifies local and cloud AI backends behind a single interface. - Intelligent selection: Resource-aware model recommendation in
summary_engine/commands.rsoptimizes 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-updatesignals 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, 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 and structured in 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, 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.
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 →