mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
Merge branch 'main' into re-add-autocomplete
This commit is contained in:
parent
48426403f3
commit
ef9ac7a9e3
30 changed files with 720 additions and 388 deletions
87
package-lock.json
generated
87
package-lock.json
generated
|
|
@ -23,6 +23,7 @@
|
|||
"@vscode/sudo-prompt": "9.3.1",
|
||||
"@vscode/tree-sitter-wasm": "^0.0.4",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"@vscode/windows-mutex": "^0.5.0",
|
||||
"@vscode/windows-process-tree": "^0.6.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
"https-proxy-agent": "^7.0.2",
|
||||
"jschardet": "3.1.3",
|
||||
"kerberos": "2.1.1",
|
||||
"lucide-react": "^0.460.0",
|
||||
"minimist": "^1.2.6",
|
||||
"native-is-elevated": "0.7.0",
|
||||
"native-keymap": "^3.3.5",
|
||||
|
|
@ -2065,6 +2067,52 @@
|
|||
"resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz",
|
||||
"integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ=="
|
||||
},
|
||||
"node_modules/@microsoft/fast-element": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.14.0.tgz",
|
||||
"integrity": "sha512-zXvuSOzvsu8zDTy9eby8ix8VqLop2rwKRgp++ZN2kTCsoB3+QJVoaGD2T/Cyso2ViZQFXNpiNCVKfnmxBvmWkQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@microsoft/fast-foundation": {
|
||||
"version": "2.50.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.50.0.tgz",
|
||||
"integrity": "sha512-8mFYG88Xea1jZf2TI9Lm/jzZ6RWR8x29r24mGuLojNYqIR2Bl8+hnswoV6laApKdCbGMPKnsAL/O68Q0sRxeVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/fast-element": "^1.14.0",
|
||||
"@microsoft/fast-web-utilities": "^5.4.1",
|
||||
"tabbable": "^5.2.0",
|
||||
"tslib": "^1.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/fast-foundation/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/@microsoft/fast-react-wrapper": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/fast-react-wrapper/-/fast-react-wrapper-0.3.25.tgz",
|
||||
"integrity": "sha512-jKzmk2xJV93RL/jEFXEZgBvXlKIY4N4kXy3qrjmBfFpqNi3VjY+oUTWyMnHRMC5EUhIFxD+Y1VD4u9uIPX3jQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/fast-element": "^1.14.0",
|
||||
"@microsoft/fast-foundation": "^2.50.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@microsoft/fast-web-utilities": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz",
|
||||
"integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"exenv-es6": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
|
@ -4111,6 +4159,21 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@vscode/webview-ui-toolkit": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.4.0.tgz",
|
||||
"integrity": "sha512-modXVHQkZLsxgmd5yoP3ptRC/G8NBDD+ob+ngPiWNQdlrH6H1xR/qgOBD85bfU3BhOB5sZzFWBwwhp9/SfoHww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@microsoft/fast-element": "^1.12.0",
|
||||
"@microsoft/fast-foundation": "^2.49.4",
|
||||
"@microsoft/fast-react-wrapper": "^0.3.22",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vscode/windows-ca-certs": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vscode/windows-ca-certs/-/windows-ca-certs-0.3.1.tgz",
|
||||
|
|
@ -8022,6 +8085,12 @@
|
|||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/exenv-es6": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz",
|
||||
"integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expand-brackets": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
|
||||
|
|
@ -14202,6 +14271,15 @@
|
|||
"es5-ext": "~0.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.460.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.460.0.tgz",
|
||||
"integrity": "sha512-BVtq/DykVeIvRTJvRAgCsOwaGL8Un3Bxh8MbDxMhEWlZay3T4IpEKDEpwt5KZ0KJMHzgm6jrltxlT5eXOWXDHg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
|
||||
|
|
@ -20392,6 +20470,12 @@
|
|||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/tabbable": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz",
|
||||
"integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/table": {
|
||||
"version": "5.4.6",
|
||||
"resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
|
||||
|
|
@ -21271,8 +21355,7 @@
|
|||
"node_modules/tslib": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
|
||||
},
|
||||
"node_modules/tsscmp": {
|
||||
"version": "1.0.6",
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@
|
|||
"@vscode/sudo-prompt": "9.3.1",
|
||||
"@vscode/tree-sitter-wasm": "^0.0.4",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"@vscode/windows-mutex": "^0.5.0",
|
||||
"@vscode/windows-process-tree": "^0.6.0",
|
||||
"@vscode/windows-registry": "^1.1.0",
|
||||
|
|
@ -100,6 +101,7 @@
|
|||
"https-proxy-agent": "^7.0.2",
|
||||
"jschardet": "3.1.3",
|
||||
"kerberos": "2.1.1",
|
||||
"lucide-react": "^0.460.0",
|
||||
"minimist": "^1.2.6",
|
||||
"native-is-elevated": "0.7.0",
|
||||
"native-keymap": "^3.3.5",
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ import { normalizeNFC } from '../../base/common/normalization.js';
|
|||
import { ICSSDevelopmentService, CSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js';
|
||||
import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationService } from '../../platform/extensionManagement/node/extensionSignatureVerificationService.js';
|
||||
|
||||
import { LLMMessageChannel } from '../../platform/void/electron-main/llmMessageChannel.js';
|
||||
|
||||
/**
|
||||
* The main VS Code application. There will only ever be one instance,
|
||||
* even if the user starts many instances (e.g. from the command line).
|
||||
|
|
@ -148,7 +150,7 @@ export class CodeApplication extends Disposable {
|
|||
@IStateService private readonly stateService: IStateService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IUserDataProfilesMainService private readonly userDataProfilesMainService: IUserDataProfilesMainService
|
||||
@IUserDataProfilesMainService private readonly userDataProfilesMainService: IUserDataProfilesMainService,
|
||||
) {
|
||||
super();
|
||||
|
||||
|
|
@ -508,6 +510,16 @@ export class CodeApplication extends Disposable {
|
|||
});
|
||||
|
||||
//#endregion
|
||||
|
||||
// //#region Void IPC
|
||||
// validatedIpcMain.handle('vscode:sendLLMMessage', async (event, data) => {
|
||||
// try {
|
||||
// await this.sendLLMMessage(data);
|
||||
// } catch (error) {
|
||||
// console.error('Error sending LLM message:', error);
|
||||
// }
|
||||
// });
|
||||
// //#endregion
|
||||
}
|
||||
|
||||
private onUnexpectedError(error: Error): void {
|
||||
|
|
@ -1225,6 +1237,11 @@ export class CodeApplication extends Disposable {
|
|||
mainProcessElectronServer.registerChannel('logger', loggerChannel);
|
||||
sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel));
|
||||
|
||||
// Void
|
||||
// const sendLLMMessageChannel = ProxyChannel.fromService(accessor.get(ISendLLMMessageService), disposables);
|
||||
const sendLLMMessageChannel = new LLMMessageChannel();
|
||||
mainProcessElectronServer.registerChannel('void-channel-sendLLMMessage', sendLLMMessageChannel);
|
||||
|
||||
// Extension Host Debug Broadcasting
|
||||
const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService));
|
||||
mainProcessElectronServer.registerChannel('extensionhostdebugservice', electronExtensionHostDebugBroadcastChannel);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export interface IInlineDiffService {
|
|||
removeDiffs(editor: ICodeEditor): void;
|
||||
}
|
||||
|
||||
export const IInlineDiffService = createDecorator<IInlineDiffService>('inlineDiffService');
|
||||
export const IInlineDiffService = createDecorator<IInlineDiffService>('inlineDiffServiceOld');
|
||||
|
||||
class InlineDiffService extends Disposable implements IInlineDiffService {
|
||||
private readonly _diffDecorations = new Map<ICodeEditor, string[]>();
|
||||
|
|
|
|||
105
src/vs/platform/void/browser/llmMessageService.ts
Normal file
105
src/vs/platform/void/browser/llmMessageService.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, LLMMessageServiceParams, ProxyLLMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js';
|
||||
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { IMainProcessService } from '../../ipc/common/mainProcessService.js';
|
||||
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
|
||||
import { generateUuid } from '../../../base/common/uuid.js';
|
||||
import { createDecorator } from '../../instantiation/common/instantiation.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { IDisposable } from '../../../base/common/lifecycle.js';
|
||||
|
||||
|
||||
// BROWSER IMPLEMENTATION OF SENDLLMMESSAGE
|
||||
export const ISendLLMMessageService = createDecorator<ISendLLMMessageService>('sendLLMMessageService');
|
||||
|
||||
// defines an interface that node/ creates and browser/ uses
|
||||
export interface ISendLLMMessageService {
|
||||
readonly _serviceBrand: undefined;
|
||||
sendLLMMessage: (params: LLMMessageServiceParams) => string;
|
||||
abort: (requestId: string) => void;
|
||||
}
|
||||
|
||||
|
||||
export class SendLLMMessageService implements ISendLLMMessageService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly channel: IChannel;
|
||||
|
||||
private readonly _disposablesOfRequestId: Record<string, IDisposable[]> = {}
|
||||
|
||||
constructor(
|
||||
@IMainProcessService mainProcessService: IMainProcessService // used as a renderer (only usable on client side)
|
||||
) {
|
||||
|
||||
this.channel = mainProcessService.getChannel('void-channel-sendLLMMessage')
|
||||
// const service = ProxyChannel.toService<LLMMessageChannel>(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service, not needed here
|
||||
}
|
||||
|
||||
_addDisposable(requestId: string, disposable: IDisposable) {
|
||||
if (!this._disposablesOfRequestId[requestId]) {
|
||||
this._disposablesOfRequestId[requestId] = []
|
||||
}
|
||||
this._disposablesOfRequestId[requestId].push(disposable)
|
||||
}
|
||||
|
||||
|
||||
|
||||
sendLLMMessage(params: LLMMessageServiceParams) {
|
||||
const requestId_ = generateUuid();
|
||||
const { onText, onFinalMessage, onError, ...proxyParams } = params;
|
||||
|
||||
// listen for listenerName='onText' | 'onFinalMessage' | 'onError', and call the original function on it
|
||||
|
||||
const onTextEvent: Event<ProxyOnTextPayload> = this.channel.listen('onText')
|
||||
this._addDisposable(requestId_,
|
||||
onTextEvent(e => {
|
||||
if (requestId_ !== e.requestId) return;
|
||||
onText(e)
|
||||
})
|
||||
)
|
||||
|
||||
const onFinalMessageEvent: Event<ProxyOnFinalMessagePayload> = this.channel.listen('onFinalMessage')
|
||||
this._addDisposable(requestId_,
|
||||
onFinalMessageEvent(e => {
|
||||
if (requestId_ !== e.requestId) return;
|
||||
onFinalMessage(e)
|
||||
this._dispose(requestId_)
|
||||
})
|
||||
)
|
||||
|
||||
const onErrorEvent: Event<ProxyOnErrorPayload> = this.channel.listen('onError')
|
||||
this._addDisposable(requestId_,
|
||||
onErrorEvent(e => {
|
||||
if (requestId_ !== e.requestId) return;
|
||||
console.log('event onError', JSON.stringify(e))
|
||||
onError(e)
|
||||
this._dispose(requestId_)
|
||||
})
|
||||
)
|
||||
|
||||
// params will be stripped of all its functions
|
||||
this.channel.call('sendLLMMessage', { ...proxyParams, requestId: requestId_ } satisfies ProxyLLMMessageParams);
|
||||
|
||||
return requestId_
|
||||
}
|
||||
|
||||
private _dispose(requestId: string) {
|
||||
if (!(requestId in this._disposablesOfRequestId)) return
|
||||
for (const disposable of this._disposablesOfRequestId[requestId]) {
|
||||
disposable.dispose()
|
||||
}
|
||||
delete this._disposablesOfRequestId[requestId]
|
||||
}
|
||||
|
||||
abort(requestId: string) {
|
||||
this.channel.call('abort', { requestId } satisfies ProxyLLMMessageAbortParams);
|
||||
this._dispose(requestId)
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed);
|
||||
|
||||
68
src/vs/platform/void/common/llmMessageTypes.ts
Normal file
68
src/vs/platform/void/common/llmMessageTypes.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VoidConfig } from '../../../workbench/contrib/void/browser/registerConfig.js';
|
||||
|
||||
// ---------- type definitions ----------
|
||||
|
||||
export type OnText = (p: { newText: string, fullText: string }) => void
|
||||
|
||||
export type OnFinalMessage = (p: { fullText: string }) => void
|
||||
|
||||
export type OnError = (p: { error: Error | string }) => void
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
export type LLMMessage = {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type LLMMessageOptions = {
|
||||
stopTokens?: string[],
|
||||
prefix?: string,
|
||||
suffix?: string,
|
||||
}
|
||||
|
||||
export type LLMMessageServiceParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
|
||||
messages: LLMMessage[];
|
||||
voidConfig: VoidConfig | null;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
|
||||
options: LLMMessageOptions;
|
||||
}
|
||||
|
||||
export type SendLLMMMessageParams = {
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: OnError;
|
||||
|
||||
messages: LLMMessage[];
|
||||
voidConfig: VoidConfig | null;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
options: LLMMessageOptions;
|
||||
|
||||
abortRef: AbortRef;
|
||||
}
|
||||
|
||||
// can't send functions across a proxy, use listeners instead
|
||||
export const listenerNames = ['onText', 'onFinalMessage', 'onError'] as const
|
||||
export type ProxyLLMMessageParams = Omit<LLMMessageServiceParams, typeof listenerNames[number]> & { requestId: string }
|
||||
|
||||
export type ProxyOnTextPayload = Parameters<OnText>[0] & { requestId: string }
|
||||
export type ProxyOnFinalMessagePayload = Parameters<OnFinalMessage>[0] & { requestId: string }
|
||||
export type ProxyOnErrorPayload = Parameters<OnError>[0] & { requestId: string }
|
||||
|
||||
export type ProxyLLMMessageAbortParams = { requestId: string }
|
||||
94
src/vs/platform/void/electron-main/llmMessageChannel.ts
Normal file
94
src/vs/platform/void/electron-main/llmMessageChannel.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// this channel is registered in `app.ts`
|
||||
// code convention is to make a service responsible for this stuff, and not a channel, but this is simpler.
|
||||
// you could create one instance in electron-main/my-service.ts and one in browser/my-service.ts (and define the interface IMyService in common/my-service.ts), but we just use a channel here
|
||||
// registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed);
|
||||
|
||||
import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js';
|
||||
import { Emitter, Event } from '../../../base/common/event.js';
|
||||
import { sendLLMMessage } from '../../../workbench/contrib/void/browser/react/out/util/sendLLMMessage.js';
|
||||
import { listenerNames, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, ProxyLLMMessageParams, AbortRef, SendLLMMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js';
|
||||
|
||||
// NODE IMPLEMENTATION OF SENDLLMMESSAGE - calls sendLLMMessage() and returns listeners
|
||||
|
||||
export class LLMMessageChannel implements IServerChannel {
|
||||
private readonly _onText = new Emitter<ProxyOnTextPayload>();
|
||||
readonly onText = this._onText.event;
|
||||
|
||||
private readonly _onFinalMessage = new Emitter<ProxyOnFinalMessagePayload>();
|
||||
readonly onFinalMessage = this._onFinalMessage.event;
|
||||
|
||||
private readonly _onError = new Emitter<ProxyOnErrorPayload>();
|
||||
readonly onError = this._onError.event;
|
||||
|
||||
|
||||
private readonly _abortRefOfRequestId: Record<string, AbortRef> = {}
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
// browser uses this to listen for changes
|
||||
listen(_: unknown, event: typeof listenerNames[number]): Event<any> {
|
||||
if (event === 'onText') {
|
||||
return this.onText;
|
||||
}
|
||||
else if (event === 'onFinalMessage') {
|
||||
return this.onFinalMessage;
|
||||
}
|
||||
else if (event === 'onError') {
|
||||
return this.onError;
|
||||
}
|
||||
else {
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
}
|
||||
|
||||
// browser uses this to call
|
||||
async call(_: unknown, command: string, params: any): Promise<any> {
|
||||
|
||||
try {
|
||||
if (command === 'sendLLMMessage') {
|
||||
this._callSendLLMMessage(params)
|
||||
}
|
||||
else if (command === 'abort') {
|
||||
this._callAbort(params)
|
||||
}
|
||||
else {
|
||||
throw new Error(`Void sendLLM: command "${command}" not recognized.`)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log('llmMessageChannel: Call Error:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// the only place sendLLMMessage is actually called
|
||||
private _callSendLLMMessage(params: ProxyLLMMessageParams) {
|
||||
const { requestId } = params;
|
||||
|
||||
if (!(requestId in this._abortRefOfRequestId))
|
||||
this._abortRefOfRequestId[requestId] = { current: null }
|
||||
|
||||
const mainThreadParams: SendLLMMMessageParams = {
|
||||
...params,
|
||||
onText: ({ newText, fullText }) => { this._onText.fire({ requestId, newText, fullText }); },
|
||||
onFinalMessage: ({ fullText }) => { this._onFinalMessage.fire({ requestId, fullText }); },
|
||||
onError: ({ error }) => { this._onError.fire({ requestId, error }); },
|
||||
abortRef: this._abortRefOfRequestId[requestId],
|
||||
}
|
||||
sendLLMMessage(mainThreadParams);
|
||||
}
|
||||
|
||||
private _callAbort(params: ProxyLLMMessageAbortParams) {
|
||||
const { requestId } = params;
|
||||
if (!(requestId in this._abortRefOfRequestId)) return
|
||||
this._abortRefOfRequestId[requestId].current?.()
|
||||
delete this._abortRefOfRequestId[requestId]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -741,8 +741,24 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
|
|||
|
||||
cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) });
|
||||
});
|
||||
|
||||
|
||||
// // Void: send from https://
|
||||
// this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, async (details, cb) => {
|
||||
// // const voidConfig = this.voidConfigStateService.state.voidConfig
|
||||
// // const whichApi = voidConfig.default['whichApi']
|
||||
// const endpoint = 'http://127.' //string | undefined = voidConfig[whichApi as VoidConfigField].endpoint
|
||||
|
||||
// if (endpoint && details.url.startsWith(endpoint)) {
|
||||
// details.requestHeaders['Origin'] = 'https://app.voideditor.com'
|
||||
// }
|
||||
// cb({ cancel: false, requestHeaders: details.requestHeaders });
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private marketplaceHeadersPromise: Promise<object> | undefined;
|
||||
private getMarketplaceHeaders(): Promise<object> {
|
||||
if (!this.marketplaceHeadersPromise) {
|
||||
|
|
|
|||
18
src/vs/workbench/contrib/void/browser/getCmdKey.ts
Normal file
18
src/vs/workbench/contrib/void/browser/getCmdKey.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Void Editor additions licensed under the AGPLv3 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OperatingSystem, OS } from '../../../../base/common/platform.js';
|
||||
|
||||
export function getCmdKey(): string {
|
||||
if (OS === OperatingSystem.Macintosh) {
|
||||
return '⌘';
|
||||
} else {
|
||||
return 'Ctrl';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// This is from the old repo
|
||||
|
||||
// const tailwindcss = require('tailwindcss')
|
||||
// const autoprefixer = require('autoprefixer')
|
||||
// const postcss = require('postcss')
|
||||
// const fs = require('fs')
|
||||
|
||||
// const convertTailwindToCSS = ({ from, to }) => {
|
||||
// console.log('converting ', from, ' --> ', to)
|
||||
|
||||
// const original_css_contents = fs.readFileSync(from, 'utf8')
|
||||
|
||||
// return postcss([
|
||||
// tailwindcss, // this compiles tailwind of all the files specified in tailwind.config.json
|
||||
// autoprefixer,
|
||||
// ])
|
||||
// .process(original_css_contents, { from, to })
|
||||
// .then(processed_css_contents => { fs.writeFileSync(to, processed_css_contents.css) })
|
||||
// .catch(error => {
|
||||
// console.error('Error in build-css:', error)
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
// const esbuild = require('esbuild')
|
||||
|
||||
// const convertTSXtoJS = async ({ from, to }) => {
|
||||
// console.log('converting ', from, ' --> ', to)
|
||||
|
||||
// return esbuild.build({
|
||||
// entryPoints: [from],
|
||||
// bundle: true,
|
||||
// minify: true,
|
||||
// sourcemap: true,
|
||||
// outfile: to,
|
||||
// format: 'iife', // apparently iife is safe for browsers (safer than cjs)
|
||||
// platform: 'browser',
|
||||
// external: ['vscode'],
|
||||
// }).catch(() => process.exit(1));
|
||||
// }
|
||||
|
||||
// (async () => {
|
||||
// // convert tsx to js
|
||||
// await convertTSXtoJS({
|
||||
// from: 'src/webviews/sidebar/index.tsx',
|
||||
// to: 'dist/webviews/sidebar/index.js',
|
||||
// })
|
||||
|
||||
// await convertTSXtoJS({
|
||||
// from: 'src/webviews/ctrlk/index.tsx',
|
||||
// to: 'dist/webviews/ctrlk/index.js',
|
||||
// })
|
||||
|
||||
// await convertTSXtoJS({
|
||||
// from: 'src/webviews/diffline/index.tsx',
|
||||
// to: 'dist/webviews/diffline/index.js',
|
||||
// })
|
||||
|
||||
// // convert tailwind to css
|
||||
// await convertTailwindToCSS({
|
||||
// from: 'src/webviews/styles.css',
|
||||
// to: 'dist/webviews/styles.css',
|
||||
// })
|
||||
|
||||
// })()
|
||||
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
{
|
||||
"name": "void",
|
||||
"publisher": "void",
|
||||
"displayName": "Void",
|
||||
"description": "",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "*"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"enabledApiProposals": [
|
||||
"editorInsets"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./out/extension/extension.js",
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
"title": "Void",
|
||||
"properties": {}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "typeInspector.inspect",
|
||||
"title": "Inspect Types of All Variables"
|
||||
},
|
||||
{
|
||||
"command": "void.ctrl+l",
|
||||
"title": "Show Sidebar"
|
||||
},
|
||||
{
|
||||
"command": "void.ctrl+k",
|
||||
"title": "Make Inline Edit"
|
||||
},
|
||||
{
|
||||
"command": "void.acceptDiff",
|
||||
"title": "Approve Diff"
|
||||
},
|
||||
{
|
||||
"command": "void.rejectDiff",
|
||||
"title": "Discard Diff"
|
||||
},
|
||||
{
|
||||
"command": "void.startNewThread",
|
||||
"title": "Start a new chat",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "void.toggleThreadSelector",
|
||||
"title": "View past chats",
|
||||
"icon": "$(history)"
|
||||
},
|
||||
{
|
||||
"command": "void.toggleSettings",
|
||||
"title": "Void settings",
|
||||
"icon": "$(settings-gear)"
|
||||
}
|
||||
],
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "voidViewContainer",
|
||||
"title": "Chat",
|
||||
"icon": "$(hubot)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"voidViewContainer": [
|
||||
{
|
||||
"type": "webview",
|
||||
"id": "void.viewnumberone",
|
||||
"name": "Void"
|
||||
}
|
||||
]
|
||||
},
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "void.ctrl+l",
|
||||
"key": "ctrl+l",
|
||||
"mac": "cmd+l"
|
||||
},
|
||||
{
|
||||
"command": "void.ctrl+k",
|
||||
"key": "ctrl+k",
|
||||
"mac": "cmd+k"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "void.startNewThread",
|
||||
"when": "view == 'void.viewnumberone'",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "void.toggleThreadSelector",
|
||||
"when": "view == 'void.viewnumberone'",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"command": "void.toggleSettings",
|
||||
"when": "view == 'void.viewnumberone'",
|
||||
"group": "navigation"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"build": "rimraf dist && node build/build.js",
|
||||
"pretest": "tsc -p ./ && eslint src --ext ts",
|
||||
"test": "vscode-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "^0.31.0",
|
||||
"@eslint/js": "^9.9.1",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@rrweb/types": "^2.0.0-alpha.17",
|
||||
"@types/diff": "^5.2.2",
|
||||
"@types/diff-match-patch": "^1.0.36",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.12",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.5.1",
|
||||
"@types/react": "^18.3.4",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
||||
"@typescript-eslint/parser": "^8.3.0",
|
||||
"@vscode/test-cli": "^0.0.10",
|
||||
"@vscode/test-electron": "2.4.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"esbuild": "^0.23.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react": "^7.35.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"globals": "^15.9.0",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^14.1.0",
|
||||
"ollama": "^0.5.9",
|
||||
"openai": "^4.70.2",
|
||||
"postcss": "^8.4.41",
|
||||
"posthog-js": "^1.176.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"rrweb-snapshot": "^2.0.0-alpha.4",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "5.5.4",
|
||||
"typescript-eslint": "^8.3.0",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.2",
|
||||
"tree-sitter": "^0.21.1",
|
||||
"tree-sitter-javascript": "^0.23.1",
|
||||
"tree-sitter-python": "^0.23.4"
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,6 @@ A couple things to remember:
|
|||
|
||||
- Make sure to add .js at the end of any external imports used in here, e.g. ../../../../../my_file.js. If you don't do this, you will get untraceable errors.
|
||||
|
||||
- src/ needs to be shallow so the detection of externals works properly (see tsup.config.js).
|
||||
- src/ needs to be shallow (1 folder deep) so the detection of externals works properly (see tsup.config.js).
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ execSync('npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "pref
|
|||
execSync('npx tsup')
|
||||
|
||||
|
||||
console.log('✅ Done building! Press Cmd+Shift+B again.')
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import React, { FormEvent, Fragment, useCallback, useEffect, useRef, useState }
|
|||
|
||||
|
||||
import { useConfigState, useService, useThreadsState } from '../util/services.js';
|
||||
import { sendLLMMessage } from '../util/sendLLMMessage.js';
|
||||
import { generateDiffInstructions } from '../../../prompt/systemPrompts.js';
|
||||
import { userInstructionsStr } from '../../../prompt/stringifyFiles.js';
|
||||
import { CodeSelection, CodeStagingSelection } from '../../../registerThreads.js';
|
||||
|
|
@ -17,8 +16,10 @@ import { IModelService } from '../../../../../../../editor/common/services/model
|
|||
import { URI } from '../../../../../../../base/common/uri.js';
|
||||
import { EndOfLinePreference } from '../../../../../../../editor/common/model.js';
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
import { ErrorDisplay } from '../util/ErrorDisplay.js';
|
||||
import { LLMMessageServiceParams } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
|
||||
|
||||
// import { } from '@vscode/webview-ui-toolkit/react';
|
||||
|
||||
// read files from VSCode
|
||||
const VSReadFile = async (modelService: IModelService, uri: URI): Promise<string | null> => {
|
||||
|
|
@ -174,11 +175,11 @@ export const SidebarChat = () => {
|
|||
// state of chat
|
||||
const [messageStream, setMessageStream] = useState('')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const abortFnRef = useRef<(() => void) | null>(null)
|
||||
|
||||
const [latestError, setLatestError] = useState('')
|
||||
const latestRequestIdRef = useRef<string | null>(null)
|
||||
|
||||
const [latestError, setLatestError] = useState<Error | string | null>(null)
|
||||
|
||||
const sendLLMMessageService = useService('sendLLMMessageService')
|
||||
|
||||
const isDisabled = !instructions
|
||||
|
||||
|
|
@ -209,11 +210,12 @@ export const SidebarChat = () => {
|
|||
|
||||
|
||||
// send message to LLM
|
||||
sendLLMMessage({
|
||||
|
||||
const object: LLMMessageServiceParams = {
|
||||
logging: { loggingName: 'Chat' },
|
||||
messages: [...(currentThread?.messages ?? []).map(m => ({ role: m.role, content: m.content })),],
|
||||
onText: (newText, fullText) => setMessageStream(fullText),
|
||||
onFinalMessage: (content) => {
|
||||
onText: ({ newText, fullText }) => setMessageStream(fullText),
|
||||
onFinalMessage: ({ fullText: content }) => {
|
||||
console.log('chat: running final message')
|
||||
|
||||
// add assistant's message to chat history, and clear selection
|
||||
|
|
@ -222,8 +224,8 @@ export const SidebarChat = () => {
|
|||
setMessageStream('')
|
||||
setIsLoading(false)
|
||||
},
|
||||
onError: (error) => {
|
||||
console.log('chat: running error')
|
||||
onError: ({ error }) => {
|
||||
console.log('chat: running error', error)
|
||||
|
||||
// add assistant's message to chat history, and clear selection
|
||||
let content = messageStream; // just use the current content
|
||||
|
|
@ -236,22 +238,25 @@ export const SidebarChat = () => {
|
|||
setLatestError(error)
|
||||
},
|
||||
voidConfig,
|
||||
abortRef: abortFnRef,
|
||||
options: {},
|
||||
})
|
||||
}
|
||||
|
||||
const latestRequestId = sendLLMMessageService.sendLLMMessage(object)
|
||||
latestRequestIdRef.current = latestRequestId
|
||||
|
||||
|
||||
setIsLoading(true)
|
||||
setInstructions('');
|
||||
formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens
|
||||
threadsStateService.setStaging([]) // clear staging
|
||||
setLatestError('')
|
||||
setLatestError(null)
|
||||
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
// abort claude
|
||||
abortFnRef.current?.()
|
||||
// abort the LLM
|
||||
if (latestRequestIdRef.current)
|
||||
sendLLMMessageService.abort(latestRequestIdRef.current)
|
||||
|
||||
// if messageStream was not empty, add it to the history
|
||||
const llmContent = messageStream || '(null)'
|
||||
|
|
@ -339,9 +344,11 @@ export const SidebarChat = () => {
|
|||
</div>
|
||||
|
||||
{/* error message */}
|
||||
{!latestError ? null : <div>
|
||||
{latestError}
|
||||
</div>}
|
||||
{latestError === null ? null :
|
||||
<ErrorDisplay
|
||||
error={latestError}
|
||||
onDismiss={() => { setLatestError(null) }}
|
||||
/>}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,13 +109,6 @@ export const SidebarSettings = () => {
|
|||
))}
|
||||
</div>
|
||||
})}
|
||||
|
||||
{/* Remove this after 10/21/24, this is just to give developers a heads up about the recent change */}
|
||||
<div className='pt-20'>
|
||||
{`We recently updated Settings. To copy your old Void settings over, press Ctrl+Shift+P, `}
|
||||
{`type 'Open User Settings (JSON)',`}
|
||||
{` and look for 'void.'. `}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
import React, { useState } from 'react';
|
||||
import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react';
|
||||
|
||||
import { getCmdKey } from '../../../getCmdKey.js';
|
||||
|
||||
// const opaqueMessage = `\
|
||||
// Unfortunately, Void can't see the full error. However, you should be able to find more details by pressing ${getCmdKey()}+Shift+P, typing "Toggle Developer Tools", and looking at the console.\n
|
||||
// This error often means you have an incorrect API key. If you're self-hosting your own server, it might mean your CORS headers are off, and you should make sure your server's response has the header "Access-Control-Allow-Origins" set to "*", or at least allows "vscode-file://vscode-app".`
|
||||
// if ((error instanceof Error) && (error.cause + '').includes('TypeError: Failed to fetch')) {
|
||||
// e = error as any
|
||||
// e['Void Team'] = opaqueMessage
|
||||
// }
|
||||
|
||||
|
||||
type Details = {
|
||||
message: string,
|
||||
name: string,
|
||||
stack: string | null,
|
||||
cause: string | null,
|
||||
code: string | null,
|
||||
additional: Record<string, any>
|
||||
}
|
||||
|
||||
// Get detailed error information
|
||||
const getErrorDetails = (error: unknown) => {
|
||||
|
||||
let details: Details;
|
||||
|
||||
let e: Error & { [other: string]: undefined | any }
|
||||
|
||||
// If fetch() fails, it gives an opaque message. We add extra details to the error.
|
||||
if (error instanceof Error) {
|
||||
e = error
|
||||
}
|
||||
// sometimes error is an object but not an Error
|
||||
else if (typeof error === 'object') {
|
||||
e = new Error(`The server didn't give a very useful error message. More details below.`, { cause: JSON.stringify(error) })
|
||||
|
||||
}
|
||||
else {
|
||||
e = new Error(String(error))
|
||||
}
|
||||
// console.log('error display', JSON.stringify(e))
|
||||
|
||||
const message = e.message && e.error ?
|
||||
(e.message + ':\n' + e.error)
|
||||
: e.message || e.error || JSON.stringify(error)
|
||||
|
||||
details = {
|
||||
name: e.name || 'Error',
|
||||
message: message,
|
||||
stack: null, // e.stack is ignored because it's ugly and not very useful
|
||||
cause: e.cause ? String(e.cause) : null,
|
||||
code: e.code || null,
|
||||
additional: {}
|
||||
}
|
||||
|
||||
|
||||
// Collect any additional properties from the e
|
||||
for (let prop of Object.getOwnPropertyNames(e).filter((prop) => !Object.keys(details).includes(prop)))
|
||||
details.additional[prop] = (e as any)[prop]
|
||||
|
||||
return details;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const ErrorDisplay = ({
|
||||
error,
|
||||
onDismiss = null,
|
||||
showDismiss = true,
|
||||
className = ''
|
||||
}: {
|
||||
error: Error | object | string,
|
||||
onDismiss: (() => void) | null,
|
||||
showDismiss?: boolean,
|
||||
className?: string
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const details = getErrorDetails(error);
|
||||
const hasDetails = details.cause || Object.keys(details.additional).length > 0;
|
||||
|
||||
return (
|
||||
<div className={`rounded-lg border border-red-200 bg-red-50 p-4 ${className}`}>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex gap-3">
|
||||
<AlertCircle className="h-5 w-5 text-red-500 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-red-800">
|
||||
{details.name}
|
||||
</h3>
|
||||
<p className="text-red-700 mt-1">
|
||||
{details.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{hasDetails && (
|
||||
<button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="text-red-600 hover:text-red-800 p-1 rounded"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronUp className="h-5 w-5" />
|
||||
) : (
|
||||
<ChevronDown className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
{showDismiss && onDismiss && (
|
||||
<button
|
||||
onClick={onDismiss}
|
||||
className="text-red-600 hover:text-red-800 p-1 rounded"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Expandable Details */}
|
||||
{isExpanded && hasDetails && (
|
||||
<div className="mt-4 space-y-3 border-t border-red-200 pt-3">
|
||||
{details.code && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Error Code: </span>
|
||||
<span className="text-red-700">{details.code}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{details.cause && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Cause: </span>
|
||||
<span className="text-red-700">{details.cause}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{Object.keys(details.additional).length > 0 && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Additional Information:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{Object.keys(details.additional).map(key => `${key}:\n${details.additional[key]}`).join('\n')}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{/* {details.stack && (
|
||||
<div>
|
||||
<span className="font-semibold text-red-800">Stack Trace:</span>
|
||||
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
|
||||
{details.stack}
|
||||
</pre>
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import * as ReactDOM from 'react-dom/client'
|
||||
import { ReactServicesType, VoidSidebarState } from '../../../registerSidebar.js';
|
||||
import { ConfigState } from '../../../registerConfig.js';
|
||||
import { ThreadsState } from '../../../registerThreads.js';
|
||||
import { _registerServices } from './services.js';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,54 +4,21 @@ import { Ollama } from 'ollama/browser'
|
|||
import { Content, GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai';
|
||||
import { posthog } from 'posthog-js'
|
||||
import type { VoidConfig } from '../../../registerConfig.js';
|
||||
import type { LLMMessage, OnText, OnError, OnFinalMessage, SendLLMMMessageParams, LLMMessageOptions, } from '../../../../../../../platform/void/common/llmMessageTypes.js';
|
||||
|
||||
export type AbortRef = { current: (() => void) | null }
|
||||
|
||||
export type OnText = (newText: string, fullText: string) => void
|
||||
|
||||
export type OnFinalMessage = (input: string) => void
|
||||
|
||||
export type LLMMessageAnthropic = {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type LLMMessage = {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type LLMMessageOptions = {
|
||||
stopTokens?: string[],
|
||||
prefix?: string,
|
||||
suffix?: string,
|
||||
}
|
||||
|
||||
type SendLLMMessageFnTypeInternal = (params: {
|
||||
messages: LLMMessage[];
|
||||
options: LLMMessageOptions;
|
||||
onText: OnText;
|
||||
onFinalMessage: OnFinalMessage;
|
||||
onError: (error: string) => void;
|
||||
onError: OnError;
|
||||
voidConfig: VoidConfig;
|
||||
|
||||
_setAborter: (aborter: () => void) => void;
|
||||
}) => void
|
||||
|
||||
type SendLLMMessageFnTypeExternal = (params: {
|
||||
messages: LLMMessage[];
|
||||
options: LLMMessageOptions;
|
||||
onText: OnText;
|
||||
onFinalMessage: (fullText: string) => void;
|
||||
onError: (error: string) => void;
|
||||
voidConfig: VoidConfig | null;
|
||||
abortRef: AbortRef;
|
||||
|
||||
logging: {
|
||||
loggingName: string,
|
||||
};
|
||||
}) => void
|
||||
|
||||
const parseMaxTokensStr = (maxTokensStr: string) => {
|
||||
// parse the string but only if the full string is a valid number, eg parseInt('100abc') should return NaN
|
||||
const int = isNaN(Number(maxTokensStr)) ? undefined : parseInt(maxTokensStr)
|
||||
|
|
@ -61,6 +28,10 @@ const parseMaxTokensStr = (maxTokensStr: string) => {
|
|||
}
|
||||
|
||||
// Anthropic
|
||||
type LLMMessageAnthropic = {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinalMessage, onError, voidConfig, _setAborter }) => {
|
||||
|
||||
const anthropic = new Anthropic({ apiKey: voidConfig.anthropic.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"]
|
||||
|
|
@ -84,23 +55,23 @@ const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFi
|
|||
|
||||
// when receive text
|
||||
stream.on('text', (newText, fullText) => {
|
||||
onText(newText, fullText)
|
||||
onText({ newText, fullText })
|
||||
})
|
||||
|
||||
// when we get the final message on this stream (or when error/fail)
|
||||
stream.on('finalMessage', (claude_response) => {
|
||||
// stringify the response's content
|
||||
const content = claude_response.content.map(c => c.type === 'text' ? c.text : c.type).join('\n');
|
||||
onFinalMessage(content)
|
||||
onFinalMessage({ fullText: content })
|
||||
})
|
||||
|
||||
stream.on('error', (error) => {
|
||||
// the most common error will be invalid API key (401), so we handle this with a nice message
|
||||
if (error instanceof Anthropic.APIError && error.status === 401) {
|
||||
onError('Invalid API key.')
|
||||
onError({ error: 'Invalid API key.' })
|
||||
}
|
||||
else {
|
||||
onError(error.message)
|
||||
onError({ error })
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -139,21 +110,16 @@ const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, o
|
|||
for await (const chunk of response.stream) {
|
||||
const newText = chunk.text();
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof GoogleGenerativeAIFetchError) {
|
||||
if (error.status === 400) {
|
||||
onError('Invalid API key.');
|
||||
}
|
||||
else {
|
||||
onError(`${error.name}:\n${error.message}`);
|
||||
}
|
||||
if (error instanceof GoogleGenerativeAIFetchError && error.status === 400) {
|
||||
onError({ error: 'Invalid API key.' });
|
||||
}
|
||||
else {
|
||||
onError(error);
|
||||
onError({ error });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -199,22 +165,17 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal
|
|||
for await (const chunk of response) {
|
||||
const newText = chunk.choices[0]?.delta?.content || '';
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
// when error/fail - this catches errors of both .create() and .then(for await)
|
||||
.catch(error => {
|
||||
if (error instanceof OpenAI.APIError) {
|
||||
if (error.status === 401) {
|
||||
onError('Invalid API key.');
|
||||
}
|
||||
else {
|
||||
onError(`${error.name}:\n${error.message}`);
|
||||
}
|
||||
if (error instanceof OpenAI.APIError && error.status === 401) {
|
||||
onError({ error: 'Invalid API key.' });
|
||||
}
|
||||
else {
|
||||
onError(error);
|
||||
onError({ error });
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -233,19 +194,17 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, options,
|
|||
prompt: options.prefix ?? '',
|
||||
suffix: options.suffix ?? '',
|
||||
stream: true,
|
||||
options: {
|
||||
num_predict: parseMaxTokensStr(voidConfig.default.maxTokens),
|
||||
stop: options.stopTokens,
|
||||
}
|
||||
options: { num_predict: parseMaxTokensStr(voidConfig.default.maxTokens), stop: options.stopTokens, }
|
||||
})
|
||||
.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);
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
onFinalMessage({ fullText });
|
||||
|
||||
})
|
||||
// when error/fail
|
||||
|
|
@ -259,10 +218,7 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, options,
|
|||
model: voidConfig.ollama.model,
|
||||
messages: messages,
|
||||
stream: true,
|
||||
options: {
|
||||
num_predict: parseMaxTokensStr(voidConfig.default.maxTokens), // this is max_tokens
|
||||
stop: options.stopTokens,
|
||||
}
|
||||
options: { num_predict: parseMaxTokensStr(voidConfig.default.maxTokens) } // this is max_tokens
|
||||
})
|
||||
.then(async stream => {
|
||||
_setAborter(() => stream.abort())
|
||||
|
|
@ -270,9 +226,9 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, options,
|
|||
for await (const chunk of stream) {
|
||||
const newText = chunk.message.content;
|
||||
fullText += newText;
|
||||
onText(newText, fullText);
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
onFinalMessage(fullText);
|
||||
onFinalMessage({ fullText });
|
||||
|
||||
})
|
||||
// when error/fail
|
||||
|
|
@ -320,24 +276,24 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin
|
|||
// when receive text
|
||||
if (type === 'message') {
|
||||
fullText += message
|
||||
onText(message, fullText)
|
||||
onText({ newText: message, fullText })
|
||||
}
|
||||
else if (type === 'sources') {
|
||||
const { filepath, linestart: _, lineend: _2 } = message as { filepath: string; linestart: number | null; lineend: number | null }
|
||||
fullText += filepath
|
||||
onText(filepath, fullText)
|
||||
onText({ newText: filepath, fullText })
|
||||
}
|
||||
// type: 'status' with an empty 'message' means last message
|
||||
else if (type === 'status') {
|
||||
if (!message) {
|
||||
onFinalMessage(fullText)
|
||||
onFinalMessage({ fullText })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.catch(e => {
|
||||
onError(e)
|
||||
.catch(error => {
|
||||
onError({ error })
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -346,7 +302,8 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin
|
|||
|
||||
|
||||
|
||||
export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
||||
|
||||
export const sendLLMMessage = ({
|
||||
messages,
|
||||
options,
|
||||
onText: onText_,
|
||||
|
|
@ -354,8 +311,8 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
|||
onError: onError_,
|
||||
abortRef: abortRef_,
|
||||
voidConfig,
|
||||
logging: { loggingName },
|
||||
}) => {
|
||||
logging: { loggingName }
|
||||
}: SendLLMMMessageParams) => {
|
||||
if (!voidConfig) return;
|
||||
|
||||
// set messages appropriately if fill in middle mode
|
||||
|
|
@ -391,22 +348,23 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
|||
let _setAborter = (fn: () => void) => { _aborter = fn }
|
||||
let _didAbort = false
|
||||
|
||||
const onText = (newText: string, fullText: string) => {
|
||||
const onText: OnText = ({ newText, fullText }) => {
|
||||
if (_didAbort) return
|
||||
onText_(newText, fullText)
|
||||
onText_({ newText, fullText })
|
||||
_fullTextSoFar = fullText
|
||||
}
|
||||
|
||||
const onFinalMessage = (fullText: string) => {
|
||||
const onFinalMessage: OnFinalMessage = ({ fullText }) => {
|
||||
if (_didAbort) return
|
||||
captureChatEvent(`${loggingName} - Received Full Message`, { messageLength: fullText.length, duration: new Date().getMilliseconds() - submit_time.getMilliseconds() })
|
||||
onFinalMessage_(fullText)
|
||||
onFinalMessage_({ fullText })
|
||||
}
|
||||
|
||||
const onError = (error: string) => {
|
||||
const onError: OnError = ({ error }) => {
|
||||
console.error('sendLLMMessage onError:', error)
|
||||
if (_didAbort) return
|
||||
captureChatEvent(`${loggingName} - Error`, { error })
|
||||
onError_(error)
|
||||
onError_({ error })
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
|
|
@ -438,14 +396,15 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({
|
|||
sendGreptileMsg({ messages, options, onText, onFinalMessage, onError, voidConfig, _setAborter, });
|
||||
break;
|
||||
default:
|
||||
onError(`Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!`)
|
||||
onError({ error: `Error: whichApi was ${voidConfig.default.whichApi}, which is not recognized!` })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
catch (e) {
|
||||
onError(`Unexpected Error in sendLLMMessage: ${e}`);
|
||||
(_aborter as any)?.()
|
||||
catch (error) {
|
||||
if (error instanceof Error) { onError({ error }) }
|
||||
else { onError({ error: `Unexpected Error in sendLLMMessage: ${error}` }); }
|
||||
; (_aborter as any)?.()
|
||||
_didAbort = true
|
||||
}
|
||||
|
||||
|
|
@ -589,7 +548,7 @@ ${suffix}
|
|||
|
||||
// export type AbortRef = { current: (() => void) }
|
||||
|
||||
// export type OnText = (newText: string, fullText: string) => void
|
||||
// export type LLMMessageOnText = (newText: string, fullText: string) => void
|
||||
|
||||
// export type OnFinalMessage = (input: string) => void
|
||||
|
||||
|
|
@ -609,7 +568,7 @@ ${suffix}
|
|||
// mode: 'chat' | 'fim',
|
||||
// messages: LLMMessage[],
|
||||
// options?: LLMMessageOptions,
|
||||
// onText: OnText,
|
||||
// onText: LLMMessageOnText,
|
||||
// onFinalMessage: OnFinalMessage,
|
||||
// onError: (error: string) => void,
|
||||
// abortRef: AbortRef,
|
||||
|
|
@ -622,7 +581,7 @@ ${suffix}
|
|||
// | { mode: 'fim', messages?: undefined, fimInfo: FimInfo, }
|
||||
// ) & {
|
||||
// options?: LLMMessageOptions,
|
||||
// onText: OnText,
|
||||
// onText: LLMMessageOnText,
|
||||
// onFinalMessage: OnFinalMessage,
|
||||
// onError: (error: string) => void,
|
||||
// abortRef: AbortRef,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ import { ThreadsState } from '../../../registerThreads.js'
|
|||
|
||||
|
||||
// normally to do this you'd use a useEffect that calls .onDidChangeState(), but useEffect mounts too late and misses initial state changes
|
||||
|
||||
let services: ReactServicesType
|
||||
|
||||
// even if React hasn't mounted yet, these variables are always updated to the latest state:
|
||||
let sidebarState: VoidSidebarState
|
||||
let configState: ConfigState
|
||||
let threadsState: ThreadsState
|
||||
|
||||
// React listens by adding a setState function to these:
|
||||
const sidebarStateListeners: Set<(s: VoidSidebarState) => void> = new Set()
|
||||
const configStateListeners: Set<(s: ConfigState) => void> = new Set()
|
||||
const threadsStateListeners: Set<(s: ThreadsState) => void> = new Set()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export default defineConfig({
|
|||
// sourcemap: true,
|
||||
|
||||
clean: true,
|
||||
platform: 'browser',
|
||||
platform: 'browser', // 'node'
|
||||
target: 'esnext',
|
||||
injectStyle: true, // bundle css into the output file
|
||||
outExtension: () => ({ js: '.js' }),
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ registerAction2(class extends Action2 {
|
|||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: 'settings' })
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: stateService.state.currentTab === 'settings' ? 'chat' : 'settings' })
|
||||
stateService.fireBlurChat()
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ import { ILanguageFeaturesService } from '../../../../editor/common/services/lan
|
|||
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
import { sendLLMMessage } from './react/out/util/sendLLMMessage.js';
|
||||
import { ITextModel } from '../../../../editor/common/model.js';
|
||||
import { Position } from '../../../../editor/common/core/position.js';
|
||||
import { InlineCompletion, InlineCompletionContext } from '../../../../editor/common/languages.js';
|
||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
||||
import { Range } from '../../../../editor/common/core/range.js';
|
||||
import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js';
|
||||
|
||||
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts
|
||||
|
||||
|
|
@ -138,10 +138,10 @@ type Autocompletion = {
|
|||
suffix: string,
|
||||
startTime: number,
|
||||
endTime: number | undefined,
|
||||
abortRef: { current: () => void },
|
||||
status: AutocompletionStatus,
|
||||
llmPromise: Promise<string> | undefined,
|
||||
result: string,
|
||||
requestId: string | null,
|
||||
}
|
||||
|
||||
const DEBOUNCE_TIME = 500
|
||||
|
|
@ -516,7 +516,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
this._autocompletionsOfDocument[docUriStr] = new LRUCache<number, Autocompletion>(
|
||||
MAX_CACHE_SIZE,
|
||||
(autocompletion: Autocompletion) => {
|
||||
autocompletion.abortRef.current()
|
||||
if (autocompletion.requestId)
|
||||
this._sendLLMMessageService.abort(autocompletion.requestId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -624,29 +625,29 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
suffix: suffix,
|
||||
startTime: Date.now(),
|
||||
endTime: undefined,
|
||||
abortRef: { current: () => { } },
|
||||
status: 'pending',
|
||||
llmPromise: undefined,
|
||||
result: '',
|
||||
requestId: null,
|
||||
}
|
||||
|
||||
// set parameters of `newAutocompletion` appropriately
|
||||
newAutocompletion.llmPromise = new Promise((resolve, reject) => {
|
||||
|
||||
sendLLMMessage({
|
||||
const requestId = this._sendLLMMessageService.sendLLMMessage({
|
||||
logging: { loggingName: 'Autocomplete' },
|
||||
messages: [],
|
||||
options: { prefix, suffix, stopTokens, },
|
||||
onText: async (tokenStr: string, completionStr: string) => {
|
||||
onText: async ({ newText, fullText }) => {
|
||||
|
||||
newAutocompletion.result = completionStr
|
||||
newAutocompletion.result = fullText
|
||||
|
||||
// if generation doesn't match the prefix for the first few tokens generated, reject it
|
||||
if (!getPrefixAutocompletionMatch({ prefix: this._lastPrefix, autocompletion: newAutocompletion })) {
|
||||
reject('LLM response did not match user\'s text.')
|
||||
}
|
||||
},
|
||||
onFinalMessage: (finalMessage: string) => {
|
||||
onFinalMessage: ({ fullText }) => {
|
||||
|
||||
// newAutocompletion.prefix = prefix
|
||||
// newAutocompletion.suffix = suffix
|
||||
|
|
@ -655,19 +656,19 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
// newAutocompletion.abortRef = { current: () => { } }
|
||||
newAutocompletion.status = 'finished'
|
||||
// newAutocompletion.promise = undefined
|
||||
newAutocompletion.result = postprocessResult(extractCodeFromResult(finalMessage))
|
||||
newAutocompletion.result = postprocessResult(extractCodeFromResult(fullText))
|
||||
|
||||
resolve(newAutocompletion.result)
|
||||
|
||||
},
|
||||
onError: (e: any) => {
|
||||
onError: ({ error }) => {
|
||||
newAutocompletion.endTime = Date.now()
|
||||
newAutocompletion.status = 'error'
|
||||
reject(e)
|
||||
reject(error)
|
||||
},
|
||||
voidConfig: this._voidConfigStateService.state.voidConfig,
|
||||
abortRef: newAutocompletion.abortRef,
|
||||
})
|
||||
newAutocompletion.requestId = requestId
|
||||
|
||||
// if the request hasnt resolved in TIMEOUT_TIME seconds, reject it
|
||||
setTimeout(() => {
|
||||
|
|
@ -676,9 +677,10 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
}
|
||||
}, TIMEOUT_TIME)
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
// add autocompletion to cache
|
||||
this._autocompletionsOfDocument[docUriStr].set(newAutocompletion.id, newAutocompletion)
|
||||
|
||||
|
|
@ -703,6 +705,7 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ
|
|||
constructor(
|
||||
@ILanguageFeaturesService private _langFeatureService: ILanguageFeaturesService,
|
||||
@IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService,
|
||||
@ISendLLMMessageService private readonly _sendLLMMessageService: ISendLLMMessageService,
|
||||
) {
|
||||
super()
|
||||
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ class VoidConfigStateService extends Disposable implements IVoidConfigStateServi
|
|||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event;
|
||||
readonly onDidChangeState: Event<void> = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes
|
||||
|
||||
state: ConfigState;
|
||||
readonly voidConfigInfo: VoidConfigInfo = voidConfigInfo; // just putting this here for simplicity, it's static though
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import { ICodeEditor, IOverlayWidget, IViewZone } from '../../../../editor/brows
|
|||
|
||||
// import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js';
|
||||
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
|
||||
import { sendLLMMessage } from './react/out/util/sendLLMMessage.js';
|
||||
// import { throttle } from '../../../../base/common/decorators.js';
|
||||
import { IVoidConfigStateService } from './registerConfig.js';
|
||||
import { writeFileWithDiffInstructions } from './prompt/systemPrompts.js';
|
||||
|
|
@ -29,6 +28,10 @@ import { ILanguageService } from '../../../../editor/common/languages/language.j
|
|||
import * as dom from '../../../../base/browser/dom.js';
|
||||
import { Widget } from '../../../../base/browser/ui/widget.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { LLMMessageServiceParams } from '../../../../platform/void/common/llmMessageTypes.js';
|
||||
import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js';
|
||||
// import { ISendLLMMessageService } from '../../../../platform/void/common/sendLLMMessage.js';
|
||||
// import { sendLLMMessage } from './react/out/util/sendLLMMessage.js';
|
||||
|
||||
|
||||
// gets converted to --vscode-void-greenBG, see void.css
|
||||
|
|
@ -115,7 +118,7 @@ export interface IInlineDiffsService {
|
|||
|
||||
}
|
||||
|
||||
export const IInlineDiffsService = createDecorator<IInlineDiffsService>('inlineDiffsService');
|
||||
export const IInlineDiffsService = createDecorator<IInlineDiffsService>('inlineDiffAreasService');
|
||||
|
||||
class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
||||
_serviceBrand: undefined;
|
||||
|
|
@ -148,6 +151,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
@IModelService private readonly _modelService: IModelService,
|
||||
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z
|
||||
@ILanguageService private readonly _langService: ILanguageService,
|
||||
@ISendLLMMessageService private readonly _sendLLMMessageService: ISendLLMMessageService,
|
||||
) {
|
||||
super();
|
||||
|
||||
|
|
@ -730,23 +734,22 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
// <SUF>${suffix}</SUF>
|
||||
// <MID>`;
|
||||
|
||||
|
||||
|
||||
|
||||
const abortRef = { current: null } as { current: null | (() => void) }
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
sendLLMMessage({
|
||||
|
||||
let streamRequestId: string | null = null
|
||||
|
||||
const object: LLMMessageServiceParams = {
|
||||
logging: { loggingName: 'streamChunk' },
|
||||
messages: [
|
||||
{ role: 'system', content: writeFileWithDiffInstructions, },
|
||||
// TODO include more context too
|
||||
{ role: 'user', content: promptContent, }
|
||||
],
|
||||
onText: (newText: string, fullText: string) => {
|
||||
onText: ({ newText, fullText }) => {
|
||||
this._writeDiffAreaLLMText(diffArea, fullText)
|
||||
this._refreshDiffsInURI(uri)
|
||||
},
|
||||
onFinalMessage: (fullText: string) => {
|
||||
onFinalMessage: ({ fullText }) => {
|
||||
this._writeText(uri, fullText,
|
||||
{ startLineNumber: diffArea.startLine, startColumn: 1, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER }, // 1-indexed
|
||||
)
|
||||
|
|
@ -757,14 +760,17 @@ Please finish writing the new file by applying the diff to the original file. Re
|
|||
onError: (e: any) => {
|
||||
console.error('Error rewriting file with diff', e);
|
||||
// TODO indicate there was an error
|
||||
abortRef.current?.()
|
||||
if (streamRequestId)
|
||||
this._sendLLMMessageService.abort(streamRequestId)
|
||||
|
||||
diffArea._sweepState = { isStreaming: false, line: null }
|
||||
resolve();
|
||||
},
|
||||
voidConfig,
|
||||
abortRef,
|
||||
options: {},
|
||||
})
|
||||
options: {}
|
||||
}
|
||||
|
||||
streamRequestId = this._sendLLMMessageService.sendLLMMessage(object)
|
||||
})
|
||||
|
||||
onFinishEdit()
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ import { IVoidConfigStateService } from './registerConfig.js';
|
|||
import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
import { IInlineDiffsService } from './registerInlineDiffs.js';
|
||||
import { IModelService } from '../../../../editor/common/services/model.js';
|
||||
import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js';
|
||||
|
||||
|
||||
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||
|
||||
|
||||
|
|
@ -65,6 +68,7 @@ export type ReactServicesType = {
|
|||
fileService: IFileService;
|
||||
modelService: IModelService;
|
||||
inlineDiffService: IInlineDiffsService;
|
||||
sendLLMMessageService: ISendLLMMessageService;
|
||||
}
|
||||
|
||||
// ---------- Define viewpane ----------
|
||||
|
|
@ -109,6 +113,7 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
fileService: accessor.get(IFileService),
|
||||
modelService: accessor.get(IModelService),
|
||||
inlineDiffService: accessor.get(IInlineDiffsService),
|
||||
sendLLMMessageService: accessor.get(ISendLLMMessageService),
|
||||
}
|
||||
mountFn(root, services);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -328,6 +328,11 @@ export class DesktopMain extends Disposable {
|
|||
//
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// // Void
|
||||
// const sendLLMMessageService = new SendLLMMessageService();
|
||||
// serviceCollection.set(ISendLLMMessageService, sendLLMMessageService);
|
||||
|
||||
|
||||
|
||||
return { serviceCollection, logService, storageService, configurationService };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ import './browser/workbench.contribution.js';
|
|||
|
||||
|
||||
|
||||
//#region --- void
|
||||
//#region --- Void
|
||||
// Void added this:
|
||||
import './contrib/void/browser/void.contribution.js';
|
||||
import '../platform/void/browser/llmMessageService.js';
|
||||
//#endregion
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ import './electron-sandbox/parts/dialogs/dialog.contribution.js';
|
|||
|
||||
//#endregion
|
||||
|
||||
// //#region --- Void
|
||||
// // Void added this (modeling off of import '.*clipboardservice.js'):
|
||||
// import './services/void/electron-main/sendLLMMessage.js';
|
||||
// //#endregion
|
||||
|
||||
|
||||
|
||||
//#region --- workbench services
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ import './browser/web.main.js';
|
|||
//#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//#region --- workbench services
|
||||
|
||||
import './services/integrity/browser/integrityService.js';
|
||||
|
|
|
|||
|
|
@ -77,6 +77,18 @@
|
|||
process.on(type, callback);
|
||||
}
|
||||
},
|
||||
|
||||
// Void : {
|
||||
// /**
|
||||
// * Send a message to the LLM.
|
||||
// * @param {any} data The data to send to the LLM.
|
||||
// * @returns {Promise<any>} The response from the LLM.
|
||||
// */
|
||||
// sendLLMMessage: async (data) => {
|
||||
// // Use ipcRenderer.invoke to send the message to the main process (see app.ts)
|
||||
// return await ipcRenderer.invoke('vscode:sendLLMMessage', data);
|
||||
// }
|
||||
// },
|
||||
};
|
||||
|
||||
if (process.contextIsolated) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue