add react-hot-toast and surface OpenAI API errors to users (#328)
This commit is contained in:
parent
23ad285a4b
commit
b7b6bbaaca
|
@ -17,6 +17,7 @@
|
|||
"openai": "^3.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hot-toast": "^2.4.0",
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-markdown": "^8.0.5",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
|
@ -3455,6 +3456,14 @@
|
|||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.12.tgz",
|
||||
"integrity": "sha512-yXHAvO08FU1JgTXX6Zn6sYCUFfB/OJSX8HHjDSgerZHZmFKAb08cykp5LBw5QnmyMcZyPRMqkdyHUSSzge788Q==",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
|
@ -6394,6 +6403,21 @@
|
|||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.0.tgz",
|
||||
"integrity": "sha512-qnnVbXropKuwUpriVVosgo8QrB+IaPJCpL8oBI6Ov84uvHZ5QQcTp2qg6ku2wNfgJl6rlQXJIQU5q+5lmPOutA==",
|
||||
"dependencies": {
|
||||
"goober": "^2.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "12.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.2.0.tgz",
|
||||
|
@ -10613,6 +10637,12 @@
|
|||
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
|
||||
"dev": true
|
||||
},
|
||||
"goober": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.12.tgz",
|
||||
"integrity": "sha512-yXHAvO08FU1JgTXX6Zn6sYCUFfB/OJSX8HHjDSgerZHZmFKAb08cykp5LBw5QnmyMcZyPRMqkdyHUSSzge788Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
|
@ -12517,6 +12547,14 @@
|
|||
"scheduler": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"react-hot-toast": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.0.tgz",
|
||||
"integrity": "sha512-qnnVbXropKuwUpriVVosgo8QrB+IaPJCpL8oBI6Ov84uvHZ5QQcTp2qg6ku2wNfgJl6rlQXJIQU5q+5lmPOutA==",
|
||||
"requires": {
|
||||
"goober": "^2.1.10"
|
||||
}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "12.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.2.0.tgz",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"openai": "^3.2.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hot-toast": "^2.4.0",
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-markdown": "^8.0.5",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
|
|
|
@ -2,12 +2,14 @@ import '@/styles/globals.css';
|
|||
import { appWithTranslation } from 'next-i18next';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
function App({ Component, pageProps }: AppProps<{}>) {
|
||||
return (
|
||||
<main className={inter.className}>
|
||||
<Toaster />
|
||||
<Component {...pageProps} />
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ChatBody, Message } from '@/types/chat';
|
||||
import { DEFAULT_SYSTEM_PROMPT } from '@/utils/app/const';
|
||||
import { OpenAIStream } from '@/utils/server';
|
||||
import { OpenAIError, OpenAIStream } from '@/utils/server';
|
||||
import tiktokenModel from '@dqbd/tiktoken/encoders/cl100k_base.json';
|
||||
import { init, Tiktoken } from '@dqbd/tiktoken/lite/init';
|
||||
// @ts-expect-error
|
||||
|
@ -49,8 +49,12 @@ const handler = async (req: Request): Promise<Response> => {
|
|||
return new Response(stream);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error instanceof OpenAIError) {
|
||||
return new Response('Error', { status: 500, statusText: error.message });
|
||||
} else {
|
||||
return new Response('Error', { status: 500 });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default handler;
|
||||
|
|
|
@ -34,6 +34,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
|||
import Head from 'next/head';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
interface HomeProps {
|
||||
serverSideApiKeyIsSet: boolean;
|
||||
|
@ -120,6 +121,7 @@ const Home: React.FC<HomeProps> = ({
|
|||
if (!response.ok) {
|
||||
setLoading(false);
|
||||
setMessageIsStreaming(false);
|
||||
toast.error(response.statusText);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,20 @@ import {
|
|||
} from 'eventsource-parser';
|
||||
import { OPENAI_API_HOST } from '../app/const';
|
||||
|
||||
export class OpenAIError extends Error {
|
||||
type: string;
|
||||
param: string;
|
||||
code: string;
|
||||
|
||||
constructor(message: string, type: string, param: string, code: string) {
|
||||
super(message);
|
||||
this.name = 'OpenAIError';
|
||||
this.type = type;
|
||||
this.param = param;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
export const OpenAIStream = async (
|
||||
model: OpenAIModel,
|
||||
systemPrompt: string,
|
||||
|
@ -41,9 +55,21 @@ export const OpenAIStream = async (
|
|||
const decoder = new TextDecoder();
|
||||
|
||||
if (res.status !== 200) {
|
||||
const statusText = res.statusText;
|
||||
const result = await res.body?.getReader().read();
|
||||
throw new Error(`OpenAI API returned an error: ${decoder.decode(result?.value) || statusText}`);
|
||||
const result = await res.json();
|
||||
if (result.error) {
|
||||
throw new OpenAIError(
|
||||
result.error.message,
|
||||
result.error.type,
|
||||
result.error.param,
|
||||
result.error.code,
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`OpenAI API returned an error: ${
|
||||
decoder.decode(result?.value) || result.statusText
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const stream = new ReadableStream({
|
||||
|
|
Loading…
Reference in New Issue