Skip to content

Commit 43b7707

Browse files
Sai Rishi SSai Rishi S
authored andcommitted
feat: changed presentation generator functionality
1 parent 514eb55 commit 43b7707

7 files changed

Lines changed: 276 additions & 67 deletions

File tree

app/api/router.py

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,92 @@
99
from app.services.logger import setup_logger
1010
from app.api.error_utilities import InputValidationError, ErrorResponse
1111
from app.tools.utils.tool_utilities import load_tool_metadata, execute_tool, finalize_inputs
12-
from fastapi.responses import FileResponse
13-
from starlette.background import BackgroundTask
12+
from app.tools.presentation_generator.tools.slides_generator import SlidesGenerator
13+
import uuid
14+
from fastapi import FastAPI
1415

1516
logger = setup_logger(__name__)
1617
router = APIRouter()
18+
app = FastAPI()
19+
20+
# Initialize presentation contexts in app state if not exists
21+
if not hasattr(app.state, "presentation_contexts"):
22+
app.state.presentation_contexts = {}
1723

1824
@router.get("/")
1925
def read_root():
2026
return {"Hello": "World"}
2127

28+
# Handles two-step presentation generation:
29+
# 1. Generate outline with initial inputs
30+
# 2. Generate slides using stored outline and inputs
31+
@router.post("/generate-outline", response_model=Union[ToolResponse, ErrorResponse])
32+
async def generate_outline(data: ToolRequest, _ = Depends(key_check)):
33+
try:
34+
# Execute outline generation and store context for slides
35+
request_data = data.tool_data
36+
requested_tool = load_tool_metadata(request_data.tool_id)
37+
request_inputs_dict = finalize_inputs(request_data.inputs, requested_tool['inputs'])
38+
result = execute_tool(request_data.tool_id, request_inputs_dict)
39+
40+
# Store in app state, to use as context for slides generation
41+
presentation_id = str(uuid.uuid4())
42+
app.state.presentation_contexts[presentation_id] = {
43+
"outline": result,
44+
"inputs": request_inputs_dict
45+
}
46+
47+
return ToolResponse(data={
48+
"outline": result,
49+
"presentation_id": presentation_id
50+
})
51+
52+
except InputValidationError as e:
53+
logger.error(f"InputValidationError: {e}")
54+
return JSONResponse(
55+
status_code=400,
56+
content=jsonable_encoder(ErrorResponse(status=400, message=e.message))
57+
)
58+
59+
except HTTPException as e:
60+
logger.error(f"HTTPException: {e}")
61+
return JSONResponse(
62+
status_code=e.status_code,
63+
content=jsonable_encoder(ErrorResponse(status=e.status_code, message=e.detail))
64+
)
65+
66+
@router.post("/generate-slides/{presentation_id}", response_model=Union[ToolResponse, ErrorResponse])
67+
async def generate_slides(presentation_id: str, _ = Depends(key_check)):
68+
try:
69+
# Retrieve stored context and generate slides
70+
context = app.state.presentation_contexts.get(presentation_id)
71+
if not context:
72+
raise HTTPException(
73+
status_code=404,
74+
detail="Presentation context not found"
75+
)
76+
77+
slides = SlidesGenerator(
78+
outline=context["outline"],
79+
inputs=context["inputs"]
80+
).compile()
81+
82+
return ToolResponse(data=slides)
83+
84+
except InputValidationError as e:
85+
logger.error(f"InputValidationError: {e}")
86+
return JSONResponse(
87+
status_code=400,
88+
content=jsonable_encoder(ErrorResponse(status=400, message=e.message))
89+
)
90+
91+
except HTTPException as e:
92+
logger.error(f"HTTPException: {e}")
93+
return JSONResponse(
94+
status_code=e.status_code,
95+
content=jsonable_encoder(ErrorResponse(status=e.status_code, message=e.detail))
96+
)
97+
2298
@router.post("/submit-tool", response_model=Union[ToolResponse, ErrorResponse])
2399
async def submit_tool( data: ToolRequest, _ = Depends(key_check)):
24100
try:

app/services/schemas.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,12 @@ class ConnectWithThemArgs(BaseModel):
110110
student_description_file_type: str
111111
lang: str = Field(..., description="The language in which the subject is being taught.")
112112

113-
class PresentationGeneratorInput(BaseModel):
113+
# Changed request schema to match the new functionality of the presentation generator
114+
class PresentationGeneratorArgs(BaseModel):
114115
grade_level: str
115116
n_slides: int
116117
topic: str
117118
objectives: str
118-
additional_comments: str
119-
objectives_file_url: str
120-
objectives_file_type: str
121-
additional_comments_file_url: str
122-
additional_comments_file_type: str
123119
lang: Optional[str] = "en"
124120

125121
class RubricGeneratorArgs(BaseModel):

app/tools/presentation_generator/core.py

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from app.utils.document_loaders import get_docs
2-
from app.tools.presentation_generator.tools import PresentationGenerator
3-
from app.services.schemas import PresentationGeneratorInput
2+
from app.tools.presentation_generator.tools.outline_generator import OutlineGenerator
3+
from app.services.schemas import PresentationGeneratorArgs
44
from app.services.logger import setup_logger
55
from app.api.error_utilities import LoaderError, ToolExecutorError
66

@@ -10,48 +10,20 @@ def executor(grade_level: str,
1010
n_slides: int,
1111
topic: str,
1212
objectives: str,
13-
additional_comments: str,
14-
objectives_file_url: str,
15-
objectives_file_type: str,
16-
additional_comments_file_url: str,
17-
additional_comments_file_type: str,
1813
lang: str,
1914
verbose=False):
2015

2116
try:
22-
if(objectives_file_type):
23-
logger.info(f"Generating docs. from {objectives_file_type}")
24-
if(additional_comments_file_type):
25-
logger.info(f"Generating docs. from {additional_comments_file_type}")
2617

27-
docs = None
28-
29-
def fetch_docs(file_url, file_type):
30-
return get_docs(file_url, file_type, True) if file_url and file_type else None
31-
32-
objectives_docs = fetch_docs(objectives_file_url, objectives_file_type)
33-
additional_comments_docs = fetch_docs(additional_comments_file_url, additional_comments_file_type)
34-
35-
docs = (
36-
objectives_docs + additional_comments_docs
37-
if objectives_docs and additional_comments_docs
38-
else objectives_docs or additional_comments_docs
39-
)
40-
41-
presentation_generator_args = PresentationGeneratorInput(
18+
presentation_generator_args = PresentationGeneratorArgs(
4219
grade_level=grade_level,
4320
n_slides=n_slides,
4421
topic=topic,
4522
objectives=objectives,
46-
additional_comments=additional_comments,
47-
objectives_file_url=objectives_file_url,
48-
objectives_file_type=objectives_file_type,
49-
additional_comments_file_url=additional_comments_file_url,
50-
additional_comments_file_type=additional_comments_file_type,
5123
lang=lang
5224
)
5325

54-
output = PresentationGenerator(args=presentation_generator_args, verbose=verbose).generate_presentation(docs)
26+
output = OutlineGenerator(args=presentation_generator_args, verbose=verbose).compile()
5527

5628
logger.info(f"Presentation generated successfully")
5729

app/tools/presentation_generator/metadata.json

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"inputs": [
33
{
44
"label": "Grade Level",
@@ -16,35 +16,10 @@
1616
"type": "text"
1717
},
1818
{
19-
"label": "Standards/Objectives",
19+
"label": "Objectives/Description",
2020
"name": "objectives",
2121
"type": "text"
2222
},
23-
{
24-
"label": "Additional Comments",
25-
"name": "additional_comments",
26-
"type": "text"
27-
},
28-
{
29-
"label": "Standards/Objectives File URL",
30-
"name": "objectives_file_url",
31-
"type": "text"
32-
},
33-
{
34-
"label": "Standards/Objectives File Type",
35-
"name": "objectives_file_type",
36-
"type": "text"
37-
},
38-
{
39-
"label": "Additional Comments File URL",
40-
"name": "additional_comments_file_url",
41-
"type": "text"
42-
},
43-
{
44-
"label": "Additional Comments File Type",
45-
"name": "additional_comments_file_type",
46-
"type": "text"
47-
},
4823
{
4924
"label": "Language",
5025
"name": "lang",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from pydantic import BaseModel, Field
2+
from typing import List, Optional
3+
import os
4+
from app.services.logger import setup_logger
5+
from langchain_chroma import Chroma
6+
from langchain_core.prompts import PromptTemplate
7+
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
8+
from langchain_core.output_parsers import JsonOutputParser
9+
from langchain_google_genai import GoogleGenerativeAI
10+
from langchain_google_genai import GoogleGenerativeAIEmbeddings
11+
from langchain_core.documents import Document
12+
from app.services.schemas import PresentationGeneratorArgs
13+
from fastapi import HTTPException
14+
15+
logger = setup_logger(__name__)
16+
17+
class OutlineGenerator:
18+
def __init__(self, args: PresentationGeneratorArgs, verbose=False):
19+
# Initialize LLM and parser for outline generation
20+
self.args = args
21+
self.verbose = verbose
22+
self.model = GoogleGenerativeAI(model="gemini-1.5-pro")
23+
self.parser = JsonOutputParser(pydantic_object=OutlineSchema)
24+
25+
def compile(self) -> dict:
26+
try:
27+
# Create prompt for outline generation with learning objectives and structure
28+
prompt = PromptTemplate(
29+
template=(
30+
"Generate a coherent presentation outline for grade {grade_level} students.\n\n"
31+
"Topic: {topic}\n"
32+
"Number of slides needed: {n_slides}\n"
33+
"Learning objectives: {objectives}\n"
34+
"Language: {lang}\n\n"
35+
"Create an outline where:\n"
36+
"1. Each slide has a clear topic\n"
37+
"2. Include a brief description of the content\n"
38+
"3. Add transitions between slides for smooth flow\n"
39+
"4. Ensure content builds progressively\n"
40+
"5. Match the grade level's comprehension\n"
41+
"6. Generate exactly {n_slides} slides\n\n"
42+
"{format_instructions}"
43+
),
44+
input_variables=["grade_level", "topic", "n_slides", "objectives", "lang"],
45+
partial_variables={"format_instructions": self.parser.get_format_instructions()}
46+
)
47+
48+
chain = prompt | self.model | self.parser
49+
50+
result = chain.invoke({
51+
"grade_level": self.args.grade_level,
52+
"topic": self.args.topic,
53+
"n_slides": self.args.n_slides,
54+
"objectives": self.args.objectives,
55+
"lang": self.args.lang
56+
})
57+
58+
if self.verbose:
59+
logger.info(f"Generated outline successfully")
60+
61+
return dict(result)
62+
63+
except Exception as e:
64+
logger.error(f"Failed to generate outline: {str(e)}")
65+
raise HTTPException(status_code=500, detail=f"Failed to generate outline: {str(e)}")
66+
67+
# Defines expected structure for each slide in the outline
68+
class OutlineSlide(BaseModel):
69+
topic: str = Field(description="The main topic or title of the slide")
70+
description: str = Field(description="Brief description of the slide content")
71+
transition: str = Field(description="How this slide connects to the next one for smooth flow")
72+
73+
class OutlineSchema(BaseModel):
74+
slides: List[OutlineSlide] = Field(description="List of slides with their topics and descriptions")

0 commit comments

Comments
 (0)