#when i use a local model like qwen2.5:14B i get responses in curly brackets, why?

1 messages · Page 1 of 1 (latest)

empty quest
#

{"content": "Hello! I'm here to assist with a wide range of tasks, from answering questions and providing information to helping with creative projects or problem-solving. How would you like me to assist you today?", "message_id": 2518}

slender kelpBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

naive daggerBOT
#

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:

  1. openclaw --version
  2. how you configured the model/provider (just the models.providers.ollama bit, or tell me if you used the Dashboard)
#

…I can point to the exact line to change.

empty quest
#

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
},

naive daggerBOT
#

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

  1. 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.)

  1. Make sure your LiteLLM → Ollama backend uses the native Ollama base URL (http://host:11434), not .../v1.

  2. 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.litellm block (especially baseUrl), plus the relevant bit of your LiteLLM config.yaml for the Ollama model.
empty quest
#

"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
naive daggerBOT
#

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).

empty quest
#

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

naive daggerBOT
#

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.

empty quest
#

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 }
},

naive daggerBOT
#

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.

empty quest
#

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
naive daggerBOT
#

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:

  1. Bypass LiteLLM for local Ollama and use OpenClaw’s native ollama provider (this is the most reliable for tools): https://docs.openclaw.ai/providers/ollama
  2. 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.

empty quest
#

how to configure ollama as provider ?

naive daggerBOT
#

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).
empty quest
#

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
      }
    ]
  }
naive daggerBOT
#

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.

empty quest
#

now i get this error
Agent failed before reply: Model context window too small (8192 tokens). Minimum is 16000.
Logs: openclaw logs --follow

naive daggerBOT
#

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)

empty quest
#

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"
},

naive daggerBOT
#

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.

empty quest
#

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": ["*"] }
},

naive daggerBOT
#

That JSON is a tool call (sessions_send). So either:

  1. your deny rule isn’t matching the model ref actually in use, or
  2. 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.