#when i use a local model like qwen2.5:14B i get responses in curly brackets, why?
1 messages · Page 1 of 1 (latest)
That “everything in { ... }” symptom is almost always OpenClaw showing raw tool-call / OpenAI payload JSON as plain text because the local provider is in a mode where tool calling isn’t working properly.
Most common cause (especially with Ollama + Qwen): you’re pointing OpenClaw at Ollama’s OpenAI-compatible endpoint:
http://<host>:11434/v1❌ (this is the problematic one)
Instead, use the native Ollama API base URL:
http://<host>:11434âś… (no/v1)
The Ollama docs call this out explicitly: using /v1 can “break tool calling and models may output raw tool JSON as plain text.”
https://docs.openclaw.ai/providers/ollama
If you paste:
openclaw --version- how you configured the model/provider (just the
models.providers.ollamabit, or tell me if you used the Dashboard)
…I can point to the exact line to change.
2026.2.16, no i configures manually the the models and i have litellm inbetween openclaw and the ollama server
here is the config
"agents": {
"defaults": {
"model": {
"primary": "qwen2.5:14b"
},
"models": {
"litellm/ollama/qwen2.5:14b": {
"alias": "qwen2.5:14b",
"streaming": true
},
Yeah — with LiteLLM in the middle, “curly bracket replies” almost always means OpenClaw is expecting tool calling (OpenAI-style tool_calls), but somewhere in the chain it degrades and the model just prints the tool JSON as normal text.
In your setup the risky bit is usually:
- OpenClaw → LiteLLM is always OpenAI-compatible (
/v1/chat/completions) - If LiteLLM → Ollama is also using Ollama’s OpenAI-compatible
/v1(or otherwise not translating tool calls), tool calling gets flaky and you see{ ... }dumped into chat
OpenClaw’s Ollama docs call this exact failure mode out for /v1:
https://docs.openclaw.ai/providers/ollama (warning box about /v1 + raw tool JSON)
Quick things to try
- Disable streaming for that model (OpenAI-compat + tools + streaming is the common combo that breaks):
"agents": {
"defaults": {
"models": {
"litellm/ollama/qwen2.5:14b": {
"alias": "qwen2.5:14b",
"params": { "streaming": false }
}
}
}
}
(Your current "streaming": true is basically the worst-case if tool calling is marginal.)
-
Make sure your LiteLLM → Ollama backend uses the native Ollama base URL (
http://host:11434), not.../v1. -
If you want a “works today” baseline: bypass LiteLLM for local Ollama and use OpenClaw’s native Ollama provider directly (that path supports streaming + tool calling reliably).
Two small asks so I can be precise
- Paste one example of what you get in curly braces (first ~10 lines is enough). Is it a
{"tool": ...}/{"function": ...}shape?
- Paste your
models.providers.litellmblock (especiallybaseUrl), plus the relevant bit of your LiteLLMconfig.yamlfor the Ollama model.
"models": {
"providers": {
"litellm": {
"baseUrl": "http://litellm.vlan/v1",
{
tools: [...]22 Items
messages: [
0: {
role: "system"
content: "You are a personal assistant running inside OpenClaw.
Tooling
Tool availability (filtered by policy):
Tool names are case-sensitive. Call tools exactly as listed.
- read: Read file contents
- write: Create or overwrite files
- edit: Make precise edits to files
- exec: Run shell commands (pty available for TTY-required CLIs)
- process: Manage background exec sessions
- web_search: Search the web (Brave API)
- web_fetch: Fetch and extract readable content from a URL
- browser: Control web browser
That snippet basically confirms it: OpenClaw is sending a proper OpenAI “tools” request (tools: [...], plus the big system prompt describing tools). If the proxy chain can’t do real tool-calls end-to-end, Qwen will often “roleplay” the tool call and print JSON in { ... } as normal text.
Two concrete fixes in what you pasted:
1) Your LiteLLM baseUrl should NOT include /v1
OpenClaw’s LiteLLM provider expects the root URL and will hit /v1/chat/completions itself.
So change:
baseUrl: "http://litellm.vlan/v1"
to:
baseUrl: "http://litellm.vlan"
(Docs example does it this way: https://docs.openclaw.ai/providers/litellm)
2) Your per-model streaming flag is in the wrong place (and streaming often makes this worse)
In OpenClaw config, streaming control is via params, e.g.:
"agents": {
"defaults": {
"models": {
"litellm/ollama/qwen2.5:14b": {
"alias": "qwen2.5:14b",
"params": { "streaming": false }
}
}
}
}
OpenClaw’s Ollama docs explicitly mention that in OpenAI-compat mode you may need params: { streaming: false } to avoid tool-call weirdness: https://docs.openclaw.ai/providers/ollama
If after those two changes you still get {...}: it’s almost certainly LiteLLM → Ollama not handling tool calling properly (often because it’s using Ollama’s OpenAI /v1 backend). In that case, the “it just works” path is: skip LiteLLM for the local model and use OpenClaw’s native Ollama provider directly (native /api/chat supports tools reliably).
If you paste the relevant LiteLLM config.yaml entry for this model (the model_list block), I can tell you if it’s routing to Ollama via /v1 (the usual culprit).
so now i have
"models": {
"providers": {
"litellm": {
"baseUrl": "http://litellm.vlan",
and disabled streaming
"models": {
"litellm/ollama/qwen2.5:14b": {
"alias": "qwen2.5:14b",
"streaming": false
},
but i still get response in brackets
{"type": "message", "content": "Hello! I can assist with a wide range of tasks, from answering questions and providing information to helping you manage your schedule and more. How would you like to get started today?"}
i can't imagine the issue is litellm because when i use online models all wroks fine
That specific shape:
{"type":"message","content":"..."}
is a big clue: that’s Responses-API style “message object” getting stringified, not a normal Chat Completions assistant message.
So I’d fix two things in your OpenClaw config:
1) Force LiteLLM provider to use chat completions
Make sure you explicitly set the provider API mode:
models: {
providers: {
litellm: {
baseUrl: "http://litellm.vlan", // no /v1
api: "openai-completions", // <-- important
apiKey: "${LITELLM_API_KEY}",
}
}
}
(That api field exists specifically to choose between openai-completions vs openai-responses etc.)
2) Your “disable streaming” override needs to be under params
This is the OpenClaw format:
agents: {
defaults: {
model: { primary: "litellm/ollama/qwen2.5:14b" },
models: {
"litellm/ollama/qwen2.5:14b": {
alias: "qwen2.5:14b",
params: { streaming: false },
},
},
},
}
If you leave it as "streaming": false at the top-level of the model entry, it’s likely just ignored.
3) Restart the gateway after config changes
Otherwise it’s easy to be testing old config.
If after setting api: "openai-completions" you still see the JSON object, paste your LiteLLM config.yaml entry for this model (the model_list item). I’m looking for anything that enables JSON mode / response_format, or routes Ollama via an OpenAI-compat endpoint in a way that makes LiteLLM return non-chat output.
did all the updates, restarted the server but i still get the responses in curly braces
"models": {
"providers": {
"litellm": {
"baseUrl": "http://litellm.vlan",
"api": "openai-completions",
"agents": {
"defaults": {
"model": {
"primary": "qwen2.5:14b"
},
"models": {
"litellm/ollama/qwen2.5:14b": {
"alias": "qwen2.5:14b",
"params": { "streaming": false }
},
That { "type": "message", "content": ... } output isn’t OpenClaw “being weird” — it’s LiteLLM’s Ollama tool-calling fallback.
OpenClaw sends a big tools: [...] block on basically every request. For online models, LiteLLM + the upstream supports OpenAI tool calling, so you get normal text back.
For Ollama/Qwen, LiteLLM often decides supports_function_calling = false and then (per LiteLLM docs) falls back to “json mode tool calls” → which is exactly “assistant replies in curly braces”. See: https://docs.litellm.ai/docs/providers/ollama (tool calling section)
Fix options
Option A (recommended): don’t put Ollama behind LiteLLM for OpenClaw tool-calling
Use OpenClaw’s native ollama provider directly (native /api/chat), because that path is designed to do streaming + tools correctly:
https://docs.openclaw.ai/providers/ollama
Option B: make LiteLLM use ollama_chat + mark the model as tool-capable
In LiteLLM config.yaml, route the model as ollama_chat/... (LiteLLM recommends this), and set model info:
model_list:
- model_name: ollama_chat/qwen2.5:14b
litellm_params:
model: ollama_chat/qwen2.5:14b
api_base: http://<your-ollama-host>:11434
model_info:
supports_function_calling: true
Then in OpenClaw, point at:
litellm/ollama_chat/qwen2.5:14b
Option C: if you just want “chat”, disable tools for that local model in OpenClaw
This stops OpenClaw from sending tools: [...], which prevents LiteLLM from going into that JSON/tool mode:
{
tools: {
byProvider: {
"litellm/ollama/qwen2.5:14b": { deny: ["*"] },
},
},
agents: {
defaults: {
model: { primary: "litellm/ollama/qwen2.5:14b" },
},
},
}
If you paste your LiteLLM config.yaml entry for the Qwen model (the model_list item), I can tell you which of B/C is the cleanest in your exact setup.
i applied option B but still getting back responses in curly braces
- model_name: ollama/qwen2.5:14b
litellm_params:
model: ollama/qwen2.5:14b
api_base: "http://ollama.vlan:11434"
model_info:
supports_function_calling: true
You didn’t actually apply the critical part of option B yet: you’re still using the ollama/... prefix, which (in LiteLLM) typically routes to Ollama’s /api/generate path. That path doesn’t do proper chat+tools, so LiteLLM falls back to “JSON mode” and you get { ... } objects back.
What you want for tool calling is ollama_chat/... (routes to /api/chat).
Fix your LiteLLM config.yaml like this
You can keep the external name ollama/qwen2.5:14b if you want, but change the internal model to ollama_chat/...:
- model_name: ollama/qwen2.5:14b # what OpenClaw sends to LiteLLM
litellm_params:
model: ollama_chat/qwen2.5:14b # IMPORTANT: forces /api/chat
api_base: "http://ollama.vlan:11434"
model_info:
supports_function_calling: true
Restart LiteLLM after changing this (not just OpenClaw).
If it STILL returns curly-brace objects after that
Then the model simply isn’t behaving with tools in this setup. Two pragmatic paths:
- Bypass LiteLLM for local Ollama and use OpenClaw’s native
ollamaprovider (this is the most reliable for tools): https://docs.openclaw.ai/providers/ollama - Or keep LiteLLM but disable tools for that specific model in OpenClaw (so it stops sending
tools: [...], which triggers the JSON/tool fallback).
If you paste the first ~20 lines of your LiteLLM startup log for that model (it usually prints what it loaded / routing), I can tell you if it’s actually hitting /api/chat now.
how to configure ollama as provider ?
To use Ollama directly as an OpenClaw model provider (no LiteLLM in between), follow the Ollama provider docs here: https://docs.openclaw.ai/providers/ollama
1) Make sure you use the native Ollama URL (no /v1)
Use:
http://ollama.vlan:11434âś…
Not:http://ollama.vlan:11434/v1❌ (often breaks tool calling / causes JSON-ish outputs)
Quick check:
curl http://ollama.vlan:11434/api/tags
2) Add an ollama provider in ~/.openclaw/openclaw.json (JSON5)
Since you’re using a remote Ollama host, you’ll want explicit config:
{
models: {
providers: {
ollama: {
baseUrl: "http://ollama.vlan:11434", // no /v1
apiKey: "ollama-local", // any string is fine
api: "ollama", // force native /api/chat behavior
// Explicit config disables auto-discovery, so define the model(s):
models: [
{
id: "qwen2.5:14b",
name: "Qwen2.5 14B",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 8192,
maxTokens: 8192 * 10,
},
],
},
},
},
agents: {
defaults: {
model: { primary: "ollama/qwen2.5:14b" },
},
},
}
3) Restart OpenClaw gateway after changing config
That’s required for provider/model catalog changes to fully apply.
If you still see curly-brace JSON after going direct-to-Ollama, paste:
- the first ~10 lines of one of those “{ ... }” replies, and
- whether you want tools enabled for this local model or you just want “normal chat” (tools are what usually trigger the JSON fallback behavior).
i have this issue configuring ollama as a provider
Problem:
- models.providers.ollama.api: Invalid input
config in place:
"ollama": {
"baseUrl": "http://ollama.vlan:11434",
"apiKey": "ollama-local",
"api": "ollama",
"models": [
{
"id": "qwen2.5:14b",
"name": "Qwen2.5 14B",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 8192
}
]
}
That validation error usually means your OpenClaw build doesn’t accept "ollama" as a value for models.providers.<provider>.api (the docs are a bit ahead of some releases).
Two things to do:
1) Remove the api field entirely (recommended)
For the built-in ollama provider, OpenClaw will use the native Ollama API when your baseUrl is not /v1 anyway.
models: {
providers: {
ollama: {
baseUrl: "http://ollama.vlan:11434",
apiKey: "ollama-local",
models: [
{
id: "qwen2.5:14b",
name: "Qwen2.5 14B",
input: ["text"],
reasoning: false,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 8192,
maxTokens: 8192 * 10,
},
],
},
},
},
agents: {
defaults: {
model: { primary: "ollama/qwen2.5:14b" },
},
},
(Also: set the default model to ollama/qwen2.5:14b — otherwise it won’t pick that provider/model.)
Docs reference: https://docs.openclaw.ai/providers/ollama
2) If you want, paste the full validation error line
Zod usually prints what it expected for api (the allowed enum values). That tells us if you need:
- a version bump (where
api: "ollama"is supported), or - to just omit it as above.
If you paste the exact error output + openclaw --version, I can confirm which case you’re in.
now i get this error
Agent failed before reply: Model context window too small (8192 tokens). Minimum is 16000.
Logs: openclaw logs --follow
That error is OpenClaw refusing to even start the run because your model is configured as 8192 context, but the OpenClaw agent needs ≥ 16000 (system prompt + tool schemas are big).
Fix (OpenClaw side)
In your models.providers.ollama.models[] entry, set contextWindow to at least 16000 (people usually use 16384 or 32768) and add a sane maxTokens, then restart the gateway:
models: {
providers: {
ollama: {
baseUrl: "http://ollama.vlan:11434",
apiKey: "ollama-local",
models: [
{
id: "qwen2.5:14b",
name: "Qwen2.5 14B",
input: ["text"],
reasoning: false,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 32768, // >= 16000
maxTokens: 4096,
},
],
},
},
},
Also important (Ollama side)
Make sure Ollama actually runs the model with a big enough context. With Ollama you typically do this by creating a custom model with PARAMETER num_ctx (Modelfile), e.g. 16384 or 32768:
https://docs.ollama.com/modelfile (see PARAMETER num_ctx)
If you don’t raise it on the Ollama side, you can set contextWindow higher in OpenClaw but you’ll still hit truncation / weird behavior later.
Docs: https://docs.openclaw.ai/gateway/local-models (contextWindow guidance)
ok i switched over to direct ollama connection but still getting back responses un culry braces
my config:
"ollama": {
"baseUrl": "http://ollama.vlan:11434",
"apiKey": "ollama-local",
"models": [
{
"id": "qwen2.5-14b-1m-heretic",
"name": "richardyoung/qwen2.5-14b-1m-heretic",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 1000000
}
]
}
"agents": {
"defaults": {
"model": {
"primary": "qwen2.5-14b-1m-heretic"
},
Two separate issues in that config:
1) Your primary model ref is missing the provider prefix
OpenClaw expects provider/model, not a bare Ollama tag.
Change:
agents: { defaults: { model: { primary: "qwen2.5-14b-1m-heretic" } } }
to:
agents: { defaults: { model: { primary: "ollama/qwen2.5-14b-1m-heretic" } } }
(Or add an alias in agents.defaults.models, but the simplest is to just use the full ollama/... ref.)
2) Curly-brace replies mean the model is dumping tool/JSON instead of “normal chat”
OpenClaw sends tools: [...] on requests. If the model doesn’t really support tool calling, it often responds by printing JSON blobs (what you’re seeing).
Quick test/fix: disable tools for just this model:
{
tools: {
byProvider: {
"ollama/qwen2.5-14b-1m-heretic": { deny: ["*"] }
}
}
}
Docs on tool allow/deny: https://docs.openclaw.ai/tools
If disabling tools makes it reply normally, then that model simply isn’t tool-capable in practice. To keep tools, you’ll need a model that handles tool calling well under Ollama (or keep hosted models for tool-heavy work).
Also: contextWindow: 1000000 is very likely not real for that model (and can cause other weirdness). If you know the actual ctx (e.g. 16384/32768), set it to that.
If you paste one full example of the { ... } response you get after switching primary to ollama/..., I can confirm whether it’s tool-call JSON vs some “JSON mode” template from the model.
still json response
{"function": "sessions_send", "parameters": {"sessionKey": "2593", "label": "capabilities", "agentId": "default", "message": "I'm here to help with a wide range of tasks! From answering questions and providing information, to assisting with creative projects or troubleshooting issues. How can I assist you today?", "timeoutSeconds": 10}}
with this config
"tools": {
"byProvider": {
"ollama/qwen2.5-14b-1m-heretic": { "deny": ["*"] }
},
That JSON is a tool call (sessions_send). So either:
- your deny rule isn’t matching the model ref actually in use, or
- that Ollama model is trained/prompted to always emit function-call JSON, even when tools aren’t provided.
Step 1: make sure the session is actually using the exact ref you’re denying
Your deny rule key is:
"ollama/qwen2.5-14b-1m-heretic"
So your default model must be exactly:
agents: { defaults: { model: { primary: "ollama/qwen2.5-14b-1m-heretic" } } }
…and then start a fresh session in chat (/reset or /new ...), because an existing session can keep its previous model/tool context. Docs: https://docs.openclaw.ai/concepts/session
Step 2 (debug): deny tools for all Ollama models
This removes any chance of a model-ref mismatch:
tools: {
byProvider: {
ollama: { deny: ["*"] },
},
},
Restart the gateway, then /reset in the chat and try again.
If it STILL outputs tool JSON after that
Then it’s the model/template. Some “function calling / agent” fine-tunes will still answer in {...} even with zero tools.