feat(ai-providers): multi-model discovery, OpenRouter or/ prefix routing, LiteLLM redesign, theme icons, disabled-provider reconciliation - #396
Conversation
…esign, theme-aware Lobe icons, focus lock fixes & expanded dropdown width
|
| Filename | Overview |
|---|---|
| electron/LLMHelper.ts | Adds OpenRouter client initialization, prefixed model classification, request routing, prefix removal, and preference injection. |
| electron/ipcHandlers.ts | Adds provider configuration IPC and reconciles the runtime model against disabled-provider and enabled-model filters. |
| electron/services/CredentialsManager.ts | Persists OpenRouter configuration and provider/model-selection state in encrypted credential storage. |
| electron/audio/whisper/inferenceConfig.ts | Validates the Windows DML memory threshold and selects CPU inference below the configured floor. |
| src/components/settings/AIProvidersSettings.tsx | Redesigns cloud-provider and LiteLLM configuration and model-management controls. |
| src/components/settings/ProviderCard.tsx | Adds searchable multi-model selection with selected-only filtering and optimized row transitions. |
| src/utils/modelUtils.ts | Pools preset and discovered models for provider selection. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
UI[Provider settings and model selector] --> IPC[Preload and IPC handlers]
IPC --> Creds[CredentialsManager]
IPC --> Runtime[LLMHelper active model]
Runtime --> Native[Native cloud providers]
Runtime --> Lite[LiteLLM proxy]
Runtime --> OR[OpenRouter client]
Runtime --> Gemini[Gemini fallback]
Reviews (16): Last reviewed commit: "fix(ipc): do not install any model when ..." | Re-trigger Greptile
…nguage/accent, sticky keybind tabs & UI skill creator
…model reconciliation, and DirectML threshold
…r routing controls, and model modality badges
…al open, and infer vision/reasoning modality badges
…urious API calls, fix greedy reasoning heuristics
…nd eliminate all heuristic guessing
…prevent model ID misrouting
…ter and all main features
… toggle & visible scrollbar in Model Manager
… _streamChatInner
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
…Available check - Replace slash heuristic with LiteLLM-style or/ prefix for OpenRouter models - Guard isOpenAiModel/isClaudeModel/isGroqModel from matching or/ prefix - Strip or/ prefix at API boundary (generateWithOpenai, streamWithOpenai, streamWithOpenaiMultimodal) - Move OpenRouter check after Groq in both streaming and non-streaming paths - Add providerFamily() recognition of or/ prefix to refreshRuntimeDefaultIfUnavailable - Fix clientOverride identity check for OpenRouter preferences (was using isOpenRouterModel on stripped model which always returned false) - Add resolveOpenRouterModel helper
…ter tuning, and style model manager scrollbars
…esh to LiteLLM handler - Replacement chain now calls modelAvailable() instead of raw has() checks, so disabled/enabled-model filters are respected when picking a fallback - set-litellm-enabled-models now calls refreshRuntimeDefaultIfUnavailable() after updating, same as set-cloud-enabled-models
When all providers are disabled or no credential is configured, refreshRuntimeDefaultIfUnavailable now resets the persisted default to 'natively' instead of returning null without updating state. This prevents LLMHelper from continuing to route requests to a stale disabled model identifier.
…odel available The !next fallback used 'natively' as the reset sentinel, but natively may have been explicitly disabled by the user and was already rejected by modelAvailable() above — re-setting to it would re-activate the disabled provider. Use 'gemini-3.6-flash' (the constant default) which gracefully falls through to 'No AI providers configured'.
The !next fallback previously installed gemini-3.6-flash as a sentinel, but gemini-3.6-flash was already rejected by modelAvailable() above (along with every other candidate). Persisting a rejected model re-activates the disabled provider, which is the exact bug Greptile flagged. Now returns null without touching the default model, so the persisted state stays unchanged and the routing chain fails at execution time with 'No AI providers configured'.
Summary of Changes
1. Multi-Model Discovery & Selection for Cloud Providers
buildAvailableModelOptions()pools standard preset models + discovered/cached models fromlocalStorage(cached-models-*).cloudEnabledModels(Record<string, string[]>), keeping selections intact when toggling providers on or off.2. LiteLLM Proxy Card UI Redesign & Enhancements
Proxy URL: http://...indicator badge with a globe icon when configured.[ Manage Models (X/Y Enabled) ▾ ][ Connected / Test Connection ][ 🗑️ Remove Configuration ].3. Theme-Aware Icon System (
@lobehub/icons)LobeProviderIcon.tsxwrapping@lobehub/iconswith fallback local SVGs/images for all providers.4. Chromium Input Focus Lock Bug Fix (Windows)
window.confirm(...)dialogs in Windows Electron locks Chromium's input focus and pointer-event subsystem when the modal closes.confirm()calls from API key and configuration removal handlers. Inputs and Save buttons remain 100% focused and interactive immediately upon deletion without needing to switch focus away and back.5. Model Selector Dropdown Window Expansion
BrowserWindowwidth from140pxto350px(height300px) inModelSelectorWindowHelper.tsso long model names display without right-side clipping.ModelSelectorWindow.tsxcontainer tow-full h-[290px]and trigger button styling inNativelyInterface.tsxtomin-w-[170px] max-w-[240px].6. Popup Performance & Dark Mode Aesthetics
useMemoso searching and filtering run efficiently without recalculating on every mouse event.transition-allon list rows with lightweighttransition-colors duration-150, eliminating hover lag.7. OpenRouter
or/Prefix (LiteLLM-style)modelId.includes('/')) with a uniqueor/prefix, matching the same pattern LiteLLM uses (litellm/). OpenRouter models are stored asor/<provider>/<model>(e.g.or/openai/gpt-4o,or/anthropic/claude-sonnet-4).isOpenRouterModel()now usesmodelId.startsWith('or/')— no exclusion list needed.isOpenAiModel,isClaudeModel,isGroqModelso they never matchor/-prefixed models.generateWithOpenai,streamWithOpenai,streamWithOpenaiMultimodal.resolveOpenRouterModel()helper added.modelFetcher.tsprefixes all fetched OpenRouter models withor/.modelUtils.tspreset OpenRouter models already useor/prefix.this.openrouterClient === clientOverrideidentity check instead ofisOpenRouterModel(model)(which returned false after the stripped model name).8. Provider Routing Order
chatWithGeminiand_streamChatInner.Natively → OpenAI → Claude → DeepSeek → LiteLLM → Groq → OpenRouter → Gemini fallback9. Disabled Provider & Enabled-Model Reconciliation
refreshRuntimeDefaultIfUnavailable()now consultsdisabledProvidersandcloudEnabledModelsfilters so disabling a provider or de-selecting a model immediately takes effect.modelAvailable()instead of rawhas()checks, so disabled/enabled-model filters are respected when picking a fallback.set-litellm-enabled-modelsnow callsrefreshRuntimeDefaultIfUnavailable()after updating, same asset-cloud-enabled-models.providerFamily()inipcHandlers.tsrecognizesor/prefix as'openrouter'.'gemini-3.6-flash'(not'natively', which may have been disabled) so the stale model identifier is never re-activated.10. OpenRouter Credentials & Preferences
setOpenrouterApiKeyIPC handler persists the key and updates LLMHelper.get-openrouter-key-infofetches key metadata from OpenRouter API.get-openrouter-preferences/set-openrouter-preferencespersist reasoning effort, provider sort, and fallback preferences.extraOpenRouterParams(reasoning effort, provider sorting).11. Local Whisper Memory Safeguards
NATIVELY_DML_MIN_AVAIL_GBenv var withNumber.isFinite()and falls back to the default 8GB threshold for malformed values.Files Changed
electron/LLMHelper.tsor/prefix routing, provider guards, prefix stripping, routing order fixelectron/ipcHandlers.tselectron/ModelSelectorWindowHelper.tselectron/preload.tselectron/services/CredentialsManager.tscloudEnabledModels, OpenRouter preferenceselectron/utils/modelFetcher.tsor/prefix on fetched OpenRouter modelselectron/audio/whisper/inferenceConfig.tssrc/components/ModelSelectorWindow.tsxsrc/components/NativelyInterface.tsxsrc/components/settings/AIProvidersSettings.tsxsrc/components/settings/ProviderCard.tsxsrc/components/settings/LobeProviderIcon.tsxsrc/assets/provider-logos/src/utils/modelUtils.tsbuildAvailableModelOptions()pooling,or/preset modelssrc/types/electron.d.tsVerification
npx tsc --noEmit— ✅ 0 compilation errors