From 928479bce98f8af47df63d92c24e6058771b49e6 Mon Sep 17 00:00:00 2001 From: Rob Ellison Date: Sun, 17 Mar 2024 20:17:56 +0000 Subject: [PATCH] feat: readded streaming api --- app/api/chat/route.js | 20 +++++++--- components/chatbot/Chatbot.js | 44 +++++++++++++++++---- components/chatbot/Persona.js | 57 ++++++++++++++++++++++++++++ components/chatbot/RelatedContent.js | 4 +- components/chatbot/index.js | 2 +- 5 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 components/chatbot/Persona.js diff --git a/app/api/chat/route.js b/app/api/chat/route.js index 931676c1..61b9d21b 100644 --- a/app/api/chat/route.js +++ b/app/api/chat/route.js @@ -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 @@ -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} ---------------- @@ -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 @@ -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(); diff --git a/components/chatbot/Chatbot.js b/components/chatbot/Chatbot.js index 342eba95..b09932e5 100644 --- a/components/chatbot/Chatbot.js +++ b/components/chatbot/Chatbot.js @@ -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); @@ -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); @@ -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; @@ -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 ( - + {/* setShowClearChatRect(true)} onMouseLeave={() => setShowClearChatRect(false)} @@ -262,7 +287,12 @@ export function Chatbot() { borderRadius: "0 4px 4px 0", // Adjusted border radius }} /> - + */} + + + + + {messages.map((msg, index) => ( { + // Implement the logic to clear the chat here + console.log('Chat cleared'); + }; + + return ( + + + + + {/* Uncomment and update path for ChatGPT avatar and persona selection as needed + */} + + + + + + + + ); +} diff --git a/components/chatbot/RelatedContent.js b/components/chatbot/RelatedContent.js index 1f78dc6b..79698198 100644 --- a/components/chatbot/RelatedContent.js +++ b/components/chatbot/RelatedContent.js @@ -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 (
-

Related Documents of the Selected Message

+

Related Documents

{filteredDocs.length > 0 ? ( filteredDocs.map((doc, index) => (
diff --git a/components/chatbot/index.js b/components/chatbot/index.js index 68edd337..78dd8de6 100644 --- a/components/chatbot/index.js +++ b/components/chatbot/index.js @@ -1 +1 @@ -export { Chatbot } from './Chatbot'; \ No newline at end of file +export { Chatbot } from './Chatbot';