Add LM Studio as a local model provider - #434
Open
zalex666 wants to merge 3 commits into
Open
Conversation
Second keyless local provider alongside Ollama, riding the same OpenAI-compatible /v1 path (OpenAIProvider with a placeholder key, default http://localhost:1234): - registry: lmstudio descriptor + builder; _normalize_ollama_url generalized to _normalize_local_url(url, default); the keyless verify branch (GET /v1/models, no auth) now covers both local providers - capabilities: lmstudio gets the same conservative local-model defaults as ollama (tools yes, parallel/vision no) - manager: _ollama_alive/_ollama_models generalized to _local_alive/_local_models driven by a LOCAL_MODEL_SERVERS table (ollama via native /api/tags, LM Studio via OpenAI-shaped /v1/models); picker gating and suggestions follow - GUI: lobe-icons LM Studio mark, gallery order, and the keyless install-help copy generalized into a LOCAL_HELP map - tests mirror the existing Ollama coverage (normalize/build/verify/ capabilities/liveness gating) plus a parse test for both list shapes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y semantics
Fixes from the gpt-5.6-sol review of the LM Studio provider addition
(all pre-existing Ollama behaviors this change inherited or generalized):
- keyless Detect now persists on every pass (GUI): keyless providers
report configured out of the box, so the dirty/configured gate skipped
the first-time save — no stored profile, and set_provider's
recommended-model auto-add never ran. _local_models pairs with this by
treating a stored EMPTY profile as engaged (is None check).
- _provider_available: local servers must actually answer, everyone else
must be configured — now drives picker gating, model_ready, and the
first-working-provider default handoff (a dead local server no longer
reads as ready, and no longer blocks a working provider from taking
the default).
- probes require the provider's list shape, not just a 200: /v1/models
must serve {"data": [...]}, /api/tags {"models": [...]} — some other
service answering on the port no longer passes verify or liveness.
- verify_provider: non-secret fields sent explicitly blank mean 'back to
the default' instead of resurrecting the stored value (a passing Test
used to validate a URL the subsequent save then removed); blank
secrets still fall back to the stored key.
- _refresh_provider drops the per-provider liveness cache, so repointing
or removing a local server re-probes immediately.
- coverage: probe shape/URLs, cache invalidation, model_ready gating,
first-Detect end-to-end (empty profile -> recommended auto-add ->
default handoff), explicit-blank verify, the GUI hook's keyless
Detect save, and an lmstudio entry in the e2e provider fixture.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex confirmation pass (round 2) verdicts: findings 1-5 FIXED, plus three residuals addressed here: - runTestAndSave no longer swallows a rejected/not-ok setProvider: a passing Detect whose save fails now shows the error instead of '✓ Tested & saved' (affected keyed providers too; the keyless path made it likelier) - the explicit-blank verify test stubs httpx BEFORE set_provider, so its suggested-models pass can't hit the network - regression test for the dead-local-default handoff: a dead local default yields to a newly configured provider, a live one is never stolen (pins _provider_available in the handoff path) - GUI test for the failed-save path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 tasks
zalex666
marked this pull request as ready for review
August 3, 2026 20:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #122
What
Adds LM Studio as a first-class local model provider, alongside Ollama — the second keyless provider riding the OpenAI-compatible
/v1path (OpenAIProviderwith a placeholder key, defaulthttp://localhost:1234).Prior art
#184 by @sobiya-22 predates this PR and implements the same core pattern — credit to them for getting there first (I should have searched before opening this one). #206 by @hacksics independently identified the keyless-provider discovery bug before self-closing. On top of the core feature, this PR ships the brand mark, README, a recommended model, and the six correctness fixes below — the first of which is what makes "Detect → models appear" work at the default URL — and is rebased on current
main. Zero attachment to which PR lands: if maintainers prefer #184, everything here can be cherry-picked onto it and I'll close this.Users get the same experience Ollama has today: pick LM Studio in the provider gallery, optionally adjust the server URL, hit Detect, and the models already downloaded in LM Studio appear in the composer picker — gated on a liveness probe so a machine without LM Studio never shows phantom local models.
Why
LM Studio is one of the most popular ways to run local models, exposes the same OpenAI-compatible surface the provider layer already speaks, and (like the compat vendors, per the registry's own note) deserves a first-class listing rather than the "point the OpenAI slot at a custom endpoint with a dummy key" trick.
How
providers/registry.py— newlmstudiodescriptor + builder (OpenAIProvider(api_key="lm-studio", base_url=<root>/v1))._normalize_ollama_urlis generalized to_normalize_local_url(url, default)since both servers want<root>/v1. The keyless verify branch (GET /v1/models, no auth header, friendly 404/401 copy) now covers both local providers viaLOCAL_PROVIDERS.providers/capabilities.py—lmstudio:*models get the same conservative local-model defaults as Ollama (tools on, vision/parallel-tool-calls off) unless the matrix says otherwise.server/manager.py—_ollama_alive/_ollama_modelsgeneralized to_local_alive(name)/_local_models(name), driven by a smallLOCAL_MODEL_SERVERStable. Ollama keeps its native/api/tags; LM Studio uses the OpenAI-shaped/v1/models(its native REST API is still beta and can sit behind an auth token). Liveness stays cached 30s, now per provider. Picker gating and add-model suggestions follow the table.LOCAL_HELPmap (it previously hardcoded Ollama as the only keyless provider).qwen/qwen3-coder-30b, the LM Studio catalog id of the same verified tool-calling pick the Ollama row recommends.Review hardening (second commit)
An adversarial review pass (GPT-5.6 Sol @ xhigh via Codex CLI) surfaced five latent issues in the keyless-local-provider flow — all pre-existing Ollama behaviors this change inherited or generalized — fixed here since the PR owns that surface now:
configuredout of the box, so the GUI's dirty/configured gate skipped the first-time save: no stored profile, andset_provider's recommended-model auto-add never ran._local_modelspairs with this by treating a stored empty profile as engaged (is Nonegate)._provider_available(local servers must actually answer; everyone else must be configured) now drives picker gating,model_ready, and the first-working-provider default handoff — a dead local server no longer reads as ready, and no longer blocks a working provider from taking over the default.dataat/v1/models,modelsat/api/tags) — a random service answering on the port no longer passes as a model server.verify_provider, instead of resurrecting the stored URL (a passing Test used to validate a config the subsequent save then removed). Blank secrets still fall back to the stored key.A follow-up confirmation review verified the fixes.
Tests
Mirrors and extends the existing Ollama coverage: URL normalization, builder base_url/placeholder-key, router prefix-strip for slash-carrying LM Studio ids, capabilities, keyless verify request shape + non-model-server rejection, liveness-gated picker entries (per-provider: one local server being up doesn't surface the other's models), both list-shape parses, cache invalidation,
model_readygating, the first-Detect end-to-end path, and the GUI hook's keyless-Detect save. The Playwright provider fixture gains anlmstudioentry.pytest tests/— 1124 passed, 1 skippedtsc --noEmitclean,vitest run— 110 passedmodel_ready, and tool-calling round trips onqwen-qwen2.5-coder-7bandqwen/qwen3.6-27b— JIT model load, structuredget_weather(city=Paris)call, tool-result fold-in, usage metering, and streaming (a thinking model'sreasoning_contentflows through the reasoning sidecar correctly)🤖 Generated with Claude Code