minor style changes and refresh model works in general

This commit is contained in:
Andrew Pareles 2024-12-18 16:06:53 -08:00
parent 8661fe1417
commit f49a6f8e41
10 changed files with 248 additions and 89 deletions

View file

@ -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
},
});
}

View file

@ -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<OllamaModelResponse>) => void;
openAICompatibleList: (params: ServiceModelListParams<OpenaiCompatibleModelResponse>) => 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<OllamaModelResponse>) => void) } = {}
private readonly onError_ollama: { [eventId: string]: ((params: EventModelListOnErrorParams<OllamaModelResponse>) => void) } = {}
// openAICompatibleList
private readonly onSuccess_openAICompatible: { [eventId: string]: ((params: EventModelListOnSuccessParams<OpenaiCompatibleModelResponse>) => void) } = {}
private readonly onError_openAICompatible: { [eventId: string]: ((params: EventModelListOnErrorParams<OpenaiCompatibleModelResponse>) => 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<EventOllamaListOnSuccessParams>)(e => {
this._register((this.channel.listen('onSuccess_ollama') satisfies Event<EventModelListOnSuccessParams<OllamaModelResponse>>)(e => {
this.onSuccess_ollama[e.requestId]?.(e)
}))
this._register((this.channel.listen('onError_ollama') satisfies Event<EventOllamaListOnErrorParams>)(e => {
this._register((this.channel.listen('onError_ollama') satisfies Event<EventModelListOnErrorParams<OllamaModelResponse>>)(e => {
this.onError_ollama[e.requestId]?.(e)
}))
// openaiCompatible
this._register((this.channel.listen('onSuccess_openAICompatible') satisfies Event<EventModelListOnSuccessParams<OpenaiCompatibleModelResponse>>)(e => {
this.onSuccess_openAICompatible[e.requestId]?.(e)
}))
this._register((this.channel.listen('onError_openAICompatible') satisfies Event<EventModelListOnErrorParams<OpenaiCompatibleModelResponse>>)(e => {
this.onError_openAICompatible[e.requestId]?.(e)
}))
}
@ -113,7 +124,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService
}
ollamaList = (params: ServiceOllamaListParams) => {
ollamaList = (params: ServiceModelListParams<OllamaModelResponse>) => {
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<OllamaModelResponse>)
}
openAICompatibleList = (params: ServiceModelListParams<OpenaiCompatibleModelResponse>) => {
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<OpenaiCompatibleModelResponse>)
}

View file

@ -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<modelResponse> = {
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<modelResponse> = {
onSuccess: (param: { models: modelResponse[] }) => void;
onError: (param: { error: any }) => void;
}
type BlockedMainOllamaListParams = 'onSuccess' | 'onError'
export type MainOllamaListParams = Omit<OllamaListParams, BlockedMainOllamaListParams> & { requestId: string }
type BlockedMainModelListParams = 'onSuccess' | 'onError'
export type MainModelListParams<modelResponse> = Omit<ModelListParams<modelResponse>, BlockedMainModelListParams> & { requestId: string }
export type EventOllamaListOnSuccessParams = Parameters<OllamaListParams['onSuccess']>[0] & { requestId: string }
export type EventOllamaListOnErrorParams = Parameters<OllamaListParams['onError']>[0] & { requestId: string }
export type EventModelListOnSuccessParams<modelResponse> = Parameters<ModelListParams<modelResponse>['onSuccess']>[0] & { requestId: string }
export type EventModelListOnErrorParams<modelResponse> = Parameters<ModelListParams<modelResponse>['onError']>[0] & { requestId: string }
export type _InternalOllamaListFnType = (params: OllamaListParams) => void
export type _InternalModelListFnType<modelResponse> = (params: ModelListParams<modelResponse>) => void

View file

@ -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<RefreshableProviderName, {
state: ModelRefreshState,
timeoutId: NodeJS.Timeout | null // not really part of state
}>
const REFRESH_INTERVAL = 5000
// element-wise equals
function eq<T>(a: T[], b: T[]): boolean {
@ -23,9 +35,9 @@ function eq<T>(a: T[], b: T[]): boolean {
}
export interface IRefreshModelService {
readonly _serviceBrand: undefined;
refreshOllamaModels(): void;
refreshModels: (providerName: RefreshableProviderName) => Promise<void>;
onDidChangeState: Event<void>;
state: RefreshModelState;
state: RefreshModelStateOfProvider;
}
export const IRefreshModelService = createDecorator<IRefreshModelService>('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()
}
}

View file

@ -136,7 +136,7 @@ type CommonProviderSettings = {
models: ModelInfo[], // if null, user can type in any string as a model
}
type SettingsForProvider<providerName extends ProviderName> = CustomProviderSettings<providerName> & CommonProviderSettings
export type SettingsForProvider<providerName extends ProviderName> = CustomProviderSettings<providerName> & CommonProviderSettings
// part of state
export type SettingsOfProvider = {

View file

@ -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<OllamaModelResponse> = 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 })

View file

@ -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<Model> = 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 => {

View file

@ -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<string, AbortRef> = {}
// ollamaList
private readonly _onSuccess_ollama = new Emitter<EventOllamaListOnSuccessParams>();
private readonly _onError_ollama = new Emitter<EventOllamaListOnErrorParams>();
private readonly _onSuccess_ollama = new Emitter<EventModelListOnSuccessParams<OllamaModelResponse>>();
private readonly _onError_ollama = new Emitter<EventModelListOnErrorParams<OllamaModelResponse>>();
// openaiCompatibleList
private readonly _onSuccess_openAICompatible = new Emitter<EventModelListOnSuccessParams<OpenaiCompatibleModelResponse>>();
private readonly _onError_openAICompatible = new Emitter<EventModelListOnErrorParams<OpenaiCompatibleModelResponse>>();
// 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<OllamaModelResponse>) {
const { requestId } = params;
const mainThreadParams: OllamaListParams = {
const mainThreadParams: ModelListParams<OllamaModelResponse> = {
...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<OpenaiCompatibleModelResponse>) {
const { requestId } = params;
const mainThreadParams: ModelListParams<OpenaiCompatibleModelResponse> = {
...params,
onSuccess: ({ models }) => { this._onSuccess_openAICompatible.fire({ requestId, models }); },
onError: ({ error }) => { this._onError_openAICompatible.fire({ requestId, error }); },
}
openaiCompatibleList(mainThreadParams)
}
}

View file

@ -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()

View file

@ -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 <div className='flex items-center py-1 px-3 rounded-sm overflow-hidden gap-2 hover:bg-black/10 dark:hover:bg-gray-200/10'>
<button className='flex items-center' disabled={isRefreshing || justFinished} onClick={() => { refreshModelService.refreshModels(providerName) }}>
{isRefreshing ? <Loader2 className='size-3 animate-spin' /> : (justFinished ? <Check className='stroke-green-500 size-3' /> : <RefreshCw className='size-3' />)}
</button>
<span className='opacity-50'>Refresh Default Models for {providerTitle}.</span>
</div>
}
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 <RefreshModelButton key={providerName} providerName={providerName} />
})
return <>
{buttons}
</>
return <div>
<button onClick={() => refreshModelService.refreshOllamaModels()}>refresh Ollama built-in models</button>
{refreshModelState === 'loading' ? 'loading...' : 'good!'}
</div>
}
@ -234,10 +260,10 @@ export const Settings = () => {
{/* tabs */}
<div className='flex flex-col w-full max-w-32'>
<button className={`text-left p-1 my-0.5 rounded-sm overflow-hidden ${tab === 'models' ? 'bg-vscode-button-hover-bg' : 'bg-vscode-button-active-bg'} hover:bg-vscode-button-hover-bg active:bg-vscode-button-active-bg`}
<button className={`text-left p-1 my-0.5 rounded-sm overflow-hidden ${tab === 'models' ? 'bg-black/10 dark:bg-gray-200/10' : ''} hover:bg-black/10 hover:dark:bg-gray-200/10 active:bg-black/10 active:dark:bg-gray-200/10 `}
onClick={() => { setTab('models') }}
>Models</button>
<button className={`text-left p-1 my-0.5 rounded-sm overflow-hidden ${tab === 'features' ? 'bg-vscode-button-hover-bg' : 'bg-vscode-button-active-bg'} hover:bg-vscode-button-hover-bg active:bg-vscode-button-active-bg`}
<button className={`text-left p-1 my-0.5 rounded-sm overflow-hidden ${tab === 'features' ? 'bg-black/10 dark:bg-gray-200/10' : ''} hover:bg-black/10 hover:dark:bg-gray-200/10 active:bg-black/10 active:dark:bg-gray-200/10 `}
onClick={() => { setTab('features') }}
>Features</button>
</div>