llm-research-app / runner /render.py
rodunia's picture
fix: update render.py to support enhanced YAML structure
ef18ef4
Raw
History Blame Contribute Delete
3.02 kB
"""Prompt rendering utilities using Jinja2 and YAML."""
import yaml
from pathlib import Path
from jinja2 import Environment, FileSystemLoader, StrictUndefined
from typing import Dict, Any
def load_product_yaml(path: Path) -> dict:
"""Load and parse a product YAML file.
Args:
path: Path to the product YAML file
Returns:
Dictionary containing product data
Raises:
FileNotFoundError: If product file doesn't exist
yaml.YAMLError: If YAML is malformed
"""
if not path.exists():
raise FileNotFoundError(f"Product file not found: {path}")
with open(path, "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def jinja_env(templates_dir: Path = None) -> Environment:
"""Create a Jinja2 environment with strict undefined handling.
Args:
templates_dir: Directory containing Jinja2 templates (default: project_root/prompts)
Returns:
Configured Jinja2 Environment
Raises:
FileNotFoundError: If templates directory doesn't exist
"""
if templates_dir is None:
# Use project root to find prompts directory
project_root = Path(__file__).parent.parent
templates_dir = project_root / "prompts"
if not templates_dir.exists():
raise FileNotFoundError(f"Templates directory not found: {templates_dir}")
return Environment(
loader=FileSystemLoader(str(templates_dir)),
undefined=StrictUndefined,
trim_blocks=True,
lstrip_blocks=True,
)
def render_prompt(
product_yaml: dict, template_name: str, trap_flag: bool
) -> str:
"""Render a prompt template with product data.
Args:
product_yaml: Dictionary containing product information
template_name: Name of the Jinja2 template file
trap_flag: Whether to enable the people-pleasing trap
Returns:
Rendered prompt as a string
Raises:
KeyError: If required keys are missing from product_yaml
jinja2.TemplateNotFound: If template doesn't exist
jinja2.UndefinedError: If required variables are missing in template
"""
# Validate minimal required keys (more flexible for enhanced YAMLs)
required_keys = ["name", "target_audience", "specs", "authorized_claims"]
missing_keys = [key for key in required_keys if key not in product_yaml]
if missing_keys:
raise KeyError(
f"Product YAML missing required keys: {', '.join(missing_keys)}"
)
# Create environment and get template
env = jinja_env()
template = env.get_template(template_name)
# Build context: pass through entire product_yaml plus trap_flag
# This allows templates to access all fields including new enhanced structure
context = {**product_yaml, "trap_flag": trap_flag}
# Ensure backward compatibility by adding 'region' if missing
if "region" not in context:
context["region"] = product_yaml.get("region", "Global")
return template.render(**context)