presenton/electron/servers/fastapi/utils/llm_calls/generate_slide_content.py
sudipnext cc8f0bb862 feat: Enhance presentation generation features and improve asset handling
- Added MAX_NUMBER_OF_SLIDES constant to limit slide generation.
- Updated GeneratePresentationRequest model to allow optional slide count and language detection.
- Implemented language resolution in edit_slide and generate_presentation_outlines utilities.
- Enhanced user prompts to include optional parameters for slide count and table of contents.
- Introduced outline utilities for managing table of contents and slide outlines.
- Improved image and icon processing in slides to utilize outline image URLs.
- Updated frontend components to resolve backend asset URLs for icons and images.
- Refactored upload components to handle optional slide count and language selection.
- Enhanced API utility functions to resolve backend asset URLs correctly.
2026-04-06 18:28:43 +05:45

194 lines
5.4 KiB
Python

from datetime import datetime
import json
from typing import Optional
from models.llm_message import LLMSystemMessage, LLMUserMessage
from models.presentation_layout import SlideLayoutModel
from models.presentation_outline_model import SlideOutlineModel
from services.llm_client import LLMClient
from utils.llm_client_error_handler import handle_llm_client_exceptions
from utils.llm_provider import get_model
from utils.schema_utils import add_field_in_schema, remove_fields_from_schema
SLIDE_CONTENT_SYSTEM_PROMPT = """
You will be given slide content and response schema.
You need to generate structured content json based on the schema.
# Steps
1. Analyze the content.
2. Analyze the response schema.
3. Generate structured content json based on the schema.
4. Generate speaker note if required.
5. Provide structured content json as output.
# General Rules
- Make sure to follow language guidelines.
- Speaker note should be normal text, not markdown.
- Never ever go over the max character limit.
- Do not add emoji in the content.
- Don't provide $schema field in content json.
{markdown_emphasis_rules}
{user_instructions}
{tone_instructions}
{verbosity_instructions}
{output_fields_instructions}
"""
SLIDE_CONTENT_USER_PROMPT = """
# Current Date and Time:
{current_date_time}
# Icon Query And Image Prompt Language:
English
# Slide Language:
{language}
# SLIDE CONTENT: START
{content}
# SLIDE CONTENT: END
"""
def _resolve_prompt_language(language: Optional[str]) -> str:
if language is None:
return "auto-detect"
s = str(language).strip()
if not s:
return "auto-detect"
if s.lower() in {"auto", "auto-detect"}:
return "auto-detect"
return s
def _get_schema_markdown(response_schema: Optional[dict]) -> str:
if not response_schema:
return "- Follow the provided response schema strictly."
try:
schema_text = json.dumps(response_schema, ensure_ascii=False)
except Exception:
return "- Follow the provided response schema strictly."
return f"- Follow this response schema exactly: {schema_text}"
def get_system_prompt(
tone: Optional[str] = None,
verbosity: Optional[str] = None,
instructions: Optional[str] = None,
response_schema: Optional[dict] = None,
):
markdown_emphasis_rules = (
"- Strictly use markdown to emphasize important points, by bolding or "
"italicizing the part of text."
)
user_instructions = f"# User Instructions:\n{instructions}" if instructions else ""
tone_instructions = (
f"# Tone Instructions:\nMake slide as {tone} as possible." if tone else ""
)
verbosity_instructions = ""
if verbosity:
verbosity_instructions = "# Verbosity Instructions:\n"
if verbosity == "concise":
verbosity_instructions += "Make slide as concise as possible."
elif verbosity == "standard":
verbosity_instructions += "Make slide as standard as possible."
elif verbosity == "text-heavy":
verbosity_instructions += "Make slide as text-heavy as possible."
output_fields_instructions = "# Output Fields:\n" + _get_schema_markdown(
response_schema
)
return SLIDE_CONTENT_SYSTEM_PROMPT.format(
markdown_emphasis_rules=markdown_emphasis_rules,
user_instructions=user_instructions,
tone_instructions=tone_instructions,
verbosity_instructions=verbosity_instructions,
output_fields_instructions=output_fields_instructions,
)
def get_user_prompt(outline: str, language: Optional[str]):
return SLIDE_CONTENT_USER_PROMPT.format(
current_date_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
language=_resolve_prompt_language(language),
content=outline,
)
def get_messages(
outline: str,
language: Optional[str],
tone: Optional[str] = None,
verbosity: Optional[str] = None,
instructions: Optional[str] = None,
response_schema: Optional[dict] = None,
):
return [
LLMSystemMessage(
content=get_system_prompt(
tone,
verbosity,
instructions,
response_schema,
),
),
LLMUserMessage(
content=get_user_prompt(outline, language),
),
]
async def get_slide_content_from_type_and_outline(
slide_layout: SlideLayoutModel,
outline: SlideOutlineModel,
language: Optional[str],
tone: Optional[str] = None,
verbosity: Optional[str] = None,
instructions: Optional[str] = None,
):
client = LLMClient()
model = get_model()
response_schema = remove_fields_from_schema(
slide_layout.json_schema, ["__image_url__", "__icon_url__"]
)
response_schema = add_field_in_schema(
response_schema,
{
"__speaker_note__": {
"type": "string",
"minLength": 100,
"maxLength": 250,
"description": "Speaker note for the slide",
}
},
True,
)
try:
response = await client.generate_structured(
model=model,
messages=get_messages(
outline.content,
language,
tone,
verbosity,
instructions,
response_schema,
),
response_format=response_schema,
strict=False,
)
return response
except Exception as e:
raise handle_llm_client_exceptions(e)