(null);
const { connectorSourceItems, isLoading: isLoadingConnectors } = useSearchSourceConnectors();
@@ -476,43 +474,10 @@ const ChatPage = () => {
updateChat();
}, [messages, status, chat_id, researchMode, selectedConnectors, search_space_id]);
- // Memoize connector sources to prevent excessive re-renders
- const processedConnectorSources = React.useMemo(() => {
- if (messages.length === 0) return connectorSources;
-
- // Only process when we have a complete message (not streaming)
- if (status !== 'ready') return connectorSources;
-
- // Find the latest assistant message
- const assistantMessages = messages.filter(msg => msg.role === 'assistant');
- if (assistantMessages.length === 0) return connectorSources;
-
- const latestAssistantMessage = assistantMessages[assistantMessages.length - 1];
- if (!latestAssistantMessage?.annotations) return connectorSources;
-
- // Find the latest SOURCES annotation
- const annotations = latestAssistantMessage.annotations as any[];
- const sourcesAnnotations = annotations.filter(a => a.type === 'SOURCES');
-
- if (sourcesAnnotations.length === 0) return connectorSources;
-
- const latestSourcesAnnotation = sourcesAnnotations[sourcesAnnotations.length - 1];
- if (!latestSourcesAnnotation.content) return connectorSources;
-
- // Use this content if it differs from current
- return latestSourcesAnnotation.content;
- }, [messages, status, connectorSources]);
-
- // Update connector sources when processed value changes
- useEffect(() => {
- if (processedConnectorSources !== connectorSources) {
- setConnectorSources(processedConnectorSources);
- }
- }, [processedConnectorSources, connectorSources]);
-
// Check and scroll terminal when terminal info is available
useEffect(() => {
- if (messages.length === 0 || status !== 'ready') return;
+ // Modified to trigger during streaming as well (removed status check)
+ if (messages.length === 0) return;
// Find the latest assistant message
const assistantMessages = messages.filter(msg => msg.role === 'assistant');
@@ -526,10 +491,27 @@ const ChatPage = () => {
const terminalInfoAnnotations = annotations.filter(a => a.type === 'TERMINAL_INFO');
if (terminalInfoAnnotations.length > 0) {
- // Schedule scrolling after the DOM has been updated
- setTimeout(scrollTerminalToBottom, 100);
+ // Always scroll to bottom when terminal info is updated, even during streaming
+ scrollTerminalToBottom();
}
- }, [messages, status]);
+ }, [messages]); // Removed status from dependencies to ensure it triggers during streaming
+
+ // Pure function to get connector sources for a specific message
+ const getMessageConnectorSources = (message: any): any[] => {
+ if (!message || message.role !== 'assistant' || !message.annotations) return [];
+
+ // Find all SOURCES annotations
+ const annotations = message.annotations as any[];
+ const sourcesAnnotations = annotations.filter(a => a.type === 'SOURCES');
+
+ // Get the latest SOURCES annotation
+ if (sourcesAnnotations.length === 0) return [];
+ const latestSourcesAnnotation = sourcesAnnotations[sourcesAnnotations.length - 1];
+
+ if (!latestSourcesAnnotation.content) return [];
+
+ return latestSourcesAnnotation.content;
+ };
// Custom handleSubmit function to include selected connectors and answer type
const handleSubmit = (e: React.FormEvent) => {
@@ -561,17 +543,12 @@ const ChatPage = () => {
scrollToBottom();
}, [messages]);
- // Set activeTab when connectorSources change using a memoized value
- const activeTabValue = React.useMemo(() => {
- return connectorSources.length > 0 ? connectorSources[0].type : "";
- }, [connectorSources]);
-
- // Update activeTab when the memoized value changes
+ // Reset sources page when new messages arrive
useEffect(() => {
- if (activeTabValue && activeTabValue !== activeTab) {
- setActiveTab(activeTabValue);
- }
- }, [activeTabValue, activeTab]);
+ // Reset pagination when we get new messages
+ setSourcesPage(1);
+ setExpandedSources(false);
+ }, [messages]);
// Scroll terminal to bottom when expanded
useEffect(() => {
@@ -582,7 +559,7 @@ const ChatPage = () => {
// Get total sources count for a connector type
const getSourcesCount = (connectorType: string) => {
- return getSourcesCountUtil(connectorSources, connectorType);
+ return getSourcesCountUtil(getMessageConnectorSources(messages[messages.length - 1]), connectorType);
};
// Function to check scroll position and update indicators
@@ -638,23 +615,14 @@ const ChatPage = () => {
if (assistantMessages.length === 0) return null;
const latestAssistantMessage = assistantMessages[assistantMessages.length - 1];
- if (!latestAssistantMessage?.annotations) return null;
-
- // Find all SOURCES annotations
- const annotations = latestAssistantMessage.annotations as any[];
- const sourcesAnnotations = annotations.filter(
- (annotation) => annotation.type === 'SOURCES'
- );
-
- // Get the latest SOURCES annotation
- if (sourcesAnnotations.length === 0) return null;
- const latestSourcesAnnotation = sourcesAnnotations[sourcesAnnotations.length - 1];
-
- if (!latestSourcesAnnotation.content) return null;
+
+ // Use our helper function to get sources
+ const sources = getMessageConnectorSources(latestAssistantMessage);
+ if (sources.length === 0) return null;
// Flatten all sources from all connectors
const allSources: Source[] = [];
- latestSourcesAnnotation.content.forEach((connector: ConnectorSource) => {
+ sources.forEach((connector: ConnectorSource) => {
if (connector.sources && Array.isArray(connector.sources)) {
connector.sources.forEach((source: SourceItem) => {
allSources.push({
@@ -675,23 +643,14 @@ const ChatPage = () => {
} else {
// Use the specific message by index
const message = messages[messageIndex];
- if (!message || message.role !== 'assistant' || !message.annotations) return null;
-
- // Find all SOURCES annotations
- const annotations = message.annotations as any[];
- const sourcesAnnotations = annotations.filter(
- (annotation) => annotation.type === 'SOURCES'
- );
-
- // Get the latest SOURCES annotation
- if (sourcesAnnotations.length === 0) return null;
- const latestSourcesAnnotation = sourcesAnnotations[sourcesAnnotations.length - 1];
-
- if (!latestSourcesAnnotation.content) return null;
+
+ // Use our helper function to get sources
+ const sources = getMessageConnectorSources(message);
+ if (sources.length === 0) return null;
// Flatten all sources from all connectors
const allSources: Source[] = [];
- latestSourcesAnnotation.content.forEach((connector: ConnectorSource) => {
+ sources.forEach((connector: ConnectorSource) => {
if (connector.sources && Array.isArray(connector.sources)) {
connector.sources.forEach((source: SourceItem) => {
allSources.push({
@@ -712,6 +671,34 @@ const ChatPage = () => {
}
}, [messages]);
+ // Pure function for rendering terminal content - no hooks allowed here
+ const renderTerminalContent = (message: any) => {
+ if (!message.annotations) return null;
+
+ // Get all TERMINAL_INFO annotations
+ const terminalInfoAnnotations = (message.annotations as any[])
+ .filter(a => a.type === 'TERMINAL_INFO');
+
+ // Get the latest TERMINAL_INFO annotation
+ const latestTerminalInfo = terminalInfoAnnotations.length > 0
+ ? terminalInfoAnnotations[terminalInfoAnnotations.length - 1]
+ : null;
+
+ // Render the content of the latest TERMINAL_INFO annotation
+ return latestTerminalInfo?.content.map((item: any, idx: number) => (
+
+ [{String(idx).padStart(2, '0')}:{String(Math.floor(idx * 2)).padStart(2, '0')}]
+ {'>'}
+ {item.text}
+
+ ));
+ };
+
return (
<>
@@ -781,30 +768,9 @@ const ChatPage = () => {
$
surfsense-researcher
- {message.annotations && (() => {
- // Get all TERMINAL_INFO annotations
- const terminalInfoAnnotations = (message.annotations as any[])
- .filter(a => a.type === 'TERMINAL_INFO');
-
- // Get the latest TERMINAL_INFO annotation
- const latestTerminalInfo = terminalInfoAnnotations.length > 0
- ? terminalInfoAnnotations[terminalInfoAnnotations.length - 1]
- : null;
-
- // Render the content of the latest TERMINAL_INFO annotation
- return latestTerminalInfo?.content.map((item: any, idx: number) => (
-
- [{String(idx).padStart(2, '0')}:{String(Math.floor(idx * 2)).padStart(2, '0')}]
- {'>'}
- {item.text}
-
- ));
- })()}
+
+ {renderTerminalContent(message)}
+
[00:13]
researcher@surfsense
@@ -836,105 +802,120 @@ const ChatPage = () => {
Sources
- 0 ? connectorSources[0].type : "CRAWLED_URL"}
- className="w-full"
- onValueChange={setActiveTab}
- >
-
-
-
+ {(() => {
+ // Get sources for this specific message
+ const messageConnectorSources = getMessageConnectorSources(message);
+
+ if (messageConnectorSources.length === 0) {
+ return (
+
+
+
+ );
+ }
+
+ // Use these message-specific sources for the Tabs component
+ return (
+
0 ? messageConnectorSources[0].type : "CRAWLED_URL"}
+ className="w-full"
+ >
+
+
+
-
-
-
- {connectorSources.map((connector) => (
-
- {getConnectorIcon(connector.type)}
- {connector.name.split(' ')[0]}
-
- {getSourcesCount(connector.type)}
-
-
- ))}
-
+
+
+
+ {messageConnectorSources.map((connector) => (
+
+ {getConnectorIcon(connector.type)}
+ {connector.name.split(' ')[0]}
+
+ {connector.sources?.length || 0}
+
+
+ ))}
+
+
+
+
+
-
-
-
+ {messageConnectorSources.map(connector => (
+
+
+ {connector.sources?.slice(0, INITIAL_SOURCES_DISPLAY)?.map((source: any) => (
+
+
+
+ {getConnectorIcon(connector.type)}
+
+
+
{source.title}
+
{source.description}
+
+
+
+
+ ))}
- {connectorSources.map(connector => (
-
-
- {getMainViewSources(connector)?.map((source: any) => (
-
-
-
- {getConnectorIcon(connector.type)}
-
-
-
{source.title}
-
{source.description}
-
-
-
-
- ))}
-
- {connector.sources.length > INITIAL_SOURCES_DISPLAY && (
-
- )}
-
-
- ))}
-
+ {connector.sources?.length > INITIAL_SOURCES_DISPLAY && (
+
+ )}
+
+
+ ))}
+
+ );
+ })()}
{/* Answer Section */}