From d1c5cf518eec46b0a2b1e0d2187be440333c5891 Mon Sep 17 00:00:00 2001 From: Baws Deep <150625487+bawsdeep@users.noreply.github.com> Date: Wed, 29 Jan 2025 05:41:12 +0100 Subject: [PATCH 01/14] Add files via upload --- create-appimage.sh | 112 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 create-appimage.sh diff --git a/create-appimage.sh b/create-appimage.sh new file mode 100644 index 00000000..c79a351c --- /dev/null +++ b/create-appimage.sh @@ -0,0 +1,112 @@ +#!/bin/bash +set -e # Exit on error +set -x # Print commands as they are executed + +# Configuration +APP_NAME="void" +APP_VERSION="1.0.0" +ARCH="x86_64" + +export ARCH + +# Check if void binary exists in current directory +if [ ! -f "./void" ]; then + echo "Error: void binary not found in current directory" + exit 1 +fi + +# Check if icon exists +if [ ! -f "./void.png" ]; then + echo "Error: void.png icon not found in current directory" + exit 1 +fi + +# Create temporary directory +TEMP_DIR="$(mktemp -d)" +echo "Created temporary directory: $TEMP_DIR" +APP_DIR="$TEMP_DIR/$APP_NAME.AppDir" + +# Create basic AppDir structure +mkdir -pv "$APP_DIR/usr/bin" +mkdir -pv "$APP_DIR/usr/lib" +mkdir -pv "$APP_DIR/usr/share/applications" +mkdir -pv "$APP_DIR/usr/share/icons/hicolor/256x256/apps" + +# Exclude create-appimage.sh and appimagetool-x86_64.AppImage from being copied +echo "Copying files excluding create-appimage.sh and appimagetool-x86_64.AppImage..." +for file in ./*; do + if [[ "$file" != "./create-appimage.sh" && "$file" != "./appimagetool-x86_64.AppImage" ]]; then + cp -rv "$file" "$APP_DIR/usr/bin/" + fi +done + +# Copy the icon to required locations +cp -v ./void.png "$APP_DIR/void.png" +cp -v ./void.png "$APP_DIR/usr/share/icons/hicolor/256x256/apps/void.png" + +# Copy dependencies with error checking +echo "Copying dependencies..." +for lib in $(ldd ./void | grep "=> /" | awk '{print $3}'); do + if [ -f "$lib" ]; then + cp -v "$lib" "$APP_DIR/usr/lib/" || echo "Failed to copy $lib" + else + echo "Warning: Library $lib not found" + fi +done + +# Create desktop file with error checking +echo "Creating desktop file..." +if ! cat > "$APP_DIR/$APP_NAME.desktop" < "$APP_DIR/AppRun" < Date: Fri, 31 Jan 2025 13:11:33 +1100 Subject: [PATCH 02/14] Update voidSettingsTypes.ts Removed "distil-whisper-large-v3-en" because Whisper is not a model you chat with. Added "llama3-70b-8192" + "mixtral-8x7b-32768" --- src/vs/platform/void/common/voidSettingsTypes.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 43a29f5b..8caea3d2 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -86,10 +86,11 @@ export const defaultDeepseekModels = modelInfoOfDefaultNames([ // https://console.groq.com/docs/models export const defaultGroqModels = modelInfoOfDefaultNames([ - "distil-whisper-large-v3-en", + "llama3-70b-8192", "llama-3.3-70b-versatile", "llama-3.1-8b-instant", - "gemma2-9b-it" + "gemma2-9b-it", + "mixtral-8x7b-32768" ]) From e3941d5565bc0b1c91add77f86e65808e9ec5e11 Mon Sep 17 00:00:00 2001 From: Andrew Pareles <43356051+andrewpareles@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:41:56 -0800 Subject: [PATCH 03/14] + --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c42e3446..64129df3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,8 +14,6 @@ There are a few ways to contribute: We highly recommend reading [this](https://github.com/microsoft/vscode/wiki/Source-Code-Organization) article on VSCode's sourcecode organization. -We are currently putting together our own articles on VSCode and Void's sourcecode organization. The best way to get this information right now is by attending a weekly meeting. - From 2f49650e15732e6d57fac9a6a8cf52be9f848ee3 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Thu, 6 Feb 2025 01:32:31 -0800 Subject: [PATCH 04/14] model and provider error handling --- .../platform/void/common/llmMessageService.ts | 27 +-- .../platform/void/common/llmMessageTypes.ts | 5 +- .../void/common/refreshModelService.ts | 8 +- .../void/common/voidSettingsService.ts | 169 +++++++++++------- .../platform/void/common/voidSettingsTypes.ts | 78 +++++--- .../void/electron-main/llmMessage/openai.ts | 1 + .../llmMessage/sendLLMMessage.ts | 18 +- .../void/browser/autocompleteService.ts | 4 +- .../contrib/void/browser/chatThreadService.ts | 6 +- .../void/browser/inlineDiffsService.ts | 34 ++-- .../react/src/markdown/ChatMarkdownRender.tsx | 2 +- .../src/quick-edit-tsx/QuickEditChat.tsx | 13 +- .../react/src/sidebar-tsx/ErrorDisplay.tsx | 5 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 22 +-- .../void/browser/react/src/util/inputs.tsx | 25 +-- .../src/void-settings-tsx/ModelDropdown.tsx | 61 ++++--- .../react/src/void-settings-tsx/Settings.tsx | 42 ++--- 17 files changed, 296 insertions(+), 224 deletions(-) diff --git a/src/vs/platform/void/common/llmMessageService.ts b/src/vs/platform/void/common/llmMessageService.ts index 2096209a..4718e6c2 100644 --- a/src/vs/platform/void/common/llmMessageService.ts +++ b/src/vs/platform/void/common/llmMessageService.ts @@ -12,7 +12,7 @@ import { createDecorator } from '../../instantiation/common/instantiation.js'; import { Event } from '../../../base/common/event.js'; import { Disposable } from '../../../base/common/lifecycle.js'; import { IVoidSettingsService } from './voidSettingsService.js'; -import { getProvidersWithoutModels } from './voidSettingsTypes.js'; +import { displayInfoOfProviderName, isFeatureNameDisabled } from './voidSettingsTypes.js'; // import { INotificationService } from '../../notification/common/notification.js'; // calls channel to implement features @@ -91,19 +91,24 @@ export class LLMMessageService extends Disposable implements ILLMMessageService const { onText, onFinalMessage, onError, ...proxyParams } = params; const { useProviderFor: featureName } = proxyParams - // end early if no provider + // throw an error if no model/provider selected (this should usually never be reached, the UI should check this first, but might happen in cases like Apply where we haven't built much UI/checks yet, good practice to have check logic on backend) + const isDisabled = isFeatureNameDisabled(featureName, this.voidSettingsService.state) const modelSelection = this.voidSettingsService.state.modelSelectionOfFeature[featureName] + if (isDisabled || modelSelection === null) { + let message: string - // throw an error for providers without models - const providersWithoutModels = getProvidersWithoutModels(this.voidSettingsService.state.settingsOfProvider) - if (providersWithoutModels.length !== 0) { - onError({ message: `You haven't added any models for ${providersWithoutModels.join(', ')}.`, fullError: null }) - return null - } + if (isDisabled === 'addProvider' || isDisabled === 'providerNotAutoDetected') + message = `Please add a provider in Void Settings.` + else if (isDisabled === 'addModel') + message = `Please add a model.` + else if (isDisabled === 'needToEnableModel') + message = `Please enable a model.` + else if (isDisabled === 'notFilledIn') + message = `Please fill in Void Settings${modelSelection !== null ? ` for ${displayInfoOfProviderName(modelSelection.providerName).title}` : ''}.` + else + message = 'Please add a provider in Void Settings.' - // throw an error if no models - if (modelSelection === null) { - onError({ message: 'Please add a Provider in Settings!', fullError: null }) + onError({ message, fullError: null }) return null } diff --git a/src/vs/platform/void/common/llmMessageTypes.ts b/src/vs/platform/void/common/llmMessageTypes.ts index cbc6856e..fb6a94fc 100644 --- a/src/vs/platform/void/common/llmMessageTypes.ts +++ b/src/vs/platform/void/common/llmMessageTypes.ts @@ -11,6 +11,7 @@ export const errorDetails = (fullError: Error | null): string | null => { return null } else if (typeof fullError === 'object') { + if (Object.keys(fullError).length === 0) return null return JSON.stringify(fullError, null, 2) } else if (typeof fullError === 'string') { @@ -41,10 +42,10 @@ type _InternalSendFIMMessage = { } type SendLLMType = { - type: 'sendChatMessage'; + messagesType: 'chatMessages'; messages: LLMChatMessage[]; } | { - type: 'sendFIMMessage'; + messagesType: 'FIMMessage'; messages: _InternalSendFIMMessage; } diff --git a/src/vs/platform/void/common/refreshModelService.ts b/src/vs/platform/void/common/refreshModelService.ts index 811db0db..7ef6a068 100644 --- a/src/vs/platform/void/common/refreshModelService.ts +++ b/src/vs/platform/void/common/refreshModelService.ts @@ -44,8 +44,8 @@ export type RefreshModelStateOfProvider = Record -type EventProp = T extends 'globalSettings' ? [T, keyof VoidSettingsState[T]] : T | 'all' +// type RealVoidSettings = Exclude +// type EventProp = T extends 'globalSettings' ? [T, keyof VoidSettingsState[T]] : T | 'all' export interface IVoidSettingsService { @@ -51,7 +51,7 @@ export interface IVoidSettingsService { readonly state: VoidSettingsState; // in order to play nicely with react, you should immutably change state readonly waitForInitState: Promise; - onDidChangeState: Event; + onDidChangeState: Event; setSettingOfProvider: SetSettingOfProviderFn; setModelSelectionOfFeature: SetModelSelectionOfFeatureFn; @@ -64,26 +64,76 @@ export interface IVoidSettingsService { } -let _computeModelOptions = (settingsOfProvider: SettingsOfProvider) => { - let modelOptions: ModelOption[] = [] + +const _updatedValidatedState = (state: Omit) => { + + let newSettingsOfProvider = state.settingsOfProvider + + // recompute _didFillInProviderSettings for (const providerName of providerNames) { - const providerConfig = settingsOfProvider[providerName] - if (!providerConfig._enabled) continue // if disabled, don't display model options - for (const { modelName, isHidden } of providerConfig.models) { - if (isHidden) continue - modelOptions.push({ name: `${modelName} (${providerName})`, selection: { providerName, modelName } }) + const settingsAtProvider = newSettingsOfProvider[providerName] + + const didFillInProviderSettings = Object.keys(defaultProviderSettings[providerName]).every(key => !!settingsAtProvider[key as keyof typeof settingsAtProvider]) + + if (didFillInProviderSettings === settingsAtProvider._didFillInProviderSettings) continue + + newSettingsOfProvider = { + ...newSettingsOfProvider, + [providerName]: { + ...settingsAtProvider, + _didFillInProviderSettings: didFillInProviderSettings, + }, } } - return modelOptions + + // update model options + let newModelOptions: ModelOption[] = [] + for (const providerName of providerNames) { + const providerTitle = displayInfoOfProviderName(providerName).title.toLowerCase() // looks better lowercase, best practice to not use raw providerName + if (!newSettingsOfProvider[providerName]._didFillInProviderSettings) continue // if disabled, don't display model options + for (const { modelName, isHidden } of newSettingsOfProvider[providerName].models) { + if (isHidden) continue + newModelOptions.push({ name: `${modelName} (${providerTitle})`, selection: { providerName, modelName } }) + } + } + + // now that model options are updated, make sure the selection is valid + // if the user-selected model is no longer in the list, update the selection for each feature that needs it to something relevant (the 0th model available, or null) + let newModelSelectionOfFeature = state.modelSelectionOfFeature + for (const featureName of featureNames) { + + const modelSelectionAtFeature = newModelSelectionOfFeature[featureName] + const selnIdx = modelSelectionAtFeature === null ? -1 : newModelOptions.findIndex(m => modelSelectionsEqual(m.selection, modelSelectionAtFeature)) + + if (selnIdx !== -1) continue + + newModelSelectionOfFeature = { + ...newModelSelectionOfFeature, + [featureName]: newModelOptions.length === 0 ? null : newModelOptions[0].selection + } + } + + + const newState = { + ...state, + settingsOfProvider: newSettingsOfProvider, + modelSelectionOfFeature: newModelSelectionOfFeature, + _modelOptions: newModelOptions, + } satisfies VoidSettingsState + + return newState } + + + const defaultState = () => { const d: VoidSettingsState = { settingsOfProvider: deepClone(defaultSettingsOfProvider), modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null, 'FastApply': null }, globalSettings: deepClone(defaultGlobalSettings), - _modelOptions: _computeModelOptions(defaultSettingsOfProvider), // computed + _modelOptions: [], // computed later } return d } @@ -93,8 +143,8 @@ export const IVoidSettingsService = createDecorator('VoidS class VoidSettingsService extends Disposable implements IVoidSettingsService { _serviceBrand: undefined; - private readonly _onDidChangeState = new Emitter(); - readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes + private readonly _onDidChangeState = new Emitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes state: VoidSettingsState; waitForInitState: Promise // await this if you need a valid state initially @@ -118,39 +168,47 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { this._readState().then(readS => { // 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) - readS = { - ...readS, - settingsOfProvider: { - // A HACK BECAUSE WE ADDED DEEPSEEK (did not exist before, comes before readS) - ...{ deepseek: defaultSettingsOfProvider.deepseek }, + const newSettingsOfProvider = { + // A HACK BECAUSE WE ADDED DEEPSEEK (did not exist before, comes before readS) + ...{ deepseek: defaultSettingsOfProvider.deepseek }, - // A HACK BECAUSE WE ADDED MISTRAL (did not exist before, comes before readS) - ...{ mistral: defaultSettingsOfProvider.mistral }, + // A HACK BECAUSE WE ADDED MISTRAL (did not exist before, comes before readS) + ...{ mistral: defaultSettingsOfProvider.mistral }, - ...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)) - ] - } - }, - modelSelectionOfFeature: { - // A HACK BECAUSE WE ADDED FastApply - ...{ 'FastApply': null }, - ...readS.modelSelectionOfFeature, + // 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)) + ] } } - this.state = readS + const newModelSelectionOfFeature = { + // A HACK BECAUSE WE ADDED FastApply + ...{ 'FastApply': null }, + ...readS.modelSelectionOfFeature, + } + + readS = { + ...readS, + settingsOfProvider: newSettingsOfProvider, + modelSelectionOfFeature: newModelSelectionOfFeature, + } + + this.state = _updatedValidatedState(readS) + resolver() - this._onDidChangeState.fire('all') + this._onDidChangeState.fire() }) + + } + private async _readState(): Promise { const encryptedState = this._storageService.get(STORAGE_KEY, StorageScope.APPLICATION) @@ -172,7 +230,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { const newModelSelectionOfFeature = this.state.modelSelectionOfFeature - const newSettingsOfProvider = { + const newSettingsOfProvider: SettingsOfProvider = { ...this.state.settingsOfProvider, [providerName]: { ...this.state.settingsOfProvider[providerName], @@ -182,38 +240,17 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { const newGlobalSettings = this.state.globalSettings - // if changed models or enabled a provider, recompute models list - const modelsListChanged = settingName === 'models' || settingName === '_enabled' - const newModelsList = modelsListChanged ? _computeModelOptions(newSettingsOfProvider) : this.state._modelOptions - - const newState: VoidSettingsState = { + const newState = { modelSelectionOfFeature: newModelSelectionOfFeature, settingsOfProvider: newSettingsOfProvider, globalSettings: newGlobalSettings, - _modelOptions: newModelsList, } - // this must go above this.setanythingelse() - this.state = newState - - // if the user-selected model is no longer in the list, update the selection for each feature that needs it to something relevant (the 0th model available, or null) - if (modelsListChanged) { - for (const featureName of featureNames) { - - const currentSelection = newModelSelectionOfFeature[featureName] - const selnIdx = currentSelection === null ? -1 : newModelsList.findIndex(m => modelSelectionsEqual(m.selection, currentSelection)) - - if (selnIdx === -1) { - if (newModelsList.length !== 0) - this.setModelSelectionOfFeature(featureName, newModelsList[0].selection, { doNotApplyEffects: true }) - else - this.setModelSelectionOfFeature(featureName, null, { doNotApplyEffects: true }) - } - } - } + this.state = _updatedValidatedState(newState) await this._storeState() - this._onDidChangeState.fire('settingsOfProvider') + this._onDidChangeState.fire() + } @@ -227,7 +264,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } this.state = newState await this._storeState() - this._onDidChangeState.fire(['globalSettings', settingName]) + this._onDidChangeState.fire() } @@ -247,7 +284,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { return await this._storeState() - this._onDidChangeState.fire('modelSelectionOfFeature') + this._onDidChangeState.fire() } diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 26758593..fe79d73d 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -4,7 +4,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ - +import { VoidSettingsState } from './voidSettingsService.js' export type VoidModelInfo = { @@ -186,28 +186,23 @@ export const customSettingNamesOfProvider = (providerName: ProviderName) => { return Object.keys(defaultProviderSettings[providerName]) as CustomSettingName[] } -export const getProvidersWithoutModels = (settingsOfProvider: SettingsOfProvider) => { - return Object.entries(settingsOfProvider) - .filter(([name, provider]) => provider._enabled && provider.models.length === 0) - .map(([name]) => name) -} type CommonProviderSettings = { - _enabled: boolean | undefined, // undefined initially, computed when user types in all fields + _didFillInProviderSettings: boolean | undefined, // undefined initially, computed when user types in all fields models: VoidModelInfo[], } -export type SettingsForProvider = CustomProviderSettings & CommonProviderSettings +export type SettingsAtProvider = CustomProviderSettings & CommonProviderSettings // part of state export type SettingsOfProvider = { - [providerName in ProviderName]: SettingsForProvider + [providerName in ProviderName]: SettingsAtProvider } -export type SettingName = keyof SettingsForProvider +export type SettingName = keyof SettingsAtProvider @@ -316,7 +311,7 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName undefined, } } - else if (settingName === '_enabled') { + else if (settingName === '_didFillInProviderSettings') { return { title: '(never)', placeholder: '(never)', @@ -380,55 +375,55 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...defaultCustomSettings, ...defaultProviderSettings.anthropic, ...voidInitModelOptions.anthropic, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, openAI: { ...defaultCustomSettings, ...defaultProviderSettings.openAI, ...voidInitModelOptions.openAI, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, deepseek: { ...defaultCustomSettings, ...defaultProviderSettings.deepseek, ...voidInitModelOptions.deepseek, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, gemini: { ...defaultCustomSettings, ...defaultProviderSettings.gemini, ...voidInitModelOptions.gemini, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, mistral: { ...defaultCustomSettings, ...defaultProviderSettings.mistral, ...voidInitModelOptions.mistral, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, groq: { // aggregator ...defaultCustomSettings, ...defaultProviderSettings.groq, ...voidInitModelOptions.groq, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, openRouter: { // aggregator ...defaultCustomSettings, ...defaultProviderSettings.openRouter, ...voidInitModelOptions.openRouter, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, openAICompatible: { // aggregator ...defaultCustomSettings, ...defaultProviderSettings.openAICompatible, ...voidInitModelOptions.openAICompatible, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, ollama: { // aggregator ...defaultCustomSettings, ...defaultProviderSettings.ollama, ...voidInitModelOptions.ollama, - _enabled: undefined, + _didFillInProviderSettings: undefined, }, } @@ -458,10 +453,6 @@ export const displayInfoOfFeatureName = (featureName: FeatureName) => { } - - - - // the models of these can be refreshed (in theory all can, but not all should) export const refreshableProviderNames = localProviderNames export type RefreshableProviderName = typeof refreshableProviderNames[number] @@ -471,6 +462,45 @@ export type RefreshableProviderName = typeof refreshableProviderNames[number] +// use this in isFeatuerNameDissbled +export const isProviderNameDisabled = (providerName: ProviderName, settingsState: VoidSettingsState) => { + + const settingsAtProvider = settingsState.settingsOfProvider[providerName] + const isAutodetected = (refreshableProviderNames as string[]).includes(providerName) + + const isDisabled = settingsAtProvider.models.length === 0 + if (isDisabled) { + return isAutodetected ? 'providerNotAutoDetected' : (!settingsAtProvider._didFillInProviderSettings ? 'notFilledIn' : 'addModel') + } + return false +} + +export const isFeatureNameDisabled = (featureName: FeatureName, settingsState: VoidSettingsState) => { + // if has a selected provider, check if it's enabled + const selectedProvider = settingsState.modelSelectionOfFeature[featureName] + + if (selectedProvider) { + const { providerName } = selectedProvider + return isProviderNameDisabled(providerName, settingsState) + } + + // if there are any models they can turn on, tell them that + const canTurnOnAModel = !!providerNames.find(providerName => settingsState.settingsOfProvider[providerName].models.filter(m => m.isHidden).length !== 0) + if (canTurnOnAModel) return 'needToEnableModel' + + // if there are any providers filled in, then they just need to add a model + const anyFilledIn = !!providerNames.find(providerName => settingsState.settingsOfProvider[providerName]._didFillInProviderSettings) + if (anyFilledIn) return 'addModel' + + return 'addProvider' +} + + + + + + + export type GlobalSettings = { diff --git a/src/vs/platform/void/electron-main/llmMessage/openai.ts b/src/vs/platform/void/electron-main/llmMessage/openai.ts index 8b7afd94..268eadfb 100644 --- a/src/vs/platform/void/electron-main/llmMessage/openai.ts +++ b/src/vs/platform/void/electron-main/llmMessage/openai.ts @@ -99,6 +99,7 @@ export const sendOpenAIFIM: _InternalSendLLMFIMMessageFnType = ({ messages, onTe .create(options) .then(async response => { // TODO!!! + console.log('RESPONSE', response) }) } diff --git a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts index 8b393f1a..7cea9d5a 100644 --- a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts @@ -12,6 +12,7 @@ import { sendOpenAIChat } from './openai.js'; import { sendGeminiChat } from './gemini.js'; import { sendGroqChat } from './groq.js'; import { sendMistralChat } from './mistral.js'; +import { displayInfoOfProviderName } from '../../common/voidSettingsTypes.js'; const cleanChatMessages = (messages: LLMChatMessage[]): _InternalLLMChatMessage[] => { @@ -49,7 +50,7 @@ const cleanChatMessages = (messages: LLMChatMessage[]): _InternalLLMChatMessage[ export const sendLLMMessage = ({ - type, + messagesType, aiInstructions, messages: messages_, onText: onText_, @@ -66,20 +67,20 @@ export const sendLLMMessage = ({ ) => { // messages.unshift({ role: 'system', content: aiInstructions }) - const messagesArr = type === 'sendChatMessage' ? cleanChatMessages(messages_) : [] + const messagesArr = messagesType === 'chatMessages' ? cleanChatMessages(messages_) : [] // only captures number of messages and message "shape", no actual code, instructions, prompts, etc const captureLLMEvent = (eventId: string, extras?: object) => { metricsService.capture(eventId, { providerName, modelName, - ...type === 'sendChatMessage' ? { + ...messagesType === 'chatMessages' ? { numMessages: messagesArr?.length, messagesShape: messagesArr?.map(msg => ({ role: msg.role, length: msg.content.length })), origNumMessages: messages_?.length, origMessagesShape: messages_?.map(msg => ({ role: msg.role, length: msg.content.length })), - } : type === 'sendFIMMessage' ? { + } : messagesType === 'FIMMessage' ? { prefixLength: messages_.prefix.length, suffixLength: messages_.suffix.length, } : {}, @@ -109,6 +110,11 @@ export const sendLLMMessage = ({ const onError: OnError = ({ message: error, fullError }) => { if (_didAbort) return console.error('sendLLMMessage onError:', error) + + // handle failed to fetch errors, which give 0 information by design + if (error === 'TypeError: fetch failed') + error = `Failed to fetch from ${displayInfoOfProviderName(providerName).title}. This likely means you specified the wrong endpoint in Void Settings, or your local model provider like Ollama is powered off.` + captureLLMEvent(`${loggingName} - Error`, { error }) onError_({ message: error, fullError }) } @@ -139,8 +145,8 @@ export const sendLLMMessage = ({ break; case 'ollama': if ( // TODO @andrew in future we want to use our own templates instead of using ollamaFIM - type === 'sendFIMMessage' - && settingsOfProvider['ollama']._enabled + messagesType === 'FIMMessage' + && settingsOfProvider['ollama']._didFillInProviderSettings && settingsOfProvider['ollama'].models.some(m => !m.isHidden) ) sendOllamaFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName }) diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 64d181f0..6b67c6be 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -785,13 +785,13 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ } console.log('BB') - console.log(predictionType) + console.log('type', predictionType) // set parameters of `newAutocompletion` appropriately newAutocompletion.llmPromise = new Promise((resolve, reject) => { const requestId = this._llmMessageService.sendLLMMessage({ - type: 'sendFIMMessage', + messagesType: 'FIMMessage', messages: { prefix: llmPrefix, suffix: llmSuffix, diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 34b4dad4..d0ca28b2 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -70,7 +70,7 @@ export type ThreadsState = { export type ThreadStreamState = { [threadId: string]: undefined | { - error?: { message: string, fullError: Error | null }; + error?: { message: string, fullError: Error | null, }; messageSoFar?: string; streamingToken?: string; } @@ -202,12 +202,12 @@ class ChatThreadService extends Disposable implements IChatThreadService { this._setStreamState(threadId, { error: undefined }) const llmCancelToken = this._llmMessageService.sendLLMMessage({ - type: 'sendChatMessage', + messagesType: 'chatMessages', logging: { loggingName: 'Chat' }, useProviderFor: 'Ctrl+L', messages: [ { role: 'system', content: chat_systemMessage }, - ...this.getCurrentThread().messages.map(m => ({ role: m.role, content: m.content || '(null)' })), + ...this.getCurrentThread().messages.map(m => ({ role: m.role, content: m.content || '(empty model output)' })), ], onText: ({ newText, fullText }) => { this._setStreamState(threadId, { messageSoFar: fullText }) diff --git a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts index 09da6af3..d4d7d065 100644 --- a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts +++ b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts @@ -102,13 +102,13 @@ const getLeadingWhitespacePx = (editor: ICodeEditor, startLine: number): number // similar to ServiceLLM export type StartApplyingOpts = { - featureName: 'Ctrl+K'; + from: 'QuickEdit'; diffareaid: number; // id of the CtrlK area (contains text selection) } | { - featureName: 'Ctrl+L'; + from: 'Chat'; applyStr: string; } | { - featureName: 'Autocomplete'; + from: 'Autocomplete'; range: IRange; userMessage: string; } @@ -1209,13 +1209,13 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { private _initializeStartApplying(opts: StartApplyingOpts): DiffZone | undefined { - const { featureName } = opts + const { from } = opts let startLine: number let endLine: number let uri: URI - if (featureName === 'Ctrl+L') { + if (from === 'Chat') { const uri_ = this._getActiveEditorURI() if (!uri_) return @@ -1231,7 +1231,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { endLine = numLines } - else if (featureName === 'Ctrl+K') { + else if (from === 'QuickEdit') { const { diffareaid } = opts const ctrlKZone = this.diffAreaOfId[diffareaid] if (ctrlKZone.type !== 'CtrlKZone') return @@ -1242,7 +1242,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { endLine = endLine_ } else { - throw new Error(`Void: diff.type not recognized on: ${featureName}`) + throw new Error(`Void: diff.type not recognized on: ${from}`) } const currentFileStr = this._readURI(uri) @@ -1278,7 +1278,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { this._onDidChangeStreaming.fire({ uri, diffareaid: diffZone.diffareaid }) this._onDidAddOrDeleteDiffZones.fire({ uri }) - if (featureName === 'Ctrl+K') { + if (from === 'QuickEdit') { const { diffareaid } = opts const ctrlKZone = this.diffAreaOfId[diffareaid] if (ctrlKZone.type !== 'CtrlKZone') return @@ -1289,14 +1289,14 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // now handle messages let messages: LLMChatMessage[] - if (featureName === 'Ctrl+L') { + if (from === 'Chat') { const userContent = fastApply_userMessage({ originalCode, applyStr: opts.applyStr, uri }) messages = [ { role: 'system', content: fastApply_systemMessage, }, { role: 'user', content: userContent, } ] } - else if (featureName === 'Ctrl+K') { + else if (from === 'QuickEdit') { const { diffareaid } = opts const ctrlKZone = this.diffAreaOfId[diffareaid] if (ctrlKZone.type !== 'CtrlKZone') return @@ -1323,14 +1323,14 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { ] // } } - else { throw new Error(`featureName ${featureName} is invalid`) } + else { throw new Error(`featureName ${from} is invalid`) } const onDone = (hadError: boolean) => { diffZone._streamState = { isStreaming: false, } this._onDidChangeStreaming.fire({ uri, diffareaid: diffZone.diffareaid }) - if (featureName === 'Ctrl+K') { + if (from === 'QuickEdit') { const ctrlKZone = this.diffAreaOfId[opts.diffareaid] as CtrlKZone ctrlKZone._linkedStreamingDiffZone = null @@ -1350,11 +1350,11 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { const extractText = (fullText: string, recentlyAddedTextLen: number) => { - if (featureName === 'Ctrl+K') { + if (from === 'QuickEdit') { if (isOllamaFIM) return fullText return extractCodeFromFIM({ text: fullText, recentlyAddedTextLen, midTag: modelFimTags.midTag }) } - else if (featureName === 'Ctrl+L') { + else if (from === 'Chat') { return extractCodeFromRegular({ text: fullText, recentlyAddedTextLen }) } throw 1 @@ -1367,9 +1367,9 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { let prevIgnoredSuffix = '' streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ - type: 'sendChatMessage', - useProviderFor: opts.featureName === 'Ctrl+L' ? 'FastApply' : 'Ctrl+K', - logging: { loggingName: `startApplying - ${featureName}` }, + messagesType: 'chatMessages', + useProviderFor: opts.from === 'Chat' ? 'FastApply' : 'Ctrl+K', + logging: { loggingName: `startApplying - ${from}` }, messages, onText: ({ newText: newText_ }) => { 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 6d7fc46b..28cd2539 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 @@ -45,7 +45,7 @@ const CodeButtonsOnHover = ({ text }: { text: string }) => { const onApply = useCallback(() => { inlineDiffService.startApplying({ - featureName: 'Ctrl+L', + from: 'Chat', applyStr: text, }) metricsService.capture('Apply Code', { length: text.length }) // capture the length only diff --git a/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx index f1a3456a..71609e14 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx @@ -7,11 +7,12 @@ import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'reac import { useSettingsState, useSidebarState, useChatThreadsState, useQuickEditState, useAccessor } from '../util/services.js'; import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js'; import { QuickEditPropsType } from '../../../quickEditActions.js'; -import { ButtonStop, ButtonSubmit, IconX, VoidInputForm } from '../sidebar-tsx/SidebarChat.js'; +import { ButtonStop, ButtonSubmit, IconX, VoidChatArea } from '../sidebar-tsx/SidebarChat.js'; import { ModelDropdown } from '../void-settings-tsx/ModelDropdown.js'; import { VOID_CTRL_K_ACTION_ID } from '../../../actionIDs.js'; import { useRefState } from '../util/helpers.js'; import { useScrollbarStyles } from '../util/useScrollbarStyles.js'; +import { isFeatureNameDisabled } from '../../../../../../../platform/void/common/voidSettingsTypes.js'; export const QuickEditChat = ({ diffareaid, @@ -42,9 +43,11 @@ export const QuickEditChat = ({ }, [onChangeHeight]); + const settingsState = useSettingsState() + // state of current message const [instructionsAreEmpty, setInstructionsAreEmpty] = useState(!(initText ?? '')) // the user's instructions - const isDisabled = instructionsAreEmpty + const isDisabled = instructionsAreEmpty || !!isFeatureNameDisabled('Ctrl+K', settingsState) const [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone] = useRefState(initStreamingDiffZoneId) const isStreaming = currStreamingDiffZoneRef.current !== null @@ -55,7 +58,7 @@ export const QuickEditChat = ({ textAreaFnsRef.current?.disable() const id = inlineDiffsService.startApplying({ - featureName: 'Ctrl+K', + from: 'QuickEdit', diffareaid: diffareaid, }) setCurrentlyStreamingDiffZone(id ?? null) @@ -79,7 +82,7 @@ export const QuickEditChat = ({ const keybindingString = accessor.get('IKeybindingService').lookupKeybinding(VOID_CTRL_K_ACTION_ID)?.getLabel() return
- - +
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 2aa7da49..425ce3c9 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 @@ -23,8 +23,9 @@ export const ErrorDisplay = ({ const [isExpanded, setIsExpanded] = useState(false); const details = errorDetails(fullError) + const isExpandable = !!details - const message = message_ === 'TypeError: fetch failed' ? `TypeError: fetch failed. This likely means you specified the wrong endpoint in Void Settings, or a provider like Ollama is powered off.` : message_ + '' + const message = message_ + '' return (
@@ -45,7 +46,7 @@ export const ErrorDisplay = ({
- {details && ( + {isExpandable && (