How Meetily's Template-Based Summary Generation System Enables Dynamic Template Discovery
Meetily enables dynamic template discovery through a hierarchical three-tier fallback system that checks custom user directories, bundled app resources, and built-in embedded templates at runtime, allowing new JSON templates to be added without recompilation.
Meetily is an open-source meeting assistant that generates AI-powered summaries using a flexible, template-driven workflow. Unlike static configuration systems, Meetily's template-based summary generation system discovers templates dynamically at runtime from multiple hierarchical sources. This architecture allows users and developers to extend the application's summarization capabilities by simply dropping new JSON template files into designated directories.
The Three-Tier Template Hierarchy
Meetily's discovery mechanism prioritizes three distinct sources, enabling both default behavior and user customization without code changes.
Built-in Templates (Embedded at Compile Time)
Located in frontend/src-tauri/templates/, these default templates are compiled directly into the binary via defaults::get_builtin_template(). They serve as the final fallback when no external templates exist, ensuring the application always has functional defaults.
Bundled Templates (Application Resources)
Set at startup via set_bundled_templates_dir (typically pointing to resources/templates/), these templates are scanned dynamically using load_bundled_template(). The system references the global BUNDLED_TEMPLATES_DIR variable to locate and enumerate these files at runtime.
Custom User Templates (Platform-Specific Data)
Stored in the user's data directory, these templates receive highest priority:
- macOS:
~/Library/Application Support/Meetily/templates/ - Windows:
%APPDATA%\Meetily\templates\ - Linux:
~/.config/Meetily/templates/
The load_custom_template() function searches these directories for *.json files, allowing users to override built-in behavior instantly.
The Runtime Discovery Fallback Chain
When the UI or internal components request a template via get_template() in frontend/src-tauri/src/summary/templates/loader.rs, the system implements a strict priority fallback strategy:
- Custom directory – User-provided templates take precedence
- Bundled directory – Application-specific resources
- Built-in embedded – Compile-time defaults as last resort
If none of the locations contain the requested <id>.json, the function returns an error along with a complete list of discovered template IDs.
// src/summary/templates/loader.rs
pub fn get_template(template_id: &str) -> Result<Template, String> {
// 1️⃣ Try custom → 2️⃣ bundled → 3️⃣ built‑in
let json_content = if let Some(custom) = load_custom_template(template_id) {
debug!("Using custom template for '{}'", template_id);
custom
} else if let Some(bundled) = load_bundled_template(template_id) {
debug!("Using bundled template for '{}'", template_id);
bundled
} else if let Some(builtin) = defaults::get_builtin_template(template_id) {
debug!("Using built‑in template for '{}'", template_id);
builtin.to_string()
} else {
return Err(format!(
"Template '{}' not found. Available templates: {}",
template_id,
list_template_ids().join(", ")
));
};
// 4️⃣ Validate & deserialize into `Template`
validate_and_parse_template(&json_content)
}
Enumerating Available Templates
The list_template_ids() function in loader.rs performs complete enumeration by walking both the bundled and custom directories, merging these IDs with the built-in set, removing duplicates, and returning a sorted list.
// src/summary/templates/loader.rs – list_template_ids()
let mut ids: Vec<String> = defaults::list_builtin_template_ids()
.into_iter()
.map(|s| s.to_string())
.collect();
// … scan bundled_dir and custom_dir, push unique IDs …
ids.sort();
ids
This dynamic enumeration ensures the UI always reflects the current state of the filesystem, including templates added since the application started.
Exposing Discovery to the Frontend
Two Tauri commands translate the Rust loader's data into JSON for the React UI:
api_list_templates: ReturnsVec<TemplateInfo>containing identifiers, names, and descriptionsapi_get_template_details: ReturnsTemplateDetailswith the full section list
Both commands call templates::list_templates(), triggering fresh filesystem discovery on each invocation.
import { invoke } from '@tauri-apps/api/tauri';
async function fetchTemplates() {
const templates = await invoke<{
id: string;
name: string;
description: string;
}[]>('api_list_templates');
console.log('Available templates:', templates);
}
Because the loader reads the filesystem each call, adding a new *.json file to either the bundled or user template directory instantly makes the template available without restarting the application.
From Discovery to LLM Generation
Once a Template is loaded, its methods in frontend/src-tauri/src/summary/templates/types.rs create the artifacts needed for LLM prompting:
to_markdown_structure(): Generates a markdown skeleton the UI can displayto_section_instructions(): Creates natural-language instructions per section sent to the LLM
use app_lib::summary::templates;
fn generate_prompt(template_id: &str) -> Result<String, String> {
let tmpl = templates::get_template(template_id)?; // dynamic discovery
let markdown = tmpl.to_markdown_structure(); // skeleton
let instructions = tmpl.to_section_instructions(); // LLM prompt
Ok(format!("{}\n\n{}", instructions, markdown))
}
Creating Custom Templates Without Recompilation
To add a custom template, create a JSON file in the platform-specific directory (e.g., ~/Library/Application Support/Meetily/templates/):
{
"name": "My Custom Review",
"description": "Summarize weekly project updates.",
"sections": [
{
"title": "Highlights",
"instruction": "List the top three achievements",
"format": "list",
"item_format": "- {item}"
},
{
"title": "Risks",
"instruction": "Identify any blockers or risks",
"format": "paragraph"
}
]
}
The next call to api_list_templates will automatically include my_custom in the available options.
Summary
- Three-tier hierarchy: Meetily checks custom user directories first, then bundled resources, then built-in embedded templates.
- Runtime discovery: The
get_template()function inloader.rsimplements a fallback chain that reads the filesystem fresh on each request. - Live enumeration:
list_template_ids()merges and deduplicates templates from all sources, ensuring the UI always shows current availability. - Zero-downtime extension: Adding a valid JSON template to the user data directory makes it immediately available without recompilation or restart.
- LLM integration: Discovered templates drive summary generation through
to_markdown_structure()andto_section_instructions()methods.
Frequently Asked Questions
How does Meetily prioritize templates when multiple sources exist?
Meetily implements a strict priority fallback: custom user templates take precedence over bundled application templates, which in turn override built-in embedded defaults. When get_template() is called, it checks the custom directory first, then the bundled directory, and finally falls back to compile-time defaults. This ensures user customization always wins while maintaining reliable defaults.
Can I add new templates without restarting the Meetily application?
Yes. Because the template loader reads the filesystem dynamically on each invocation of list_templates() or get_template(), adding a new *.json file to the custom user directory or bundled resources directory makes it immediately available. The next call to api_list_templates from the frontend will include the new template in the returned list.
What is the required structure for a custom template JSON file?
Custom templates must define a name, description, and a sections array. Each section requires a title and instruction field, with optional format and item_format fields for styling hints. The file must be valid JSON and placed in the platform-specific data directory (e.g., ~/Library/Application Support/Meetily/templates/ on macOS) with a .json extension matching the template ID.
Where is the template discovery logic implemented in the source code?
The core discovery logic resides in frontend/src-tauri/src/summary/templates/loader.rs, specifically the get_template() and list_template_ids() functions. Data structures and validation are defined in types.rs, while Tauri commands exposing these functions to the frontend are located in template_commands.rs. Built-in defaults are managed in defaults.rs.
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 →