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 and in expression columns for deterministic data generation.
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
conditionalsBasic
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 |
---|---|---|---|---|---|
|
✅ |
|
❌ |
|
✅ |
|
❌ |
|
✅ |
|
✅ |
|
✅ |
|
✅ |
|
✅ |
|
❌ |
|
❌ |
|
❌ |
|
❌ |
|
✅ |
|
❌ |
|
❌ |
|
❌ |
|
❌ |
|
❌ |
|
❌ |
|
❌ |
|
❌ |
|
✅ |
||
|
✅ |
|
✅ |
||
|
✅ |
|
✅ |
||
|
✅ |
|
❌ |
||
|
❌ |
|
❌ |
||
|
❌ |
|
❌ |
||
|
❌ |
|
❌ |
||
|
❌ |
|
✅ |
||
|
✅ |
|
✅ |
||
|
✅ |
|
❌ |
||
|
✅ |
|
❌ |
||
|
✅ |
|
✅ |
||
|
✅ |
|
❌ |
||
|
✅ |
|
✅ |
||
|
✅ |
|
✅ |
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
orblock
.Variable or function definitions with
macro
orset
.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.
import os
from nemo_microservices import NeMoMicroservices
from nemo_microservices.beta.data_designer import DataDesignerClient, DataDesignerConfigBuilder
from nemo_microservices.beta.data_designer.config import columns as C
from nemo_microservices.beta.data_designer.config import params as P
data_designer_client = DataDesignerClient(
client=NeMoMicroservices(base_url=os.environ["NEMO_MICROSERVICES_BASE_URL"])
)
config_builder = DataDesignerConfigBuilder(model_configs="path/to/your/model_configs.yaml")
# Add uniform sampler column for apple weight
config_builder.add_column(
C.SamplerColumn(
name="apple_weight",
type=P.SamplerType.UNIFORM,
params=P.UniformSamplerParams(low=1, high=10)
)
)
# Add LLM text column with Jinja template
config_builder.add_column(
C.LLMTextColumn(
name="apple_cost_question",
prompt="You are at a farmer's market. Ask the farmer how much {{ apple_weight }} lbs. of apples will cost.",
model_alias="text"
)
)
# Generate the data
preview = data_designer_client.preview(config_builder)
preview.display_sample_record()
Let’s take a look at an example from this generated dataset.
Column |
Value |
---|---|
|
6.726121999941741 |
|
“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_cost_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
.
# Add LLM text column with Jinja template and filter
config_builder.add_column(
C.LLMTextColumn(
name="apple_cost_question",
prompt="You are at a farmer's market. Ask the farmer how much {{ apple_weight | round(2) }} lbs. of apples will cost.",
model_alias="text"
)
)
Column |
Value |
---|---|
|
6.726121999941741 |
|
“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 using ExpressionColumn
. Let’s use this to make a short-hand for easier prompting.
# Add expression column for formatted weight
config_builder.add_column(
C.ExpressionColumn(
name="apple_weight_str",
expr="{{ apple_weight | round(2) }} lbs."
)
)
# Use the formatted string in the prompt
config_builder.add_column(
C.LLMTextColumn(
name="apple_cost_question",
prompt="You are at a farmer's market. Ask the farmer how much {{ apple_weight_str }} of apples will cost.",
model_alias="text"
)
)
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 use for other purposes.
Column |
Value |
---|---|
|
6.726121999941741 |
|
“6.73 lbs.” |
|
“Excuse me, sir. I’m interested in purchasing some of your apples. Could you please tell me how much 6.73 lbs. 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 this in conjunction with synthetically generated Person
types or our own locally defined structured data types.
from pydantic import BaseModel
# Define structured data model
class ShoppingCart(BaseModel):
pear_lbs: float
apple_lbs: float
carrot_lbs: float
broccoli_lbs: float
# Add person column
config_builder.add_column(
C.SamplerColumn(
name="farmer_person",
type=P.SamplerType.PERSON,
params=P.PersonSamplerParams(locale="en_US")
)
)
# Add structured shopping cart column
config_builder.add_column(
C.LLMStructuredColumn(
name="my_shopping_cart",
prompt="I'm at a local farmer's market. Fill my shopping cart full of healthy fruits and vegetables.",
output_format=ShoppingCart,
model_alias="text"
)
)
# Use structured data in prompts
config_builder.add_column(
C.LLMTextColumn(
name="apple_cost_question",
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.",
model_alias="text"
)
)
Column |
Value |
---|---|
|
{“first_name”: “Donna”, …} |
|
{“pear_lbs”: 2.5, “apple_lbs”: 3.2, “carrot_lbs”: 4.8, “broccoli_lbs”: 1.9} |
|
“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 can use Jinja template if/else logic to help us do that.
config_builder.add_column(
C.LLMTextColumn(
name="fruit_cost_question",
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.""",
model_alias="text"
)
)
Column |
Value |
---|---|
|
{“first_name”: “Donna”, …} |
|
{“pear_lbs”: 2.5, “apple_lbs”: 3.2, “carrot_lbs”: 4.8, “broccoli_lbs”: 1.9} |
|
“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?” |