From aa835d468b0043f13b24f924893b3962ddea14c4 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Thu, 27 Feb 2025 20:13:50 -0800 Subject: [PATCH] misc --- .../contrib/void/browser/aiRegexService.ts | 221 ++++++------------ .../contrib/void/browser/prompt/prompts.ts | 2 +- .../react/src/markdown/ChatMarkdownRender.tsx | 6 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 6 +- .../void/browser/react/src/util/services.tsx | 3 +- .../contrib/void/browser/sidebarActions.ts | 2 +- .../contrib/void/browser/void.contribution.ts | 6 +- .../{browser => common}/chatThreadService.ts | 95 +------- .../contrib/void/common/llmMessageTypes.ts | 2 +- .../void/common/voidSettingsService.ts | 77 +++--- 10 files changed, 139 insertions(+), 281 deletions(-) rename src/vs/workbench/contrib/void/{browser => common}/chatThreadService.ts (88%) diff --git a/src/vs/workbench/contrib/void/browser/aiRegexService.ts b/src/vs/workbench/contrib/void/browser/aiRegexService.ts index f38236a9..4b2a919a 100644 --- a/src/vs/workbench/contrib/void/browser/aiRegexService.ts +++ b/src/vs/workbench/contrib/void/browser/aiRegexService.ts @@ -3,185 +3,106 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../../base/common/event.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -// import { URI } from '../../../../base/common/uri.js'; -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -// import { IToolService, ToolService } from '../common/toolsService.js'; +// 1. search(ai) +// - tool use to find all possible changes +// - if search only: is this file related to the search? +// - if search + replace: should I modify this file? +// 2. replace(ai) +// - what changes to make? +// 3. postprocess errors +// -fastapply changes simultaneously +// -iterate on syntax errors (all files can be changed from a syntax error, not just the one with the error) +// private async _searchUsingAI({ searchClause }: { searchClause: string }) { -export type ChatMessageLocation = { - threadId: string; - messageIdx: number; -} +// // const relevantURIs: URI[] = [] +// // const gatherPrompt = `\ +// // asdasdas +// // ` +// // const filterPrompt = `\ +// // Is this file relevant? +// // ` -export type SearchAndReplaceBlock = { - search: string; - replace: string; -} +// // // optimizations (DO THESE LATER!!!!!!) +// // // if tool includes a uri in uriSet, skip it obviously +// // let uriSet = new Set() +// // // gather +// // let messages = [] +// // while (true) { +// // const result = await new Promise((res, rej) => { +// // sendLLMMessage({ +// // messages, +// // tools: ['search'], +// // onFinalMessage: ({ result: r, }) => { +// // res(r) +// // }, +// // onError: (error) => { +// // rej(error) +// // } +// // }) +// // }) -// service that manages state -export type ApplyState = { - [applyBoxId: string]: { - searchAndReplaceBlocks: SearchAndReplaceBlock; - } -} +// // messages.push({ role: 'tool', content: turnToString(result) }) -// the purpose of this service is to generate search and replace blocks for a given codeblock `codeblockId` and on a file `fileName` and version `fileVersion` +// // sendLLMMessage({ +// // messages: { 'Output ': result }, +// // onFinalMessage: (r) => { +// // // output is file1\nfile2\nfile3\n... +// // } +// // }) -export interface IFastApplyService { - readonly _serviceBrand: undefined; +// // uriSet.add(...) +// // } - // readonly state: ApplyState; // readonly to the user - // setState(newState: Partial): void; - // onDidChangeState: Event; -} +// // // writes +// // if (!replaceClause) return -export const IVoidFastApplyService = createDecorator('voidFastApplyService'); -class VoidFastApplyService extends Disposable implements IFastApplyService { - _serviceBrand: undefined; - - // static readonly ID = 'voidFastApplyService'; - - private readonly _onDidChangeState = new Emitter(); - readonly onDidChangeState: Event = this._onDidChangeState.event; - - - // state - // state: ApplyState - - constructor( - // @IToolService private readonly toolService: ToolService - ) { - super() - - // initial state - // this.state = { currentUri: undefined } - } - - setState(newState: Partial) { - - // this.state = { ...this.state, ...newState } - this._onDidChangeState.fire() - } - - aiSearch(searchStr: string) { - - } - - aiReplace(searchStr: string, replaceStr: string) { - - } - - - // 1. search(ai) - // - tool use to find all possible changes - // - if search only: is this file related to the search? - // - if search + replace: should I modify this file? - // 2. replace(ai) - // - what changes to make? - // 3. postprocess errors - // -fastapply changes simultaneously - // -iterate on syntax errors (all files can be changed from a syntax error, not just the one with the error) - - - // private async _searchUsingAI({ searchClause }: { searchClause: string }) { - - // // const relevantURIs: URI[] = [] - // // const gatherPrompt = `\ - // // asdasdas - // // ` - // // const filterPrompt = `\ - // // Is this file relevant? - // // ` - - - // // // optimizations (DO THESE LATER!!!!!!) - // // // if tool includes a uri in uriSet, skip it obviously - // // let uriSet = new Set() - // // // gather - // // let messages = [] - // // while (true) { - // // const result = await new Promise((res, rej) => { - // // sendLLMMessage({ - // // messages, - // // tools: ['search'], - // // onFinalMessage: ({ result: r, }) => { - // // res(r) - // // }, - // // onError: (error) => { - // // rej(error) - // // } - // // }) - // // }) - - // // messages.push({ role: 'tool', content: turnToString(result) }) - - // // sendLLMMessage({ - // // messages: { 'Output ': result }, - // // onFinalMessage: (r) => { - // // // output is file1\nfile2\nfile3\n... - // // } - // // }) - - // // uriSet.add(...) - // // } - - // // // writes - // // if (!replaceClause) return - - // // for (const uri of uriSet) { - // // // in future, batch these - // // applyWorkflow({ uri, applyStr: replaceClause }) - // // } +// // for (const uri of uriSet) { +// // // in future, batch these +// // applyWorkflow({ uri, applyStr: replaceClause }) +// // } - // // while (true) { - // // const result = new Promise((res, rej) => { - // // sendLLMMessage({ - // // messages, - // // tools: ['search'], - // // onResult: (r) => { - // // res(r) - // // } - // // }) - // // }) +// // while (true) { +// // const result = new Promise((res, rej) => { +// // sendLLMMessage({ +// // messages, +// // tools: ['search'], +// // onResult: (r) => { +// // res(r) +// // } +// // }) +// // }) - // // messages.push(result) +// // messages.push(result) - // // } +// // } - // } +// } - // private async _replaceUsingAI({ searchClause, replaceClause, relevantURIs }: { searchClause: string, replaceClause: string, relevantURIs: URI[] }) { +// private async _replaceUsingAI({ searchClause, replaceClause, relevantURIs }: { searchClause: string, replaceClause: string, relevantURIs: URI[] }) { - // for (const uri of relevantURIs) { +// for (const uri of relevantURIs) { - // uri +// uri - // } +// } - // // should I change this file? - // // if so what changes to make? +// // should I change this file? +// // if so what changes to make? - // // fast apply the changes - // } +// // fast apply the changes +// } - - -} - -registerSingleton(IVoidFastApplyService, VoidFastApplyService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts index 90e01d50..22d0ad52 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts @@ -6,7 +6,7 @@ import { URI } from '../../../../../base/common/uri.js'; import { filenameToVscodeLanguage } from '../helpers/detectLanguage.js'; -import { CodeSelection, StagingSelectionItem, FileSelection } from '../chatThreadService.js'; +import { CodeSelection, StagingSelectionItem, FileSelection } from '../../common/chatThreadService.js'; import { IModelService } from '../../../../../editor/common/services/model.js'; import { os } from '../helpers/systemInfo.js'; import { IVoidFileService } from '../../common/voidFileService.js'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index 1aa55e61..c8571403 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -6,10 +6,14 @@ import React, { JSX } from 'react' import { marked, MarkedToken, Token } from 'marked' import { BlockCode } from './BlockCode.js' -import { ChatMessageLocation, } from '../../../aiRegexService.js' import { nameToVscodeLanguage } from '../../../helpers/detectLanguage.js' import { ApplyBlockHoverButtons } from './ApplyBlockHoverButtons.js' +export type ChatMessageLocation = { + threadId: string; + messageIdx: number; +} + type ApplyBoxLocation = ChatMessageLocation & { tokenIdx: string } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 0d5cee5b..5c413b30 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -7,10 +7,10 @@ import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, K import { useAccessor, useSidebarState, useChatThreadsState, useChatThreadsStreamState, useUriState, useSettingsState } from '../util/services.js'; -import { ChatMessage, StagingSelectionItem, ToolMessage } from '../../../chatThreadService.js'; +import { ChatMessage, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadService.js'; import { BlockCode } from '../markdown/BlockCode.js'; -import { ChatMarkdownRender } from '../markdown/ChatMarkdownRender.js'; +import { ChatMarkdownRender, ChatMessageLocation } from '../markdown/ChatMarkdownRender.js'; import { URI } from '../../../../../../../base/common/uri.js'; import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; import { ErrorDisplay } from './ErrorDisplay.js'; @@ -24,7 +24,7 @@ import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js'; import { ChevronRight, Pencil, X } from 'lucide-react'; import { FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js'; import { WarningBox } from '../void-settings-tsx/WarningBox.js'; -import { ChatMessageLocation } from '../../../aiRegexService.js'; + import { ToolCallReturnType, ToolName } from '../../../../common/toolsService.js'; 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 68de21ec..9898ef52 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 @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------*/ import React, { useState, useEffect, useCallback } from 'react' -import { ThreadStreamState, ThreadsState } from '../../../chatThreadService.js' +import { ThreadStreamState,IChatThreadService, ThreadsState } from '../../../../common/chatThreadService.js' import { RefreshableProviderName, SettingsOfProvider } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js' import { IDisposable } from '../../../../../../../base/common/lifecycle.js' import { VoidSidebarState } from '../../../sidebarStateService.js' @@ -29,7 +29,6 @@ import { IEditCodeService, URIStreamState } from '../../../editCodeService.js'; import { IVoidUriStateService } from '../../../voidUriStateService.js'; import { IQuickEditStateService } from '../../../quickEditStateService.js'; import { ISidebarStateService } from '../../../sidebarStateService.js'; -import { IChatThreadService } from '../../../chatThreadService.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js' import { ICodeEditorService } from '../../../../../../../editor/browser/services/codeEditorService.js' import { ICommandService } from '../../../../../../../platform/commands/common/commands.js' diff --git a/src/vs/workbench/contrib/void/browser/sidebarActions.ts b/src/vs/workbench/contrib/void/browser/sidebarActions.ts index 722eaa57..499c9887 100644 --- a/src/vs/workbench/contrib/void/browser/sidebarActions.ts +++ b/src/vs/workbench/contrib/void/browser/sidebarActions.ts @@ -11,7 +11,7 @@ import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; -import { StagingSelectionItem, IChatThreadService } from './chatThreadService.js'; +import { StagingSelectionItem, IChatThreadService } from '../common/chatThreadService.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IRange } from '../../../../editor/common/core/range.js'; diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index b179e603..f9f6a29b 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -15,8 +15,6 @@ import './sidebarStateService.js' // register quick edit (Ctrl+K) import './quickEditActions.js' -// register Thread History -import './chatThreadService.js' // register Autocomplete import './autocompleteService.js' @@ -56,3 +54,7 @@ import '../common/voidUpdateService.js' // tools import '../common/toolsService.js' + +// register Thread History +import '../common/chatThreadService.js' + diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/common/chatThreadService.ts similarity index 88% rename from src/vs/workbench/contrib/void/browser/chatThreadService.ts rename to src/vs/workbench/contrib/void/common/chatThreadService.ts index 3a9f4156..57d9dfe1 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/common/chatThreadService.ts @@ -11,12 +11,12 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IRange } from '../../../../editor/common/core/range.js'; -import { ILLMMessageService } from '../common/llmMessageService.js'; -import { chat_userMessageContent, chat_systemMessage, chat_userMessageContentWithAllFilesToo as chat_userMessageContentWithAllFiles, chat_selectionsString } from './prompt/prompts.js'; -import { InternalToolInfo, IToolsService, ToolCallReturnType, ToolFns, ToolName, voidTools } from '../common/toolsService.js'; -import { toLLMChatMessage } from '../common/llmMessageTypes.js'; +import { ILLMMessageService } from './llmMessageService.js'; +import { chat_userMessageContent, chat_systemMessage, chat_userMessageContentWithAllFilesToo as chat_userMessageContentWithAllFiles, chat_selectionsString } from '../browser/prompt/prompts.js'; +import { InternalToolInfo, IToolsService, ToolCallReturnType, ToolFns, ToolName, voidTools } from './toolsService.js'; +import { toLLMChatMessage } from './llmMessageTypes.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { IVoidFileService } from '../common/voidFileService.js'; +import { IVoidFileService } from './voidFileService.js'; import { generateUuid } from '../../../../base/common/uuid.js'; @@ -101,7 +101,6 @@ export type ChatThreads = { type ThreadType = ChatThreads[string] -const defaultThreadState: ThreadType['state'] = { stagingSelections: [], focusedMessageIdx: undefined, isCheckedOfSelectionId: {} } export type ThreadsState = { allThreads: ChatThreads; @@ -134,10 +133,7 @@ const newThreadObject = () => { } satisfies ChatThreads[string] } -const THREAD_VERSION_KEY = 'void.chatThreadVersion' -const LATEST_THREAD_VERSION = 'v2' - -const THREAD_STORAGE_KEY = 'void.chatThreadStorage' +export const THREAD_STORAGE_KEY = 'void.chatThreadStorage' type ChatMode = 'agent' | 'chat' @@ -200,35 +196,11 @@ class ChatThreadService extends Disposable implements IChatThreadService { @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, ) { super() + this.state = { allThreads: {}, currentThreadId: null as unknown as string } // default state + const readThreads = this._readAllThreads() || {} - setInterval(() => { - const thread = this.getCurrentThread() - if (!thread) return - - // print out all staging selections for all messages - for (const message of thread.messages) { - if (message.role === 'user' && message.state.stagingSelections.length > 0) { - console.log('Message staging selections:', message.state.stagingSelections) - } - } - // also print thread-level staging selections - if (thread.state.stagingSelections.length > 0) { - console.log('Thread staging selections:', thread.state.stagingSelections) - } - }, 1000) - - const oldVersionNum = this._storageService.get(THREAD_VERSION_KEY, StorageScope.APPLICATION) - - - const readThreads = this._readAllThreads() - const updatedThreads = this._updatedThreadsToVersion(readThreads, oldVersionNum) - - if (updatedThreads !== null) { - this._storeAllThreads(updatedThreads) - } - - const allThreads = updatedThreads ?? readThreads + const allThreads = readThreads this.state = { allThreads: allThreads, currentThreadId: null as unknown as string, // gets set in startNewThread() @@ -236,9 +208,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { // always be in a thread this.openNewThread() - - this._storageService.store(THREAD_VERSION_KEY, LATEST_THREAD_VERSION, StorageScope.APPLICATION, StorageTarget.USER) - } // !!! this is important for properly restoring URIs from storage @@ -251,10 +220,10 @@ class ChatThreadService extends Disposable implements IChatThreadService { }); } - private _readAllThreads(): ChatThreads { + private _readAllThreads(): ChatThreads | null { const threadsStr = this._storageService.get(THREAD_STORAGE_KEY, StorageScope.APPLICATION); if (!threadsStr) { - return {}; + return null } return this._convertThreadDataFromStorage(threadsStr); } @@ -270,48 +239,6 @@ class ChatThreadService extends Disposable implements IChatThreadService { } - // returns if should update - private _updatedThreadsToVersion(oldThreadsObject: any, oldVersion: string | undefined): ChatThreads | null { - - if (!oldVersion) { - - // unknown, just reset chat? - return null - } - - /** v1 -> v2 - - threads.state.currentStagingSelections: CodeStagingSelection[] | null; - + thread[threadIdx].state - + message.state - + chatMessage.staging: StagingInfo | null - */ - else if (oldVersion === 'v1') { - const threads = oldThreadsObject as Omit - // update the threads - for (const thread of Object.values(threads)) { - if (!thread.state) { - thread.state = defaultThreadState - } - for (const chatMessage of Object.values(thread.messages)) { - if (chatMessage.role === 'user' && !chatMessage.state) { - chatMessage.state = defaultMessageState - } - } - } - - // push the update - return threads - } - else if (oldVersion === 'v2') { - return null - } - - // up to date - return null - - } - - // this should be the only place this.state = ... appears besides constructor private _setState(state: Partial, affectsCurrent: boolean) { this.state = { diff --git a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts index e8800562..4f767928 100644 --- a/src/vs/workbench/contrib/void/common/llmMessageTypes.ts +++ b/src/vs/workbench/contrib/void/common/llmMessageTypes.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { ChatMessage } from '../browser/chatThreadService.js' +import { ChatMessage } from './chatThreadService.js' import { InternalToolInfo, ToolName } from './toolsService.js' import { FeatureName, ProviderName, SettingsOfProvider } from './voidSettingsTypes.js' diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts index 72095c83..98ab45fc 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts @@ -28,7 +28,7 @@ type SetModelSelectionOfFeatureFn = ( options?: { doNotApplyEffects?: true } ) => Promise; -type SetGlobalSettingFn = (settingName: T, newVal: GlobalSettings[T]) => void; +type SetGlobalSettingFn = (settingName: T, newVal: GlobalSettings[T]) => void; export type ModelOption = { name: string, selection: ModelSelection } @@ -49,6 +49,8 @@ export interface IVoidSettingsService { readonly state: VoidSettingsState; // in order to play nicely with react, you should immutably change state readonly waitForInitState: Promise; + readAndInitializeState: (providedState?: VoidSettingsState) => Promise; + onDidChangeState: Event; setSettingOfProvider: SetSettingOfProviderFn; @@ -168,6 +170,8 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes state: VoidSettingsState; + + private readonly _resolver: () => void waitForInitState: Promise // await this if you need a valid state initially constructor( @@ -181,56 +185,57 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // at the start, we haven't read the partial config yet, but we need to set state to something this.state = defaultState() - let resolver: () => void = () => { } this.waitForInitState = new Promise((res, rej) => resolver = res) + this._resolver = resolver - // read and update the actual state immediately - this._readState().then(readS => { + this.readAndInitializeState() + } - // the stored data structure might be outdated, so we need to update it here (can do a more general solution later when we need to) - const newSettingsOfProvider = { - // A HACK BECAUSE WE ADDED DEEPSEEK (did not exist before, comes before readS) - ...{ deepseek: defaultSettingsOfProvider.deepseek }, + async readAndInitializeState(providedState?: VoidSettingsState) { + // If providedState is given, use it instead of reading from storage + const readS = providedState || await this._readState(); - // A HACK BECAUSE WE ADDED XAI (did not exist before, comes before readS) - ...{ xAI: defaultSettingsOfProvider.xAI }, + // the stored data structure might be outdated, so we need to update it here + const newSettingsOfProvider = { + // A HACK BECAUSE WE ADDED DEEPSEEK (did not exist before, comes before readS) + ...{ deepseek: defaultSettingsOfProvider.deepseek }, - // A HACK BECAUSE WE ADDED VLLM (did not exist before, comes before readS) - ...{ vLLM: defaultSettingsOfProvider.vLLM }, + // A HACK BECAUSE WE ADDED XAI (did not exist before, comes before readS) + ...{ xAI: defaultSettingsOfProvider.xAI }, + // A HACK BECAUSE WE ADDED VLLM (did not exist before, comes before readS) + ...{ vLLM: defaultSettingsOfProvider.vLLM }, - ...readS.settingsOfProvider, + ...readS.settingsOfProvider, - // A HACK BECAUSE WE ADDED NEW GEMINI MODELS (existed before, comes after readS) - gemini: { - ...readS.settingsOfProvider.gemini, - models: [ - ...readS.settingsOfProvider.gemini.models, - ...defaultSettingsOfProvider.gemini.models.filter(m => /* if cant find the model in readS (yes this is O(n^2), very small) */ !readS.settingsOfProvider.gemini.models.find(m2 => m2.modelName === m.modelName)) - ] - } + // A HACK BECAUSE WE ADDED NEW GEMINI MODELS (existed before, comes after readS) + gemini: { + ...readS.settingsOfProvider.gemini, + models: [ + ...readS.settingsOfProvider.gemini.models, + ...defaultSettingsOfProvider.gemini.models.filter(m => /* if cant find the model in readS (yes this is O(n^2), very small) */ !readS.settingsOfProvider.gemini.models.find(m2 => m2.modelName === m.modelName)) + ] } + }; - const newModelSelectionOfFeature = { - // A HACK BECAUSE WE ADDED FastApply - ...{ 'Apply': null }, - ...readS.modelSelectionOfFeature, - } + const newModelSelectionOfFeature = { + // A HACK BECAUSE WE ADDED FastApply + ...{ 'Apply': null }, + ...readS.modelSelectionOfFeature, + }; - readS = { - ...readS, - settingsOfProvider: newSettingsOfProvider, - modelSelectionOfFeature: newModelSelectionOfFeature, - } + const finalState = { + ...readS, + settingsOfProvider: newSettingsOfProvider, + modelSelectionOfFeature: newModelSelectionOfFeature, + }; - this.state = _validatedState(readS) - - resolver() - this._onDidChangeState.fire() - }) + this.state = _validatedState(finalState); + this._resolver(); + this._onDidChangeState.fire(); }