vEth
02/11/2026, 7:46 AMmessages parameter and rendered in the prompt template.vEth
02/11/2026, 7:46 AMmessages: Message[] containing user, assistant, and tool role messages
- Each assistant message may contain tool_calls (id, name, arguments)
- Each tool message contains the result for a specific tool_call_id
- The function is called in a loop: LLM returns a tool call → executor runs it → results are appended to messages → function is called againvEth
02/11/2026, 7:47 AMtool messages:
jinja
{%- for msg in messages %}
{%- if msg.role == "user" %}
{{ _.role("user") }}
{{ msg.content }}
{%- elif msg.role == "assistant" %}
{%- if msg.tool_calls %}
{{ _.role("assistant") }}
{%- for tc in msg.tool_calls %}
→ tool_call id={{ tc.id[-6:] }} name={{ tc.fn_info.name }}
→ params: {{ tc.fn_info.arguments }}
{%- for result_msg in messages %}
{%- if result_msg.role == "tool" and result_msg.tool_call_id == tc.id %}
← result id={{ tc.id[-6:] }}:
{{ result_msg.content }}
{%- endif %}
{%- endfor %}
{%- endfor %}
{%- else %}
{{ _.role("assistant") }}
{{ msg.content }}
{%- endif %}
{%- elif msg.role == "tool" %}
{# already rendered inside the assistant block above #}
{%- endif %}
{%- endfor %}vEth
02/11/2026, 7:47 AMmessages are assistant (tool_calls) + tool (result). Since tool messages are skipped in the template (rendered inside the assistant block), the final API message sent to Claude is an assistant message. Claude treats this as a prefill — it sees a completed tool call with its result already present, considers the turn done, and returns near-empty content (a few whitespace tokens → end_turn).
This worked fine with Gemini, but broke after switching to the Anthropic provider because Claude interprets trailing assistant messages as prefill.vEth
02/11/2026, 7:47 AM_.role("user") instead of embedding them in the assistant block — this naturally makes the last API message a user message, avoiding the prefill issue. This also aligns with Anthropic's native Messages API where tool_result lives inside user messages.
2. Append a synthetic user message in the application layer before calling the BAML function again — works but feels like a workaround.vEth
02/11/2026, 7:47 AM_.role("user") the right approach, or is there a better way to handle this in BAML templates?alex
02/12/2026, 7:09 AMvEth
02/12/2026, 8:15 AMvEth
02/12/2026, 8:16 AMsekai.ni.itamio
02/12/2026, 11:16 AMuser role.
Here’s what the tool response looks like from my BAML integration with langchain’s create_agent:
<|im_start|>system
You are a helpful assistant.
# Tools
You may call one or more functions to assist with the user query.
Answer in JSON-like string (no double quotes in keys) using this schema. Call a tool only when explicitly needed for the task; otherwise, respond with text-only output:
string or {
// If you want to use a tool, specify the tool and its arguments here.
selected_tool: {
// Add two integers.
name: "add",
arguments: {
// First integer to add
a: int,
// Second integer to add
// Default: 5
b: int,
},
} or {
// Multiply two integers.
name: "multiply",
arguments: {
// First integer to multiply
x: int,
// Second integer to multiply
// Default: 5
y: int,
},
},
}<|im_end|>
<|im_start|>user
1+2=?<|im_end|>
<|im_start|>assistant
<tool_call>
{"name": "add", "arguments": {"a": 1, "b": 2}}
</tool_call><|im_end|>
<|im_start|>user
<tool_response>
3
</tool_response><|im_end|>
<|im_start|>assistant
It might be worth checking:
- What chat template Claude actually uses (if it even exposes one)
- Whether there’s any way to inspect it via a self-hosted setup like vLLM
- Or alternatively, looking at LangChain’s ChatAnthropic implementation to see how tool results are handled internally
https://cdn.discordapp.com/attachments/1471049818108592158/1471465060722278561/image.png?ex=698f0820&is=698db6a0&hm=f63b370fb738011feca2a25693bf34972e9067c4803a752d812b7380566627a0&vEth
02/12/2026, 11:43 AMsekai.ni.itamio
02/12/2026, 1:05 PM_.role("tool") instead.
client<llm> ChatBaml {
...
allowed_roles ["system", "user", "assistant", "tool"]
...
}
{{ _.role( "tool" ) }}
3
chat template render this:
<|im_start|>user
<tool_response>
3
</tool_response><|im_end|>