mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-09-21 11:50:11 +02:00
chat history performance improvements with memo
This commit is contained in:
parent
d36c3ff8b2
commit
8f068b80d7
@ -23,9 +23,8 @@ const HistoricalMessage = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={uuid}
|
key={uuid}
|
||||||
className={`flex justify-center items-end w-full ${
|
className={`flex justify-center items-end w-full ${role === "user" ? USER_BACKGROUND_COLOR : AI_BACKGROUND_COLOR
|
||||||
role === "user" ? USER_BACKGROUND_COLOR : AI_BACKGROUND_COLOR
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col`}
|
className={`py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col`}
|
||||||
@ -92,4 +91,17 @@ function ProfileImage({ role, workspace }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(HistoricalMessage);
|
export default memo(
|
||||||
|
HistoricalMessage,
|
||||||
|
// Skip re-render the historical message:
|
||||||
|
// if the content is the exact same AND (not streaming)
|
||||||
|
// the lastMessage status is the same (regen icon)
|
||||||
|
// and the chatID matches between renders. (feedback icons)
|
||||||
|
(prevProps, nextProps) => {
|
||||||
|
return (
|
||||||
|
(prevProps.message === nextProps.message) &&
|
||||||
|
(prevProps.isLastMessage === nextProps.isLastMessage) &&
|
||||||
|
(prevProps.chatId === nextProps.chatId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
@ -12,20 +12,33 @@ import AvailableAgentsButton, {
|
|||||||
useAvailableAgents,
|
useAvailableAgents,
|
||||||
} from "./AgentMenu";
|
} from "./AgentMenu";
|
||||||
import TextSizeButton from "./TextSizeMenu";
|
import TextSizeButton from "./TextSizeMenu";
|
||||||
|
|
||||||
|
export const PROMPT_INPUT_EVENT = 'set_prompt_input';
|
||||||
export default function PromptInput({
|
export default function PromptInput({
|
||||||
message,
|
|
||||||
submit,
|
submit,
|
||||||
onChange,
|
onChange,
|
||||||
inputDisabled,
|
inputDisabled,
|
||||||
buttonDisabled,
|
buttonDisabled,
|
||||||
sendCommand,
|
sendCommand,
|
||||||
}) {
|
}) {
|
||||||
|
const [promptInput, setPromptInput] = useState('');
|
||||||
const { showAgents, setShowAgents } = useAvailableAgents();
|
const { showAgents, setShowAgents } = useAvailableAgents();
|
||||||
const { showSlashCommand, setShowSlashCommand } = useSlashCommands();
|
const { showSlashCommand, setShowSlashCommand } = useSlashCommands();
|
||||||
const formRef = useRef(null);
|
const formRef = useRef(null);
|
||||||
const textareaRef = useRef(null);
|
const textareaRef = useRef(null);
|
||||||
const [_, setFocused] = useState(false);
|
const [_, setFocused] = useState(false);
|
||||||
|
|
||||||
|
// To prevent too many re-renders we remotely listen for updates from the parent
|
||||||
|
// via an event cycle. Otherwise, using message as a prop leads to a re-render every
|
||||||
|
// change on the input.
|
||||||
|
function handlePromptUpdate(e) { setPromptInput(e?.detail ?? ''); }
|
||||||
|
useEffect(() => {
|
||||||
|
if (!!window) window.addEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate);
|
||||||
|
return () => (
|
||||||
|
window?.removeEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate)
|
||||||
|
)
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!inputDisabled && textareaRef.current) {
|
if (!inputDisabled && textareaRef.current) {
|
||||||
textareaRef.current.focus();
|
textareaRef.current.focus();
|
||||||
@ -102,6 +115,7 @@ export default function PromptInput({
|
|||||||
watchForSlash(e);
|
watchForSlash(e);
|
||||||
watchForAt(e);
|
watchForAt(e);
|
||||||
adjustTextArea(e);
|
adjustTextArea(e);
|
||||||
|
setPromptInput(e.target.value)
|
||||||
}}
|
}}
|
||||||
onKeyDown={captureEnter}
|
onKeyDown={captureEnter}
|
||||||
required={true}
|
required={true}
|
||||||
@ -111,7 +125,7 @@ export default function PromptInput({
|
|||||||
setFocused(false);
|
setFocused(false);
|
||||||
adjustTextArea(e);
|
adjustTextArea(e);
|
||||||
}}
|
}}
|
||||||
value={message}
|
value={promptInput}
|
||||||
className="cursor-text max-h-[100px] md:min-h-[40px] mx-2 md:mx-0 py-2 w-full text-[16px] md:text-md text-white bg-transparent placeholder:text-white/60 resize-none active:outline-none focus:outline-none flex-grow"
|
className="cursor-text max-h-[100px] md:min-h-[40px] mx-2 md:mx-0 py-2 w-full text-[16px] md:text-md text-white bg-transparent placeholder:text-white/60 resize-none active:outline-none focus:outline-none flex-grow"
|
||||||
placeholder={"Send a message"}
|
placeholder={"Send a message"}
|
||||||
/>
|
/>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import ChatHistory from "./ChatHistory";
|
import ChatHistory from "./ChatHistory";
|
||||||
import PromptInput from "./PromptInput";
|
import PromptInput, { PROMPT_INPUT_EVENT } from "./PromptInput";
|
||||||
import Workspace from "@/models/workspace";
|
import Workspace from "@/models/workspace";
|
||||||
import handleChat, { ABORT_STREAM_EVENT } from "@/utils/chat";
|
import handleChat, { ABORT_STREAM_EVENT } from "@/utils/chat";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
@ -20,10 +20,19 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
const [chatHistory, setChatHistory] = useState(knownHistory);
|
const [chatHistory, setChatHistory] = useState(knownHistory);
|
||||||
const [socketId, setSocketId] = useState(null);
|
const [socketId, setSocketId] = useState(null);
|
||||||
const [websocket, setWebsocket] = useState(null);
|
const [websocket, setWebsocket] = useState(null);
|
||||||
|
|
||||||
|
// Maintain state of message from whatever is in PromptInput
|
||||||
const handleMessageChange = (event) => {
|
const handleMessageChange = (event) => {
|
||||||
setMessage(event.target.value);
|
setMessage(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Emit an update to the sate of the prompt input without directly
|
||||||
|
// passing a prop in so that it does not re-render constantly.
|
||||||
|
function setMessageEmit(messageContent = '') {
|
||||||
|
setMessage(messageContent);
|
||||||
|
window.dispatchEvent(new CustomEvent(PROMPT_INPUT_EVENT, { detail: messageContent }))
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmit = async (event) => {
|
const handleSubmit = async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!message || message === "") return false;
|
if (!message || message === "") return false;
|
||||||
@ -41,14 +50,14 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
setChatHistory(prevChatHistory);
|
setChatHistory(prevChatHistory);
|
||||||
setMessage("");
|
setMessageEmit("");
|
||||||
setLoadingResponse(true);
|
setLoadingResponse(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendCommand = async (command, submit = false) => {
|
const sendCommand = async (command, submit = false) => {
|
||||||
if (!command || command === "") return false;
|
if (!command || command === "") return false;
|
||||||
if (!submit) {
|
if (!submit) {
|
||||||
setMessage(command);
|
setMessageEmit(command);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +74,7 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
setChatHistory(prevChatHistory);
|
setChatHistory(prevChatHistory);
|
||||||
setMessage("");
|
setMessageEmit("");
|
||||||
setLoadingResponse(true);
|
setLoadingResponse(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -208,7 +217,6 @@ export default function ChatContainer({ workspace, knownHistory = [] }) {
|
|||||||
sendCommand={sendCommand}
|
sendCommand={sendCommand}
|
||||||
/>
|
/>
|
||||||
<PromptInput
|
<PromptInput
|
||||||
message={message}
|
|
||||||
submit={handleSubmit}
|
submit={handleSubmit}
|
||||||
onChange={handleMessageChange}
|
onChange={handleMessageChange}
|
||||||
inputDisabled={loadingResponse}
|
inputDisabled={loadingResponse}
|
||||||
|
Loading…
Reference in New Issue
Block a user