OpenClaw Model Manager: a GUI so I stop hand-editing model routing JSON

Posted
July 11, 2026
By
Jacob Lloyd — written with AI assistance, post-project
Read time
7 min read

In plain terms: A free desktop app that gives my home AI setup a proper control panel, replacing risky hand-editing of a settings file. It shows which AI "brain" each helper is using and lets me switch them with a click, refusing to save anything that would break the system. It exists because one typo once silently knocked an assistant offline.

I once typo'd a model id in a JSON file and the agent using it just went quiet. No error, no crash. It sat there talking to a model that didn't exist while I hunted for a much more interesting bug. This app is my never-again.

TL;DR

  • What it is: a GTK4/libadwaita desktop app that edits openclaw.json, the config deciding which LLM every agent, subagent, and cron job in my OpenClaw stack talks to.
  • What it costs: free, MIT licensed, source in the zip below.
  • What you need: Linux with GTK4 + libadwaita + PyGObject (one package-manager line), Python 3.11+, and an OpenClaw install — or the bundled example config to start from scratch.
  • What you end up with: a one-screen dashboard of every agent's model, saves that can't corrupt the file or assign a dead model, and a point-and-click switch that routes the Claude Code CLI to a free local model, cheap DeepSeek, or real Anthropic billing.

What you end up with

Screenshots first. These come from a sandboxed copy running against a dummy config in a throwaway HOME. Real app, fake data, none of my keys.

Main window of OpenClaw Model Manager showing the Model Usage inventory, a Connectivity and Routing status panel, and color-coded API/Local Defaults profiles
Main window: every agent's model on one screen, plus live connectivity status per provider.

The main window answers the question I used to answer by grepping JSON: which model is running where, and what does it cost? Every agent, the stack-wide defaults, the subagent override, cron jobs, even a sibling email agent's models. Each entry gets a color badge: Local (this PC), Remote (the second PC), or Cloud (the one that shows up on a bill).

Set Models for All Agents bulk apply panel and the Coding Helper section with a mode picker and a live warning that Cloud mode has no working key on this machine
Bulk-apply a model to every agent, or pick the Coding Helper's backend. The app warns when the mode you picked won't actually work.
Model Catalog view listing per-model cost, context length, and reasoning support, with Add LM Studio Model and Add Anthropic Model buttons
The catalog: cost, context length, and reasoning support per model, plus a discovery button that asks LM Studio what it actually has loaded.

Why this exists

OpenClaw is the multi-agent stack I run at home: several AI agents, each pointed at a model, some local and free, some cloud and billed. All the routing lives in one file, ~/.openclaw/openclaw.json: model catalog, per-agent primary/fallback assignments, provider configs, and the gateway's own auth token. Hand-editing it kept going wrong in two ways.

First, a bad model id fails silently. A stale default once pointed an agent at vllm/google/gemma-4-31b-qat, a model the provider never served. A malformed save is worse: one bad edit and the whole gateway crash-loops.

Second, switching the Claude Code CLI between backends is invisible env-var surgery. OpenClaw spawns Claude Code for coding tasks, and where those requests go — and who gets billed — is set by ANTHROPIC_* environment variables on the gateway's systemd unit. Flipping between free local, cheap DeepSeek, and real Anthropic meant hand-writing a systemd drop-in each time. Exactly the kind of thing I get wrong at 11pm.

The save path

The part I need to trust is what happens when you click Save. This is it, in order:

Two steps carry the weight. Validation refuses any model id its provider doesn't advertise, which makes the typo bug from the intro structurally impossible instead of something I have to remember to check. And the atomic write exists because openclaw.json holds the gateway's auth token and must stay chmod 600: the temp file gets the original's permissions before any content is written, then it's fsync'd and renamed over the original. Crash mid-save and the old file is untouched.

A bad save is one rename away from undone, and you keep five generations of history instead of one.

The Coding Helper drop-in

Rerouting the Claude Code CLI is a separate flow because it never touches openclaw.json. It writes a systemd drop-in that overrides the gateway's environment before the gateway spawns Claude Code.

Four modes, one dropdown: LM Studio (free, local), DeepSeek via its Anthropic-compatible endpoint (cheap), Anthropic Cloud (the default: no drop-in at all, nothing overridden), or Disabled, which points the CLI at http://127.0.0.1:9/blocked so every call fails fast instead of hanging. There's also a one-click live test that fires a real Anthropic-shaped ping at LM Studio or DeepSeek and reports which model actually answered. You're not trusting a config screen, you're trusting a request that just happened.

Keeping the API key out of the unit file

DeepSeek mode needs an API key, and a systemd unit file is a lousy place for one: anything written there directly sits in plain text on disk and in systemctl show output. So the drop-in never contains the key. It writes this instead:

ExecStart=/usr/bin/bash -c 'export ANTHROPIC_API_KEY="$$DEEPSEEK_API_KEY"; exec <gateway cmd>'

The trick is $$. In a unit file, $$ escapes a literal $. Without it, systemd expands the variable itself while building the command line, and the real key lands in the process's argv where ps can read it. With the double dollar, bash receives a literal $DEEPSEEK_API_KEY and expands it at runtime from the gateway's EnvironmentFile (~/.openclaw/gateway.systemd.env, chmod 600). The secret never appears in the unit file, in systemctl show, or in a process listing — only inside bash's own environment, for that one process. The zip's examples/ folder documents the pattern with CHANGE_ME placeholders.

Setup

From the README shipped in the zip:

# 1. Install GTK4 + libadwaita + PyGObject from your distro's packages
#    (one dnf/apt line — see README-SETUP.md for the exact package names)

# 2. Install the app and its launcher
install -Dm755 openclaw-model-manager ~/.local/bin/openclaw-model-manager
# .desktop file goes to ~/.local/share/applications/

# 3. If you don't already have an OpenClaw config:
cp examples/openclaw.json.example ~/.openclaw/openclaw.json
chmod 600 ~/.openclaw/openclaw.json
# then replace every CHANGE_ME placeholder inside it

# 4. Optional — API keys, both chmod 600, examples provided:
#    ~/.openclaw/.env
#    ~/.openclaw/gateway.systemd.env

No pip installs beyond PyGObject; everything else is Python 3.11+ stdlib, tomllib included. The whole thing is one executable script (164KB, 3,570 lines), a .desktop launcher, a README, an MIT license, and four example configs. Zips to 49KB.

Gotchas

  • Cloud mode means "no drop-in," not "definitely my Anthropic account." Nothing gets overridden, so whatever was already configured on the unit keeps running. The app warns when the mode you picked has no working key, but check anyway.
  • The drop-in shadows your real Anthropic key, by design. Local mode sets ANTHROPIC_API_KEY=lm-studio for everything the gateway spawns. On my machine that silently rerouted my interactive claude CLI aliases to LM Studio — Claude Code kept "working," the answers just came from a local model. If Claude starts sounding off, run systemctl --user cat openclaw-gateway.service and look for a drop-in before blaming the model.
  • After openclaw update changes the gateway's ExecStart, re-select DeepSeek mode once to re-wrap the new command. The app reads the canonical ExecStart from the unit fragment and detects its own wrapper (it keys on index.js plus the absence of $$DEEPSEEK_API_KEY), so it never double-wraps — but it won't auto-fix a command it hasn't been told to look at again.
  • reasoning: true is the safe default for local models. A model that emits reasoning_content while configured reasoning: false reads as stalled to the gateway: about 390 seconds of silence, then a kill. It's harmless left on for non-thinking models, so the add-model dialog defaults it on. This one cost real debugging time.
  • A non-empty plugins.allow is a strict allowlist. enabled: true on a plugin entry does nothing unless its id is also in allow. The app reads this correctly and deliberately never writes plugins.* at all, so if a plugin misbehaves, this isn't the tool that'll tell you why.
  • The gateway rewrites openclaw.json on its own (identity reconcile, openclaw update). The app tracks the file's mtime and warns before overriding an external edit, and the pre-save backup keeps whatever was actually on disk.
  • Model ids are provider/raw-id. A bare id with no slash resolves through the local LM Studio endpoint. Not an error, just a convention worth knowing before you go hunting for a provider field that isn't there.

Downloads

Free for personal use. If it saves you an afternoon, the coffee button's nearby.


← More Tools & Downloads