Ollama models emit tool calls as plain text instead of ToolUseBlock when routed through the SDK/CLI (works fine against Ollama's raw API)
Environment:
SDK: claude-agent-sdk-python
SDK version: Version: 0.2.128
CLI version bundled: 2.1.220 (Claude Code)
Ollama (version: 0.32.5) + models tested: qwen3:8b, llama3.1:8b, mistral-nemo:12b
OS: Windows
Summary:
When configured to use a local Ollama model as the provider, tool-enabled prompts never produce a structured ToolUseBlock. The model instead writes the tool call as plain assistant text (e.g. {"input": {"tool": "rag_query", "query": "..."}}), or in one case names an entirely different registered tool by name in prose. This happens even on maximally explicit prompts ("call the rag_query tool now").
NOTE: The same models correctly emit proper tool calls when queried directly against Ollama's native API/tool format, outside the SDK/CLI — which suggests the issue is in how the SDK/CLI frames tool-enabled requests for local providers, not a capability limit of the models themselves.
Repro steps:
Configure ClaudeAgentOptions (or equivalent) with an Ollama-backed provider and register N tools (mine: 3 : rag_query, write_ship30_essay, generate_artifact).
Send an escalating sequence of prompts against the same session/tool config:
a bare natural-language question with no tool hint
an explicit "use the rag_query tool" instruction
single-tool-allowed + a forceful "call it now" instruction
Inspect the returned message stream for ToolUseBlock.
Expected:
At least the most forceful prompt (step 3) should produce a ToolUseBlock that the SDK/harness can execute, consistent with Anthropic-model behavior.
Actual:
No ToolUseBlock at any step, for any of the three models tested. The model output is either:
a plain-text JSON object shaped like the expected tool-call payload, or
prose naming a tool by name without invoking it.
results.citations (my execution logging) stays empty across all steps — zero real tool executions.
Isolating evidence:
Identical harness, tool definitions, and system prompt against Anthropic → ToolUseBlock produced reliably, on the first, least forceful prompt.
Same three Ollama models, queried directly against Ollama's own endpoint/tool format (bypassing the SDK/CLI) → tool calls work correctly.
A 12B model Ollama itself tags as tools-capable (mistral-nemo:12b) failed identically to two smaller 8B models, and degraded into the plain-text fakeout with less prompting pressure than the 8B models needed — inconsistent with a pure model-capability explanation, since a larger tools-tagged model performing worse points away from "model too small/undertrained" and toward something in the request framing common to all three.
Feel free to request for any additional requirements. :)
Ollama models emit tool calls as plain text instead of ToolUseBlock when routed through the SDK/CLI (works fine against Ollama's raw API)
Environment:
SDK: claude-agent-sdk-python
SDK version: Version: 0.2.128
CLI version bundled: 2.1.220 (Claude Code)
Ollama (version: 0.32.5) + models tested: qwen3:8b, llama3.1:8b, mistral-nemo:12b
OS: Windows
Summary:
When configured to use a local Ollama model as the provider, tool-enabled prompts never produce a structured ToolUseBlock. The model instead writes the tool call as plain assistant text (e.g. {"input": {"tool": "rag_query", "query": "..."}}), or in one case names an entirely different registered tool by name in prose. This happens even on maximally explicit prompts ("call the rag_query tool now").
NOTE: The same models correctly emit proper tool calls when queried directly against Ollama's native API/tool format, outside the SDK/CLI — which suggests the issue is in how the SDK/CLI frames tool-enabled requests for local providers, not a capability limit of the models themselves.
Repro steps:
Configure ClaudeAgentOptions (or equivalent) with an Ollama-backed provider and register N tools (mine: 3 : rag_query, write_ship30_essay, generate_artifact).
Send an escalating sequence of prompts against the same session/tool config:
a bare natural-language question with no tool hint
an explicit "use the rag_query tool" instruction
single-tool-allowed + a forceful "call it now" instruction
Inspect the returned message stream for ToolUseBlock.
Expected:
At least the most forceful prompt (step 3) should produce a ToolUseBlock that the SDK/harness can execute, consistent with Anthropic-model behavior.
Actual:
No ToolUseBlock at any step, for any of the three models tested. The model output is either:
a plain-text JSON object shaped like the expected tool-call payload, or
prose naming a tool by name without invoking it.
results.citations (my execution logging) stays empty across all steps — zero real tool executions.
Isolating evidence:
Identical harness, tool definitions, and system prompt against Anthropic → ToolUseBlock produced reliably, on the first, least forceful prompt.
Same three Ollama models, queried directly against Ollama's own endpoint/tool format (bypassing the SDK/CLI) → tool calls work correctly.
A 12B model Ollama itself tags as tools-capable (mistral-nemo:12b) failed identically to two smaller 8B models, and degraded into the plain-text fakeout with less prompting pressure than the 8B models needed — inconsistent with a pure model-capability explanation, since a larger tools-tagged model performing worse points away from "model too small/undertrained" and toward something in the request framing common to all three.
Feel free to request for any additional requirements. :)