import { useState, useEffect } from "react"; import { FeedbackButton } from "@banorte/chat-ui"; import Markdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import { SSE } from "sse.js"; export { ChatMessage }; interface ChatMessageProps { isUser: boolean; content: string; event: CallableFunction; conversationId: string; withDeepResearch: boolean; setReceivingMsg: (receiving: boolean) => void; userAvatar: string; botAvatar: string; onFeedback?: (key: string, rating: string) => Promise; } function ChatMessage({ isUser, content, event, conversationId, withDeepResearch, setReceivingMsg, userAvatar, botAvatar, onFeedback, }: ChatMessageProps) { const [buff, setBuff] = useState(""); const [responseId, setResponseId] = useState(""); const [loading, setLoading] = useState(false); const [images, setImages] = useState([]); const [currentImageIndex, setCurrentImageIndex] = useState(0); const [acceptFeedback, setAcceptFeedback] = useState(false); const [streamIndex, setStreamIndex] = useState(0); const [fullResponse, setFullResponse] = useState(""); const [pendingReferences, setPendingReferences] = useState>([]); const [streamingComplete, setStreamingComplete] = useState(false); const nextImage = () => { if (currentImageIndex < images.length - 1) { setCurrentImageIndex((prev) => prev + 1); } }; const prevImage = () => { if (currentImageIndex > 0) { setCurrentImageIndex((prev) => prev - 1); } }; function setReferences(buff: string, references: Array) { const citations = buff.match(/\[(\d+)\]/g); let newText = buff; if (citations) { citations.forEach((citation) => { const citationNumber = parseInt(citation.replace(/\[|\]/g, "")) - 1; const reference = references[citationNumber]; const anchorTag = `${citation}`; newText = newText.replaceAll(citation, anchorTag); }); } return newText; } useEffect(() => { if (fullResponse && streamIndex < fullResponse.length) { setLoading(false); const timer = setTimeout(() => { setBuff((prev) => prev + fullResponse[streamIndex]); setStreamIndex((prev) => prev + 1); event(); }, 3); return () => clearTimeout(timer); } else if (fullResponse && streamIndex === fullResponse.length) { setReceivingMsg(false); setStreamingComplete(true); // Apply references after streaming is complete if (pendingReferences.length > 0) { const referencedText = setReferences(fullResponse, pendingReferences); setBuff(referencedText); setPendingReferences([]); } } }, [fullResponse, streamIndex, pendingReferences]); async function getStream() { const payload = JSON.stringify({ prompt: content, conversation_id: conversationId, with_deep_research: withDeepResearch, }); const url = "/api/v1/message?stream=True"; const eventSource = new SSE(url, { withCredentials: true, headers: { "Content-Type": "application/json" }, payload: payload, }); eventSource.onmessage = async (event) => { console.log(event.data); const ResponseChunk = JSON.parse(event.data); if (ResponseChunk["type"] === "text") { setFullResponse((prev) => prev + ResponseChunk["content"]); } else if (ResponseChunk["type"] === "reference") { setPendingReferences(ResponseChunk["content"]); } else if (ResponseChunk["type"] === "end") { setResponseId(ResponseChunk["content"]); eventSource.close(); } else if (ResponseChunk["type"] === "image") { const newImages = ResponseChunk.content.slice(0, 3); setImages((prev) => { const combinedImages = [...prev, ...newImages]; return combinedImages.slice(0, 3); }); } else if (ResponseChunk["type"] == "tool") { setAcceptFeedback(true); } else if (ResponseChunk["type"] === "error") { setFullResponse((prev) => prev + "\n\n" + ResponseChunk["content"]); eventSource.close(); } }; eventSource.onerror = async (e) => { console.log("error" + e); setReceivingMsg(false); setLoading(false); eventSource.close(); }; } useEffect(() => { if (!isUser) { setLoading(true); setReceivingMsg(true); getStream(); } else { setBuff(content); event(); } }, []); const ImageViewer = () => { if (images.length === 0) return null; return (
{`Generated
Imagen {currentImageIndex + 1} de {images.length}
); }; return ( <> {isUser ? (
user avatar icon
{loading && ( )} {buff}
) : (
bot avatar icon
{loading && ( <> {withDeepResearch ? (
Pensamiento profundo...
) : ( )} )} (

), h2: ({ ...props }) => (

), h3: ({ ...props }) => (

), p: ({ ...props }) =>

, ul: ({ ...props }) => (

{streamingComplete && acceptFeedback && onFeedback && ( )}
)} ); }