improved error handling

This commit is contained in:
Andrew Pareles 2024-11-23 16:05:26 -08:00
parent 51280d4e77
commit 1d5fef43c1
4 changed files with 68 additions and 38 deletions

View file

@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Glass Devtools, Inc. All rights reserved.
* Void Editor additions licensed under the AGPLv3 License.
*--------------------------------------------------------------------------------------------*/
import { OperatingSystem, OS } from '../../../../base/common/platform.js';
export function getCmdKey(): string {
if (OS === OperatingSystem.Macintosh) {
return '⌘';
} else {
return 'Ctrl';
}
}

View file

@ -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<string | null> => {
@ -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<Error | string | null>(null)
@ -338,9 +339,11 @@ export const SidebarChat = () => {
</div>
{/* error message */}
{!latestError ? null : <div>
{latestError}
</div>}
{!latestError ? null :
<ErrorDisplay
error={latestError}
onDismiss={() => { setLatestError(null) }}
/>}
</div>
</>
}

View file

@ -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<string, any> };
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 && (
<div>
<span className="font-semibold text-red-800">Additional Information:</span>
<pre className="mt-1 text-sm text-red-700 overflow-x-auto">
{JSON.stringify(details.additional, null, 2)}
<pre className="mt-1 text-sm text-red-700 overflow-x-auto whitespace-pre-wrap">
{Object.keys(details.additional).map(key => `${key}:\n${details.additional[key]}`).join('\n')}
</pre>
</div>
)}
@ -124,5 +142,3 @@ const ErrorDisplay = ({
</div>
);
};
export default ErrorDisplay;

View file

@ -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
}