Add LM Studio provider support with multi-provider configuration#3
Conversation
Adds LM Studio as a selectable provider alongside Hermes. Users can switch between providers in Settings, with each showing its relevant fields (API key for LM Studio, bearer token for Hermes). LM Studio defaults to localhost:1234 and uses /v1/models for health checks since it has no /health endpoint. Old configs migrate cleanly via spread defaults. https://claude.ai/code/session_01LBn5Di3x1CNMj1Chdq6NZh
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe codebase is extended to support multiple LLM provider backends. A new Changes
Sequence DiagramsequenceDiagram
participant App as App Initialization
participant Storage as Config Storage
participant Hermes as API Client
participant API as LLM Provider API
App->>Storage: loadConfig()
activate Storage
Storage->>Storage: Load saved config or use DEFAULT_CONFIG
Storage->>Storage: Merge provider field with defaults
Storage-->>App: HermesConfig with provider
deactivate Storage
alt provider === 'lmstudio'
App->>Hermes: Health check call
Hermes->>API: GET /v1/models (LM Studio)
API-->>Hermes: Model list response
else provider === 'hermes'
App->>Hermes: Health check call
Hermes->>API: GET /health (Hermes)
API-->>Hermes: Health status response
end
Hermes-->>App: Health check result
alt Health check passes
App->>App: Keep normal Chat flow
else Health check fails
App->>App: Mark offline / enable user settings
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 54 minutes and 32 seconds.Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f9e8a3b100
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (port === PROVIDER_DEFAULTS[provider].port) setPort(defaults.port) | ||
| } | ||
|
|
||
| const currentConfig = (): HermesConfig => ({ provider, host, port, token, apiKey }) |
There was a problem hiding this comment.
Normalize empty LM Studio host to localhost
When provider === 'lmstudio', the UI explicitly allows an empty host (canSave is always true and effectiveHost treats blank as localhost), but currentConfig() still persists and tests with host: ''. That flows into checkHealth/chat requests and produces http://:1234/..., so connection tests fail and the app stays offline unless users manually type localhost, contradicting the new default-host behavior.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a second LLM backend (LM Studio) alongside Hermes and updates configuration + UI flow so users can select and configure a provider.
Changes:
- Introduces
Provider('hermes' | 'lmstudio') and extends config storage + migration to include provider-specific credentials. - Updates Settings UI to switch providers and show provider-specific fields (Hermes token vs LM Studio API key) with defaults.
- Updates health-check behavior and error messaging to be provider-aware.
Reviewed changes
Copilot reviewed 4 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/Settings.tsx | Adds provider selector + provider-specific form fields and defaults. |
| src/lib/storage.ts | Adds default config + migration merge for newly added config fields. |
| src/lib/hermes.ts | Adds Provider, extends config, provider-specific auth header and health endpoint. |
| src/App.tsx | Updates setup/online gating to allow LM Studio with empty host. |
| dist/sw.js | Updates precache manifest (build output). |
| dist/registerSW.js | Changes service worker registration path/scope to root. |
| dist/manifest.webmanifest | Changes PWA start_url/scope to root. |
| dist/index.html | Updates asset and manifest paths to root-based URLs. |
| dist/assets/index-C_cH2dMP.js | Removes old built JS bundle (build output). |
| dist/assets/index-BqdFBxHZ.css | Removes old built CSS bundle (build output). |
| dist/assets/index-BUjqIbjB.css | Adds new built CSS bundle (build output). |
Comments suppressed due to low confidence (2)
src/lib/hermes.ts:21
- getBaseUrl()
always usescfg.host, but the UI/app intentionally allows LM Studio to have an empty host (treated as localhost). Withhost === '', requests will be sent tohttp://:1234/...which will always fail. Fix by normalizing the host for LM Studio either insidegetBaseUrl()(recommended, so all API calls benefit) or by ensuring callers pass a config withhost` coerced to 'localhost' when provider is 'lmstudio'.
const getBaseUrl = (cfg: HermesConfig) => `http://${cfg.host}:${cfg.port}`
src/lib/hermes.ts:40
- getBaseUrl()
always usescfg.host, but the UI/app intentionally allows LM Studio to have an empty host (treated as localhost). Withhost === '', requests will be sent tohttp://:1234/...which will always fail. Fix by normalizing the host for LM Studio either insidegetBaseUrl()(recommended, so all API calls benefit) or by ensuring callers pass a config withhost` coerced to 'localhost' when provider is 'lmstudio'.
'Authorization': `Bearer ${cfg.provider === 'lmstudio' ? cfg.apiKey : cfg.token}`,
})
export async function checkHealth(cfg: HermesConfig): Promise<boolean> {
try {
// LM Studio has no /health endpoint — use /v1/models instead
const path = cfg.provider === 'lmstudio' ? '/v1/models' : '/health'
const res = await fetch(`${getBaseUrl(cfg)}${path}`, {
headers: headers(cfg),
signal: AbortSignal.timeout(5000),
})
return res.ok
} catch {
return false
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (port === PROVIDER_DEFAULTS[provider].port) setPort(defaults.port) | ||
| } | ||
|
|
||
| const currentConfig = (): HermesConfig => ({ provider, host, port, token, apiKey }) |
| setTesting(false) | ||
| } | ||
|
|
||
| const effectiveHost = provider === 'lmstudio' ? (host || 'localhost') : host |
| <button | ||
| onClick={test} | ||
| disabled={!host || testing} | ||
| disabled={!effectiveHost || testing} |
| if (!config.host && config.provider !== 'lmstudio') { setOnline(false); return } | ||
| let alive = true | ||
| const poll = async () => { | ||
| const ok = await checkHealth(config) |
| // LM Studio defaults to localhost so an empty host is still usable | ||
| const needsSetup = !config.host && config.provider !== 'lmstudio' |
| const headers = (cfg: HermesConfig) => ({ | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${cfg.token}`, | ||
| 'Authorization': `Bearer ${cfg.provider === 'lmstudio' ? cfg.apiKey : cfg.token}`, | ||
| }) |
| host: '', | ||
| port: '8765', | ||
| token: 'jacob-local-key', | ||
| apiKey: 'lm-studio', |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/Settings.tsx (1)
35-53:⚠️ Potential issue | 🟠 Major | ⚡ Quick winApply the LM Studio fallbacks inside
currentConfig()Right now the defaults are UI-only. If the user leaves LM Studio host or API key blank,
test()andsave()still use{ host: '', apiKey: '' }, sosrc/lib/hermes.ts:21-26ends up buildinghttp://:1234/...andAuthorization: Bearer. That breaks the exact “blank host/default key” path this screen advertises.Proposed fix
- const currentConfig = (): HermesConfig => ({ provider, host, port, token, apiKey }) + const currentConfig = (): HermesConfig => ({ + provider, + host: provider === 'lmstudio' ? (host || PROVIDER_DEFAULTS.lmstudio.host) : host, + port: port || PROVIDER_DEFAULTS[provider].port, + token, + apiKey: provider === 'lmstudio' ? (apiKey || 'lm-studio') : apiKey, + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Settings.tsx` around lines 35 - 53, The currentConfig() helper returns raw UI fields so save() and test() send empty host/apiKey for LM Studio; change currentConfig() to apply the LM Studio fallbacks (use the same logic as effectiveHost and an effectiveApiKey fallback) and return those values so checkHealth(currentConfig()) and saveConfig(cfg) get host='localhost' when provider==='lmstudio' and a non-empty apiKey when provider==='lmstudio' (e.g., apiKey || '<default-key>' or the app's configured default); update currentConfig() to compute effectiveHost = provider==='lmstudio' ? (host || 'localhost') : host and effectiveApiKey = provider==='lmstudio' ? (apiKey || '<default-key>') : apiKey and return { provider, host: effectiveHost, port, token, apiKey: effectiveApiKey } so test() and save() use the fallbacks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/pages/Settings.tsx`:
- Around line 35-53: The currentConfig() helper returns raw UI fields so save()
and test() send empty host/apiKey for LM Studio; change currentConfig() to apply
the LM Studio fallbacks (use the same logic as effectiveHost and an
effectiveApiKey fallback) and return those values so
checkHealth(currentConfig()) and saveConfig(cfg) get host='localhost' when
provider==='lmstudio' and a non-empty apiKey when provider==='lmstudio' (e.g.,
apiKey || '<default-key>' or the app's configured default); update
currentConfig() to compute effectiveHost = provider==='lmstudio' ? (host ||
'localhost') : host and effectiveApiKey = provider==='lmstudio' ? (apiKey ||
'<default-key>') : apiKey and return { provider, host: effectiveHost, port,
token, apiKey: effectiveApiKey } so test() and save() use the fallbacks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d6dfb33a-8fa2-4eed-b6ff-33f236d77345
⛔ Files ignored due to path filters (8)
dist/assets/index-BUjqIbjB.cssis excluded by!**/dist/**dist/assets/index-BqdFBxHZ.cssis excluded by!**/dist/**dist/assets/index-C_cH2dMP.jsis excluded by!**/dist/**dist/assets/index-DRttcgiP.jsis excluded by!**/dist/**dist/index.htmlis excluded by!**/dist/**dist/manifest.webmanifestis excluded by!**/dist/**dist/registerSW.jsis excluded by!**/dist/**dist/sw.jsis excluded by!**/dist/**
📒 Files selected for processing (4)
src/App.tsxsrc/lib/hermes.tssrc/lib/storage.tssrc/pages/Settings.tsx
- getBaseUrl now normalizes empty host to localhost for LM Studio, covering App.tsx health polling and all other callers - Authorization header is omitted entirely when credential is empty (LM Studio with no API key configured) - Default apiKey changed from 'lm-studio' to '' so blank is truly blank - Fix stale effectiveHost reference in Test button disabled prop https://claude.ai/code/session_01LBn5Di3x1CNMj1Chdq6NZh
LM Studio's native API lives under /api/v1, not /v1:
- Health check + model listing: GET /api/v1/models
- Chat (streaming and non-streaming): POST /api/v1/chat
Also handle LM Studio's flat model array response vs the
OpenAI-wrapped { data: [...] } format used by Hermes.
https://claude.ai/code/session_01LBn5Di3x1CNMj1Chdq6NZh
|
@copilot apply changes based on the comments in this thread |
All the changes from the review thread have already been applied in commit
|
Summary
This PR adds support for LM Studio as an alternative provider alongside the existing Hermes provider, allowing users to choose between different LLM backends through an updated settings interface.
Key Changes
Providertype ('hermes' | 'lmstudio') and provider selection to the configuration systemHermesConfiginterface withproviderandapiKeyfieldsPROVIDER_DEFAULTSmapping with sensible defaults (Hermes: port 8765, LM Studio: localhost:1234)loadConfig()to handle existing configs saved before provider field was addedcheckHealth()to use provider-specific endpoints (/healthfor Hermes,/v1/modelsfor LM Studio)apiKeyfor LM Studio,tokenfor Hermes)needsSetuplogic to account for LM Studio's localhost default (empty host is acceptable for LM Studio)Implementation Details
https://claude.ai/code/session_01LBn5Di3x1CNMj1Chdq6NZh
Summary by CodeRabbit