From f49a6f8e41203df160230940dbedaefed56c11cb Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Wed, 18 Dec 2024 16:06:53 -0800 Subject: [PATCH] minor style changes and refresh model works in general --- .../lineSelection/browser/lineSelection.ts | 2 +- .../platform/void/common/llmMessageService.ts | 44 +++++-- .../platform/void/common/llmMessageTypes.ts | 33 +++-- .../void/common/refreshModelService.ts | 121 +++++++++++------- .../platform/void/common/voidSettingsTypes.ts | 2 +- .../void/electron-main/llmMessage/ollama.ts | 7 +- .../void/electron-main/llmMessage/openai.ts | 39 +++++- .../void/electron-main/llmMessageChannel.ts | 35 ++++- .../void/browser/react/src/util/services.tsx | 6 +- .../react/src/void-settings-tsx/Settings.tsx | 48 +++++-- 10 files changed, 248 insertions(+), 89 deletions(-) diff --git a/src/vs/editor/contrib/lineSelection/browser/lineSelection.ts b/src/vs/editor/contrib/lineSelection/browser/lineSelection.ts index 1ad614b8..5d65964e 100644 --- a/src/vs/editor/contrib/lineSelection/browser/lineSelection.ts +++ b/src/vs/editor/contrib/lineSelection/browser/lineSelection.ts @@ -22,7 +22,7 @@ export class ExpandLineSelectionAction extends EditorAction { kbOpts: { weight: KeybindingWeight.EditorCore, kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KeyL + primary: KeyMod.CtrlCmd | KeyCode.KeyM // Void changed this to Cmd+M }, }); } diff --git a/src/vs/platform/void/common/llmMessageService.ts b/src/vs/platform/void/common/llmMessageService.ts index e0ec2b12..606f4487 100644 --- a/src/vs/platform/void/common/llmMessageService.ts +++ b/src/vs/platform/void/common/llmMessageService.ts @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPL 3.0 License. *--------------------------------------------------------------------------------------------*/ -import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainLLMMessageParams, MainLLMMessageAbortParams, ServiceOllamaListParams, EventOllamaListOnSuccessParams, EventOllamaListOnErrorParams, MainOllamaListParams } from './llmMessageTypes.js'; +import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from './llmMessageTypes.js'; import { IChannel } from '../../../base/parts/ipc/common/ipc.js'; import { IMainProcessService } from '../../ipc/common/mainProcessService.js'; import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; @@ -21,7 +21,8 @@ export interface ILLMMessageService { readonly _serviceBrand: undefined; sendLLMMessage: (params: ServiceSendLLMMessageParams) => string | null; abort: (requestId: string) => void; - ollamaList: (params: ServiceOllamaListParams) => void; + ollamaList: (params: ServiceModelListParams) => void; + openAICompatibleList: (params: ServiceModelListParams) => void; } export class LLMMessageService extends Disposable implements ILLMMessageService { @@ -36,9 +37,12 @@ export class LLMMessageService extends Disposable implements ILLMMessageService // ollamaList - private readonly onSuccess_ollama: { [eventId: string]: ((params: EventOllamaListOnSuccessParams) => void) } = {} - private readonly onError_ollama: { [eventId: string]: ((params: EventOllamaListOnErrorParams) => void) } = {} + private readonly onSuccess_ollama: { [eventId: string]: ((params: EventModelListOnSuccessParams) => void) } = {} + private readonly onError_ollama: { [eventId: string]: ((params: EventModelListOnErrorParams) => void) } = {} + // openAICompatibleList + private readonly onSuccess_openAICompatible: { [eventId: string]: ((params: EventModelListOnSuccessParams) => void) } = {} + private readonly onError_openAICompatible: { [eventId: string]: ((params: EventModelListOnErrorParams) => void) } = {} constructor( @IMainProcessService private readonly mainProcessService: IMainProcessService, // used as a renderer (only usable on client side) @@ -65,12 +69,19 @@ export class LLMMessageService extends Disposable implements ILLMMessageService this._onRequestIdDone(e.requestId) })) // ollama - this._register((this.channel.listen('onSuccess_ollama') satisfies Event)(e => { + this._register((this.channel.listen('onSuccess_ollama') satisfies Event>)(e => { this.onSuccess_ollama[e.requestId]?.(e) })) - this._register((this.channel.listen('onError_ollama') satisfies Event)(e => { + this._register((this.channel.listen('onError_ollama') satisfies Event>)(e => { this.onError_ollama[e.requestId]?.(e) })) + // openaiCompatible + this._register((this.channel.listen('onSuccess_openAICompatible') satisfies Event>)(e => { + this.onSuccess_openAICompatible[e.requestId]?.(e) + })) + this._register((this.channel.listen('onError_openAICompatible') satisfies Event>)(e => { + this.onError_openAICompatible[e.requestId]?.(e) + })) } @@ -113,7 +124,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService } - ollamaList = (params: ServiceOllamaListParams) => { + ollamaList = (params: ServiceModelListParams) => { const { onSuccess, onError, ...proxyParams } = params const { settingsOfProvider } = this.voidSettingsService.state @@ -127,7 +138,24 @@ export class LLMMessageService extends Disposable implements ILLMMessageService ...proxyParams, settingsOfProvider, requestId: requestId_, - } satisfies MainOllamaListParams) + } satisfies MainModelListParams) + } + + openAICompatibleList = (params: ServiceModelListParams) => { + const { onSuccess, onError, ...proxyParams } = params + + const { settingsOfProvider } = this.voidSettingsService.state + + // add state for request id + const requestId_ = generateUuid(); + this.onSuccess_openAICompatible[requestId_] = onSuccess + this.onError_openAICompatible[requestId_] = onError + + this.channel.call('openAICompatibleList', { + ...proxyParams, + settingsOfProvider, + requestId: requestId_, + } satisfies MainModelListParams) } diff --git a/src/vs/platform/void/common/llmMessageTypes.ts b/src/vs/platform/void/common/llmMessageTypes.ts index b4e3c54b..5bc92e24 100644 --- a/src/vs/platform/void/common/llmMessageTypes.ts +++ b/src/vs/platform/void/common/llmMessageTypes.ts @@ -97,7 +97,7 @@ export type _InternalSendLLMMessageFnType = (params: { // These are from 'ollama' SDK -interface ModelDetails { +interface OllamaModelDetails { parent_model: string; format: string; family: string; @@ -106,35 +106,44 @@ interface ModelDetails { quantization_level: string; } -export type ModelResponse = { +export type OllamaModelResponse = { name: string; modified_at: Date; size: number; digest: string; - details: ModelDetails; + details: OllamaModelDetails; expires_at: Date; size_vram: number; } +export type OpenaiCompatibleModelResponse = { + id: string; + created: number; + object: 'model'; + owned_by: string; +} + // params to the true list fn -export type OllamaListParams = { +export type ModelListParams = { settingsOfProvider: SettingsOfProvider; - onSuccess: (param: { models: ModelResponse[] }) => void; + onSuccess: (param: { models: modelResponse[] }) => void; onError: (param: { error: string }) => void; } -export type ServiceOllamaListParams = { - onSuccess: (param: { models: ModelResponse[] }) => void; +// params to the service +export type ServiceModelListParams = { + onSuccess: (param: { models: modelResponse[] }) => void; onError: (param: { error: any }) => void; } -type BlockedMainOllamaListParams = 'onSuccess' | 'onError' -export type MainOllamaListParams = Omit & { requestId: string } +type BlockedMainModelListParams = 'onSuccess' | 'onError' +export type MainModelListParams = Omit, BlockedMainModelListParams> & { requestId: string } -export type EventOllamaListOnSuccessParams = Parameters[0] & { requestId: string } -export type EventOllamaListOnErrorParams = Parameters[0] & { requestId: string } +export type EventModelListOnSuccessParams = Parameters['onSuccess']>[0] & { requestId: string } +export type EventModelListOnErrorParams = Parameters['onError']>[0] & { requestId: string } -export type _InternalOllamaListFnType = (params: OllamaListParams) => void + +export type _InternalModelListFnType = (params: ModelListParams) => void diff --git a/src/vs/platform/void/common/refreshModelService.ts b/src/vs/platform/void/common/refreshModelService.ts index 1f6b08f3..09c1dd7f 100644 --- a/src/vs/platform/void/common/refreshModelService.ts +++ b/src/vs/platform/void/common/refreshModelService.ts @@ -9,9 +9,21 @@ import { IVoidSettingsService } from './voidSettingsService.js'; import { ILLMMessageService } from './llmMessageService.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable } from '../../../base/common/lifecycle.js'; +import { ProviderName, SettingsOfProvider } from './voidSettingsTypes.js'; +import { OllamaModelResponse, OpenaiCompatibleModelResponse } from './llmMessageTypes.js'; -export type RefreshModelState = 'done' | 'loading' +export const refreshableProviderNames = ['ollama', 'openAICompatible'] satisfies ProviderName[] + +export type RefreshableProviderName = typeof refreshableProviderNames[number] + +type ModelRefreshState = 'nothing' | 'refreshing' | 'success' +export type RefreshModelStateOfProvider = Record + +const REFRESH_INTERVAL = 5000 // element-wise equals function eq(a: T[], b: T[]): boolean { @@ -23,9 +35,9 @@ function eq(a: T[], b: T[]): boolean { } export interface IRefreshModelService { readonly _serviceBrand: undefined; - refreshOllamaModels(): void; + refreshModels: (providerName: RefreshableProviderName) => Promise; onDidChangeState: Event; - state: RefreshModelState; + state: RefreshModelStateOfProvider; } export const IRefreshModelService = createDecorator('RefreshModelService'); @@ -43,61 +55,84 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ ) { super() - // on mount, refresh ollama models - this.refreshOllamaModels() - - // every time ollama.enabled changes, refresh ollama models, like useEffect - let relevantVals = () => [this.voidSettingsService.state.settingsOfProvider.ollama.enabled, this.voidSettingsService.state.settingsOfProvider.ollama.endpoint] - let prevVals = relevantVals() - this._register( - this.voidSettingsService.onDidChangeState(() => { // we might want to debounce this - const newVals = relevantVals() - if (!eq(prevVals, newVals)) { - this.refreshOllamaModels() - prevVals = newVals - } - }) - ) - - } - - state: RefreshModelState = 'done' - - private _timeoutId: NodeJS.Timeout | null = null - private _cancelTimeout = () => { - if (this._timeoutId) { - clearTimeout(this._timeoutId) - this._timeoutId = null + // on mount, start refreshing models if there are no defaults + const refreshables: { [k in RefreshableProviderName]: (keyof SettingsOfProvider[k])[] } = { + ollama: ['enabled', 'endpoint'], + openAICompatible: ['enabled', 'endpoint', 'apiKey'], } - } - async refreshOllamaModels() { - // cancel any existing poll - this._cancelTimeout() - // if ollama is disabled, obivously done - if (!this.voidSettingsService.state.settingsOfProvider.ollama.enabled) { - this._setState('done') + for (const p in refreshables) { + const providerName = p as keyof typeof refreshables + this.refreshModels(providerName) + + // every time providerName.enabled changes, refresh models too, like useEffect + let relevantVals = () => refreshables[providerName].map(settingName => this.voidSettingsService.state.settingsOfProvider[providerName][settingName]) + let prevVals = relevantVals() + this._register( + this.voidSettingsService.onDidChangeState(() => { // we might want to debounce this + const newVals = relevantVals() + if (!eq(prevVals, newVals)) { + this.refreshModels(providerName) + prevVals = newVals + } + }) + ) + } + + + + + } + + state: RefreshModelStateOfProvider = { + ollama: { state: 'nothing', timeoutId: null }, + openAICompatible: { state: 'nothing', timeoutId: null }, + } + + async refreshModels(providerName: RefreshableProviderName) { + // cancel any existing poll + if (this.state[providerName].timeoutId) { + clearTimeout(this.state[providerName].timeoutId) + this._setTimeoutId(providerName, null) + } + + // if provider is disabled, obivously done + if (!this.voidSettingsService.state.settingsOfProvider[providerName].enabled) { + this._setIsRefreshing(providerName, 'nothing') return } // start loading models - this._setState('loading') + this._setIsRefreshing(providerName, 'refreshing') - this.llmMessageService.ollamaList({ + const fn = providerName === 'ollama' ? this.llmMessageService.ollamaList + : providerName === 'openAICompatible' ? this.llmMessageService.openAICompatibleList + : () => { } + + fn({ onSuccess: ({ models }) => { - this.voidSettingsService.setDefaultModels('ollama', models.map(model => model.name)) - this._setState('done') + this.voidSettingsService.setDefaultModels(providerName, models.map(model => { + if (providerName === 'ollama') return (model as OllamaModelResponse).name + else if (providerName === 'openAICompatible') return (model as OpenaiCompatibleModelResponse).id + else throw new Error('refreshMode fn: unknown provider', providerName) + })) + this._setIsRefreshing(providerName, 'success') }, onError: ({ error }) => { // poll - console.log('retrying ollamaList:', error) - this._timeoutId = setTimeout(() => this.refreshOllamaModels(), 5000) + console.log('retrying list models:', providerName, error) + const timeoutId = setTimeout(() => this.refreshModels(providerName), REFRESH_INTERVAL) + this._setTimeoutId(providerName, timeoutId) } }) } - private _setState(state: RefreshModelState) { - this.state = state + private _setTimeoutId(providerName: RefreshableProviderName, timeoutId: NodeJS.Timeout | null) { + this.state[providerName].timeoutId = timeoutId + } + + private _setIsRefreshing(providerName: RefreshableProviderName, state: ModelRefreshState) { + this.state[providerName].state = state this._onDidChangeState.fire() } } diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 3c53d6f1..893a84b6 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -136,7 +136,7 @@ type CommonProviderSettings = { models: ModelInfo[], // if null, user can type in any string as a model } -type SettingsForProvider = CustomProviderSettings & CommonProviderSettings +export type SettingsForProvider = CustomProviderSettings & CommonProviderSettings // part of state export type SettingsOfProvider = { diff --git a/src/vs/platform/void/electron-main/llmMessage/ollama.ts b/src/vs/platform/void/electron-main/llmMessage/ollama.ts index 03cf29dc..d9184157 100644 --- a/src/vs/platform/void/electron-main/llmMessage/ollama.ts +++ b/src/vs/platform/void/electron-main/llmMessage/ollama.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Ollama } from 'ollama'; -import { _InternalOllamaListFnType, _InternalSendLLMMessageFnType, ModelResponse } from '../../common/llmMessageTypes.js'; +import { _InternalModelListFnType, _InternalSendLLMMessageFnType, OllamaModelResponse } from '../../common/llmMessageTypes.js'; -export const ollamaList: _InternalOllamaListFnType = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => { +export const ollamaList: _InternalModelListFnType = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => { - const onSuccess = ({ models }: { models: ModelResponse[] }) => { + const onSuccess = ({ models }: { models: OllamaModelResponse[] }) => { onSuccess_({ models }) } @@ -16,7 +16,6 @@ export const ollamaList: _InternalOllamaListFnType = async ({ onSuccess: onSucce onError_({ error }) } - try { const thisConfig = settingsOfProvider.ollama const ollama = new Ollama({ host: thisConfig.endpoint }) diff --git a/src/vs/platform/void/electron-main/llmMessage/openai.ts b/src/vs/platform/void/electron-main/llmMessage/openai.ts index 03eb7761..fa6ac5f3 100644 --- a/src/vs/platform/void/electron-main/llmMessage/openai.ts +++ b/src/vs/platform/void/electron-main/llmMessage/openai.ts @@ -4,10 +4,46 @@ *--------------------------------------------------------------------------------------------*/ import OpenAI from 'openai'; -import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js'; +import { _InternalModelListFnType, _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js'; +import { Model } from 'openai/resources/models.js'; // import { parseMaxTokensStr } from './util.js'; + +export const openaiCompatibleList: _InternalModelListFnType = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => { + const onSuccess = ({ models }: { models: Model[] }) => { + onSuccess_({ models }) + } + + const onError = ({ error }: { error: string }) => { + onError_({ error }) + } + + try { + const thisConfig = settingsOfProvider.openAICompatible + const openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }) + + openai.models.list() + .then(async (response) => { + const models: Model[] = [] + models.push(...response.data) + while (response.hasNextPage()) { + models.push(...(await response.getNextPage()).data) + } + onSuccess({ models }) + }) + .catch((error) => { + onError({ error: error + '' }) + }) + } + catch (error) { + onError({ error: error + '' }) + } +} + + + + // OpenAI, OpenRouter, OpenAICompatible export const sendOpenAIMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) => { @@ -43,6 +79,7 @@ export const sendOpenAIMsg: _InternalSendLLMMessageFnType = ({ messages, onText, throw new Error(`providerName was invalid: ${providerName}`) } + openai.models.list() openai.chat.completions .create(options) .then(async response => { diff --git a/src/vs/platform/void/electron-main/llmMessageChannel.ts b/src/vs/platform/void/electron-main/llmMessageChannel.ts index 803539d9..bf08934e 100644 --- a/src/vs/platform/void/electron-main/llmMessageChannel.ts +++ b/src/vs/platform/void/electron-main/llmMessageChannel.ts @@ -8,10 +8,11 @@ import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js'; import { Emitter, Event } from '../../../base/common/event.js'; -import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainLLMMessageParams, AbortRef, LLMMMessageParams, MainLLMMessageAbortParams, MainOllamaListParams, OllamaListParams, EventOllamaListOnSuccessParams, EventOllamaListOnErrorParams } from '../common/llmMessageTypes.js'; +import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainLLMMessageParams, AbortRef, LLMMMessageParams, MainLLMMessageAbortParams, MainModelListParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from '../common/llmMessageTypes.js'; import { sendLLMMessage } from './llmMessage/sendLLMMessage.js' import { IMetricsService } from '../common/metricsService.js'; import { ollamaList } from './llmMessage/ollama.js'; +import { openaiCompatibleList } from './llmMessage/openai.js'; // NODE IMPLEMENTATION - calls actual sendLLMMessage() and returns listeners to it @@ -25,8 +26,12 @@ export class LLMMessageChannel implements IServerChannel { private readonly _abortRefOfRequestId_llm: Record = {} // ollamaList - private readonly _onSuccess_ollama = new Emitter(); - private readonly _onError_ollama = new Emitter(); + private readonly _onSuccess_ollama = new Emitter>(); + private readonly _onError_ollama = new Emitter>(); + + // openaiCompatibleList + private readonly _onSuccess_openAICompatible = new Emitter>(); + private readonly _onError_openAICompatible = new Emitter>(); // stupidly, channels can't take in @IService constructor( @@ -50,6 +55,12 @@ export class LLMMessageChannel implements IServerChannel { else if (event === 'onError_ollama') { return this._onError_ollama.event; } + else if (event === 'onSuccess_openAICompatible') { + return this._onSuccess_openAICompatible.event; + } + else if (event === 'onError_openAICompatible') { + return this._onError_openAICompatible.event; + } else { throw new Error(`Event not found: ${event}`); } @@ -67,6 +78,9 @@ export class LLMMessageChannel implements IServerChannel { else if (command === 'ollamaList') { this._callOllamaList(params) } + else if (command === 'openAICompatibleList') { + this._callOpenAICompatibleList(params) + } else { throw new Error(`Void sendLLM: command "${command}" not recognized.`) } @@ -100,10 +114,10 @@ export class LLMMessageChannel implements IServerChannel { delete this._abortRefOfRequestId_llm[requestId] } - private _callOllamaList(params: MainOllamaListParams) { + private _callOllamaList(params: MainModelListParams) { const { requestId } = params; - const mainThreadParams: OllamaListParams = { + const mainThreadParams: ModelListParams = { ...params, onSuccess: ({ models }) => { this._onSuccess_ollama.fire({ requestId, models }); }, onError: ({ error }) => { this._onError_ollama.fire({ requestId, error }); }, @@ -111,5 +125,16 @@ export class LLMMessageChannel implements IServerChannel { ollamaList(mainThreadParams) } + private _callOpenAICompatibleList(params: MainModelListParams) { + const { requestId } = params; + + const mainThreadParams: ModelListParams = { + ...params, + onSuccess: ({ models }) => { this._onSuccess_openAICompatible.fire({ requestId, models }); }, + onError: ({ error }) => { this._onError_openAICompatible.fire({ requestId, error }); }, + } + openaiCompatibleList(mainThreadParams) + } + } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx index 250363ce..5d85c492 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/services.tsx @@ -6,12 +6,12 @@ import { useState, useEffect } from 'react' import { ThreadsState } from '../../../threadHistoryService.js' import { SettingsOfProvider } from '../../../../../../../platform/void/common/voidSettingsTypes.js' -import { RefreshModelState } from '../../../../../../../platform/void/common/refreshModelService.js' import { IDisposable } from '../../../../../../../base/common/lifecycle.js' import { ReactServicesType } from '../../../helpers/reactServicesHelper.js' import { VoidSidebarState } from '../../../sidebarStateService.js' import { VoidSettingsState } from '../../../../../../../platform/void/common/voidSettingsService.js' import { ColorScheme } from '../../../../../../../platform/theme/common/theme.js' +import { RefreshModelStateOfProvider } from '../../../../../../../platform/void/common/refreshModelService.js' // normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes @@ -29,8 +29,8 @@ const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set() let settingsState: VoidSettingsState const settingsStateListeners: Set<(s: VoidSettingsState) => void> = new Set() -let refreshModelState: RefreshModelState -const refreshModelStateListeners: Set<(s: RefreshModelState) => void> = new Set() +let refreshModelState: RefreshModelStateOfProvider +const refreshModelStateListeners: Set<(s: RefreshModelStateOfProvider) => void> = new Set() let colorThemeState: ColorScheme const colorThemeStateListeners: Set<(s: ColorScheme) => void> = new Set() 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 943b7bb8..7479f76d 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,25 +4,51 @@ import { ProviderName, SettingName, displayInfoOfSettingName, titleOfProviderNam import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' import { VoidInputBox, VoidSelectBox } from '../util/inputs.js' import { useIsDark, useRefreshModelState, useService, useSettingsState } from '../util/services.js' -import { X } from 'lucide-react' +import { X, RefreshCw, Loader2, Check } from 'lucide-react' +import { RefreshableProviderName, refreshableProviderNames } from '../../../../../../../platform/void/common/refreshModelService.js' // models +const RefreshModelButton = ({ providerName }: { providerName: RefreshableProviderName }) => { + const refreshModelState = useRefreshModelState() + const refreshModelService = useService('refreshModelService') + + + const [justFinished, setJustFinished] = useState(false) + const { state } = refreshModelState[providerName] + useEffect(() => { + if (state !== 'success') return + // if no longer refreshing + setJustFinished(true) + const tid = setTimeout(() => { setJustFinished(false) }, 2000) + return () => clearTimeout(tid) + }, [state]) + + const isRefreshing = state === 'refreshing' + + const providerTitle = titleOfProviderName(providerName) + return
+ + Refresh Default Models for {providerTitle}. +
+} const RefreshableModels = () => { const settingsState = useSettingsState() - const refreshModelState = useRefreshModelState() - const refreshModelService = useService('refreshModelService') - if (!settingsState.settingsOfProvider.ollama.enabled) - return null + const buttons = refreshableProviderNames.map(providerName => { + if (!settingsState.settingsOfProvider[providerName].enabled) return null + return + }) + + return <> + {buttons} + - return
- - {refreshModelState === 'loading' ? 'loading...' : 'good!'} -
} @@ -234,10 +260,10 @@ export const Settings = () => { {/* tabs */}
- -