feat: adds tone, verbosity and refactors tables
This commit is contained in:
parent
5ec4144f9f
commit
614948887f
33 changed files with 333 additions and 148 deletions
|
|
@ -7,7 +7,7 @@ from constants.documents import UPLOAD_ACCEPTED_FILE_TYPES
|
|||
from models.decomposed_file_info import DecomposedFileInfo
|
||||
from services.temp_file_service import TEMP_FILE_SERVICE
|
||||
from services.documents_loader import DocumentsLoader
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
from utils.validators import validate_files
|
||||
|
||||
FILES_ROUTER = APIRouter(prefix="/files", tags=["Files"])
|
||||
|
|
@ -18,7 +18,7 @@ async def upload_files(files: Optional[List[UploadFile]]):
|
|||
if not files:
|
||||
raise HTTPException(400, "Documents are required")
|
||||
|
||||
temp_dir = TEMP_FILE_SERVICE.create_temp_dir(get_random_uuid())
|
||||
temp_dir = TEMP_FILE_SERVICE.create_temp_dir(str(uuid.uuid4()))
|
||||
|
||||
validate_files(files, True, True, 100, UPLOAD_ACCEPTED_FILE_TYPES)
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ async def upload_files(files: Optional[List[UploadFile]]):
|
|||
|
||||
@FILES_ROUTER.post("/decompose", response_model=List[DecomposedFileInfo])
|
||||
async def decompose_files(file_paths: Annotated[List[str], Body(embed=True)]):
|
||||
temp_dir = TEMP_FILE_SERVICE.create_temp_dir(get_random_uuid())
|
||||
temp_dir = TEMP_FILE_SERVICE.create_temp_dir(str(uuid.uuid4()))
|
||||
|
||||
txt_files = []
|
||||
other_files = []
|
||||
|
|
@ -56,7 +56,7 @@ async def decompose_files(file_paths: Annotated[List[str], Body(embed=True)]):
|
|||
response = []
|
||||
for index, parsed_doc in enumerate(parsed_documents):
|
||||
file_path = TEMP_FILE_SERVICE.create_temp_file_path(
|
||||
f"{get_random_uuid()}.txt", temp_dir
|
||||
f"{uuid.uuid4()}.txt", temp_dir
|
||||
)
|
||||
parsed_doc = parsed_doc.replace("<br>", "\n")
|
||||
with open(file_path, "w") as text_file:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from typing import List, Dict, Any, Optional
|
|||
from fastapi import APIRouter, HTTPException, File, UploadFile
|
||||
from pydantic import BaseModel
|
||||
from utils.asset_directory_utils import get_app_data_directory_env
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
try:
|
||||
from fontTools.ttLib import TTFont
|
||||
|
|
@ -157,7 +157,7 @@ async def upload_font(
|
|||
# Generate unique filename to avoid conflicts
|
||||
file_ext = os.path.splitext(font_file.filename)[1].lower()
|
||||
base_name = os.path.splitext(font_file.filename)[0]
|
||||
unique_filename = f"{base_name}_{get_random_uuid()[:8]}{file_ext}"
|
||||
unique_filename = f"{base_name}_{str(uuid.uuid4())[:8]}{file_ext}"
|
||||
|
||||
# Get fonts directory
|
||||
fonts_dir = get_fonts_directory()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import asyncio
|
||||
import json
|
||||
import uuid
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
|
@ -18,7 +19,7 @@ OUTLINES_ROUTER = APIRouter(prefix="/outlines", tags=["Outlines"])
|
|||
|
||||
@OUTLINES_ROUTER.get("/stream")
|
||||
async def stream_outlines(
|
||||
presentation_id: str, sql_session: AsyncSession = Depends(get_async_session)
|
||||
presentation_id: uuid.UUID, sql_session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
presentation = await sql_session.get(PresentationModel, presentation_id)
|
||||
|
||||
|
|
@ -59,11 +60,13 @@ async def stream_outlines(
|
|||
if not presentation_outlines:
|
||||
presentation_outlines_text = ""
|
||||
async for chunk in generate_ppt_outline(
|
||||
presentation.prompt,
|
||||
presentation.content,
|
||||
presentation.n_slides,
|
||||
presentation.language,
|
||||
additional_context,
|
||||
presentation.instruction,
|
||||
presentation.tone,
|
||||
presentation.verbosity,
|
||||
presentation.instructions,
|
||||
):
|
||||
# Give control to the event loop
|
||||
await asyncio.sleep(0)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from fastapi import APIRouter, UploadFile, File, HTTPException
|
|||
from pydantic import BaseModel
|
||||
|
||||
from utils.asset_directory_utils import get_images_directory
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
from constants.documents import PDF_MIME_TYPES
|
||||
|
||||
|
||||
|
|
@ -68,8 +68,8 @@ async def process_pdf_slides(
|
|||
|
||||
# Move screenshots to images directory and generate URLs
|
||||
images_dir = get_images_directory()
|
||||
presentation_id = get_random_uuid()
|
||||
presentation_images_dir = os.path.join(images_dir, presentation_id)
|
||||
presentation_id = uuid.uuid4()
|
||||
presentation_images_dir = os.path.join(images_dir, str(presentation_id))
|
||||
os.makedirs(presentation_images_dir, exist_ok=True)
|
||||
|
||||
slides_data = []
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import xml.etree.ElementTree as ET
|
|||
import re
|
||||
|
||||
from utils.asset_directory_utils import get_images_directory
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
from constants.documents import POWERPOINT_TYPES
|
||||
|
||||
|
||||
|
|
@ -308,8 +308,8 @@ async def process_pptx_slides(
|
|||
|
||||
# Move screenshots to images directory and generate URLs
|
||||
images_dir = get_images_directory()
|
||||
presentation_id = get_random_uuid()
|
||||
presentation_images_dir = os.path.join(images_dir, presentation_id)
|
||||
presentation_id = uuid.uuid4()
|
||||
presentation_images_dir = os.path.join(images_dir, str(presentation_id))
|
||||
os.makedirs(presentation_images_dir, exist_ok=True)
|
||||
|
||||
slides_data = []
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ from models.presentation_outline_model import (
|
|||
from models.pptx_models import PptxPresentationModel
|
||||
from models.presentation_layout import PresentationLayoutModel
|
||||
from models.presentation_structure_model import PresentationStructureModel
|
||||
from models.presentation_with_slides import PresentationWithSlides
|
||||
from models.presentation_with_slides import (
|
||||
PresentationWithSlides,
|
||||
)
|
||||
|
||||
from services.documents_loader import DocumentsLoader
|
||||
from services.score_based_chunker import ScoreBasedChunker
|
||||
|
|
@ -45,7 +47,7 @@ from utils.process_slides import (
|
|||
process_slide_add_placeholder_assets,
|
||||
process_slide_and_fetch_assets,
|
||||
)
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
|
||||
PRESENTATION_ROUTER = APIRouter(prefix="/presentation", tags=["Presentation"])
|
||||
|
|
@ -53,7 +55,7 @@ PRESENTATION_ROUTER = APIRouter(prefix="/presentation", tags=["Presentation"])
|
|||
|
||||
@PRESENTATION_ROUTER.get("", response_model=PresentationWithSlides)
|
||||
async def get_presentation(
|
||||
id: str, sql_session: AsyncSession = Depends(get_async_session)
|
||||
id: uuid.UUID, sql_session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
presentation = await sql_session.get(PresentationModel, id)
|
||||
if not presentation:
|
||||
|
|
@ -71,7 +73,7 @@ async def get_presentation(
|
|||
|
||||
@PRESENTATION_ROUTER.delete("", status_code=204)
|
||||
async def delete_presentation(
|
||||
id: str, sql_session: AsyncSession = Depends(get_async_session)
|
||||
id: uuid.UUID, sql_session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
presentation = await sql_session.get(PresentationModel, id)
|
||||
if not presentation:
|
||||
|
|
@ -112,10 +114,12 @@ async def create_presentation(
|
|||
n_slides: Annotated[int, Body()],
|
||||
language: Annotated[str, Body()],
|
||||
file_paths: Annotated[Optional[List[str]], Body()] = None,
|
||||
instruction: Annotated[Optional[str], Body()] = None,
|
||||
tone: Annotated[Optional[str], Body()] = None,
|
||||
verbosity: Annotated[Optional[str], Body()] = None,
|
||||
instructions: Annotated[Optional[str], Body()] = None,
|
||||
sql_session: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
presentation_id = get_random_uuid()
|
||||
presentation_id = uuid.uuid4()
|
||||
|
||||
presentation = PresentationModel(
|
||||
id=presentation_id,
|
||||
|
|
@ -123,7 +127,9 @@ async def create_presentation(
|
|||
n_slides=n_slides,
|
||||
language=language,
|
||||
file_paths=file_paths,
|
||||
instruction=instruction,
|
||||
tone=tone,
|
||||
verbosity=verbosity,
|
||||
instructions=instructions,
|
||||
)
|
||||
|
||||
sql_session.add(presentation)
|
||||
|
|
@ -134,7 +140,7 @@ async def create_presentation(
|
|||
|
||||
@PRESENTATION_ROUTER.post("/prepare", response_model=PresentationModel)
|
||||
async def prepare_presentation(
|
||||
presentation_id: Annotated[str, Body()],
|
||||
presentation_id: Annotated[uuid.UUID, Body()],
|
||||
outlines: Annotated[List[SlideOutlineModel], Body()],
|
||||
layout: Annotated[PresentationLayoutModel, Body()],
|
||||
title: Annotated[Optional[str], Body()] = None,
|
||||
|
|
@ -159,7 +165,7 @@ async def prepare_presentation(
|
|||
await generate_presentation_structure(
|
||||
presentation_outline=presentation_outline_model,
|
||||
presentation_layout=layout,
|
||||
instruction=presentation.instruction,
|
||||
instructions=presentation.instructions,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -184,7 +190,7 @@ async def prepare_presentation(
|
|||
|
||||
@PRESENTATION_ROUTER.get("/stream", response_model=PresentationWithSlides)
|
||||
async def stream_presentation(
|
||||
presentation_id: str, sql_session: AsyncSession = Depends(get_async_session)
|
||||
presentation_id: uuid.UUID, sql_session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
presentation = await sql_session.get(PresentationModel, presentation_id)
|
||||
if not presentation:
|
||||
|
|
@ -222,7 +228,9 @@ async def stream_presentation(
|
|||
slide_layout,
|
||||
outline.slides[i],
|
||||
presentation.language,
|
||||
presentation.instruction,
|
||||
presentation.tone,
|
||||
presentation.verbosity,
|
||||
presentation.instructions,
|
||||
)
|
||||
|
||||
slide = SlideModel(
|
||||
|
|
@ -283,14 +291,22 @@ async def update_presentation(
|
|||
):
|
||||
updated_presentation = presentation_with_slides.to_presentation_model()
|
||||
updated_slides = presentation_with_slides.slides
|
||||
|
||||
presentation = await sql_session.get(PresentationModel, updated_presentation.id)
|
||||
if not presentation:
|
||||
raise HTTPException(status_code=404, detail="Presentation not found")
|
||||
|
||||
presentation.sqlmodel_update(updated_presentation)
|
||||
|
||||
await sql_session.execute(
|
||||
delete(SlideModel).where(SlideModel.presentation == updated_presentation.id)
|
||||
)
|
||||
|
||||
# Just to make sure id is UUID
|
||||
for slide in updated_slides:
|
||||
slide.presentation = uuid.UUID(slide.presentation)
|
||||
slide.id = uuid.UUID(slide.id)
|
||||
|
||||
sql_session.add_all(updated_slides)
|
||||
await sql_session.commit()
|
||||
|
||||
|
|
@ -311,7 +327,7 @@ async def create_pptx(
|
|||
|
||||
export_directory = get_exports_directory()
|
||||
pptx_path = os.path.join(
|
||||
export_directory, f"{pptx_model.name or get_random_uuid()}.pptx"
|
||||
export_directory, f"{pptx_model.name or uuid.uuid4()}.pptx"
|
||||
)
|
||||
pptx_creator.save(pptx_path)
|
||||
|
||||
|
|
@ -323,7 +339,7 @@ async def generate_presentation_api(
|
|||
request: GeneratePresentationRequest,
|
||||
sql_session: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
presentation_id = get_random_uuid()
|
||||
presentation_id = uuid.uuid4()
|
||||
|
||||
# 3. Generate Outlines
|
||||
presentation_outlines = None
|
||||
|
|
@ -351,7 +367,10 @@ async def generate_presentation_api(
|
|||
request.n_slides,
|
||||
request.language,
|
||||
additional_context,
|
||||
request.instruction,
|
||||
request.tone,
|
||||
request.verbosity,
|
||||
request.instructions,
|
||||
request.web_search,
|
||||
):
|
||||
presentation_outlines_text += chunk
|
||||
|
||||
|
|
@ -383,7 +402,7 @@ async def generate_presentation_api(
|
|||
await generate_presentation_structure(
|
||||
presentation_outlines,
|
||||
layout_model,
|
||||
request.instruction,
|
||||
request.instructions,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -405,7 +424,9 @@ async def generate_presentation_api(
|
|||
outlines=presentation_outlines.model_dump(),
|
||||
layout=layout_model.model_dump(),
|
||||
structure=presentation_structure.model_dump(),
|
||||
instruction=request.instruction,
|
||||
tone=request.tone,
|
||||
verbosity=request.verbosity,
|
||||
instructions=request.instructions,
|
||||
)
|
||||
|
||||
image_generation_service = ImageGenerationService(get_images_directory())
|
||||
|
|
@ -418,7 +439,12 @@ async def generate_presentation_api(
|
|||
slide_layout = layout_model.slides[slide_layout_index]
|
||||
print(f"Generating content for slide {i} with layout {slide_layout.id}")
|
||||
slide_content = await get_slide_content_from_type_and_outline(
|
||||
slide_layout, outlines[i], request.language, request.instruction
|
||||
slide_layout,
|
||||
outlines[i],
|
||||
request.language,
|
||||
request.tone,
|
||||
request.verbosity,
|
||||
request.instructions,
|
||||
)
|
||||
slide = SlideModel(
|
||||
presentation=presentation_id,
|
||||
|
|
@ -447,7 +473,7 @@ async def generate_presentation_api(
|
|||
|
||||
# 9. Export
|
||||
presentation_and_path = await export_presentation(
|
||||
presentation_id, presentation.title or get_random_uuid(), request.export_as
|
||||
presentation_id, presentation.title or str(uuid.uuid4()), request.export_as
|
||||
)
|
||||
|
||||
return PresentationPathAndEditPath(
|
||||
|
|
@ -484,7 +510,7 @@ async def from_template(
|
|||
await sql_session.commit()
|
||||
|
||||
presentation_and_path = await export_presentation(
|
||||
new_presentation.id, new_presentation.title or get_random_uuid(), data.export_as
|
||||
new_presentation.id, new_presentation.title or str(uuid.uuid4()), data.export_as
|
||||
)
|
||||
|
||||
return PresentationPathAndEditPath(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Annotated, Optional
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
import uuid
|
||||
|
||||
from models.sql.presentation import PresentationModel
|
||||
from models.sql.slide import SlideModel
|
||||
|
|
@ -11,7 +12,7 @@ from utils.llm_calls.edit_slide import get_edited_slide_content
|
|||
from utils.llm_calls.edit_slide_html import get_edited_slide_html
|
||||
from utils.llm_calls.select_slide_type_on_edit import get_slide_layout_from_prompt
|
||||
from utils.process_slides import process_old_and_new_slides_and_fetch_assets
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
|
||||
SLIDE_ROUTER = APIRouter(prefix="/slide", tags=["Slide"])
|
||||
|
|
@ -19,7 +20,7 @@ SLIDE_ROUTER = APIRouter(prefix="/slide", tags=["Slide"])
|
|||
|
||||
@SLIDE_ROUTER.post("/edit")
|
||||
async def edit_slide(
|
||||
id: Annotated[str, Body()],
|
||||
id: Annotated[uuid.UUID, Body()],
|
||||
prompt: Annotated[str, Body()],
|
||||
sql_session: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
|
|
@ -49,7 +50,7 @@ async def edit_slide(
|
|||
)
|
||||
|
||||
# Always assign a new unique id to the slide
|
||||
slide.id = get_random_uuid()
|
||||
slide.id = uuid.uuid4()
|
||||
|
||||
sql_session.add(slide)
|
||||
slide.content = edited_slide_content
|
||||
|
|
@ -63,7 +64,7 @@ async def edit_slide(
|
|||
|
||||
@SLIDE_ROUTER.post("/edit-html", response_model=SlideModel)
|
||||
async def edit_slide_html(
|
||||
id: Annotated[str, Body()],
|
||||
id: Annotated[uuid.UUID, Body()],
|
||||
prompt: Annotated[str, Body()],
|
||||
html: Annotated[Optional[str], Body()] = None,
|
||||
sql_session: AsyncSession = Depends(get_async_session),
|
||||
|
|
@ -80,7 +81,7 @@ async def edit_slide_html(
|
|||
|
||||
# Always assign a new unique id to the slide
|
||||
# This is to ensure that the nextjs can track slide updates
|
||||
slide.id = get_random_uuid()
|
||||
slide.id = uuid.uuid4()
|
||||
|
||||
sql_session.add(slide)
|
||||
slide.html_content = edited_slide_html
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import os
|
|||
import base64
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict
|
||||
from uuid import UUID
|
||||
from fastapi import APIRouter, HTTPException, File, UploadFile, Form, Depends
|
||||
from pydantic import BaseModel
|
||||
from openai import OpenAI
|
||||
|
|
@ -54,7 +55,7 @@ class HtmlToReactResponse(BaseModel):
|
|||
|
||||
# Request/Response models for layout management endpoints
|
||||
class LayoutData(BaseModel):
|
||||
presentation_id: str # UUID of the presentation
|
||||
presentation: UUID # UUID of the presentation
|
||||
layout_id: str # Unique identifier for the layout
|
||||
layout_name: str # Display name of the layout
|
||||
layout_code: str # TSX/React component code for the layout
|
||||
|
|
@ -80,7 +81,7 @@ class GetLayoutsResponse(BaseModel):
|
|||
|
||||
|
||||
class PresentationSummary(BaseModel):
|
||||
presentation_id: str
|
||||
presentation_id: UUID
|
||||
layout_count: int
|
||||
last_updated_at: Optional[datetime] = None
|
||||
template: Optional[dict] = None
|
||||
|
|
@ -101,7 +102,7 @@ class ErrorResponse(BaseModel):
|
|||
|
||||
|
||||
class TemplateCreateRequest(BaseModel):
|
||||
id: str
|
||||
id: UUID
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
|
||||
|
|
@ -113,7 +114,7 @@ class TemplateCreateResponse(BaseModel):
|
|||
|
||||
|
||||
class TemplateInfo(BaseModel):
|
||||
id: str
|
||||
id: UUID
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
created_at: Optional[datetime] = None
|
||||
|
|
@ -688,7 +689,7 @@ async def save_layouts(
|
|||
|
||||
for i, layout_data in enumerate(request.layouts):
|
||||
# Validate individual layout data
|
||||
if not layout_data.presentation_id or not layout_data.presentation_id.strip():
|
||||
if not layout_data.presentation or not str(layout_data.presentation).strip():
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Layout {i+1}: presentation_id cannot be empty"
|
||||
|
|
@ -714,7 +715,7 @@ async def save_layouts(
|
|||
|
||||
# Check if layout already exists for this presentation and layout_id
|
||||
stmt = select(PresentationLayoutCodeModel).where(
|
||||
PresentationLayoutCodeModel.presentation_id == layout_data.presentation_id,
|
||||
PresentationLayoutCodeModel.presentation == layout_data.presentation,
|
||||
PresentationLayoutCodeModel.layout_id == layout_data.layout_id
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
|
|
@ -729,7 +730,7 @@ async def save_layouts(
|
|||
else:
|
||||
# Create new layout
|
||||
new_layout = PresentationLayoutCodeModel(
|
||||
presentation_id=layout_data.presentation_id,
|
||||
presentation=layout_data.presentation,
|
||||
layout_id=layout_data.layout_id,
|
||||
layout_name=layout_data.layout_name,
|
||||
layout_code=layout_data.layout_code,
|
||||
|
|
@ -762,7 +763,7 @@ async def save_layouts(
|
|||
|
||||
# ENDPOINT 5: Get layouts for a presentation
|
||||
@LAYOUT_MANAGEMENT_ROUTER.get(
|
||||
"/get-templates/{presentation_id}",
|
||||
"/get-templates/{presentation}",
|
||||
response_model=GetLayoutsResponse,
|
||||
responses={
|
||||
400: {"model": ErrorResponse, "description": "Invalid presentation ID"},
|
||||
|
|
@ -771,14 +772,14 @@ async def save_layouts(
|
|||
}
|
||||
)
|
||||
async def get_layouts(
|
||||
presentation_id: str,
|
||||
presentation: UUID,
|
||||
session: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
"""
|
||||
Retrieve all layouts for a specific presentation.
|
||||
|
||||
Args:
|
||||
presentation_id: UUID of the presentation
|
||||
presentation: UUID of the presentation
|
||||
session: Database session
|
||||
|
||||
Returns:
|
||||
|
|
@ -789,7 +790,7 @@ async def get_layouts(
|
|||
"""
|
||||
try:
|
||||
# Validate presentation_id format (basic UUID check)
|
||||
if not presentation_id or len(presentation_id.strip()) == 0:
|
||||
if not presentation or len(str(presentation).strip()) == 0:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Presentation ID cannot be empty"
|
||||
|
|
@ -797,7 +798,7 @@ async def get_layouts(
|
|||
|
||||
# Query layouts for the given presentation_id
|
||||
stmt = select(PresentationLayoutCodeModel).where(
|
||||
PresentationLayoutCodeModel.presentation_id == presentation_id
|
||||
PresentationLayoutCodeModel.presentation == presentation
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
layouts_db = result.scalars().all()
|
||||
|
|
@ -806,13 +807,13 @@ async def get_layouts(
|
|||
if not layouts_db:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"No layouts found for presentation ID: {presentation_id}"
|
||||
detail=f"No layouts found for presentation ID: {presentation}"
|
||||
)
|
||||
|
||||
# Convert to response format
|
||||
layouts = [
|
||||
LayoutData(
|
||||
presentation_id=layout.presentation_id,
|
||||
presentation=layout.presentation,
|
||||
layout_id=layout.layout_id,
|
||||
layout_name=layout.layout_name,
|
||||
layout_code=layout.layout_code,
|
||||
|
|
@ -829,7 +830,7 @@ async def get_layouts(
|
|||
fonts_list = sorted(list(aggregated_fonts)) if aggregated_fonts else None
|
||||
|
||||
# Fetch template meta
|
||||
template_meta = await session.get(TemplateModel, presentation_id)
|
||||
template_meta = await session.get(TemplateModel, presentation)
|
||||
template = None
|
||||
if template_meta:
|
||||
template = {
|
||||
|
|
@ -842,7 +843,7 @@ async def get_layouts(
|
|||
return GetLayoutsResponse(
|
||||
success=True,
|
||||
layouts=layouts,
|
||||
message=f"Retrieved {len(layouts)} layout(s) for presentation {presentation_id}",
|
||||
message=f"Retrieved {len(layouts)} layout(s) for presentation {presentation}",
|
||||
template=template,
|
||||
fonts=fonts_list,
|
||||
)
|
||||
|
|
@ -851,7 +852,7 @@ async def get_layouts(
|
|||
# Re-raise HTTP exceptions as-is
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Error retrieving layouts for presentation {presentation_id}: {str(e)}")
|
||||
print(f"Error retrieving layouts for presentation {presentation}: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Internal server error while retrieving layouts: {str(e)}"
|
||||
|
|
@ -878,10 +879,10 @@ async def get_presentations_summary(
|
|||
try:
|
||||
# Query to get presentation_id, count of layouts, and MAX(updated_at)
|
||||
stmt = select(
|
||||
PresentationLayoutCodeModel.presentation_id,
|
||||
PresentationLayoutCodeModel.presentation,
|
||||
func.count(PresentationLayoutCodeModel.id).label('layout_count'),
|
||||
func.max(PresentationLayoutCodeModel.updated_at).label('last_updated_at')
|
||||
).group_by(PresentationLayoutCodeModel.presentation_id)
|
||||
).group_by(PresentationLayoutCodeModel.presentation)
|
||||
|
||||
result = await session.execute(stmt)
|
||||
presentation_data = result.all()
|
||||
|
|
@ -889,7 +890,7 @@ async def get_presentations_summary(
|
|||
# Convert to response format with template info if available
|
||||
presentations = []
|
||||
for row in presentation_data:
|
||||
template_meta = await session.get(TemplateModel, row.presentation_id)
|
||||
template_meta = await session.get(TemplateModel, row.presentation)
|
||||
template = None
|
||||
if template_meta:
|
||||
template = {
|
||||
|
|
@ -900,7 +901,7 @@ async def get_presentations_summary(
|
|||
}
|
||||
presentations.append(
|
||||
PresentationSummary(
|
||||
presentation_id=row.presentation_id,
|
||||
presentation=row.presentation,
|
||||
layout_count=row.layout_count,
|
||||
last_updated_at=row.last_updated_at,
|
||||
template=template,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,16 @@ from pydantic import BaseModel, Field
|
|||
|
||||
class GeneratePresentationRequest(BaseModel):
|
||||
content: str = Field(..., description="The content for generating the presentation")
|
||||
instruction: Optional[str] = Field(
|
||||
instructions: Optional[str] = Field(
|
||||
default=None, description="The instruction for generating the presentation"
|
||||
)
|
||||
tone: Optional[str] = Field(
|
||||
default=None, description="The tone for the presentation"
|
||||
)
|
||||
verbosity: Optional[str] = Field(
|
||||
default=None, description="The verbosity for the presentation"
|
||||
)
|
||||
web_search: bool = Field(default=False, description="Whether to enable web search")
|
||||
n_slides: int = Field(default=8, description="Number of slides to generate")
|
||||
language: str = Field(
|
||||
default="English", description="Language for the presentation"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from pydantic import BaseModel
|
||||
import uuid
|
||||
|
||||
|
||||
class PresentationAndPath(BaseModel):
|
||||
presentation_id: str
|
||||
presentation_id: uuid.UUID
|
||||
path: str
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from typing import List, Literal
|
||||
from pydantic import BaseModel
|
||||
import uuid
|
||||
|
||||
|
||||
class SlideContentUpdate(BaseModel):
|
||||
|
|
@ -8,6 +9,6 @@ class SlideContentUpdate(BaseModel):
|
|||
|
||||
|
||||
class GetPresentationUsingTemplateRequest(BaseModel):
|
||||
presentation_id: str
|
||||
presentation_id: uuid.UUID
|
||||
data: List[SlideContentUpdate]
|
||||
export_as: Literal["pptx", "pdf"] = "pptx"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
|
@ -11,18 +12,38 @@ from models.sql.slide import SlideModel
|
|||
|
||||
|
||||
class PresentationWithSlides(BaseModel):
|
||||
id: str
|
||||
id: uuid.UUID
|
||||
content: str
|
||||
n_slides: int
|
||||
language: str
|
||||
file_paths: Optional[List[str]]
|
||||
title: Optional[str] = None
|
||||
outlines: Optional[PresentationOutlineModel]
|
||||
outlines: Optional[PresentationOutlineModel] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
layout: Optional[PresentationLayoutModel]
|
||||
structure: Optional[PresentationStructureModel]
|
||||
instruction: Optional[str] = None
|
||||
structure: Optional[PresentationStructureModel] = None
|
||||
instructions: Optional[str] = None
|
||||
tone: Optional[str] = None
|
||||
verbosity: Optional[str] = None
|
||||
slides: List[SlideModel]
|
||||
|
||||
def to_presentation_model(self) -> PresentationModel:
|
||||
return PresentationModel(**self.model_dump())
|
||||
return PresentationModel(
|
||||
id=self.id,
|
||||
content=self.content,
|
||||
n_slides=self.n_slides,
|
||||
language=self.language,
|
||||
file_paths=self.file_paths,
|
||||
title=self.title,
|
||||
outlines=self.outlines.model_dump(mode="json") if self.outlines else None,
|
||||
created_at=self.created_at,
|
||||
updated_at=self.updated_at,
|
||||
layout=self.layout.model_dump(mode="json") if self.layout else None,
|
||||
structure=(
|
||||
self.structure.model_dump(mode="json") if self.structure else None
|
||||
),
|
||||
instructions=self.instructions,
|
||||
tone=self.tone,
|
||||
verbosity=self.verbosity,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.randomizers import get_random_uuid
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class ImageAsset(SQLModel, table=True):
|
||||
id: str = Field(default_factory=get_random_uuid, primary_key=True)
|
||||
created_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
path: str
|
||||
extras: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import uuid
|
||||
from sqlmodel import Field, Column, JSON, SQLModel
|
||||
|
||||
from utils.randomizers import get_random_uuid
|
||||
|
||||
|
||||
class KeyValueSqlModel(SQLModel, table=True):
|
||||
id: str = Field(default_factory=get_random_uuid, primary_key=True)
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
key: str = Field(index=True)
|
||||
value: dict = Field(sa_column=Column(JSON))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from datetime import datetime
|
||||
import uuid
|
||||
from sqlmodel import Field, Column, JSON, SQLModel, DateTime
|
||||
|
||||
|
||||
class OllamaPullStatus(SQLModel, table=True):
|
||||
id: str = Field(primary_key=True)
|
||||
id: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4)
|
||||
last_updated: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
status: dict = Field(sa_column=Column(JSON))
|
||||
|
|
|
|||
|
|
@ -1,31 +1,47 @@
|
|||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
from sqlalchemy import JSON, Column, DateTime, String
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from models.presentation_layout import PresentationLayoutModel
|
||||
from models.presentation_outline_model import PresentationOutlineModel
|
||||
from models.presentation_structure_model import PresentationStructureModel
|
||||
from utils.randomizers import get_random_uuid
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class PresentationModel(SQLModel, table=True):
|
||||
id: str = Field(primary_key=True)
|
||||
__tablename__ = "presentations"
|
||||
|
||||
id: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4)
|
||||
content: str
|
||||
n_slides: int
|
||||
language: str
|
||||
title: Optional[str] = None
|
||||
file_paths: Optional[List[str]] = Field(sa_column=Column(JSON), default=None)
|
||||
outlines: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
created_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
updated_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
onupdate=get_current_utc_datetime,
|
||||
),
|
||||
)
|
||||
layout: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
structure: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
instruction: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
instructions: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
tone: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
verbosity: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
|
||||
def get_new_presentation(self):
|
||||
return PresentationModel(
|
||||
id=get_random_uuid(),
|
||||
id=uuid.uuid4(),
|
||||
content=self.content,
|
||||
n_slides=self.n_slides,
|
||||
language=self.language,
|
||||
|
|
@ -34,7 +50,7 @@ class PresentationModel(SQLModel, table=True):
|
|||
outlines=self.outlines,
|
||||
layout=self.layout,
|
||||
structure=self.structure,
|
||||
instruction=self.instruction,
|
||||
instructions=self.instructions,
|
||||
)
|
||||
|
||||
def get_presentation_outline(self):
|
||||
|
|
|
|||
|
|
@ -1,19 +1,37 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
import uuid
|
||||
from sqlalchemy import Column, DateTime, Text, JSON
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class PresentationLayoutCodeModel(SQLModel, table=True):
|
||||
"""Model for storing presentation layout codes"""
|
||||
|
||||
|
||||
__tablename__ = "presentation_layout_codes"
|
||||
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
presentation_id: str = Field(index=True, description="UUID of the presentation")
|
||||
presentation: uuid.UUID = Field(index=True, description="UUID of the presentation")
|
||||
layout_id: str = Field(description="Unique identifier for the layout")
|
||||
layout_name: str = Field(description="Display name of the layout")
|
||||
layout_code: str = Field(sa_column=Column(Text), description="TSX/React component code for the layout")
|
||||
fonts: Optional[List[str]] = Field(sa_column=Column(JSON), default=None, description="Optional list of font links")
|
||||
created_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
updated_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now, onupdate=datetime.now))
|
||||
layout_code: str = Field(
|
||||
sa_column=Column(Text), description="TSX/React component code for the layout"
|
||||
)
|
||||
fonts: Optional[List[str]] = Field(
|
||||
sa_column=Column(JSON), default=None, description="Optional list of font links"
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
)
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
onupdate=get_current_utc_datetime,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,28 @@
|
|||
from typing import Optional
|
||||
import uuid
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlmodel import Field, Column, JSON, SQLModel
|
||||
|
||||
from utils.randomizers import get_random_uuid
|
||||
|
||||
|
||||
class SlideModel(SQLModel, table=True):
|
||||
id: str = Field(primary_key=True, default_factory=get_random_uuid)
|
||||
presentation: str
|
||||
__tablename__ = "slides"
|
||||
|
||||
id: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4)
|
||||
presentation: uuid.UUID = Field(
|
||||
sa_column=Column(ForeignKey("presentations.id", ondelete="CASCADE"), index=True)
|
||||
)
|
||||
layout_group: str
|
||||
layout: str
|
||||
index: int
|
||||
content: dict = Field(sa_column=Column(JSON))
|
||||
html_content: Optional[str]
|
||||
speaker_note: str
|
||||
speaker_note: Optional[str] = None
|
||||
properties: Optional[dict] = Field(sa_column=Column(JSON))
|
||||
|
||||
def get_new_slide(self, presentation_id: str, content: Optional[dict] = None):
|
||||
def get_new_slide(self, presentation: uuid.UUID, content: Optional[dict] = None):
|
||||
return SlideModel(
|
||||
id=get_random_uuid(),
|
||||
presentation=presentation_id,
|
||||
id=uuid.uuid4(),
|
||||
presentation=presentation,
|
||||
layout_group=self.layout_group,
|
||||
layout=self.layout,
|
||||
index=self.index,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,26 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlmodel import SQLModel, Field
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class TemplateModel(SQLModel, table=True):
|
||||
__tablename__ = "templates"
|
||||
|
||||
id: str = Field(primary_key=True, description="UUID for the template (matches presentation_id)")
|
||||
id: uuid.UUID = Field(
|
||||
default_factory=uuid.uuid4,
|
||||
primary_key=True,
|
||||
description="UUID for the template (matches presentation_id)",
|
||||
)
|
||||
name: str = Field(description="Human friendly template name")
|
||||
description: Optional[str] = Field(default=None, description="Optional template description")
|
||||
created_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
description: Optional[str] = Field(
|
||||
default=None, description="Optional template description"
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ from utils.image_provider import (
|
|||
is_gemini_flash_selected,
|
||||
is_dalle3_selected,
|
||||
)
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
|
||||
class ImageGenerationService:
|
||||
|
|
@ -104,7 +104,7 @@ class ImageGenerationService:
|
|||
if part.text is not None:
|
||||
print(part.text)
|
||||
elif part.inline_data is not None:
|
||||
image_path = os.path.join(output_directory, f"{get_random_uuid()}.jpg")
|
||||
image_path = os.path.join(output_directory, f"{uuid.uuid4()}.jpg")
|
||||
with open(image_path, "wb") as f:
|
||||
f.write(part.inline_data.data)
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ from utils.image_utils import (
|
|||
round_image_corners,
|
||||
set_image_opacity,
|
||||
)
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
BLANK_SLIDE_LAYOUT = 6
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ class PptxPresentationCreator:
|
|||
image = invert_image(image)
|
||||
if picture_model.opacity:
|
||||
image = set_image_opacity(image, picture_model.opacity)
|
||||
image_path = os.path.join(self._temp_dir, f"{get_random_uuid()}.png")
|
||||
image_path = os.path.join(self._temp_dir, f"{uuid.uuid4()}.png")
|
||||
image.save(image_path)
|
||||
|
||||
margined_position = self.get_margined_position(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
from typing import Optional, Union
|
||||
|
||||
from utils.get_env import get_temp_directory_env
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
|
||||
class TempFileService:
|
||||
|
|
@ -13,7 +13,7 @@ class TempFileService:
|
|||
os.makedirs(self.base_dir, exist_ok=True)
|
||||
|
||||
def create_dir_in_dir(self, base_dir: str, dir_name: Optional[str] = None) -> str:
|
||||
temp_dir = os.path.join(base_dir, dir_name if dir_name else get_random_uuid())
|
||||
temp_dir = os.path.join(base_dir, dir_name if dir_name else str(uuid.uuid4()))
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
return temp_dir
|
||||
|
||||
|
|
|
|||
5
servers/fastapi/utils/datetime_utils.py
Normal file
5
servers/fastapi/utils/datetime_utils.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
def get_current_utc_datetime():
|
||||
return datetime.now(timezone.utc)
|
||||
|
|
@ -6,7 +6,7 @@ from urllib.parse import urlparse
|
|||
|
||||
import aiohttp
|
||||
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
|
||||
async def download_file(
|
||||
|
|
@ -36,9 +36,9 @@ async def download_file(
|
|||
content_type.split(";")[0]
|
||||
)
|
||||
if extension:
|
||||
filename = f"{get_random_uuid()}{extension}"
|
||||
filename = f"{uuid.uuid4()}{extension}"
|
||||
|
||||
filename = filename or get_random_uuid()
|
||||
filename = filename or str(uuid.uuid4())
|
||||
save_path = os.path.join(save_directory, filename)
|
||||
|
||||
async with aiohttp.ClientSession(trust_env=True) as session:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import json
|
|||
import os
|
||||
import aiohttp
|
||||
from typing import Literal
|
||||
import uuid
|
||||
from fastapi import HTTPException
|
||||
from pathvalidate import sanitize_filename
|
||||
|
||||
|
|
@ -10,11 +11,11 @@ from models.presentation_and_path import PresentationAndPath
|
|||
from services.pptx_presentation_creator import PptxPresentationCreator
|
||||
from services.temp_file_service import TEMP_FILE_SERVICE
|
||||
from utils.asset_directory_utils import get_exports_directory
|
||||
from utils.randomizers import get_random_uuid
|
||||
import uuid
|
||||
|
||||
|
||||
async def export_presentation(
|
||||
presentation_id: str, title: str, export_as: Literal["pptx", "pdf"]
|
||||
presentation_id: uuid.UUID, title: str, export_as: Literal["pptx", "pdf"]
|
||||
) -> PresentationAndPath:
|
||||
if export_as == "pptx":
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ async def export_presentation(
|
|||
export_directory = get_exports_directory()
|
||||
pptx_path = os.path.join(
|
||||
export_directory,
|
||||
f"{sanitize_filename(title or get_random_uuid())}.pptx",
|
||||
f"{sanitize_filename(title or str(uuid.uuid4()))}.pptx",
|
||||
)
|
||||
pptx_creator.save(pptx_path)
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ async def export_presentation(
|
|||
"http://localhost/api/export-as-pdf",
|
||||
json={
|
||||
"id": presentation_id,
|
||||
"title": sanitize_filename(title or get_random_uuid()),
|
||||
"title": sanitize_filename(title or str(uuid.uuid4())),
|
||||
},
|
||||
) as response:
|
||||
response_json = await response.json()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from models.llm_message import LLMSystemMessage, LLMUserMessage
|
||||
from models.presentation_layout import SlideLayoutModel
|
||||
from models.sql.slide import SlideModel
|
||||
|
|
@ -5,9 +7,23 @@ from services.llm_client import LLMClient
|
|||
from utils.llm_provider import get_model
|
||||
from utils.schema_utils import add_field_in_schema, remove_fields_from_schema
|
||||
|
||||
system_prompt = """
|
||||
|
||||
def get_system_prompt(
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
return f"""
|
||||
Edit Slide data and speaker note based on provided prompt, follow mentioned steps and notes and provide structured output.
|
||||
|
||||
{"# User Instruction:" if instructions else ""}
|
||||
{instructions or ""}
|
||||
|
||||
{"# Tone:" if tone else ""}
|
||||
{tone or ""}
|
||||
|
||||
{"# Verbosity:" if verbosity else ""}
|
||||
{verbosity or ""}
|
||||
|
||||
# Notes
|
||||
- Provide output in language mentioned in **Input**.
|
||||
|
|
@ -19,7 +35,7 @@ system_prompt = """
|
|||
- Speaker note should be simple, clear, concise and to the point.
|
||||
|
||||
**Go through all notes and steps and make sure they are followed, including mentioned constraints**
|
||||
"""
|
||||
"""
|
||||
|
||||
|
||||
def get_user_prompt(prompt: str, slide_data: dict, language: str):
|
||||
|
|
@ -27,6 +43,9 @@ def get_user_prompt(prompt: str, slide_data: dict, language: str):
|
|||
## Icon Query And Image Prompt Language
|
||||
English
|
||||
|
||||
## Current Date and Time
|
||||
{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
||||
|
||||
## Slide Content Language
|
||||
{language}
|
||||
|
||||
|
|
@ -42,10 +61,13 @@ def get_messages(
|
|||
prompt: str,
|
||||
slide_data: dict,
|
||||
language: str,
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
return [
|
||||
LLMSystemMessage(
|
||||
content=system_prompt,
|
||||
content=get_system_prompt(tone, verbosity, instructions),
|
||||
),
|
||||
LLMUserMessage(
|
||||
content=get_user_prompt(prompt, slide_data, language),
|
||||
|
|
@ -58,6 +80,9 @@ async def get_edited_slide_content(
|
|||
slide: SlideModel,
|
||||
language: str,
|
||||
slide_layout: SlideLayoutModel,
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
model = get_model()
|
||||
|
||||
|
|
@ -80,7 +105,9 @@ async def get_edited_slide_content(
|
|||
client = LLMClient()
|
||||
response = await client.generate_structured(
|
||||
model=model,
|
||||
messages=get_messages(prompt, slide.content, language),
|
||||
messages=get_messages(
|
||||
prompt, slide.content, language, tone, verbosity, instructions
|
||||
),
|
||||
response_format=response_schema,
|
||||
strict=False,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,21 +8,31 @@ from utils.get_dynamic_models import get_presentation_outline_model_with_n_slide
|
|||
from utils.llm_provider import get_model
|
||||
|
||||
|
||||
def get_system_prompt(instruction: Optional[str] = None):
|
||||
def get_system_prompt(
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
return f"""
|
||||
You are an expert presentation creator. Generate structured presentations based on user requirements and format them according to the specified JSON schema with markdown content.
|
||||
|
||||
Try to use available tools for better results.
|
||||
|
||||
{"# User Instruction:" if instructions else ""}
|
||||
{instructions or ""}
|
||||
|
||||
{"# Tone:" if tone else ""}
|
||||
{tone or ""}
|
||||
|
||||
{"# Verbosity:" if verbosity else ""}
|
||||
{verbosity or ""}
|
||||
|
||||
- Provide content for each slide in markdown format.
|
||||
- Make sure that flow of the presentation is logical and consistent.
|
||||
- Place greater emphasis on numerical data.
|
||||
- If Additional Information is provided, divide it into slides.
|
||||
- Make sure no images are provided in the content.
|
||||
- Make sure that content follows language guidelines.
|
||||
|
||||
{"# User Instruction:" if instruction else ""}
|
||||
{instruction or ""}
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -47,11 +57,13 @@ def get_messages(
|
|||
n_slides: int,
|
||||
language: str,
|
||||
additional_context: Optional[str] = None,
|
||||
instruction: Optional[str] = None,
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
return [
|
||||
LLMSystemMessage(
|
||||
content=get_system_prompt(instruction),
|
||||
content=get_system_prompt(tone, verbosity, instructions),
|
||||
),
|
||||
LLMUserMessage(
|
||||
content=get_user_prompt(content, n_slides, language, additional_context),
|
||||
|
|
@ -64,7 +76,10 @@ async def generate_ppt_outline(
|
|||
n_slides: int,
|
||||
language: Optional[str] = None,
|
||||
additional_context: Optional[str] = None,
|
||||
instruction: Optional[str] = None,
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
web_search: bool = False,
|
||||
):
|
||||
model = get_model()
|
||||
response_model = get_presentation_outline_model_with_n_slides(n_slides)
|
||||
|
|
@ -78,10 +93,14 @@ async def generate_ppt_outline(
|
|||
n_slides,
|
||||
language,
|
||||
additional_context,
|
||||
instruction,
|
||||
tone,
|
||||
verbosity,
|
||||
instructions,
|
||||
),
|
||||
response_model.model_json_schema(),
|
||||
strict=True,
|
||||
tools=[SearchWebTool] if client.enable_web_grounding() else None,
|
||||
tools=(
|
||||
[SearchWebTool] if (client.enable_web_grounding() and web_search) else None
|
||||
),
|
||||
):
|
||||
yield chunk
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ def get_messages(
|
|||
presentation_layout: PresentationLayoutModel,
|
||||
n_slides: int,
|
||||
data: str,
|
||||
instruction: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
return [
|
||||
LLMSystemMessage(
|
||||
|
|
@ -47,8 +47,8 @@ def get_messages(
|
|||
|
||||
**Trust your design instincts. Focus on creating the most effective presentation for the content and audience.**
|
||||
|
||||
{"# User Instruction:" if instruction else ""}
|
||||
{instruction or ""}
|
||||
{"# User Instruction:" if instructions else ""}
|
||||
{instructions or ""}
|
||||
|
||||
Select layout index for each of the {n_slides} slides based on what will best serve the presentation's goals.
|
||||
""",
|
||||
|
|
@ -64,7 +64,7 @@ def get_messages(
|
|||
async def generate_presentation_structure(
|
||||
presentation_outline: PresentationOutlineModel,
|
||||
presentation_layout: PresentationLayoutModel,
|
||||
instruction: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
) -> PresentationStructureModel:
|
||||
|
||||
client = LLMClient()
|
||||
|
|
@ -79,7 +79,7 @@ async def generate_presentation_structure(
|
|||
presentation_layout,
|
||||
len(presentation_outline.slides),
|
||||
presentation_outline.to_string(),
|
||||
instruction,
|
||||
instructions,
|
||||
),
|
||||
response_format=response_model.model_json_schema(),
|
||||
strict=True,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,23 @@ from utils.llm_provider import get_model
|
|||
from utils.schema_utils import add_field_in_schema, remove_fields_from_schema
|
||||
|
||||
|
||||
def get_system_prompt(instruction: Optional[str] = None):
|
||||
def get_system_prompt(
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
return f"""
|
||||
Generate structured slide based on provided outline, follow mentioned steps and notes and provide structured output.
|
||||
|
||||
{"# User Instructions:" if instructions else ""}
|
||||
{instructions or ""}
|
||||
|
||||
{"# Tone:" if tone else ""}
|
||||
{tone or ""}
|
||||
|
||||
{"# Verbosity:" if verbosity else ""}
|
||||
{verbosity or ""}
|
||||
|
||||
# Steps
|
||||
1. Analyze the outline.
|
||||
2. Generate structured slide based on the outline.
|
||||
|
|
@ -35,8 +48,6 @@ def get_system_prompt(instruction: Optional[str] = None):
|
|||
__icon_query__: string,
|
||||
}}
|
||||
|
||||
{"# User Instruction:" if instruction else ""}
|
||||
{instruction or ""}
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -56,11 +67,17 @@ def get_user_prompt(outline: str, language: str):
|
|||
"""
|
||||
|
||||
|
||||
def get_messages(outline: str, language: str, instruction: Optional[str] = None):
|
||||
def get_messages(
|
||||
outline: str,
|
||||
language: str,
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
|
||||
return [
|
||||
LLMSystemMessage(
|
||||
content=get_system_prompt(instruction),
|
||||
content=get_system_prompt(tone, verbosity, instructions),
|
||||
),
|
||||
LLMUserMessage(
|
||||
content=get_user_prompt(outline, language),
|
||||
|
|
@ -72,7 +89,9 @@ async def get_slide_content_from_type_and_outline(
|
|||
slide_layout: SlideLayoutModel,
|
||||
outline: SlideOutlineModel,
|
||||
language: str,
|
||||
instruction: Optional[str] = None,
|
||||
tone: Optional[str] = None,
|
||||
verbosity: Optional[str] = None,
|
||||
instructions: Optional[str] = None,
|
||||
):
|
||||
client = LLMClient()
|
||||
model = get_model()
|
||||
|
|
@ -98,7 +117,9 @@ async def get_slide_content_from_type_and_outline(
|
|||
messages=get_messages(
|
||||
outline.content,
|
||||
language,
|
||||
instruction,
|
||||
tone,
|
||||
verbosity,
|
||||
instructions,
|
||||
),
|
||||
response_format=response_schema,
|
||||
strict=False,
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
import uuid
|
||||
|
||||
|
||||
def get_random_uuid():
|
||||
return str(uuid.uuid4())
|
||||
|
|
@ -50,12 +50,12 @@ export class PresentationGenerationApi {
|
|||
}
|
||||
|
||||
static async createPresentation({
|
||||
prompt,
|
||||
content,
|
||||
n_slides,
|
||||
file_paths,
|
||||
language,
|
||||
}: {
|
||||
prompt: string;
|
||||
content: string;
|
||||
n_slides: number | null;
|
||||
file_paths?: string[];
|
||||
language: string | null;
|
||||
|
|
@ -67,7 +67,7 @@ export class PresentationGenerationApi {
|
|||
method: "POST",
|
||||
headers: getHeader(),
|
||||
body: JSON.stringify({
|
||||
prompt,
|
||||
content,
|
||||
n_slides,
|
||||
file_paths,
|
||||
language,
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ const GroupLayoutPreview = () => {
|
|||
const payload = {
|
||||
layouts: [
|
||||
{
|
||||
presentation_id: presentationId,
|
||||
presentation: presentationId,
|
||||
layout_id: currentLayoutId,
|
||||
layout_name: currentLayoutName,
|
||||
layout_code: currentCode,
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ const UploadPage = () => {
|
|||
// Use the first available layout group for direct generation
|
||||
trackEvent(MixpanelEvent.Upload_Create_Presentation_API_Call);
|
||||
const createResponse = await PresentationGenerationApi.createPresentation({
|
||||
prompt: config?.prompt ?? "",
|
||||
content: config?.prompt ?? "",
|
||||
n_slides: config?.slides ? parseInt(config.slides) : null,
|
||||
file_paths: [],
|
||||
language: config?.language ?? "",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue