Creating Custom Templates¶
Overview¶
The Chat Template System is designed to be highly extensible, allowing you to create custom templates that fit your specific needs. This guide covers how to create, configure, and register custom templates.
Template Components¶
Core Template Fields¶
from chat_bricks import Template, register_template, Chat
register_template(
Template(
name="my-custom-template", # Unique identifier
system_template="System: {system_message}", # System message format
system_message="You are a helpful assistant.", # Default system message
user_template="User: {content}", # User message format
assistant_template="Assistant: {content}</s>", # Assistant message format
observations_template="Tool: {observation}", # Tool response format
stop_words=["</s>"] # Stop generation tokens
)
)
messages = [
{"role": "user", "content": "What is the capital of France?"},
{"role": "assistant", "content": "The capital of France is Paris."},
{"role": "user", "content": "Tell me more about Paris."}
]
tools = [
{
"function": {
"name": "get_weather",
"description": "Get weather information for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}
]
chat = Chat(template="my-custom-template", messages=messages, tools=tools)
prompt = chat.prompt()
print(prompt)
Advanced Template Fields¶
template = Template(
# ... core fields ...
# Tool support — the {tools} slot in system_template is filled by tools_template
system_template="System: {system_message}{tools}",
tools_template="\n\nTools: {tools}",
# Optional: wrap each individual tool entry before joining
# single_tool_template="\n- {tool}",
# Skill support — same two-pass pattern, but skills always live in the system message
skills_template="\n\nSkills:\n{skills}",
single_skill_template="- {name}: {description}",
# Vision support
vision_start="<vision>",
vision_end="</vision>",
image_token="<image>",
video_token="<video>",
# Custom chat template (Jinja)
chat_template="{% for message in messages %}{{ message.content }}{% endfor %}"
)
Template Creation Examples¶
1. Simple Chat Template¶
register_template(
Template(
name="simple-chat",
system_template="You are a helpful assistant.\n",
system_message="You are a helpful assistant.",
user_template="User: {content}\n",
assistant_template="Assistant: {content}\n",
stop_words=["\n"]
)
)
chat = Chat(template="simple-chat", messages=messages, tools=tools)
print(chat.prompt())
2. XML-Style Template¶
register_template(
Template(
name="xml-style",
system_template="<system>{system_message}</system>\n",
system_message="You are an AI assistant.",
user_template="<user>{content}</user>\n",
assistant_template="<assistant>{content}</assistant>\n",
stop_words=["</assistant>"]
)
)
chat = Chat(template="xml-style", messages=messages, tools=tools)
print(chat.prompt())
3. Markdown-Style Template¶
register_template(
Template(
name="markdown-style",
system_template="# System\n{system_message}\n\n",
system_message="You are a helpful AI assistant.",
user_template="## User\n{content}\n\n",
assistant_template="## Assistant\n{content}\n\n",
stop_words=["\n\n"]
)
)
chat = Chat(template="markdown-style", messages=messages, tools=tools)
print(chat.prompt())
4. Tool-Enabled Template¶
register_template(
Template(
name="tool-enabled",
# {tools} slot in system_template is filled by tools_template when tools are passed;
# when no tools are passed it expands to "".
system_template="System: {system_message}{tools}\n",
tools_template="\n\nAvailable Tools:\n{tools}",
system_message="You are an AI assistant with access to tools.",
user_template="User: {content}\n",
user_template_with_tools="User: {content}\n\nTools: {tools}\n",
assistant_template="Assistant: {content}\n",
observations_template="Tool Response: {observation}\n",
stop_words=["\n"]
)
)
messages = [
{"role": "user", "content": "Find me the weather of Paris"},
{"role": "assistant", "content": "Tool: get_weather Arguments: {'city': 'Paris'}"},
{"role": "Tool", "content": "24 degrees, raining."}
]
tools = [
{
"function": {
"name": "get_weather",
"description": "Get weather information for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}
]
chat = Chat(template="tool-enabled", messages=messages, tools=tools)
print(chat.prompt())
5. Skill-Enabled Template¶
Skills are (name, description) pairs that get listed in the system prompt so
the model knows which named skills it can load (typically via a load_skill
tool). They use the same two-pass pattern as tools: each entry is wrapped by
single_skill_template, joined, then wrapped by skills_template and inserted
into the {skills} slot of system_template.
register_template(
Template(
name="skill-enabled",
system_template="<|im_start|>system\n{system_message}{tools}{skills}<|im_end|>\n",
tools_template="\n\n# Tools\n<tools>\n{tools}\n</tools>",
skills_template="\n\n# Skills\n<skills>\n{skills}\n</skills>",
single_skill_template="- {name}: {description}", # default
system_message="You are an agent.",
user_template="<|im_start|>user\n{content}<|im_end|>\n",
assistant_template="<|im_start|>assistant\n{content}<|im_end|>\n",
observations_template="<|im_start|>tool\n{observation}<|im_end|>\n",
stop_words=["<|im_end|>"],
)
)
skills = [
{"name": "add-numbers", "description": "Adds two integers."},
{"name": "word-count", "description": "Counts words in text."},
]
chat = Chat(template="skill-enabled", messages=messages, skills=skills)
print(chat.prompt())
Skill entries may be dicts (as above) or any object that exposes .name and
.description attributes (e.g. dataclasses, pydantic models). If a template has
no skills_template, passing skills=... is silently ignored — making the
argument safe to thread through generic code.
6. Vision-Enabled Template¶
register_template(
Template(
name="vision-enabled",
system_template="You are a vision-capable AI assistant.\n",
system_message="You are a vision-capable AI assistant.",
user_template="User: {content}\n",
assistant_template="Assistant: {content}\n",
vision_start="<vision>",
vision_end="</vision>",
image_token="<image>",
video_token="<video>",
stop_words=["\n"]
)
)
messages = [
{
"role": "system",
"content": "You are a multi-modal assistant that can answer questions about images.",
},
{
"role": "user",
"content": [
{
"type": "image",
"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg",
},
{"type": "text", "text": "Describe this image."},
],
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "The image is a cat.",
},
],
}
]
chat = Chat(template="vision-enabled", messages=messages)
print(chat.prompt())
Policy Configuration¶
System Policy¶
from chat_bricks import SystemPolicy
# Basic system policy
system_policy = SystemPolicy(
use_system=True, # Always include system message
use_system_without_system_message=True, # Include system even without explicit message
content_processor=None # No content processing
)
# System policy with content processor
def add_timestamp(system_message):
from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return f"[{timestamp}] {system_message}"
system_policy = SystemPolicy(
use_system=True,
use_system_without_system_message=True,
content_processor=add_timestamp
)
Tool Policy¶
from chat_bricks import ToolPolicy, JsonFormatter, JsonIndentedFormatter
from chat_bricks import ToolPlacement
# Basic tool policy
tool_policy = ToolPolicy(
placement=ToolPlacement.SYSTEM, # Place tools in system message
formatter=JsonFormatter(indent=2) # Pretty-printed JSON
)
# Advanced tool policy
tool_policy = ToolPolicy(
placement=ToolPlacement.FIRST_USER, # Place tools with first user message
formatter=JsonIndentedFormatter(indent=4), # Indented JSON
content_processor=None # No content processing
)
Global Policy¶
from chat_bricks import GlobalPolicy
global_policy = GlobalPolicy(
prefix="<|begin_of_text|>" # Add prefix to all prompts
)
Complete Template Example¶
from chat_bricks import Template, GlobalPolicy
from chat_bricks import SystemPolicy
from chat_bricks import ToolPolicy, JsonIndentedFormatter
from chat_bricks import ToolPlacement
# Create a comprehensive template
comprehensive_template = Template(
name="comprehensive-example",
# Basic templates — {tools} and {skills} slots are filled by the section
# templates below; they expand to "" when no tools/skills are passed.
system_template="<|im_start|>system\n{system_message}{tools}{skills}<|im_end|>\n",
system_message="You are a comprehensive AI assistant with multiple capabilities.",
# Tool support
tools_template="\n\nAvailable Tools:\n{tools}",
user_template="<|im_start|>user\n{content}<|im_end|>\n",
user_template_with_tools="<|im_start|>user\n{content}\n\nTools: {tools}<|im_end|>\n",
assistant_template="<|im_start|>assistant\n{content}<|im_end|>\n",
observations_template="<|im_start|>tool\n{observation}<|im_end|>\n",
# Skill support
skills_template="\n\nSkills:\n{skills}",
# Vision support
vision_start="<|vision_start|>",
vision_end="<|vision_end|>",
image_token="<|image_pad|>",
video_token="<|video_pad|>",
# Stop words
stop_words=["<|im_end|>"],
# Policies
global_policy=GlobalPolicy(prefix="<|begin_of_text|>"),
system_policy=SystemPolicy(
use_system=True,
use_system_without_system_message=True
),
tool_policy=ToolPolicy(
placement=ToolPlacement.SYSTEM,
formatter=JsonIndentedFormatter(indent=2)
)
)
Template Registration¶
Registering a Template¶
from chat_bricks import register_template
# Register the template
register_template(comprehensive_template)
# Now you can use it
from chat_bricks import get_template
template = get_template("comprehensive-example")
Overriding Existing Templates¶
# Override an existing template
register_template(comprehensive_template, override=True)
Template Registry Management¶
from chat_bricks import TEMPLATES
# List all registered templates
print("Available templates:", list(TEMPLATES.keys()))
# Check if template exists
if "my-template" in TEMPLATES:
print("Template exists")
# Remove a template
if "my-template" in TEMPLATES:
del TEMPLATES["my-template"]
Advanced Template Features¶
Custom Jinja Templates¶
# Use a custom Jinja template
custom_jinja_template = Template(
name="custom-jinja",
chat_template="""
{% for message in messages %}
{% if message.role == 'system' %}
System: {{ message.content }}
{% elif message.role == 'user' %}
User: {{ message.content }}
{% elif message.role == 'assistant' %}
Assistant: {{ message.content }}
{% endif %}
{% endfor %}
""",
system_message="You are a helpful assistant."
)
Template Inheritance¶
# Create a base template
base_template = Template(
name="base",
system_template="Base: {system_message}\n",
user_template="User: {content}\n",
assistant_template="Assistant: {content}\n"
)
# Create a specialized template
specialized_template = Template(
name="specialized",
system_template="Specialized: {system_message}\n",
user_template=base_template.user_template, # Inherit user template
assistant_template=base_template.assistant_template, # Inherit assistant template
system_message="You are a specialized assistant."
)
Template Copying¶
# Copy an existing template
original = get_template("qwen2.5")
modified = original.copy()
modified.name = "qwen2.5-modified"
modified.system_message = "Modified system message"
# Register the modified version
register_template(modified)
Best Practices¶
1. Template Naming¶
- Use descriptive, unique names
- Include version information when appropriate
- Use consistent naming conventions across your project
2. Template Structure¶
- Keep templates focused and single-purpose
- Use consistent formatting patterns
- Document special tokens and their meanings
3. Policy Configuration¶
- Start with default policies and customize as needed
- Use appropriate tool placement strategies
- Consider the impact of content processors
4. Vision Support¶
- Only add vision tokens when needed
- Use appropriate vision start/end markers
- Consider token expansion implications
5. Testing¶
- Test templates with various message types
- Verify tool integration works correctly
- Test vision processing if applicable
- Validate Jinja template generation
Example: Complete Custom Template¶
Here's a complete example of creating a custom template for a specific use case:
from chat_bricks import Template, register_template
from chat_bricks import ToolPolicy, JsonCompactFormatter
from chat_bricks import ToolPlacement
# Create a coding assistant template
coding_template = Template(
name="coding-assistant",
# System message — the {tools} slot stays empty when no tools are passed,
# so a single template handles both the tool-free and tool-enabled cases.
system_template="""<|im_start|>system
You are an expert coding assistant. You help users write, debug, and understand code.
Always provide clear explanations and follow best practices.
{system_message}{tools}<|im_end|>
""",
system_message="You are an expert coding assistant.",
# User and assistant templates
user_template="<|im_start|>user\n{content}<|im_end|>\n",
assistant_template="<|im_start|>assistant\n{content}<|im_end|>\n",
# Tool support for code execution
tools_template="\n\nAvailable Tools:\n{tools}",
user_template_with_tools="<|im_start|>user\n{content}\n\nTools: {tools}<|im_end|>\n",
observations_template="<|im_start|>tool\n{observation}<|im_end|>\n",
# Stop words
stop_words=["<|im_end|>"],
# Tool policy - place tools with first user message
tool_policy=ToolPolicy(
placement=ToolPlacement.FIRST_USER,
formatter=JsonCompactFormatter()
)
)
# Register the template
register_template(coding_template)
# Test the template
from chat_bricks import Chat
chat = Chat(template="coding-assistant", messages=[
{"role": "user", "content": "Write a Python function to calculate fibonacci numbers"}
])
prompt = chat.prompt()
print(prompt)
This comprehensive guide should help you create custom templates that meet your specific requirements. Remember to test thoroughly and follow the best practices outlined above.