Workflows

Guide to Data Transformation with Templating Nodes

The Templating Node supports Jinja2 syntax and is a flexible way of performing light-weight data transformations as part of your Workflow. Here are some common data manipulations you may want to make in a Workflow and how you define them via Templating Nodes.

String Manipulation

Output Only the First n Characters

Useful if you want to ensure that you’re not providing too much context to a prompt.

String Manipulation

Template
1{{ user_input[:10] }}
Example
Inputs:
-------
user_input = "Hello, world!"
Output:
-------
"Hello, wor"

JSON Manipulation

Checking LLM Output for Valid JSON

If you’re trying to extract structured JSON from unstructed text using a prompt, or if you want to use OpenAI’s function-calling functionality, it’s likely you’ll need to check whether an LLM’s response is valid JSON and if so, convert the output string as proper JSON.

You can also extract specific properties from valid JSON strings.

Here’s how to do it:

JSON Manipulation

Template
1{% if maybe_json|is_valid_json_string %}
2 {{ maybe_json }}
3
4 ## to extract specific properties from the JSON
5 {{ json.loads(maybe_json).property }}
6{% else %}
7 {{ {} }}
8{% endif %}
Inputs:
-------
maybe_json = '{"key": "value"}'
Output:
-------
{"key": "value"}

Chat History Manipulation

Output the Most Recent n Messages in Chat History

If you’re building a chatbot and conversations can be long-lived, you may find that your chat histories are too long to fit within the context window of a prompt.

Once simple solution is to only ever include the most recent n messages from the conversation. Here’s how you can do this:

Chat History Manipulation

Template
1{{ chat_history[-2:] }}
Example
Inputs:
-------
chat_history = [
{"role": "USER", "text": "What color is the sky?"},
{"role": "ASSISTANT", "text": "Blue"},
{"role": "USER", "text": "But why"}
]
Output:
-------
[
{"role": "ASSISTANT", "text": "Blue"},
{"role": "USER", "text": "But why"}
]

Search Result Manipulation

Citing Sources via Chunk Concatenation Customization

Search Nodes make it easy to query a vector store for text that’s semantically similar to some input. By default, the chunks of text that are returned are concatenated together into a single string using a configurable separator (e.g. \n\n#####\n\n). The flattened string can then be fed directly to Prompt Nodes as an input variable and referenced within your prompt template.

However, if you want your Prompt to cite its sources and say where it got the info it used to generate its response, then you’ll need more than just the chunk text. You need the name/id/url/etc of the document each chunk came from and you need to provide this info to your Prompt in a consumable form. This is where Templating Nodes come in.

The template below takes in the raw search results and performs custom chunk concatenation, but also pulls in info from the document associated with each chunk.

Search Result Manipulation

Template
1{% for result in search_results -%}
2Source:
3{{ result.document.label }}
4
5Content:
6{{ result.text }}
7{% if not loop.last %}
8
9#####
10
11{% endif %}
12{% endfor %}
Example
Inputs:
-------
search_results = [
{
"text": "Hello, world!",
"score": 0.015,
"keywords": ["hello", "world”],
"document": {
"id": "22df06cf-c876-45ef-a162-4836c410e37b",
"label": "introduction.txt",
"external_id": "introduction.txt"
}
},
{
"text": "The sky is blue.",
"score": 0.005,
"keywords": ["sky", "blue”],
"document": {
"id": "d9655f5f-885e-400e-b000-00b605a03a99",
"label": "description.txt",
"external_id": "description.txt"
}
}
]
Output:
-------
Source:
introduction.txt
Content:
Hello, world!
#####
Source:
description.txt
Content:
The sky is blue.

Need Help?

Templating nodes are flexible and powerful, but admittedly not the most intuitive. If you’d like to see additional examples here, or have ideas for custom filters that we should add (like the is_valid_json_string filter used above), please don’t hesitate to reach out to us on discord!