1- import React , { useEffect , useRef , useState } from "react" ;
1+ import { useEffect , useRef , useState , useMemo , useCallback } from "react" ;
22import HistoricalMessage from "./HistoricalMessage" ;
33import PromptReply from "./PromptReply" ;
4+ import StatusResponse from "./StatusResponse" ;
45import { useManageWorkspaceModal } from "../../../Modals/ManageWorkspace" ;
56import ManageWorkspace from "../../../Modals/ManageWorkspace" ;
67import { ArrowDown } from "@phosphor-icons/react" ;
@@ -12,6 +13,7 @@ import { useParams } from "react-router-dom";
1213import paths from "@/utils/paths" ;
1314import Appearance from "@/models/appearance" ;
1415import useTextSize from "@/hooks/useTextSize" ;
16+ import { v4 } from "uuid" ;
1517
1618export default function ChatHistory ( {
1719 history = [ ] ,
@@ -136,6 +138,42 @@ export default function ChatHistory({
136138 ) ;
137139 } ;
138140
141+ const compiledHistory = useMemo (
142+ ( ) =>
143+ buildMessages ( {
144+ workspace,
145+ history,
146+ regenerateAssistantMessage,
147+ saveEditedMessage,
148+ forkThread,
149+ } ) ,
150+ [
151+ workspace ,
152+ history ,
153+ regenerateAssistantMessage ,
154+ saveEditedMessage ,
155+ forkThread ,
156+ ]
157+ ) ;
158+ const lastMessageInfo = useMemo ( ( ) => getLastMessageInfo ( history ) , [ history ] ) ;
159+ const renderStatusResponse = useCallback (
160+ ( item , index ) => {
161+ const hasSubsequentMessages = index < compiledHistory . length - 1 ;
162+ return (
163+ < StatusResponse
164+ key = { `status-group-${ index } ` }
165+ messages = { item }
166+ isThinking = { ! hasSubsequentMessages && lastMessageInfo . isAnimating }
167+ showCheckmark = {
168+ hasSubsequentMessages ||
169+ ( ! lastMessageInfo . isAnimating && ! lastMessageInfo . isStatusResponse )
170+ }
171+ />
172+ ) ;
173+ } ,
174+ [ compiledHistory . length , lastMessageInfo ]
175+ ) ;
176+
139177 if ( history . length === 0 && ! hasAttachments ) {
140178 return (
141179 < div className = "flex flex-col h-full md:mt-0 pb-44 md:pb-40 w-full justify-end items-center" >
@@ -176,61 +214,14 @@ export default function ChatHistory({
176214
177215 return (
178216 < div
179- className = { `markdown text-white/80 light:text-theme-text-primary font-light ${ textSizeClass } h-full md:h-[83%] pb-[100px] pt-6 md:pt-0 md:pb-20 md:mx-0 overflow-y-scroll flex flex-col justify-start ${
180- showScrollbar ? "show-scrollbar" : "no-scroll"
181- } `}
217+ className = { `markdown text-white/80 light:text-theme-text-primary font-light ${ textSizeClass } h-full md:h-[83%] pb-[100px] pt-6 md:pt-0 md:pb-20 md:mx-0 overflow-y-scroll flex flex-col justify-start ${ showScrollbar ? "show-scrollbar" : "no-scroll" } ` }
182218 id = "chat-history"
183219 ref = { chatHistoryRef }
184220 onScroll = { handleScroll }
185221 >
186- { history . map ( ( props , index ) => {
187- const isLastBotReply =
188- index === history . length - 1 && props . role === "assistant" ;
189-
190- if ( props ?. type === "statusResponse" && ! ! props . content ) {
191- return < StatusResponse key = { props . uuid } props = { props } /> ;
192- }
193-
194- if ( props . type === "rechartVisualize" && ! ! props . content ) {
195- return (
196- < Chartable key = { props . uuid } workspace = { workspace } props = { props } />
197- ) ;
198- }
199-
200- if ( isLastBotReply && props . animate ) {
201- return (
202- < PromptReply
203- key = { props . uuid }
204- uuid = { props . uuid }
205- reply = { props . content }
206- pending = { props . pending }
207- sources = { props . sources }
208- error = { props . error }
209- workspace = { workspace }
210- closed = { props . closed }
211- />
212- ) ;
213- }
214-
215- return (
216- < HistoricalMessage
217- key = { index }
218- message = { props . content }
219- role = { props . role }
220- workspace = { workspace }
221- sources = { props . sources }
222- feedbackScore = { props . feedbackScore }
223- chatId = { props . chatId }
224- error = { props . error }
225- attachments = { props . attachments }
226- regenerateMessage = { regenerateAssistantMessage }
227- isLastMessage = { isLastBotReply }
228- saveEditedMessage = { saveEditedMessage }
229- forkThread = { forkThread }
230- metrics = { props . metrics }
231- />
232- ) ;
233- } ) }
222+ { compiledHistory . map ( ( item , index ) =>
223+ Array . isArray ( item ) ? renderStatusResponse ( item , index ) : item
224+ ) }
234225 { showing && (
235226 < ManageWorkspace hideModal = { hideModal } providedSlug = { workspace . slug } />
236227 ) }
@@ -253,21 +244,13 @@ export default function ChatHistory({
253244 ) ;
254245}
255246
256- function StatusResponse ( { props } ) {
257- return (
258- < div className = "flex justify-center items-end w-full" >
259- < div className = "py-2 px-4 w-full flex gap-x-5 md:max-w-[80%] flex-col" >
260- < div className = "flex gap-x-5" >
261- < span
262- className = { `text-xs inline-block p-2 rounded-lg text-white/60 font-mono whitespace-pre-line` }
263- >
264- { props . content }
265- </ span >
266- </ div >
267- </ div >
268- </ div >
269- ) ;
270- }
247+ const getLastMessageInfo = ( history ) => {
248+ const lastMessage = history ?. [ history . length - 1 ] || { } ;
249+ return {
250+ isAnimating : lastMessage ?. animate ,
251+ isStatusResponse : lastMessage ?. type === "statusResponse" ,
252+ } ;
253+ } ;
271254
272255function WorkspaceChatSuggestions ( { suggestions = [ ] , sendSuggestion } ) {
273256 if ( suggestions . length === 0 ) return null ;
@@ -286,3 +269,78 @@ function WorkspaceChatSuggestions({ suggestions = [], sendSuggestion }) {
286269 </ div >
287270 ) ;
288271}
272+
273+ /**
274+ * Builds the history of messages for the chat.
275+ * This is mostly useful for rendering the history in a way that is easy to understand.
276+ * as well as compensating for agent thinking and other messages that are not part of the history, but
277+ * are still part of the chat.
278+ *
279+ * @param {Object } param0 - The parameters for building the messages.
280+ * @param {Array } param0.history - The history of messages.
281+ * @param {Object } param0.workspace - The workspace object.
282+ * @param {Function } param0.regenerateAssistantMessage - The function to regenerate the assistant message.
283+ * @param {Function } param0.saveEditedMessage - The function to save the edited message.
284+ * @param {Function } param0.forkThread - The function to fork the thread.
285+ * @returns {Array } The compiled history of messages.
286+ */
287+ function buildMessages ( {
288+ history,
289+ workspace,
290+ regenerateAssistantMessage,
291+ saveEditedMessage,
292+ forkThread,
293+ } ) {
294+ return history . reduce ( ( acc , props , index ) => {
295+ const isLastBotReply =
296+ index === history . length - 1 && props . role === "assistant" ;
297+
298+ if ( props ?. type === "statusResponse" && ! ! props . content ) {
299+ if ( acc . length > 0 && Array . isArray ( acc [ acc . length - 1 ] ) ) {
300+ acc [ acc . length - 1 ] . push ( props ) ;
301+ } else {
302+ acc . push ( [ props ] ) ;
303+ }
304+ return acc ;
305+ }
306+
307+ if ( props . type === "rechartVisualize" && ! ! props . content ) {
308+ acc . push (
309+ < Chartable key = { props . uuid } workspace = { workspace } props = { props } />
310+ ) ;
311+ } else if ( isLastBotReply && props . animate ) {
312+ acc . push (
313+ < PromptReply
314+ key = { props . uuid || v4 ( ) }
315+ uuid = { props . uuid }
316+ reply = { props . content }
317+ pending = { props . pending }
318+ sources = { props . sources }
319+ error = { props . error }
320+ workspace = { workspace }
321+ closed = { props . closed }
322+ />
323+ ) ;
324+ } else {
325+ acc . push (
326+ < HistoricalMessage
327+ key = { index }
328+ message = { props . content }
329+ role = { props . role }
330+ workspace = { workspace }
331+ sources = { props . sources }
332+ feedbackScore = { props . feedbackScore }
333+ chatId = { props . chatId }
334+ error = { props . error }
335+ attachments = { props . attachments }
336+ regenerateMessage = { regenerateAssistantMessage }
337+ isLastMessage = { isLastBotReply }
338+ saveEditedMessage = { saveEditedMessage }
339+ forkThread = { forkThread }
340+ metrics = { props . metrics }
341+ />
342+ ) ;
343+ }
344+ return acc ;
345+ } , [ ] ) ;
346+ }
0 commit comments