Merge pull request #197 from presenton/fix/outlines-issue
fix/outlines issue
This commit is contained in:
commit
dcfe8a68e1
18 changed files with 116 additions and 94 deletions
|
|
@ -87,7 +87,8 @@ async def stream_outlines(
|
|||
|
||||
presentation.outlines = presentation_outlines.model_dump()
|
||||
presentation.title = (
|
||||
presentation_outlines.slides[0][:50]
|
||||
presentation_outlines.slides[0]
|
||||
.content[:50]
|
||||
.replace("#", "")
|
||||
.replace("/", "")
|
||||
.replace("\\", "")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ from sqlmodel import select
|
|||
from constants.documents import UPLOAD_ACCEPTED_FILE_TYPES
|
||||
from models.presentation_and_path import PresentationPathAndEditPath
|
||||
from models.presentation_from_template import GetPresentationUsingTemplateRequest
|
||||
from models.presentation_outline_model import PresentationOutlineModel
|
||||
from models.presentation_outline_model import (
|
||||
PresentationOutlineModel,
|
||||
SlideOutlineModel,
|
||||
)
|
||||
from models.pptx_models import PptxPresentationModel
|
||||
from models.presentation_layout import PresentationLayoutModel
|
||||
from models.presentation_structure_model import PresentationStructureModel
|
||||
|
|
@ -126,7 +129,7 @@ async def create_presentation(
|
|||
@PRESENTATION_ROUTER.post("/prepare", response_model=PresentationModel)
|
||||
async def prepare_presentation(
|
||||
presentation_id: Annotated[str, Body()],
|
||||
outlines: Annotated[List[str], Body()],
|
||||
outlines: Annotated[List[SlideOutlineModel], Body()],
|
||||
layout: Annotated[PresentationLayoutModel, Body()],
|
||||
title: Annotated[Optional[str], Body()] = None,
|
||||
sql_session: AsyncSession = Depends(get_async_session),
|
||||
|
|
@ -161,7 +164,9 @@ async def prepare_presentation(
|
|||
presentation_structure.slides[index] = random_slide_index
|
||||
|
||||
sql_session.add(presentation)
|
||||
presentation.outlines = PresentationOutlineModel(slides=outlines).model_dump()
|
||||
presentation.outlines = PresentationOutlineModel(slides=outlines).model_dump(
|
||||
mode="json"
|
||||
)
|
||||
presentation.title = title or presentation.title
|
||||
presentation.set_layout(layout)
|
||||
presentation.set_structure(presentation_structure)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from pydantic import BaseModel
|
||||
|
||||
from models.presentation_outline_model import SlideOutlineModel
|
||||
|
||||
|
||||
class DocumentChunk(BaseModel):
|
||||
heading: str
|
||||
|
|
@ -7,5 +9,5 @@ class DocumentChunk(BaseModel):
|
|||
heading_index: int
|
||||
score: float
|
||||
|
||||
def to_slide_outline(self) -> str:
|
||||
return f"{self.heading}\n{self.content}"
|
||||
def to_slide_outline(self) -> SlideOutlineModel:
|
||||
return SlideOutlineModel(content=f"{self.heading}\n{self.content}")
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ from typing import List
|
|||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SlideOutlineModel(BaseModel):
|
||||
content: str
|
||||
|
||||
|
||||
class PresentationOutlineModel(BaseModel):
|
||||
slides: List[str]
|
||||
slides: List[SlideOutlineModel]
|
||||
|
||||
def to_string(self):
|
||||
message = ""
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ class LLMClient:
|
|||
|
||||
# ? Disable thinking
|
||||
def disable_thinking(self) -> bool:
|
||||
if self.llm_provider != LLMProvider.CUSTOM:
|
||||
return False
|
||||
return parse_bool_or_none(get_disable_thinking_env()) or False
|
||||
|
||||
# ? Clients
|
||||
|
|
@ -122,15 +120,14 @@ class LLMClient:
|
|||
model: str,
|
||||
messages: List[LLMMessage],
|
||||
max_tokens: Optional[int] = None,
|
||||
extra_body: Optional[dict] = None,
|
||||
):
|
||||
client: AsyncOpenAI = self._client
|
||||
response = await client.chat.completions.create(
|
||||
model=model,
|
||||
messages=[message.model_dump() for message in messages],
|
||||
max_completion_tokens=max_tokens,
|
||||
extra_body={
|
||||
"enable_thinking": not self.disable_thinking(),
|
||||
},
|
||||
extra_body=extra_body,
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
|
||||
|
|
@ -185,7 +182,10 @@ class LLMClient:
|
|||
async def _generate_custom(
|
||||
self, model: str, messages: List[LLMMessage], max_tokens: Optional[int] = None
|
||||
):
|
||||
return await self._generate_openai(model, messages, max_tokens)
|
||||
extra_body = {"enable_thinking": not self.disable_thinking()}
|
||||
return await self._generate_openai(
|
||||
model, messages, max_tokens, extra_body=extra_body
|
||||
)
|
||||
|
||||
async def generate(
|
||||
self,
|
||||
|
|
@ -220,6 +220,7 @@ class LLMClient:
|
|||
response_format: dict,
|
||||
strict: bool = False,
|
||||
max_tokens: Optional[int] = None,
|
||||
extra_body: Optional[dict] = None,
|
||||
):
|
||||
client: AsyncOpenAI = self._client
|
||||
use_tool_calls = self.use_tool_calls()
|
||||
|
|
@ -245,9 +246,7 @@ class LLMClient:
|
|||
),
|
||||
},
|
||||
max_completion_tokens=max_tokens,
|
||||
extra_body={
|
||||
"enable_thinking": not self.disable_thinking(),
|
||||
},
|
||||
extra_body=extra_body,
|
||||
)
|
||||
content = response.choices[0].message.content
|
||||
else:
|
||||
|
|
@ -267,9 +266,7 @@ class LLMClient:
|
|||
],
|
||||
tool_choice="required",
|
||||
max_completion_tokens=max_tokens,
|
||||
extra_body={
|
||||
"enable_thinking": not self.disable_thinking(),
|
||||
},
|
||||
extra_body=extra_body,
|
||||
)
|
||||
tool_calls = response.choices[0].message.tool_calls
|
||||
if tool_calls:
|
||||
|
|
@ -359,8 +356,9 @@ class LLMClient:
|
|||
strict: bool = False,
|
||||
max_tokens: Optional[int] = None,
|
||||
):
|
||||
extra_body = {"enable_thinking": not self.disable_thinking()}
|
||||
return await self._generate_openai_structured(
|
||||
model, messages, response_format, strict, max_tokens
|
||||
model, messages, response_format, strict, max_tokens, extra_body
|
||||
)
|
||||
|
||||
async def generate_structured(
|
||||
|
|
@ -406,15 +404,14 @@ class LLMClient:
|
|||
model: str,
|
||||
messages: List[LLMMessage],
|
||||
max_tokens: Optional[int] = None,
|
||||
extra_body: Optional[dict] = None,
|
||||
):
|
||||
client: AsyncOpenAI = self._client
|
||||
async with client.chat.completions.stream(
|
||||
model=model,
|
||||
messages=[message.model_dump() for message in messages],
|
||||
max_completion_tokens=max_tokens,
|
||||
extra_body={
|
||||
"enable_thinking": not self.disable_thinking(),
|
||||
},
|
||||
extra_body=extra_body,
|
||||
) as stream:
|
||||
async for event in stream:
|
||||
if event.type == "content.delta":
|
||||
|
|
@ -474,7 +471,8 @@ class LLMClient:
|
|||
messages: List[LLMMessage],
|
||||
max_tokens: Optional[int] = None,
|
||||
):
|
||||
return self._stream_openai(model, messages, max_tokens)
|
||||
extra_body = {"enable_thinking": not self.disable_thinking()}
|
||||
return self._stream_openai(model, messages, max_tokens, extra_body)
|
||||
|
||||
def stream(
|
||||
self, model: str, messages: List[LLMMessage], max_tokens: Optional[int] = None
|
||||
|
|
@ -499,6 +497,7 @@ class LLMClient:
|
|||
response_format: dict,
|
||||
strict: bool = False,
|
||||
max_tokens: Optional[int] = None,
|
||||
extra_body: Optional[dict] = None,
|
||||
):
|
||||
client: AsyncOpenAI = self._client
|
||||
use_tool_calls = self.use_tool_calls()
|
||||
|
|
@ -524,9 +523,7 @@ class LLMClient:
|
|||
},
|
||||
}
|
||||
),
|
||||
extra_body={
|
||||
"enable_thinking": not self.disable_thinking(),
|
||||
},
|
||||
extra_body=extra_body,
|
||||
) as stream:
|
||||
async for event in stream:
|
||||
if event.type == "content.delta":
|
||||
|
|
@ -548,9 +545,7 @@ class LLMClient:
|
|||
}
|
||||
],
|
||||
tool_choice="required",
|
||||
extra_body={
|
||||
"enable_thinking": not self.disable_thinking(),
|
||||
},
|
||||
extra_body=extra_body,
|
||||
) as stream:
|
||||
async for event in stream:
|
||||
if event.type == "tool_calls.function.arguments.delta":
|
||||
|
|
@ -630,8 +625,9 @@ class LLMClient:
|
|||
strict: bool = False,
|
||||
max_tokens: Optional[int] = None,
|
||||
):
|
||||
extra_body = {"enable_thinking": not self.disable_thinking()}
|
||||
return self._stream_openai_structured(
|
||||
model, messages, response_format, strict, max_tokens
|
||||
model, messages, response_format, strict, max_tokens, extra_body
|
||||
)
|
||||
|
||||
def stream_structured(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
from typing import List
|
||||
from pydantic import Field
|
||||
from models.presentation_outline_model import PresentationOutlineModel
|
||||
from models.presentation_outline_model import (
|
||||
PresentationOutlineModel,
|
||||
SlideOutlineModel,
|
||||
)
|
||||
from models.presentation_structure_model import PresentationStructureModel
|
||||
|
||||
|
||||
def get_presentation_outline_model_with_n_slides(n_slides: int):
|
||||
class SlideOutlineModelWithNSlides(SlideOutlineModel):
|
||||
content: str = Field(
|
||||
description="Markdown content for each slide",
|
||||
min_length=100,
|
||||
max_length=300,
|
||||
)
|
||||
|
||||
class PresentationOutlineModelWithNSlides(PresentationOutlineModel):
|
||||
slides: List[str] = Field(
|
||||
description="Markdown content for each slide in about 100 to 200 words",
|
||||
slides: List[SlideOutlineModelWithNSlides] = Field(
|
||||
description="List of slide outlines",
|
||||
min_items=n_slides,
|
||||
max_items=n_slides,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import asyncio
|
||||
from typing import Optional
|
||||
|
||||
from models.llm_message import LLMMessage
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from models.llm_message import LLMMessage
|
||||
from models.presentation_layout import SlideLayoutModel
|
||||
from models.presentation_outline_model import SlideOutlineModel
|
||||
from services.llm_client import LLMClient
|
||||
from utils.llm_provider import get_model
|
||||
from utils.schema_utils import remove_fields_from_schema
|
||||
|
|
@ -50,7 +51,7 @@ def get_messages(outline: str, language: str):
|
|||
|
||||
|
||||
async def get_slide_content_from_type_and_outline(
|
||||
slide_layout: SlideLayoutModel, outline: str, language: str
|
||||
slide_layout: SlideLayoutModel, outline: SlideOutlineModel, language: str
|
||||
):
|
||||
client = LLMClient()
|
||||
model = get_model()
|
||||
|
|
@ -62,7 +63,7 @@ async def get_slide_content_from_type_and_outline(
|
|||
response = await client.generate_structured(
|
||||
model=model,
|
||||
messages=get_messages(
|
||||
outline,
|
||||
outline.content,
|
||||
language,
|
||||
),
|
||||
response_format=response_schema,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { LoadingState, StreamState, LayoutGroup } from "../types/index";
|
||||
import { LoadingState, LayoutGroup } from "../types/index";
|
||||
|
||||
interface GenerateButtonProps {
|
||||
loadingState: LoadingState;
|
||||
streamState: StreamState;
|
||||
streamState: { isStreaming: boolean, isLoading: boolean };
|
||||
selectedLayoutGroup: LayoutGroup | null;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { FileText } from "lucide-react";
|
||||
|
||||
interface OutlineContentProps {
|
||||
outlines: string[] | null;
|
||||
outlines: { content: string }[] | null;
|
||||
isLoading: boolean;
|
||||
isStreaming: boolean;
|
||||
onDragEnd: (event: any) => void;
|
||||
|
|
@ -32,7 +32,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
|
|||
onDragEnd,
|
||||
onAddSlide
|
||||
}) => {
|
||||
|
||||
console.log('isLoading', isLoading)
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
|
|
@ -83,7 +83,18 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
|
|||
collisionDetection={closestCenter}
|
||||
onDragEnd={onDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
{isStreaming ? (
|
||||
|
||||
outlines.map((item, index) => (
|
||||
<OutlineItem
|
||||
key={`slide-${index}`}
|
||||
index={index + 1}
|
||||
slideOutline={item}
|
||||
isStreaming={isStreaming}
|
||||
/>
|
||||
))
|
||||
) :
|
||||
<SortableContext
|
||||
items={outlines?.map((item, index) => ({ id: `slide-${index}` })) || []}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
|
|
@ -95,7 +106,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
|
|||
isStreaming={isStreaming}
|
||||
/>
|
||||
))}
|
||||
</SortableContext>
|
||||
</SortableContext>}
|
||||
</DndContext>
|
||||
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import { useEffect } from "react"
|
|||
|
||||
|
||||
interface OutlineItemProps {
|
||||
slideOutline: string,
|
||||
slideOutline: {
|
||||
content: string,
|
||||
},
|
||||
index: number
|
||||
isStreaming: boolean
|
||||
}
|
||||
|
|
@ -38,7 +40,7 @@ export function OutlineItem({
|
|||
}
|
||||
}, [outlines.length]);
|
||||
|
||||
const handleSlideChange = (newOutline: string) => {
|
||||
const handleSlideChange = (newOutline:any) => {
|
||||
if (isStreaming) return;
|
||||
const newData = outlines?.map((each, idx) => {
|
||||
if (idx === index - 1) {
|
||||
|
|
@ -100,10 +102,10 @@ export function OutlineItem({
|
|||
{isStreaming ? <p
|
||||
className="text-sm flex-1 font-normal"
|
||||
>
|
||||
{slideOutline || ''}
|
||||
{slideOutline.content || ''}
|
||||
</p> : <MarkdownEditor
|
||||
key={index}
|
||||
content={slideOutline || ''}
|
||||
content={slideOutline.content || ''}
|
||||
onChange={(content) => handleSlideChange(content)}
|
||||
/>}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
const PageHeader: React.FC = () => (
|
||||
<div className="mb-8">
|
||||
{/* <h4 className="text-2xl font-bold mb-2 text-gray-900">
|
||||
Customize Your Presentation
|
||||
</h4> */}
|
||||
{/* <p className="text-gray-600">
|
||||
Review your outline and select a layout style for your presentation.
|
||||
</p> */}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PageHeader;
|
||||
|
|
@ -3,7 +3,7 @@ import { useDispatch } from "react-redux";
|
|||
import { arrayMove } from "@dnd-kit/sortable";
|
||||
import { setOutlines } from "@/store/slices/presentationGeneration";
|
||||
|
||||
export const useOutlineManagement = (outlines: string[] | null) => {
|
||||
export const useOutlineManagement = (outlines: { content: string }[] | null) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleDragEnd = useCallback((event: any) => {
|
||||
|
|
@ -12,8 +12,8 @@ export const useOutlineManagement = (outlines: string[] | null) => {
|
|||
if (!active || !over || !outlines) return;
|
||||
|
||||
if (active.id !== over.id) {
|
||||
const oldIndex = outlines.findIndex((item) => item === active.id);
|
||||
const newIndex = outlines.findIndex((item) => item === over.id);
|
||||
const oldIndex = outlines.findIndex((item) => item.content === active.id);
|
||||
const newIndex = outlines.findIndex((item) => item.content === over.id);
|
||||
const reorderedArray = arrayMove(outlines, oldIndex, newIndex);
|
||||
dispatch(setOutlines(reorderedArray));
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ export const useOutlineManagement = (outlines: string[] | null) => {
|
|||
const handleAddSlide = useCallback(() => {
|
||||
if (!outlines) return;
|
||||
|
||||
const updatedOutlines = [...outlines, "Outline title"];
|
||||
const updatedOutlines = [...outlines, { content: "Outline title" }];
|
||||
dispatch(setOutlines(updatedOutlines));
|
||||
}, [outlines, dispatch]);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,15 @@ import { useDispatch, useSelector } from "react-redux";
|
|||
import { toast } from "sonner";
|
||||
import { setOutlines } from "@/store/slices/presentationGeneration";
|
||||
import { jsonrepair } from "jsonrepair";
|
||||
import { StreamState } from "../types/index";
|
||||
import { RootState } from "@/store/store";
|
||||
|
||||
const DEFAULT_STREAM_STATE: StreamState = {
|
||||
isStreaming: false,
|
||||
isLoading: true,
|
||||
};
|
||||
|
||||
|
||||
export const useOutlineStreaming = (presentationId: string | null) => {
|
||||
const dispatch = useDispatch();
|
||||
const { outlines } = useSelector((state: RootState) => state.presentationGeneration);
|
||||
const [streamState, setStreamState] = useState<StreamState>(DEFAULT_STREAM_STATE);
|
||||
const [isStreaming, setIsStreaming] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!presentationId || outlines.length > 0) return;
|
||||
|
|
@ -23,8 +20,8 @@ export const useOutlineStreaming = (presentationId: string | null) => {
|
|||
let accumulatedChunks = "";
|
||||
|
||||
const initializeStream = async () => {
|
||||
setStreamState({ isStreaming: true, isLoading: true });
|
||||
|
||||
setIsStreaming(true)
|
||||
setIsLoading(true)
|
||||
try {
|
||||
eventSource = new EventSource(
|
||||
`/api/v1/ppt/outlines/stream?presentation_id=${presentationId}`
|
||||
|
|
@ -34,13 +31,16 @@ export const useOutlineStreaming = (presentationId: string | null) => {
|
|||
const data = JSON.parse(event.data);
|
||||
switch (data.type) {
|
||||
case "chunk":
|
||||
// console.log('data', data)
|
||||
accumulatedChunks += data.chunk;
|
||||
// console.log('accumulatedChunks', accumulatedChunks)
|
||||
try {
|
||||
const repairedJson = jsonrepair(accumulatedChunks);
|
||||
const partialData = JSON.parse(repairedJson);
|
||||
console.log('partialData', partialData)
|
||||
if (partialData.slides) {
|
||||
dispatch(setOutlines(partialData.slides));
|
||||
setStreamState(prev => ({ ...prev, isLoading: false }));
|
||||
setIsLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
// JSON isn't complete yet, continue accumulating
|
||||
|
|
@ -48,11 +48,13 @@ export const useOutlineStreaming = (presentationId: string | null) => {
|
|||
break;
|
||||
|
||||
case "complete":
|
||||
console.log('complete', data)
|
||||
try {
|
||||
const outlinesData: string[] = data.presentation.outlines.slides;
|
||||
const outlinesData: { content: string }[] = data.presentation.outlines.slides;
|
||||
dispatch(setOutlines(outlinesData));
|
||||
setStreamState({ isStreaming: false, isLoading: false });
|
||||
eventSource.close();
|
||||
setIsStreaming(false)
|
||||
setIsLoading(false)
|
||||
eventSource.close();
|
||||
} catch (error) {
|
||||
console.error("Error parsing accumulated chunks:", error);
|
||||
toast.error("Failed to parse presentation data");
|
||||
|
|
@ -62,11 +64,15 @@ export const useOutlineStreaming = (presentationId: string | null) => {
|
|||
break;
|
||||
|
||||
case "closing":
|
||||
setStreamState({ isStreaming: false, isLoading: false });
|
||||
console.log('closing', data)
|
||||
setIsStreaming(false)
|
||||
setIsLoading(false)
|
||||
eventSource.close();
|
||||
break;
|
||||
case "error":
|
||||
setStreamState({ isStreaming: false, isLoading: false });
|
||||
console.log('error', data)
|
||||
setIsStreaming(false)
|
||||
setIsLoading(false)
|
||||
eventSource.close();
|
||||
toast.error('Error in outline streaming',
|
||||
{
|
||||
|
|
@ -78,18 +84,20 @@ export const useOutlineStreaming = (presentationId: string | null) => {
|
|||
});
|
||||
|
||||
eventSource.onerror = () => {
|
||||
setStreamState({ isStreaming: false, isLoading: false });
|
||||
console.log('onerror')
|
||||
setIsStreaming(false)
|
||||
setIsLoading(false)
|
||||
eventSource.close();
|
||||
toast.error("Failed to connect to the server. Please try again.");
|
||||
};
|
||||
} catch (error) {
|
||||
setStreamState({ isStreaming: false, isLoading: false });
|
||||
console.log('error', error)
|
||||
setIsStreaming(false)
|
||||
setIsLoading(false)
|
||||
toast.error("Failed to initialize connection");
|
||||
}finally{
|
||||
setStreamState({ isStreaming: false, isLoading: false });
|
||||
}
|
||||
};
|
||||
initializeStream();
|
||||
initializeStream();
|
||||
return () => {
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
|
|
@ -97,5 +105,5 @@ export const useOutlineStreaming = (presentationId: string | null) => {
|
|||
};
|
||||
}, [presentationId, dispatch]);
|
||||
|
||||
return streamState;
|
||||
return { isStreaming, isLoading };
|
||||
};
|
||||
|
|
@ -15,7 +15,7 @@ const DEFAULT_LOADING_STATE: LoadingState = {
|
|||
|
||||
export const usePresentationGeneration = (
|
||||
presentationId: string | null,
|
||||
outlines: string[] | null,
|
||||
outlines: { content: string }[] | null,
|
||||
selectedLayoutGroup: LayoutGroup | null,
|
||||
setActiveTab: (tab: string) => void
|
||||
) => {
|
||||
|
|
|
|||
|
|
@ -14,10 +14,7 @@ export interface LoadingState {
|
|||
duration: number;
|
||||
}
|
||||
|
||||
export interface StreamState {
|
||||
isStreaming: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
|
||||
export const TABS = {
|
||||
OUTLINE: 'outline',
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ const UploadPage = () => {
|
|||
config,
|
||||
files: responses,
|
||||
}));
|
||||
dispatch(clearOutlines());
|
||||
dispatch(clearOutlines())
|
||||
router.push("/documents-preview");
|
||||
};
|
||||
|
||||
|
|
@ -155,7 +155,7 @@ const UploadPage = () => {
|
|||
});
|
||||
|
||||
dispatch(setPresentationId(createResponse.id));
|
||||
dispatch(clearOutlines());
|
||||
dispatch(clearOutlines())
|
||||
router.push("/outline");
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ interface PresentationGenerationState {
|
|||
presentation_id: string | null;
|
||||
isLoading: boolean;
|
||||
isStreaming: boolean | null;
|
||||
outlines: string[];
|
||||
outlines: { content: string }[];
|
||||
error: string | null;
|
||||
presentationData: PresentationData | null;
|
||||
isSlidesRendered: boolean;
|
||||
|
|
@ -72,7 +72,7 @@ const presentationGenerationSlice = createSlice({
|
|||
state.outlines = [];
|
||||
},
|
||||
// Set outlines
|
||||
setOutlines: (state, action: PayloadAction<string[]>) => {
|
||||
setOutlines: (state, action: PayloadAction<{ content: string }[]>) => {
|
||||
state.outlines = action.payload;
|
||||
},
|
||||
// Set presentation data
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue