Skip to content

Utilities and Support API

HoloDeck provides several utility modules for template rendering and error handling.

Template Engine

Jinja2-based template rendering for dynamic configuration and instruction generation.

TemplateRenderer()

Renders Jinja2 templates and validates output against schemas.

Provides safe template rendering with: - Restricted Jinja2 filters for security - YAML validation against AgentConfig schema - Clear error messages for debugging

Initialize the TemplateRenderer with a secure Jinja2 environment.

Source code in src/holodeck/lib/template_engine.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def __init__(self) -> None:
    """Initialize the TemplateRenderer with a secure Jinja2 environment."""
    # Create Jinja2 environment with strict mode (undefined variables cause errors)
    # autoescape disabled for YAML templates (appropriate for config generation)
    self.env = Environment(
        undefined=StrictUndefined,
        trim_blocks=True,
        lstrip_blocks=True,
        autoescape=select_autoescape(
            enabled_extensions=("html", "xml"),
            default_for_string=False,
            default=False,
        ),
    )

    # Only allow safe filters
    self._setup_safe_filters()

get_available_templates() staticmethod

Get available templates with metadata.

Discovers templates from the templates/ directory and extracts metadata (name, display_name, description) from their manifest.yaml files.

Returns:

Type Description
list[dict[str, str]]

List of dicts with 'value', 'display_name', 'description' keys.

list[dict[str, str]]

Returns empty list if templates directory doesn't exist.

Source code in src/holodeck/lib/template_engine.py
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
@staticmethod
def get_available_templates() -> list[dict[str, str]]:
    """Get available templates with metadata.

    Discovers templates from the templates/ directory and extracts
    metadata (name, display_name, description) from their manifest.yaml files.

    Returns:
        List of dicts with 'value', 'display_name', 'description' keys.
        Returns empty list if templates directory doesn't exist.
    """
    templates: list[dict[str, str]] = []

    for template_dir in TemplateRenderer._discover_template_dirs():
        manifest_path = template_dir / "manifest.yaml"
        with open(manifest_path) as f:
            data = yaml.safe_load(f)
        if data and isinstance(data, dict):
            templates.append(
                {
                    "value": str(data.get("name", template_dir.name)),
                    "display_name": str(
                        data.get("display_name", template_dir.name)
                    ),
                    "description": str(data.get("description", "")),
                }
            )

    return templates

list_available_templates() staticmethod

List all available built-in templates.

Discovers templates from the templates/ directory structure.

Returns:

Type Description
list[str]

List of template names (e.g., ['conversational', 'research',

list[str]

'customer-support'])

Source code in src/holodeck/lib/template_engine.py
225
226
227
228
229
230
231
232
233
234
235
@staticmethod
def list_available_templates() -> list[str]:
    """List all available built-in templates.

    Discovers templates from the templates/ directory structure.

    Returns:
        List of template names (e.g., ['conversational', 'research',
        'customer-support'])
    """
    return [d.name for d in TemplateRenderer._discover_template_dirs()]

render_and_validate(template_path, variables)

Render a Jinja2 template and validate output (for YAML files).

Combines rendering and validation in a safe way: only returns rendered content if both rendering and validation succeed. This is the recommended way to process agent.yaml templates.

Parameters:

Name Type Description Default
template_path str

Path to the Jinja2 template file

required
variables dict[str, Any]

Dictionary of variables to pass to the template

required

Returns:

Type Description
str

Rendered and validated template content as a string

Raises:

Type Description
InitError

If rendering fails

ValidationError

If validation fails

Source code in src/holodeck/lib/template_engine.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def render_and_validate(self, template_path: str, variables: dict[str, Any]) -> str:
    """Render a Jinja2 template and validate output (for YAML files).

    Combines rendering and validation in a safe way: only returns
    rendered content if both rendering and validation succeed.
    This is the recommended way to process agent.yaml templates.

    Args:
        template_path: Path to the Jinja2 template file
        variables: Dictionary of variables to pass to the template

    Returns:
        Rendered and validated template content as a string

    Raises:
        InitError: If rendering fails
        ValidationError: If validation fails
    """
    # Render template first
    rendered = self.render_template(template_path, variables)

    # Determine if this is agent.yaml specifically (not all YAML files)
    template_file = Path(template_path)
    is_agent_yaml = (
        template_file.name == "agent.yaml.j2" or template_file.stem == "agent.yaml"
    )

    if is_agent_yaml:
        # Validate YAML against schema
        # This will raise ValidationError if invalid
        self.validate_agent_config(rendered)

    # Return rendered content (safe to write to disk)
    return rendered

render_template(template_path, variables)

Render a Jinja2 template with provided variables.

Parameters:

Name Type Description Default
template_path str

Path to the Jinja2 template file

required
variables dict[str, Any]

Dictionary of variables to pass to the template

required

Returns:

Type Description
str

Rendered template content as a string

Raises:

Type Description
FileNotFoundError

If template file doesn't exist

InitError

If rendering fails (syntax errors, undefined variables, etc.)

Source code in src/holodeck/lib/template_engine.py
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def render_template(self, template_path: str, variables: dict[str, Any]) -> str:
    """Render a Jinja2 template with provided variables.

    Args:
        template_path: Path to the Jinja2 template file
        variables: Dictionary of variables to pass to the template

    Returns:
        Rendered template content as a string

    Raises:
        FileNotFoundError: If template file doesn't exist
        InitError: If rendering fails (syntax errors, undefined variables, etc.)
    """
    from holodeck.cli.exceptions import InitError

    template_file = Path(template_path)

    if not template_file.exists():
        raise FileNotFoundError(f"Template file not found: {template_path}")

    try:
        # Load template from file
        loader = FileSystemLoader(str(template_file.parent))
        env = Environment(
            loader=loader,
            undefined=StrictUndefined,
            trim_blocks=True,
            lstrip_blocks=True,
            autoescape=select_autoescape(
                enabled_extensions=("html", "xml"),
                default_for_string=False,
                default=False,
            ),
        )

        template = env.get_template(template_file.name)

        # Render template
        return template.render(variables)

    except TemplateSyntaxError as e:
        raise InitError(
            f"Template syntax error in {template_path}:\n"
            f"  Line {e.lineno}: {e.message}"
        ) from e
    except UndefinedError as e:
        raise InitError(
            f"Template rendering error in {template_path}:\n"
            f"  Undefined variable: {str(e)}"
        ) from e
    except Exception as e:
        raise InitError(f"Template rendering failed: {str(e)}") from e

validate_agent_config(yaml_content)

Validate YAML content against Agent schema.

Parses YAML and validates it against the Agent Pydantic model. This is the critical validation gate for agent.yaml files.

Parameters:

Name Type Description Default
yaml_content str

YAML content as a string

required

Returns:

Name Type Description
Agent Agent

Validated Agent configuration object

Raises:

Type Description
ValidationError

If YAML is invalid or doesn't match schema

InitError

If parsing fails

Source code in src/holodeck/lib/template_engine.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def validate_agent_config(self, yaml_content: str) -> Agent:
    """Validate YAML content against Agent schema.

    Parses YAML and validates it against the Agent Pydantic model.
    This is the critical validation gate for agent.yaml files.

    Args:
        yaml_content: YAML content as a string

    Returns:
        Agent: Validated Agent configuration object

    Raises:
        ValidationError: If YAML is invalid or doesn't match schema
        InitError: If parsing fails
    """
    from holodeck.cli.exceptions import InitError, ValidationError

    try:
        # Parse YAML
        data = yaml.safe_load(yaml_content)

        if not data:
            raise ValidationError("agent.yaml content is empty")

        # Validate against Agent schema
        agent = Agent.model_validate(data)
        return agent

    except yaml.YAMLError as e:
        raise ValidationError(f"YAML parsing error:\n" f"  {str(e)}") from e
    except ValidationError:
        # Re-raise our validation errors as-is
        raise
    except Exception as e:
        # Catch Pydantic validation errors
        if hasattr(e, "errors"):
            # Pydantic ValidationError
            errors = e.errors()
            error_msg = "Agent configuration validation failed:\n"
            for error in errors:
                field = ".".join(str(loc) for loc in error["loc"])
                error_msg += f"  {field}: {error['msg']}\n"
            raise ValidationError(error_msg) from e
        else:
            raise InitError(
                f"Agent configuration validation failed: {str(e)}"
            ) from e

Usage Examples

Template Rendering

from holodeck.lib.template_engine import TemplateRenderer

renderer = TemplateRenderer()

# Render inline template
result = renderer.render_template(
    "template_string",
    {"name": "Alice"}
)