chat_template.jinjarenders messages into a prompt.response_template, intokenizer_config.json, parses generated text back into a message: the reasoning goes tothinking, the answer tocontent, and tool calls totool_calls.
Run the examples
Each section below has a collapsed example you can run. The examples use the tokenizer from the Beam checkpoint. They don’t load model weights or need a GPU. Install Transformers and setBEAM_MODEL_ID to the checkpoint’s Hugging Face repo ID or a local path. Every example builds on this setup. It defines render, which renders a prompt for generation, and two inputs the examples reuse: hello, a one-message conversation, and get_weather, a sample tool.
Messages
Each message starts with<|start|> and the speaker, and ends with <|end|>, <|call|>, or <|return|>:
Each marker is a single token in Beam’s vocabulary, but only
<|return|>, the end-of-sequence token, is a special token. Decoding with skip_special_tokens=True keeps the other markers, so the parser still sees message boundaries. Channel names, recipients such as to=get_weather, and rfl:parameter tags are plain text.
The rendered prompt has no line breaks between messages; the examples on this page add them for readability.
Chat template
System message
The template writes the system message itself. By default:model_identity replaces the first line, and reasoning_effort sets the effort line. Built-in tools are declared before the channels line.
Example: render a prompt
Example: render a prompt
Reasoning effort
The effort is an integer. Pass one directly, or use a label:minimal through xhigh follow the names of OpenAI’s reasoning_effort API parameter, and max is Beam’s highest effort. If you leave reasoning_effort unset or pass None, the template uses 512. Any other string raises an error, including OpenAI’s "none": Beam always reasons, so it has no effort level that turns reasoning off. Beam wasn’t trained below 8, so smaller values aren’t respected.
Example: set the reasoning effort
Example: set the reasoning effort
Instructions
system and developer messages are equivalent. Consecutive ones at the start of the conversation are joined, a blank line apart, under # Instructions in a developer message:
A
system or developer message that comes after a message with another role isn’t rendered. Put all system and developer messages at the start of the conversation.Tools
Tools passed intools are declared as TypeScript under # Tools, after the instructions:
// comments, optional parameters get ?, and integer becomes number. Tool names are used as is, with no functions. namespace.
Example: declare tools
Example: declare tools
This schema covers the JSON Schema features the template renders:Parameters keep the order of
properties.Built-in tools
builtin_tools moves the named tools from tools to the system message, with their own definitions. Only name search and browse, and only with the definitions Beam was trained with, shown in the example below. Every other tool, including a search or browse with a different definition, belongs in the developer message, where tools declares it by default.
Example: built-in search and browse
Example: built-in search and browse
These are the
search and browse definitions Beam was trained with:Example: the bash tool
Example: the bash tool
The first render passes only
command, so the template replaces the tool with Beam’s own bash definition, in the system message. The second adds a timeout parameter, so the tool stays in the developer message as written, with its own description and both parameters:Assistant turns
An assistant turn starts with oneanalysis message that holds the reasoning. It then ends with either a final answer and <|return|>, or one or more tool calls and <|call|>. After tool results come back, the next turn starts with analysis again. Beam never writes an empty analysis message.
In history, put reasoning in reasoning_content and the answer in content. The template also reads reasoning from thinking, the field the response template returns, and from reasoning, so a parsed message can go back into the history as is. Both take plain text: the template raises an error if they contain Harmony channel headers.
Reasoning across turns
Beam’s output degrades when earlier reasoning is missing. Many OpenAI-compatible clients dropreasoning_content from the history they send back, so check that yours keeps it.
By default, the template renders an assistant turn without reasoning as an empty analysis message. With allow_missing_thinking=False and add_generation_prompt=True, it raises an error instead if any earlier assistant turn has empty or missing reasoning:
allow_missing_thinking=False so that clients which drop reasoning get an error instead of degraded output. On SGLang and vLLM, start the server with --default-chat-template-kwargs '{"allow_missing_thinking": false}'. TokenSpeed has no server-wide default, so send "chat_template_kwargs": {"allow_missing_thinking": false} with each request.
The check doesn’t run without add_generation_prompt, so rendering complete conversations for training works either way.
Example: send reasoning back
Example: send reasoning back
By default, a history whose assistant turn has no reasoning renders an empty analysis message:With With the reasoning restored, it renders as an
allow_missing_thinking=False, the same history raises an error:analysis message before the answer:Tool calls
A tool call is an assistant message addressed to the tool, with XML arguments. The result comes back from the tool, addressed to the assistant:function.argumentsmust be a dict. Parse JSON strings before rendering.- String values are written as is. Other values are written as JSON.
- A value can’t contain
</rfl:parameter>. - The tool message needs a
name, or atool_call_idthat matches an earlier call. - Report errors in the tool message’s
content, like any other result.
Example: render a tool loop
Example: render a tool loop
tool_call_id, so the template takes the speaker name, get_weather, from the matching call. call_1 itself doesn’t appear in the prompt.Parallel calls
Each call is a separate message. All but the last end with<|end|>, and the last ends with <|call|>:
Example: parallel calls
Example: parallel calls
get_weather, so only their order tells Beam which city each one answers.Template arguments
tools and add_generation_prompt work as with any chat template.
Training masks
The assistant’s output is wrapped in a{% generation %} block, so apply_chat_template(..., return_dict=True, return_assistant_tokens_mask=True) returns assistant_masks. The mask covers each assistant turn from its first <|channel|> through <|return|> or <|call|>. TRL’s SFTConfig(assistant_only_loss=True) uses this mask.
Example: build a training mask
Example: build a training mask
Response template
A response template is the output-side counterpart of a chat template: it describes how to turn generated text back into a message. It is a recent Transformers standard; see Response parsing.generatedis the text the model wrote afterprompt.prefixis the prompt you generated from. The parser needs it because the prompt ends with an open<|start|>assistantheader.toolslets the parser cast arguments to the types in each tool’s schema. Without it, every argument is a string.- Parsed tool calls have no
id. Assign your own before running the tools.
tokenizer.get_response_parser(prefix=prompt, tools=tools) returns a parser that you feed() text to as it arrives.
These examples parse hand-written output. With a real model, parse the decoded new tokens instead.
Example: parse an answer
Example: parse an answer
Example: parse a tool call
Example: parse a tool call
Without Parallel calls come back as one entry each, in order:
tools, every argument is a string. With tools, arguments are cast to the schema’s types:Example: parse a stream
Example: parse a stream
get_response_parser returns a parser that you feed text as it arrives. It emits region_open, region_chunk, and region_close events:Differences from OpenAI Harmony
- XML arguments: JSON escaping interacts poorly with code in tool arguments, which XML tags avoid.
- No commentary channel: Beam doesn’t write tool preambles or other commentary, so tool calls and results use the
analysischannel. - Required reasoning: Beam was trained to reason on every turn, so each assistant turn starts with a non-empty
analysismessage.
<|refusal|> marker.