mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
andrew ollamaFIM progress
This commit is contained in:
parent
01b1358a01
commit
e4d747d0a6
9 changed files with 205 additions and 99 deletions
|
|
@ -26,6 +26,7 @@
|
||||||
"cookie": "^0.4.0",
|
"cookie": "^0.4.0",
|
||||||
"http-proxy-agent": "^7.0.0",
|
"http-proxy-agent": "^7.0.0",
|
||||||
"https-proxy-agent": "^7.0.2",
|
"https-proxy-agent": "^7.0.2",
|
||||||
|
"debounced": "1.0.2",
|
||||||
"jschardet": "3.1.3",
|
"jschardet": "3.1.3",
|
||||||
"kerberos": "2.1.1",
|
"kerberos": "2.1.1",
|
||||||
"minimist": "^1.2.6",
|
"minimist": "^1.2.6",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||||
*--------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from './llmMessageTypes.js';
|
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, ServiceSendLLMMessageParams, MainSendLLMMessageParams, MainLLMMessageAbortParams, ServiceModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, MainModelListParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from './llmMessageTypes.js';
|
||||||
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
|
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||||
import { IMainProcessService } from '../../ipc/common/mainProcessService.js';
|
import { IMainProcessService } from '../../ipc/common/mainProcessService.js';
|
||||||
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
|
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
|
||||||
|
|
@ -96,31 +96,29 @@ export class LLMMessageService extends Disposable implements ILLMMessageService
|
||||||
onError({ message: 'Please add a Provider in Settings!', fullError: null })
|
onError({ message: 'Please add a Provider in Settings!', fullError: null })
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const { providerName, modelName } = modelSelection
|
const { providerName, modelName } = modelSelection
|
||||||
|
|
||||||
// add ai instructions here because we don't have access to voidSettingsService on the other side of the proxy
|
|
||||||
const aiInstructions = this.voidSettingsService.state.globalSettings.aiInstructions
|
|
||||||
if (aiInstructions)
|
|
||||||
proxyParams.messages.unshift({ role: 'system', content: aiInstructions })
|
|
||||||
|
|
||||||
// add state for request id
|
// add state for request id
|
||||||
const requestId_ = generateUuid();
|
const requestId = generateUuid();
|
||||||
this.onTextHooks_llm[requestId_] = onText
|
this.onTextHooks_llm[requestId] = onText
|
||||||
this.onFinalMessageHooks_llm[requestId_] = onFinalMessage
|
this.onFinalMessageHooks_llm[requestId] = onFinalMessage
|
||||||
this.onErrorHooks_llm[requestId_] = onError
|
this.onErrorHooks_llm[requestId] = onError
|
||||||
|
|
||||||
|
const { aiInstructions } = this.voidSettingsService.state.globalSettings
|
||||||
const { settingsOfProvider } = this.voidSettingsService.state
|
const { settingsOfProvider } = this.voidSettingsService.state
|
||||||
|
|
||||||
// params will be stripped of all its functions over the IPC channel
|
// params will be stripped of all its functions over the IPC channel
|
||||||
this.channel.call('sendLLMMessage', {
|
this.channel.call('sendLLMMessage', {
|
||||||
...proxyParams,
|
...proxyParams,
|
||||||
requestId: requestId_,
|
aiInstructions,
|
||||||
|
requestId,
|
||||||
providerName,
|
providerName,
|
||||||
modelName,
|
modelName,
|
||||||
settingsOfProvider,
|
settingsOfProvider,
|
||||||
} satisfies MainLLMMessageParams);
|
} satisfies MainSendLLMMessageParams);
|
||||||
|
|
||||||
return requestId_
|
return requestId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -147,6 +145,7 @@ export class LLMMessageService extends Disposable implements ILLMMessageService
|
||||||
} satisfies MainModelListParams<OllamaModelResponse>)
|
} satisfies MainModelListParams<OllamaModelResponse>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
openAICompatibleList = (params: ServiceModelListParams<OpenaiCompatibleModelResponse>) => {
|
openAICompatibleList = (params: ServiceModelListParams<OpenaiCompatibleModelResponse>) => {
|
||||||
const { onSuccess, onError, ...proxyParams } = params
|
const { onSuccess, onError, ...proxyParams } = params
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||||
*--------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { IRange } from '../../../editor/common/core/range'
|
|
||||||
import { ProviderName, SettingsOfProvider } from './voidSettingsTypes.js'
|
import { ProviderName, SettingsOfProvider } from './voidSettingsTypes.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,66 +35,67 @@ export type _InternalLLMMessage = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type ServiceSendLLMFeatureParams = {
|
type SendLLMType = {
|
||||||
useProviderFor: 'Ctrl+K';
|
type: 'sendLLMMessage';
|
||||||
range: IRange;
|
|
||||||
} | {
|
|
||||||
useProviderFor: 'Ctrl+L';
|
|
||||||
} | {
|
|
||||||
useProviderFor: 'Autocomplete';
|
|
||||||
range: IRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
// params to the true sendLLMMessage function
|
|
||||||
export type LLMMMessageParams = {
|
|
||||||
onText: OnText;
|
|
||||||
onFinalMessage: OnFinalMessage;
|
|
||||||
onError: OnError;
|
|
||||||
abortRef: AbortRef;
|
|
||||||
|
|
||||||
messages: LLMMessage[];
|
messages: LLMMessage[];
|
||||||
|
} | {
|
||||||
logging: {
|
type: 'ollamaFIM';
|
||||||
loggingName: string,
|
messages: {
|
||||||
};
|
prefix: string;
|
||||||
providerName: ProviderName;
|
suffix: string;
|
||||||
modelName: string;
|
}
|
||||||
settingsOfProvider: SettingsOfProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// service types
|
||||||
export type ServiceSendLLMMessageParams = {
|
export type ServiceSendLLMMessageParams = {
|
||||||
onText: OnText;
|
onText: OnText;
|
||||||
onFinalMessage: OnFinalMessage;
|
onFinalMessage: OnFinalMessage;
|
||||||
onError: OnError;
|
onError: OnError;
|
||||||
|
logging: { loggingName: string, };
|
||||||
|
useProviderFor: 'Ctrl+K' | 'Ctrl+L' | 'Autocomplete';
|
||||||
|
} & SendLLMType
|
||||||
|
|
||||||
|
// params to the true sendLLMMessage function
|
||||||
|
export type SendLLMMMessageParams = {
|
||||||
|
onText: OnText;
|
||||||
|
onFinalMessage: OnFinalMessage;
|
||||||
|
onError: OnError;
|
||||||
|
logging: { loggingName: string, };
|
||||||
|
abortRef: AbortRef;
|
||||||
|
|
||||||
|
aiInstructions: string;
|
||||||
|
|
||||||
|
providerName: ProviderName;
|
||||||
|
modelName: string;
|
||||||
|
settingsOfProvider: SettingsOfProvider;
|
||||||
|
} & SendLLMType
|
||||||
|
|
||||||
messages: LLMMessage[];
|
|
||||||
|
|
||||||
logging: {
|
|
||||||
loggingName: string,
|
|
||||||
};
|
|
||||||
} & ServiceSendLLMFeatureParams
|
|
||||||
|
|
||||||
// can't send functions across a proxy, use listeners instead
|
// can't send functions across a proxy, use listeners instead
|
||||||
export type BlockedMainLLMMessageParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef'
|
export type BlockedMainLLMMessageParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef'
|
||||||
|
export type MainSendLLMMessageParams = Omit<SendLLMMMessageParams, BlockedMainLLMMessageParams> & { requestId: string } & SendLLMType
|
||||||
|
|
||||||
export type MainLLMMessageParams = Omit<LLMMMessageParams, BlockedMainLLMMessageParams> & { requestId: string }
|
|
||||||
export type MainLLMMessageAbortParams = { requestId: string }
|
export type MainLLMMessageAbortParams = { requestId: string }
|
||||||
|
|
||||||
export type EventLLMMessageOnTextParams = Parameters<OnText>[0] & { requestId: string }
|
export type EventLLMMessageOnTextParams = Parameters<OnText>[0] & { requestId: string }
|
||||||
export type EventLLMMessageOnFinalMessageParams = Parameters<OnFinalMessage>[0] & { requestId: string }
|
export type EventLLMMessageOnFinalMessageParams = Parameters<OnFinalMessage>[0] & { requestId: string }
|
||||||
export type EventLLMMessageOnErrorParams = Parameters<OnError>[0] & { requestId: string }
|
export type EventLLMMessageOnErrorParams = Parameters<OnError>[0] & { requestId: string }
|
||||||
|
|
||||||
export type _InternalSendLLMMessageFnType = (params: {
|
export type _InternalSendLLMMessageFnType = (
|
||||||
messages: _InternalLLMMessage[];
|
params: {
|
||||||
onText: OnText;
|
onText: OnText;
|
||||||
onFinalMessage: OnFinalMessage;
|
onFinalMessage: OnFinalMessage;
|
||||||
onError: OnError;
|
onError: OnError;
|
||||||
settingsOfProvider: SettingsOfProvider;
|
messages: _InternalLLMMessage[];
|
||||||
providerName: ProviderName;
|
|
||||||
modelName: string;
|
|
||||||
|
|
||||||
_setAborter: (aborter: () => void) => void;
|
settingsOfProvider: SettingsOfProvider;
|
||||||
}) => void
|
providerName: ProviderName;
|
||||||
|
modelName: string;
|
||||||
|
|
||||||
|
_setAborter: (aborter: () => void) => void;
|
||||||
|
}
|
||||||
|
) => void
|
||||||
|
|
||||||
// service -> main -> internal -> event (back to main)
|
// service -> main -> internal -> event (back to main)
|
||||||
// (browser)
|
// (browser)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ export const ollamaList: _InternalModelListFnType<OllamaModelResponse> = async (
|
||||||
const ollama = new Ollama({ host: thisConfig.endpoint })
|
const ollama = new Ollama({ host: thisConfig.endpoint })
|
||||||
ollama.list()
|
ollama.list()
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
console.log('MODELS!!!!!!!!!!!!!!!!!', response)
|
||||||
const { models } = response
|
const { models } = response
|
||||||
onSuccess({ models })
|
onSuccess({ models })
|
||||||
})
|
})
|
||||||
|
|
@ -38,6 +39,7 @@ export const ollamaList: _InternalModelListFnType<OllamaModelResponse> = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Ollama
|
// Ollama
|
||||||
export const sendOllamaMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
export const sendOllamaMsg: _InternalSendLLMMessageFnType = ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||||
|
|
||||||
|
|
@ -68,14 +70,6 @@ export const sendOllamaMsg: _InternalSendLLMMessageFnType = ({ messages, onText,
|
||||||
})
|
})
|
||||||
// when error/fail
|
// when error/fail
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
// if (typeof error === 'object') {
|
|
||||||
// const e = error.error as ErrorResponse['error']
|
|
||||||
// if (e) {
|
|
||||||
// const name = error.name ?? 'Error'
|
|
||||||
// onError({ error: `${name}: ${e}` })
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
onError({ message: error + '', fullError: error })
|
onError({ message: error + '', fullError: error })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||||
*--------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { LLMMMessageParams, OnText, OnFinalMessage, OnError, LLMMessage, _InternalLLMMessage } from '../../common/llmMessageTypes.js';
|
import { SendLLMMMessageParams, OnText, OnFinalMessage, OnError, LLMMessage, _InternalLLMMessage } from '../../common/llmMessageTypes.js';
|
||||||
import { IMetricsService } from '../../common/metricsService.js';
|
import { IMetricsService } from '../../common/metricsService.js';
|
||||||
|
|
||||||
import { sendAnthropicMsg } from './anthropic.js';
|
import { sendAnthropicMsg } from './anthropic.js';
|
||||||
|
|
@ -48,6 +48,8 @@ const cleanMessages = (messages: LLMMessage[]): _InternalLLMMessage[] => {
|
||||||
|
|
||||||
|
|
||||||
export const sendLLMMessage = ({
|
export const sendLLMMessage = ({
|
||||||
|
type,
|
||||||
|
aiInstructions,
|
||||||
messages: messages_,
|
messages: messages_,
|
||||||
onText: onText_,
|
onText: onText_,
|
||||||
onFinalMessage: onFinalMessage_,
|
onFinalMessage: onFinalMessage_,
|
||||||
|
|
@ -57,21 +59,31 @@ export const sendLLMMessage = ({
|
||||||
settingsOfProvider,
|
settingsOfProvider,
|
||||||
providerName,
|
providerName,
|
||||||
modelName,
|
modelName,
|
||||||
}: LLMMMessageParams,
|
}: SendLLMMMessageParams,
|
||||||
|
|
||||||
metricsService: IMetricsService
|
metricsService: IMetricsService
|
||||||
) => {
|
) => {
|
||||||
const messages = cleanMessages(messages_)
|
messages.unshift({ role: 'system', content: aiInstructions })
|
||||||
|
|
||||||
|
const messages = type === 'sendLLMMessage' ? cleanMessages(messages_) : []
|
||||||
|
|
||||||
|
|
||||||
|
const prefixAndSuffix = type === 'ollamaFIM' ? messages_ : null
|
||||||
|
|
||||||
// only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
// only captures number of messages and message "shape", no actual code, instructions, prompts, etc
|
||||||
const captureChatEvent = (eventId: string, extras?: object) => {
|
const captureLLMEvent = (eventId: string, extras?: object) => {
|
||||||
metricsService.capture(eventId, {
|
metricsService.capture(eventId, {
|
||||||
providerName,
|
providerName,
|
||||||
modelName,
|
modelName,
|
||||||
numMessages: messages?.length,
|
...type === 'sendLLMMessage' ? {
|
||||||
messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
numMessages: messages?.length,
|
||||||
origNumMessages: messages_?.length,
|
messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||||
origMessagesShape: messages_?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
origNumMessages: messages_?.length,
|
||||||
|
origMessagesShape: messages_?.map(msg => ({ role: msg.role, length: msg.content.length })),
|
||||||
|
|
||||||
|
} : type === 'ollamaFIM' ? {
|
||||||
|
|
||||||
|
} : {},
|
||||||
|
|
||||||
...extras,
|
...extras,
|
||||||
})
|
})
|
||||||
|
|
@ -91,26 +103,26 @@ export const sendLLMMessage = ({
|
||||||
|
|
||||||
const onFinalMessage: OnFinalMessage = ({ fullText }) => {
|
const onFinalMessage: OnFinalMessage = ({ fullText }) => {
|
||||||
if (_didAbort) return
|
if (_didAbort) return
|
||||||
captureChatEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() })
|
captureLLMEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() })
|
||||||
onFinalMessage_({ fullText })
|
onFinalMessage_({ fullText })
|
||||||
}
|
}
|
||||||
|
|
||||||
const onError: OnError = ({ message: error, fullError }) => {
|
const onError: OnError = ({ message: error, fullError }) => {
|
||||||
if (_didAbort) return
|
if (_didAbort) return
|
||||||
console.error('sendLLMMessage onError:', error)
|
console.error('sendLLMMessage onError:', error)
|
||||||
captureChatEvent(`${loggingName} - Error`, { error })
|
captureLLMEvent(`${loggingName} - Error`, { error })
|
||||||
onError_({ message: error, fullError })
|
onError_({ message: error, fullError })
|
||||||
}
|
}
|
||||||
|
|
||||||
const onAbort = () => {
|
const onAbort = () => {
|
||||||
captureChatEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length })
|
captureLLMEvent(`${loggingName} - Abort`, { messageLengthSoFar: _fullTextSoFar.length })
|
||||||
try { _aborter?.() } // aborter sometimes automatically throws an error
|
try { _aborter?.() } // aborter sometimes automatically throws an error
|
||||||
catch (e) { }
|
catch (e) { }
|
||||||
_didAbort = true
|
_didAbort = true
|
||||||
}
|
}
|
||||||
abortRef_.current = onAbort
|
abortRef_.current = onAbort
|
||||||
|
|
||||||
captureChatEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length })
|
captureLLMEvent(`${loggingName} - Sending Message`, { messageLength: messages[messages.length - 1]?.content.length })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (providerName) {
|
switch (providerName) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
|
import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||||
import { Emitter, Event } from '../../../base/common/event.js';
|
import { Emitter, Event } from '../../../base/common/event.js';
|
||||||
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainLLMMessageParams, AbortRef, LLMMMessageParams, MainLLMMessageAbortParams, MainModelListParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from '../common/llmMessageTypes.js';
|
import { EventLLMMessageOnTextParams, EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, MainSendLLMMessageParams, AbortRef, SendLLMMMessageParams, MainLLMMessageAbortParams, MainModelListParams, ModelListParams, EventModelListOnSuccessParams, EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, } from '../common/llmMessageTypes.js';
|
||||||
import { sendLLMMessage } from './llmMessage/sendLLMMessage.js'
|
import { sendLLMMessage } from './llmMessage/sendLLMMessage.js'
|
||||||
import { IMetricsService } from '../common/metricsService.js';
|
import { IMetricsService } from '../common/metricsService.js';
|
||||||
import { ollamaList } from './llmMessage/ollama.js';
|
import { ollamaList } from './llmMessage/ollama.js';
|
||||||
|
|
@ -91,13 +91,13 @@ export class LLMMessageChannel implements IServerChannel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the only place sendLLMMessage is actually called
|
// the only place sendLLMMessage is actually called
|
||||||
private async _callSendLLMMessage(params: MainLLMMessageParams) {
|
private async _callSendLLMMessage(params: MainSendLLMMessageParams) {
|
||||||
const { requestId } = params;
|
const { requestId } = params;
|
||||||
|
|
||||||
if (!(requestId in this._abortRefOfRequestId_llm))
|
if (!(requestId in this._abortRefOfRequestId_llm))
|
||||||
this._abortRefOfRequestId_llm[requestId] = { current: null }
|
this._abortRefOfRequestId_llm[requestId] = { current: null }
|
||||||
|
|
||||||
const mainThreadParams: LLMMMessageParams = {
|
const mainThreadParams: SendLLMMMessageParams = {
|
||||||
...params,
|
...params,
|
||||||
onText: ({ newText, fullText }) => { this._onText_llm.fire({ requestId, newText, fullText }); },
|
onText: ({ newText, fullText }) => { this._onText_llm.fire({ requestId, newText, fullText }); },
|
||||||
onFinalMessage: ({ fullText }) => { this._onFinalMessage_llm.fire({ requestId, fullText }); },
|
onFinalMessage: ({ fullText }) => { this._onFinalMessage_llm.fire({ requestId, fullText }); },
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@
|
||||||
|
|
||||||
import { Disposable } from '../../../../base/common/lifecycle.js';
|
import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||||
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.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 { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||||
import { ITextModel } from '../../../../editor/common/model.js';
|
import { ITextModel } from '../../../../editor/common/model.js';
|
||||||
import { Position } from '../../../../editor/common/core/position.js';
|
import { Position } from '../../../../editor/common/core/position.js';
|
||||||
import { InlineCompletion, InlineCompletionContext } from '../../../../editor/common/languages.js';
|
import { InlineCompletion, InlineCompletionContext, LocationLink } from '../../../../editor/common/languages.js';
|
||||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
||||||
import { Range } from '../../../../editor/common/core/range.js';
|
import { Range } from '../../../../editor/common/core/range.js';
|
||||||
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
||||||
|
|
@ -19,6 +18,7 @@ import { EditorResourceAccessor } from '../../../common/editor.js';
|
||||||
import { IModelService } from '../../../../editor/common/services/model.js';
|
import { IModelService } from '../../../../editor/common/services/model.js';
|
||||||
import { extractCodeFromRegular } from './helpers/extractCodeFromResult.js';
|
import { extractCodeFromRegular } from './helpers/extractCodeFromResult.js';
|
||||||
import { isWindows } from '../../../../base/common/platform.js';
|
import { isWindows } from '../../../../base/common/platform.js';
|
||||||
|
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
|
||||||
|
|
||||||
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts
|
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts
|
||||||
|
|
||||||
|
|
@ -499,7 +499,7 @@ const getAutocompletionMatchup = ({ prefix, autocompletion }: { prefix: string,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCompletionOptions = (prefixAndSuffix: PrefixAndSuffixInfo) => {
|
const getCompletionOptions = (prefixAndSuffix: PrefixAndSuffixInfo, relevantContext: string) => {
|
||||||
|
|
||||||
const { prefix, suffix, prefixToTheLeftOfCursor, suffixToTheRightOfCursor, suffixLines } = prefixAndSuffix
|
const { prefix, suffix, prefixToTheLeftOfCursor, suffixToTheRightOfCursor, suffixLines } = prefixAndSuffix
|
||||||
|
|
||||||
|
|
@ -520,6 +520,8 @@ const getCompletionOptions = (prefixAndSuffix: PrefixAndSuffixInfo) => {
|
||||||
predictionType = 'single-line'
|
predictionType = 'single-line'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llmPrefix = llmPrefix + '\n\n/* Relevant context:\n' + relevantContext + '\n*/\n'
|
||||||
|
|
||||||
// default parameters
|
// default parameters
|
||||||
let shouldGenerate = true
|
let shouldGenerate = true
|
||||||
let stopTokens: string[] = allLinebreakSymbols // default to multi-line prediction
|
let stopTokens: string[] = allLinebreakSymbols // default to multi-line prediction
|
||||||
|
|
@ -545,6 +547,9 @@ export interface IAutocompleteService {
|
||||||
export const IAutocompleteService = createDecorator<IAutocompleteService>('AutocompleteService');
|
export const IAutocompleteService = createDecorator<IAutocompleteService>('AutocompleteService');
|
||||||
|
|
||||||
export class AutocompleteService extends Disposable implements IAutocompleteService {
|
export class AutocompleteService extends Disposable implements IAutocompleteService {
|
||||||
|
|
||||||
|
static readonly ID = 'void.autocompleteService'
|
||||||
|
|
||||||
_serviceBrand: undefined;
|
_serviceBrand: undefined;
|
||||||
|
|
||||||
private _autocompletionId: number = 0;
|
private _autocompletionId: number = 0;
|
||||||
|
|
@ -562,11 +567,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
): Promise<InlineCompletion[]> {
|
): Promise<InlineCompletion[]> {
|
||||||
|
|
||||||
const disabled = true
|
|
||||||
const testMode = false
|
const testMode = false
|
||||||
|
|
||||||
if (disabled) return [];
|
|
||||||
|
|
||||||
const docUriStr = model.uri.toString();
|
const docUriStr = model.uri.toString();
|
||||||
|
|
||||||
const prefixAndSuffix = getPrefixAndSuffixInfo(model, position)
|
const prefixAndSuffix = getPrefixAndSuffixInfo(model, position)
|
||||||
|
|
@ -670,7 +672,15 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { shouldGenerate, predictionType, stopTokens, llmPrefix, llmSuffix } = getCompletionOptions(prefixAndSuffix) // TODO use stop tokens
|
|
||||||
|
// NEW: 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
|
||||||
|
|
||||||
if (!shouldGenerate) return []
|
if (!shouldGenerate) return []
|
||||||
|
|
||||||
|
|
@ -700,12 +710,14 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
newAutocompletion.llmPromise = new Promise((resolve, reject) => {
|
newAutocompletion.llmPromise = new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const requestId = this._llmMessageService.sendLLMMessage({
|
const requestId = this._llmMessageService.sendLLMMessage({
|
||||||
prefix: llmPrefix,
|
type: 'ollamaFIM',
|
||||||
suffix: llmSuffix,
|
// TODO: Incorporate relevant context directly into the prefix
|
||||||
stopTokens: stopTokens,
|
messages: {
|
||||||
|
prefix: llmPrefix,
|
||||||
|
suffix: llmSuffix,
|
||||||
|
},
|
||||||
logging: { loggingName: 'Autocomplete' },
|
logging: { loggingName: 'Autocomplete' },
|
||||||
messages: [],
|
onText: async ({ fullText }) => {
|
||||||
onText: async ({ newText, fullText }) => {
|
|
||||||
|
|
||||||
newAutocompletion.insertText = fullText
|
newAutocompletion.insertText = fullText
|
||||||
|
|
||||||
|
|
@ -735,7 +747,6 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
reject(message)
|
reject(message)
|
||||||
},
|
},
|
||||||
useProviderFor: 'Autocomplete',
|
useProviderFor: 'Autocomplete',
|
||||||
range: { startLineNumber: position.lineNumber, startColumn: position.column, endLineNumber: position.lineNumber, endColumn: position.column },
|
|
||||||
})
|
})
|
||||||
newAutocompletion.requestId = requestId
|
newAutocompletion.requestId = requestId
|
||||||
|
|
||||||
|
|
@ -770,6 +781,84 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper method to gather ~N lines above and below the user's current line,
|
||||||
|
// and recursively gather lines around any symbol definitions encountered.
|
||||||
|
private async _gatherRelevantContextForPosition(
|
||||||
|
model: ITextModel,
|
||||||
|
position: Position,
|
||||||
|
recursionDepth: number,
|
||||||
|
linesAround: number
|
||||||
|
): Promise<string> {
|
||||||
|
// We'll do a BFS-like approach: for each position or definition, gather lines around it,
|
||||||
|
// then attempt to find the definition of any symbols in that range, up to 'recursionDepth' times.
|
||||||
|
|
||||||
|
// A set of "key" strings to avoid repeating the same location or line chunk
|
||||||
|
const visitedRanges = new Set<string>();
|
||||||
|
const collectedSnippets: string[] = [];
|
||||||
|
|
||||||
|
// A queue of tasks, each being a tuple of: (model, position, depth)
|
||||||
|
const tasks: Array<{ model: ITextModel, position: Position, depth: number }> = [];
|
||||||
|
tasks.push({ model, position, depth: recursionDepth });
|
||||||
|
|
||||||
|
const getSnippetAroundLine = (model: ITextModel, lineNumber: number, linesAround: number): string => {
|
||||||
|
const startLine = Math.max(1, lineNumber - linesAround);
|
||||||
|
const endLine = Math.min(model.getLineCount(), lineNumber + linesAround);
|
||||||
|
const lines: string[] = [];
|
||||||
|
for (let i = startLine; i <= endLine; i++) {
|
||||||
|
lines.push(model.getLineContent(i));
|
||||||
|
}
|
||||||
|
return lines.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
while (tasks.length > 0) {
|
||||||
|
const { model: currentModel, position: currentPos, depth } = tasks.shift()!;
|
||||||
|
|
||||||
|
if (depth < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather snippet around the current line
|
||||||
|
const snippet = getSnippetAroundLine(currentModel, currentPos.lineNumber, linesAround);
|
||||||
|
const snippetKey = `${currentModel.uri.toString()}:${currentPos.lineNumber}`;
|
||||||
|
if (!visitedRanges.has(snippetKey)) {
|
||||||
|
visitedRanges.add(snippetKey);
|
||||||
|
collectedSnippets.push(`-- Snippet around line ${currentPos.lineNumber} --\n${snippet}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to gather definitions for the symbol at this position
|
||||||
|
// We just pick all definition providers and see if any has a definition
|
||||||
|
const providers = this._langFeatureService.definitionProvider.ordered(currentModel);
|
||||||
|
for (const provider of providers) {
|
||||||
|
try {
|
||||||
|
const definitions = await provider.provideDefinition(currentModel, currentPos, CancellationToken.None);
|
||||||
|
if (!definitions) continue;
|
||||||
|
|
||||||
|
// definitions can be a single LocationLink or an array
|
||||||
|
const defArray: LocationLink[] = Array.isArray(definitions) ? definitions : [definitions];
|
||||||
|
for (const def of defArray) {
|
||||||
|
if (!def.uri) continue;
|
||||||
|
if (typeof def.range === 'undefined') continue;
|
||||||
|
const definitionModel = this._modelService.getModel(def.uri);
|
||||||
|
if (!definitionModel) continue;
|
||||||
|
|
||||||
|
// We'll queue up a new task for that definition range
|
||||||
|
const defPos = new Position(def.range.startLineNumber, def.range.startColumn);
|
||||||
|
const defKey = `${def.uri.toString()}:${defPos.lineNumber}`;
|
||||||
|
if (!visitedRanges.has(defKey)) {
|
||||||
|
tasks.push({ model: definitionModel, position: defPos, depth: depth - 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// If a provider fails, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the joined context
|
||||||
|
return collectedSnippets.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ILanguageFeaturesService private _langFeatureService: ILanguageFeaturesService,
|
@ILanguageFeaturesService private _langFeatureService: ILanguageFeaturesService,
|
||||||
@ILLMMessageService private readonly _llmMessageService: ILLMMessageService,
|
@ILLMMessageService private readonly _llmMessageService: ILLMMessageService,
|
||||||
|
|
@ -780,12 +869,14 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
|
|
||||||
this._langFeatureService.inlineCompletionsProvider.register('*', {
|
this._langFeatureService.inlineCompletionsProvider.register('*', {
|
||||||
provideInlineCompletions: async (model, position, context, token) => {
|
provideInlineCompletions: async (model, position, context, token) => {
|
||||||
|
console.log('AAAAAAAAA')
|
||||||
const items = await this._provideInlineCompletionItems(model, position, context, token)
|
const items = await this._provideInlineCompletionItems(model, position, context, token)
|
||||||
|
|
||||||
// console.log('item: ', items?.[0]?.insertText)
|
// console.log('item: ', items?.[0]?.insertText)
|
||||||
return { items: items, }
|
return { items: items, }
|
||||||
},
|
},
|
||||||
freeInlineCompletions: (completions) => {
|
freeInlineCompletions: (completions) => {
|
||||||
|
console.log('BBBBBBBB')
|
||||||
|
|
||||||
// get the `docUriStr` and the `position` of the cursor
|
// get the `docUriStr` and the `position` of the cursor
|
||||||
const activePane = this._editorService.activeEditorPane;
|
const activePane = this._editorService.activeEditorPane;
|
||||||
|
|
@ -807,9 +898,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
// autocompletion.prefix + autocompletion.insertedText ~== insertedText
|
// autocompletion.prefix + autocompletion.insertedText ~== insertedText
|
||||||
completions.items.forEach(item => {
|
completions.items.forEach(item => {
|
||||||
this._autocompletionsOfDocument[docUriStr].items.forEach((autocompletion: Autocompletion) => {
|
this._autocompletionsOfDocument[docUriStr].items.forEach((autocompletion: Autocompletion) => {
|
||||||
if (removeLeftTabsAndTrimEnds(prefix)
|
if (removeLeftTabsAndTrimEnds(prefix) === removeLeftTabsAndTrimEnds(autocompletion.prefix + autocompletion.insertText)) {
|
||||||
=== removeLeftTabsAndTrimEnds(autocompletion.prefix + autocompletion.insertText)
|
|
||||||
) {
|
|
||||||
this._autocompletionsOfDocument[docUriStr].delete(autocompletion.id);
|
this._autocompletionsOfDocument[docUriStr].delete(autocompletion.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -822,7 +911,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerWorkbenchContribution2(AutocompleteService.ID, AutocompleteService, WorkbenchPhase.BlockRestore);
|
||||||
registerSingleton(IAutocompleteService, AutocompleteService, InstantiationType.Eager);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import * as dom from '../../../../base/browser/dom.js';
|
||||||
import { Widget } from '../../../../base/browser/ui/widget.js';
|
import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||||
import { URI } from '../../../../base/common/uri.js';
|
import { URI } from '../../../../base/common/uri.js';
|
||||||
import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js';
|
import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js';
|
||||||
import { ctrlKStream_prefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, fastApply_userMessage, fastApply_systemMessage, defaultFimTags } from './prompt/prompts.js';
|
import { voidPrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, fastApply_userMessage, fastApply_systemMessage, defaultFimTags } from './prompt/prompts.js';
|
||||||
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
import { ILLMMessageService } from '../../../../platform/void/common/llmMessageService.js';
|
||||||
|
|
||||||
import { mountCtrlK } from '../browser/react/out/quick-edit-tsx/index.js'
|
import { mountCtrlK } from '../browser/react/out/quick-edit-tsx/index.js'
|
||||||
|
|
@ -1304,13 +1304,24 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
||||||
const instructions = _mountInfo?.textAreaRef.current?.value ?? ''
|
const instructions = _mountInfo?.textAreaRef.current?.value ?? ''
|
||||||
|
|
||||||
// __TODO__ use Ollama's FIM api, if (isOllamaFIM) {...} else:
|
// __TODO__ use Ollama's FIM api, if (isOllamaFIM) {...} else:
|
||||||
const { prefix, suffix } = ctrlKStream_prefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
|
const { prefix, suffix } = voidPrefixAndSuffix({ fullFileStr: currentFileStr, startLine, endLine })
|
||||||
|
// if (isOllamaFIM) {
|
||||||
|
// messages = {
|
||||||
|
// type: 'ollamaFIM',
|
||||||
|
// prefix,
|
||||||
|
// suffix,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
const language = filenameToVscodeLanguage(uri.fsPath) ?? ''
|
||||||
const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, isOllamaFIM: false, fimTags: modelFimTags, language })
|
const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, isOllamaFIM: false, fimTags: modelFimTags, language })
|
||||||
|
// type: 'messages',
|
||||||
messages = [
|
messages = [
|
||||||
{ role: 'system', content: ctrlKStream_systemMessage({ fimTags: modelFimTags }), },
|
{ role: 'system', content: ctrlKStream_systemMessage({ fimTags: modelFimTags }), },
|
||||||
{ role: 'user', content: userContent, }
|
{ role: 'user', content: userContent, }
|
||||||
]
|
]
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
else { throw new Error(`featureName ${featureName} is invalid`) }
|
else { throw new Error(`featureName ${featureName} is invalid`) }
|
||||||
|
|
||||||
|
|
@ -1356,6 +1367,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
||||||
let prevIgnoredSuffix = ''
|
let prevIgnoredSuffix = ''
|
||||||
|
|
||||||
streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({
|
streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({
|
||||||
|
type: 'sendLLMMessage',
|
||||||
useProviderFor: featureName,
|
useProviderFor: featureName,
|
||||||
logging: { loggingName: `startApplying - ${featureName}` },
|
logging: { loggingName: `startApplying - ${featureName}` },
|
||||||
messages,
|
messages,
|
||||||
|
|
@ -1400,7 +1412,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
||||||
onDone(true)
|
onDone(true)
|
||||||
},
|
},
|
||||||
|
|
||||||
range: { startLineNumber: startLine, endLineNumber: endLine, startColumn: 1, endColumn: Number.MAX_SAFE_INTEGER },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return diffZone
|
return diffZone
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ Please finish writing the new file by applying the change to the original file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const ctrlKStream_prefixAndSuffix = ({ fullFileStr, startLine, endLine }: { fullFileStr: string, startLine: number, endLine: number }) => {
|
export const voidPrefixAndSuffix = ({ fullFileStr, startLine, endLine }: { fullFileStr: string, startLine: number, endLine: number }) => {
|
||||||
|
|
||||||
const fullFileLines = fullFileStr.split('\n')
|
const fullFileLines = fullFileStr.split('\n')
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue