← All ivy-nodes
IVYXSTUDIO · IVY NODE
T

Template Render

ivy.node.template-render · v0.1.0

ivyx✓

Fills {{ name }} placeholders in a text template from a mapping, and reports which names were missing. Substitution is textual, never evaluated, so a template that arrives from a model or a file cannot run code. Generalises the hand-written prompt-creator nodes; pure compute with no network or file access.

#template#prompt#text

Inputs

FieldTypeDescription
templaterequiredstringText with {{ name }} placeholders. Dotted names read nested values.
valuesobjectThe mapping the placeholders are filled from.
on_missingstringWhat a missing name does: error, empty or keep.

Outputs

FieldTypeDescription
textrequiredstringThe filled template.
missingrequiredarrayPlaceholder names that had no value, sorted and deduplicated.

Source

python

# Input preparation
inp = __ivy_ctx__["nodes"][__ivy_node_id__]["input"]
template = inp["template"]
values = inp.get("values") or {}
on_missing = inp.get("on_missing", "error")

# Compute
import re

PLACEHOLDER = re.compile(r"\{\{\s*([A-Za-z_][A-Za-z0-9_.]*)\s*\}\}")


def resolve(values, dotted):
    """Walk a dotted name through nested dicts. Absent yields None, False."""
    node = values
    for part in dotted.split("."):
        if not isinstance(node, dict) or part not in node:
            return None, False
        node = node[part]
    return node, True


def render(template, values, on_missing="error"):
    """
    Fill {{ name }} placeholders from a mapping.

    Deliberately not `str.format` and not an f-string: both execute parts of
    the template, so a template that arrives from a model or a file becomes a
    way to run code. This substitutes text and nothing else.

    `on_missing` is the whole of the policy about a name that is not there —
    `error` refuses, `empty` writes nothing, `keep` leaves the placeholder
    visible so the gap is obvious downstream. There is no silent default,
    because a half-filled prompt that looks finished is the failure this node
    exists to prevent.
    """
    if not isinstance(template, str):
        raise ValueError("template must be a string")
    if on_missing not in ("error", "empty", "keep"):
        raise ValueError("on_missing must be one of error, empty, keep")
    missing = []

    def substitute(match):
        name = match.group(1)
        value, found = resolve(values, name)
        if found:
            return "" if value is None else str(value)
        missing.append(name)
        if on_missing == "keep":
            return match.group(0)
        return ""

    text = PLACEHOLDER.sub(substitute, template)
    if missing and on_missing == "error":
        raise ValueError("missing value for: " + ", ".join(sorted(set(missing))))
    return text, sorted(set(missing))


rendered, missing_names = render(template, values, on_missing)

# Output collection (runner reads __ivy_ctx__)
out = __ivy_ctx__["nodes"][__ivy_node_id__]["output"]
out["text"] = rendered
out["missing"] = missing_names

Tests

Requires: python:3.9

  • fills-and-nests

    A flat name and a dotted name are both filled from the mapping.

  • keeps-the-gap-visible

    With on_missing keep, an unfilled placeholder stays in the text and is reported.

  • no-code-execution

    Braces that look like format syntax are left alone: the template is not evaluated.

  • missing-is-an-error-by-default

    By default a missing name refuses rather than producing a half-filled prompt.