From 51280d4e77c685cda81f84f36097d07f9f557124 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Fri, 22 Nov 2024 00:43:07 -0800 Subject: [PATCH 01/12] error ui improvements --- package-lock.json | 87 +++++++++++- package.json | 2 + .../contrib/void/browser/react/build.js | 1 + .../browser/react/src/util/ErrorDisplay.tsx | 128 ++++++++++++++++++ 4 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx diff --git a/package-lock.json b/package-lock.json index 70f4138a..87c05495 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 0a5189a7..dc1b4e96 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/vs/workbench/contrib/void/browser/react/build.js b/src/vs/workbench/contrib/void/browser/react/build.js index 80befcdb..5383c4a4 100755 --- a/src/vs/workbench/contrib/void/browser/react/build.js +++ b/src/vs/workbench/contrib/void/browser/react/build.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.') diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx new file mode 100644 index 00000000..bd5240bb --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx @@ -0,0 +1,128 @@ +import React, { useState } from 'react'; +import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react'; +import { } from '@vscode/webview-ui-toolkit/react'; + + +// Get detailed error information +const getErrorDetails = (error: any) => { + + let details: { message: string, name: string, stack: string | null, cause: string | null, code: string | null, additional: Record }; + + const e = error instanceof Error ? error : new Error(String(error)); + + details = { + message: e.message || String(e), + name: e.name || 'Error', + stack: e.stack || null, + cause: e.cause ? String(e.cause) : null, + code: (e as any).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; +}; + + + +const ErrorDisplay = ({ + error, + onDismiss = null, + showDismiss = true, + className = '' +}: { + error: Error, + onDismiss: (() => void) | null, + showDismiss?: boolean, + className?: string +}) => { + const [isExpanded, setIsExpanded] = useState(false); + + const details = getErrorDetails(error); + const hasDetails = details.stack || details.cause || Object.keys(details.additional).length > 0; + + return ( +
+ {/* Header */} +
+
+ +
+

+ {details.name} +

+

+ {details.message} +

+
+
+ +
+ {hasDetails && ( + + )} + {showDismiss && onDismiss && ( + + )} +
+
+ + {/* Expandable Details */} + {isExpanded && hasDetails && ( +
+ {details.code && ( +
+ Error Code: + {details.code} +
+ )} + + {details.cause && ( +
+ Cause: + {details.cause} +
+ )} + + {Object.keys(details.additional).length > 0 && ( +
+ Additional Information: +
+								{JSON.stringify(details.additional, null, 2)}
+							
+
+ )} + + {details.stack && ( +
+ Stack Trace: +
+								{details.stack}
+							
+
+ )} +
+ )} +
+ ); +}; + +export default ErrorDisplay; From 1d5fef43c150d200826d60f81333ab4e648f6b50 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sat, 23 Nov 2024 16:05:26 -0800 Subject: [PATCH 02/12] improved error handling --- .../contrib/void/browser/getCmdKey.ts | 18 +++++++++ .../react/src/sidebar-tsx/SidebarChat.tsx | 13 ++++--- .../browser/react/src/util/ErrorDisplay.tsx | 38 +++++++++++++------ .../browser/react/src/util/sendLLMMessage.tsx | 37 ++++++++---------- 4 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 src/vs/workbench/contrib/void/browser/getCmdKey.ts diff --git a/src/vs/workbench/contrib/void/browser/getCmdKey.ts b/src/vs/workbench/contrib/void/browser/getCmdKey.ts new file mode 100644 index 00000000..76344138 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/getCmdKey.ts @@ -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'; + } +} + + + + diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 3b0dffe6..888a3357 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -17,8 +17,9 @@ 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 { } from '@vscode/webview-ui-toolkit/react'; // read files from VSCode const VSReadFile = async (modelService: IModelService, uri: URI): Promise => { @@ -176,7 +177,7 @@ export const SidebarChat = () => { const [isLoading, setIsLoading] = useState(false) const abortFnRef = useRef<(() => void) | null>(null) - const [latestError, setLatestError] = useState('') + const [latestError, setLatestError] = useState(null) @@ -338,9 +339,11 @@ export const SidebarChat = () => { {/* error message */} - {!latestError ? null :
- {latestError} -
} + {!latestError ? null : + { setLatestError(null) }} + />} } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx index bd5240bb..db59bff6 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx @@ -1,21 +1,39 @@ import React, { useState } from 'react'; import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react'; -import { } from '@vscode/webview-ui-toolkit/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".` // Get detailed error information -const getErrorDetails = (error: any) => { +const getErrorDetails = (error: unknown) => { let details: { message: string, name: string, stack: string | null, cause: string | null, code: string | null, additional: Record }; - const e = error instanceof Error ? error : new Error(String(error)); + 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) && (error.cause + '').includes('TypeError: Failed to fetch')) { + e = error as any + e.voidMessage = opaqueMessage + } + else if (error instanceof Error) { + e = error + } + else { + e = new Error(String(error)) + } details = { - message: e.message || String(e), name: e.name || 'Error', + message: e.message || String(e), stack: e.stack || null, cause: e.cause ? String(e.cause) : null, - code: (e as any).code || null, + code: e.code || null, additional: {} } @@ -29,13 +47,13 @@ const getErrorDetails = (error: any) => { -const ErrorDisplay = ({ +export const ErrorDisplay = ({ error, onDismiss = null, showDismiss = true, className = '' }: { - error: Error, + error: Error | string, onDismiss: (() => void) | null, showDismiss?: boolean, className?: string @@ -105,8 +123,8 @@ const ErrorDisplay = ({ {Object.keys(details.additional).length > 0 && (
Additional Information: -
-								{JSON.stringify(details.additional, null, 2)}
+							
+								{Object.keys(details.additional).map(key => `${key}:\n${details.additional[key]}`).join('\n')}
 							
)} @@ -124,5 +142,3 @@ const ErrorDisplay = ({ ); }; - -export default ErrorDisplay; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index 039c16c9..d40966ef 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -25,7 +25,7 @@ type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; onText: OnText; onFinalMessage: OnFinalMessage; - onError: (error: string) => void; + onError: (error: Error | string) => void; voidConfig: VoidConfig; _setAborter: (aborter: () => void) => void; @@ -35,7 +35,7 @@ type SendLLMMessageFnTypeExternal = (params: { messages: LLMMessage[]; onText: OnText; onFinalMessage: (fullText: string) => void; - onError: (error: string) => void; + onError: (error: Error | string) => void; voidConfig: VoidConfig | null; abortRef: AbortRef; @@ -92,7 +92,7 @@ const sendAnthropicMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFi onError('Invalid API key.') } else { - onError(error.message) + onError(error) } }) @@ -136,13 +136,8 @@ const sendGeminiMsg: SendLLMMessageFnTypeInternal = async ({ messages, onText, o 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('Invalid API key.'); } else { onError(error); @@ -197,13 +192,8 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal }) // 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('Invalid API key.'); } else { onError(error); @@ -297,8 +287,8 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin } }) - .catch(e => { - onError(e) + .catch(error => { + onError(error) }); } @@ -307,6 +297,7 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin + export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText: onText_, @@ -350,7 +341,8 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ onFinalMessage_(fullText) } - const onError = (error: string) => { + const onError = (error: Error | string) => { + console.error('sendLLMMessage onError:', error) if (_didAbort) return captureChatEvent(`${loggingName} - Error`, { error }) onError_(error) @@ -391,8 +383,9 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ } catch (e) { - onError(`Unexpected Error in sendLLMMessage: ${e}`); - (_aborter as any)?.() + if (e instanceof Error) { onError(e) } + else { onError(`Unexpected Error in sendLLMMessage: ${e}`); } + ; (_aborter as any)?.() _didAbort = true } From e4107e125710ae5934e567ef5bc1f697b6a2fd6a Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 24 Nov 2024 17:53:57 -0800 Subject: [PATCH 03/12] add server? --- .../windows/electron-main/windowImpl.ts | 16 +++ .../browser/react/src/util/ErrorDisplay.tsx | 2 +- .../react/src/util/mountFnGenerator.tsx | 2 - .../browser/react/src/util/sendLLMMessage.tsx | 43 ++----- .../void/browser/registerSendLLMMessage.ts | 110 ++++++++++++++++++ 5 files changed, 137 insertions(+), 36 deletions(-) create mode 100644 src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index ae108b99..0b426a16 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -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 | undefined; private getMarketplaceHeaders(): Promise { if (!this.marketplaceHeadersPromise) { diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx index db59bff6..8ff04ebf 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx @@ -19,7 +19,7 @@ const getErrorDetails = (error: unknown) => { // If fetch() fails, it gives an opaque message. We add extra details to the error. if ((error instanceof Error) && (error.cause + '').includes('TypeError: Failed to fetch')) { e = error as any - e.voidMessage = opaqueMessage + e['Void Team'] = opaqueMessage } else if (error instanceof Error) { e = error diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx index f075e9ac..6ab7a361 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx @@ -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'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index d40966ef..509dd534 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,26 +4,11 @@ import { Ollama } from 'ollama/browser' import { Content, GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai'; import { posthog } from 'posthog-js' import type { VoidConfig } from '../../../registerConfig.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; -} +import type { LLMMessage, LLMMessageOnText, OnFinalMessage, SendLLMMessageFnType, } from '../../../registerSendLLMMessage.js'; type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; - onText: OnText; + onText: LLMMessageOnText; onFinalMessage: OnFinalMessage; onError: (error: Error | string) => void; voidConfig: VoidConfig; @@ -31,18 +16,6 @@ type SendLLMMessageFnTypeInternal = (params: { _setAborter: (aborter: () => void) => void; }) => void -type SendLLMMessageFnTypeExternal = (params: { - messages: LLMMessage[]; - onText: OnText; - onFinalMessage: (fullText: string) => void; - onError: (error: 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 @@ -53,6 +26,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"] @@ -298,7 +275,7 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin -export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ +export const sendLLMMessage: SendLLMMessageFnType = ({ messages, onText: onText_, onFinalMessage: onFinalMessage_, @@ -573,7 +550,7 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ // 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 @@ -593,7 +570,7 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ // mode: 'chat' | 'fim', // messages: LLMMessage[], // options?: LLMMessageOptions, -// onText: OnText, +// onText: LLMMessageOnText, // onFinalMessage: OnFinalMessage, // onError: (error: string) => void, // abortRef: AbortRef, @@ -606,7 +583,7 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ // | { mode: 'fim', messages?: undefined, fimInfo: FimInfo, } // ) & { // options?: LLMMessageOptions, -// onText: OnText, +// onText: LLMMessageOnText, // onFinalMessage: OnFinalMessage, // onError: (error: string) => void, // abortRef: AbortRef, diff --git a/src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts b/src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts new file mode 100644 index 00000000..e2dff4d2 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { Server as UtilityProcessServer } from '../../../../base/parts/ipc/node/ipc.mp.js'; +import { IChannel, IServerChannel, StaticRouter } from '../../../../base/parts/ipc/common/ipc.js'; +import { Event } from '../../../../base/common/event.js'; +import { VoidConfig } from './registerConfig.js'; + +export type LLMMessageAbortRef = { current: (() => void) | null } + +export type LLMMessageOnText = (newText: string, fullText: string) => void + +export type OnFinalMessage = (input: string) => void + +export type LLMMessage = { + role: 'system' | 'user' | 'assistant'; + content: string; +} + +export type SendLLMMessageFnType = (params: { + messages: LLMMessage[]; + onText: LLMMessageOnText; + onFinalMessage: (fullText: string) => void; + onError: (error: Error | string) => void; + voidConfig: VoidConfig | null; + abortRef: LLMMessageAbortRef; + + logging: { + loggingName: string, + }; +}) => void + +export const ISendLLMMessageService = createDecorator('sendLLMMessageService'); + +export interface ISendLLMMessageService { + readonly _serviceBrand: undefined; + + sendMessage(params: { + messages: LLMMessage[]; + onText: LLMMessageOnText; + onFinalMessage: OnFinalMessage; + onError: (error: Error | string) => void; + voidConfig: VoidConfig; + }): Promise; +} + +class SendLLMMessageChannel implements IServerChannel { + constructor() { + + + } + + listen(_: unknown, event: string): Event { + throw new Error('No events available'); + } + + call(context: any, command: string, args?: any[]): Promise { + switch (command) { + case 'sendMessage': + console.log('ARGS', args) + // this.service.sendMessage(args![0]); + default: + throw new Error(`Invalid command ${command}`); + } + } +} + +export class SendLLMMessageService extends Disposable implements ISendLLMMessageService { + _serviceBrand: undefined; + private readonly server: UtilityProcessServer; + private channel: IChannel | undefined; + + constructor() { + super(); + + // Create the utility process server + this.server = this._register(new UtilityProcessServer()); + + // Register our channel + this.server.registerChannel('sendLLMMessage', new SendLLMMessageChannel()); + + // Get the channel from the utility process + this.channel = this.server.getChannel('sendLLMMessage', new StaticRouter(() => true)); + } + + async sendMessage(params: { + messages: LLMMessage[]; + onText: LLMMessageOnText; + onFinalMessage: OnFinalMessage; + onError: (error: Error | string) => void; + voidConfig: VoidConfig; + }): Promise { + if (!this.channel) { + throw new Error('LLM Message service not initialized'); + } + + try { + await this.channel.call('sendMessage', [params]); + } catch (error) { + params.onError(error instanceof Error ? error : new Error(String(error))); + } + } +} + +registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Eager); From 9ef1aa2c6020802b1d2ec83d41dafc61ddb17252 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 24 Nov 2024 20:28:54 -0800 Subject: [PATCH 04/12] fix cors progress --- src/vs/code/electron-main/app.ts | 6 +- .../contrib/void/browser/misc/build.js | 66 ------- .../contrib/void/browser/misc/oldpackage.json | 168 ------------------ .../browser/react/src/util/sendLLMMessage.tsx | 2 +- .../void/browser/registerInlineDiffs.ts | 6 +- .../void/browser/registerSendLLMMessage.ts | 110 ------------ .../services/void/browser/sendLLMMessage.ts | 59 ++++++ .../services/void/common/sendLLMMessage.ts | 14 ++ .../void/electron-sandbox/sendLLMMessage.ts | 17 ++ src/vs/workbench/workbench.common.main.ts | 2 +- src/vs/workbench/workbench.desktop.main.ts | 9 + .../workbench/workbench.web.main.internal.ts | 8 + 12 files changed, 118 insertions(+), 349 deletions(-) delete mode 100644 src/vs/workbench/contrib/void/browser/misc/build.js delete mode 100644 src/vs/workbench/contrib/void/browser/misc/oldpackage.json delete mode 100644 src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts create mode 100644 src/vs/workbench/services/void/browser/sendLLMMessage.ts create mode 100644 src/vs/workbench/services/void/common/sendLLMMessage.ts create mode 100644 src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 29429516..6d19d280 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -148,7 +148,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(); @@ -1181,6 +1181,10 @@ export class CodeApplication extends Disposable { const keyboardLayoutChannel = ProxyChannel.fromService(accessor.get(IKeyboardLayoutMainService), disposables); mainProcessElectronServer.registerChannel('keyboardLayout', keyboardLayoutChannel); + // Void + const sendLLMMessageChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService), disposables); + mainProcessElectronServer.registerChannel('sendLLMMessage', sendLLMMessageChannel); + // Native host (main & shared process) this.nativeHostMainService = accessor.get(INativeHostMainService); const nativeHostChannel = ProxyChannel.fromService(this.nativeHostMainService, disposables); diff --git a/src/vs/workbench/contrib/void/browser/misc/build.js b/src/vs/workbench/contrib/void/browser/misc/build.js deleted file mode 100644 index 60699b7a..00000000 --- a/src/vs/workbench/contrib/void/browser/misc/build.js +++ /dev/null @@ -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', -// }) - -// })() - diff --git a/src/vs/workbench/contrib/void/browser/misc/oldpackage.json b/src/vs/workbench/contrib/void/browser/misc/oldpackage.json deleted file mode 100644 index f1eedb08..00000000 --- a/src/vs/workbench/contrib/void/browser/misc/oldpackage.json +++ /dev/null @@ -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" - } -} diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index 509dd534..7430db42 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,7 +4,7 @@ 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, LLMMessageOnText, OnFinalMessage, SendLLMMessageFnType, } from '../../../registerSendLLMMessage.js'; +import type { LLMMessage, LLMMessageOnText, OnFinalMessage, SendLLMMessageFnType, } from '../../../../../../services/void/browser/sendLLMMessage.js'; type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index 85d1584d..4dafd4d4 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -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,8 @@ 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 { ISendLLMMessageService } from '../../../services/void/common/sendLLMMessage.js'; +// import { sendLLMMessage } from './react/out/util/sendLLMMessage.js'; // gets converted to --vscode-void-greenBG, see void.css @@ -148,6 +149,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(); @@ -729,7 +731,7 @@ Please finish writing the new file by applying the diff to the original file. Re const abortRef = { current: null } as { current: null | (() => void) } await new Promise((resolve, reject) => { - sendLLMMessage({ + this._sendLLMMessageService.sendLLMMessage({ logging: { loggingName: 'streamChunk' }, messages: [ { role: 'system', content: writeFileWithDiffInstructions, }, diff --git a/src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts b/src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts deleted file mode 100644 index e2dff4d2..00000000 --- a/src/vs/workbench/contrib/void/browser/registerSendLLMMessage.ts +++ /dev/null @@ -1,110 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Glass Devtools, Inc. All rights reserved. - * Void Editor additions licensed under the AGPLv3 License. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { Server as UtilityProcessServer } from '../../../../base/parts/ipc/node/ipc.mp.js'; -import { IChannel, IServerChannel, StaticRouter } from '../../../../base/parts/ipc/common/ipc.js'; -import { Event } from '../../../../base/common/event.js'; -import { VoidConfig } from './registerConfig.js'; - -export type LLMMessageAbortRef = { current: (() => void) | null } - -export type LLMMessageOnText = (newText: string, fullText: string) => void - -export type OnFinalMessage = (input: string) => void - -export type LLMMessage = { - role: 'system' | 'user' | 'assistant'; - content: string; -} - -export type SendLLMMessageFnType = (params: { - messages: LLMMessage[]; - onText: LLMMessageOnText; - onFinalMessage: (fullText: string) => void; - onError: (error: Error | string) => void; - voidConfig: VoidConfig | null; - abortRef: LLMMessageAbortRef; - - logging: { - loggingName: string, - }; -}) => void - -export const ISendLLMMessageService = createDecorator('sendLLMMessageService'); - -export interface ISendLLMMessageService { - readonly _serviceBrand: undefined; - - sendMessage(params: { - messages: LLMMessage[]; - onText: LLMMessageOnText; - onFinalMessage: OnFinalMessage; - onError: (error: Error | string) => void; - voidConfig: VoidConfig; - }): Promise; -} - -class SendLLMMessageChannel implements IServerChannel { - constructor() { - - - } - - listen(_: unknown, event: string): Event { - throw new Error('No events available'); - } - - call(context: any, command: string, args?: any[]): Promise { - switch (command) { - case 'sendMessage': - console.log('ARGS', args) - // this.service.sendMessage(args![0]); - default: - throw new Error(`Invalid command ${command}`); - } - } -} - -export class SendLLMMessageService extends Disposable implements ISendLLMMessageService { - _serviceBrand: undefined; - private readonly server: UtilityProcessServer; - private channel: IChannel | undefined; - - constructor() { - super(); - - // Create the utility process server - this.server = this._register(new UtilityProcessServer()); - - // Register our channel - this.server.registerChannel('sendLLMMessage', new SendLLMMessageChannel()); - - // Get the channel from the utility process - this.channel = this.server.getChannel('sendLLMMessage', new StaticRouter(() => true)); - } - - async sendMessage(params: { - messages: LLMMessage[]; - onText: LLMMessageOnText; - onFinalMessage: OnFinalMessage; - onError: (error: Error | string) => void; - voidConfig: VoidConfig; - }): Promise { - if (!this.channel) { - throw new Error('LLM Message service not initialized'); - } - - try { - await this.channel.call('sendMessage', [params]); - } catch (error) { - params.onError(error instanceof Error ? error : new Error(String(error))); - } - } -} - -registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Eager); diff --git a/src/vs/workbench/services/void/browser/sendLLMMessage.ts b/src/vs/workbench/services/void/browser/sendLLMMessage.ts new file mode 100644 index 00000000..44b12b83 --- /dev/null +++ b/src/vs/workbench/services/void/browser/sendLLMMessage.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Glass Devtools, Inc. All rights reserved. + * Void Editor additions licensed under the AGPLv3 License. + *--------------------------------------------------------------------------------------------*/ + +import { VoidConfig } from '../../../contrib/void/browser/registerConfig.js'; +import { ISendLLMMessageService } from '../common/sendLLMMessage.js'; +import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; +import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; + +export type LLMMessageAbortRef = { current: (() => void) | null } + +export type LLMMessageOnText = (newText: string, fullText: string) => void + +export type OnFinalMessage = (input: string) => void + +export type LLMMessage = { + role: 'system' | 'user' | 'assistant'; + content: string; +} + +export type SendLLMMessageFnType = (params: { + messages: LLMMessage[]; + onText: LLMMessageOnText; + onFinalMessage: (fullText: string) => void; + onError: (error: Error | string) => void; + voidConfig: VoidConfig | null; + abortRef: LLMMessageAbortRef; + + logging: { + loggingName: string, + }; +}) => void + + +// BROWSER IMPLEMENTATION OF SENDLLMMESSAGE +// Uses a proxy to the actual Node implementation of SendLLMMessageService + +export class SendLLMMessageService implements ISendLLMMessageService { + static readonly ID = 'void.contrib.browserSendLLMMessageService'; + + readonly _serviceBrand: undefined; + + readonly _proxySendLLMService: ISendLLMMessageService + + constructor( + @IMainProcessService mainProcessService: IMainProcessService + ) { + this._proxySendLLMService = ProxyChannel.toService(mainProcessService.getChannel('sendLLMMessage')); + } + + sendLLMMessage(data: any): Promise { + return this._proxySendLLMService.sendLLMMessage(data); + } +} + +registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed); + diff --git a/src/vs/workbench/services/void/common/sendLLMMessage.ts b/src/vs/workbench/services/void/common/sendLLMMessage.ts new file mode 100644 index 00000000..c4c45605 --- /dev/null +++ b/src/vs/workbench/services/void/common/sendLLMMessage.ts @@ -0,0 +1,14 @@ +// void/common/sendLLMMessage.ts + +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; + +export const ISendLLMMessageService = createDecorator('sendLLMMessageService'); + +// defines an interface that node/ creates and browser/ uses +export interface ISendLLMMessageService { + readonly _serviceBrand: undefined; + + sendLLMMessage(data: any): Promise; +} + + diff --git a/src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts b/src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts new file mode 100644 index 00000000..906cf84e --- /dev/null +++ b/src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts @@ -0,0 +1,17 @@ +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { ISendLLMMessageService } from '../common/sendLLMMessage.js'; + + +// NODE IMPLEMENTATION OF SENDLLMMESSAGE + +export class SendLLMMessageService implements ISendLLMMessageService { + readonly _serviceBrand: undefined; + + async sendLLMMessage(data: any): Promise { + console.log('NODE sendLLMMessage', data); + // Your existing logic to send a message to the server + // For example: + // return fetch('https://your-server.com/api', { method: 'POST', body: JSON.stringify(data) }); + } +} +registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 30e4ecc9..655ec116 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -14,7 +14,7 @@ import './browser/workbench.contribution.js'; -//#region --- void +//#region --- Void // Void added this: import './contrib/void/browser/void.contribution.js'; //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 57bf9f01..f8a16b85 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -32,6 +32,15 @@ import './electron-sandbox/parts/dialogs/dialog.contribution.js'; //#endregion + + +//#region --- Void +// Void added this (modeling off of import '.*clipboardservice.js'): +import './services/void/electron-sandbox/sendLLMMessage.js'; +//#endregion + + + //#region --- workbench services import './services/textfile/electron-sandbox/nativeTextFileService.js'; diff --git a/src/vs/workbench/workbench.web.main.internal.ts b/src/vs/workbench/workbench.web.main.internal.ts index 402696a4..27cbc8c4 100644 --- a/src/vs/workbench/workbench.web.main.internal.ts +++ b/src/vs/workbench/workbench.web.main.internal.ts @@ -32,6 +32,14 @@ import './browser/web.main.js'; //#endregion + +//#region --- Void +// Void added this (modeling off of import '.*clipboardservice.js'): +import './services/void/browser/sendLLMMessage.js'; +//#endregion + + + //#region --- workbench services import './services/integrity/browser/integrityService.js'; From aa95ceafd2c801be2cd473580af6ff868e85f392 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 24 Nov 2024 20:39:15 -0800 Subject: [PATCH 05/12] proxy to node appears to work!! --- .../react/src/sidebar-tsx/SidebarChat.tsx | 4 +-- .../browser/react/src/util/sendLLMMessage.tsx | 7 ++-- .../contrib/void/browser/registerSidebar.ts | 3 ++ .../services/void/browser/sendLLMMessage.ts | 31 ++--------------- .../services/void/common/sendLLMMessage.ts | 33 +++++++++++++++++-- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 888a3357..a3b8d35a 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -179,7 +179,7 @@ export const SidebarChat = () => { const [latestError, setLatestError] = useState(null) - + const sendLLMMessageService = useService('sendLLMMessageService') const isDisabled = !instructions @@ -210,7 +210,7 @@ export const SidebarChat = () => { // send message to LLM - sendLLMMessage({ + sendLLMMessageService.sendLLMMessage({ logging: { loggingName: 'Chat' }, messages: [...(currentThread?.messages ?? []).map(m => ({ role: m.role, content: m.content })),], onText: (newText, fullText) => setMessageStream(fullText), diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index 7430db42..3f37ca94 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,7 +4,8 @@ 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, LLMMessageOnText, OnFinalMessage, SendLLMMessageFnType, } from '../../../../../../services/void/browser/sendLLMMessage.js'; +import type { LLMMessage, LLMMessageOnText, OnFinalMessage, } from '../../../../../../services/void/common/sendLLMMessage.js'; +import { SendLLMMessageParams } from '../../../../../../services/void/common/sendLLMMessage.js'; type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; @@ -275,7 +276,7 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin -export const sendLLMMessage: SendLLMMessageFnType = ({ +export const sendLLMMessage = ({ messages, onText: onText_, onFinalMessage: onFinalMessage_, @@ -283,7 +284,7 @@ export const sendLLMMessage: SendLLMMessageFnType = ({ abortRef: abortRef_, voidConfig, logging: { loggingName } -}) => { +}: SendLLMMessageParams) => { if (!voidConfig) return; // trim message content (Anthropic and other providers give an error if there is trailing whitespace) diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/registerSidebar.ts index f3b53d82..f5447ba2 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/registerSidebar.ts @@ -47,6 +47,7 @@ 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 '../../../services/void/common/sendLLMMessage.js'; // import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; @@ -65,6 +66,7 @@ export type ReactServicesType = { fileService: IFileService; modelService: IModelService; inlineDiffService: IInlineDiffsService; + sendLLMMessageService: ISendLLMMessageService; } // ---------- Define viewpane ---------- @@ -109,6 +111,7 @@ class VoidSidebarViewPane extends ViewPane { fileService: accessor.get(IFileService), modelService: accessor.get(IModelService), inlineDiffService: accessor.get(IInlineDiffsService), + sendLLMMessageService: accessor.get(ISendLLMMessageService), } mountFn(root, services); }); diff --git a/src/vs/workbench/services/void/browser/sendLLMMessage.ts b/src/vs/workbench/services/void/browser/sendLLMMessage.ts index 44b12b83..6b7cf2f9 100644 --- a/src/vs/workbench/services/void/browser/sendLLMMessage.ts +++ b/src/vs/workbench/services/void/browser/sendLLMMessage.ts @@ -3,36 +3,11 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { VoidConfig } from '../../../contrib/void/browser/registerConfig.js'; -import { ISendLLMMessageService } from '../common/sendLLMMessage.js'; +import { ISendLLMMessageService, SendLLMMessageParams } from '../common/sendLLMMessage.js'; import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -export type LLMMessageAbortRef = { current: (() => void) | null } - -export type LLMMessageOnText = (newText: string, fullText: string) => void - -export type OnFinalMessage = (input: string) => void - -export type LLMMessage = { - role: 'system' | 'user' | 'assistant'; - content: string; -} - -export type SendLLMMessageFnType = (params: { - messages: LLMMessage[]; - onText: LLMMessageOnText; - onFinalMessage: (fullText: string) => void; - onError: (error: Error | string) => void; - voidConfig: VoidConfig | null; - abortRef: LLMMessageAbortRef; - - logging: { - loggingName: string, - }; -}) => void - // BROWSER IMPLEMENTATION OF SENDLLMMESSAGE // Uses a proxy to the actual Node implementation of SendLLMMessageService @@ -50,8 +25,8 @@ export class SendLLMMessageService implements ISendLLMMessageService { this._proxySendLLMService = ProxyChannel.toService(mainProcessService.getChannel('sendLLMMessage')); } - sendLLMMessage(data: any): Promise { - return this._proxySendLLMService.sendLLMMessage(data); + sendLLMMessage(params: SendLLMMessageParams) { + this._proxySendLLMService.sendLLMMessage(params); } } diff --git a/src/vs/workbench/services/void/common/sendLLMMessage.ts b/src/vs/workbench/services/void/common/sendLLMMessage.ts index c4c45605..f50f4611 100644 --- a/src/vs/workbench/services/void/common/sendLLMMessage.ts +++ b/src/vs/workbench/services/void/common/sendLLMMessage.ts @@ -1,6 +1,35 @@ // void/common/sendLLMMessage.ts import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { VoidConfig } from '../../../contrib/void/browser/registerConfig.js'; + + + +export type LLMMessageAbortRef = { current: (() => void) | null } + +export type LLMMessageOnText = (newText: string, fullText: string) => void + +export type OnFinalMessage = (input: string) => void + +export type LLMMessage = { + role: 'system' | 'user' | 'assistant'; + content: string; +} + +export type SendLLMMessageParams = { + messages: LLMMessage[]; + onText: LLMMessageOnText; + onFinalMessage: (fullText: string) => void; + onError: (error: Error | string) => void; + voidConfig: VoidConfig | null; + abortRef: LLMMessageAbortRef; + + logging: { + loggingName: string, + }; +} + + export const ISendLLMMessageService = createDecorator('sendLLMMessageService'); @@ -8,7 +37,7 @@ export const ISendLLMMessageService = createDecorator('s export interface ISendLLMMessageService { readonly _serviceBrand: undefined; - sendLLMMessage(data: any): Promise; + sendLLMMessage: (params: SendLLMMessageParams) => void; + } - From e6c779d91a2c4be6488e66c9fc3ad542a66d788c Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Sun, 24 Nov 2024 23:43:10 -0800 Subject: [PATCH 06/12] progress --- src/vs/code/electron-main/app.ts | 20 +++++++++++++++++-- .../inlineDiffService/inlineDiffService.ts | 2 +- .../void/browser/sendLLMMessage.ts | 8 ++++---- .../void/common/sendLLMMessage.ts | 4 ++-- .../void/electron-main/sendLLMMessage.ts | 20 +++++++++++++++++++ .../contrib/void/browser/react/README.md | 2 +- .../react/src/sidebar-tsx/SidebarChat.tsx | 1 - .../browser/react/src/util/sendLLMMessage.tsx | 4 ++-- .../void/browser/registerInlineDiffs.ts | 5 +++-- .../contrib/void/browser/registerSidebar.ts | 2 +- .../electron-sandbox/desktop.main.ts | 5 +++++ .../void/electron-sandbox/sendLLMMessage.ts | 17 ---------------- src/vs/workbench/workbench.common.main.ts | 1 + src/vs/workbench/workbench.desktop.main.ts | 11 ++++------ .../workbench/workbench.web.main.internal.ts | 5 ----- test/unit/electron/preload.js | 12 +++++++++++ 16 files changed, 74 insertions(+), 45 deletions(-) rename src/vs/{workbench/services => platform}/void/browser/sendLLMMessage.ts (76%) rename src/vs/{workbench/services => platform}/void/common/sendLLMMessage.ts (84%) create mode 100644 src/vs/platform/void/electron-main/sendLLMMessage.ts delete mode 100644 src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 6d19d280..8f4a62ad 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -121,6 +121,9 @@ 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 { ISendLLMMessageService } from '../../platform/void/common/sendLLMMessage.js'; +import { SendLLMMessageService } from '../../platform/void/electron-main/sendLLMMessage.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). @@ -508,6 +511,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 { @@ -998,6 +1011,9 @@ export class CodeApplication extends Disposable { break; } + // Void + services.set(ISendLLMMessageService, new SyncDescriptor(SendLLMMessageService)); + // Windows services.set(IWindowsMainService, new SyncDescriptor(WindowsMainService, [machineId, sqmId, devDeviceId, this.userEnv], false)); services.set(IAuxiliaryWindowsMainService, new SyncDescriptor(AuxiliaryWindowsMainService, undefined, false)); @@ -1182,8 +1198,8 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel('keyboardLayout', keyboardLayoutChannel); // Void - const sendLLMMessageChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService), disposables); - mainProcessElectronServer.registerChannel('sendLLMMessage', sendLLMMessageChannel); + const sendLLMMessageChannel = ProxyChannel.fromService(accessor.get(ISendLLMMessageService), disposables); + mainProcessElectronServer.registerChannel('void-channel-sendLLMMessage', sendLLMMessageChannel); // Native host (main & shared process) this.nativeHostMainService = accessor.get(INativeHostMainService); diff --git a/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts b/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts index 0303e43f..7c2d1324 100644 --- a/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts +++ b/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts @@ -12,7 +12,7 @@ export interface IInlineDiffService { removeDiffs(editor: ICodeEditor): void; } -export const IInlineDiffService = createDecorator('inlineDiffService'); +export const IInlineDiffService = createDecorator('inlineDiffServiceOld'); class InlineDiffService extends Disposable implements IInlineDiffService { private readonly _diffDecorations = new Map(); diff --git a/src/vs/workbench/services/void/browser/sendLLMMessage.ts b/src/vs/platform/void/browser/sendLLMMessage.ts similarity index 76% rename from src/vs/workbench/services/void/browser/sendLLMMessage.ts rename to src/vs/platform/void/browser/sendLLMMessage.ts index 6b7cf2f9..14824b36 100644 --- a/src/vs/workbench/services/void/browser/sendLLMMessage.ts +++ b/src/vs/platform/void/browser/sendLLMMessage.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { ISendLLMMessageService, SendLLMMessageParams } from '../common/sendLLMMessage.js'; -import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; -import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { IMainProcessService } from '../../ipc/common/mainProcessService.js'; +import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; // BROWSER IMPLEMENTATION OF SENDLLMMESSAGE @@ -22,7 +22,7 @@ export class SendLLMMessageService implements ISendLLMMessageService { constructor( @IMainProcessService mainProcessService: IMainProcessService ) { - this._proxySendLLMService = ProxyChannel.toService(mainProcessService.getChannel('sendLLMMessage')); + this._proxySendLLMService = ProxyChannel.toService(mainProcessService.getChannel('void-channel-sendLLMMessage')); } sendLLMMessage(params: SendLLMMessageParams) { diff --git a/src/vs/workbench/services/void/common/sendLLMMessage.ts b/src/vs/platform/void/common/sendLLMMessage.ts similarity index 84% rename from src/vs/workbench/services/void/common/sendLLMMessage.ts rename to src/vs/platform/void/common/sendLLMMessage.ts index f50f4611..3e154790 100644 --- a/src/vs/workbench/services/void/common/sendLLMMessage.ts +++ b/src/vs/platform/void/common/sendLLMMessage.ts @@ -1,7 +1,7 @@ // void/common/sendLLMMessage.ts -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { VoidConfig } from '../../../contrib/void/browser/registerConfig.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { VoidConfig } from '../../../workbench/contrib/void/browser/registerConfig.js'; diff --git a/src/vs/platform/void/electron-main/sendLLMMessage.ts b/src/vs/platform/void/electron-main/sendLLMMessage.ts new file mode 100644 index 00000000..de2c67fb --- /dev/null +++ b/src/vs/platform/void/electron-main/sendLLMMessage.ts @@ -0,0 +1,20 @@ +// import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { ISendLLMMessageService } from '../common/sendLLMMessage.js'; +import { sendLLMMessage } from '../../../workbench/contrib/void/browser/react/out/util/sendLLMMessage.js'; +// import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; +// import { ipcMain } from 'electron'; + +// NODE IMPLEMENTATION OF SENDLLMMESSAGE +export class SendLLMMessageService implements ISendLLMMessageService { + readonly _serviceBrand: undefined; + + async sendLLMMessage(data: any) { + console.log('NODE sendLLMMessage', data); + // ipcMain.emit('vscode:sendLLMMessage', data) + + return sendLLMMessage(data) + } +} + +// we don't need to register this, it's registered in app.ts: +// registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/void/browser/react/README.md b/src/vs/workbench/contrib/void/browser/react/README.md index e6bfee13..9f998f8d 100644 --- a/src/vs/workbench/contrib/void/browser/react/README.md +++ b/src/vs/workbench/contrib/void/browser/react/README.md @@ -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). diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index a3b8d35a..1b18f93c 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -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'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index 3f37ca94..eb9a9cef 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,8 +4,8 @@ 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, LLMMessageOnText, OnFinalMessage, } from '../../../../../../services/void/common/sendLLMMessage.js'; -import { SendLLMMessageParams } from '../../../../../../services/void/common/sendLLMMessage.js'; +import type { LLMMessage, LLMMessageOnText, OnFinalMessage, } from '../../../../../../../platform/void/common/sendLLMMessage.js'; +import { SendLLMMessageParams } from '../../../../../../../platform/void/common/sendLLMMessage.js'; type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index 4dafd4d4..b3885525 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -28,7 +28,8 @@ 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 { ISendLLMMessageService } from '../../../services/void/common/sendLLMMessage.js'; +import { ISendLLMMessageService } from '../../../../platform/void/common/sendLLMMessage.js'; +// import { ISendLLMMessageService } from '../../../../platform/void/common/sendLLMMessage.js'; // import { sendLLMMessage } from './react/out/util/sendLLMMessage.js'; @@ -116,7 +117,7 @@ export interface IInlineDiffsService { } -export const IInlineDiffsService = createDecorator('inlineDiffsService'); +export const IInlineDiffsService = createDecorator('inlineDiffAreasService'); class InlineDiffsService extends Disposable implements IInlineDiffsService { _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/registerSidebar.ts index f5447ba2..104a2354 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/registerSidebar.ts @@ -47,7 +47,7 @@ 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 '../../../services/void/common/sendLLMMessage.js'; +import { ISendLLMMessageService } from '../../../../platform/void/common/sendLLMMessage.js'; // import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 24d89f63..da3a7ede 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -328,6 +328,11 @@ export class DesktopMain extends Disposable { // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // // Void + // const sendLLMMessageService = new SendLLMMessageService(); + // serviceCollection.set(ISendLLMMessageService, sendLLMMessageService); + + return { serviceCollection, logService, storageService, configurationService }; } diff --git a/src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts b/src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts deleted file mode 100644 index 906cf84e..00000000 --- a/src/vs/workbench/services/void/electron-sandbox/sendLLMMessage.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { ISendLLMMessageService } from '../common/sendLLMMessage.js'; - - -// NODE IMPLEMENTATION OF SENDLLMMESSAGE - -export class SendLLMMessageService implements ISendLLMMessageService { - readonly _serviceBrand: undefined; - - async sendLLMMessage(data: any): Promise { - console.log('NODE sendLLMMessage', data); - // Your existing logic to send a message to the server - // For example: - // return fetch('https://your-server.com/api', { method: 'POST', body: JSON.stringify(data) }); - } -} -registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 655ec116..0c3b9dd3 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -17,6 +17,7 @@ import './browser/workbench.contribution.js'; //#region --- Void // Void added this: import './contrib/void/browser/void.contribution.js'; +import '../platform/void/browser/sendLLMMessage.js'; //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index f8a16b85..5b0a42b6 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -31,13 +31,10 @@ import './electron-sandbox/parts/dialogs/dialog.contribution.js'; //#endregion - - - -//#region --- Void -// Void added this (modeling off of import '.*clipboardservice.js'): -import './services/void/electron-sandbox/sendLLMMessage.js'; -//#endregion +// //#region --- Void +// // Void added this (modeling off of import '.*clipboardservice.js'): +// import './services/void/electron-main/sendLLMMessage.js'; +// //#endregion diff --git a/src/vs/workbench/workbench.web.main.internal.ts b/src/vs/workbench/workbench.web.main.internal.ts index 27cbc8c4..f8cfa0e0 100644 --- a/src/vs/workbench/workbench.web.main.internal.ts +++ b/src/vs/workbench/workbench.web.main.internal.ts @@ -33,11 +33,6 @@ import './browser/web.main.js'; -//#region --- Void -// Void added this (modeling off of import '.*clipboardservice.js'): -import './services/void/browser/sendLLMMessage.js'; -//#endregion - //#region --- workbench services diff --git a/test/unit/electron/preload.js b/test/unit/electron/preload.js index 04439d66..4c878e1c 100644 --- a/test/unit/electron/preload.js +++ b/test/unit/electron/preload.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} 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) { From 790a2b6a4fdf262ab0d2740ac7bfc3ff4c3f5a2f Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 26 Nov 2024 00:25:47 -0800 Subject: [PATCH 07/12] llm messages work! sent from node! --- src/vs/code/electron-main/app.ts | 15 ++--- .../platform/void/browser/sendLLMMessage.ts | 54 ++++++++++++--- src/vs/platform/void/common/sendLLMMessage.ts | 43 ------------ src/vs/platform/void/common/sendLLMTypes.ts | 43 ++++++++++++ .../void/electron-main/LLMMessageChannel.ts | 66 +++++++++++++++++++ .../void/electron-main/sendLLMMessage.ts | 20 ------ .../react/src/sidebar-tsx/SidebarChat.tsx | 16 +++-- .../browser/react/src/util/sendLLMMessage.tsx | 58 ++++++++-------- .../void/browser/registerInlineDiffs.ts | 15 +++-- .../contrib/void/browser/registerSidebar.ts | 4 +- 10 files changed, 214 insertions(+), 120 deletions(-) delete mode 100644 src/vs/platform/void/common/sendLLMMessage.ts create mode 100644 src/vs/platform/void/common/sendLLMTypes.ts create mode 100644 src/vs/platform/void/electron-main/LLMMessageChannel.ts delete mode 100644 src/vs/platform/void/electron-main/sendLLMMessage.ts diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 8f4a62ad..72a5c2ce 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -121,8 +121,7 @@ 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 { ISendLLMMessageService } from '../../platform/void/common/sendLLMMessage.js'; -import { SendLLMMessageService } from '../../platform/void/electron-main/sendLLMMessage.js'; +import { LLMMessageChannel } from '../../platform/void/electron-main/LLMMessageChannel.js'; /** * The main VS Code application. There will only ever be one instance, @@ -1011,9 +1010,6 @@ export class CodeApplication extends Disposable { break; } - // Void - services.set(ISendLLMMessageService, new SyncDescriptor(SendLLMMessageService)); - // Windows services.set(IWindowsMainService, new SyncDescriptor(WindowsMainService, [machineId, sqmId, devDeviceId, this.userEnv], false)); services.set(IAuxiliaryWindowsMainService, new SyncDescriptor(AuxiliaryWindowsMainService, undefined, false)); @@ -1197,10 +1193,6 @@ export class CodeApplication extends Disposable { const keyboardLayoutChannel = ProxyChannel.fromService(accessor.get(IKeyboardLayoutMainService), disposables); mainProcessElectronServer.registerChannel('keyboardLayout', keyboardLayoutChannel); - // Void - const sendLLMMessageChannel = ProxyChannel.fromService(accessor.get(ISendLLMMessageService), disposables); - mainProcessElectronServer.registerChannel('void-channel-sendLLMMessage', sendLLMMessageChannel); - // Native host (main & shared process) this.nativeHostMainService = accessor.get(INativeHostMainService); const nativeHostChannel = ProxyChannel.fromService(this.nativeHostMainService, disposables); @@ -1245,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); diff --git a/src/vs/platform/void/browser/sendLLMMessage.ts b/src/vs/platform/void/browser/sendLLMMessage.ts index 14824b36..65e68e27 100644 --- a/src/vs/platform/void/browser/sendLLMMessage.ts +++ b/src/vs/platform/void/browser/sendLLMMessage.ts @@ -3,30 +3,68 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { ISendLLMMessageService, SendLLMMessageParams } from '../common/sendLLMMessage.js'; -import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { LLMMessageOnTextEvent, OnErrorEvent, OnFinalMessageEvent, SendLLMMessageParams, SendLLMMessageProxyParams } from '../common/sendLLMTypes.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'; // BROWSER IMPLEMENTATION OF SENDLLMMESSAGE -// Uses a proxy to the actual Node implementation of SendLLMMessageService +export const ISendLLMMessageService = createDecorator('sendLLMMessageService'); + +// defines an interface that node/ creates and browser/ uses +export interface ISendLLMMessageService { + readonly _serviceBrand: undefined; + sendLLMMessage: (params: SendLLMMessageParams) => void; +} + export class SendLLMMessageService implements ISendLLMMessageService { static readonly ID = 'void.contrib.browserSendLLMMessageService'; readonly _serviceBrand: undefined; - - readonly _proxySendLLMService: ISendLLMMessageService + readonly channel: IChannel; constructor( - @IMainProcessService mainProcessService: IMainProcessService + @IMainProcessService mainProcessService: IMainProcessService // used as a renderer (only usable on client side) ) { - this._proxySendLLMService = ProxyChannel.toService(mainProcessService.getChannel('void-channel-sendLLMMessage')); + + this.channel = mainProcessService.getChannel('void-channel-sendLLMMessage') + // const service = ProxyChannel.toService(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service, not needed here } sendLLMMessage(params: SendLLMMessageParams) { - this._proxySendLLMService.sendLLMMessage(params); + 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 = this.channel.listen('onText') + onTextEvent(e => { + console.log('event TEXT EVENT!!!:', JSON.stringify(e, null, 5)) + if (requestId_ !== e.requestId) return; + onText(e) + }) + + const onFinalMessageEvent: Event = this.channel.listen('onFinalMessage') + onFinalMessageEvent(e => { + console.log('FINAL MESSAGE EVENT!!!:', JSON.stringify(e, null, 5)) + if (requestId_ !== e.requestId) return; + onFinalMessage(e) + }) + + const onErrorEvent: Event = this.channel.listen('onError') + onErrorEvent(e => { + console.log('ERROR EVENT!!!:', JSON.stringify(e, null, 5)) + if (requestId_ !== e.requestId) return; + onError(e) + }) + + // params will be stripped of all its functions + this.channel.call('sendLLMMessage', { ...proxyParams, requestId: requestId_ } satisfies SendLLMMessageProxyParams); } } diff --git a/src/vs/platform/void/common/sendLLMMessage.ts b/src/vs/platform/void/common/sendLLMMessage.ts deleted file mode 100644 index 3e154790..00000000 --- a/src/vs/platform/void/common/sendLLMMessage.ts +++ /dev/null @@ -1,43 +0,0 @@ -// void/common/sendLLMMessage.ts - -import { createDecorator } from '../../instantiation/common/instantiation.js'; -import { VoidConfig } from '../../../workbench/contrib/void/browser/registerConfig.js'; - - - -export type LLMMessageAbortRef = { current: (() => void) | null } - -export type LLMMessageOnText = (newText: string, fullText: string) => void - -export type OnFinalMessage = (input: string) => void - -export type LLMMessage = { - role: 'system' | 'user' | 'assistant'; - content: string; -} - -export type SendLLMMessageParams = { - messages: LLMMessage[]; - onText: LLMMessageOnText; - onFinalMessage: (fullText: string) => void; - onError: (error: Error | string) => void; - voidConfig: VoidConfig | null; - abortRef: LLMMessageAbortRef; - - logging: { - loggingName: string, - }; -} - - - -export const ISendLLMMessageService = createDecorator('sendLLMMessageService'); - -// defines an interface that node/ creates and browser/ uses -export interface ISendLLMMessageService { - readonly _serviceBrand: undefined; - - sendLLMMessage: (params: SendLLMMessageParams) => void; - -} - diff --git a/src/vs/platform/void/common/sendLLMTypes.ts b/src/vs/platform/void/common/sendLLMTypes.ts new file mode 100644 index 00000000..bbb163af --- /dev/null +++ b/src/vs/platform/void/common/sendLLMTypes.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +// ---------- definitions ---------- + +export type LLMMessageOnText = (p: { newText: string, fullText: string }) => void + +export type OnFinalMessage = (p: { fullText: string }) => void + +export type OnError = (p: { error: Error | string }) => void + +export type LLMMessageAbortRef = { current: (() => void) | null } + +export type LLMMessage = { + role: 'system' | 'user' | 'assistant'; + content: string; +} + +export type SendLLMMessageParams = { + onText: LLMMessageOnText; + onFinalMessage: OnFinalMessage; + onError: OnError; + + messages: LLMMessage[]; + voidConfig: VoidConfig | null; + abortRef: LLMMessageAbortRef; + + logging: { + loggingName: string, + }; +} + +// can't send functions across a proxy, use listeners instead +export const listenerNames = ['onText', 'onFinalMessage', 'onError'] as const +export type SendLLMMessageProxyParams = Omit & { requestId: string } + +export type LLMMessageOnTextEvent = Parameters[0] & { requestId: string } +export type OnFinalMessageEvent = Parameters[0] & { requestId: string } +export type OnErrorEvent = Parameters[0] & { requestId: string } diff --git a/src/vs/platform/void/electron-main/LLMMessageChannel.ts b/src/vs/platform/void/electron-main/LLMMessageChannel.ts new file mode 100644 index 00000000..3c4b0c8b --- /dev/null +++ b/src/vs/platform/void/electron-main/LLMMessageChannel.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * 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, LLMMessageOnTextEvent, OnErrorEvent, OnFinalMessageEvent, SendLLMMessageParams, SendLLMMessageProxyParams } from '../common/sendLLMTypes.js'; + +// NODE IMPLEMENTATION OF SENDLLMMESSAGE + +export class LLMMessageChannel implements IServerChannel { + private readonly _onText = new Emitter(); + readonly onText = this._onText.event; + + private readonly _onFinalMessage = new Emitter(); + readonly onFinalMessage = this._onFinalMessage.event; + + private readonly _onError = new Emitter(); + readonly onError = this._onError.event; + + constructor() { } + + // browser uses this + listen(_: unknown, event: typeof listenerNames[number]): Event { + console.log('event LISTENING!!!:', event) + 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}`); + } + } + + // both use this + async call(_: unknown, command: string, params: SendLLMMessageProxyParams): Promise { + + if (command !== 'sendLLMMessage') throw new Error(`Invalid call in sendLLMMessage channel: ${command}.\nArgs:\n${JSON.stringify(params, null, 5)}`); + + try { + const { requestId } = params; + const mainThreadParams: SendLLMMessageParams = { + ...params, + onText: ({ newText, fullText }) => { this._onText.fire({ requestId, newText, fullText }); }, + onFinalMessage: ({ fullText }) => { this._onFinalMessage.fire({ requestId, fullText }); }, + onError: ({ error }) => { this._onError.fire({ requestId, error }); }, + } + sendLLMMessage(mainThreadParams); + } + catch (e) { + console.log('sendLLM channel: call error', e) + } + } +} diff --git a/src/vs/platform/void/electron-main/sendLLMMessage.ts b/src/vs/platform/void/electron-main/sendLLMMessage.ts deleted file mode 100644 index de2c67fb..00000000 --- a/src/vs/platform/void/electron-main/sendLLMMessage.ts +++ /dev/null @@ -1,20 +0,0 @@ -// import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { ISendLLMMessageService } from '../common/sendLLMMessage.js'; -import { sendLLMMessage } from '../../../workbench/contrib/void/browser/react/out/util/sendLLMMessage.js'; -// import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; -// import { ipcMain } from 'electron'; - -// NODE IMPLEMENTATION OF SENDLLMMESSAGE -export class SendLLMMessageService implements ISendLLMMessageService { - readonly _serviceBrand: undefined; - - async sendLLMMessage(data: any) { - console.log('NODE sendLLMMessage', data); - // ipcMain.emit('vscode:sendLLMMessage', data) - - return sendLLMMessage(data) - } -} - -// we don't need to register this, it's registered in app.ts: -// registerSingleton(ISendLLMMessageService, SendLLMMessageService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 1b18f93c..74049ef1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -17,6 +17,7 @@ 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 { SendLLMMessageParams } from '../../../../../../../platform/void/common/sendLLMTypes.js'; // import { } from '@vscode/webview-ui-toolkit/react'; @@ -209,11 +210,12 @@ export const SidebarChat = () => { // send message to LLM - sendLLMMessageService.sendLLMMessage({ + + const object: SendLLMMessageParams = { 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,7 +224,7 @@ export const SidebarChat = () => { setMessageStream('') setIsLoading(false) }, - onError: (error) => { + onError: ({ error }) => { console.log('chat: running error') // add assistant's message to chat history, and clear selection @@ -237,7 +239,11 @@ export const SidebarChat = () => { }, voidConfig, abortRef: abortFnRef, - }) + } + + console.log('object!!!!!2', Object.keys(object)) + + sendLLMMessageService.sendLLMMessage(object) setIsLoading(true) diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index eb9a9cef..8dcf6769 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,14 +4,14 @@ 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, LLMMessageOnText, OnFinalMessage, } from '../../../../../../../platform/void/common/sendLLMMessage.js'; -import { SendLLMMessageParams } from '../../../../../../../platform/void/common/sendLLMMessage.js'; +import type { LLMMessage, LLMMessageOnText, OnError, OnFinalMessage, } from '../../../../../../../platform/void/common/sendLLMTypes.js'; +import { SendLLMMessageParams } from '../../../../../../../platform/void/common/sendLLMTypes.js'; type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; onText: LLMMessageOnText; onFinalMessage: OnFinalMessage; - onError: (error: Error | string) => void; + onError: OnError; voidConfig: VoidConfig; _setAborter: (aborter: () => void) => void; @@ -54,23 +54,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) + onError({ error }) } }) @@ -109,16 +109,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 && error.status === 400) { - onError('Invalid API key.'); + onError({ error: 'Invalid API key.' }); } else { - onError(error); + onError({ error }); } }) } @@ -164,14 +164,14 @@ 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 && error.status === 401) { - onError('Invalid API key.'); + onError({ error: 'Invalid API key.' }); } else { onError(error); @@ -199,9 +199,9 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, 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 @@ -249,17 +249,17 @@ 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 }) } } } @@ -307,23 +307,23 @@ export const sendLLMMessage = ({ let _setAborter = (fn: () => void) => { _aborter = fn } let _didAbort = false - const onText = (newText: string, fullText: string) => { + const onText: LLMMessageOnText = ({ 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: Error | string) => { + const onError: OnError = ({ error }) => { console.error('sendLLMMessage onError:', error) if (_didAbort) return captureChatEvent(`${loggingName} - Error`, { error }) - onError_(error) + onError_({ error }) } const onAbort = () => { @@ -355,14 +355,14 @@ export const sendLLMMessage = ({ sendGreptileMsg({ messages, 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) { - if (e instanceof Error) { onError(e) } - else { onError(`Unexpected Error in sendLLMMessage: ${e}`); } + catch (error) { + if (error instanceof Error) { onError({ error }) } + else { onError({ error: `Unexpected Error in sendLLMMessage: ${error}` }); } ; (_aborter as any)?.() _didAbort = true } diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index b3885525..69d2482c 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -28,7 +28,8 @@ 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 { ISendLLMMessageService } from '../../../../platform/void/common/sendLLMMessage.js'; +import { SendLLMMessageParams } from '../../../../platform/void/common/sendLLMTypes.js'; +import { ISendLLMMessageService } from '../../../../platform/void/browser/sendLLMMessage.js'; // import { ISendLLMMessageService } from '../../../../platform/void/common/sendLLMMessage.js'; // import { sendLLMMessage } from './react/out/util/sendLLMMessage.js'; @@ -732,18 +733,19 @@ Please finish writing the new file by applying the diff to the original file. Re const abortRef = { current: null } as { current: null | (() => void) } await new Promise((resolve, reject) => { - this._sendLLMMessageService.sendLLMMessage({ + + const object: SendLLMMessageParams = { 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 ) @@ -760,7 +762,10 @@ Please finish writing the new file by applying the diff to the original file. Re }, voidConfig, abortRef, - }) + } + + console.log('object!!!!!', Object.keys(object)) + this._sendLLMMessageService.sendLLMMessage(object) }) onFinishEdit() diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/registerSidebar.ts index 104a2354..c3e6a768 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/registerSidebar.ts @@ -47,7 +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/common/sendLLMMessage.js'; +import { ISendLLMMessageService } from '../../../../platform/void/browser/sendLLMMessage.js'; + + // import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; From bdc3c9cb7ee10b419cc271990377dee928e3a73f Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 26 Nov 2024 00:41:31 -0800 Subject: [PATCH 08/12] proxy works! --- src/vs/code/electron-main/app.ts | 2 +- ...sendLLMMessage.ts => llmMessageService.ts} | 17 +++++++--------- .../{sendLLMTypes.ts => llmMessageTypes.ts} | 20 +++++++++---------- .../void/electron-main/LLMMessageChannel.ts | 14 ++++++------- .../react/src/sidebar-tsx/SidebarChat.tsx | 6 ++---- .../browser/react/src/util/sendLLMMessage.tsx | 10 +++++----- .../void/browser/registerInlineDiffs.ts | 7 +++---- .../contrib/void/browser/registerSidebar.ts | 2 +- src/vs/workbench/workbench.common.main.ts | 2 +- 9 files changed, 37 insertions(+), 43 deletions(-) rename src/vs/platform/void/browser/{sendLLMMessage.ts => llmMessageService.ts} (75%) rename src/vs/platform/void/common/{sendLLMTypes.ts => llmMessageTypes.ts} (58%) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 72a5c2ce..59e8af61 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -121,7 +121,7 @@ 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'; +import { LLMMessageChannel } from '../../platform/void/electron-main/llmMessageChannel.js'; /** * The main VS Code application. There will only ever be one instance, diff --git a/src/vs/platform/void/browser/sendLLMMessage.ts b/src/vs/platform/void/browser/llmMessageService.ts similarity index 75% rename from src/vs/platform/void/browser/sendLLMMessage.ts rename to src/vs/platform/void/browser/llmMessageService.ts index 65e68e27..b5e6081d 100644 --- a/src/vs/platform/void/browser/sendLLMMessage.ts +++ b/src/vs/platform/void/browser/llmMessageService.ts @@ -3,7 +3,7 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { LLMMessageOnTextEvent, OnErrorEvent, OnFinalMessageEvent, SendLLMMessageParams, SendLLMMessageProxyParams } from '../common/sendLLMTypes.js'; +import { ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, LLMMessageServiceParams, ProxyLLMMessageParams } 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'; @@ -18,7 +18,7 @@ export const ISendLLMMessageService = createDecorator('s // defines an interface that node/ creates and browser/ uses export interface ISendLLMMessageService { readonly _serviceBrand: undefined; - sendLLMMessage: (params: SendLLMMessageParams) => void; + sendLLMMessage: (params: LLMMessageServiceParams) => void; } @@ -36,35 +36,32 @@ export class SendLLMMessageService implements ISendLLMMessageService { // const service = ProxyChannel.toService(mainProcessService.getChannel('void-channel-sendLLMMessage')); // lets you call it like a service, not needed here } - sendLLMMessage(params: SendLLMMessageParams) { + 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 = this.channel.listen('onText') + const onTextEvent: Event = this.channel.listen('onText') onTextEvent(e => { - console.log('event TEXT EVENT!!!:', JSON.stringify(e, null, 5)) if (requestId_ !== e.requestId) return; onText(e) }) - const onFinalMessageEvent: Event = this.channel.listen('onFinalMessage') + const onFinalMessageEvent: Event = this.channel.listen('onFinalMessage') onFinalMessageEvent(e => { - console.log('FINAL MESSAGE EVENT!!!:', JSON.stringify(e, null, 5)) if (requestId_ !== e.requestId) return; onFinalMessage(e) }) - const onErrorEvent: Event = this.channel.listen('onError') + const onErrorEvent: Event = this.channel.listen('onError') onErrorEvent(e => { - console.log('ERROR EVENT!!!:', JSON.stringify(e, null, 5)) if (requestId_ !== e.requestId) return; onError(e) }) // params will be stripped of all its functions - this.channel.call('sendLLMMessage', { ...proxyParams, requestId: requestId_ } satisfies SendLLMMessageProxyParams); + this.channel.call('sendLLMMessage', { ...proxyParams, requestId: requestId_ } satisfies ProxyLLMMessageParams); } } diff --git a/src/vs/platform/void/common/sendLLMTypes.ts b/src/vs/platform/void/common/llmMessageTypes.ts similarity index 58% rename from src/vs/platform/void/common/sendLLMTypes.ts rename to src/vs/platform/void/common/llmMessageTypes.ts index bbb163af..a8172630 100644 --- a/src/vs/platform/void/common/sendLLMTypes.ts +++ b/src/vs/platform/void/common/llmMessageTypes.ts @@ -5,29 +5,29 @@ import { VoidConfig } from '../../../workbench/contrib/void/browser/registerConfig.js'; -// ---------- definitions ---------- +// ---------- type definitions ---------- -export type LLMMessageOnText = (p: { newText: string, fullText: string }) => void +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 LLMMessageAbortRef = { current: (() => void) | null } +export type AbortRef = { current: (() => void) | null } export type LLMMessage = { role: 'system' | 'user' | 'assistant'; content: string; } -export type SendLLMMessageParams = { - onText: LLMMessageOnText; +export type LLMMessageServiceParams = { + onText: OnText; onFinalMessage: OnFinalMessage; onError: OnError; messages: LLMMessage[]; voidConfig: VoidConfig | null; - abortRef: LLMMessageAbortRef; + abortRef: AbortRef; logging: { loggingName: string, @@ -36,8 +36,8 @@ export type SendLLMMessageParams = { // can't send functions across a proxy, use listeners instead export const listenerNames = ['onText', 'onFinalMessage', 'onError'] as const -export type SendLLMMessageProxyParams = Omit & { requestId: string } +export type ProxyLLMMessageParams = Omit & { requestId: string } -export type LLMMessageOnTextEvent = Parameters[0] & { requestId: string } -export type OnFinalMessageEvent = Parameters[0] & { requestId: string } -export type OnErrorEvent = Parameters[0] & { requestId: string } +export type ProxyOnTextPayload = Parameters[0] & { requestId: string } +export type ProxyOnFinalMessagePayload = Parameters[0] & { requestId: string } +export type ProxyOnErrorPayload = Parameters[0] & { requestId: string } diff --git a/src/vs/platform/void/electron-main/LLMMessageChannel.ts b/src/vs/platform/void/electron-main/LLMMessageChannel.ts index 3c4b0c8b..27700928 100644 --- a/src/vs/platform/void/electron-main/LLMMessageChannel.ts +++ b/src/vs/platform/void/electron-main/LLMMessageChannel.ts @@ -11,18 +11,18 @@ 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, LLMMessageOnTextEvent, OnErrorEvent, OnFinalMessageEvent, SendLLMMessageParams, SendLLMMessageProxyParams } from '../common/sendLLMTypes.js'; +import { listenerNames, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, LLMMessageServiceParams, ProxyLLMMessageParams } from '../common/llmMessageTypes.js'; -// NODE IMPLEMENTATION OF SENDLLMMESSAGE +// NODE IMPLEMENTATION OF SENDLLMMESSAGE - calls sendLLMMessage() and returns listeners export class LLMMessageChannel implements IServerChannel { - private readonly _onText = new Emitter(); + private readonly _onText = new Emitter(); readonly onText = this._onText.event; - private readonly _onFinalMessage = new Emitter(); + private readonly _onFinalMessage = new Emitter(); readonly onFinalMessage = this._onFinalMessage.event; - private readonly _onError = new Emitter(); + private readonly _onError = new Emitter(); readonly onError = this._onError.event; constructor() { } @@ -45,13 +45,13 @@ export class LLMMessageChannel implements IServerChannel { } // both use this - async call(_: unknown, command: string, params: SendLLMMessageProxyParams): Promise { + async call(_: unknown, command: string, params: ProxyLLMMessageParams): Promise { if (command !== 'sendLLMMessage') throw new Error(`Invalid call in sendLLMMessage channel: ${command}.\nArgs:\n${JSON.stringify(params, null, 5)}`); try { const { requestId } = params; - const mainThreadParams: SendLLMMessageParams = { + const mainThreadParams: LLMMessageServiceParams = { ...params, onText: ({ newText, fullText }) => { this._onText.fire({ requestId, newText, fullText }); }, onFinalMessage: ({ fullText }) => { this._onFinalMessage.fire({ requestId, fullText }); }, diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 74049ef1..5b62674c 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -17,7 +17,7 @@ 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 { SendLLMMessageParams } from '../../../../../../../platform/void/common/sendLLMTypes.js'; +import { LLMMessageServiceParams } from '../../../../../../../platform/void/common/llmMessageTypes.js'; // import { } from '@vscode/webview-ui-toolkit/react'; @@ -211,7 +211,7 @@ export const SidebarChat = () => { // send message to LLM - const object: SendLLMMessageParams = { + const object: LLMMessageServiceParams = { logging: { loggingName: 'Chat' }, messages: [...(currentThread?.messages ?? []).map(m => ({ role: m.role, content: m.content })),], onText: ({ newText, fullText }) => setMessageStream(fullText), @@ -241,8 +241,6 @@ export const SidebarChat = () => { abortRef: abortFnRef, } - console.log('object!!!!!2', Object.keys(object)) - sendLLMMessageService.sendLLMMessage(object) diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index 8dcf6769..7457a447 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,12 +4,12 @@ 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, LLMMessageOnText, OnError, OnFinalMessage, } from '../../../../../../../platform/void/common/sendLLMTypes.js'; -import { SendLLMMessageParams } from '../../../../../../../platform/void/common/sendLLMTypes.js'; +import type { LLMMessage, OnText, OnError, OnFinalMessage, } from '../../../../../../../platform/void/common/llmMessageTypes.js'; +import { LLMMessageServiceParams } from '../../../../../../../platform/void/common/llmMessageTypes.js'; type SendLLMMessageFnTypeInternal = (params: { messages: LLMMessage[]; - onText: LLMMessageOnText; + onText: OnText; onFinalMessage: OnFinalMessage; onError: OnError; voidConfig: VoidConfig; @@ -284,7 +284,7 @@ export const sendLLMMessage = ({ abortRef: abortRef_, voidConfig, logging: { loggingName } -}: SendLLMMessageParams) => { +}: LLMMessageServiceParams) => { if (!voidConfig) return; // trim message content (Anthropic and other providers give an error if there is trailing whitespace) @@ -307,7 +307,7 @@ export const sendLLMMessage = ({ let _setAborter = (fn: () => void) => { _aborter = fn } let _didAbort = false - const onText: LLMMessageOnText = ({ newText, fullText }) => { + const onText: OnText = ({ newText, fullText }) => { if (_didAbort) return onText_({ newText, fullText }) _fullTextSoFar = fullText diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index 69d2482c..1946cef2 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -28,8 +28,8 @@ 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 { SendLLMMessageParams } from '../../../../platform/void/common/sendLLMTypes.js'; -import { ISendLLMMessageService } from '../../../../platform/void/browser/sendLLMMessage.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'; @@ -734,7 +734,7 @@ Please finish writing the new file by applying the diff to the original file. Re const abortRef = { current: null } as { current: null | (() => void) } await new Promise((resolve, reject) => { - const object: SendLLMMessageParams = { + const object: LLMMessageServiceParams = { logging: { loggingName: 'streamChunk' }, messages: [ { role: 'system', content: writeFileWithDiffInstructions, }, @@ -764,7 +764,6 @@ Please finish writing the new file by applying the diff to the original file. Re abortRef, } - console.log('object!!!!!', Object.keys(object)) this._sendLLMMessageService.sendLLMMessage(object) }) diff --git a/src/vs/workbench/contrib/void/browser/registerSidebar.ts b/src/vs/workbench/contrib/void/browser/registerSidebar.ts index c3e6a768..cddd9853 100644 --- a/src/vs/workbench/contrib/void/browser/registerSidebar.ts +++ b/src/vs/workbench/contrib/void/browser/registerSidebar.ts @@ -47,7 +47,7 @@ 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/sendLLMMessage.js'; +import { ISendLLMMessageService } from '../../../../platform/void/browser/llmMessageService.js'; // import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 0c3b9dd3..0f33a1d1 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -17,7 +17,7 @@ import './browser/workbench.contribution.js'; //#region --- Void // Void added this: import './contrib/void/browser/void.contribution.js'; -import '../platform/void/browser/sendLLMMessage.js'; +import '../platform/void/browser/llmMessageService.js'; //#endregion From c7e9175fd75a6f63ba3dc684012f339fceb868bc Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 26 Nov 2024 01:08:25 -0800 Subject: [PATCH 09/12] add ability to abort --- .../void/browser/llmMessageService.ts | 64 ++++++++++++++----- .../platform/void/common/llmMessageTypes.ts | 17 ++++- .../void/electron-main/LLMMessageChannel.ts | 56 ++++++++++++---- .../browser/react/src/util/sendLLMMessage.tsx | 4 +- .../void/browser/registerInlineDiffs.ts | 1 - 5 files changed, 109 insertions(+), 33 deletions(-) diff --git a/src/vs/platform/void/browser/llmMessageService.ts b/src/vs/platform/void/browser/llmMessageService.ts index b5e6081d..644c38ba 100644 --- a/src/vs/platform/void/browser/llmMessageService.ts +++ b/src/vs/platform/void/browser/llmMessageService.ts @@ -3,13 +3,14 @@ * Void Editor additions licensed under the AGPLv3 License. *--------------------------------------------------------------------------------------------*/ -import { ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, LLMMessageServiceParams, ProxyLLMMessageParams } from '../common/llmMessageTypes.js'; +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 @@ -23,10 +24,11 @@ export interface ISendLLMMessageService { export class SendLLMMessageService implements ISendLLMMessageService { - static readonly ID = 'void.contrib.browserSendLLMMessageService'; readonly _serviceBrand: undefined; - readonly channel: IChannel; + private readonly channel: IChannel; + + private readonly _disposablesOfRequestId: Record = {} constructor( @IMainProcessService mainProcessService: IMainProcessService // used as a renderer (only usable on client side) @@ -36,6 +38,15 @@ export class SendLLMMessageService implements ISendLLMMessageService { // const service = ProxyChannel.toService(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; @@ -43,25 +54,48 @@ export class SendLLMMessageService implements ISendLLMMessageService { // listen for listenerName='onText' | 'onFinalMessage' | 'onError', and call the original function on it const onTextEvent: Event = this.channel.listen('onText') - onTextEvent(e => { - if (requestId_ !== e.requestId) return; - onText(e) - }) + this._addDisposable(requestId_, + onTextEvent(e => { + if (requestId_ !== e.requestId) return; + onText(e) + }) + ) const onFinalMessageEvent: Event = this.channel.listen('onFinalMessage') - onFinalMessageEvent(e => { - if (requestId_ !== e.requestId) return; - onFinalMessage(e) - }) + this._addDisposable(requestId_, + onFinalMessageEvent(e => { + if (requestId_ !== e.requestId) return; + onFinalMessage(e) + this._dispose(requestId_) + }) + ) const onErrorEvent: Event = this.channel.listen('onError') - onErrorEvent(e => { - if (requestId_ !== e.requestId) return; - onError(e) - }) + this._addDisposable(requestId_, + onErrorEvent(e => { + if (requestId_ !== e.requestId) return; + 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) } } diff --git a/src/vs/platform/void/common/llmMessageTypes.ts b/src/vs/platform/void/common/llmMessageTypes.ts index a8172630..28733511 100644 --- a/src/vs/platform/void/common/llmMessageTypes.ts +++ b/src/vs/platform/void/common/llmMessageTypes.ts @@ -27,13 +27,26 @@ export type LLMMessageServiceParams = { messages: LLMMessage[]; voidConfig: VoidConfig | null; - abortRef: AbortRef; logging: { loggingName: string, }; } +export type SendLLMMMessageParams = { + onText: OnText; + onFinalMessage: OnFinalMessage; + onError: OnError; + + messages: LLMMessage[]; + voidConfig: VoidConfig | null; + + logging: { + loggingName: string, + }; + abortRef: AbortRef; +} + // can't send functions across a proxy, use listeners instead export const listenerNames = ['onText', 'onFinalMessage', 'onError'] as const export type ProxyLLMMessageParams = Omit & { requestId: string } @@ -41,3 +54,5 @@ export type ProxyLLMMessageParams = Omit[0] & { requestId: string } export type ProxyOnFinalMessagePayload = Parameters[0] & { requestId: string } export type ProxyOnErrorPayload = Parameters[0] & { requestId: string } + +export type ProxyLLMMessageAbortParams = { requestId: string } diff --git a/src/vs/platform/void/electron-main/LLMMessageChannel.ts b/src/vs/platform/void/electron-main/LLMMessageChannel.ts index 27700928..82c747ac 100644 --- a/src/vs/platform/void/electron-main/LLMMessageChannel.ts +++ b/src/vs/platform/void/electron-main/LLMMessageChannel.ts @@ -11,7 +11,7 @@ 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, LLMMessageServiceParams, ProxyLLMMessageParams } from '../common/llmMessageTypes.js'; +import { listenerNames, ProxyOnTextPayload, ProxyOnErrorPayload, ProxyOnFinalMessagePayload, ProxyLLMMessageParams, AbortRef, SendLLMMMessageParams, ProxyLLMMessageAbortParams } from '../common/llmMessageTypes.js'; // NODE IMPLEMENTATION OF SENDLLMMESSAGE - calls sendLLMMessage() and returns listeners @@ -25,11 +25,14 @@ export class LLMMessageChannel implements IServerChannel { private readonly _onError = new Emitter(); readonly onError = this._onError.event; + + private readonly _abortRefOfRequestId: Record = {} + + constructor() { } - // browser uses this + // browser uses this to listen for changes listen(_: unknown, event: typeof listenerNames[number]): Event { - console.log('event LISTENING!!!:', event) if (event === 'onText') { return this.onText; } @@ -44,23 +47,48 @@ export class LLMMessageChannel implements IServerChannel { } } - // both use this - async call(_: unknown, command: string, params: ProxyLLMMessageParams): Promise { - - if (command !== 'sendLLMMessage') throw new Error(`Invalid call in sendLLMMessage channel: ${command}.\nArgs:\n${JSON.stringify(params, null, 5)}`); + // browser uses this to call + async call(_: unknown, command: string, params: any): Promise { try { - const { requestId } = params; - const mainThreadParams: LLMMessageServiceParams = { - ...params, - onText: ({ newText, fullText }) => { this._onText.fire({ requestId, newText, fullText }); }, - onFinalMessage: ({ fullText }) => { this._onFinalMessage.fire({ requestId, fullText }); }, - onError: ({ error }) => { this._onError.fire({ requestId, error }); }, + if (command === 'sendLLMMessage') { + this._callSendLLMMessage(params) + } + else if (command === 'abort') { + this._callAbort(params) + } + else { + throw new Error(`Void sendLLM: command "${command}" not recognized.`) } - sendLLMMessage(mainThreadParams); } catch (e) { console.log('sendLLM channel: call error', e) } } + + 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] + } + + } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index 7457a447..e83d3c58 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -4,7 +4,7 @@ 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, } from '../../../../../../../platform/void/common/llmMessageTypes.js'; +import type { LLMMessage, OnText, OnError, OnFinalMessage, SendLLMMMessageParams, } from '../../../../../../../platform/void/common/llmMessageTypes.js'; import { LLMMessageServiceParams } from '../../../../../../../platform/void/common/llmMessageTypes.js'; type SendLLMMessageFnTypeInternal = (params: { @@ -284,7 +284,7 @@ export const sendLLMMessage = ({ abortRef: abortRef_, voidConfig, logging: { loggingName } -}: LLMMessageServiceParams) => { +}: SendLLMMMessageParams) => { if (!voidConfig) return; // trim message content (Anthropic and other providers give an error if there is trailing whitespace) diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index 1946cef2..d5e3baa3 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -761,7 +761,6 @@ Please finish writing the new file by applying the diff to the original file. Re resolve(); }, voidConfig, - abortRef, } this._sendLLMMessageService.sendLLMMessage(object) From f6f6259f23d844a72f79d93ca7d8fdb7f7bfb472 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 26 Nov 2024 01:55:42 -0800 Subject: [PATCH 10/12] fix error handling --- src/vs/platform/void/browser/llmMessageService.ts | 4 +++- .../void/electron-main/LLMMessageChannel.ts | 4 ++-- .../browser/react/src/sidebar-tsx/SidebarChat.tsx | 15 ++++++++------- .../void/browser/react/src/util/ErrorDisplay.tsx | 6 +++++- .../browser/react/src/util/sendLLMMessage.tsx | 6 +++--- .../contrib/void/browser/react/tsup.config.js | 2 +- .../contrib/void/browser/registerInlineDiffs.ts | 9 ++++++--- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/void/browser/llmMessageService.ts b/src/vs/platform/void/browser/llmMessageService.ts index 644c38ba..61f51ed8 100644 --- a/src/vs/platform/void/browser/llmMessageService.ts +++ b/src/vs/platform/void/browser/llmMessageService.ts @@ -19,7 +19,8 @@ export const ISendLLMMessageService = createDecorator('s // defines an interface that node/ creates and browser/ uses export interface ISendLLMMessageService { readonly _serviceBrand: undefined; - sendLLMMessage: (params: LLMMessageServiceParams) => void; + sendLLMMessage: (params: LLMMessageServiceParams) => string; + abort: (requestId: string) => void; } @@ -74,6 +75,7 @@ export class SendLLMMessageService implements ISendLLMMessageService { this._addDisposable(requestId_, onErrorEvent(e => { if (requestId_ !== e.requestId) return; + console.log('event onError', JSON.stringify(e)) onError(e) this._dispose(requestId_) }) diff --git a/src/vs/platform/void/electron-main/LLMMessageChannel.ts b/src/vs/platform/void/electron-main/LLMMessageChannel.ts index 82c747ac..9fb7224a 100644 --- a/src/vs/platform/void/electron-main/LLMMessageChannel.ts +++ b/src/vs/platform/void/electron-main/LLMMessageChannel.ts @@ -62,10 +62,11 @@ export class LLMMessageChannel implements IServerChannel { } } catch (e) { - console.log('sendLLM channel: call error', e) + console.log('llmMessageChannel: Call Error:', e) } } + // the only place sendLLMMessage is actually called private _callSendLLMMessage(params: ProxyLLMMessageParams) { const { requestId } = params; @@ -82,7 +83,6 @@ export class LLMMessageChannel implements IServerChannel { sendLLMMessage(mainThreadParams); } - private _callAbort(params: ProxyLLMMessageAbortParams) { const { requestId } = params; if (!(requestId in this._abortRefOfRequestId)) return diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 5b62674c..fd36dfa4 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -175,7 +175,7 @@ export const SidebarChat = () => { // state of chat const [messageStream, setMessageStream] = useState('') const [isLoading, setIsLoading] = useState(false) - const abortFnRef = useRef<(() => void) | null>(null) + const latestRequestIdRef = useRef(null) const [latestError, setLatestError] = useState(null) @@ -225,7 +225,7 @@ export const SidebarChat = () => { setIsLoading(false) }, onError: ({ error }) => { - console.log('chat: running 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 @@ -238,10 +238,10 @@ export const SidebarChat = () => { setLatestError(error) }, voidConfig, - abortRef: abortFnRef, } - sendLLMMessageService.sendLLMMessage(object) + const latestRequestId = sendLLMMessageService.sendLLMMessage(object) + latestRequestIdRef.current = latestRequestId setIsLoading(true) @@ -253,8 +253,9 @@ export const SidebarChat = () => { } 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)' @@ -342,7 +343,7 @@ export const SidebarChat = () => { {/* error message */} - {!latestError ? null : + {latestError === null ? null : { setLatestError(null) }} diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx index 8ff04ebf..b51545c2 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx @@ -28,9 +28,13 @@ const getErrorDetails = (error: unknown) => { e = new Error(String(error)) } + const message = e.message && e.error ? + (e.message + ':\n' + e.error) + : e.message || e.error || JSON.stringify(error) + details = { name: e.name || 'Error', - message: e.message || String(e), + message: message, stack: e.stack || null, cause: e.cause ? String(e.cause) : null, code: e.code || null, diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx index e83d3c58..128ffd08 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.tsx @@ -174,7 +174,7 @@ const sendOpenAIMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal onError({ error: 'Invalid API key.' }); } else { - onError(error); + onError({ error }); } }) @@ -206,7 +206,7 @@ export const sendOllamaMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, }) // when error/fail .catch(error => { - onError(error) + onError({ error }) }) }; @@ -266,7 +266,7 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin }) .catch(error => { - onError(error) + onError({ error }) }); } diff --git a/src/vs/workbench/contrib/void/browser/react/tsup.config.js b/src/vs/workbench/contrib/void/browser/react/tsup.config.js index 31bdc100..a3432000 100644 --- a/src/vs/workbench/contrib/void/browser/react/tsup.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tsup.config.js @@ -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' }), diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index d5e3baa3..60894207 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -731,9 +731,10 @@ Please finish writing the new file by applying the diff to the original file. Re // ${suffix} // `; - const abortRef = { current: null } as { current: null | (() => void) } await new Promise((resolve, reject) => { + let streamRequestId: string | null = null + const object: LLMMessageServiceParams = { logging: { loggingName: 'streamChunk' }, messages: [ @@ -756,14 +757,16 @@ 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, } - this._sendLLMMessageService.sendLLMMessage(object) + streamRequestId = this._sendLLMMessageService.sendLLMMessage(object) }) onFinishEdit() From 8ff4f1c6e19ddf6c4d9bb89b4fb32349d0fb29a0 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 26 Nov 2024 02:16:57 -0800 Subject: [PATCH 11/12] update error handling --- .../react/src/sidebar-tsx/SidebarChat.tsx | 2 +- .../browser/react/src/util/ErrorDisplay.tsx | 46 ++++++++++++------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index fd36dfa4..fb289f37 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -248,7 +248,7 @@ export const SidebarChat = () => { setInstructions(''); formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens threadsStateService.setStaging([]) // clear staging - setLatestError('') + setLatestError(null) } diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx index b51545c2..fee117cb 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx @@ -3,30 +3,43 @@ 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".` +// 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 +} // Get detailed error information const getErrorDetails = (error: unknown) => { - let details: { message: string, name: string, stack: string | null, cause: string | null, code: string | null, additional: Record }; + 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) && (error.cause + '').includes('TypeError: Failed to fetch')) { - e = error as any - e['Void Team'] = opaqueMessage - } - else if (error instanceof Error) { + if (error instanceof Error) { e = error } + // sometimes error is an object but not an Error + else if (typeof error === 'object') { + e = new Error('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) @@ -35,7 +48,7 @@ const getErrorDetails = (error: unknown) => { details = { name: e.name || 'Error', message: message, - stack: e.stack || null, + // stack: e.stack || null, // can we remove this? it looks awful and not sure stack is ever useful cause: e.cause ? String(e.cause) : null, code: e.code || null, additional: {} @@ -57,7 +70,7 @@ export const ErrorDisplay = ({ showDismiss = true, className = '' }: { - error: Error | string, + error: Error | object | string, onDismiss: (() => void) | null, showDismiss?: boolean, className?: string @@ -65,7 +78,7 @@ export const ErrorDisplay = ({ const [isExpanded, setIsExpanded] = useState(false); const details = getErrorDetails(error); - const hasDetails = details.stack || details.cause || Object.keys(details.additional).length > 0; + const hasDetails = details.cause || Object.keys(details.additional).length > 0; return (
@@ -132,15 +145,14 @@ export const ErrorDisplay = ({
)} - - {details.stack && ( + {/* {details.stack && (
Stack Trace:
 								{details.stack}
 							
- )} + )} */} )} From 9f27b17f11979e1240f665f090d45cb71f6da285 Mon Sep 17 00:00:00 2001 From: Andrew Pareles Date: Tue, 26 Nov 2024 02:22:05 -0800 Subject: [PATCH 12/12] ignore e.stack --- .../contrib/void/browser/react/src/util/ErrorDisplay.tsx | 4 ++-- src/vs/workbench/contrib/void/browser/registerActions.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx index fee117cb..8c34f427 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/ErrorDisplay.tsx @@ -15,7 +15,7 @@ import { getCmdKey } from '../../../getCmdKey.js'; type Details = { message: string, name: string, - // stack: string | null, + stack: string | null, cause: string | null, code: string | null, additional: Record @@ -48,7 +48,7 @@ const getErrorDetails = (error: unknown) => { details = { name: e.name || 'Error', message: message, - // stack: e.stack || null, // can we remove this? it looks awful and not sure stack is ever useful + 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: {} diff --git a/src/vs/workbench/contrib/void/browser/registerActions.ts b/src/vs/workbench/contrib/void/browser/registerActions.ts index 226bbf39..8e5cc561 100644 --- a/src/vs/workbench/contrib/void/browser/registerActions.ts +++ b/src/vs/workbench/contrib/void/browser/registerActions.ts @@ -147,7 +147,7 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor): Promise { const stateService = accessor.get(IVoidSidebarStateService) - stateService.setState({ isHistoryOpen: false, currentTab: 'settings' }) + stateService.setState({ isHistoryOpen: false, currentTab: stateService.state.currentTab === 'settings' ? 'chat' : 'settings' }) stateService.fireBlurChat() } })