Template Engine¶
Estimated time to read: 5 minutes
The template engine of POML allows you to incorporate dynamic content and control structures. Here are some key features.
Expressions¶
You can use expressions enclosed in double curly brackets ({{
}}
) to evaluate variables or expressions dynamically:
In this example, if name
is set to "Alice" (e.g., using a <let>
tag, described below), the output will be "Hello, Alice!".
Usage in Attributes¶
Expressions can also be used within attribute values:
This renders to the following when index
is set to 1.
Expression Usages¶
POML supports various JavaScript expressions within the double curly brackets. This includes but is not limited to:
- Variables:
{{variableName}}
- Arithmetic:
{{a + b}}
,{{x * y}}
,{{count / total}}
- String Concatenation:
{{firstName + " " + lastName}}
- Array Access:
{{myArray[0]}}
- Object Property Access:
{{myObject.propertyName}}
- Function Calls:
{{myFunction(arg1, arg2)}}
(ifmyFunction
is defined in the context) - Ternary Operators:
{{condition ? valueIfTrue : valueIfFalse}}
- Accessing loop variables:
{{loop.index}}
(explained in the "For Attribute" section)
Let Expressions¶
The <let>
tag allows you to define variables, import data from external files, and set values within your POML template.
Syntax 1: Setting a variable from a value¶
This will output "Hello, world!". When using the content approach (as shown above), the text is treated as a literal string.
Alternatively, you can use the value
attribute which must contain an evaluatable JavaScript expression:
Note that when using the value
attribute, string literals must be properly quoted (e.g., "'Hello, world!'"
or '"Hello, world!"'
) since the value is evaluated as JavaScript. The value
attribute can contain strings, numbers, arrays, objects, or any valid JavaScript expression.
Syntax 2: Importing data from a file¶
This imports the contents of users.json
and assigns it to the users
variable. The src
attribute specifies the path to the file (relative to the POML file). The optional type
attribute can specify the file type (e.g., "json", "text", "csv"). If not provided, POML attempts to infer it from the file extension.
Syntax 3: Importing data from a file without a name¶
Ifconfig.json
contains { "apiKey": "your_api_key" }
, this will output "API Key: your_api_key". When you use src
without name
, and the file content is a JSON object, the properties of that object are directly added to the context.
Syntax 4: Setting a variable using inline JSON¶
<poml>
<let name="person">
{
"name": "Alice",
"age": 30
}
</let>
<p>Name: {{person.name}}, Age: {{person.age}}</p>
</poml>
This defines a person
variable with the given JSON object. You can also specify the type
attribute:
Syntax 5: Setting a variable from an expression¶
<poml>
<let name="base" value="10" />
<let name="increment" value="5" />
<let name="total" value="{{ base + increment }}" />
<p>Total: {{ total }}</p> <!-- Output: Total: 15 -->
</poml>
Type-Autocasting in Attributes¶
The attributes of components will be automatically cast based on their defined types in the component documentation. This means you don't have to worry about manually converting types in many cases.
- Boolean: If an attribute is defined as a boolean, values like
"true"
,1
,"1"
, or{{true}}
will be cast to the boolean valuetrue
. Similarly,"false"
,0
,"0"
, or{{false}}
will be cast tofalse
. - Number: If an attribute is defined as a number, values like
"123"
,45.6
,{{anyNumber}}
or{{myNumber+1.3}}
will be cast to their corresponding numeric values. - Object: If an attribute is defined as an object, POML will attempt to parse the attribute value as a JSON string. For example,
data="{{{name: 'John', age: 30}}}"
ordata='{"name":"John","age":30}'
will be parsed into the corresponding JavaScript object. - String: If an attribute is a string, no casting is performed.
In the following example, the first auto-casting happened at let, where true
is converted to boolean at let
expression.
<poml>
<let name="boolVar" type="boolean" value="true"/>
<let name="numVar" type="number" value="42"/>
<let name="objVar" type="object" value="{{ { key: 'value' } }}"/>
<MyComponent boolProp="{{boolVar}}" numProp="{{numVar}}" objProp="{{objVar}}" stringProp="hello"/>
</poml>
If MyComponent is defined with boolProp
as boolean, numProp
as number, objProp
as object, and stringProp
as string, the values will be interpreted and auto-casted again when MyComponent
is used.
For Attribute¶
To loop over a list, use the for
attribute. The syntax is for="itemName in listName"
.
This will render a list with "apple", "banana", and "cherry".
Loop Variables¶
Inside the loop, you have access to special loop
variables:
loop.index
: The current iteration index (starting from 0).loop.length
: The total number of items in the list.loop.first
:true
if it's the first iteration,false
otherwise.loop.last
:true
if it's the last iteration,false
otherwise.
Example:
<poml>
<let name="all_demos" value='[
{ "input": "What is your name?", "output": "My name is POML." },
{ "input": "What can you do?", "output": "I can generate prompts." }
]'/>
<examples>
<example for="example in all_demos" chat="false" caption="Example {{ loop.index + 1 }}" captionStyle="header">
<input>{{ example.input }}</input>
<output>{{ example.output }}</output>
</example>
</examples>
</poml>
This will generate two examples, with captions "Example 1" and "Example 2", displaying the input and output from each demo in the all_demos
array. Note that we use loop.index + 1
because loop.index
starts from 0.
If Condition¶
You can conditionally render elements using the if
attribute:
<poml>
<let name="isVisible" value="true"/>
<let name="isHidden" value="{{ !isVisible }}"/>
<p if="isVisible">This paragraph is visible.</p>
<p if="isHidden">This paragraph is hidden.</p>
</poml>
If isVisible
is true
, the first paragraph will be rendered. The second paragraph will not be rendered because isHidden is false. The value of if
can be a simple variable name (which is treated as a boolean) or a POML expression.
Include Files¶
You can split prompts into multiple files and include them using the <include>
tag.
The file specified in src
is read and its contents are injected as if they were written in place. Variables from the current context are available inside the included file. The for
and if
attributes work as expected: