diff --git a/web-app/src/containers/StreamingContent.tsx b/web-app/src/containers/StreamingContent.tsx index d391ff5a6..4592e2598 100644 --- a/web-app/src/containers/StreamingContent.tsx +++ b/web-app/src/containers/StreamingContent.tsx @@ -1,6 +1,7 @@ import { useAppState } from '@/hooks/useAppState' import { ThreadContent } from './ThreadContent' import { memo } from 'react' +import { useMessages } from '@/hooks/useMessages' type Props = { threadId: string @@ -9,10 +10,22 @@ type Props = { // Use memo with no dependencies to allow re-renders when props change export const StreamingContent = memo(({ threadId }: Props) => { const { streamingContent } = useAppState() + const { getMessages } = useMessages() + const messages = getMessages(threadId) if (!streamingContent || streamingContent.thread_id !== threadId) return null // Pass a new object to ThreadContent to avoid reference issues // The streaming content is always the last message - return + return ( + 0 + ? messages[messages.length - 1].role !== 'assistant' + : true + } + /> + ) }) diff --git a/web-app/src/containers/ThreadContent.tsx b/web-app/src/containers/ThreadContent.tsx index a4d9c608d..5562f5676 100644 --- a/web-app/src/containers/ThreadContent.tsx +++ b/web-app/src/containers/ThreadContent.tsx @@ -31,6 +31,7 @@ import { TooltipContent, TooltipTrigger, } from '@/components/ui/tooltip' +import { formatDate } from '@/utils/formatDate' const CopyButton = ({ text }: { text: string }) => { const [copied, setCopied] = useState(false) @@ -67,7 +68,13 @@ const CopyButton = ({ text }: { text: string }) => { // Use memo to prevent unnecessary re-renders, but allow re-renders when props change export const ThreadContent = memo( - (item: ThreadMessage & { isLastMessage?: boolean; index?: number }) => { + ( + item: ThreadMessage & { + isLastMessage?: boolean + index?: number + showAssistant?: boolean + } + ) => { const [message, setMessage] = useState(item.content?.[0]?.text?.value || '') // Use useMemo to stabilize the components prop @@ -135,6 +142,10 @@ export const ThreadContent = memo( Array.isArray(item.metadata.tool_calls) && item.metadata.tool_calls.length + const assistant = item.metadata?.assistant as + | { avatar?: React.ReactNode; name?: React.ReactNode } + | undefined + return ( {item.content?.[0]?.text && item.role === 'user' && ( @@ -221,6 +232,25 @@ export const ThreadContent = memo( )} {item.content?.[0]?.text && item.role !== 'user' && ( <> + {item.showAssistant && ( +
+
+ {assistant?.avatar || '👋'} +
+ +
+ + {assistant?.name || 'Jan'} + + {item?.created_at && ( + + {formatDate(item?.created_at)} + + )} +
+
+ )} + {reasoningSegment && ( ()((set) => ({ serverStatus: 'stopped', abortControllers: {}, tokenSpeed: undefined, - updateStreamingContent: (content) => { - set({ streamingContent: content }) + updateStreamingContent: (content: ThreadMessage | undefined) => { + set(() => ({ + streamingContent: content + ? { + ...content, + created_at: content.created_at || Date.now(), + metadata: { + ...content.metadata, + assistant: useAssistant.getState().currentAssistant, + }, + } + : undefined, + })) }, updateLoadingModel: (loading) => { set({ loadingModel: loading }) diff --git a/web-app/src/hooks/useMessages.ts b/web-app/src/hooks/useMessages.ts index 21d75f08b..4c38e73b7 100644 --- a/web-app/src/hooks/useMessages.ts +++ b/web-app/src/hooks/useMessages.ts @@ -6,6 +6,7 @@ import { createMessage, deleteMessage as deleteMessageExt, } from '@/services/messages' +import { useAssistant } from './useAssistant' type MessageState = { messages: Record @@ -31,7 +32,16 @@ export const useMessages = create()( })) }, addMessage: (message) => { - createMessage(message).then((createdMessage) => { + const currentAssistant = useAssistant.getState().currentAssistant + const newMessage = { + ...message, + created_at: message.created_at || Date.now(), + metadata: { + ...message.metadata, + assistant: currentAssistant, + }, + } + createMessage(newMessage).then((createdMessage) => { set((state) => ({ messages: { ...state.messages, diff --git a/web-app/src/routes/threads/$threadId.tsx b/web-app/src/routes/threads/$threadId.tsx index 31f64f143..07867031b 100644 --- a/web-app/src/routes/threads/$threadId.tsx +++ b/web-app/src/routes/threads/$threadId.tsx @@ -196,6 +196,11 @@ function ThreadDetail() { diff --git a/web-app/src/utils/formatDate.ts b/web-app/src/utils/formatDate.ts new file mode 100644 index 000000000..5f6547e07 --- /dev/null +++ b/web-app/src/utils/formatDate.ts @@ -0,0 +1,10 @@ +export const formatDate = (date: string | number | Date): string => { + return new Date(date).toLocaleString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + hour12: true, + }) +}