Skip to content

Rendering Variables

PromptContent.render(variables) substitutes {{variable}} placeholders with values you supply. It does the right thing per kind:

Kindrender(vars) returns
instructionstr — the prompt text (no substitution; instructions don't have variables)
f_stringstr — template with {{vars}} substituted
chatlist[{role, content}] — messages with {{vars}} substituted in each body
structureddict{base: <rendered>, response_schema: <dict>}

Substitution rules

The substitution syntax is {{name}} — double curly braces around an identifier. Whitespace inside the braces is allowed and ignored:

txt
{{name}}     ✓
{{ name }}   ✓  (same as above)
{name}       ✗  (not recognized)
{{name }}    ✓

The identifier matches [A-Za-z_]\w* — start with letter or underscore, then any word characters. No dots, no expressions, no method calls.

Missing variables

A {{name}} whose value isn't provided is replaced with an empty string. This is a deliberate choice — it makes templates forgiving but means you should validate inputs ahead of time if missing variables are bugs:

python
prompt = pm.get_active_prompt_version("greet")
# template: "Hi {{name}}, welcome to {{product}}."

prompt.render({"name": "Ada"})
# → "Hi Ada, welcome to ."     ← product missing → empty string

To enforce presence:

python
def render_strict(prompt, variables):
    declared = {v["name"] for v in prompt.variables}
    missing = declared - variables.keys()
    if missing:
        raise ValueError(f"Missing variables: {missing}")
    return prompt.render(variables)

Extra variables

Passing extra keys is harmless — they're ignored.

python
prompt.render({"name": "Ada", "extra": "ignored"})
# Works fine.

Variable values are stringified

Values pass through str(). Numbers, booleans, even None become strings:

python
prompt.render({"count": 42, "ready": True, "missing": None})
# {{count}}   → "42"
# {{ready}}   → "True"
# {{missing}} → "None"     ← surprising!

If you want None to render as empty, normalize before calling:

python
clean = {k: (v if v is not None else "") for k, v in variables.items()}
prompt.render(clean)

Repeated variables

Same placeholder used multiple times is substituted everywhere:

txt
"{{name}} likes {{food}}. {{name}} is hungry."
python
prompt.render({"name": "Ada", "food": "tea"})
# → "Ada likes tea. Ada is hungry."

Variables are NOT recursive

A value that itself contains {{...}} is not re-rendered. The substitution is one-pass.

python
prompt.render({"name": "{{evil}}"})
# Renders literally as "{{evil}}" in the output, not as a recursive expansion.

This is intentional — it prevents template-injection issues.

Recipes per kind

Instruction — no variables

python
prompt = pm.get_active_prompt_version("system_intro")  # kind: instruction
text = prompt.render()    # variables arg ignored

F-string — full substitution

python
prompt = pm.get_active_prompt_version("translator")  # kind: f_string

result = prompt.render({
    "source_lang": "English",
    "target_lang": "Spanish",
    "text": "Hello, world!",
})
# → "Translate the following from English to Spanish:\n\nHello, world!"

Chat — substitute inside every message body

python
prompt = pm.get_active_prompt_version("interview_bot")  # kind: chat

messages = prompt.render({
    "role_description": "senior backend engineer",
    "topic": "system design",
})
# → [
#     {"role": "system", "content": "You are interviewing a senior backend engineer about system design."},
#     {"role": "user",   "content": "Start with a question about system design."},
#   ]

Structured — recursive on the base

python
prompt = pm.get_active_prompt_version("entity_extractor")  # kind: structured

# Variables come from the BASE prompt, not the structured wrapper:
print(prompt.base.variables)
# [{"name": "text", ...}]

result = prompt.render({"text": "Apple released..."})
# → {
#     "base": "Extract entities from: Apple released...",
#     "response_schema": { ... unchanged ... }
#   }

response_schema is never substituted — it's static metadata, not a template.

Driving a UI from prompt.variables

The variable spec attached to f-string and chat prompts is {name, type, description} per variable. Use it to render a form, validate inputs, or document API endpoints automatically:

python
prompt = pm.get_active_prompt_version("translator")

# Render an HTML form
for var in prompt.variables:
    print(f'<label>{var["description"]}</label>')
    print(f'<input name="{var["name"]}" type="text">')

# Or validate FastAPI request bodies dynamically:
from pydantic import create_model

Model = create_model(
    "PromptInputs",
    **{var["name"]: (str, ...) for var in prompt.variables}
)

Defensive helper

A reusable wrapper that validates and renders in one call:

python
def render_with_validation(prompt, variables):
    """Render with required-variable enforcement."""
    if prompt.kind in ("instruction",):
        return prompt.render()

    declared = {v["name"] for v in prompt.variables}
    missing = declared - variables.keys()
    if missing:
        raise ValueError(
            f"Prompt '{prompt.prompt_name}' requires {sorted(declared)}; "
            f"missing: {sorted(missing)}"
        )

    return prompt.render(variables)

Copyright © 2026 elsai foundry.