From bb4f99f6873a917a0498fe177e97a61b97fb09dd Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 10 Dec 2024 23:29:39 -0800 Subject: [PATCH] error state, sendLLMMessage fix, continue adding state for provider/model pair selection --- src/vs/code/electron-main/app.ts | 1 - .../void/browser/llmMessageService.ts | 28 ++++++-- .../platform/void/common/llmMessageTypes.ts | 46 +++++++++---- src/vs/platform/void/common/metricsService.ts | 5 ++ .../void/common/voidConfigService.ts} | 19 +++--- .../{configTypes.ts => voidConfigTypes.ts} | 0 .../electron-main/llmMessage/anthropic.ts | 9 +-- .../void/electron-main/llmMessage/gemini.ts | 8 +-- .../void/electron-main/llmMessage/greptile.ts | 6 +- .../void/electron-main/llmMessage/groq.ts | 8 +-- .../void/electron-main/llmMessage/ollama.ts | 8 +-- .../void/electron-main/llmMessage/openai.ts | 16 ++--- .../llmMessage/sendLLMMessage.ts | 16 ++--- .../void/electron-main/llmMessage/util.ts | 13 ---- .../void/electron-main/llmMessageChannel.ts | 11 ++-- .../windows/electron-main/windowImpl.ts | 12 ---- .../contrib/void/browser/media/void.css | 7 +- .../void/browser/prompt/stringifyFiles.ts | 4 ++ .../void/browser/prompt/systemPrompts.ts | 31 ++++++++- .../react/src/markdown/ChatMarkdownRender.tsx | 7 +- .../react/src/sidebar-tsx/ErrorBoundary.tsx | 65 +++++++++++++++++++ .../react/src/sidebar-tsx/ErrorDisplay.tsx | 5 ++ .../browser/react/src/sidebar-tsx/Sidebar.tsx | 17 +++-- .../react/src/sidebar-tsx/SidebarChat.tsx | 5 +- .../src/sidebar-tsx/SidebarModelSettings.tsx | 26 ++++++-- .../sidebar-tsx/SidebarProviderSettings.tsx | 33 ++++------ .../src/sidebar-tsx/SidebarThreadSelector.tsx | 1 + .../browser/react/src/sidebar-tsx/inputs.tsx | 7 +- .../void/browser/react/src/util/diffLines.tsx | 5 ++ .../react/src/util/mountFnGenerator.tsx | 5 ++ .../void/browser/react/src/util/services.tsx | 35 +++++----- .../contrib/void/browser/registerActions.ts | 1 - .../void/browser/registerAutocomplete.ts | 8 +-- .../void/browser/registerInlineDiffs.ts | 56 ++++------------ .../contrib/void/browser/registerSidebar.ts | 2 +- .../contrib/void/browser/void.contribution.ts | 2 +- src/vs/workbench/workbench.common.main.ts | 1 + 37 files changed, 333 insertions(+), 196 deletions(-) rename src/vs/{workbench/contrib/void/browser/registerConfig.ts => platform/void/common/voidConfigService.ts} (85%) rename src/vs/platform/void/common/{configTypes.ts => voidConfigTypes.ts} (100%) create mode 100644 src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 2cd9bffa..afb593cc 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -1245,7 +1245,6 @@ export class CodeApplication extends Disposable { // Void const metricsChannel = ProxyChannel.fromService(accessor.get(IMetricsService), disposables); mainProcessElectronServer.registerChannel('void-channel-metrics', metricsChannel); - const sendLLMMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService)); mainProcessElectronServer.registerChannel('void-channel-sendLLMMessage', sendLLMMessageChannel); diff --git a/src/vs/platform/void/browser/llmMessageService.ts b/src/vs/platform/void/browser/llmMessageService.ts index a31d56b8..cbbf1f2c 100644 --- a/src/vs/platform/void/browser/llmMessageService.ts +++ b/src/vs/platform/void/browser/llmMessageService.ts @@ -11,6 +11,8 @@ import { generateUuid } from '../../../base/common/uuid.js'; import { createDecorator } from '../../instantiation/common/instantiation.js'; import { Event } from '../../../base/common/event.js'; import { Disposable } from '../../../base/common/lifecycle.js'; +import { IVoidConfigStateService } from '../common/voidConfigService.js'; +import { INotificationService } from '../../notification/common/notification.js'; // BROWSER IMPLEMENTATION OF SENDLLMMESSAGE @@ -19,7 +21,7 @@ export const ISendLLMMessageService = createDecorator('s // defines an interface that node/ creates and browser/ uses export interface ISendLLMMessageService { readonly _serviceBrand: undefined; - sendLLMMessage: (params: LLMMessageServiceParams) => string; + sendLLMMessage: (params: LLMMessageServiceParams) => string | null; abort: (requestId: string) => void; } @@ -34,12 +36,14 @@ export class SendLLMMessageService extends Disposable implements ISendLLMMessage private readonly onErrorHooks: { [eventId: string]: ((params: ProxyOnErrorPayload) => void) } = {} constructor( - @IMainProcessService mainProcessService: IMainProcessService // used as a renderer (only usable on client side) + @IMainProcessService private readonly mainProcessService: IMainProcessService, // used as a renderer (only usable on client side) + @IVoidConfigStateService private readonly voidConfigStateService: IVoidConfigStateService, + @INotificationService private readonly notificationService: INotificationService, ) { super() // const service = ProxyChannel.toService(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service - this.channel = mainProcessService.getChannel('void-channel-sendLLMMessage') + this.channel = this.mainProcessService.getChannel('void-channel-sendLLMMessage') // this sets up an IPC channel and takes a few ms, so we set up listeners immediately and add hooks to them instead const onTextEvent: Event = this.channel.listen('onText') @@ -77,9 +81,25 @@ export class SendLLMMessageService extends Disposable implements ISendLLMMessage this.onFinalMessageHooks[requestId_] = onFinalMessage this.onErrorHooks[requestId_] = onError + const { featureName } = params // params will be stripped of all its functions - this.channel.call('sendLLMMessage', { ...proxyParams, requestId: requestId_ } satisfies ProxyLLMMessageParams); + const stateOfFeature = this.voidConfigStateService.state.modelSelectionOfFeature[featureName] + if (stateOfFeature === null) { + this.notificationService.warn('Please add a Provider in Settings!') + return null + } + const { providerName, modelName } = stateOfFeature + + const { settingsOfProvider } = this.voidConfigStateService.state + + this.channel.call('sendLLMMessage', { + ...proxyParams, + requestId: requestId_, + providerName, + modelName, + settingsOfProvider, + } satisfies ProxyLLMMessageParams); return requestId_ } diff --git a/src/vs/platform/void/common/llmMessageTypes.ts b/src/vs/platform/void/common/llmMessageTypes.ts index aad810f3..3c76b644 100644 --- a/src/vs/platform/void/common/llmMessageTypes.ts +++ b/src/vs/platform/void/common/llmMessageTypes.ts @@ -3,11 +3,10 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { ProviderName, VoidProviderState } from './configTypes' +import { IRange } from '../../../editor/common/core/range' +import { ProviderName, SettingsOfProvider } from './voidConfigTypes' -// ---------- type definitions ---------- - export type OnText = (p: { newText: string, fullText: string }) => void export type OnFinalMessage = (p: { fullText: string }) => void @@ -21,42 +20,67 @@ export type LLMMessage = { content: string; } +export type LLMFeatureSelection = { + featureName: 'Ctrl+K', + range: IRange +} | { + featureName: 'Ctrl+L', +} | { + featureName: 'Autocomplete', + range: IRange +} + export type LLMMessageServiceParams = { onText: OnText; onFinalMessage: OnFinalMessage; onError: OnError; messages: LLMMessage[]; - voidConfig: VoidProviderState | null; logging: { loggingName: string, }; - providerName: ProviderName; - -} +} & LLMFeatureSelection +// params to the true sendLLMMessage function export type SendLLMMMessageParams = { onText: OnText; onFinalMessage: OnFinalMessage; onError: OnError; + abortRef: AbortRef; messages: LLMMessage[]; - voidConfig: VoidProviderState | null; logging: { loggingName: string, }; providerName: ProviderName; - abortRef: AbortRef; + modelName: string; + settingsOfProvider: SettingsOfProvider; } // can't send functions across a proxy, use listeners instead -export const listenerNames = ['onText', 'onFinalMessage', 'onError'] as const -export type ProxyLLMMessageParams = Omit & { requestId: string } +export type BlockedProxyParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef' +export type ProxyLLMMessageParams = Omit & { requestId: string } export type ProxyOnTextPayload = Parameters[0] & { requestId: string } export type ProxyOnFinalMessagePayload = Parameters[0] & { requestId: string } export type ProxyOnErrorPayload = Parameters[0] & { requestId: string } export type ProxyLLMMessageAbortParams = { requestId: string } + + + + + +export type SendLLMMessageFnTypeInternal = (params: { + messages: LLMMessage[]; + onText: OnText; + onFinalMessage: OnFinalMessage; + onError: OnError; + settingsOfProvider: SettingsOfProvider; + providerName: ProviderName; + modelName: string; + + _setAborter: (aborter: () => void) => void; +}) => void diff --git a/src/vs/platform/void/common/metricsService.ts b/src/vs/platform/void/common/metricsService.ts index 2459214e..fa1deacc 100644 --- a/src/vs/platform/void/common/metricsService.ts +++ b/src/vs/platform/void/common/metricsService.ts @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import { createDecorator } from '../../instantiation/common/instantiation.js'; export interface IMetricsService { diff --git a/src/vs/workbench/contrib/void/browser/registerConfig.ts b/src/vs/platform/void/common/voidConfigService.ts similarity index 85% rename from src/vs/workbench/contrib/void/browser/registerConfig.ts rename to src/vs/platform/void/common/voidConfigService.ts index a2a3e504..12b4676d 100644 --- a/src/vs/workbench/contrib/void/browser/registerConfig.ts +++ b/src/vs/platform/void/common/voidConfigService.ts @@ -3,15 +3,14 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../../base/common/event.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { deepClone } from '../../../../base/common/objects.js'; -import { IEncryptionService } from '../../../../platform/encryption/common/encryptionService.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { defaultVoidProviderState, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider } from '../../../../platform/void/common/configTypes.js'; - +import { Emitter, Event } from '../../../base/common/event.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { deepClone } from '../../../base/common/objects.js'; +import { IEncryptionService } from '../../encryption/common/encryptionService.js'; +import { registerSingleton, InstantiationType } from '../../instantiation/common/extensions.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js'; +import { defaultVoidProviderState, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider } from './voidConfigTypes.js'; const CONFIG_STORAGE_KEY = 'void.voidConfigStateII' @@ -89,7 +88,7 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi private async _storeVoidConfigState(voidConfigState: VoidConfigState) { const encryptedVoidConfigStr = await this._encryptionService.encrypt(JSON.stringify(voidConfigState)) - this._storageService.store(CONFIG_STORAGE_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER) + this._storageService.store(CONFIG_STORAGE_KEY, encryptedVoidConfigStr, StorageScope.APPLICATION, StorageTarget.USER); } setSettingOfProvider: SetSettingOfProviderFn = async (providerName, option, newVal) => { diff --git a/src/vs/platform/void/common/configTypes.ts b/src/vs/platform/void/common/voidConfigTypes.ts similarity index 100% rename from src/vs/platform/void/common/configTypes.ts rename to src/vs/platform/void/common/voidConfigTypes.ts diff --git a/src/vs/platform/void/electron-main/llmMessage/anthropic.ts b/src/vs/platform/void/electron-main/llmMessage/anthropic.ts index 9e433bec..f09e807f 100644 --- a/src/vs/platform/void/electron-main/llmMessage/anthropic.ts +++ b/src/vs/platform/void/electron-main/llmMessage/anthropic.ts @@ -1,14 +1,15 @@ import Anthropic from '@anthropic-ai/sdk'; -import { parseMaxTokensStr, SendLLMMessageFnTypeInternal } from './util.js'; +import { parseMaxTokensStr } from './util.js'; +import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js'; // Anthropic type LLMMessageAnthropic = { role: 'user' | 'assistant'; content: string; } -export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { +export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => { - const thisConfig = voidConfig.anthropic + const thisConfig = settingsOfProvider.anthropic const anthropic = new Anthropic({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }); @@ -24,7 +25,7 @@ export const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onTex const stream = anthropic.messages.stream({ system: systemMessage, messages: anthropicMessages, - model: thisConfig.model, + model: modelName, max_tokens: parseMaxTokensStr(thisConfig.maxTokens)!, // this might be undefined, but it will just throw an error for the user to see }); diff --git a/src/vs/platform/void/electron-main/llmMessage/gemini.ts b/src/vs/platform/void/electron-main/llmMessage/gemini.ts index ebefa6d8..aa4e2bdc 100644 --- a/src/vs/platform/void/electron-main/llmMessage/gemini.ts +++ b/src/vs/platform/void/electron-main/llmMessage/gemini.ts @@ -1,15 +1,15 @@ import { Content, GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai'; -import { SendLLMMessageFnTypeInternal } from './util'; +import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js'; // Gemini -export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { +export const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => { let fullText = '' - const thisConfig = voidConfig.gemini + const thisConfig = settingsOfProvider.gemini const genAI = new GoogleGenerativeAI(thisConfig.apiKey); - const model = genAI.getGenerativeModel({ model: thisConfig.model }); + const model = genAI.getGenerativeModel({ model: modelName }); // remove system messages that get sent to Gemini // str of all system messages diff --git a/src/vs/platform/void/electron-main/llmMessage/greptile.ts b/src/vs/platform/void/electron-main/llmMessage/greptile.ts index e0c993ac..43452334 100644 --- a/src/vs/platform/void/electron-main/llmMessage/greptile.ts +++ b/src/vs/platform/void/electron-main/llmMessage/greptile.ts @@ -3,13 +3,13 @@ // // https://docs.greptile.com/api-reference/query // // https://docs.greptile.com/quickstart#sample-response-streamed -// import { SendLLMMessageFnTypeInternal } from './util'; +// import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js'; -// export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { +// export const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, _setAborter }) => { // let fullText = '' -// const thisConfig = voidConfig.greptile +// const thisConfig = settingsOfProvider.greptile // fetch('https://api.greptile.com/v2/query', { // method: 'POST', diff --git a/src/vs/platform/void/electron-main/llmMessage/groq.ts b/src/vs/platform/void/electron-main/llmMessage/groq.ts index 7fbc3459..6514b722 100644 --- a/src/vs/platform/void/electron-main/llmMessage/groq.ts +++ b/src/vs/platform/void/electron-main/llmMessage/groq.ts @@ -1,12 +1,12 @@ import Groq from 'groq-sdk'; -import { SendLLMMessageFnTypeInternal } from './util'; +import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js'; import { parseMaxTokensStr } from './util.js'; // Groq -export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { +export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => { let fullText = ''; - const thisConfig = voidConfig.groq + const thisConfig = settingsOfProvider.groq const groq = new Groq({ apiKey: thisConfig.apiKey, @@ -16,7 +16,7 @@ export const sendGroqMsg: SendLLMMessageFnTypeInternal = async ({ messages, onTe await groq.chat.completions .create({ messages: messages, - model: thisConfig.model, + model: modelName, stream: true, temperature: 0.7, max_tokens: parseMaxTokensStr(thisConfig.maxTokens), diff --git a/src/vs/platform/void/electron-main/llmMessage/ollama.ts b/src/vs/platform/void/electron-main/llmMessage/ollama.ts index 4e5bf52d..f5e5a56c 100644 --- a/src/vs/platform/void/electron-main/llmMessage/ollama.ts +++ b/src/vs/platform/void/electron-main/llmMessage/ollama.ts @@ -1,18 +1,18 @@ import { Ollama } from 'ollama'; -import { SendLLMMessageFnTypeInternal } from './util'; +import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js'; import { parseMaxTokensStr } from './util.js'; // Ollama -export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => { +export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => { - const thisConfig = voidConfig.ollama + const thisConfig = settingsOfProvider.ollama let fullText = '' const ollama = new Ollama({ host: thisConfig.endpoint }) ollama.chat({ - model: thisConfig.model, + model: modelName, messages: messages, stream: true, options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens diff --git a/src/vs/platform/void/electron-main/llmMessage/openai.ts b/src/vs/platform/void/electron-main/llmMessage/openai.ts index 89f5f40c..9b6128fb 100644 --- a/src/vs/platform/void/electron-main/llmMessage/openai.ts +++ b/src/vs/platform/void/electron-main/llmMessage/openai.ts @@ -1,10 +1,10 @@ import OpenAI from 'openai'; -import { SendLLMMessageFnTypeInternal } from './util'; +import { SendLLMMessageFnTypeInternal } from '../../common/llmMessageTypes.js'; import { parseMaxTokensStr } from './util.js'; // OpenAI, OpenRouter, OpenAICompatible -export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }) => { +export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) => { let fullText = '' @@ -13,12 +13,12 @@ export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, if (providerName === 'openAI') { - const thisConfig = voidConfig.openAI + const thisConfig = settingsOfProvider.openAI openai = new OpenAI({ apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }); - options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } + options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } } else if (providerName === 'openRouter') { - const thisConfig = voidConfig.openRouter + const thisConfig = settingsOfProvider.openRouter openai = new OpenAI({ baseURL: 'https://openrouter.ai/api/v1', apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true, defaultHeaders: { @@ -26,12 +26,12 @@ export const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, 'X-Title': 'Void Editor', // Optional. Shows in rankings on openrouter.ai. }, }); - options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } + options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } } else if (providerName === 'openAICompatible') { - const thisConfig = voidConfig.openAICompatible + const thisConfig = settingsOfProvider.openAICompatible openai = new OpenAI({ baseURL: thisConfig.endpoint, apiKey: thisConfig.apiKey, dangerouslyAllowBrowser: true }) - options = { model: thisConfig.model, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } + options = { model: modelName, messages: messages, stream: true, max_completion_tokens: parseMaxTokensStr(thisConfig.maxTokens) } } else { console.error(`sendOpenAIMsg: invalid providerName: ${providerName}`) diff --git a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts index 664dc1c8..2e0fd71c 100644 --- a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts @@ -13,14 +13,14 @@ export const sendLLMMessage = ({ onFinalMessage: onFinalMessage_, onError: onError_, abortRef: abortRef_, - voidConfig, logging: { loggingName }, - providerName + settingsOfProvider, + providerName, + modelName, }: SendLLMMMessageParams, metricsService: IMetricsService ) => { - if (!voidConfig) return; // trim message content (Anthropic and other providers give an error if there is trailing whitespace) messages = messages.map(m => ({ ...m, content: m.content.trim() })) @@ -74,21 +74,21 @@ export const sendLLMMessage = ({ try { switch (providerName) { case 'anthropic': - sendAnthropicMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }); + sendAnthropicMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }); break; case 'openAI': case 'openRouter': case 'openAICompatible': - sendOpenAIMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }); + sendOpenAIMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }); break; case 'gemini': - sendGeminiMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }); + sendGeminiMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }); break; case 'ollama': - sendOllamaMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }); + sendOllamaMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }); break; case 'groq': - sendGroqMsg({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter, providerName }); + sendGroqMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }); break; default: onError({ error: `Error: whichApi was "${providerName}", which is not recognized!` }) diff --git a/src/vs/platform/void/electron-main/llmMessage/util.ts b/src/vs/platform/void/electron-main/llmMessage/util.ts index 00517168..4f11a855 100644 --- a/src/vs/platform/void/electron-main/llmMessage/util.ts +++ b/src/vs/platform/void/electron-main/llmMessage/util.ts @@ -1,6 +1,3 @@ -import { ProviderName, VoidProviderState } from '../../common/configTypes' -import { LLMMessage, OnText, OnFinalMessage, OnError } from '../../common/llmMessageTypes' - export const parseMaxTokensStr = (maxTokensStr: string) => { // parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr) @@ -10,13 +7,3 @@ export const parseMaxTokensStr = (maxTokensStr: string) => { } -export type SendLLMMessageFnTypeInternal = (params: { - messages: LLMMessage[]; - onText: OnText; - onFinalMessage: OnFinalMessage; - onError: OnError; - voidConfig: VoidProviderState; - providerName: ProviderName; - - _setAborter: (aborter: () => void) => void; -}) => void diff --git a/src/vs/platform/void/electron-main/llmMessageChannel.ts b/src/vs/platform/void/electron-main/llmMessageChannel.ts index 8f8c301a..4a913c65 100644 --- a/src/vs/platform/void/electron-main/llmMessageChannel.ts +++ b/src/vs/platform/void/electron-main/llmMessageChannel.ts @@ -8,7 +8,7 @@ import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js'; import { Emitter, Event } from '../../../base/common/event.js'; -import { listenerNames, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, ProxyLLMMessageParams, AbortRef, SendLLMMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js'; +import { BlockedProxyParams, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, ProxyLLMMessageParams, AbortRef, SendLLMMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js'; import { sendLLMMessage } from './llmMessage/sendLLMMessage.js' import { IMetricsService } from '../common/metricsService.js'; @@ -28,12 +28,15 @@ export class LLMMessageChannel implements IServerChannel { private readonly _abortRefOfRequestId: Record = {} + // stupidly, channels can't take in @IService constructor( - private readonly metricsService: IMetricsService - ) { } + private readonly metricsService: IMetricsService, + ) { + + } // browser uses this to listen for changes - listen(_: unknown, event: typeof listenerNames[number]): Event { + listen(_: unknown, event: BlockedProxyParams): Event { if (event === 'onText') { return this.onText; } diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 0b426a16..64925e46 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -742,18 +742,6 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }); }); - - // // Void: send from https:// - // this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, async (details, cb) => { - // // const voidConfig = this.voidConfigStateService.state.voidConfig - // // const whichApi = voidConfig.default['whichApi'] - // const endpoint = 'http://127.' //string | undefined = voidConfig[whichApi as VoidConfigField].endpoint - - // if (endpoint && details.url.startsWith(endpoint)) { - // details.requestHeaders['Origin'] = 'https://app.voideditor.com' - // } - // cb({ cancel: false, requestHeaders: details.requestHeaders }); - // }); } diff --git a/src/vs/workbench/contrib/void/browser/media/void.css b/src/vs/workbench/contrib/void/browser/media/void.css index 90c274ba..7d8732e4 100644 --- a/src/vs/workbench/contrib/void/browser/media/void.css +++ b/src/vs/workbench/contrib/void/browser/media/void.css @@ -1,4 +1,9 @@ -.monaco-editor .void-sweepIdxBG { +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + + .monaco-editor .void-sweepIdxBG { background-color: var(--vscode-void-sweepIdxBG); } diff --git a/src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts b/src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts index 11056b43..1fd7d7ca 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/stringifyFiles.ts @@ -1,3 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ import { CodeSelection } from '../registerThreads.js'; diff --git a/src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts b/src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts index 2faa8909..2d8b4793 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/systemPrompts.ts @@ -1,5 +1,7 @@ - - +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ // // used for ctrl+l // const partialGenerationInstructions = `` @@ -9,6 +11,31 @@ // const fimInstructions = `` +// CTRL+K prompt: +// const promptContent = `Here is the user's original selection: +// \`\`\` +// ${selection} +// \`\`\` + +// The user wants to apply the following instructions to the selection: +// ${instructions} + +// Please rewrite the selection following the user's instructions. + +// Instructions to follow: +// 1. Follow the user's instructions +// 2. You may ONLY CHANGE the selection, and nothing else in the file +// 3. Make sure all brackets in the new selection are balanced the same was as in the original selection +// 3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake + +// Complete the following: +// \`\`\` +//
${prefix}
+// ${suffix} +// `; + + + export const generateDiffInstructions = ` You are a coding assistant. You are given a list of relevant files \`files\`, a selection that the user is making \`selection\`, and instructions to follow \`instructions\`. 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 4b0533cf..94b02a74 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 @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import React, { JSX, useCallback, useEffect, useState } from 'react' import { marked, MarkedToken, Token } from 'marked' import { BlockCode } from './BlockCode.js' @@ -44,7 +49,7 @@ const CodeButtonsOnHover = ({ diffRepr: text }: { diffRepr: string }) => { className="btn btn-secondary btn-sm border border-vscode-input-border rounded" onClick={async () => { - inlineDiffService.startStreaming({ type: 'ctrl+l', providerName: 'anthropic' }, text) + inlineDiffService.startStreaming({ featureName: 'Ctrl+L' }, text) }} > Apply diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx new file mode 100644 index 00000000..7f6dc1cb --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + +import React, { Component, ErrorInfo, ReactNode } from 'react'; +import { ErrorDisplay } from './ErrorDisplay.js'; + +interface Props { + children: ReactNode; + fallback?: ReactNode; + onDismiss?: () => void; +} + +interface State { + hasError: boolean; + error: Error | null; + errorInfo: ErrorInfo | null; +} + +class ErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { + hasError: false, + error: null, + errorInfo: null + }; + } + + static getDerivedStateFromError(error: Error): Partial { + return { + hasError: true, + error + }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo): void { + this.setState({ + error, + errorInfo + }); + } + + render(): ReactNode { + if (this.state.hasError && this.state.error) { + // If a custom fallback is provided, use it + if (this.props.fallback) { + return this.props.fallback; + } + + // Use ErrorDisplay component as the default error UI + return ( + + ); + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx index 62acbd7a..f17d7748 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import React, { useState } from 'react'; import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx index 5d43ad46..89f905d1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx @@ -17,6 +17,7 @@ import { SidebarThreadSelector } from './SidebarThreadSelector.js'; import { SidebarChat } from './SidebarChat.js'; import { SidebarModelSettings } from './SidebarModelSettings.js'; import { SidebarProviderSettings } from './SidebarProviderSettings.js'; +import ErrorBoundary from './ErrorBoundary.js'; const Sidebar = () => { const sidebarState = useSidebarState() @@ -33,17 +34,25 @@ const Sidebar = () => { }}>clickme {tab} */}
- + + +
- + + +
- + + + -------- - + + +
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 e026b4fb..cb19e292 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 @@ -2,6 +2,7 @@ * Copyright (c) Glass Devtools, Inc. All rights reserved. * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ + import React, { FormEvent, Fragment, useCallback, useEffect, useRef, useState } from 'react'; @@ -219,8 +220,8 @@ export const SidebarChat = () => { setLatestError(error) }, - voidConfig: voidConfigState, - providerName: 'anthropic', + featureName: 'Ctrl+L', + } const latestRequestId = sendLLMMessageService.sendLLMMessage(object) diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarModelSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarModelSettings.tsx index c2184155..b2b081d4 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarModelSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarModelSettings.tsx @@ -3,14 +3,17 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { FeatureName, featureNames, providerNames } from '../../../../../../../platform/void/common/configTypes.js' -import { useConfigState } from '../util/services.js' +import { FeatureName, featureNames, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js' +import { useConfigState, useService } from '../util/services.js' +import ErrorBoundary from './ErrorBoundary.js' +import { VoidSelectBox } from './inputs.js' export const SidebarModelSettingsForFeature = ({ featureName }: { featureName: FeatureName }) => { + const voidConfigService = useService('configStateService') const voidConfigState = useConfigState() const models: [string, string][] = [] @@ -22,10 +25,21 @@ export const SidebarModelSettingsForFeature = ({ featureName }: { featureName: F }) } - return <> -

Settings - {featureName}

- {models.map(([providerName, model], i) =>

{providerName} - {model}

)} - + return <> +

{'Models'}

+ {models.length === 0 ? +

{'Please add a provider!'}

+ : + s.join(' - '))} + onChangeSelection={(newVal) => { /*voidConfigService.setFeatureState(providerName, 'model', newVal)*/ }} + selectBoxRef={{ current: null }} + />} + + {/*

Settings - {featureName}

*/} + {/* {models.map(([providerName, model], i) =>

{providerName} - {model}

)} */} +
} export const SidebarModelSettings = () => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarProviderSettings.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarProviderSettings.tsx index 9ef1d23b..b9435a56 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarProviderSettings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarProviderSettings.tsx @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' -import { displayInfoOfSettingName, ProviderName, providerNames, ProviderSettingName, VoidProviderState } from '../../../../../../../platform/void/common/configTypes.js' +import { displayInfoOfSettingName, ProviderName, providerNames } from '../../../../../../../platform/void/common/voidConfigTypes.js' import { VoidCheckBox, VoidInputBox, VoidSelectBox } from './inputs.js' import { useConfigState, useService } from '../util/services.js' import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js' +import ErrorBoundary from './ErrorBoundary.js' const Setting = ({ providerName, settingName }: { providerName: ProviderName, settingName: any }) => { @@ -21,8 +22,12 @@ const Setting = ({ providerName, settingName }: { providerName: ProviderName, se // this is really just to sync the state on initial mount, when init value hasn't been set yet const syncState = () => { if (!instanceRef.current) return + + const settingsAtProvider = voidConfigService.state.settingsOfProvider[providerName]; + // @ts-ignore - const stateVal = voidConfigService.state[providerName][settingName] + const stateVal = settingsAtProvider[settingName] + if (instanceRef.current.value !== stateVal) instanceRef.current.value = stateVal } @@ -31,44 +36,30 @@ const Setting = ({ providerName, settingName }: { providerName: ProviderName, se return () => disposable.dispose() }, [instanceRef, voidConfigService]) - return <> + return <>

{title}

{ { - voidConfigService.setState(providerName, settingName, newVal) + voidConfigService.setSettingOfProvider(providerName, settingName, newVal) }, [voidConfigService, providerName, settingName]) } onCreateInstance={instanceRef} multiline={false} />} - +
} const SettingsForProvider = ({ providerName }: { providerName: ProviderName }) => { const voidConfigState = useConfigState() - const { models, model, ...others } = voidConfigState[providerName] - const voidConfigService = useService('configStateService') - + const { models, ...others } = voidConfigState[providerName] return <>

{providerName}

- - {/* other settings (e.g. api key) */} + {/* settings besides models (e.g. api key) */} {Object.keys(others).map((settingName, i) => { return })} - -

{'Models'}

- {models === null ? -

{'No models available.'}

- : { voidConfigService.setState(providerName, 'model', newVal) }} - selectBoxRef={{ current: null }} - />} - } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx index cc71ac3b..c89e5e55 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx @@ -2,6 +2,7 @@ * Copyright (c) Glass Devtools, Inc. All rights reserved. * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ + import React from "react"; import { useService, useThreadsState } from '../util/services.js'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx index e3204fbc..ae0646bb 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/inputs.tsx @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import React, { useCallback, useEffect, useRef } from 'react'; import { useService } from '../util/services.js'; import { HistoryInputBox, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; @@ -102,7 +107,7 @@ export const VoidSelectBox = ({ onChangeSelection, initVal, selectBoxRef, option const defaultIndex = options.indexOf(initVal); selectBoxRef.current = new SelectBox( - options.map(opt => ({ text: opt })), + options.map(opt => ({ text: opt, detail: 'detail', description: 'description' })), defaultIndex, contextViewProvider, unthemedSelectBoxStyles diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/diffLines.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/diffLines.tsx index 753a7ba8..61a39f96 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/diffLines.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/diffLines.tsx @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import { diffLines, Change } from 'diff'; export { diffLines, Change } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx index 6ab7a361..203f7446 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx @@ -1,3 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import React, { useEffect, useState } from 'react'; import * as ReactDOM from 'react-dom/client' import { ReactServicesType, VoidSidebarState } from '../../../registerSidebar.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 9a5a0406..aaf0e07f 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 @@ -1,7 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + import { useState, useEffect } from 'react' import { VoidSidebarState, ReactServicesType } from '../../../registerSidebar.js' import { ThreadsState } from '../../../registerThreads.js' -import { VoidProviderState } from '../../../../../../../platform/void/common/configTypes.js' +import { SettingsOfProvider } from '../../../../../../../platform/void/common/voidConfigTypes.js' // normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes @@ -10,13 +15,13 @@ let services: ReactServicesType // even if React hasn't mounted yet, these variables are always updated to the latest state: let sidebarState: VoidSidebarState -let configState: VoidProviderState let threadsState: ThreadsState +let settingsOfProvider: SettingsOfProvider // React listens by adding a setState function to these: const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set() -const configStateListeners: Set<(s: VoidProviderState) => void> = new Set() const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set() +const settingsOfProviderListeners: Set<(s: SettingsOfProvider) => void> = new Set() // must call this before you can use any of the hooks below // this should only be called ONCE! this is the only place you don't need to dispose onDidChange. If you use state.onDidChange anywhere else, make sure to dispose it! @@ -25,7 +30,7 @@ let wasCalled = false export const _registerServices = (services_: ReactServicesType) => { - if (wasCalled) console.error(`void _registerServices was called again! It should only be called once.`) + if (wasCalled) console.error(`⚠️ Void _registerServices was called again! It should only be called once.`) wasCalled = true services = services_ @@ -37,11 +42,6 @@ export const _registerServices = (services_: ReactServicesType) => { sidebarStateListeners.forEach(l => l(sidebarState)) }) - configState = configStateService.state - configStateService.onDidChangeState(() => { - configState = configStateService.state - configStateListeners.forEach(l => l(configState)) - }) threadsState = threadsStateService.state threadsStateService.onDidChangeCurrentThread(() => { @@ -49,15 +49,20 @@ export const _registerServices = (services_: ReactServicesType) => { threadsStateListeners.forEach(l => l(threadsState)) }) + settingsOfProvider = configStateService.state.settingsOfProvider + configStateService.onDidChangeState(() => { + settingsOfProvider = configStateService.state.settingsOfProvider + settingsOfProviderListeners.forEach(l => l(settingsOfProvider)) + }) } // -- services -- -export const useService = (serviceName: T) => { +export const useService = (serviceName: T): ReactServicesType[T] => { if (services === null) { throw new Error('useAccessor must be used within an AccessorProvider') } - return services[serviceName] as ReactServicesType[T] + return services[serviceName] } // -- state of services -- @@ -73,11 +78,11 @@ export const useSidebarState = () => { } export const useConfigState = () => { - const [s, ss] = useState(configState) + const [s, ss] = useState(settingsOfProvider) useEffect(() => { - ss(configState) - configStateListeners.add(ss) - return () => { configStateListeners.delete(ss) } + ss(settingsOfProvider) + settingsOfProviderListeners.add(ss) + return () => { settingsOfProviderListeners.delete(ss) } }, [ss]) return s } diff --git a/src/vs/workbench/contrib/void/browser/registerActions.ts b/src/vs/workbench/contrib/void/browser/registerActions.ts index 8e5cc561..716aba73 100644 --- a/src/vs/workbench/contrib/void/browser/registerActions.ts +++ b/src/vs/workbench/contrib/void/browser/registerActions.ts @@ -12,7 +12,6 @@ import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { CodeStagingSelection, IThreadHistoryService } from './registerThreads.js'; -// import { IVoidConfigService } from './registerSettings.js'; // import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; diff --git a/src/vs/workbench/contrib/void/browser/registerAutocomplete.ts b/src/vs/workbench/contrib/void/browser/registerAutocomplete.ts index 029e1d05..b1a082a2 100644 --- a/src/vs/workbench/contrib/void/browser/registerAutocomplete.ts +++ b/src/vs/workbench/contrib/void/browser/registerAutocomplete.ts @@ -7,7 +7,6 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IVoidConfigStateService } from './registerConfig.js'; import { ITextModel } from '../../../../editor/common/model.js'; import { Position } from '../../../../editor/common/core/position.js'; import { InlineCompletion, InlineCompletionContext } from '../../../../editor/common/languages.js'; @@ -516,7 +515,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ const disabled = true const testMode = false - if (disabled) { return []; } + if (disabled) return []; const docUriStr = model.uri.toString(); @@ -676,8 +675,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ newAutocompletion.status = 'error' reject(error) }, - providerName: 'anthropic', - voidConfig: this._voidConfigStateService.state, + featureName: 'Autocomplete', + range: { startLineNumber: position.lineNumber, startColumn: position.column, endLineNumber: position.lineNumber, endColumn: position.column }, }) newAutocompletion.requestId = requestId @@ -714,7 +713,6 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ constructor( @ILanguageFeaturesService private _langFeatureService: ILanguageFeaturesService, - @IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService, @ISendLLMMessageService private readonly _sendLLMMessageService: ISendLLMMessageService, @IEditorService private readonly _editorService: IEditorService, @IModelService private readonly _modelService: IModelService, diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index 2d7177c8..cb940333 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -11,7 +11,7 @@ import { ICodeEditor, IOverlayWidget, IViewZone } from '../../../../editor/brows // import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; // import { throttle } from '../../../../base/common/decorators.js'; -import { IVoidConfigStateService } from './registerConfig.js'; +// import { IVoidConfigStateService } from './registerConfig.js'; import { writeFileWithDiffInstructions } from './prompt/systemPrompts.js'; import { ComputedDiff, findDiffs } from './findDiffs.js'; import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js'; @@ -28,9 +28,8 @@ import { ILanguageService } from '../../../../editor/common/languages/language.j import * as dom from '../../../../base/browser/dom.js'; import { Widget } from '../../../../base/browser/ui/widget.js'; import { URI } from '../../../../base/common/uri.js'; -import { LLMMessageServiceParams } from '../../../../platform/void/common/llmMessageTypes.js'; +import { LLMFeatureSelection, LLMMessageServiceParams } from '../../../../platform/void/common/llmMessageTypes.js'; import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js'; -import { ProviderName } from '../../../../platform/void/common/configTypes.js'; // gets converted to --vscode-void-greenBG, see void.css @@ -104,25 +103,17 @@ type HistorySnapshot = { entireFileCode: string; } & ({ - type: 'ctrl+k'; + type: 'Ctrl+K'; ctrlKText: string; } | { - type: 'ctrl+l'; + type: 'Ctrl+L'; }) -type StartStreamingOptions = { - type: 'ctrl+k', - providerName: ProviderName, - range: IRange -} | { - type: 'ctrl+l', - providerName: ProviderName -} export interface IInlineDiffsService { readonly _serviceBrand: undefined; - startStreaming(params: StartStreamingOptions, str: string): void; + startStreaming(params: LLMFeatureSelection, str: string): void; } export const IInlineDiffsService = createDecorator('inlineDiffAreasService'); @@ -153,7 +144,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { constructor( // @IHistoryService private readonly _historyService: IHistoryService, // history service is the history of pressing alt left/right - @IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService, + // @IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService, @ICodeEditorService private readonly _editorService: ICodeEditorService, @IModelService private readonly _modelService: IModelService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z @@ -379,7 +370,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { return { snapshottedDiffAreaOfId, entireFileCode: this._readURI(uri) ?? '', // the whole file's code - type: 'ctrl+l', + type: 'Ctrl+L', } } @@ -646,7 +637,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { - private async _initializeStream(uri: URI, diffRepr: string, providerName: ProviderName) { + private async _initializeStream(opts: LLMFeatureSelection, diffRepr: string, uri: URI,) { // diff area begin and end line const numLines = this._getNumLines(uri) @@ -698,7 +689,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { this.diffAreaOfId[diffArea.diffareaid] = diffArea // actually call the LLM - const voidConfigState = this._voidConfigStateService.state const promptContent = `\ ORIGINAL_CODE \`\`\` @@ -715,29 +705,6 @@ Please finish writing the new file by applying the diff to the original file. Re ` - // CTRL+K prompt: - // const promptContent = `Here is the user's original selection: - // \`\`\` - // ${selection} - // \`\`\` - - // The user wants to apply the following instructions to the selection: - // ${instructions} - - // Please rewrite the selection following the user's instructions. - - // Instructions to follow: - // 1. Follow the user's instructions - // 2. You may ONLY CHANGE the selection, and nothing else in the file - // 3. Make sure all brackets in the new selection are balanced the same was as in the original selection - // 3. Be careful not to duplicate or remove variables, comments, or other syntax by mistake - - // Complete the following: - // \`\`\` - //
${prefix}
- // ${suffix} - // `; - await new Promise((resolve, reject) => { let streamRequestId: string | null = null @@ -770,8 +737,7 @@ Please finish writing the new file by applying the diff to the original file. Re diffArea._sweepState = { isStreaming: false, line: null } resolve(); }, - voidConfig: voidConfigState, - providerName, + ...opts } streamRequestId = this._sendLLMMessageService.sendLLMMessage(object) @@ -786,7 +752,7 @@ Please finish writing the new file by applying the diff to the original file. Re - async startStreaming(params: StartStreamingOptions, userMessage: string) { + async startStreaming(opts: LLMFeatureSelection, userMessage: string) { const editor = this._editorService.getActiveCodeEditor() if (!editor) return @@ -798,7 +764,7 @@ Please finish writing the new file by applying the diff to the original file. Re // TODO deselect user's cursor - this._initializeStream(uri, userMessage, params.providerName) + this._initializeStream(opts, userMessage, uri) } diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/registerSidebar.ts index 327fa1b6..02ed20ea 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/registerSidebar.ts @@ -42,7 +42,7 @@ import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import mountFn from './react/out/sidebar-tsx/Sidebar.js'; -import { IVoidConfigStateService } from './registerConfig.js'; +import { IVoidConfigStateService } from '../../../../platform/void/common/voidConfigService.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { IInlineDiffsService } from './registerInlineDiffs.js'; import { IModelService } from '../../../../editor/common/services/model.js'; diff --git a/src/vs/workbench/contrib/void/browser/void.contribution.ts b/src/vs/workbench/contrib/void/browser/void.contribution.ts index b5491a52..48d34d6d 100644 --- a/src/vs/workbench/contrib/void/browser/void.contribution.ts +++ b/src/vs/workbench/contrib/void/browser/void.contribution.ts @@ -7,7 +7,7 @@ import './registerActions.js' // register Settings -import './registerConfig.js' // TODO move this to platform +import '../../../../platform/void/common/voidConfigService.js' // TODO move this to platform // register inline diffs import './registerInlineDiffs.js' diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 0f33a1d1..b5e3d5e6 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -18,6 +18,7 @@ import './browser/workbench.contribution.js'; // Void added this: import './contrib/void/browser/void.contribution.js'; import '../platform/void/browser/llmMessageService.js'; +import '../platform/void/common/voidConfigService.js'; //#endregion