Skip to content

Commit 55e5b45

Browse files
committed
fix podcast generation
1 parent 678d8fb commit 55e5b45

File tree

26 files changed

+477
-223
lines changed

26 files changed

+477
-223
lines changed

surfsense_backend/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/surfsense
22

3+
SECRET_KEY=SECRET
4+
NEXT_FRONTEND_URL=http://localhost:3000
5+
6+
#Celery Config
7+
CELERY_BROKER_URL=redis://localhost:6379/0
8+
CELERY_RESULT_BACKEND=redis://localhost:6379/0
9+
310
#Celery Config
411
CELERY_BROKER_URL=redis://localhost:6379/0
512
CELERY_RESULT_BACKEND=redis://localhost:6379/0

surfsense_backend/alembic/versions/32_add_podcast_staleness_detection.py

Lines changed: 0 additions & 50 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""Add podcast staleness detection columns to chats and podcasts tables
2+
3+
This feature allows the system to detect when a podcast is outdated compared to
4+
the current state of the chat it was generated from, enabling users to regenerate
5+
podcasts when needed.
6+
7+
Revision ID: 34
8+
Revises: 33
9+
"""
10+
11+
from collections.abc import Sequence
12+
13+
from alembic import op
14+
15+
# revision identifiers
16+
revision: str = "34"
17+
down_revision: str | None = "33"
18+
branch_labels: str | Sequence[str] | None = None
19+
depends_on: str | Sequence[str] | None = None
20+
21+
22+
def upgrade() -> None:
23+
"""Add columns only if they don't already exist (safe for re-runs)."""
24+
25+
# Add 'state_version' column to chats table (default 1)
26+
op.execute("""
27+
ALTER TABLE chats
28+
ADD COLUMN IF NOT EXISTS state_version BIGINT DEFAULT 1 NOT NULL
29+
""")
30+
31+
# Add 'chat_state_version' column to podcasts table
32+
op.execute("""
33+
ALTER TABLE podcasts
34+
ADD COLUMN IF NOT EXISTS chat_state_version BIGINT
35+
""")
36+
37+
# Add 'chat_id' column to podcasts table
38+
op.execute("""
39+
ALTER TABLE podcasts
40+
ADD COLUMN IF NOT EXISTS chat_id INTEGER
41+
""")
42+
43+
44+
def downgrade() -> None:
45+
"""Remove columns only if they exist."""
46+
47+
op.execute("""
48+
ALTER TABLE podcasts
49+
DROP COLUMN IF EXISTS chat_state_version
50+
""")
51+
52+
op.execute("""
53+
ALTER TABLE podcasts
54+
DROP COLUMN IF EXISTS chat_id
55+
""")
56+
57+
op.execute("""
58+
ALTER TABLE chats
59+
DROP COLUMN IF EXISTS state_version
60+
""")

surfsense_backend/app/agents/podcaster/nodes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ async def create_podcast_transcript(
2929
configuration = Configuration.from_runnable_config(config)
3030
user_id = configuration.user_id
3131
search_space_id = configuration.search_space_id
32+
podcast_title = configuration.podcast_title
3233

3334
# Get user's long context LLM
3435
llm = await get_user_long_context_llm(state.db_session, user_id, search_space_id)
@@ -37,8 +38,8 @@ async def create_podcast_transcript(
3738
print(error_message)
3839
raise RuntimeError(error_message)
3940

40-
# Get the prompt
41-
prompt = get_podcast_generation_prompt()
41+
# Get the prompt with podcast_title as user_prompt
42+
prompt = get_podcast_generation_prompt(user_prompt=podcast_title)
4243

4344
# Create the messages
4445
messages = [

surfsense_backend/app/agents/podcaster/prompts.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import datetime
22

33

4-
def get_podcast_generation_prompt():
4+
def get_podcast_generation_prompt(user_prompt: str | None = None):
5+
user_prompt_section = ""
6+
if user_prompt:
7+
user_prompt_section = f"""
8+
<user_instructions>
9+
{user_prompt}
10+
</user_instructions>
11+
"""
12+
513
return f"""
614
Today's date: {datetime.datetime.now().strftime("%Y-%m-%d")}
715
<podcast_generation_system>
8-
You are a master podcast scriptwriter, adept at transforming diverse input content into a lively, engaging, and natural-sounding conversation between two distinct podcast hosts. Your primary objective is to craft authentic, flowing dialogue that captures the spontaneity and chemistry of a real podcast discussion, completely avoiding any hint of robotic scripting or stiff formality. Think dynamic interplay, not just information delivery.
16+
You are a master podcast scriptwriter, adept at transforming diverse input content into a lively, engaging, and natural-sounding conversation between two distinct podcast hosts. Your primary objective is to craft authentic, flowing dialogue that captures the spontaneity and chemistry of a real podcast discussion, completely avoiding any hint of robotic scripting or stiff formality. Think dynamic interplay, not just information delivery.{user_prompt_section}
917
1018
<input>
1119
- '<source_content>': A block of text containing the information to be discussed in the podcast. This could be research findings, an article summary, a detailed outline, user chat history related to the topic, or any other relevant raw information. The content might be unstructured but serves as the factual basis for the podcast dialogue.

surfsense_backend/app/tasks/celery_tasks/podcast_tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def generate_chat_podcast_task(
4646
Args:
4747
chat_id: ID of the chat to generate podcast from
4848
search_space_id: ID of the search space
49-
podcast_title: Title for the podcast
49+
podcast_title: Title for the podcast (used as user prompt for generation)
5050
user_id: ID of the user
5151
"""
5252
loop = asyncio.new_event_loop()

surfsense_backend/app/tasks/podcast_tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ async def generate_chat_podcast(
9696

9797
config = {
9898
"configurable": {
99-
"podcast_title": "SurfSense",
99+
"podcast_title": podcast_title,
100100
"user_id": str(user_id),
101101
"search_space_id": search_space_id,
102102
}

surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"use client";
22

3-
import { Loader2 } from "lucide-react";
3+
import { useAtom } from "jotai";
4+
import { Loader2, PanelRight } from "lucide-react";
45
import { usePathname, useRouter } from "next/navigation";
56
import { useTranslations } from "next-intl";
67
import type React from "react";
78
import { useEffect, useMemo, useState } from "react";
9+
import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer";
810
import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb";
911
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
1012
import { AppSidebarProvider } from "@/components/sidebar/AppSidebarProvider";
@@ -13,6 +15,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
1315
import { Separator } from "@/components/ui/separator";
1416
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
1517
import { useLLMPreferences } from "@/hooks/use-llm-configs";
18+
import { cn } from "@/lib/utils";
19+
import { chatUIAtom } from "@/stores/chat/chat-ui.atom";
1620

1721
export function DashboardClientLayout({
1822
children,
@@ -30,6 +34,10 @@ export function DashboardClientLayout({
3034
const pathname = usePathname();
3135
const searchSpaceIdNum = Number(searchSpaceId);
3236

37+
const [chatUIState, setChatUIState] = useAtom(chatUIAtom);
38+
39+
const { isChatPannelOpen } = chatUIState;
40+
3341
const { loading, error, isOnboardingComplete } = useLLMPreferences(searchSpaceIdNum);
3442
const [hasCheckedOnboarding, setHasCheckedOnboarding] = useState(false);
3543

@@ -129,28 +137,49 @@ export function DashboardClientLayout({
129137
}
130138

131139
return (
132-
<SidebarProvider open={open} onOpenChange={setOpen}>
140+
<SidebarProvider
141+
className="h-full bg-red-600 overflow-hidden"
142+
open={open}
143+
onOpenChange={setOpen}
144+
>
133145
{/* Use AppSidebarProvider which fetches user, search space, and recent chats */}
134146
<AppSidebarProvider
135147
searchSpaceId={searchSpaceId}
136148
navSecondary={translatedNavSecondary}
137149
navMain={translatedNavMain}
138150
/>
139-
<SidebarInset>
140-
<header className="sticky top-0 z-50 flex h-16 shrink-0 items-center gap-2 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border-b">
141-
<div className="flex items-center justify-between w-full gap-2 px-4">
142-
<div className="flex items-center gap-2">
143-
<SidebarTrigger className="-ml-1" />
144-
<Separator orientation="vertical" className="h-6" />
145-
<DashboardBreadcrumb />
146-
</div>
147-
<div className="flex items-center gap-2">
148-
<LanguageSwitcher />
149-
<ThemeTogglerComponent />
150-
</div>
151+
<SidebarInset className="h-full ">
152+
<main className="flex h-full">
153+
<div className="flex grow flex-col h-full border-r">
154+
<header className="sticky top-0 z-50 flex h-16 shrink-0 items-center gap-2 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border-b">
155+
<div className="flex items-center justify-between w-full gap-2 px-4">
156+
<div className="flex items-center gap-2">
157+
<SidebarTrigger className="-ml-1" />
158+
<Separator orientation="vertical" className="h-6" />
159+
<DashboardBreadcrumb />
160+
</div>
161+
<div className="flex items-center gap-2">
162+
<LanguageSwitcher />
163+
<ThemeTogglerComponent />
164+
<button
165+
type="button"
166+
onClick={() =>
167+
setChatUIState((prev) => ({
168+
...prev,
169+
isChatPannelOpen: !isChatPannelOpen,
170+
}))
171+
}
172+
className={cn(" shrink-0 rounded-full w-fit hover:bg-muted")}
173+
>
174+
<PanelRight className="h-4 w-4" />
175+
</button>
176+
</div>
177+
</div>
178+
</header>
179+
<div className="grow flex-1 overflow-auto">{children}</div>
151180
</div>
152-
</header>
153-
{children}
181+
<ChatPanelContainer />
182+
</main>
154183
</SidebarInset>
155184
</SidebarProvider>
156185
);

surfsense_web/app/dashboard/layout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ export default function DashboardLayout({ children }: DashboardLayoutProps) {
4242
}
4343

4444
return (
45-
<>
45+
<div className="h-full flex flex-col ">
4646
<AnnouncementBanner />
47-
{children}
48-
</>
47+
<div className="flex-1 min-h-0">{children}</div>
48+
</div>
4949
);
5050
}

surfsense_web/app/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { I18nProvider } from "@/components/providers/I18nProvider";
66
import { ThemeProvider } from "@/components/theme/theme-provider";
77
import { Toaster } from "@/components/ui/sonner";
88
import { LocaleProvider } from "@/contexts/LocaleContext";
9+
import { ReactQueryClientProvider } from "@/lib/query-client/query-client.provider";
910
import { cn } from "@/lib/utils";
1011

1112
const roboto = Roboto({
@@ -89,7 +90,7 @@ export default function RootLayout({
8990
// Locale state is managed by LocaleContext and persisted in localStorage
9091
return (
9192
<html lang="en" suppressHydrationWarning>
92-
<body className={cn(roboto.className, "bg-white dark:bg-black antialiased h-full w-full")}>
93+
<body className={cn(roboto.className, "bg-white dark:bg-black antialiased h-full w-full ")}>
9394
<LocaleProvider>
9495
<I18nProvider>
9596
<ThemeProvider
@@ -99,7 +100,9 @@ export default function RootLayout({
99100
defaultTheme="light"
100101
>
101102
<RootProvider>
102-
{children}
103+
<ReactQueryClientProvider>
104+
<div className=" h-[100dvh] w-[100vw] overflow-hidden">{children}</div>
105+
</ReactQueryClientProvider>
103106
<Toaster />
104107
</RootProvider>
105108
</ThemeProvider>

0 commit comments

Comments
 (0)