Skip to content

Add LM Studio provider support with multi-provider configuration#3

Merged
Jacobcdsmith merged 4 commits into
mainfrom
claude/add-lm-studio-api-efOlK
May 1, 2026
Merged

Add LM Studio provider support with multi-provider configuration#3
Jacobcdsmith merged 4 commits into
mainfrom
claude/add-lm-studio-api-efOlK

Conversation

@Jacobcdsmith
Copy link
Copy Markdown
Owner

@Jacobcdsmith Jacobcdsmith commented May 1, 2026

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

  • Multi-provider support: Added Provider type ('hermes' | 'lmstudio') and provider selection to the configuration system
  • Settings UI overhaul:
    • Added provider selector buttons in the Settings page
    • Dynamic form fields that change based on selected provider (Bearer Token for Hermes, API Key for LM Studio)
    • Provider-specific placeholder text and labels
    • Smart host/port defaults that switch when changing providers
  • Configuration updates:
    • Extended HermesConfig interface with provider and apiKey fields
    • Added PROVIDER_DEFAULTS mapping with sensible defaults (Hermes: port 8765, LM Studio: localhost:1234)
    • Implemented config migration in loadConfig() to handle existing configs saved before provider field was added
  • API integration:
    • Updated checkHealth() to use provider-specific endpoints (/health for Hermes, /v1/models for LM Studio)
    • Dynamic Authorization header selection based on provider (uses apiKey for LM Studio, token for Hermes)
    • Generic error messages ("API error" instead of "Hermes error")
  • Setup flow: Updated needsSetup logic to account for LM Studio's localhost default (empty host is acceptable for LM Studio)

Implementation Details

  • Provider switching clears test results and intelligently resets host/port only if they match the previous provider's defaults
  • LM Studio host defaults to 'localhost' when empty, making it more user-friendly for local development
  • Save button validation differs by provider: Hermes requires a host, LM Studio doesn't
  • Backward compatibility maintained through config migration that merges saved configs with defaults

https://claude.ai/code/session_01LBn5Di3x1CNMj1Chdq6NZh

Summary by CodeRabbit

  • New Features
    • Added support for LM Studio as an alternative provider alongside Hermes.
    • Added provider selection in Settings with dynamic configuration fields based on the chosen provider.
    • Conditional authentication fields: API Key for LM Studio, Bearer Token for Hermes.
    • Automatic configuration migration for existing users to the new provider system.

claude added 2 commits May 1, 2026 12:02
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
@vercel
Copy link
Copy Markdown

vercel Bot commented May 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hermes-mobile Ready Ready Preview, Comment May 1, 2026 0:58am

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@Jacobcdsmith has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 54 minutes and 32 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 216b9b18-3430-44f7-98ed-3cc394ec9c19

📥 Commits

Reviewing files that changed from the base of the PR and between f9e8a3b and 56980c9.

⛔ Files ignored due to path filters (3)
  • dist/assets/index-DLZUWvy8.js is excluded by !**/dist/**
  • dist/index.html is excluded by !**/dist/**
  • dist/sw.js is excluded by !**/dist/**
📒 Files selected for processing (3)
  • src/lib/hermes.ts
  • src/lib/storage.ts
  • src/pages/Settings.tsx
📝 Walkthrough

Walkthrough

The codebase is extended to support multiple LLM provider backends. A new provider discriminator is added to configuration, allowing the app to handle provider-specific differences in health checks (LM Studio uses /v1/models, Hermes uses /health), authentication (LM Studio uses apiKey, Hermes uses token), and default connection parameters.

Changes

Cohort / File(s) Summary
Provider Type & API Integration
src/lib/hermes.ts
Added Provider type ('hermes' | 'lmstudio'), apiKey field to HermesConfig, conditional authorization header selection, and provider-aware health check logic. Standardized error messages from "Hermes error" to "API error".
Configuration Initialization & Migration
src/lib/storage.ts
Introduced centralized DEFAULT_CONFIG including provider field. Config loading now merges parsed stored config with defaults, enabling automatic migration for older configs missing provider.
App Health Check & Setup Gating
src/App.tsx
Adjusted offline detection and setup requirement logic to treat LM Studio as a special case—both now check !config.host && config.provider !== 'lmstudio' instead of just !config.host, allowing LM Studio's default localhost scenario to bypass forced setup.
Settings UI & Provider Selection
src/pages/Settings.tsx
Added provider selector with provider-specific defaults (PROVIDER_DEFAULTS), conditional field rendering (API Key for LM Studio, Bearer Token for Hermes), provider-aware test/save button enablement, and dynamic host label/placeholder text. Includes provider switching logic that resets host/port when transitioning between providers.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop into the future, where choice blooms bright,
Hermes and Studio dance in the light,
Config migration flows smooth as a stream,
Multi-provider magic—a developer's dream!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically summarizes the primary change: adding LM Studio as a new provider alongside existing Hermes support with multi-provider configuration handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/add-lm-studio-api-efOlK

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 54 minutes and 32 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@Jacobcdsmith Jacobcdsmith marked this pull request as ready for review May 1, 2026 12:53
Copilot AI review requested due to automatic review settings May 1, 2026 12:53
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/pages/Settings.tsx Outdated
if (port === PROVIDER_DEFAULTS[provider].port) setPort(defaults.port)
}

const currentConfig = (): HermesConfig => ({ provider, host, port, token, apiKey })
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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). With host === '', requests will be sent to http://: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). With host === '', requests will be sent to http://: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.

Comment thread src/pages/Settings.tsx Outdated
if (port === PROVIDER_DEFAULTS[provider].port) setPort(defaults.port)
}

const currentConfig = (): HermesConfig => ({ provider, host, port, token, apiKey })
Comment thread src/pages/Settings.tsx Outdated
setTesting(false)
}

const effectiveHost = provider === 'lmstudio' ? (host || 'localhost') : host
Comment thread src/pages/Settings.tsx Outdated
<button
onClick={test}
disabled={!host || testing}
disabled={!effectiveHost || testing}
Comment thread src/App.tsx
Comment on lines +22 to 25
if (!config.host && config.provider !== 'lmstudio') { setOnline(false); return }
let alive = true
const poll = async () => {
const ok = await checkHealth(config)
Comment thread src/App.tsx
Comment on lines +33 to +34
// LM Studio defaults to localhost so an empty host is still usable
const needsSetup = !config.host && config.provider !== 'lmstudio'
Comment thread src/lib/hermes.ts Outdated
Comment on lines 23 to 26
const headers = (cfg: HermesConfig) => ({
'Content-Type': 'application/json',
'Authorization': `Bearer ${cfg.token}`,
'Authorization': `Bearer ${cfg.provider === 'lmstudio' ? cfg.apiKey : cfg.token}`,
})
Comment thread src/lib/storage.ts Outdated
host: '',
port: '8765',
token: 'jacob-local-key',
apiKey: 'lm-studio',
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Apply 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() and save() still use { host: '', apiKey: '' }, so src/lib/hermes.ts:21-26 ends up building http://:1234/... and Authorization: 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

📥 Commits

Reviewing files that changed from the base of the PR and between e1fbff6 and f9e8a3b.

⛔ Files ignored due to path filters (8)
  • dist/assets/index-BUjqIbjB.css is excluded by !**/dist/**
  • dist/assets/index-BqdFBxHZ.css is excluded by !**/dist/**
  • dist/assets/index-C_cH2dMP.js is excluded by !**/dist/**
  • dist/assets/index-DRttcgiP.js is excluded by !**/dist/**
  • dist/index.html is excluded by !**/dist/**
  • dist/manifest.webmanifest is excluded by !**/dist/**
  • dist/registerSW.js is excluded by !**/dist/**
  • dist/sw.js is excluded by !**/dist/**
📒 Files selected for processing (4)
  • src/App.tsx
  • src/lib/hermes.ts
  • src/lib/storage.ts
  • src/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
@Jacobcdsmith
Copy link
Copy Markdown
Owner Author

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Contributor

Copilot AI commented May 1, 2026

@copilot apply changes based on the comments in this thread

All the changes from the review thread have already been applied in commit 636cd01:

  1. Host normalization moved to API layer (hermes.ts): getBaseUrl now applies the localhost fallback for LM Studio, so App.tsx health polling and all other callers automatically get normalized hosts without extra code.

  2. Auth header omitted when credential is empty (hermes.ts): headers() only adds `Authorization: ****** when the credential is non-empty, so LM Studio without an API key sends no auth header.

  3. Default apiKey changed to '' (storage.ts): Changed from 'lm-studio' to an empty string so blank is truly blank after migration.

  4. currentConfig() uses resolved host (Settings.tsx): resolvedHost (with localhost fallback) is computed before currentConfig() and used both in the Test button's disabled state and in the actual checkHealth() call, ensuring they're consistent.

@Jacobcdsmith Jacobcdsmith merged commit 9cd9bc5 into main May 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants