diff --git a/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx b/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx index 4a9b0777d..b1f3aaf04 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx @@ -62,6 +62,7 @@ export interface Chat { type: "DOCUMENT" | "CHAT"; title: string; search_space_id: number; + state_version: number; } export interface ChatDetails { @@ -72,6 +73,7 @@ export interface ChatDetails { created_at: string; id: number; search_space_id: number; + state_version: number; } interface ChatsPageClientProps { diff --git a/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx b/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx index b8d867218..9f0a7be29 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/podcasts/podcasts-client.tsx @@ -54,6 +54,7 @@ export interface PodcastItem { file_location: string; podcast_transcript: any[]; search_space_id: number; + chat_state_version: number | null; } interface PodcastsPageClientProps { diff --git a/surfsense_web/components/chat/ChatInterface.tsx b/surfsense_web/components/chat/ChatInterface.tsx index d0d37fa04..b865c36e7 100644 --- a/surfsense_web/components/chat/ChatInterface.tsx +++ b/surfsense_web/components/chat/ChatInterface.tsx @@ -27,6 +27,8 @@ interface ChatInterfaceContext { setIsChatPannelOpen: (value: boolean) => void; chat_id: string; chatDetails: ChatDetails | null; + podcast: PodcastItem | null; + setPodcast: (podcast: PodcastItem | null) => void; } export const chatInterfaceContext = createContext(null); diff --git a/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx b/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx index 0521e070a..a489dde63 100644 --- a/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx +++ b/surfsense_web/components/chat/ChatPanel/ChatPanelView.tsx @@ -1,9 +1,10 @@ "use client"; -import { Pencil, Podcast } from "lucide-react"; +import { AlertCircle, Pencil, Podcast, RefreshCw } from "lucide-react"; import { useCallback, useContext, useTransition } from "react"; import { cn } from "@/lib/utils"; import { chatInterfaceContext } from "../ChatInterface"; +import { getPodcastStalenessMessage, isPodcastStale } from "../PodcastUtils"; import type { GeneratePodcastRequest } from "./ChatPanelContainer"; import { ConfigModal } from "./ConfigModal"; @@ -17,9 +18,13 @@ export function ChatPanelView(props: ChatPanelViewProps) { throw new Error("chatInterfaceContext must be used within a ChatProvider"); } - const { isChatPannelOpen, setIsChatPannelOpen, chatDetails } = context; + const { isChatPannelOpen, setIsChatPannelOpen, chatDetails, podcast } = context; const { generatePodcast } = props; + // Check if podcast is stale + const podcastIsStale = + podcast && chatDetails && isPodcastStale(chatDetails.state_version, podcast.chat_state_version); + const handleGeneratePost = useCallback(async () => { if (!chatDetails) return; await generatePodcast({ @@ -28,7 +33,9 @@ export function ChatPanelView(props: ChatPanelViewProps) { search_space_id: chatDetails.search_space_id, podcast_title: chatDetails.title, }); - }, [chatDetails]); + }, [chatDetails, generatePodcast]); + + console.log("podcastIsStale", podcastIsStale); return (
@@ -37,27 +44,67 @@ export function ChatPanelView(props: ChatPanelViewProps) { "w-full cursor-pointer h-full p-4 border-b", !isChatPannelOpen && "flex items-center justify-center" )} - title="Generate Podcast" + title={podcastIsStale ? "Regenerate Podcast" : "Generate Podcast"} > {isChatPannelOpen ? ( - +
+ {/* Show stale podcast warning if applicable */} + {podcastIsStale && ( +
+
+ +
+

Podcast is outdated

+

+ {getPodcastStalenessMessage( + chatDetails?.state_version || 0, + podcast?.chat_state_version + )} +

+
+
+
+ )} + + {/* Generate/Regenerate button */} + +
) : ( )}
diff --git a/surfsense_web/components/chat/PodcastUtils.ts b/surfsense_web/components/chat/PodcastUtils.ts new file mode 100644 index 000000000..5662f96b5 --- /dev/null +++ b/surfsense_web/components/chat/PodcastUtils.ts @@ -0,0 +1,43 @@ +/** + * Determines if a podcast is stale compared to the current chat state. + * A podcast is considered stale if: + * - The chat's current state_version is greater than the podcast's chat_state_version + * + * @param chatVersion - The current state_version of the chat + * @param podcastVersion - The chat_state_version stored when the podcast was generated (nullable) + * @returns true if the podcast is stale, false otherwise + */ +export function isPodcastStale( + chatVersion: number, + podcastVersion: number | null | undefined +): boolean { + // If podcast has no version, it's stale (generated before this feature) + if (!podcastVersion) { + return true; + } + // If chat version is greater than podcast version, it's stale : We can change this condition to consider staleness after a huge number of updates + return chatVersion > podcastVersion; +} + +/** + * Gets a human-readable message about podcast staleness + * + * @param chatVersion - The current state_version of the chat + * @param podcastVersion - The chat_state_version stored when the podcast was generated + * @returns A descriptive message about the podcast's staleness status + */ +export function getPodcastStalenessMessage( + chatVersion: number, + podcastVersion: number | null | undefined +): string { + if (!podcastVersion) { + return "This podcast was generated before chat updates were tracked. Consider regenerating it."; + } + + if (chatVersion > podcastVersion) { + const versionDiff = chatVersion - podcastVersion; + return `This podcast is outdated. The chat has been updated ${versionDiff} time${versionDiff > 1 ? "s" : ""} since this podcast was generated.`; + } + + return "This podcast is up to date with the current chat."; +}