Files
openclaw/docs/providers/lmstudio.md
Kaspre 44840007d4 fix(agents): scope custom provider baseUrl SSRF trust by origin (#80751)
* fix(agents): scope provider SSRF trust by origin

* fix(provider): preserve explicit private-network deny

* docs(provider): document exact-origin SSRF trust

* test(provider): cover exact-origin SSRF edges

* docs(provider): align local model private-origin guidance

* refactor(ssrf): keep policy merging in infra

* test(ssrf): cover exact-origin trust through guard

* test(ssrf): block sibling private-origin redirects

* fix(provider): keep loopback trust origin-scoped

* fix(provider): block metadata origin trust

* fix(ssrf): keep metadata rebinding blocked

* fix(ssrf): block cloud metadata origins

* fix(ssrf): block ipv6 metadata origins

* fix(ssrf): block embedded metadata origins

* test(ssrf): cover embedded link-local metadata

* test(provider): cover custom anthropic proxy classification

* test(provider): widen transport policy mock

* test(plugin-sdk): assert metadata-IP allowedOrigins entries are rejected

Plugin authors can construct an SsrFPolicy that lists any well-formed
http(s) origin in allowedOrigins. The abuse-resistance lives one layer
deeper, in resolvePinnedHostnameWithPolicy's metadata/link-local block.
Add an SDK-level smoke test asserting that contract directly:

- AWS/Alibaba IMDS IPv4 literals, GCP metadata canonical hostname,
  IPv6 ULA metadata literal, and non-metadata link-local IPv4 entries
  build a policy via ssrfPolicyFromHttpBaseUrlAllowedOrigin and are
  then rejected at resolvePinnedHostnameWithPolicy.
- DNS rebinding from a trusted private DNS origin to a metadata IP is
  rejected even when the request hostname is origin-trusted.

This would fail if the SDK helper or resolveSsrFPolicyForUrl ever
short-circuited past the metadata block.

* chore(docs): regenerate baselines after upstream rebase

upstream/main moved between rebases; the merged source state for the
PR's `src/config/schema.help.ts` change and the upstream plugin-sdk
surface changes both produce different hashes than the committed
baselines, so `config:docs:check` and `plugin-sdk:api:check` would fail.

Regenerated via `pnpm config:docs:gen` + `pnpm plugin-sdk:api:gen` on
Crabbox; both baselines verified with their respective `--check`
generators.

* test(plugin-sdk): assert SSRF blocked error class

* fix(lint): satisfy exact-origin PR lint rules

* docs: clarify custom provider origin trust

* chore(docs): refresh plugin sdk api baseline

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-15 11:00:29 +01:00

7.1 KiB

summary, read_when, title
summary read_when title
Run OpenClaw with LM Studio
You want to run OpenClaw with open source models via LM Studio
You want to set up and configure LM Studio
LM Studio

LM Studio is a friendly yet powerful app for running open-weight models on your own hardware. It lets you run llama.cpp (GGUF) or MLX models (Apple Silicon). Comes in a GUI package or headless daemon (llmster). For product and setup docs, see lmstudio.ai.

Quick start

  1. Install LM Studio (desktop) or llmster (headless), then start the local server:
curl -fsSL https://lmstudio.ai/install.sh | bash
  1. Start the server

Make sure you either start the desktop app or run the daemon using the following command:

lms daemon up
lms server start --port 1234

If you are using the app, make sure you have JIT enabled for a smooth experience. Learn more in the LM Studio JIT and TTL guide.

  1. If LM Studio authentication is enabled, set LM_API_TOKEN:
export LM_API_TOKEN="your-lm-studio-api-token"

If LM Studio authentication is disabled, you can leave the API key blank during interactive OpenClaw setup.

For LM Studio auth setup details, see LM Studio Authentication.

  1. Run onboarding and choose LM Studio:
openclaw onboard
  1. In onboarding, use the Default model prompt to pick your LM Studio model.

You can also set or change it later:

openclaw models set lmstudio/qwen/qwen3.5-9b

LM Studio model keys follow a author/model-name format (e.g. qwen/qwen3.5-9b). OpenClaw model refs prepend the provider name: lmstudio/qwen/qwen3.5-9b. You can find the exact key for a model by running curl http://localhost:1234/api/v1/models and looking at the key field.

Non-interactive onboarding

Use non-interactive onboarding when you want to script setup (CI, provisioning, remote bootstrap):

openclaw onboard \
  --non-interactive \
  --accept-risk \
  --auth-choice lmstudio

Or specify the base URL, model, and optional API key:

openclaw onboard \
  --non-interactive \
  --accept-risk \
  --auth-choice lmstudio \
  --custom-base-url http://localhost:1234/v1 \
  --lmstudio-api-key "$LM_API_TOKEN" \
  --custom-model-id qwen/qwen3.5-9b

--custom-model-id takes the model key as returned by LM Studio (e.g. qwen/qwen3.5-9b), without the lmstudio/ provider prefix.

For authenticated LM Studio servers, pass --lmstudio-api-key or set LM_API_TOKEN. For unauthenticated LM Studio servers, omit the key; OpenClaw stores a local non-secret marker.

--custom-api-key remains supported for compatibility, but --lmstudio-api-key is preferred for LM Studio.

This writes models.providers.lmstudio and sets the default model to lmstudio/<custom-model-id>. When you provide an API key, setup also writes the lmstudio:default auth profile.

Interactive setup can prompt for an optional preferred load context length and applies it across the discovered LM Studio models it saves into config. LM Studio plugin config trusts the configured LM Studio endpoint for model requests, including loopback, LAN, and tailnet hosts. Metadata/link-local origins still require explicit opt-in. You can opt out by setting models.providers.lmstudio.request.allowPrivateNetwork: false.

Configuration

Streaming usage compatibility

LM Studio is streaming-usage compatible. When it does not emit an OpenAI-shaped usage object, OpenClaw recovers token counts from llama.cpp-style timings.prompt_n / timings.predicted_n metadata instead.

Same streaming usage behavior applies to these OpenAI-compatible local backends:

  • vLLM
  • SGLang
  • llama.cpp
  • LocalAI
  • Jan
  • TabbyAPI
  • text-generation-webui

Thinking compatibility

When LM Studio's /api/v1/models discovery reports model-specific reasoning options, OpenClaw exposes the matching OpenAI-compatible reasoning_effort values in model compat metadata. Current LM Studio builds can advertise binary UI options such as allowed_options: ["off", "on"] while rejecting those values on /v1/chat/completions; OpenClaw normalizes that binary discovery shape to none, minimal, low, medium, high, and xhigh before sending requests. Older saved LM Studio config that contains off/on reasoning maps is normalized the same way when the catalog is loaded.

Explicit configuration

{
  models: {
    providers: {
      lmstudio: {
        baseUrl: "http://localhost:1234/v1",
        apiKey: "${LM_API_TOKEN}",
        api: "openai-completions",
        models: [
          {
            id: "qwen/qwen3-coder-next",
            name: "Qwen 3 Coder Next",
            reasoning: false,
            input: ["text"],
            cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
            contextWindow: 128000,
            maxTokens: 8192,
          },
        ],
      },
    },
  },
}

Troubleshooting

LM Studio not detected

Make sure LM Studio is running. If authentication is enabled, also set LM_API_TOKEN:

# Start via desktop app, or headless:
lms server start --port 1234

Verify the API is accessible:

curl http://localhost:1234/api/v1/models

Authentication errors (HTTP 401)

If setup reports HTTP 401, verify your API key:

  • Check that LM_API_TOKEN matches the key configured in LM Studio.
  • For LM Studio auth setup details, see LM Studio Authentication.
  • If your server does not require authentication, leave the key blank during setup.

Just-in-time model loading

LM Studio supports just-in-time (JIT) model loading, where models are loaded on first request. OpenClaw preloads models through LM Studio's native load endpoint by default, which helps when JIT is disabled. To let LM Studio's JIT, idle TTL, and auto-evict behavior own model lifecycle, disable OpenClaw's preload step:

{
  models: {
    providers: {
      lmstudio: {
        baseUrl: "http://localhost:1234/v1",
        api: "openai-completions",
        params: { preload: false },
        models: [{ id: "qwen/qwen3.5-9b" }],
      },
    },
  },
}

LAN or tailnet LM Studio host

Use the LM Studio host's reachable address, keep /v1, and make sure LM Studio is bound beyond loopback on that machine:

{
  models: {
    providers: {
      lmstudio: {
        baseUrl: "http://gpu-box.local:1234/v1",
        apiKey: "lmstudio",
        api: "openai-completions",
        models: [{ id: "qwen/qwen3.5-9b" }],
      },
    },
  },
}

lmstudio automatically trusts its configured local/private endpoint for guarded model requests. Custom/local OpenAI-compatible provider entries also trust their exact configured baseUrl origin, except metadata/link-local origins; requests to different private ports or destinations still require models.providers.<id>.request.allowPrivateNetwork: true. Set models.providers.<id>.request.allowPrivateNetwork: false to opt out of exact-origin trust.