# How Meetily's Template-Based Summary Generation System Enables Dynamic Template Discovery

> Discover how Meetily's template based summary generation system dynamically finds new templates at runtime without recompilation using a three-tier fallback. Add custom templates easily.

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

---

**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`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/summary/templates/loader.rs), the system implements a strict priority fallback strategy:

1. **Custom directory** – User-provided templates take precedence
2. **Bundled directory** – Application-specific resources
3. **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.

```rust
// 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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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.

```rust
// 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`**: Returns `Vec<TemplateInfo>` containing identifiers, names, and descriptions
- **`api_get_template_details`**: Returns `TemplateDetails` with the full section list

Both commands call `templates::list_templates()`, triggering fresh filesystem discovery on each invocation.

```typescript
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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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 display
- **`to_section_instructions()`**: Creates natural-language instructions per section sent to the LLM

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

```json
{
  "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 in [`loader.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/loader.rs) implements 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()` and `to_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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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`](https://github.com/Zackriya-Solutions/meetily/blob/main/types.rs), while Tauri commands exposing these functions to the frontend are located in [`template_commands.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/template_commands.rs). Built-in defaults are managed in [`defaults.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/defaults.rs).