mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
autocomplete draft
This commit is contained in:
parent
e4d747d0a6
commit
e4bb15ef64
6 changed files with 217 additions and 86 deletions
|
|
@ -34,16 +34,18 @@ export type _InternalLLMMessage = {
|
|||
content: string;
|
||||
}
|
||||
|
||||
type _InternalOllamaFIMMessages = {
|
||||
prefix: string;
|
||||
suffix: string;
|
||||
stopTokens: string[];
|
||||
}
|
||||
|
||||
type SendLLMType = {
|
||||
type: 'sendLLMMessage';
|
||||
messages: LLMMessage[];
|
||||
} | {
|
||||
type: 'ollamaFIM';
|
||||
messages: {
|
||||
prefix: string;
|
||||
suffix: string;
|
||||
}
|
||||
messages: _InternalOllamaFIMMessages;
|
||||
}
|
||||
|
||||
// service types
|
||||
|
|
@ -56,7 +58,7 @@ export type ServiceSendLLMMessageParams = {
|
|||
} & SendLLMType
|
||||
|
||||
// params to the true sendLLMMessage function
|
||||
export type SendLLMMMessageParams = {
|
||||
export type SendLLMMessageParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
|
|
@ -74,7 +76,7 @@ export type SendLLMMMessageParams = {
|
|||
|
||||
// can't send functions across a proxy, use listeners instead
|
||||
export type BlockedMainLLMMessageParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef'
|
||||
export type MainSendLLMMessageParams = Omit<SendLLMMMessageParams, BlockedMainLLMMessageParams> & { requestId: string } & SendLLMType
|
||||
export type MainSendLLMMessageParams = Omit<SendLLMMessageParams, BlockedMainLLMMessageParams> & { requestId: string } & SendLLMType
|
||||
|
||||
export type MainLLMMessageAbortParams = { requestId: string }
|
||||
|
||||
|
|
@ -82,18 +84,32 @@ export type EventLLMMessageOnTextParams = Parameters<OnText>[0] & { requestId: s
|
|||
export type EventLLMMessageOnFinalMessageParams = Parameters<OnFinalMessage>[0] & { requestId: string }
|
||||
export type EventLLMMessageOnErrorParams = Parameters<OnError>[0] & { requestId: string }
|
||||
|
||||
|
||||
export type _InternalSendLLMMessageFnType = (
|
||||
params: {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
messages: _InternalLLMMessage[];
|
||||
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
providerName: ProviderName;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
modelName: string;
|
||||
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
|
||||
messages: _InternalLLMMessage[];
|
||||
}
|
||||
) => void
|
||||
|
||||
export type _InternalOllamaFIMMessageFnType = (
|
||||
params: {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
providerName: ProviderName;
|
||||
settingsOfProvider: SettingsOfProvider;
|
||||
modelName: string;
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
|
||||
messages: _InternalOllamaFIMMessages;
|
||||
}
|
||||
) => void
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Ollama } from 'ollama';
|
||||
import { _InternalModelListFnType, _InternalSendLLMMessageFnType, OllamaModelResponse } from '../../common/llmMessageTypes.js';
|
||||
import { _InternalModelListFnType, _InternalOllamaFIMMessageFnType, _InternalSendLLMMessageFnType, OllamaModelResponse } from '../../common/llmMessageTypes.js';
|
||||
import { defaultProviderSettings } from '../../common/voidSettingsTypes.js';
|
||||
|
||||
export const ollamaList: _InternalModelListFnType<OllamaModelResponse> = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }) => {
|
||||
|
|
@ -25,7 +25,7 @@ export const ollamaList: _InternalModelListFnType<OllamaModelResponse> = async (
|
|||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||
ollama.list()
|
||||
.then((response) => {
|
||||
console.log('MODELS!!!!!!!!!!!!!!!!!', response)
|
||||
// console.log('MODELS!!!!!!!!!!!!!!!!!', response)
|
||||
const { models } = response
|
||||
onSuccess({ models })
|
||||
})
|
||||
|
|
@ -39,6 +39,44 @@ export const ollamaList: _InternalModelListFnType<OllamaModelResponse> = async (
|
|||
}
|
||||
|
||||
|
||||
export const sendOllamaFIM: _InternalOllamaFIMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
||||
const thisConfig = settingsOfProvider.ollama
|
||||
// if endpoint is empty, normally ollama will send to 11434, but we want it to fail - the user should type it in
|
||||
if (!thisConfig.endpoint) throw new Error(`Ollama Endpoint was empty (please enter ${defaultProviderSettings.ollama.endpoint} if you want the default).`)
|
||||
|
||||
let fullText = ''
|
||||
|
||||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||
|
||||
ollama.generate({
|
||||
model: modelName,
|
||||
prompt: messages.prefix,
|
||||
suffix: messages.suffix,
|
||||
options: {
|
||||
stop: messages.stopTokens,
|
||||
},
|
||||
raw: true,
|
||||
stream: true,
|
||||
// options: { num_predict: parseMaxTokensStr(thisConfig.maxTokens) } // this is max_tokens
|
||||
})
|
||||
.then(async stream => {
|
||||
_setAborter(() => stream.abort())
|
||||
// iterate through the stream
|
||||
for await (const chunk of stream) {
|
||||
const newText = chunk.response;
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage({ fullText });
|
||||
console.log('!!!!! OLLAMA RESULT', JSON.stringify(fullText))
|
||||
})
|
||||
// when error/fail
|
||||
.catch((error) => {
|
||||
onError({ message: error + '', fullError: error })
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
// Ollama
|
||||
export const sendOllamaMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SendLLMMMessageParams, OnText, OnFinalMessage, OnError, LLMMessage, _InternalLLMMessage } from '../../common/llmMessageTypes.js';
|
||||
import { SendLLMMessageParams, OnText, OnFinalMessage, OnError, LLMMessage, _InternalLLMMessage } from '../../common/llmMessageTypes.js';
|
||||
import { IMetricsService } from '../../common/metricsService.js';
|
||||
|
||||
import { sendAnthropicMsg } from './anthropic.js';
|
||||
import { sendOllamaMsg } from './ollama.js';
|
||||
import { sendOllamaFIM, sendOllamaMsg } from './ollama.js';
|
||||
import { sendOpenAIMsg } from './openai.js';
|
||||
import { sendGeminiMsg } from './gemini.js';
|
||||
import { sendGroqMsg } from './groq.js';
|
||||
|
|
@ -59,16 +59,13 @@ export const sendLLMMessage = ({
|
|||
settingsOfProvider,
|
||||
providerName,
|
||||
modelName,
|
||||
}: SendLLMMMessageParams,
|
||||
}: SendLLMMessageParams,
|
||||
|
||||
metricsService: IMetricsService
|
||||
) => {
|
||||
messages.unshift({ role: 'system', content: aiInstructions })
|
||||
// messages.unshift({ role: 'system', content: aiInstructions })
|
||||
|
||||
const messages = type === 'sendLLMMessage' ? cleanMessages(messages_) : []
|
||||
|
||||
|
||||
const prefixAndSuffix = type === 'ollamaFIM' ? messages_ : null
|
||||
const messagesArr = type === 'sendLLMMessage' ? cleanMessages(messages_) : []
|
||||
|
||||
// only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
||||
const captureLLMEvent = (eventId: string, extras?: object) => {
|
||||
|
|
@ -76,8 +73,8 @@ export const sendLLMMessage = ({
|
|||
providerName,
|
||||
modelName,
|
||||
...type === 'sendLLMMessage' ? {
|
||||
numMessages: messages?.length,
|
||||
messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||
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 })),
|
||||
|
||||
|
|
@ -122,27 +119,30 @@ export const sendLLMMessage = ({
|
|||
}
|
||||
abortRef_.current = onAbort
|
||||
|
||||
captureLLMEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length })
|
||||
captureLLMEvent(`${loggingName} - Sending Message`, { messageLength: messagesArr[messagesArr.length - 1]?.content.length })
|
||||
|
||||
try {
|
||||
switch (providerName) {
|
||||
case 'anthropic':
|
||||
sendAnthropicMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
sendAnthropicMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'openAI':
|
||||
case 'openRouter':
|
||||
case 'deepseek':
|
||||
case 'openAICompatible':
|
||||
sendOpenAIMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
sendOpenAIMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'gemini':
|
||||
sendGeminiMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
sendGeminiMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'ollama':
|
||||
sendOllamaMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
if (type === 'ollamaFIM')
|
||||
sendOllamaFIM({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName })
|
||||
else
|
||||
sendOllamaMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
case 'groq':
|
||||
sendGroqMsg({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
sendGroqMsg({ messages: messagesArr, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, providerName });
|
||||
break;
|
||||
default:
|
||||
onError({ message: `Error: Void provider was "${providerName}", which is not recognized.`, fullError: null })
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainSendLLMMessageParams, AbortRef, SendLLMMMessageParams, MainLLMMessageAbortParams, MainModelListParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from '../common/llmMessageTypes.js';
|
||||
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainSendLLMMessageParams, AbortRef, SendLLMMessageParams, MainLLMMessageAbortParams, MainModelListParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from '../common/llmMessageTypes.js';
|
||||
import { sendLLMMessage } from './llmMessage/sendLLMMessage.js'
|
||||
import { IMetricsService } from '../common/metricsService.js';
|
||||
import { ollamaList } from './llmMessage/ollama.js';
|
||||
|
|
@ -97,7 +97,7 @@ export class LLMMessageChannel implements IServerChannel {
|
|||
if (!(requestId in this._abortRefOfRequestId_llm))
|
||||
this._abortRefOfRequestId_llm[requestId] = { current: null }
|
||||
|
||||
const mainThreadParams: SendLLMMMessageParams = {
|
||||
const mainThreadParams: SendLLMMessageParams = {
|
||||
...params,
|
||||
onText: ({ newText, fullText }) => { this._onText_llm.fire({ requestId, newText, fullText }); },
|
||||
onFinalMessage: ({ fullText }) => { this._onFinalMessage_llm.fire({ requestId, fullText }); },
|
||||
|
|
|
|||
|
|
@ -135,6 +135,12 @@ class LRUCache<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
type AutocompletionPredictionType =
|
||||
| 'single-line-fill-middle'
|
||||
| 'single-line-redo-suffix'
|
||||
// | 'multi-line-start-here'
|
||||
| 'multi-line-start-on-next-line'
|
||||
| 'do-not-predict'
|
||||
|
||||
type Autocompletion = {
|
||||
id: number,
|
||||
|
|
@ -145,7 +151,7 @@ type Autocompletion = {
|
|||
startTime: number,
|
||||
endTime: number | undefined,
|
||||
status: 'pending' | 'finished' | 'error',
|
||||
type: 'single-line' | 'single-line-redo-suffix' | 'multi-line'
|
||||
type: AutocompletionPredictionType,
|
||||
llmPromise: Promise<string> | undefined,
|
||||
insertText: string,
|
||||
requestId: string | null,
|
||||
|
|
@ -345,25 +351,26 @@ const postprocessAutocompletion = ({ autocompletionMatchup, autocompletion, pref
|
|||
// returns the text in the autocompletion to display, assuming the prefix is already matched
|
||||
const toInlineCompletions = ({ autocompletionMatchup, autocompletion, prefixAndSuffix, position, debug }: { autocompletionMatchup: AutocompletionMatchupBounds, autocompletion: Autocompletion, prefixAndSuffix: PrefixAndSuffixInfo, position: Position, debug?: boolean }): { insertText: string, range: Range }[] => {
|
||||
|
||||
// postprocess the insertText
|
||||
let trimmedInsertText = postprocessAutocompletion({ autocompletionMatchup, autocompletion, prefixAndSuffix, })
|
||||
|
||||
// postprocess `rangeToReplace`
|
||||
let rangeToReplace: Range = new Range(position.lineNumber, position.column, position.lineNumber, position.column)
|
||||
if (autocompletion.type === 'single-line-redo-suffix' // did we redo the line? if so, replace the whole suffix
|
||||
|
||||
// handle special cases
|
||||
|
||||
// if we are predicting starting on the next line, add a newline character
|
||||
if (autocompletion.type === 'multi-line-start-on-next-line') {
|
||||
trimmedInsertText = _ln + trimmedInsertText
|
||||
}
|
||||
// if we redid the suffix, replace the suffix
|
||||
if (autocompletion.type === 'single-line-redo-suffix'
|
||||
&& isSubsequence({ // check that the old text contains the same brackets + symbols as the new text
|
||||
subsequence: removeAllWhitespace(prefixAndSuffix.suffixToTheRightOfCursor),
|
||||
of: removeAllWhitespace(autocompletion.insertText), // should not be `trimmedInsertText`
|
||||
of: removeAllWhitespace(autocompletion.insertText), // note that this should not be `trimmedInsertText`
|
||||
})
|
||||
) {
|
||||
rangeToReplace = new Range(position.lineNumber, position.column, position.lineNumber, Number.MAX_SAFE_INTEGER)
|
||||
} else { // did not matchup, do not show the autocompletion
|
||||
return [{
|
||||
insertText: '',
|
||||
range: rangeToReplace
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
return [{
|
||||
insertText: trimmedInsertText,
|
||||
range: rangeToReplace,
|
||||
|
|
@ -499,44 +506,86 @@ const getAutocompletionMatchup = ({ prefix, autocompletion }: { prefix: string,
|
|||
|
||||
}
|
||||
|
||||
const getCompletionOptions = (prefixAndSuffix: PrefixAndSuffixInfo, relevantContext: string) => {
|
||||
// const x = []
|
||||
// const
|
||||
// c[[]]
|
||||
// asd[[]] =
|
||||
// const [{{}}]
|
||||
//
|
||||
type CompletionOptions = {
|
||||
predictionType: AutocompletionPredictionType,
|
||||
shouldGenerate: boolean,
|
||||
llmPrefix: string,
|
||||
llmSuffix: string,
|
||||
stopTokens: string[],
|
||||
}
|
||||
const getCompletionOptions = (prefixAndSuffix: PrefixAndSuffixInfo, relevantContext: string, justAcceptedAutocompletion: boolean): CompletionOptions => {
|
||||
|
||||
const { prefix, suffix, prefixToTheLeftOfCursor, suffixToTheRightOfCursor, suffixLines } = prefixAndSuffix
|
||||
|
||||
// single line prediction unless the current line is blank
|
||||
let predictionType: Autocompletion['type']
|
||||
let llmPrefix = prefix
|
||||
let llmSuffix = suffix
|
||||
let completionOptions: CompletionOptions
|
||||
|
||||
if (!prefixToTheLeftOfCursor.trim() && !suffixToTheRightOfCursor.trim()) { // line is empty
|
||||
predictionType = 'multi-line'
|
||||
// if line is empty, do multiline completion
|
||||
const isLineEmpty = !prefixToTheLeftOfCursor.trim() && !suffixToTheRightOfCursor.trim()
|
||||
const isLinePrefixEmpty = removeAllWhitespace(prefixToTheLeftOfCursor).length === 0
|
||||
const isLineSuffixEmpty = removeAllWhitespace(suffixToTheRightOfCursor).length === 0
|
||||
|
||||
} else if (removeAllWhitespace(prefixAndSuffix.suffixToTheRightOfCursor).length < 4) { // suffix is less than 4 characters
|
||||
predictionType = 'single-line-redo-suffix'
|
||||
// TODO add context to prefix
|
||||
// llmPrefix = '\n\n/* Relevant context:\n' + relevantContext + '\n*/\n' + llmPrefix
|
||||
|
||||
// if we just accepted an autocompletion, predict a multiline completion starting on the next line
|
||||
if (justAcceptedAutocompletion && isLineSuffixEmpty) {
|
||||
const prefixWithNewline = prefix + _ln
|
||||
completionOptions = {
|
||||
predictionType: 'multi-line-start-on-next-line',
|
||||
shouldGenerate: true,
|
||||
llmPrefix: prefixWithNewline,
|
||||
llmSuffix: suffix,
|
||||
stopTokens: [`${_ln}${_ln}`] // double newlines
|
||||
}
|
||||
}
|
||||
// if the current line is empty, predict a single-line completion
|
||||
else if (isLineEmpty) {
|
||||
completionOptions = {
|
||||
predictionType: 'single-line-fill-middle',
|
||||
shouldGenerate: true,
|
||||
llmPrefix: prefix,
|
||||
llmSuffix: suffix,
|
||||
stopTokens: allLinebreakSymbols
|
||||
}
|
||||
}
|
||||
// if suffix is 3 or less characters, attempt to complete the line ignorning it
|
||||
else if (removeAllWhitespace(suffixToTheRightOfCursor).length <= 3) {
|
||||
const suffixLinesIgnoringThisLine = suffixLines.slice(1)
|
||||
llmSuffix = suffixLinesIgnoringThisLine.length === 0 ? '' : _ln + suffixLinesIgnoringThisLine.join(_ln)
|
||||
|
||||
const suffixStringIgnoringThisLine = suffixLinesIgnoringThisLine.length === 0 ? '' : _ln + suffixLinesIgnoringThisLine.join(_ln)
|
||||
completionOptions = {
|
||||
predictionType: 'single-line-redo-suffix',
|
||||
shouldGenerate: true,
|
||||
llmPrefix: prefix,
|
||||
llmSuffix: suffixStringIgnoringThisLine,
|
||||
stopTokens: allLinebreakSymbols
|
||||
}
|
||||
}
|
||||
// else attempt to complete the middle of the line if there is a prefix (the completion looks bad if there is no prefix)
|
||||
else if (!isLinePrefixEmpty) {
|
||||
completionOptions = {
|
||||
predictionType: 'single-line-fill-middle',
|
||||
shouldGenerate: true,
|
||||
llmPrefix: prefix,
|
||||
llmSuffix: suffix,
|
||||
stopTokens: allLinebreakSymbols
|
||||
}
|
||||
} else {
|
||||
predictionType = 'single-line'
|
||||
completionOptions = {
|
||||
predictionType: 'do-not-predict',
|
||||
shouldGenerate: false,
|
||||
llmPrefix: prefix,
|
||||
llmSuffix: suffix,
|
||||
stopTokens: []
|
||||
}
|
||||
}
|
||||
|
||||
llmPrefix = llmPrefix + '\n\n/* Relevant context:\n' + relevantContext + '\n*/\n'
|
||||
|
||||
// default parameters
|
||||
let shouldGenerate = true
|
||||
let stopTokens: string[] = allLinebreakSymbols // default to multi-line prediction
|
||||
|
||||
// Case 1: User is on a line with text to the left or right
|
||||
if (prefixToTheLeftOfCursor.trim() !== '' || suffixToTheRightOfCursor.trim() !== '') {
|
||||
stopTokens = allLinebreakSymbols // single line prediction
|
||||
}
|
||||
|
||||
// Don't generate if at the very beginning of a line
|
||||
if (prefixToTheLeftOfCursor === '') {
|
||||
shouldGenerate = false
|
||||
}
|
||||
|
||||
return { shouldGenerate, predictionType, stopTokens, llmPrefix, llmSuffix }
|
||||
return completionOptions
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -555,8 +604,9 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
private _autocompletionId: number = 0;
|
||||
private _autocompletionsOfDocument: { [docUriStr: string]: LRUCache<number, Autocompletion> } = {}
|
||||
|
||||
private _lastCompletionTime = 0
|
||||
private _lastPrefix: string = ''
|
||||
private _lastCompletionStart = 0
|
||||
private _lastCompletionAccept = 0
|
||||
// private _lastPrefix: string = ''
|
||||
|
||||
// used internally by vscode
|
||||
// fires after every keystroke and returns the completion to show
|
||||
|
|
@ -567,6 +617,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
token: CancellationToken,
|
||||
): Promise<InlineCompletion[]> {
|
||||
|
||||
console.log('START1')
|
||||
|
||||
const testMode = false
|
||||
|
||||
const docUriStr = model.uri.toString();
|
||||
|
|
@ -585,7 +637,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
}
|
||||
)
|
||||
}
|
||||
this._lastPrefix = prefix
|
||||
// this._lastPrefix = prefix
|
||||
console.log('START2')
|
||||
|
||||
// print all pending autocompletions
|
||||
// let _numPending = 0
|
||||
|
|
@ -604,19 +657,24 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
}
|
||||
}
|
||||
|
||||
console.log('START3')
|
||||
|
||||
// if there is a cached autocompletion, return it
|
||||
if (cachedAutocompletion && autocompletionMatchup) {
|
||||
|
||||
console.log('AAA')
|
||||
|
||||
|
||||
// console.log('id: ' + cachedAutocompletion.id)
|
||||
|
||||
if (cachedAutocompletion.status === 'finished') {
|
||||
// console.log('A1')
|
||||
console.log('A1')
|
||||
|
||||
const inlineCompletions = toInlineCompletions({ autocompletionMatchup, autocompletion: cachedAutocompletion, prefixAndSuffix, position, debug: true })
|
||||
return inlineCompletions
|
||||
|
||||
} else if (cachedAutocompletion.status === 'pending') {
|
||||
// console.log('A2')
|
||||
console.log('A2')
|
||||
|
||||
try {
|
||||
await cachedAutocompletion.llmPromise;
|
||||
|
|
@ -629,7 +687,9 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
}
|
||||
|
||||
} else if (cachedAutocompletion.status === 'error') {
|
||||
// console.log('A3')
|
||||
console.log('A3')
|
||||
} else {
|
||||
console.log('A4')
|
||||
}
|
||||
|
||||
return []
|
||||
|
|
@ -638,10 +698,10 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
// else if no more typing happens, then go forwards with the request
|
||||
// wait DEBOUNCE_TIME for the user to stop typing
|
||||
const thisTime = Date.now()
|
||||
this._lastCompletionTime = thisTime
|
||||
this._lastCompletionStart = thisTime
|
||||
const didTypingHappenDuringDebounce = await new Promise((resolve, reject) =>
|
||||
setTimeout(() => {
|
||||
if (this._lastCompletionTime === thisTime) {
|
||||
if (this._lastCompletionStart === thisTime) {
|
||||
resolve(false)
|
||||
} else {
|
||||
resolve(true)
|
||||
|
|
@ -651,6 +711,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
|
||||
// if more typing happened, then do not go forwards with the request
|
||||
if (didTypingHappenDuringDebounce) {
|
||||
console.log('START4')
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
@ -667,20 +729,24 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
if (numPending >= MAX_PENDING_REQUESTS) {
|
||||
// cancel the oldest pending request and remove it from cache
|
||||
this._autocompletionsOfDocument[docUriStr].delete(oldestPending.id)
|
||||
console.log('START5')
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('START6')
|
||||
|
||||
// NEW: gather relevant context from the code around the user's selection and definitions
|
||||
// gather relevant context from the code around the user's selection and definitions
|
||||
const relevantContext = await this._gatherRelevantContextForPosition(
|
||||
model,
|
||||
position,
|
||||
3, //recursion depth
|
||||
1 // number of lines to view in each recursion
|
||||
);
|
||||
const { shouldGenerate, predictionType, llmPrefix, llmSuffix } = getCompletionOptions(prefixAndSuffix, relevantContext) // TODO use stop tokens
|
||||
const justAcceptedAutocompletion = thisTime - this._lastCompletionAccept < 500
|
||||
|
||||
const { shouldGenerate, predictionType, llmPrefix, llmSuffix, stopTokens } = getCompletionOptions(prefixAndSuffix, relevantContext, justAcceptedAutocompletion)
|
||||
|
||||
if (!shouldGenerate) return []
|
||||
|
||||
|
|
@ -688,6 +754,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
return []
|
||||
}
|
||||
|
||||
|
||||
|
||||
// console.log('B')
|
||||
|
||||
// create a new autocompletion and add it to cache
|
||||
|
|
@ -706,15 +774,20 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
requestId: null,
|
||||
}
|
||||
|
||||
console.log('BBBBBBB')
|
||||
console.log('PREFIX', JSON.stringify(llmPrefix))
|
||||
console.log('SUFFIX', JSON.stringify(llmSuffix))
|
||||
console.log('PREDICTION_TYPE', predictionType)
|
||||
|
||||
// set parameters of `newAutocompletion` appropriately
|
||||
newAutocompletion.llmPromise = new Promise((resolve, reject) => {
|
||||
|
||||
const requestId = this._llmMessageService.sendLLMMessage({
|
||||
type: 'ollamaFIM',
|
||||
// TODO: Incorporate relevant context directly into the prefix
|
||||
messages: {
|
||||
prefix: llmPrefix,
|
||||
suffix: llmSuffix,
|
||||
stopTokens: stopTokens,
|
||||
},
|
||||
logging: { loggingName: 'Autocomplete' },
|
||||
onText: async ({ fullText }) => {
|
||||
|
|
@ -722,12 +795,13 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
newAutocompletion.insertText = fullText
|
||||
|
||||
// if generation doesn't match the prefix for the first few tokens generated, reject it
|
||||
if (!getAutocompletionMatchup({ prefix: this._lastPrefix, autocompletion: newAutocompletion })) {
|
||||
reject('LLM response did not match user\'s text.')
|
||||
}
|
||||
// if (!getAutocompletionMatchup({ prefix: this._lastPrefix, autocompletion: newAutocompletion })) {
|
||||
// reject('LLM response did not match user\'s text.')
|
||||
// }
|
||||
},
|
||||
onFinalMessage: ({ fullText }) => {
|
||||
|
||||
console.log('FULL TEXT', JSON.stringify(fullText))
|
||||
// newAutocompletion.prefix = prefix
|
||||
// newAutocompletion.suffix = suffix
|
||||
// newAutocompletion.startTime = Date.now()
|
||||
|
|
@ -738,6 +812,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
const [text, _] = extractCodeFromRegular({ text: fullText, recentlyAddedTextLen: 0 })
|
||||
newAutocompletion.insertText = postprocessResult(text)
|
||||
|
||||
console.log('RESULT', JSON.stringify(newAutocompletion.insertText))
|
||||
|
||||
resolve(newAutocompletion.insertText)
|
||||
|
||||
},
|
||||
|
|
@ -869,14 +945,12 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
|
||||
this._langFeatureService.inlineCompletionsProvider.register('*', {
|
||||
provideInlineCompletions: async (model, position, context, token) => {
|
||||
console.log('AAAAAAAAA')
|
||||
const items = await this._provideInlineCompletionItems(model, position, context, token)
|
||||
|
||||
// console.log('item: ', items?.[0]?.insertText)
|
||||
return { items: items, }
|
||||
},
|
||||
freeInlineCompletions: (completions) => {
|
||||
console.log('BBBBBBBB')
|
||||
|
||||
// get the `docUriStr` and the `position` of the cursor
|
||||
const activePane = this._editorService.activeEditorPane;
|
||||
|
|
@ -899,6 +973,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
completions.items.forEach(item => {
|
||||
this._autocompletionsOfDocument[docUriStr].items.forEach((autocompletion: Autocompletion) => {
|
||||
if (removeLeftTabsAndTrimEnds(prefix) === removeLeftTabsAndTrimEnds(autocompletion.prefix + autocompletion.insertText)) {
|
||||
console.log('ACCEPT AUTCOMPLETE', autocompletion.id)
|
||||
this._lastCompletionAccept = Date.now()
|
||||
this._autocompletionsOfDocument[docUriStr].delete(autocompletion.id);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
|
|||
this._setStreamState(threadId, { error: undefined })
|
||||
|
||||
const llmCancelToken = this._llmMessageService.sendLLMMessage({
|
||||
type: 'sendLLMMessage',
|
||||
logging: { loggingName: 'Chat' },
|
||||
messages: [
|
||||
{ role: 'system', content: chat_systemMessage },
|
||||
|
|
|
|||
Loading…
Reference in a new issue