diff --git a/components/Composer.tsx b/components/Composer.tsx index 6d1fbe62c..36eb3b8c4 100644 --- a/components/Composer.tsx +++ b/components/Composer.tsx @@ -10,7 +10,7 @@ import TelegramIcon from '@mui/icons-material/Telegram'; import { useComposerStore } from '../utilities/store'; import { useSpeechRecognition } from '../utilities/speechRecognition'; -import { NoSSR } from './NoSSR'; +import { NoSSR } from './util/NoSSR'; /// Text template helpers diff --git a/components/ChatMessage.tsx b/components/Message.tsx similarity index 94% rename from components/ChatMessage.tsx rename to components/Message.tsx index 6e9500811..98632b4a9 100644 --- a/components/ChatMessage.tsx +++ b/components/Message.tsx @@ -22,7 +22,7 @@ import StopOutlinedIcon from '@mui/icons-material/StopOutlined'; import { Alert, Avatar, Box, Button, IconButton, ListDivider, ListItem, ListItemDecorator, Menu, MenuItem, Stack, Textarea, Tooltip, Typography, useTheme } from '@mui/joy'; import { SxProps, Theme } from '@mui/joy/styles/types'; -import { Link } from './Link'; +import { Link } from './util/Link'; // One message in the chat @@ -154,7 +154,7 @@ function RunnableCode({ codeBlock, theme }: { codeBlock: CodeMessageBlock, theme ); } -function ChatMessageCodeBlock({ codeBlock, theme, sx }: { codeBlock: CodeMessageBlock, theme: Theme, sx?: SxProps }) { +function CodeBlock({ codeBlock, theme, sx }: { codeBlock: CodeMessageBlock, theme: Theme, sx?: SxProps }) { const [showSandpack, setShowSandpack] = React.useState(false); const handleCopyToClipboard = () => @@ -183,15 +183,47 @@ function ChatMessageCodeBlock({ codeBlock, theme, sx }: { codeBlock: CodeMessage ; } -function prettyModel(model: string): string { +function prettyBaseModel(model: string): string { if (model.startsWith('gpt-4')) return 'gpt-4'; if (model.startsWith('gpt-3.5-turbo')) return '3.5-turbo'; return model; } +function explainErrorInMessage(message: UiMessage) { + let errorMessage: JSX.Element | null = null; + const isAssistantError = message.role === 'assistant' && (message.text.startsWith('Error: ') || message.text.startsWith('OpenAI API error: ')); + if (isAssistantError) { + if (message.text.startsWith('OpenAI API error: 429 Too Many Requests')) { + // TODO: retry at the api/chat level a few times instead of showing this error + errorMessage = <> + The model appears to be occupied at the moment. Kindly select GPT-3.5 Turbo via settings icon, + or give it another go by selecting Run again from the message menu. + ; + } else if (message.text.includes('"model_not_found"')) { + // note that "model_not_found" is different than "The model `gpt-xyz` does not exist" message + errorMessage = <> + Your API key appears to be unauthorized for {message.model || 'this model'}. You can change to GPT-3.5 Turbo via the settings + icon and simultaneously request + access to the desired model. + ; + } else if (message.text.includes('"context_length_exceeded"')) { + // TODO: propose to summarize or split the input? + const pattern: RegExp = /maximum context length is (\d+) tokens.+resulted in (\d+) tokens/; + const match = pattern.exec(message.text); + const usedText = match ? ` (${match[2]} tokens, max ${match[1]})` : ''; + errorMessage = <> + This thread surpasses the maximum size allowed for {message.model || 'this model'}{usedText}. + Please consider removing some earlier messages from the conversation, start a new conversation, + choose a model with larger context, or submit a shorter new message. + ; + } + } + return { errorMessage, isAssistantError }; +} + /** - * ChatMessage component is a customizable chat message UI component that supports + * The Message component is a customizable chat message UI component that supports * different roles (user, assistant, and system), text editing, syntax highlighting, * and code execution using Sandpack for TypeScript, JavaScript, and HTML code blocks. * The component also provides options for copying code to clipboard and expanding @@ -201,7 +233,7 @@ function prettyModel(model: string): string { * @param {Function} props.onDelete - The function to call when the delete button is clicked. * @param {Function} props.onEdit - The function to call when the edit button is clicked and the edited text is submitted. */ -export function ChatMessage(props: { uiMessage: UiMessage, onDelete: () => void, onEdit: (text: string) => void, onRunAgain: () => void }) { +export function Message(props: { uiMessage: UiMessage, onDelete: () => void, onEdit: (text: string) => void, onRunAgain: () => void }) { const theme = useTheme(); const message = props.uiMessage; @@ -253,34 +285,7 @@ export function ChatMessage(props: { uiMessage: UiMessage, onDelete: () => void, // soft error handling - let errorMessage: JSX.Element | null = null; - const isAssistantError = message.role === 'assistant' && (message.text.startsWith('Error: ') || message.text.startsWith('OpenAI API error: ')); - if (isAssistantError) { - if (message.text.startsWith('OpenAI API error: 429 Too Many Requests')) { - // TODO: retry at the api/chat level a few times instead of showing this error - errorMessage = <> - The model appears to be occupied at the moment. Kindly select GPT-3.5 Turbo via settings icon, - or give it another go by selecting Run again from the message menu. - ; - } else if (message.text.includes('"model_not_found"')) { - // note that "model_not_found" is different than "The model `gpt-xyz` does not exist" message - errorMessage = <> - Your API key appears to be unauthorized for {message.model || 'this model'}. You can change to GPT-3.5 Turbo via the settings - icon and simultaneously request - access to the desired model. - ; - } else if (message.text.includes('"context_length_exceeded"')) { - // TODO: propose to summarize or split the input? - const pattern: RegExp = /maximum context length is (\d+) tokens.+resulted in (\d+) tokens/; - const match = pattern.exec(message.text); - const usedText = match ? ` (${match[2]} tokens, max ${match[1]})` : ''; - errorMessage = <> - This thread surpasses the maximum size allowed for {message.model || 'this model'}{usedText}. - Please consider removing some earlier messages from the conversation, start a new conversation, - choose a model with larger context, or submit a shorter new message. - ; - } - } + const { isAssistantError, errorMessage } = explainErrorInMessage(message); // theming @@ -342,10 +347,12 @@ export function ChatMessage(props: { uiMessage: UiMessage, onDelete: () => void, : )} - {message.role === 'system' && (system)} + {message.role === 'system' && ( + system + )} {message.role === 'assistant' && ( - {prettyModel(message.model)} + {prettyBaseModel(message.model)} )} @@ -388,7 +395,7 @@ export function ChatMessage(props: { uiMessage: UiMessage, onDelete: () => void, {parseAndHighlightCodeBlocks(collapsedText).map((part, index) => part.type === 'code' ? ( - + ) : ( diff --git a/components/Settings.tsx b/components/Settings.tsx index 9c8311409..5b3079b84 100644 --- a/components/Settings.tsx +++ b/components/Settings.tsx @@ -4,8 +4,8 @@ import { shallow } from 'zustand/shallow'; import { Box, Button, Input, Modal, ModalClose, ModalDialog, Option, Select, Typography } from '@mui/joy'; import { GptChatModelId, GptChatModels, useSettingsStore } from '../utilities/store'; -import { Link } from './Link'; -import { NoSSR } from './NoSSR'; +import { Link } from './util/Link'; +import { NoSSR } from './util/NoSSR'; export const isValidOpenAIApiKey = (apiKey?: string) => diff --git a/components/Link.tsx b/components/util/Link.tsx similarity index 100% rename from components/Link.tsx rename to components/util/Link.tsx diff --git a/components/NoSSR.tsx b/components/util/NoSSR.tsx similarity index 100% rename from components/NoSSR.tsx rename to components/util/NoSSR.tsx diff --git a/pages/api/chat.ts b/pages/api/chat.ts index 810c4509d..d9ed9b755 100644 --- a/pages/api/chat.ts +++ b/pages/api/chat.ts @@ -9,7 +9,7 @@ if (!process.env.OPENAI_API_KEY) // definition for OpenAI wire types -export interface ChatMessage { +interface ChatMessage { role: 'assistant' | 'system' | 'user'; content: string; } diff --git a/pages/index.tsx b/pages/index.tsx index 6af3ac838..e56f08934 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -8,10 +8,10 @@ import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; import SmartToyTwoToneIcon from '@mui/icons-material/SmartToyTwoTone'; import { ChatApiInput } from './api/chat'; -import { ChatMessage, UiMessage } from '../components/ChatMessage'; import { Composer } from '../components/Composer'; import { GptChatModels, SystemPurposeId, SystemPurposes, useSettingsStore } from '../utilities/store'; -import { NoSSR } from '../components/NoSSR'; +import { Message, UiMessage } from '../components/Message'; +import { NoSSR } from '../components/util/NoSSR'; import { isValidOpenAIApiKey, Settings } from '../components/Settings'; @@ -255,10 +255,10 @@ export default function Conversation() { <> {messages.map(message => - handleListDelete(message.uid)} - onEdit={newText => handleListEdit(message.uid, newText)} - onRunAgain={() => handleListRunAgain(message.uid)} />)} + handleListDelete(message.uid)} + onEdit={newText => handleListEdit(message.uid, newText)} + onRunAgain={() => handleListRunAgain(message.uid)} />)}
diff --git a/tsconfig.json b/tsconfig.json index 86f9cc8c1..0f7aeb965 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ES6", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true,