Skip to content

Commit cc0052f

Browse files
committed
Decouple rendering logic from chat completion logic
* Only store string content in `ChatMessage` type * Move rendering function outside component to prevent unnecessary recreation * Update render function signature * Restore GPT-generated output warning for bot messages * Refactor render function logic * Fix React render warnings * Add TODOs for full Markdown/stories-like parsing Also uses non-greedy regex to match and split code blocks: * Only match JavaScript code blocks * Fix false matches * Supports multiple code blocks in a single message
1 parent b8cf72b commit cc0052f

File tree

1 file changed

+29
-32
lines changed

1 file changed

+29
-32
lines changed

src/pages/sicp/subcomponents/chatbot/ChatBox.tsx

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ type Props = {
1212
getText: () => string;
1313
};
1414

15-
type ChatMessage = { role: 'user' | 'bot'; content: string[] };
15+
type ChatMessage = { role: 'user' | 'bot'; content: string };
1616
const initialMessage: ChatMessage = {
17-
content: ['Ask me something about this paragraph!'],
17+
content: 'Ask me something about this paragraph!',
1818
role: 'bot'
1919
};
2020
const botErrorMessage: ChatMessage = {
21-
content: ['Sorry, I am down with a cold, please try again later.'],
21+
content: 'Sorry, I am down with a cold, please try again later.',
2222
role: 'bot'
2323
};
2424

@@ -37,18 +37,12 @@ const ChatBox: React.FC<Props> = ({ getSection, getText }) => {
3737
setUserInput(event.target.value);
3838
};
3939

40-
// TODO: Reimplement
41-
// // To get code snippets
42-
// const codeBlocks = (temp: string) => {
43-
// return temp.split('```');
44-
// };
45-
4640
const sendMessage = async () => {
4741
if (userInput.trim() === '') {
4842
return;
4943
}
5044
setUserInput('');
51-
setMessages(prev => [...prev, { role: 'user', content: [userInput] }]);
45+
setMessages(prev => [...prev, { role: 'user', content: userInput }]);
5246
};
5347

5448
// Watch whenever a new message comes in
@@ -72,16 +66,14 @@ const ChatBox: React.FC<Props> = ({ getSection, getText }) => {
7266
{ role: 'system', content: prompt },
7367
...messages.slice(-CONTEXT_SIZE).map(message => {
7468
const role = message.role === 'user' ? 'user' : 'assistant';
75-
const content = message.content.join('\n');
76-
const segment: Payload[number] = { role, content };
69+
const segment: Payload[number] = { role, content: message.content };
7770
return segment;
7871
})
7972
];
8073

8174
chat(tokens, payload)
8275
.then(text => {
83-
// TODO: + '\n\nThe answer is generated by GPT-4 and may not be correct.'
84-
setMessages(prev => [...prev, { role: 'bot', content: [text] }]);
76+
setMessages(prev => [...prev, { role: 'bot', content: text }]);
8577
})
8678
.catch(e => {
8779
setMessages(prev => [...prev, botErrorMessage]);
@@ -95,23 +87,6 @@ const ChatBox: React.FC<Props> = ({ getSection, getText }) => {
9587
setMessages([initialMessage]);
9688
};
9789

98-
const renderMessageContent = (message: string | string[]) => {
99-
if (!Array.isArray(message)) {
100-
return message;
101-
}
102-
103-
return message.map((block, index) =>
104-
// Assume that only javascript code snippets will appear
105-
block.substring(0, 10) === 'javascript' ? (
106-
<SyntaxHighlighter language="javascript" style={SourceTheme} key={index}>
107-
{block.substring(11, block.length)}
108-
</SyntaxHighlighter>
109-
) : (
110-
block
111-
)
112-
);
113-
};
114-
11590
const keyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
11691
if (event.key === 'Enter') {
11792
sendMessage();
@@ -135,7 +110,7 @@ const ChatBox: React.FC<Props> = ({ getSection, getText }) => {
135110
className={classes[`${message.role}`]}
136111
style={{ whiteSpace: 'pre-line' }}
137112
>
138-
{renderMessageContent(message.content)}
113+
{renderMessageContent(message)}
139114
</div>
140115
))}
141116
{isLoading && <p>loading...</p>}
@@ -160,4 +135,26 @@ const ChatBox: React.FC<Props> = ({ getSection, getText }) => {
160135
);
161136
};
162137

138+
const renderMessageContent = (message: ChatMessage) => {
139+
let contentToRender = message.content;
140+
if (message.role === 'bot') {
141+
contentToRender += '\n\nThe answer is generated by GPT-4 and may not be correct.';
142+
}
143+
// TODO: Parse full Markdown, make snippets runnable
144+
if (!contentToRender.includes('```javascript')) {
145+
return contentToRender;
146+
}
147+
const renderableRegex = /```javascript\n([\s\S]*?)\n```/g;
148+
const chunks = contentToRender.split(renderableRegex);
149+
return chunks.map((chunk, i) => {
150+
return renderableRegex.test(chunk) ? (
151+
<SyntaxHighlighter language="javascript" style={SourceTheme} key={i}>
152+
{chunk}
153+
</SyntaxHighlighter>
154+
) : (
155+
<React.Fragment key={i}>{chunk}</React.Fragment>
156+
);
157+
});
158+
};
159+
163160
export default ChatBox;

0 commit comments

Comments
 (0)