From d4fc59034a327640e048a83b44b714c0108672af Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Wed, 15 Jan 2025 05:25:43 -0800 Subject: [PATCH] add ai instructions, fix prompts --- .../platform/void/common/llmMessageService.ts | 8 +- .../void/common/refreshModelService.ts | 8 +- .../void/common/voidSettingsService.ts | 31 +++---- .../platform/void/common/voidSettingsTypes.ts | 22 ++--- .../llmMessage/sendLLMMessage.ts | 2 - .../void/browser/inlineDiffsService.ts | 16 ++-- .../contrib/void/browser/prompt/prompts.ts | 43 ++++------ .../void/browser/react/src/util/inputs.tsx | 2 +- .../react/src/void-settings-tsx/Settings.tsx | 83 ++++++++----------- 9 files changed, 91 insertions(+), 124 deletions(-) diff --git a/src/vs/platform/void/common/llmMessageService.ts b/src/vs/platform/void/common/llmMessageService.ts index 9fd039c6..1ec051fc 100644 --- a/src/vs/platform/void/common/llmMessageService.ts +++ b/src/vs/platform/void/common/llmMessageService.ts @@ -69,14 +69,14 @@ export class LLMMessageService extends Disposable implements ILLMMessageService this.onErrorHooks_llm[e.requestId]?.(e) this._onRequestIdDone(e.requestId) })) - // ollama + // ollama .list() this._register((this.channel.listen('onSuccess_ollama') satisfies Event>)(e => { this.onSuccess_ollama[e.requestId]?.(e) })) this._register((this.channel.listen('onError_ollama') satisfies Event>)(e => { this.onError_ollama[e.requestId]?.(e) })) - // openaiCompatible + // openaiCompatible .list() this._register((this.channel.listen('onSuccess_openAICompatible') satisfies Event>)(e => { this.onSuccess_openAICompatible[e.requestId]?.(e) })) @@ -98,6 +98,10 @@ export class LLMMessageService extends Disposable implements ILLMMessageService } const { providerName, modelName } = modelSelection + const aiInstructions = this.voidSettingsService.state.globalSettings.aiInstructions + if (aiInstructions) + proxyParams.messages.unshift({ role: 'system', content: aiInstructions }) + // add state for request id const requestId_ = generateUuid(); this.onTextHooks_llm[requestId_] = onText diff --git a/src/vs/platform/void/common/refreshModelService.ts b/src/vs/platform/void/common/refreshModelService.ts index ea53537c..425393ef 100644 --- a/src/vs/platform/void/common/refreshModelService.ts +++ b/src/vs/platform/void/common/refreshModelService.ts @@ -80,7 +80,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ disposables.forEach(d => d.dispose()) disposables.clear() - if (!voidSettingsService.state.featureFlagSettings.autoRefreshModels) return + if (!voidSettingsService.state.globalSettings['autoRefreshModels']) return for (const providerName of refreshableProviderNames) { @@ -117,11 +117,11 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ } } - // on mount (when get init settings state), and if a relevant feature flag changes (detected natively right now by refreshing if any flag changes), start refreshing models + // on mount (when get init settings state), and if a relevant feature flag changes, start refreshing models voidSettingsService.waitForInitState.then(() => { initializePollingAndOnChange() this._register( - voidSettingsService.onDidChangeState((type) => { if (type === 'featureFlagSettings') initializePollingAndOnChange() }) + voidSettingsService.onDidChangeState((type) => { if (typeof type === 'object' && type[1] === 'autoRefreshModels') initializePollingAndOnChange() }) ) }) @@ -184,7 +184,7 @@ export class RefreshModelService extends Disposable implements IRefreshModelServ // check if we should poll // if it was originally called as `isPolling` and if the `autoRefreshModels` flag is enabled - if (isPolling && this.voidSettingsService.state.featureFlagSettings.autoRefreshModels) { + if (isPolling && this.voidSettingsService.state.globalSettings['autoRefreshModels']) { const timeoutId = setTimeout(() => this.refreshModels(providerName, enableProviderOnSuccess, options), REFRESH_INTERVAL) this._setTimeoutId(providerName, timeoutId) } diff --git a/src/vs/platform/void/common/voidSettingsService.ts b/src/vs/platform/void/common/voidSettingsService.ts index f3d43e6b..a85c31ca 100644 --- a/src/vs/platform/void/common/voidSettingsService.ts +++ b/src/vs/platform/void/common/voidSettingsService.ts @@ -11,10 +11,10 @@ import { registerSingleton, InstantiationType } from '../../instantiation/common import { createDecorator } from '../../instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js'; import { IMetricsService } from './metricsService.js'; -import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, modelInfoOfDefaultNames, VoidModelInfo, FeatureFlagSettings, FeatureFlagName, defaultFeatureFlagSettings } from './voidSettingsTypes.js'; +import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, modelInfoOfDefaultNames, VoidModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings } from './voidSettingsTypes.js'; -const STORAGE_KEY = 'void.voidSettingsStorage' +const STORAGE_KEY = 'void.settingsServiceStorage' type SetSettingOfProviderFn = ( providerName: ProviderName, @@ -28,7 +28,7 @@ type SetModelSelectionOfFeatureFn = ( options?: { doNotApplyEffects?: true } ) => Promise; -type SetFeatureFlagFn = (flagName: FeatureFlagName, newVal: boolean) => void; +type SetGlobalSettingFn = (settingName: T, newVal: GlobalSettings[T]) => void; export type ModelOption = { name: string, selection: ModelSelection } @@ -37,12 +37,13 @@ export type ModelOption = { name: string, selection: ModelSelection } export type VoidSettingsState = { readonly settingsOfProvider: SettingsOfProvider; // optionsOfProvider readonly modelSelectionOfFeature: ModelSelectionOfFeature; // stateOfFeature - readonly featureFlagSettings: FeatureFlagSettings; + readonly globalSettings: GlobalSettings; readonly _modelOptions: ModelOption[] // computed based on the two above items } -type EventProp = Exclude | 'all' +type RealVoidSettings = Exclude +type EventProp = T extends 'globalSettings' ? [T, keyof VoidSettingsState[T]] : T | 'all' export interface IVoidSettingsService { @@ -54,7 +55,7 @@ export interface IVoidSettingsService { setSettingOfProvider: SetSettingOfProviderFn; setModelSelectionOfFeature: SetModelSelectionOfFeatureFn; - setFeatureFlag: SetFeatureFlagFn; + setGlobalSetting: SetGlobalSettingFn; setAutodetectedModels(providerName: ProviderName, modelNames: string[], logging: { enableProviderOnSuccess?: boolean, isPolling?: boolean, isInvisible?: boolean }): void; toggleModelHidden(providerName: ProviderName, modelName: string): void; @@ -81,7 +82,7 @@ const defaultState = () => { const d: VoidSettingsState = { settingsOfProvider: deepClone(defaultSettingsOfProvider), modelSelectionOfFeature: { 'Ctrl+L': null, 'Ctrl+K': null, 'Autocomplete': null }, - featureFlagSettings: deepClone(defaultFeatureFlagSettings), + globalSettings: deepClone(defaultGlobalSettings), _modelOptions: _computeModelOptions(defaultSettingsOfProvider), // computed } return d @@ -150,7 +151,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } } - const newFeatureFlags = this.state.featureFlagSettings + const newGlobalSettings = this.state.globalSettings // if changed models or enabled a provider, recompute models list const modelsListChanged = settingName === 'models' || settingName === '_enabled' @@ -159,7 +160,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { const newState: VoidSettingsState = { modelSelectionOfFeature: newModelSelectionOfFeature, settingsOfProvider: newSettingsOfProvider, - featureFlagSettings: newFeatureFlags, + globalSettings: newGlobalSettings, _modelOptions: newModelsList, } @@ -187,17 +188,17 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } - setFeatureFlag: SetFeatureFlagFn = async (flagName, newVal) => { - const newState = { + setGlobalSetting: SetGlobalSettingFn = async (settingName, newVal) => { + const newState: VoidSettingsState = { ...this.state, - featureFlagSettings: { - ...this.state.featureFlagSettings, - [flagName]: newVal + globalSettings: { + ...this.state.globalSettings, + [settingName]: newVal } } this.state = newState await this._storeState() - this._onDidChangeState.fire('featureFlagSettings') + this._onDidChangeState.fire(['globalSettings', settingName]) } diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts index 2b068780..2577bfe7 100644 --- a/src/vs/platform/void/common/voidSettingsTypes.ts +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -397,26 +397,16 @@ export type RefreshableProviderName = typeof refreshableProviderNames[number] -export type FeatureFlagSettings = { +export type GlobalSettings = { autoRefreshModels: boolean; + aiInstructions: string; } -export const defaultFeatureFlagSettings: FeatureFlagSettings = { +export const defaultGlobalSettings: GlobalSettings = { autoRefreshModels: true, + aiInstructions: '', } -export type FeatureFlagName = keyof FeatureFlagSettings -export const featureFlagNames = Object.keys(defaultFeatureFlagSettings) as FeatureFlagName[] - -type FeatureFlagDisplayInfo = { - description: string, -} -export const displayInfoOfFeatureFlag = (featureFlag: FeatureFlagName): FeatureFlagDisplayInfo => { - if (featureFlag === 'autoRefreshModels') { - return { - description: `Automatically detect local providers and models (${refreshableProviderNames.map(providerName => displayInfoOfProviderName(providerName).title).join(', ')}).`, - } - } - throw new Error(`featureFlagInfo: Unknown feature flag: "${featureFlag}"`) -} +export type GlobalSettingName = keyof GlobalSettings +export const globalSettingNames = Object.keys(defaultGlobalSettings) as GlobalSettingName[] diff --git a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts index b3970614..5d1866db 100644 --- a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts +++ b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts @@ -37,7 +37,6 @@ export const sendLLMMessage = ({ modelName, numMessages: messages?.length, messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })), - version: '2024-11-14', ...extras, }) } @@ -62,7 +61,6 @@ export const sendLLMMessage = ({ const onError: OnError = ({ message: error, fullError }) => { if (_didAbort) return - console.log("ERROR!!!!!", error) console.error('sendLLMMessage onError:', error) captureChatEvent(`${loggingName} - Error`, { error }) onError_({ message: error, fullError }) diff --git a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts index bc8a47fa..a678bf58 100644 --- a/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts +++ b/src/vs/workbench/contrib/void/browser/inlineDiffsService.ts @@ -1203,9 +1203,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // add to history const { onFinishEdit } = this._addToHistory(uri) - - // __TODO__ ctrl+K should use Ollama's FIM method. - const ollamaStyleFIM = false + // __TODO__ let users customize modelFimTags + const isOllamaFIM = false // this._voidSettingsService.state.modelSelectionOfFeature['Ctrl+K']?.providerName === 'ollama' const modelFimTags = defaultFimTags const adding: Omit = { @@ -1240,20 +1239,21 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { if (featureName === 'Ctrl+L') { const userContent = ctrlLStream_prompt({ originalCode, userMessage, uri }) messages = [ - // TODO include more context too { role: 'system', content: ctrlLStream_systemMessage, }, { role: 'user', content: userContent, } ] } else if (featureName === 'Ctrl+K') { const { prefix, suffix } = ctrlKStream_prefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine }) - const language = filenameToVscodeLanguage(uri.fsPath) ?? '' - const userContent = ctrlKStream_prompt({ selection: originalCode, userMessage, prefix, suffix, ollamaStyleFIM, fimTags: modelFimTags, language }) // console.log('PREFIX:\n', prefix) // console.log('SUFFIX:\n', suffix) // console.log('USER CONTENT:\n', userContent) + + // __TODO__ use Ollama's FIM api + // if (isOllamaFIM) {...} else: + const language = filenameToVscodeLanguage(uri.fsPath) ?? '' + const userContent = ctrlKStream_prompt({ selection: originalCode, userMessage, prefix, suffix, isOllamaFIM: false, fimTags: modelFimTags, language }) messages = [ - // TODO include more context too (LSP, file history, etc) { role: 'system', content: ctrlKStream_systemMessage, }, { role: 'user', content: userContent, } ] @@ -1286,7 +1286,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { const extractText = (fullText: string, recentlyAddedTextLen: number) => { if (featureName === 'Ctrl+K') { - if (ollamaStyleFIM) return fullText + if (isOllamaFIM) return fullText return extractCodeFromFIM({ text: fullText, recentlyAddedTextLen, midTag: modelFimTags.midTag }) } else if (featureName === 'Ctrl+L') { diff --git a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts index 1d769964..ba064384 100644 --- a/src/vs/workbench/contrib/void/browser/prompt/prompts.ts +++ b/src/vs/workbench/contrib/void/browser/prompt/prompts.ts @@ -341,32 +341,18 @@ export const defaultFimTags: FimTagsType = { midTag: 'SELECTION', } -export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage, fimTags, ollamaStyleFIM, language }: - { selection: string, prefix: string, suffix: string, userMessage: string, ollamaStyleFIM: boolean, fimTags: FimTagsType, language: string }) => { +export const ctrlKStream_prompt = ({ selection, prefix, suffix, userMessage, fimTags, isOllamaFIM, language }: + { + selection: string, prefix: string, suffix: string, userMessage: string, fimTags: FimTagsType, language: string, + isOllamaFIM: false, // we require this be false for clarity + }) => { const { preTag, sufTag, midTag } = fimTags - - - if (ollamaStyleFIM) { - // const preTag = 'PRE' - // const sufTag = 'SUF' - // const midTag = 'MID' - return `\ -<${preTag}> -/* Original Selection: -${selection}*/ -/* Instructions: -${userMessage}*/ -${prefix} -<${sufTag}>${suffix} -<${midTag}>` - } // prompt the model artifically on how to do FIM - else { - // const preTag = 'BEFORE' - // const sufTag = 'AFTER' - // const midTag = 'SELECTION' - return `\ + // const preTag = 'BEFORE' + // const sufTag = 'AFTER' + // const midTag = 'SELECTION' + return `\ The user is selecting this code as their SELECTION: \`\`\` ${language} <${midTag}>${selection} @@ -377,12 +363,12 @@ ${userMessage} Please edit the SELECTION following the user's INSTRUCTIONS, and return the edited selection. -Note that the SELECTION has code that comes before it. This code is indicated with <${preTag}>...before<${preTag}/>. -Note also that the SELECTION has code that comes after it. This code is indicated with <${sufTag}>...after<${sufTag}/>. +Note that the SELECTION has code that comes before it. This code is indicated with <${preTag}>...before. +Note also that the SELECTION has code that comes after it. This code is indicated with <${sufTag}>...after. Instructions: -1. Your OUTPUT should be a SINGLE PIECE OF CODE of the form <${midTag}>...new_selection<${midTag}/>. Do NOT output any text or explanations before or after this. -2. You may ONLY CHANGE the original SELECTION, and NOT the content in the <${preTag}>...<${preTag}/> or <${sufTag}>...<${sufTag}/> tags. +1. Your OUTPUT should be a SINGLE PIECE OF CODE of the form <${midTag}>...new_selection. Do NOT output any text or explanations before or after this. +2. You may ONLY CHANGE the original SELECTION, and NOT the content in the <${preTag}>... or <${sufTag}>... tags. 3. Make sure all brackets in the new selection are balanced the same as in the original selection. 4. Be careful not to duplicate or remove variables, comments, or other syntax by mistake. @@ -390,8 +376,7 @@ Given the code: <${preTag}>${prefix} <${sufTag}>${suffix} -Return only the completion block of code (of the form \`\`\` ${language}\n <${midTag}>...new_selection<${midTag}/>\`\`\`):` - } +Return only the completion block of code (of the form \`\`\` ${language}\n <${midTag}>...new_selection\`\`\`):` }; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx index f82b32d9..c1421f66 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx @@ -73,7 +73,7 @@ export const VoidInputBox2 = forwardRef(fun if (r.scrollHeight === 0) return requestAnimationFrame(adjustHeight) const h = r.scrollHeight - const newHeight = Math.min(h, 500) + const newHeight = Math.min(h + 1, 500) // plus one to avoid scrollbar appearing when it shouldn't r.style.height = `${newHeight}px` }, []); diff --git a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx index ee446418..99cd222f 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/void-settings-tsx/Settings.tsx @@ -5,7 +5,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js' -import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidModelInfo, featureFlagNames, displayInfoOfFeatureFlag, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, defaultProviderSettings, nonlocalProviderNames, localProviderNames } from '../../../../../../../platform/void/common/voidSettingsTypes.js' +import { ProviderName, SettingName, displayInfoOfSettingName, providerNames, VoidModelInfo, globalSettingNames, customSettingNamesOfProvider, RefreshableProviderName, refreshableProviderNames, displayInfoOfProviderName, defaultProviderSettings, nonlocalProviderNames, localProviderNames, GlobalSettingName } from '../../../../../../../platform/void/common/voidSettingsTypes.js' import ErrorBoundary from '../sidebar-tsx/ErrorBoundary.js' import { VoidButton, VoidCheckBox, VoidCustomSelectBox, VoidInputBox, VoidInputBox2, VoidSwitch } from '../util/inputs.js' import { useAccessor, useIsDark, useRefreshModelListener, useRefreshModelState, useSettingsState } from '../util/services.js' @@ -355,31 +355,10 @@ export const VoidProviderSettings = ({ providerNames }: { providerNames: Provide } -// export const VoidFeatureFlagSettings = () => { -// const accessor = useAccessor() -// const voidSettingsService = accessor.get('IVoidSettingsService') -// const voidSettingsState = useSettingsState() - -// return <> -// {featureFlagNames.map((flagName) => { -// const value = voidSettingsState.featureFlagSettings[flagName] -// const { description } = displayInfoOfFeatureFlag(flagName) -// return
-//
-// { voidSettingsService.setFeatureFlag(flagName, !value) }} -// /> -//

{description}

-//
-//
-// })} -// -// } type TabName = 'models' | 'general' -export const VoidFeatureFlagSettings = () => { +export const AutoRefreshToggle = () => { + const settingName: GlobalSettingName = 'autoRefreshModels' const accessor = useAccessor() const voidSettingsService = accessor.get('IVoidSettingsService') @@ -387,22 +366,33 @@ export const VoidFeatureFlagSettings = () => { const voidSettingsState = useSettingsState() - return featureFlagNames.map((flagName) => { + // right now this is just `enabled_autoRefreshModels` + const enabled = voidSettingsState.globalSettings[settingName] - // right now this is just `enabled_autoRefreshModels` - const enabled = voidSettingsState.featureFlagSettings[flagName] - const { description } = displayInfoOfFeatureFlag(flagName) + return { + voidSettingsService.setGlobalSetting(settingName, !enabled) + metricsService.capture('Click', { action: 'Autorefresh Toggle', settingName, enabled: !enabled }) + }} + text={`Automatically detect local providers and models (${refreshableProviderNames.map(providerName => displayInfoOfProviderName(providerName).title).join(', ')}).`} + icon={enabled ? : } + disabled={false} + /> +} - return { - voidSettingsService.setFeatureFlag(flagName, !enabled) - metricsService.capture('Click', { action: 'Autorefresh Toggle', flagName, enabled: !enabled }) - }} - text={description} - icon={enabled ? : } - disabled={false} - /> - }) +export const AIInstructionsBox = () => { + const accessor = useAccessor() + const voidSettingsService = accessor.get('IVoidSettingsService') + const voidSettingsState = useSettingsState() + return { + voidSettingsService.setGlobalSetting('aiInstructions', newText) + }} + /> } export const FeaturesTab = () => { @@ -434,7 +424,7 @@ export const FeaturesTab = () => {

Models

- + @@ -571,13 +561,6 @@ const GeneralTab = () => { - {/*
-

Rules for AI

- {`placeholder: "Do not add ;'s. Do not change or delete spacing, formatting, or comments. Respond to queries in French when applicable. "`} -
*/} - - -

Built-in Settings

@@ -600,7 +583,13 @@ const GeneralTab = () => {
- {/* */} + +
+

AI Instructions

+

{`Instructions to include on all AI requests.`}

+ +
+ }