[GH-ISSUE #14360] Kimi K2.5: tool calls leak as raw text — template missing ToolCalls block #55844

Closed
opened 2026-04-29 09:48:03 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @timetwisterfounder-tech on GitHub (Feb 22, 2026).
Original GitHub issue: https://github.com/ollama/ollama/issues/14360

The Kimi K2.5 chat template shipped in the official model is missing a {{ .ToolCalls }} block, which means Ollama's generic tool call parser has no reference format to learn from. As a result, when Kimi K2.5 emits structured tool calls using its native tokens (<|tool_calls_section_begin|>, <|tool_call_begin|>, etc.), they pass through as raw assistant text instead of being parsed into tool_calls objects in the API response.

Reproduce

ollama pull kimi-k2.5
ollama show kimi-k2.5 --template  # note: no ToolCalls block

Then call the /api/chat endpoint with tools defined — the model generates tool call tokens in plain text rather than returning structured message.tool_calls.

Fix

Add a ToolCalls block to the assistant message section of the template, and add <|tool_calls_section_end|> as a stop token:

 {{- if .Content }}
 {{ .Content }}
+{{- end }}
+{{- if .ToolCalls }}<|tool_calls_section_begin|>
+{{- range .ToolCalls }}<|tool_call_begin|>functions.{{ .Function.Name }}:0<|tool_call_argument_begin|>{{ .Function.Arguments }}<|tool_call_end|>
+{{- end }}<|tool_calls_section_end|>
 {{- end }}<|im_end|>
 PARAMETER stop <|im_system|>
+PARAMETER stop <|tool_calls_section_end|>

This matches Kimi K2.5's native tool call token format (from the model's original tokenizer_config.json chat template) and gives Ollama's parser the pattern it needs to extract structured tool calls.

Full working template

For reference, here's the complete working Modelfile with both thinking support and tool calling:

FROM kimi-k2.5:latest

TEMPLATE """
{{- /* Kimi K2.5 chat template for Ollama with thinking support and tool calling */ -}}
{{- if .Tools }}<|im_system|>tool_declare<|im_middle|>{{ .Tools }}<|im_end|>
{{- end }}
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 }}
{{- if and (eq $i 0) (ne .Role "system") }}<|im_system|>system<|im_middle|>You are a helpful assistant.<|im_end|>
{{- end }}
{{- if eq .Role "system" }}<|im_system|>system<|im_middle|>{{ .Content }}<|im_end|>
{{- else if eq .Role "user" }}<|im_user|>user<|im_middle|>{{ .Content }}<|im_end|>
{{- else if eq .Role "assistant" }}<|im_assistant|>assistant<|im_middle|>
{{- if and $last .Thinking }}
<think>{{ .Thinking }}</think>
{{- end }}
{{- if .Content }}
{{ .Content }}
{{- end }}
{{- if .ToolCalls }}<|tool_calls_section_begin|>
{{- range .ToolCalls }}<|tool_call_begin|>functions.{{ .Function.Name }}:0<|tool_call_argument_begin|>{{ .Function.Arguments }}<|tool_call_end|>
{{- end }}<|tool_calls_section_end|>
{{- end }}<|im_end|>
{{- else if eq .Role "tool" }}<|im_system|>tool<|im_middle|>{{ .Content }}<|im_end|>
{{- end }}
{{- if and (ne .Role "assistant") $last }}<|im_assistant|>assistant<|im_middle|>
<think>
{{- end }}
{{- end }}"""

PARAMETER stop <|im_end|>
PARAMETER stop <|im_user|>
PARAMETER stop <|im_system|>
PARAMETER stop <|tool_calls_section_end|>
PARAMETER temperature 1
PARAMETER top_p 0.95
PARAMETER min_p 0.01
PARAMETER num_ctx 65536

Environment

  • Ollama 0.16.2 (macOS, Apple Silicon)
  • Kimi K2.5 imported from unsloth/Kimi-K2.5-GGUF (UD-TQ1_0), but the template issue also applies to the official registry version
Originally created by @timetwisterfounder-tech on GitHub (Feb 22, 2026). Original GitHub issue: https://github.com/ollama/ollama/issues/14360 The Kimi K2.5 chat template shipped in the official model is missing a `{{ .ToolCalls }}` block, which means Ollama's generic tool call parser has no reference format to learn from. As a result, when Kimi K2.5 emits structured tool calls using its native tokens (`<|tool_calls_section_begin|>`, `<|tool_call_begin|>`, etc.), they pass through as raw assistant text instead of being parsed into `tool_calls` objects in the API response. ## Reproduce ```bash ollama pull kimi-k2.5 ollama show kimi-k2.5 --template # note: no ToolCalls block ``` Then call the `/api/chat` endpoint with `tools` defined — the model generates tool call tokens in plain text rather than returning structured `message.tool_calls`. ## Fix Add a `ToolCalls` block to the assistant message section of the template, and add `<|tool_calls_section_end|>` as a stop token: ```diff {{- if .Content }} {{ .Content }} +{{- end }} +{{- if .ToolCalls }}<|tool_calls_section_begin|> +{{- range .ToolCalls }}<|tool_call_begin|>functions.{{ .Function.Name }}:0<|tool_call_argument_begin|>{{ .Function.Arguments }}<|tool_call_end|> +{{- end }}<|tool_calls_section_end|> {{- end }}<|im_end|> ``` ```diff PARAMETER stop <|im_system|> +PARAMETER stop <|tool_calls_section_end|> ``` This matches Kimi K2.5's native tool call token format (from the model's original `tokenizer_config.json` chat template) and gives Ollama's parser the pattern it needs to extract structured tool calls. ## Full working template For reference, here's the complete working Modelfile with both thinking support and tool calling: ``` FROM kimi-k2.5:latest TEMPLATE """ {{- /* Kimi K2.5 chat template for Ollama with thinking support and tool calling */ -}} {{- if .Tools }}<|im_system|>tool_declare<|im_middle|>{{ .Tools }}<|im_end|> {{- end }} {{- range $i, $_ := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 }} {{- if and (eq $i 0) (ne .Role "system") }}<|im_system|>system<|im_middle|>You are a helpful assistant.<|im_end|> {{- end }} {{- if eq .Role "system" }}<|im_system|>system<|im_middle|>{{ .Content }}<|im_end|> {{- else if eq .Role "user" }}<|im_user|>user<|im_middle|>{{ .Content }}<|im_end|> {{- else if eq .Role "assistant" }}<|im_assistant|>assistant<|im_middle|> {{- if and $last .Thinking }} <think>{{ .Thinking }}</think> {{- end }} {{- if .Content }} {{ .Content }} {{- end }} {{- if .ToolCalls }}<|tool_calls_section_begin|> {{- range .ToolCalls }}<|tool_call_begin|>functions.{{ .Function.Name }}:0<|tool_call_argument_begin|>{{ .Function.Arguments }}<|tool_call_end|> {{- end }}<|tool_calls_section_end|> {{- end }}<|im_end|> {{- else if eq .Role "tool" }}<|im_system|>tool<|im_middle|>{{ .Content }}<|im_end|> {{- end }} {{- if and (ne .Role "assistant") $last }}<|im_assistant|>assistant<|im_middle|> <think> {{- end }} {{- end }}""" PARAMETER stop <|im_end|> PARAMETER stop <|im_user|> PARAMETER stop <|im_system|> PARAMETER stop <|tool_calls_section_end|> PARAMETER temperature 1 PARAMETER top_p 0.95 PARAMETER min_p 0.01 PARAMETER num_ctx 65536 ``` ## Environment - Ollama 0.16.2 (macOS, Apple Silicon) - Kimi K2.5 imported from unsloth/Kimi-K2.5-GGUF (UD-TQ1_0), but the template issue also applies to the official registry version
Author
Owner

@rick-github commented on GitHub (Feb 22, 2026):

$ ollama pull kimi-k2.5
pulling manifest 
Error: pull model manifest: file does not exist
<!-- gh-comment-id:3940518146 --> @rick-github commented on GitHub (Feb 22, 2026): ```console $ ollama pull kimi-k2.5 pulling manifest Error: pull model manifest: file does not exist ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/ollama#55844