import { useMemo } from 'react'; import type { TAttachment } from 'librechat-data-provider'; import { StackedFavicons } from '~/components/Web/Sources'; import { useSearchContext } from '~/Providers'; import ProgressText from './ProgressText'; import { useLocalize } from '~/hooks'; type ProgressKeys = | 'com_ui_web_searching' | 'com_ui_web_searching_again' | 'com_ui_web_search_processing' | 'com_ui_web_search_reading'; export default function WebSearch({ initialProgress: progress = 0.1, isSubmitting, isLast, output, }: { isLast?: boolean; isSubmitting: boolean; output?: string | null; initialProgress: number; attachments?: TAttachment[]; }) { const localize = useLocalize(); const { searchResults } = useSearchContext(); const error = typeof output === 'string' && output.toLowerCase().includes('error processing'); const cancelled = (!isSubmitting && progress < 1) || error === true; const complete = !isLast && progress === 1; const finalizing = isSubmitting && isLast && progress === 1; const processedSources = useMemo(() => { if (complete && !finalizing) { return []; } if (!searchResults) return []; const values = Object.values(searchResults); const result = values[values.length - 1]; if (!result) return []; if (finalizing) { return [...(result.organic || []), ...(result.topStories || [])]; } return [...(result.organic || []), ...(result.topStories || [])].filter( (source) => source.processed === true, ); }, [searchResults, complete, finalizing]); const turns = useMemo(() => { if (!searchResults) return 0; return Object.values(searchResults).length; }, [searchResults]); const clampedProgress = useMemo(() => { return Math.min(progress, 0.99); }, [progress]); const showSources = processedSources.length > 0; const progressText = useMemo(() => { let text: ProgressKeys = turns > 1 ? 'com_ui_web_searching_again' : 'com_ui_web_searching'; if (showSources) { text = 'com_ui_web_search_processing'; } if (finalizing) { text = 'com_ui_web_search_reading'; } return localize(text); }, [turns, localize, showSources, finalizing]); if (complete || cancelled) { return null; } return ( <>