diff --git a/frontend/components/navigation.tsx b/frontend/components/navigation.tsx
index 41ba5216..a2d6e08b 100644
--- a/frontend/components/navigation.tsx
+++ b/frontend/components/navigation.tsx
@@ -4,15 +4,17 @@ import Link from "next/link"
import { usePathname } from "next/navigation"
import { Library, MessageSquare, Settings2, Plus, FileText } from "lucide-react"
import { cn } from "@/lib/utils"
-import { useState, useEffect, useRef } from "react"
+import { useState, useEffect, useRef, useCallback } from "react"
import { useChat } from "@/contexts/chat-context"
-interface ChatConversation {
+import { EndpointType } from "@/contexts/chat-context"
+
+interface RawConversation {
response_id: string
title: string
- endpoint: 'chat' | 'langflow'
+ endpoint: string
messages: Array<{
- role: 'user' | 'assistant'
+ role: string
content: string
timestamp?: string
response_id?: string
@@ -21,6 +23,24 @@ interface ChatConversation {
last_activity?: string
previous_response_id?: string
total_messages: number
+ [key: string]: unknown
+}
+
+interface ChatConversation {
+ response_id: string
+ title: string
+ endpoint: EndpointType
+ messages: Array<{
+ role: string
+ content: string
+ timestamp?: string
+ response_id?: string
+ }>
+ created_at?: string
+ last_activity?: string
+ previous_response_id?: string
+ total_messages: number
+ [key: string]: unknown
}
@@ -128,14 +148,7 @@ export function Navigation() {
const isOnChatPage = pathname === "/" || pathname === "/chat"
- // Fetch chat conversations when on chat page, endpoint changes, or refresh is triggered
- useEffect(() => {
- if (isOnChatPage) {
- fetchConversations()
- }
- }, [isOnChatPage, endpoint, refreshTrigger])
-
- const fetchConversations = async () => {
+ const fetchConversations = useCallback(async () => {
setLoadingConversations(true)
try {
// Fetch from the selected endpoint only
@@ -144,7 +157,13 @@ export function Navigation() {
const response = await fetch(apiEndpoint)
if (response.ok) {
const history = await response.json()
- const conversations = history.conversations || []
+ const rawConversations = history.conversations || []
+
+ // Cast conversations to proper type and ensure endpoint is correct
+ const conversations: ChatConversation[] = rawConversations.map((conv: RawConversation) => ({
+ ...conv,
+ endpoint: conv.endpoint as EndpointType
+ }))
// Sort conversations by last activity (most recent first)
conversations.sort((a: ChatConversation, b: ChatConversation) => {
@@ -166,7 +185,14 @@ export function Navigation() {
} finally {
setLoadingConversations(false)
}
- }
+ }, [endpoint])
+
+ // Fetch chat conversations when on chat page, endpoint changes, or refresh is triggered
+ useEffect(() => {
+ if (isOnChatPage) {
+ fetchConversations()
+ }
+ }, [isOnChatPage, endpoint, refreshTrigger, fetchConversations])
return (
diff --git a/frontend/src/app/chat/page.tsx b/frontend/src/app/chat/page.tsx
index 82040e53..3c489999 100644
--- a/frontend/src/app/chat/page.tsx
+++ b/frontend/src/app/chat/page.tsx
@@ -70,7 +70,7 @@ interface RequestBody {
function ChatPage() {
const isDebugMode = process.env.NODE_ENV === 'development' || process.env.NEXT_PUBLIC_OPENRAG_DEBUG === 'true'
const { user } = useAuth()
- const { endpoint, setEndpoint, refreshConversations, currentConversationId, conversationData, setCurrentConversationId, addConversationDoc, startNewConversation } = useChat()
+ const { endpoint, setEndpoint, currentConversationId, conversationData, setCurrentConversationId, addConversationDoc, forkFromResponse } = useChat()
const [messages, setMessages] = useState
([
{
role: "assistant",
@@ -370,8 +370,13 @@ function ChatPage() {
if (conversationData && conversationData.messages) {
console.log("Loading conversation with", conversationData.messages.length, "messages")
// Convert backend message format to frontend Message interface
- const convertedMessages: Message[] = conversationData.messages.map((msg: any) => ({
- role: msg.role,
+ const convertedMessages: Message[] = conversationData.messages.map((msg: {
+ role: string;
+ content: string;
+ timestamp?: string;
+ response_id?: string;
+ }) => ({
+ role: msg.role as "user" | "assistant",
content: msg.content,
timestamp: new Date(msg.timestamp || new Date()),
// Add any other necessary properties
@@ -1097,19 +1102,23 @@ function ChatPage() {
// This means we're continuing the conversation thread from that point
const responseIdToForkFrom = currentConversationId || previousResponseIds[endpoint]
- // Create a new conversation by clearing the current conversation ID
- // but keeping the messages truncated to the fork point
+ // Create a new conversation by properly forking
setMessages(messagesToKeep)
- setCurrentConversationId(null) // This creates a new conversation thread
- // We'll handle clearing conversation data differently
-
- // Set the response_id we want to continue from as the previous response ID
- // This tells the backend to continue the conversation from this point
- setPreviousResponseIds(prev => ({
- ...prev,
- [endpoint]: responseIdToForkFrom
- }))
+ // Use the chat context's fork method which handles creating a new conversation properly
+ if (forkFromResponse) {
+ forkFromResponse(responseIdToForkFrom || '')
+ } else {
+ // Fallback to manual approach
+ setCurrentConversationId(null) // This creates a new conversation thread
+
+ // Set the response_id we want to continue from as the previous response ID
+ // This tells the backend to continue the conversation from this point
+ setPreviousResponseIds(prev => ({
+ ...prev,
+ [endpoint]: responseIdToForkFrom
+ }))
+ }
console.log("Forked conversation with", messagesToKeep.length, "messages")
diff --git a/frontend/src/contexts/chat-context.tsx b/frontend/src/contexts/chat-context.tsx
index 9965a06f..0ff7839d 100644
--- a/frontend/src/contexts/chat-context.tsx
+++ b/frontend/src/contexts/chat-context.tsx
@@ -9,6 +9,20 @@ interface ConversationDocument {
uploadTime: Date
}
+interface ConversationMessage {
+ role: string
+ content: string
+ timestamp?: string
+ response_id?: string
+}
+
+interface ConversationData {
+ messages: ConversationMessage[]
+ endpoint: EndpointType
+ response_id: string
+ [key: string]: unknown
+}
+
interface ChatContextType {
endpoint: EndpointType
setEndpoint: (endpoint: EndpointType) => void
@@ -21,10 +35,10 @@ interface ChatContextType {
setPreviousResponseIds: (ids: { chat: string | null; langflow: string | null }) => void
refreshConversations: () => void
refreshTrigger: number
- loadConversation: (conversation: any) => void
+ loadConversation: (conversation: ConversationData) => void
startNewConversation: () => void
- conversationData: any
- forkFromResponse: (responseId: string, messagesToKeep: any[]) => void
+ conversationData: ConversationData | null
+ forkFromResponse: (responseId: string) => void
conversationDocs: ConversationDocument[]
addConversationDoc: (filename: string) => void
clearConversationDocs: () => void
@@ -44,14 +58,14 @@ export function ChatProvider({ children }: ChatProviderProps) {
langflow: string | null
}>({ chat: null, langflow: null })
const [refreshTrigger, setRefreshTrigger] = useState(0)
- const [conversationData, setConversationData] = useState(null)
+ const [conversationData, setConversationData] = useState(null)
const [conversationDocs, setConversationDocs] = useState([])
const refreshConversations = () => {
setRefreshTrigger(prev => prev + 1)
}
- const loadConversation = (conversation: any) => {
+ const loadConversation = (conversation: ConversationData) => {
setCurrentConversationId(conversation.response_id)
setEndpoint(conversation.endpoint)
// Store the full conversation data for the chat page to use
@@ -74,10 +88,10 @@ export function ChatProvider({ children }: ChatProviderProps) {
setConversationDocs([])
}
- const forkFromResponse = (responseId: string, messagesToKeep: any[]) => {
+ const forkFromResponse = (responseId: string) => {
// Start a new conversation with the messages up to the fork point
setCurrentConversationId(null) // Clear current conversation to indicate new conversation
- // Don't clear conversation data - let the chat page manage the messages
+ setConversationData(null) // Clear conversation data to prevent reloading
// Set the response ID that we're forking from as the previous response ID
setPreviousResponseIds(prev => ({
...prev,