Using Jinja Templates

Overview

Data Designer supports server-side rendering of user-provided Jinja templates to support complex expressions in prompts for LLM generated columns.

Supported Features

Data Designer supports a subset of Jinja features, as defined below.

  • Basic variable substitution: {{ variable_name }}

  • Access to object fields: {{ variable_name.attribute }}

  • Control flow with if/else conditionals

  • Basic for loops (non-nested, non-recursive)

  • Basic datatype conversions such as {{ column.upper() }} and {{ column.lower() }}

  • A limited subset of builtin Jinja2 filters, which can be found below.

Supported Builtin Jinja Filters

For more information on how to use each of these filters within your Jinja templates, please refer to the Jinja2 builtin filter documentation.

Filter
Supported
Filter
Supported
Filter
Supported

abs

map

unique

attr

max

upper

capitalize

min

urlencode

center

pprint

urlize

default

random

wordcount

dictsort

reject

wordwrap

escape

rejectattr

xmlattr

filesizeformat

replace

first

reverse

last

round

float

safe

forceescape

select

format

selectattr

groupby

slice

indent

sort

int

string

items

striptags

join

sum

last

title

length

tojson

list

trim

lower

truncate

Template Restrictions

Please note the following limitations on Jinja template rendering. Requests made with templates exhibiting any of these features will be rejected -- either preventing workflow submission or halting workflow execution.

  • Nested or recursive loops.

  • References to private object attributes.

  • References to server-side template rendering instructions like include, extends or block.

  • Variable or function definitions with macro or set.

  • Templates with excessive complexity (operation count, nesting levels, or iteration count).

  • Templates that render to character counts in excess of 128k.

If you have a particular use-case that is prevented by these restrictions, please contact us to let us know more about the problem you're tackling -- there may be another way to approach it.

Basic Data Designer Jinja Example

In this example, let's consider the case of prompting a model to ask questions about the cost of apples at a farmer's market.

from gretel_client.navigator_client import Gretel

designer = Gretel().data_designer.new(model_suite="llama-3.x")

designer.add_column(
    name="apple_weight",
    type="uniform",
    params={"low": 0, "high": 10}
)

designer.add_column(
    name="apple_cost_question",
    type="llm-gen",
    prompt="You are at a farmer's market. Ask the farmer how much {{ apple_weight }} lbs. of apples will cost."
)

Let's take a look at an example from this generated dataset.

Column
Value

apple_weight

6.726121999941741

apple_cost_question

"Excuse me, sir. I'm interested in purchasing some of your apples. Could you please tell me how much 6.726121999941741 pounds of apples will cost?

While this prompt template does include the apple weight into the prompt, and therefore make it into the resulting generated apple_question, this isn't a very natural question. Let's make use of Jinja filters to help transform the prompt the LLM receives and get a more natural result. We can make use of the round builtin Jinja filter to round off the real-valued apple_weight.

designer.add_column(
    name="apple_cost_question",
    type="llm-gen",
    prompt="You are at a farmer's market. Ask the farmer how much {{ apple_weight | round(2) }} lbs. of apples will cost."
)
Column
Value

apple_weight

6.726121999941741

apple_cost_question

"Excuse me, sir. I'm interested in purchasing some of your apples. Could you please tell me how much 6.73 pounds of apples will cost?

The usage of Jinja templating isn't restricted to just LLM prompt templates, we can also create new, deterministic columns based on Jinja templates. Let's use this to make a short-hand for easier prompting.

designer.add_column(
    name="apple_weight",
    type="uniform",
    params={"low": 0, "high": 10}
)

designer.add_column(
    name="apple_weight_str",
    type="expression",
    expr="{{ apple_weight | round(2) }} lbs."
)

designer.add_column(
    name="apple_cost_question",
    type="llm-gen",
    prompt="You are at a farmer's market. Ask the farmer how much {{ apple_weight_str }} of apples will cost."
)

When run, we'll get examples that look like the following. While we obtain the same result, we now also have a string that we can for other purposes.

Column
Value

apple_weight

6.726121999941741

apple_weight_str

"6.73 lbs."

apple_cost_question

"Excuse me, sir. I'm interested in purchasing some of your apples. Could you please tell me how much 6.73 pounds of apples will cost?

But we aren't limited to just referring to variables by name -- using Jinja templates, we can also refer to structured data fields. We can use in conjunction with synthetically generated Person types or our own locally defined structured data types.

from pydantic import BaseModel

class ShoppingCart(BaseModel):
    pear_lbs: float
    apple_lbs: float 
    carrot_lbs: float
    broccoli_lbs: float


designer.add_column(
    name="farmer_person",
    type="person",
    params={"locale": "en_US"}
)

designer.add_column(
    name="my_shopping_cart",
    type="llm-gen",
    prompt="I'm at a local farmer's market. Fill my shopping cart full of healthy fruits and vegetables.",
    data_config={"type": "structured", "params": {"json_schema": ShoppingCart.model_json_schema()}}
)

designer.add_column(
    name="apple_cost_question",
    type="llm-gen",
    prompt="You are at a farmer's market. Ask farmer {{ farmer_person.first_name }} how much {{ my_shopping_cart.apple_lbs }} lbs. of apples will cost."
)
Column
Value

farmer_person

{"first_name": "Donna", ...}

my_shopping_cart

{"pear_lbs": 2.5, "apple_lbs": 3.2, "carrot_lbs": 4.8, "broccoli_lbs": 1.9}

apple_cost_question

"Hi Farmer Donna, I'm interested in buying some of your apples. Could you tell me how much 3.2 pounds of apples would cost?"

Lastly, perhaps we want to vary what we ask based on some other property, we Jinja's if/else logic to help us do that.

designer.add_column(
    name="fruit_cost_question",
    type="llm-gen",
    prompt="""\
You are at a farmer's market. Ask farmer {{ farmer_person.first_name }} how much {% if farmer_person.sex == "Male" -%}
{{ my_shopping_cart.apple_lbs }} lbs. of apples 
{%- else %}
{{ my_shopping_cart.pear_lbs }} lbs. of pears 
{%- endif %} will cost.
"""
)
Column
Value

farmer_person

{"first_name": "Donna", ...}

my_shopping_cart

{"pear_lbs": 2.5, "apple_lbs": 3.2, "carrot_lbs": 4.8, "broccoli_lbs": 1.9}

fruit_cost_question

"Hi Farmer Donna, I'm interested in buying some of your pears. Could you tell me how much 2.5 pounds of pears would cost?"

Last updated

Was this helpful?