import re from typing import Any, Dict import jinja2 # Create a minimal Jinja2 environment for text templates. # Use StrictUndefined so missing variables raise exceptions during rendering. ENV = jinja2.Environment( undefined=jinja2.StrictUndefined, autoescape=False, ) def _convert_braced_format_to_jinja(template: str) -> str: """ Convert simple `{var}` style placeholders into Jinja `{{ var }}` so legacy templates keep working. This will not touch existing `{{ }}` or `{% %}`. """ # Skip conversion if template already contains jinja markers if ("{{" in template) or ("{%" in template): return template # Replace single-brace simple identifiers like {a_b} or {a.b} -> {{ a_b }} or {{ a.b }} pattern = re.compile(r'(? str: """ Safely render a template string using Jinja2 with StrictUndefined. - Accepts legacy `{var}` placeholders by converting them to Jinja style. - Raises jinja2 exceptions when rendering fails. """ if template_str is None: raise ValueError("template_str is None") tpl = _convert_braced_format_to_jinja(template_str) jtpl = ENV.from_string(tpl) # Jinja expects normal Python types in context; if context contains Pydantic models, # they should be converted by caller to plain dicts. return jtpl.render(**context)