This directory contains canonical prompt templates, metadata configurations, and schema definitions for models supported by LiteRT-LM.
LiteRT-LM uses Jinja2 (rendered hermetically via Minijinja) to transform structured conversation turns and tool declarations into model-specific prompt strings.
For the formal JSON Schema specification of all input variables, see
chat_template_input_schema.json.
- Input Variables
- Message Structure
- Tool Definitions
- Writing Templates for LiteRT-LM
- Examples
- Testing and Validation
When rendering a prompt template, LiteRT-LM passes a context dictionary containing the following top-level fields:
| Field | Type | Description |
|---|---|---|
messages |
array |
(Required) The conversation history as a list of message objects. |
tools |
array |
(Optional) Available tools (function declarations) that the model can invoke. |
enable_thinking |
boolean |
(Optional) Whether the model should produce reasoning thoughts before answering. |
add_generation_prompt |
boolean |
(Optional) Whether to append the model turn prefix to prompt generation (defaults to true). |
bos_token |
string |
(Optional) Beginning-of-sequence token (e.g. <bos>, <s>). |
Each entry in messages represents a single turn in the dialogue.
The role string specifies the author of the turn:
"system": System instructions, personality, guidelines, and tool schemas."user": Prompts and queries from the end user."assistant": Responses generated by the model (text and/or tool calls)."tool": Outputs and return values from executed tools/functions.
The content field is strictly a list of multimodal parts. Plain text
prompts are wrapped in a text part object:
{
"role": "user",
"content": [
{"type": "text", "text": "What is the capital of France?"}
]
}Multimodal prompts with images, audio, or video include the corresponding part objects alongside text parts:
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image"}
]
}When the assistant requests one or more function invocations, the message
includes a tool_calls list:
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"name": "get_weather",
"arguments": {"location": "San Francisco, CA"}
}
}
]
}When multiple tool calls are invoked in parallel within a single turn:
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"name": "get_weather",
"arguments": {"location": "San Francisco, CA"}
}
},
{
"type": "function",
"function": {
"name": "get_time",
"arguments": {"location": "San Francisco, CA"}
}
}
]
}Note:
argumentscan be provided either as a JSON object or as a serialized JSON string. Whentool_callsis present,contentis optional and may be omitted or empty.
When a function executes, its result is supplied as a message with role: "tool":
{
"role": "tool",
"content": [
{
"type": "tool_response",
"name": "get_weather",
"response": {"temperature": "18C", "condition": "Sunny"}
}
]
}When multiple tool calls are executed, their responses are bundled into a single
message with multiple tool_response items in content:
{
"role": "tool",
"content": [
{
"type": "tool_response",
"name": "get_weather",
"response": {"temperature": "18C", "condition": "Sunny"}
},
{
"type": "tool_response",
"name": "get_time",
"response": {"time": "14:30"}
}
]
}When tools are supplied, each item in the tools array follows the OpenAPI
standard:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Fetch the current weather for a specified location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
}Chat templates should be placed in the model's directory (e.g.
models/<model_name>/chat_template.jinja).
The Minijinja engine provides several built-in utilities:
tojson: Serializes a value into formatted JSON string (e.g.{{ tool | tojson }}).lstrip(chars)/rstrip(chars): Trims leading/trailing whitespace or specific characters.strftime_now(format): Formats the current date/time into a string using the given format (e.g.{{ strftime_now("%d %b %Y") }}). Time-aware templates should use this function rather than referencing a rawnowvariable.raise_exception(message): Raises an error during template rendering if an invariant is violated.is none: Test for undefined or null variables.
Templates that support function calling typically check if tools is defined
and loop through available function signatures:
{%- if tools is defined and tools -%}
<tools>
{%- for tool in tools %}
{{ tool.function | tojson }}
{%- endfor %}
</tools>
{%- endif %}When rendering an assistant turn with function calls:
{%- if message.tool_calls is defined and message.tool_calls -%}
{%- for tool_call in message.tool_calls %}
<tool_call>
{"name": "{{ tool_call.function.name }}", "arguments": {{ tool_call.function.arguments | tojson }}}
</tool_call>
{%- endfor %}
{%- endif -%}If the model supports configurable thinking/reasoning modes, use the default
filter:
{%- set thinking = enable_thinking | default(false) -%}
{%- if thinking -%}
<think>
</think>
{%- endif -%}{
"messages": [
{
"role": "system",
"content": [
{"type": "text", "text": "You are a concise, helpful assistant."}
]
},
{
"role": "user",
"content": [
{"type": "text", "text": "Hello!"}
]
},
{
"role": "assistant",
"content": [
{"type": "text", "text": "Hi! How can I help you today?"}
]
}
],
"add_generation_prompt": true
}{
"messages": [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": "Describe what is shown in this picture."}
]
}
],
"add_generation_prompt": true
}{
"tools": [
{
"type": "function",
"function": {
"name": "lookup_stock",
"description": "Get current stock price for a ticker symbol.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker symbol, e.g. GOOG"}
},
"required": ["symbol"]
}
}
}
],
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What is the price of GOOG?"}
]
},
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"name": "lookup_stock",
"arguments": {"symbol": "GOOG"}
}
}
]
},
{
"role": "tool",
"content": [
{
"type": "tool_response",
"name": "lookup_stock",
"response": {"price": "180.50", "currency": "USD"}
}
]
}
],
"add_generation_prompt": true
}{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location.",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"]
}
}
}
],
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What is the weather in London and Paris?"}
]
},
{
"role": "assistant",
"tool_calls": [
{
"type": "function",
"function": {
"name": "get_weather",
"arguments": {"location": "London"}
}
},
{
"type": "function",
"function": {
"name": "get_weather",
"arguments": {"location": "Paris"}
}
}
]
},
{
"role": "tool",
"content": [
{
"type": "tool_response",
"name": "get_weather",
"response": {"location": "London", "temperature": "18C"}
},
{
"type": "tool_response",
"name": "get_weather",
"response": {"location": "Paris", "temperature": "22C"}
}
]
}
],
"add_generation_prompt": true
}All chat template test cases are defined under testdata/input/*.json and
validated using the chat_template_test Bazel macro from
chat_template_test.bzl.
Rendered outputs are checked against golden reference files under
testdata/golden/.
To add a test for a new model family, define in BUILD:
load("//models:chat_template_test.bzl", "chat_template_test")
chat_template_test(
name = "chat_template_test",
chat_template = "chat_template.jinja",
golden_dir = "testdata/golden",
input_dir = "testdata/input",
pbtext_files = [":LlmMetadataProto.pbtext"],
)To run all chat template tests across all models:
bazel test //models/...