Chat Bot(Local Server)
Build the basic bot function
I think besides the standard format output and auto calenlar generation. I need to talk to my bot directly. So I went to Ollama API and check there is already a completion there.
Like I did before, I check the Request
format:
curl http://localhost:11434/api/chat -d '{
"model": "llama3",
"messages": [
{
"role": "user",
"content": "why is the sky blue?"
}
]
}'
(this is of course streaming, not the complete text, of course.)
and Response
format:
{
"model": "llama3",
"created_at": "2023-08-04T08:52:19.385406455-07:00",
"message": {
"role": "assistant",
"content": "The",
"images": null
},
"done": false
}
(there is a final response as well, for the stop recognization)
This helps me so much and saved a lot of my time! I even begin to think how I can change the code before.
Work with GPT and present the tsx code
Later, I directly use GPT to generate the code. Here is the idea of my prompt:
- Use the previous code as an example
- Let GPT know the new input(
Request
) and new output (Response
) look like - Give GPT my idea of function: able to integrate GPT on my Docusaurus page
- Present the real look like for input and output.
- Using "``" etc to separate the code, text and different parts.
These are from the Prompt Learning.
The generated code takes some time and here is the idea of its progress:
- In the
readStream
function, we decode the received block of data and split it into multiple lines by line breaks. - For each row, the JSON data deal with these:
- If the JSON object contains a
message
field, we append its contents toassistantMessage.content
. - If the JSON object contains the
done
field and the value istrue
, we treat it as the final response and output that response in the console.
- If the JSON object contains a
- When we update the
messages
array, we check if the last message is already a reply from the assistant. If so, we replace it with the updatedassistantMessage
; Otherwise, we addassistantMessage
as a new message. Finally, we display a full chat history on the page, including messages from the user and the assistant.
Add one more mark responseCompleted
for JSON respond:
- In the
readStream
function, we initializeresponseCompleted
tofalse
. - When we receive a JSON object with the
done
field and the value istrue
, we setresponseCompleted
totrue
, indicating that the complete response has been received. - If
responseCompleted
istrue
, we add the helper's complete message to themessages
array and stop reading further from the data stream. - If
responseCompleted
isfalse
, we continue to call thereadStream
function recursively to read more data until we receive a complete response. - At the end of the stream, if
responseCompleted
is stillfalse
, we add the helper's current message to the messages array.
At the last, he did a little modify:
- In the configuration of
axios.post
, we setresponseType
to 'text
' to receive the raw text data instead of the parsed JSON. - We split the response data into multiple lines by newlines and then process it line by line.
- For each row, we parse the JSON data. If the JSON object contains a
message
field, we append its contents to theassistantMessageContent
string. - If the JSON object contains the
done
field and the value istrue
, we add the complete helper message to themessages
array and return it.
Finally, I have the code for basic function I want:
Code
Create a folder(ChatComponents) under the path of src/components/
and create the .js file:
import React, { useState } from 'react';
import axios from 'axios';
import styles from './ChatComponent.module.css';
const ChatComponent = () => {
const [inputText, setInputText] = useState('');
const [messages, setMessages] = useState([]);
const handleInputChange = (event) => {
setInputText(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
const userMessage = {
role: 'user',
content: inputText,
};
setMessages((prevMessages) => [...prevMessages, userMessage]);
setInputText('');
try {
const response = await axios.post('http://localhost:11434/api/chat', {
model: 'gemma',
messages: [...messages, userMessage],
stream: true,
}, {
responseType: 'text',
});
let assistantMessageContent = '';
const lines = response.data.split('\n');
for (const line of lines) {
if (line.trim() === '') continue;
const data = JSON.parse(line);
if (data.message) {
assistantMessageContent += data.message.content;
}
if (data.done) {
setMessages((prevMessages) => [
...prevMessages,
{
role: 'assistant',
content: assistantMessageContent,
},
]);
return;
}
}
} catch (error) {
console.error('Error calling chat API:', error.message);
}
};
return (
<div className={styles.pageContainer}>
<div className={styles.chatContainer}>
<div className={styles.chatBox}>
<div className={styles.welcomeMessage}>Hello, Matthew!</div>
<div className={styles.messagesContainer}>
{messages.map((message, index) => (
<div key={index} className={styles.message}>
<strong>{message.role}:</strong> {message.content}
</div>
))}
</div>
<form onSubmit={handleSubmit} className={styles.inputForm}>
<input
type="text"
value={inputText}
onChange={handleInputChange}
placeholder="Enter your message"
className={styles.inputField}
/>
<button type="submit" className={styles.sendButton}>
Send
</button>
</form>
</div>
</div>
</div>
);
};
export default ChatComponent;
And then create the style(.css) of it, under the same path:
.pageContainer {
position: relative;
min-height: 100vh;
padding: 20px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.chatContainer {
position: absolute;
bottom: 20%;
left: 50%;
transform: translateX(-50%);
width: 50%;
}
.chatBox {
background-color: #f5f5f5;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.welcomeMessage {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.messagesContainer {
max-height: 400px;
overflow-y: auto;
margin-bottom: 10px;
}
.message {
margin-bottom: 10px;
}
.inputForm {
display: flex;
align-items: center;
}
.inputField {
flex: 1;
padding: 8px;
border: 1px solid #ccc;
border-radius: 20px;
margin-right: 10px;
width: 70%;
}
.sendButton {
padding: 8px 16px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
Finally, under the path of src/pages
, create a .jsx file and call the component:
import React from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import ChatComponent from '../components/ChatComponents/ChatComponent';
import Example from '../components/test_calendar/Example';
const MyPage = () => {
const { siteConfig } = useDocusaurusContext();
const { navbar } = siteConfig.themeConfig;
return (
<Layout title={siteConfig.title} description={siteConfig.tagline}>
<div style={{ position: 'relative', paddingTop: '20px' }}>
<div style={{ position: 'absolute', top: '20px', left: '20px' }}>
<img
src="img/final_project/anime_me.jpg"
alt="My Image"
style={{ width: '234px', height: '234px' }}
/>
<div style={{ marginTop: '20px' }}>
<Example />
</div>
</div>
</div>
{/* 其他页面内容 */}
<ChatComponent />
</Layout>
);
};
export default MyPage;
I did a little video about it, so interesting!
The UI actually is editting in the another page:operating_UI.