"use client"; import { ChatInput } from "@llamaindex/chat-ui"; import { Brain, Check, FolderOpen, Zap } from "lucide-react"; import { useParams } from "next/navigation"; import React, { Suspense, useCallback, useState } from "react"; import type { ResearchMode } from "@/components/chat"; import { ConnectorButton as ConnectorButtonComponent, getConnectorIcon, } from "@/components/chat/ConnectorComponents"; import { DocumentsDataTable } from "@/components/chat/DocumentsDataTable"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { type Document, useDocuments } from "@/hooks/use-documents"; import { useLLMConfigs, useLLMPreferences } from "@/hooks/use-llm-configs"; import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors"; const DocumentSelector = React.memo( ({ onSelectionChange, selectedDocuments = [], }: { onSelectionChange?: (documents: Document[]) => void; selectedDocuments?: Document[]; }) => { const { search_space_id } = useParams(); const [isOpen, setIsOpen] = useState(false); const { documents, loading, isLoaded, fetchDocuments } = useDocuments( Number(search_space_id), true ); const handleOpenChange = useCallback( (open: boolean) => { setIsOpen(open); if (open && !isLoaded) { fetchDocuments(); } }, [fetchDocuments, isLoaded] ); const handleSelectionChange = useCallback( (documents: Document[]) => { onSelectionChange?.(documents); }, [onSelectionChange] ); const handleDone = useCallback(() => { setIsOpen(false); }, []); const selectedCount = React.useMemo(() => selectedDocuments.length, [selectedDocuments.length]); return (
Select Documents Choose documents to include in your research context
{loading ? (

Loading documents...

) : isLoaded ? ( ) : null}
); } ); DocumentSelector.displayName = "DocumentSelector"; const ConnectorSelector = React.memo( ({ onSelectionChange, selectedConnectors = [], }: { onSelectionChange?: (connectorTypes: string[]) => void; selectedConnectors?: string[]; }) => { const [isOpen, setIsOpen] = useState(false); const { connectorSourceItems, isLoading, isLoaded, fetchConnectors } = useSearchSourceConnectors(true); const handleOpenChange = useCallback( (open: boolean) => { setIsOpen(open); if (open && !isLoaded) { fetchConnectors(); } }, [fetchConnectors, isLoaded] ); const handleConnectorToggle = useCallback( (connectorType: string) => { const isSelected = selectedConnectors.includes(connectorType); const newSelection = isSelected ? selectedConnectors.filter((type) => type !== connectorType) : [...selectedConnectors, connectorType]; onSelectionChange?.(newSelection); }, [selectedConnectors, onSelectionChange] ); const handleSelectAll = useCallback(() => { onSelectionChange?.(connectorSourceItems.map((c) => c.type)); }, [connectorSourceItems, onSelectionChange]); const handleClearAll = useCallback(() => { onSelectionChange?.([]); }, [onSelectionChange]); return ( setIsOpen(true)} connectorSources={connectorSourceItems} /> Select Connectors Choose which data sources to include in your research {/* Connector selection grid */}
{isLoading ? (
) : ( connectorSourceItems.map((connector) => { const isSelected = selectedConnectors.includes(connector.type); return ( ); }) )}
); } ); ConnectorSelector.displayName = "ConnectorSelector"; const SearchModeSelector = React.memo( ({ searchMode, onSearchModeChange, }: { searchMode?: "DOCUMENTS" | "CHUNKS"; onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void; }) => { const handleDocumentsClick = React.useCallback(() => { onSearchModeChange?.("DOCUMENTS"); }, [onSearchModeChange]); const handleChunksClick = React.useCallback(() => { onSearchModeChange?.("CHUNKS"); }, [onSearchModeChange]); return (
Scope:
); } ); SearchModeSelector.displayName = "SearchModeSelector"; const ResearchModeSelector = React.memo( ({ researchMode, onResearchModeChange, }: { researchMode?: ResearchMode; onResearchModeChange?: (mode: ResearchMode) => void; }) => { const handleValueChange = React.useCallback( (value: string) => { onResearchModeChange?.(value as ResearchMode); }, [onResearchModeChange] ); // Memoize mode options to prevent recreation const modeOptions = React.useMemo( () => [ { value: "QNA", label: "Q&A", shortLabel: "Q&A" }, { value: "REPORT_GENERAL", label: "General Report", shortLabel: "General", }, { value: "REPORT_DEEP", label: "Deep Report", shortLabel: "Deep", }, { value: "REPORT_DEEPER", label: "Deeper Report", shortLabel: "Deeper", }, ], [] ); return (
Mode:
); } ); ResearchModeSelector.displayName = "ResearchModeSelector"; const LLMSelector = React.memo(() => { const { llmConfigs, loading: llmLoading, error } = useLLMConfigs(); const { preferences, updatePreferences, loading: preferencesLoading } = useLLMPreferences(); const isLoading = llmLoading || preferencesLoading; // Memoize the selected config to avoid repeated lookups const selectedConfig = React.useMemo(() => { if (!preferences.fast_llm_id || !llmConfigs.length) return null; return llmConfigs.find((config) => config.id === preferences.fast_llm_id) || null; }, [preferences.fast_llm_id, llmConfigs]); // Memoize the display value for the trigger const displayValue = React.useMemo(() => { if (!selectedConfig) return null; return (
{selectedConfig.provider} {selectedConfig.name}
); }, [selectedConfig]); const handleValueChange = React.useCallback( (value: string) => { const llmId = value ? parseInt(value, 10) : undefined; updatePreferences({ fast_llm_id: llmId }); }, [updatePreferences] ); // Loading skeleton if (isLoading) { return (
); } // Error state if (error) { return (
); } return (
); }); LLMSelector.displayName = "LLMSelector"; const CustomChatInputOptions = React.memo( ({ onDocumentSelectionChange, selectedDocuments, onConnectorSelectionChange, selectedConnectors, searchMode, onSearchModeChange, researchMode, onResearchModeChange, }: { onDocumentSelectionChange?: (documents: Document[]) => void; selectedDocuments?: Document[]; onConnectorSelectionChange?: (connectorTypes: string[]) => void; selectedConnectors?: string[]; searchMode?: "DOCUMENTS" | "CHUNKS"; onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void; researchMode?: ResearchMode; onResearchModeChange?: (mode: ResearchMode) => void; }) => { // Memoize the loading fallback to prevent recreation const loadingFallback = React.useMemo( () =>
, [] ); return (
); } ); CustomChatInputOptions.displayName = "CustomChatInputOptions"; export const ChatInputUI = React.memo( ({ onDocumentSelectionChange, selectedDocuments, onConnectorSelectionChange, selectedConnectors, searchMode, onSearchModeChange, researchMode, onResearchModeChange, }: { onDocumentSelectionChange?: (documents: Document[]) => void; selectedDocuments?: Document[]; onConnectorSelectionChange?: (connectorTypes: string[]) => void; selectedConnectors?: string[]; searchMode?: "DOCUMENTS" | "CHUNKS"; onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void; researchMode?: ResearchMode; onResearchModeChange?: (mode: ResearchMode) => void; }) => { return ( ); } ); ChatInputUI.displayName = "ChatInputUI";