presenton/servers/fastapi/utils/llm_calls/generate_slide_content.py
sudipnext 5f5482cfd5 feat: enhance LLMClient with Codex integration and error handling
- Implemented a new method to create an AsyncOpenAI client specifically for Codex.
- Added functionality to convert Codex event responses into a dictionary format for easier consumption.
- Updated the streaming methods to utilize the new Codex client and handle API errors gracefully.
- Improved logging for debugging purposes during Codex interactions.
- Refactored message handling in the slide content generation function for clarity and efficiency.
2026-02-25 17:56:39 +05:45

152 lines
5.4 KiB
Python

from datetime import datetime
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
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.
3. Generate speaker note that is simple, clear, concise and to the point.
# Notes
- Slide body should not use words like "This slide", "This presentation".
- Rephrase the slide body to make it flow naturally.
- Only use markdown to highlight important points.
- Make sure to follow language guidelines.
- Speaker note should be normal text, not markdown.
- Strictly follow the max and min character limit for every property in the slide.
- Never ever go over the max character limit. Limit your narration to make sure you never go over the max character limit.
- Number of items should not be more than max number of items specified in slide schema. If you have to put multiple points then merge them to obey max numebr of items.
- Generate content as per the given tone.
- Be very careful with number of words to generate for given field. As generating more than max characters will overflow in the design. So, analyze early and never generate more characters than allowed.
- Do not add emoji in the content.
- Metrics should be in abbreviated form with least possible characters. Do not add long sequence of words for metrics.
- For verbosity:
- If verbosity is 'concise', then generate description as 1/3 or lower of the max character limit. Don't worry if you miss content or context.
- If verbosity is 'standard', then generate description as 2/3 of the max character limit.
- If verbosity is 'text-heavy', then generate description as 3/4 or higher of the max character limit. Make sure it does not exceed the max character limit.
User instructions, tone and verbosity should always be followed and should supercede any other instruction, except for max and min character limit, slide schema and number of items.
- Provide output in json format and **don't include <parameters> tags**.
# Image and Icon Output Format
image: {{
__image_prompt__: string,
}}
icon: {{
__icon_query__: string,
}}
"""
def get_user_prompt(outline: str, language: str):
return f"""
## Current Date and Time
{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
## Icon Query And Image Prompt Language
English
## Slide Content Language
{language}
## Slide Outline
{outline}
"""
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(tone, verbosity, instructions),
),
LLMUserMessage(
content=get_user_prompt(outline, language),
),
]
async def get_slide_content_from_type_and_outline(
slide_layout: SlideLayoutModel,
outline: SlideOutlineModel,
language: 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,
)
messages = get_messages(
outline.content,
language,
tone,
verbosity,
instructions,
)
print(
f"get_slide_content_from_type_and_outline: model={model} outline_len={len(outline.content or '')} language={language}"
)
try:
response = await client.generate_structured(
model=model,
messages=messages,
response_format=response_schema,
strict=False,
)
print(
f"get_slide_content_from_type_and_outline: response is None={response is None} keys={list(response.keys())[:6] if isinstance(response, dict) else None}"
)
return response
except Exception as e:
print(f"get_slide_content_from_type_and_outline: exception={e}")
raise handle_llm_client_exceptions(e)