mirror of
https://github.com/MODSetter/SurfSense.git
synced 2025-09-02 10:39:13 +00:00
Added search type and search mode selections for chcat input
This commit is contained in:
parent
3f051b0a19
commit
ac99613b24
3 changed files with 198 additions and 42 deletions
|
@ -20,6 +20,7 @@ export default function ResearchChatPageV2() {
|
||||||
isLoading,
|
isLoading,
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
searchMode,
|
searchMode,
|
||||||
|
setSearchMode,
|
||||||
researchMode,
|
researchMode,
|
||||||
setResearchMode,
|
setResearchMode,
|
||||||
selectedConnectors,
|
selectedConnectors,
|
||||||
|
@ -34,8 +35,6 @@ export default function ResearchChatPageV2() {
|
||||||
const { fetchChatDetails, updateChat, createChat } = useChatAPI({
|
const { fetchChatDetails, updateChat, createChat } = useChatAPI({
|
||||||
token,
|
token,
|
||||||
search_space_id: search_space_id as string,
|
search_space_id: search_space_id as string,
|
||||||
researchMode,
|
|
||||||
selectedConnectors,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Memoize document IDs to prevent infinite re-renders
|
// Memoize document IDs to prevent infinite re-renders
|
||||||
|
@ -43,31 +42,37 @@ export default function ResearchChatPageV2() {
|
||||||
return selectedDocuments.map((doc) => doc.id);
|
return selectedDocuments.map((doc) => doc.id);
|
||||||
}, [selectedDocuments]);
|
}, [selectedDocuments]);
|
||||||
|
|
||||||
// Helper functions for localStorage management
|
// Unified localStorage management for chat state
|
||||||
const getStorageKey = (searchSpaceId: string, chatId: string) =>
|
interface ChatState {
|
||||||
`surfsense_selected_docs_${searchSpaceId}_${chatId}`;
|
selectedDocuments: Document[];
|
||||||
|
searchMode: "DOCUMENTS" | "CHUNKS";
|
||||||
|
researchMode: ResearchMode;
|
||||||
|
}
|
||||||
|
|
||||||
const storeSelectedDocuments = (
|
const getChatStateStorageKey = (searchSpaceId: string, chatId: string) =>
|
||||||
|
`surfsense_chat_state_${searchSpaceId}_${chatId}`;
|
||||||
|
|
||||||
|
const storeChatState = (
|
||||||
searchSpaceId: string,
|
searchSpaceId: string,
|
||||||
chatId: string,
|
chatId: string,
|
||||||
documents: Document[]
|
state: ChatState
|
||||||
) => {
|
) => {
|
||||||
const key = getStorageKey(searchSpaceId, chatId);
|
const key = getChatStateStorageKey(searchSpaceId, chatId);
|
||||||
localStorage.setItem(key, JSON.stringify(documents));
|
localStorage.setItem(key, JSON.stringify(state));
|
||||||
};
|
};
|
||||||
|
|
||||||
const restoreSelectedDocuments = (
|
const restoreChatState = (
|
||||||
searchSpaceId: string,
|
searchSpaceId: string,
|
||||||
chatId: string
|
chatId: string
|
||||||
): Document[] | null => {
|
): ChatState | null => {
|
||||||
const key = getStorageKey(searchSpaceId, chatId);
|
const key = getChatStateStorageKey(searchSpaceId, chatId);
|
||||||
const stored = localStorage.getItem(key);
|
const stored = localStorage.getItem(key);
|
||||||
if (stored) {
|
if (stored) {
|
||||||
localStorage.removeItem(key); // Clean up after restoration
|
localStorage.removeItem(key); // Clean up after restoration
|
||||||
try {
|
try {
|
||||||
return JSON.parse(stored);
|
return JSON.parse(stored);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error parsing stored documents:", error);
|
console.error("Error parsing stored chat state:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,14 +104,14 @@ export default function ResearchChatPageV2() {
|
||||||
message: Message | CreateMessage,
|
message: Message | CreateMessage,
|
||||||
chatRequestOptions?: { data?: any }
|
chatRequestOptions?: { data?: any }
|
||||||
) => {
|
) => {
|
||||||
const newChatId = await createChat(message.content);
|
const newChatId = await createChat(message.content, researchMode);
|
||||||
if (newChatId) {
|
if (newChatId) {
|
||||||
// Store selected documents before navigation
|
// Store chat state before navigation
|
||||||
storeSelectedDocuments(
|
storeChatState(search_space_id as string, newChatId, {
|
||||||
search_space_id as string,
|
selectedDocuments,
|
||||||
newChatId,
|
searchMode,
|
||||||
selectedDocuments
|
researchMode,
|
||||||
);
|
});
|
||||||
router.replace(`/dashboard/${search_space_id}/v2/${newChatId}`);
|
router.replace(`/dashboard/${search_space_id}/v2/${newChatId}`);
|
||||||
}
|
}
|
||||||
return newChatId;
|
return newChatId;
|
||||||
|
@ -119,18 +124,26 @@ export default function ResearchChatPageV2() {
|
||||||
}
|
}
|
||||||
}, [token, isNewChat, chatIdParam]);
|
}, [token, isNewChat, chatIdParam]);
|
||||||
|
|
||||||
// Restore selected documents from localStorage on page load
|
// Restore chat state from localStorage on page load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chatIdParam && search_space_id) {
|
if (chatIdParam && search_space_id) {
|
||||||
const restoredDocuments = restoreSelectedDocuments(
|
const restoredState = restoreChatState(
|
||||||
search_space_id as string,
|
search_space_id as string,
|
||||||
chatIdParam
|
chatIdParam
|
||||||
);
|
);
|
||||||
if (restoredDocuments && restoredDocuments.length > 0) {
|
if (restoredState) {
|
||||||
setSelectedDocuments(restoredDocuments);
|
setSelectedDocuments(restoredState.selectedDocuments);
|
||||||
|
setSearchMode(restoredState.searchMode);
|
||||||
|
setResearchMode(restoredState.researchMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [chatIdParam, search_space_id, setSelectedDocuments]);
|
}, [
|
||||||
|
chatIdParam,
|
||||||
|
search_space_id,
|
||||||
|
setSelectedDocuments,
|
||||||
|
setSearchMode,
|
||||||
|
setResearchMode,
|
||||||
|
]);
|
||||||
|
|
||||||
const loadChatData = async (chatId: string) => {
|
const loadChatData = async (chatId: string) => {
|
||||||
try {
|
try {
|
||||||
|
@ -179,9 +192,9 @@ export default function ResearchChatPageV2() {
|
||||||
handler.messages.length > 0 &&
|
handler.messages.length > 0 &&
|
||||||
handler.messages[handler.messages.length - 1]?.role === "assistant"
|
handler.messages[handler.messages.length - 1]?.role === "assistant"
|
||||||
) {
|
) {
|
||||||
updateChat(chatIdParam, handler.messages);
|
updateChat(chatIdParam, handler.messages, researchMode);
|
||||||
}
|
}
|
||||||
}, [handler.messages, handler.status, chatIdParam, isNewChat, updateChat]);
|
}, [handler.messages, handler.status, chatIdParam, isNewChat]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
|
@ -199,6 +212,10 @@ export default function ResearchChatPageV2() {
|
||||||
}}
|
}}
|
||||||
onDocumentSelectionChange={setSelectedDocuments}
|
onDocumentSelectionChange={setSelectedDocuments}
|
||||||
selectedDocuments={selectedDocuments}
|
selectedDocuments={selectedDocuments}
|
||||||
|
searchMode={searchMode}
|
||||||
|
onSearchModeChange={setSearchMode}
|
||||||
|
researchMode={researchMode}
|
||||||
|
onResearchModeChange={setResearchMode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,29 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "../ui/dialog";
|
} from "../ui/dialog";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "../ui/select";
|
||||||
|
import { Badge } from "../ui/badge";
|
||||||
import { Suspense, useState, useCallback } from "react";
|
import { Suspense, useState, useCallback } from "react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { useDocuments, DocumentType, Document } from "@/hooks/use-documents";
|
import { useDocuments, DocumentType, Document } from "@/hooks/use-documents";
|
||||||
import { DocumentsDataTable } from "./DocumentsDataTable";
|
import { DocumentsDataTable } from "./DocumentsDataTable";
|
||||||
|
import { ResearchMode } from "@/components/chat";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
interface ChatInterfaceProps {
|
interface ChatInterfaceProps {
|
||||||
handler: ChatHandler;
|
handler: ChatHandler;
|
||||||
onDocumentSelectionChange?: (documents: Document[]) => void;
|
onDocumentSelectionChange?: (documents: Document[]) => void;
|
||||||
selectedDocuments?: Document[];
|
selectedDocuments?: Document[];
|
||||||
|
searchMode?: "DOCUMENTS" | "CHUNKS";
|
||||||
|
onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void;
|
||||||
|
researchMode?: ResearchMode;
|
||||||
|
onResearchModeChange?: (mode: ResearchMode) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DocumentSelector = React.memo(
|
const DocumentSelector = React.memo(
|
||||||
|
@ -118,21 +131,127 @@ const DocumentSelector = React.memo(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const SearchModeSelector = ({
|
||||||
|
searchMode,
|
||||||
|
onSearchModeChange,
|
||||||
|
}: {
|
||||||
|
searchMode?: "DOCUMENTS" | "CHUNKS";
|
||||||
|
onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1 sm:gap-2">
|
||||||
|
<span className="text-xs text-muted-foreground hidden sm:block">
|
||||||
|
Scope:
|
||||||
|
</span>
|
||||||
|
<div className="flex rounded-md border">
|
||||||
|
<Button
|
||||||
|
variant={searchMode === "DOCUMENTS" ? "default" : "ghost"}
|
||||||
|
size="sm"
|
||||||
|
className="rounded-r-none border-r h-8 px-2 sm:px-3 text-xs"
|
||||||
|
onClick={() => onSearchModeChange?.("DOCUMENTS")}
|
||||||
|
>
|
||||||
|
<span className="hidden sm:inline">Documents</span>
|
||||||
|
<span className="sm:hidden">Docs</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant={searchMode === "CHUNKS" ? "default" : "ghost"}
|
||||||
|
size="sm"
|
||||||
|
className="rounded-l-none h-8 px-2 sm:px-3 text-xs"
|
||||||
|
onClick={() => onSearchModeChange?.("CHUNKS")}
|
||||||
|
>
|
||||||
|
Chunks
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ResearchModeSelector = ({
|
||||||
|
researchMode,
|
||||||
|
onResearchModeChange,
|
||||||
|
}: {
|
||||||
|
researchMode?: ResearchMode;
|
||||||
|
onResearchModeChange?: (mode: ResearchMode) => void;
|
||||||
|
}) => {
|
||||||
|
const researchModeLabels: Record<ResearchMode, string> = {
|
||||||
|
QNA: "Q&A",
|
||||||
|
REPORT_GENERAL: "General Report",
|
||||||
|
REPORT_DEEP: "Deep Report",
|
||||||
|
REPORT_DEEPER: "Deeper Report",
|
||||||
|
};
|
||||||
|
|
||||||
|
const researchModeShortLabels: Record<ResearchMode, string> = {
|
||||||
|
QNA: "Q&A",
|
||||||
|
REPORT_GENERAL: "General",
|
||||||
|
REPORT_DEEP: "Deep",
|
||||||
|
REPORT_DEEPER: "Deeper",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1 sm:gap-2">
|
||||||
|
<span className="text-xs text-muted-foreground hidden sm:block">
|
||||||
|
Mode:
|
||||||
|
</span>
|
||||||
|
<Select
|
||||||
|
value={researchMode}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
onResearchModeChange?.(value as ResearchMode)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-auto min-w-[80px] sm:min-w-[120px] h-8 text-xs">
|
||||||
|
<SelectValue placeholder="Mode" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="QNA">Q&A</SelectItem>
|
||||||
|
<SelectItem value="REPORT_GENERAL">
|
||||||
|
<span className="hidden sm:inline">General Report</span>
|
||||||
|
<span className="sm:hidden">General</span>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="REPORT_DEEP">
|
||||||
|
<span className="hidden sm:inline">Deep Report</span>
|
||||||
|
<span className="sm:hidden">Deep</span>
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="REPORT_DEEPER">
|
||||||
|
<span className="hidden sm:inline">Deeper Report</span>
|
||||||
|
<span className="sm:hidden">Deeper</span>
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const CustomChatInputOptions = ({
|
const CustomChatInputOptions = ({
|
||||||
onDocumentSelectionChange,
|
onDocumentSelectionChange,
|
||||||
selectedDocuments,
|
selectedDocuments,
|
||||||
|
searchMode,
|
||||||
|
onSearchModeChange,
|
||||||
|
researchMode,
|
||||||
|
onResearchModeChange,
|
||||||
}: {
|
}: {
|
||||||
onDocumentSelectionChange?: (documents: Document[]) => void;
|
onDocumentSelectionChange?: (documents: Document[]) => void;
|
||||||
selectedDocuments?: Document[];
|
selectedDocuments?: Document[];
|
||||||
|
searchMode?: "DOCUMENTS" | "CHUNKS";
|
||||||
|
onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void;
|
||||||
|
researchMode?: ResearchMode;
|
||||||
|
onResearchModeChange?: (mode: ResearchMode) => void;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-wrap gap-2 sm:gap-3 items-center justify-start">
|
||||||
<Suspense fallback={<div>Loading...</div>}>
|
<Suspense fallback={<div>Loading...</div>}>
|
||||||
<DocumentSelector
|
<DocumentSelector
|
||||||
onSelectionChange={onDocumentSelectionChange}
|
onSelectionChange={onDocumentSelectionChange}
|
||||||
selectedDocuments={selectedDocuments}
|
selectedDocuments={selectedDocuments}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
<SearchModeSelector
|
||||||
|
searchMode={searchMode}
|
||||||
|
onSearchModeChange={onSearchModeChange}
|
||||||
|
/>
|
||||||
|
<ResearchModeSelector
|
||||||
|
researchMode={researchMode}
|
||||||
|
onResearchModeChange={onResearchModeChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -140,9 +259,17 @@ const CustomChatInputOptions = ({
|
||||||
const CustomChatInput = ({
|
const CustomChatInput = ({
|
||||||
onDocumentSelectionChange,
|
onDocumentSelectionChange,
|
||||||
selectedDocuments,
|
selectedDocuments,
|
||||||
|
searchMode,
|
||||||
|
onSearchModeChange,
|
||||||
|
researchMode,
|
||||||
|
onResearchModeChange,
|
||||||
}: {
|
}: {
|
||||||
onDocumentSelectionChange?: (documents: Document[]) => void;
|
onDocumentSelectionChange?: (documents: Document[]) => void;
|
||||||
selectedDocuments?: Document[];
|
selectedDocuments?: Document[];
|
||||||
|
searchMode?: "DOCUMENTS" | "CHUNKS";
|
||||||
|
onSearchModeChange?: (mode: "DOCUMENTS" | "CHUNKS") => void;
|
||||||
|
researchMode?: ResearchMode;
|
||||||
|
onResearchModeChange?: (mode: ResearchMode) => void;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ChatInput>
|
<ChatInput>
|
||||||
|
@ -153,6 +280,10 @@ const CustomChatInput = ({
|
||||||
<CustomChatInputOptions
|
<CustomChatInputOptions
|
||||||
onDocumentSelectionChange={onDocumentSelectionChange}
|
onDocumentSelectionChange={onDocumentSelectionChange}
|
||||||
selectedDocuments={selectedDocuments}
|
selectedDocuments={selectedDocuments}
|
||||||
|
searchMode={searchMode}
|
||||||
|
onSearchModeChange={onSearchModeChange}
|
||||||
|
researchMode={researchMode}
|
||||||
|
onResearchModeChange={onResearchModeChange}
|
||||||
/>
|
/>
|
||||||
</ChatInput>
|
</ChatInput>
|
||||||
);
|
);
|
||||||
|
@ -162,6 +293,10 @@ export default function ChatInterface({
|
||||||
handler,
|
handler,
|
||||||
onDocumentSelectionChange,
|
onDocumentSelectionChange,
|
||||||
selectedDocuments = [],
|
selectedDocuments = [],
|
||||||
|
searchMode,
|
||||||
|
onSearchModeChange,
|
||||||
|
researchMode,
|
||||||
|
onResearchModeChange,
|
||||||
}: ChatInterfaceProps) {
|
}: ChatInterfaceProps) {
|
||||||
return (
|
return (
|
||||||
<ChatSection handler={handler} className="flex h-full">
|
<ChatSection handler={handler} className="flex h-full">
|
||||||
|
@ -177,6 +312,10 @@ export default function ChatInterface({
|
||||||
<CustomChatInput
|
<CustomChatInput
|
||||||
onDocumentSelectionChange={onDocumentSelectionChange}
|
onDocumentSelectionChange={onDocumentSelectionChange}
|
||||||
selectedDocuments={selectedDocuments}
|
selectedDocuments={selectedDocuments}
|
||||||
|
searchMode={searchMode}
|
||||||
|
onSearchModeChange={onSearchModeChange}
|
||||||
|
researchMode={researchMode}
|
||||||
|
onResearchModeChange={onResearchModeChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,16 +49,9 @@ export function useChatState({ search_space_id, chat_id }: UseChatStateProps) {
|
||||||
interface UseChatAPIProps {
|
interface UseChatAPIProps {
|
||||||
token: string | null;
|
token: string | null;
|
||||||
search_space_id: string;
|
search_space_id: string;
|
||||||
researchMode: ResearchMode;
|
|
||||||
selectedConnectors: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useChatAPI({
|
export function useChatAPI({ token, search_space_id }: UseChatAPIProps) {
|
||||||
token,
|
|
||||||
search_space_id,
|
|
||||||
researchMode,
|
|
||||||
selectedConnectors,
|
|
||||||
}: UseChatAPIProps) {
|
|
||||||
const fetchChatDetails = useCallback(
|
const fetchChatDetails = useCallback(
|
||||||
async (chatId: string) => {
|
async (chatId: string) => {
|
||||||
if (!token) return null;
|
if (!token) return null;
|
||||||
|
@ -93,7 +86,10 @@ export function useChatAPI({
|
||||||
);
|
);
|
||||||
|
|
||||||
const createChat = useCallback(
|
const createChat = useCallback(
|
||||||
async (initialMessage: string): Promise<string | null> => {
|
async (
|
||||||
|
initialMessage: string,
|
||||||
|
researchMode: ResearchMode
|
||||||
|
): Promise<string | null> => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
console.error("Authentication token not found");
|
console.error("Authentication token not found");
|
||||||
return null;
|
return null;
|
||||||
|
@ -111,7 +107,7 @@ export function useChatAPI({
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
type: researchMode,
|
type: researchMode,
|
||||||
title: "Untitled Chat",
|
title: "Untitled Chat",
|
||||||
initial_connectors: selectedConnectors,
|
initial_connectors: [],
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
|
@ -136,11 +132,15 @@ export function useChatAPI({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[token, researchMode, selectedConnectors, search_space_id]
|
[token, search_space_id]
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateChat = useCallback(
|
const updateChat = useCallback(
|
||||||
async (chatId: string, messages: Message[]) => {
|
async (
|
||||||
|
chatId: string,
|
||||||
|
messages: Message[],
|
||||||
|
researchMode: ResearchMode
|
||||||
|
) => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -164,7 +164,7 @@ export function useChatAPI({
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
type: researchMode,
|
type: researchMode,
|
||||||
title: title,
|
title: title,
|
||||||
initial_connectors: selectedConnectors,
|
initial_connectors: [],
|
||||||
messages: messages,
|
messages: messages,
|
||||||
search_space_id: Number(search_space_id),
|
search_space_id: Number(search_space_id),
|
||||||
}),
|
}),
|
||||||
|
@ -180,7 +180,7 @@ export function useChatAPI({
|
||||||
console.error("Error updating chat:", err);
|
console.error("Error updating chat:", err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[token, researchMode, selectedConnectors, search_space_id]
|
[token, search_space_id]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Add table
Reference in a new issue