John O'Reilly
07/01/2026, 8:35 PMLLModel(
provider = LLMProvider.Ollama,
id = "gemma4:12b-mlx",
capabilities = listOf(
LLMCapability.Temperature,
LLMCapability.Schema.JSON.Standard,
LLMCapability.Tools
),
contextLength = 8192,
)John O'Reilly
07/03/2026, 8:20 PMAgentMain shows GetEmissionsTool is called correctly and returns "Emissions for DEU in 2025: 100 MtCO2e", yet the model answers things like "The tool found no emission data for Germany in 2025" (and often calls the tool twice). The LLM never actually "sees" the tool result.
#### Root cause: a Koog 1.0.0 Ollama client bug
I captured the exact HTTP request Koog sends to Ollama (via a logging proxy). On the second turn Koog sends:
json
{"role":"assistant", "content":"", "thinking":"\n", "tool_calls":[ ...GetEmissionsTool... ]},
{"role":"user", "content":""}, ← spurious empty user message
{"role":"tool", "content":"Emissions for DEU in 2025: 100 MtCO2e"}
That empty user message injected between the tool call and the tool result breaks the Gemma chat template. I verified by replaying the conversation directly against `http://localhost:11434/api/chat`:
• Without the empty user message → the model answers correctly: "The emissions for Germany in 2025 are estimated at 100 MtCO2e."
• With the empty user message → the model's thinking literally says "I haven't received the tool result yet" and it re-calls the tool or claims no data exists.
The bug is in Koog 1.0.0's prompt-executor-ollama-client, OllamaConverters.kt → `toOllamaChatMessages()`:
kotlin
is Message.User -> {
add(message.toOllamaTextChatMessage(model)) // ALWAYS emits a user message, even if empty
message.parts.filterIsInstance<MessagePart.Tool.Result>().forEach { part ->
add(OllamaChatMessageDTO(role = "tool", content = part.output))
}
}
Koog represents a tool result as a Message.User containing only a Tool.Result part, but the converter unconditionally emits the user-role message first — producing {"role":"user","content":""} before every tool message.John O'Reilly
07/03/2026, 8:24 PMVadim Briliantov
07/10/2026, 9:17 AM