From 81bd697dcb32a31c877b0049beaf35954f2bd154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Wed, 20 May 2026 23:29:12 +0200 Subject: [PATCH] Enhance local provider integration and setup instructions - Introduced separate handling for local providers: Ollama, MLX, and Apple Foundation Models. - Updated settings to include distinct sections for each local provider with relevant instructions and model filters. - Improved user interface for selecting local providers based on the operating system. - Refactored provider names to enhance clarity and maintainability in the codebase. --- .../src/void-onboarding/VoidOnboarding.tsx | 46 ++++++- .../react/src/void-settings-tsx/Settings.tsx | 116 ++++++++++++++---- .../contrib/void/common/voidSettingsTypes.ts | 8 +- 3 files changed, 142 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx index e0077a62..626e04e3 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-onboarding/VoidOnboarding.tsx @@ -6,9 +6,9 @@ import { useEffect, useRef, useState } from 'react'; import { useAccessor, useIsDark, useSettingsState } from '../util/services.js'; import { Brain, Check, ChevronRight, DollarSign, ExternalLink, Lock, X } from 'lucide-react'; -import { displayInfoOfProviderName, ProviderName, providerNames, localProviderNames, featureNames, FeatureName, isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'; -import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'; -import { OllamaSetupInstructions, OneClickSwitchButton, SettingsForProvider, ModelDump } from '../void-settings-tsx/Settings.js'; +import { displayInfoOfProviderName, ProviderName, providerNames, localProviderNames, ollamaProviderNames, mlxProviderNames, appleProviderNames, otherLocalProviderNames, featureNames, FeatureName, isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'; +import { os } from '../../../../common/helpers/systemInfo.js'; +import { OllamaSetupInstructions, MlxSetupInstructions, AppleFoundationModelsSetupInstructions, OneClickSwitchButton, SettingsForProvider, ModelDump } from '../void-settings-tsx/Settings.js'; import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js'; import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js'; import { isLinux } from '../../../../../../../base/common/platform.js'; @@ -99,6 +99,22 @@ const tabNames = ['Free', 'Paid', 'Local'] as const; type TabName = typeof tabNames[number] | 'Cloud/Other'; +type LocalSubTab = 'ollama' | 'mlx' | 'apple' | 'other'; + +const localSubTabLabels: Record = { + ollama: 'Ollama', + mlx: 'MLX', + apple: 'apple', + other: 'Other', +}; + +const providerNamesOfLocalSubTab: Record = { + ollama: [...ollamaProviderNames], + mlx: [...mlxProviderNames], + apple: [...appleProviderNames], + other: [...otherLocalProviderNames], +}; + // Data for cloud providers tab const cloudProviders: ProviderName[] = ['googleVertex', 'liteLLM', 'microsoftAzure', 'awsBedrock', 'openAICompatible']; @@ -128,6 +144,7 @@ const featureNameMap: { display: string, featureName: FeatureName }[] = [ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setPageIndex: (index: number) => void }) => { const [currentTab, setCurrentTab] = useState('Free'); + const [localSubTab, setLocalSubTab] = useState('ollama'); const settingsState = useSettingsState(); const [errorMessage, setErrorMessage] = useState(null); @@ -200,7 +217,24 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
{descriptionOfTab[currentTab]}
- {providerNamesOfTab[currentTab].map((providerName) => ( + {currentTab === 'Local' && ( +
+ {(os === 'mac' ? (['ollama', 'mlx', 'apple', 'other'] as const) : (['ollama', 'other'] as const)).map(sub => ( + + ))} +
+ )} + + {(currentTab === 'Local' ? providerNamesOfLocalSubTab[localSubTab] : providerNamesOfTab[currentTab]).map((providerName) => (
Add {displayInfoOfProviderName(providerName).title} @@ -226,6 +260,8 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
{providerName === 'ollama' && } + {providerName === 'mlx' && os === 'mac' && } + {providerName === 'appleFoundationModels' && os === 'mac' && }
))} @@ -239,7 +275,7 @@ const AddProvidersPage = ({ pageIndex, setPageIndex }: { pageIndex: number, setP
Local models should be detected automatically. You can add custom models below.
)} - {currentTab === 'Local' && } + {currentTab === 'Local' && } {currentTab === 'Cloud/Other' && } )} diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index 6280afb8..6b2c0581 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------*/ import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; // Added useRef import just in case it was missed, though likely already present -import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidStatefulModelInfo, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, nonlocalProviderNames, localProviderNames, GlobalSettingName, featureNames, displayInfoOfFeatureName, isProviderNameDisabled, FeatureName, hasDownloadButtonsOnModelsProviderNames, subTextMdOfProviderName } from '../../../../common/voidSettingsTypes.js' +import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidStatefulModelInfo, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, nonlocalProviderNames, localProviderNames, ollamaProviderNames, mlxProviderNames, appleProviderNames, otherLocalProviderNames, GlobalSettingName, featureNames, displayInfoOfFeatureName, isProviderNameDisabled, FeatureName, hasDownloadButtonsOnModelsProviderNames, subTextMdOfProviderName } from '../../../../common/voidSettingsTypes.js' import { os } from '../../../../common/helpers/systemInfo.js' import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' import { VoidButtonBgDarken, VoidCustomDropdownBox, VoidInputBox2, VoidSimpleInputBox, VoidSwitch } from '../util/inputs.js' @@ -26,7 +26,10 @@ import { StorageScope, StorageTarget } from '../../../../../../../platform/stora type Tab = | 'models' - | 'localProviders' + | 'ollama' + | 'mlx' + | 'apple' + | 'localOther' | 'providers' | 'featureOptions' | 'mcp' @@ -95,11 +98,11 @@ const RefreshModelButton = ({ providerName }: { providerName: RefreshableProvide /> } -const RefreshableModels = () => { +const RefreshableModels = ({ providerNamesFilter }: { providerNamesFilter?: readonly ProviderName[] }) => { const settingsState = useSettingsState() + const names = providerNamesFilter ?? refreshableProviderNames - - const buttons = refreshableProviderNames.map(providerName => { + const buttons = names.map(providerName => { if (!settingsState.settingsOfProvider[providerName]._didFillInProviderSettings) return null return }) @@ -746,7 +749,7 @@ export const SettingsForProvider = ({ providerName, showProviderTitle, showProvi } -export const VoidProviderSettings = ({ providerNames }: { providerNames: ProviderName[] }) => { +export const VoidProviderSettings = ({ providerNames }: { providerNames: readonly ProviderName[] }) => { return <> {providerNames.map(providerName => @@ -754,6 +757,32 @@ export const VoidProviderSettings = ({ providerNames }: { providerNames: Provide } +const LocalProviderSection = ({ title, description, instructions, providerNames, refreshable, autoSetup, modelFilter }: { + title: string + description?: string + instructions?: React.ReactNode + providerNames: readonly ProviderName[] + refreshable?: boolean + autoSetup?: React.ReactNode + modelFilter?: readonly ProviderName[] +}) => ( +
+

{title}

+ {description &&

{description}

} + {instructions &&
{instructions}
} + {autoSetup} + {refreshable && } + + {modelFilter && ( + <> +
+

Models for {title}

+ + + )} +
+) + type TabName = 'models' | 'general' export const AutoDetectLocalModelsToggle = () => { @@ -1110,7 +1139,12 @@ export const Settings = () => { const navItems: { tab: Tab; label: string }[] = [ { tab: 'models', label: 'Models' }, - { tab: 'localProviders', label: 'Local Providers' }, + { tab: 'ollama', label: 'Ollama' }, + ...(os === 'mac' ? ([ + { tab: 'mlx' as const, label: 'MLX' }, + { tab: 'apple' as const, label: 'apple' }, + ]) : []), + { tab: 'localOther', label: 'Local (other)' }, { tab: 'providers', label: 'Main Providers' }, { tab: 'featureOptions', label: 'Feature Options' }, { tab: 'general', label: 'General' }, @@ -1254,27 +1288,65 @@ export const Settings = () => {
- - - +
+

Per-provider setup and refresh are under Ollama, MLX, and apple in the sidebar.

- {/* Local Providers section */} -
+
-

Local Providers

-

{`Void can access any model that you host locally. We automatically detect your local models by default.`}

+ } + providerNames={ollamaProviderNames} + refreshable + modelFilter={ollamaProviderNames} + /> +
+
-
- -
-
- - -
+ {os === 'mac' && ( +
+ + } + providerNames={mlxProviderNames} + refreshable + autoSetup={} + modelFilter={mlxProviderNames} + /> + +
+ )} - + {os === 'mac' && ( +
+ + } + providerNames={appleProviderNames} + refreshable + autoSetup={} + modelFilter={appleProviderNames} + /> + +
+ )} + +
+ +
diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 85e03b97..7a342766 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -16,7 +16,13 @@ type UnionOfKeys = T extends T ? keyof T : never; export type ProviderName = keyof typeof defaultProviderSettings export const providerNames = Object.keys(defaultProviderSettings) as ProviderName[] -export const localProviderNames = ['ollama', 'vLLM', 'lmStudio', 'mlx', 'appleFoundationModels'] satisfies ProviderName[] // all local names +export const ollamaProviderNames = ['ollama'] as const satisfies ProviderName[] +export const mlxProviderNames = ['mlx'] as const satisfies ProviderName[] +export const appleProviderNames = ['appleFoundationModels'] as const satisfies ProviderName[] +/** vLLM, LM Studio — separate from Ollama / MLX / apple */ +export const otherLocalProviderNames = ['vLLM', 'lmStudio'] as const satisfies ProviderName[] + +export const localProviderNames = [...ollamaProviderNames, ...mlxProviderNames, ...appleProviderNames, ...otherLocalProviderNames] satisfies ProviderName[] // all local names export const nonlocalProviderNames = providerNames.filter((name) => !(localProviderNames as string[]).includes(name)) // all non-local names type CustomSettingName = UnionOfKeys