Skip to content

Commit

Permalink
feat: readded streaming api
Browse files Browse the repository at this point in the history
  • Loading branch information
Rob Ellison committed Mar 17, 2024
1 parent 2a6f3d5 commit 928479b
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 15 deletions.
20 changes: 15 additions & 5 deletions app/api/chat/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export async function POST(req) {
const MODEL_TEMPERATURE = parseInt(process.env.MODEL_TEMPERATURE);
const REDIS_HOST = process.env.REDIS_HOST;
//const jsonDelimiter = process.env.REACT_APP_CHAT_MESSAGE_DELIMITER;
const jsonDelimiter = '###%%^JSON-DELIMITER^%%###'; // to be updated to extract from env
// const jsonDelimiter = '###%%^JSON-DELIMITER^%%###'; // to be updated to extract from env
const jsonDelimiter = ',';
const SIMILARITY_THRESHOLD = process.env.SIMILARITY_THRESHOLD;

// WARNING: PLEASE DO NOT USE Langsmith when using production data
Expand Down Expand Up @@ -85,7 +86,7 @@ export async function POST(req) {

const questionPrompt = PromptTemplate.fromTemplate(
`Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know,\
don't try to make up an answer. Check if CONTEXT: is empty. If so, just say that "I'm sorry, no related information found", don't try to provide an answer.
don't try to make up an answer. Check if CONTEXT: is empty. If so, just say that "I'm sorry, I can't find any related information", don't try to provide an answer.
----------------
CHAT HISTORY: {chatHistory}
----------------
Expand Down Expand Up @@ -199,6 +200,8 @@ export async function POST(req) {
new ReadableStream({
async start(controller) {
try {
let jsonList = []; // Initialize an empty list to collect JSON objects

// Loop through the stream and push chunks to the client
for await (const chunk of stream) {
// Wrap each chunk in a JSON object before sending it
Expand All @@ -208,14 +211,21 @@ export async function POST(req) {
messageId: messageId,
role: 'bot'
});
controller.enqueue(jsonChunk + jsonDelimiter);
// jsonList.push(jsonChunk); // Add the JSON object to the list
controller.enqueue(jsonChunk + ','); // Enqueue each JSON object as soon as it's ready
}

// Send related content
for (const doc of updatedDocs) {
const jsonDoc = JSON.stringify(doc);
controller.enqueue(jsonDoc + jsonDelimiter);
// jsonList.push(jsonDoc); // Add the JSON object to the list
controller.enqueue(jsonDoc + ','); // Enqueue each JSON object as soon as it's ready

}


// Convert the list to a JSON string and send it
// controller.enqueue(JSON.stringify(jsonList));

// Signal the end of the stream
controller.close();

Expand Down
44 changes: 37 additions & 7 deletions components/chatbot/Chatbot.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import Button from "@mui/material/Button";
import SaveAltIcon from '@mui/icons-material/SaveAlt';


import Persona from "./Persona";
// import Avatar from '@mui/material/Avatar';
// import Chip from '@mui/material/Chip';
import Stack from '@mui/material/Stack';
import ToggleButton from '@mui/material/ToggleButton';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import Avatar from '@mui/material/Avatar';

export function Chatbot() {
const [messages, setMessages] = useState([]);
const messagesEndRef = useRef(null);
Expand All @@ -31,7 +39,8 @@ export function Chatbot() {
const topBarHeight = 65; // Adjust this to match the actual height of your TopBar
const endpoint = "/api/chat"; // Replace with your API endpoint
const [relevantDocs, setRelevantDocs] = useState([]);
const jsonDelimiter = '###%%^JSON-DELIMITER^%%###'; // should be same as that in route.js and to be updated to extract from env
// const jsonDelimiter = '###%%^JSON-DELIMITER^%%###'; // should be same as that in route.js and to be updated to extract from env
const jsonDelimiter = ',';
const [selectedBotMessageId, setSelectedBotMessageId] = useState(null);
const [openConfirmationDialog, setOpenConfirmationDialog] = useState(false);
const [showClearChatRect, setShowClearChatRect] = useState(false);
Expand Down Expand Up @@ -93,12 +102,20 @@ export function Chatbot() {
// Remove the delimiter from the end
jsonString = jsonString.slice(0, -jsonDelimiter.length);
}

const jsonObjects = jsonString.split(jsonDelimiter);

// console.log('jsonString:', jsonString);
// const jsonObjects = jsonString.split(jsonDelimiter);
// const jsonObjects = jsonString.split('}' + jsonDelimiter + '{');
const jsonObjects = JSON.parse('[' + jsonString + ']');
// console.log('jsonObjects:', jsonObjects);

// Check if jsonObjects is an array. If not, wrap it in an array.
// const jsonArray = Array.isArray(jsonObjects) ? jsonObjects : [jsonObjects];

jsonObjects.forEach(jsonObject => {

try {
const parsedObject = JSON.parse(jsonObject);
// const parsedObject = JSON.parse(jsonObject);
const parsedObject = jsonObject;
// Handling MessageStream type
if (parsedObject.type === 'MessageStream') {
const { content, messageId, role } = parsedObject;
Expand Down Expand Up @@ -189,6 +206,14 @@ export function Chatbot() {
const handleCancelClear = () => {
setOpenConfirmationDialog(false);
};

const [persona, setPersona] = React.useState('Jim');

const handleChange = (event, newPersona) => {
if (newPersona !== null) {
setPersona(newPersona);
}
};

return (
<Grid
Expand All @@ -204,7 +229,7 @@ export function Chatbot() {
xs={4}
sx={{ display: "flex", flexDirection: "column", height: "100%" }}
>
<Box sx={{ textAlign: "left", padding: 1, position: 'relative' }}>
{/* <Box sx={{ textAlign: "left", padding: 1, position: 'relative' }}>
<Box
onMouseEnter={() => setShowClearChatRect(true)}
onMouseLeave={() => setShowClearChatRect(false)}
Expand Down Expand Up @@ -262,7 +287,12 @@ export function Chatbot() {
borderRadius: "0 4px 4px 0", // Adjusted border radius
}}
/>
</Box>
</Box> */}
<Box sx={{ flexGrow: 1, padding: 2 }}>
<Persona/>

</Box>

<Box sx={{ overflowY: "auto", flexGrow: 1, padding: 2 }}>
{messages.map((msg, index) => (
<Message
Expand Down
57 changes: 57 additions & 0 deletions components/chatbot/Persona.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useState } from 'react';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import DeleteIcon from '@mui/icons-material/Delete';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import Tooltip from '@mui/material/Tooltip';

export default function Persona() {
const [selectedPersona, setSelectedPersona] = useState('jim');

const handleClearChat = () => {
// Implement the logic to clear the chat here
console.log('Chat cleared');
};

return (
<Box display="flex" alignItems="center" justifyContent="space-between" width="100%">
<Stack direction="row" spacing={2} alignItems="center">
<Button
variant={selectedPersona === 'jim' ? 'contained' : 'outlined'}
color="primary"
onClick={() => setSelectedPersona('jim')}
startIcon={<Avatar alt="Jim" src="/path-to-jim-avatar.jpg" />}
sx={{ textTransform: 'none'}}
>
Ask Jim
</Button>
<Button
variant={selectedPersona === 'abi' ? 'contained' : 'outlined'}
color="primary"
onClick={() => setSelectedPersona('abi')}
startIcon={<Avatar alt="Abi" src="/path-to-abi-avatar.jpg" />}
sx={{ textTransform: 'none'}}
>
Ask Abi
</Button>
{/* Uncomment and update path for ChatGPT avatar and persona selection as needed
<Button
variant={selectedPersona === 'chatgpt' ? 'contained' : 'outlined'}
color="primary"
onClick={() => setSelectedPersona('chatgpt')}
startIcon={<Avatar alt="ChatGPT" src="/path-to-chatgpt-avatar.jpg" />}
sx={{ textTransform: 'none'}}
>
Ask ChatGPT
</Button> */}
</Stack>
<Tooltip title="Clear Chat">
<IconButton onClick={handleClearChat}>
<DeleteIcon />
</IconButton>
</Tooltip>
</Box>
);
}
4 changes: 2 additions & 2 deletions components/chatbot/RelatedContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export default function RelatedContent({ relevantDocs, selectedBotMessageId }) {

// Filter relevantDocs based on selectedBotMessageId
const filteredDocs = relevantDocs.filter((doc) => doc.messageId === selectedBotMessageId);

console.log('filteredDocs:', filteredDocs);
return (
<div>
<h2>Related Documents of the Selected Message</h2>
<h2>Related Documents</h2>
{filteredDocs.length > 0 ? (
filteredDocs.map((doc, index) => (
<div key={doc.docId}>
Expand Down
2 changes: 1 addition & 1 deletion components/chatbot/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { Chatbot } from './Chatbot';
export { Chatbot } from './Chatbot';

0 comments on commit 928479b

Please sign in to comment.