update chat page to use a new filter for every conversation

This commit is contained in:
Cole Goldsmith 2025-11-19 16:23:32 -06:00
parent 230081e591
commit 15701f8b16
2 changed files with 1620 additions and 1558 deletions

View file

@ -1,12 +1,11 @@
"use client"; "use client";
import { Loader2, Zap } from "lucide-react"; import { Loader2, Zap } from "lucide-react";
import { useEffect, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom"; import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
import { ProtectedRoute } from "@/components/protected-route"; import { ProtectedRoute } from "@/components/protected-route";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { type EndpointType, useChat } from "@/contexts/chat-context"; import { type EndpointType, useChat } from "@/contexts/chat-context";
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
import { useTask } from "@/contexts/task-context"; import { useTask } from "@/contexts/task-context";
import { useChatStreaming } from "@/hooks/useChatStreaming"; import { useChatStreaming } from "@/hooks/useChatStreaming";
import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants"; import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants";
@ -40,6 +39,8 @@ function ChatPage() {
previousResponseIds, previousResponseIds,
setPreviousResponseIds, setPreviousResponseIds,
placeholderConversation, placeholderConversation,
conversationFilter,
setConversationFilter,
} = useChat(); } = useChat();
const [messages, setMessages] = useState<Message[]>([ const [messages, setMessages] = useState<Message[]>([
{ {
@ -78,8 +79,20 @@ function ChatPage() {
const lastLoadedConversationRef = useRef<string | null>(null); const lastLoadedConversationRef = useRef<string | null>(null);
const { addTask } = useTask(); const { addTask } = useTask();
const { selectedFilter, parsedFilterData, setSelectedFilter } =
useKnowledgeFilter(); // Use conversation-specific filter instead of global filter
const selectedFilter = conversationFilter;
// Parse the conversation filter data
const parsedFilterData = useMemo(() => {
if (!selectedFilter?.query_data) return null;
try {
return JSON.parse(selectedFilter.query_data);
} catch (error) {
console.error("Error parsing filter data:", error);
return null;
}
}, [selectedFilter]);
// Use the chat streaming hook // Use the chat streaming hook
const apiEndpoint = endpoint === "chat" ? "/api/chat" : "/api/langflow"; const apiEndpoint = endpoint === "chat" ? "/api/chat" : "/api/langflow";
@ -323,7 +336,8 @@ function ChatPage() {
}; };
const handleFilterSelect = (filter: KnowledgeFilterData | null) => { const handleFilterSelect = (filter: KnowledgeFilterData | null) => {
setSelectedFilter(filter); // Update conversation-specific filter
setConversationFilter(filter);
setIsFilterDropdownOpen(false); setIsFilterDropdownOpen(false);
setFilterSearchTerm(""); setFilterSearchTerm("");
setIsFilterHighlighted(false); setIsFilterHighlighted(false);
@ -388,8 +402,7 @@ function ChatPage() {
// 2. It's different from the last loaded conversation AND // 2. It's different from the last loaded conversation AND
// 3. User is not in the middle of an interaction // 3. User is not in the middle of an interaction
if ( if (
conversationData && conversationData?.messages &&
conversationData.messages &&
lastLoadedConversationRef.current !== conversationData.response_id && lastLoadedConversationRef.current !== conversationData.response_id &&
!isUserInteracting && !isUserInteracting &&
!isForkingInProgress !isForkingInProgress
@ -1021,7 +1034,7 @@ function ChatPage() {
if (isFilterHighlighted) { if (isFilterHighlighted) {
// Second backspace - remove the filter // Second backspace - remove the filter
setSelectedFilter(null); setConversationFilter(null);
setIsFilterHighlighted(false); setIsFilterHighlighted(false);
} else { } else {
// First backspace - highlight the filter // First backspace - highlight the filter
@ -1344,7 +1357,7 @@ function ChatPage() {
onAtClick={onAtClick} onAtClick={onAtClick}
onFilePickerClick={handleFilePickerClick} onFilePickerClick={handleFilePickerClick}
onFileSelected={setUploadedFile} onFileSelected={setUploadedFile}
setSelectedFilter={setSelectedFilter} setSelectedFilter={setConversationFilter}
setIsFilterHighlighted={setIsFilterHighlighted} setIsFilterHighlighted={setIsFilterHighlighted}
setIsFilterDropdownOpen={setIsFilterDropdownOpen} setIsFilterDropdownOpen={setIsFilterDropdownOpen}
/> />

View file

@ -2,7 +2,7 @@
import { import {
createContext, createContext,
ReactNode, type ReactNode,
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
@ -25,11 +25,22 @@ interface ConversationMessage {
response_id?: string; response_id?: string;
} }
interface KnowledgeFilter {
id: string;
name: string;
description: string;
query_data: string;
owner: string;
created_at: string;
updated_at: string;
}
interface ConversationData { interface ConversationData {
messages: ConversationMessage[]; messages: ConversationMessage[];
endpoint: EndpointType; endpoint: EndpointType;
response_id: string; response_id: string;
title: string; title: string;
filter?: KnowledgeFilter | null;
[key: string]: unknown; [key: string]: unknown;
} }
@ -65,6 +76,8 @@ interface ChatContextType {
setPlaceholderConversation: (conversation: ConversationData | null) => void; setPlaceholderConversation: (conversation: ConversationData | null) => void;
conversationLoaded: boolean; conversationLoaded: boolean;
setConversationLoaded: (loaded: boolean) => void; setConversationLoaded: (loaded: boolean) => void;
conversationFilter: KnowledgeFilter | null;
setConversationFilter: (filter: KnowledgeFilter | null) => void;
} }
const ChatContext = createContext<ChatContextType | undefined>(undefined); const ChatContext = createContext<ChatContextType | undefined>(undefined);
@ -92,6 +105,8 @@ export function ChatProvider({ children }: ChatProviderProps) {
const [placeholderConversation, setPlaceholderConversation] = const [placeholderConversation, setPlaceholderConversation] =
useState<ConversationData | null>(null); useState<ConversationData | null>(null);
const [conversationLoaded, setConversationLoaded] = useState(false); const [conversationLoaded, setConversationLoaded] = useState(false);
const [conversationFilter, setConversationFilterState] =
useState<KnowledgeFilter | null>(null);
// Debounce refresh requests to prevent excessive reloads // Debounce refresh requests to prevent excessive reloads
const refreshTimeoutRef = useRef<NodeJS.Timeout | null>(null); const refreshTimeoutRef = useRef<NodeJS.Timeout | null>(null);
@ -129,17 +144,31 @@ export function ChatProvider({ children }: ChatProviderProps) {
setRefreshTriggerSilent((prev) => prev + 1); setRefreshTriggerSilent((prev) => prev + 1);
}, []); }, []);
const loadConversation = useCallback((conversation: ConversationData) => { const loadConversation = useCallback(
(conversation: ConversationData) => {
setCurrentConversationId(conversation.response_id); setCurrentConversationId(conversation.response_id);
setEndpoint(conversation.endpoint); setEndpoint(conversation.endpoint);
// Store the full conversation data for the chat page to use // Store the full conversation data for the chat page to use
setConversationData(conversation); setConversationData(conversation);
// Load the filter if one exists for this conversation
// Only update the filter if this is a different conversation (to preserve user's filter selection)
setConversationFilterState((currentFilter) => {
// If we're loading a different conversation, load its filter
// Otherwise keep the current filter (don't reset it when conversation refreshes)
const isDifferentConversation =
conversation.response_id !== conversationData?.response_id;
return isDifferentConversation
? conversation.filter || null
: currentFilter;
});
// Clear placeholder when loading a real conversation // Clear placeholder when loading a real conversation
setPlaceholderConversation(null); setPlaceholderConversation(null);
setConversationLoaded(true); setConversationLoaded(true);
// Clear conversation docs to prevent duplicates when switching conversations // Clear conversation docs to prevent duplicates when switching conversations
setConversationDocs([]); setConversationDocs([]);
}, []); },
[conversationData?.response_id],
);
const startNewConversation = useCallback(() => { const startNewConversation = useCallback(() => {
// Clear current conversation data and reset state // Clear current conversation data and reset state
@ -148,6 +177,8 @@ export function ChatProvider({ children }: ChatProviderProps) {
setConversationData(null); setConversationData(null);
setConversationDocs([]); setConversationDocs([]);
setConversationLoaded(false); setConversationLoaded(false);
// Clear the filter when starting a new conversation
setConversationFilterState(null);
// Create a temporary placeholder conversation to show in sidebar // Create a temporary placeholder conversation to show in sidebar
const placeholderConversation: ConversationData = { const placeholderConversation: ConversationData = {
@ -198,6 +229,21 @@ export function ChatProvider({ children }: ChatProviderProps) {
[endpoint], [endpoint],
); );
const setConversationFilter = useCallback(
(filter: KnowledgeFilter | null) => {
setConversationFilterState(filter);
// Update the conversation data to include the filter
setConversationData((prev) => {
if (!prev) return prev;
return {
...prev,
filter,
};
});
},
[],
);
const value = useMemo<ChatContextType>( const value = useMemo<ChatContextType>(
() => ({ () => ({
endpoint, endpoint,
@ -221,6 +267,8 @@ export function ChatProvider({ children }: ChatProviderProps) {
setPlaceholderConversation, setPlaceholderConversation,
conversationLoaded, conversationLoaded,
setConversationLoaded, setConversationLoaded,
conversationFilter,
setConversationFilter,
}), }),
[ [
endpoint, endpoint,
@ -239,7 +287,8 @@ export function ChatProvider({ children }: ChatProviderProps) {
clearConversationDocs, clearConversationDocs,
placeholderConversation, placeholderConversation,
conversationLoaded, conversationLoaded,
setConversationLoaded, conversationFilter,
setConversationFilter,
], ],
); );