diff --git a/README.md b/README.md index 3a0688fe..390e36f5 100644 --- a/README.md +++ b/README.md @@ -203,10 +203,77 @@ For detailed info checkout [API documentation](https://docs.presenton.ai/using-p - [Create Presentations from CSV using AI](https://docs.presenton.ai/tutorial/generate-presentation-from-csv) - [Create Data Reports Using AI](https://docs.presenton.ai/tutorial/create-data-reports-using-ai) +## ๐Ÿ—๏ธ MCP Architecture Overview + +Presenton is built on a modular architecture featuring a FastAPI backend and a Next.js frontend. At its core is the **MCP (Model Context Protocol) server**, which orchestrates the entire presentation generation workflow using a robust state machine. This architecture ensures flexibility, reliability, and extensibility. + +### MCP Workflow Highlights + +- **Session Management:** Each presentation runs in its own session for isolation and tracking. +- **Outline Generation:** Automatically creates outlines, with or without input files. +- **Layout Selection:** Choose from built-in or custom layouts. +- **Content & Asset Generation:** Generates slide text, images, and icons using your selected AI models. +- **Export Options:** Seamlessly export presentations as PDF or PPTX files. + +All workflow logic and tool APIs are organized in the `app_mcp` package. The orchestrator handles state transitions and error management, making it easy to extend or customize. + +#### Key Files & Directories + +- `.vscode/mcp.json`: VS Code integration and MCP server configuration. +- `servers/fastapi/app_mcp/`: Backend workflow logic and tool registration. + +--- + +## โšก Quick Start: VS Code Integration + +1. **Configure MCP:** Make sure `.vscode/mcp.json` points to your running MCP server (see example below). +2. **Start a Presentation:** Use the VS Code command palette or chat to run `start_presentation` with your topic. +3. **Advance Workflow:** Use `continue_workflow` to progress through outline, layout, and slide generation steps. +4. **Export:** Use `export_presentation` to download your presentation as PDF or PPTX. +5. **Check Progress:** Use `get_status` at any time to view your workflow status. + +#### Example `.vscode/mcp.json` +```jsonc +{ + "servers": { + "my-mcp-server-5f58fb2c": { + "url": "http://localhost:5000/mcp/", + "type": "http" + } + }, + "inputs": [] +} +``` + +--- + +### ๐Ÿ—ฃ๏ธ Using Chat Commands in VS Code + +You can interact with Presenton directly from the VS Code chat window: + +- **Step-by-step Workflow:** + Type a prompt like: + ```plaintext + I want to create a presentation on "Artificial Intelligence in Healthcare". Can you please show me the step by step and verify things to me so that I can be sure that the presentation is good? + ``` + +- **Direct Commands:** + For a faster workflow, use direct commands such as: + ```plaintext + Start a presentation on "Artificial Intelligence in Healthcare" with general layout and 10 slides. + ``` + +This integration gives you full controlโ€”whether you want a guided, step-by-step experience or prefer to automate the entire process with a single command. + +--- + + + + ## Roadmap - [x] Support for custom HTML templates by developers - [x] Support for accessing custom templates over API -- [ ] Implement MCP server +- [x] Implement MCP server - [ ] Ability for users to change system prompt - [X] Support external SQL database diff --git a/servers/fastapi/api/v1/ppt/endpoints/layouts.py b/servers/fastapi/api/v1/ppt/endpoints/layouts.py index 8194c8c1..6e1051bc 100644 --- a/servers/fastapi/api/v1/ppt/endpoints/layouts.py +++ b/servers/fastapi/api/v1/ppt/endpoints/layouts.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, HTTPException import aiohttp from typing import List, Any -from services.get_layout_by_name import get_layout_by_name +from utils.get_layout_by_name import get_layout_by_name from models.presentation_layout import PresentationLayoutModel LAYOUTS_ROUTER = APIRouter(prefix="/layouts", tags=["Layouts"]) diff --git a/servers/fastapi/app_mcp/services/state_machine/constants.py b/servers/fastapi/app_mcp/services/state_machine/constants.py index c2516b9d..0c5a5170 100644 --- a/servers/fastapi/app_mcp/services/state_machine/constants.py +++ b/servers/fastapi/app_mcp/services/state_machine/constants.py @@ -2,21 +2,10 @@ from app_mcp.services.state_machine.states import PresentationState TRANSITIONS = { PresentationState.INIT: { - PresentationState.FILES_UPLOADED, PresentationState.OUTLINE_REQUESTED }, - # Upload and summary flow - PresentationState.FILES_UPLOADED: { - PresentationState.SUMMARY_GENERATED, - PresentationState.UPLOAD_FAILED - }, - PresentationState.SUMMARY_GENERATED: { - PresentationState.OUTLINE_REQUESTED, - PresentationState.SUMMARY_FAILED - }, - - # Outline generation flow + # Outline generation flow (now includes file processing) PresentationState.OUTLINE_REQUESTED: { PresentationState.OUTLINE_GENERATED, PresentationState.OUTLINE_FAILED @@ -74,14 +63,6 @@ TRANSITIONS = { }, # Error recovery transitions - PresentationState.UPLOAD_FAILED: { - PresentationState.INIT, - PresentationState.FILES_UPLOADED - }, - PresentationState.SUMMARY_FAILED: { - PresentationState.FILES_UPLOADED, - PresentationState.OUTLINE_REQUESTED - }, PresentationState.OUTLINE_FAILED: { PresentationState.OUTLINE_REQUESTED, PresentationState.INIT @@ -102,10 +83,9 @@ TRANSITIONS = { SUGGESTIONS = { - PresentationState.INIT: "Upload files or start with outline generation", - PresentationState.FILES_UPLOADED: "Generate summary from uploaded files", - PresentationState.SUMMARY_GENERATED: "Generate presentation outline", - PresentationState.OUTLINE_GENERATED: "Review and approve outline, or regenerate", + PresentationState.INIT: "Start with outline generation (files will be processed automatically if provided)", + PresentationState.OUTLINE_REQUESTED: "Generating presentation outline with file analysis if applicable", + PresentationState.OUTLINE_GENERATED: "Review and approve outline", PresentationState.OUTLINE_APPROVED: "Select presentation layout", PresentationState.LAYOUT_SELECTED: "Generate presentation", PresentationState.PRESENTATION_READY: "Export presentation or request edits", @@ -117,9 +97,7 @@ SUGGESTIONS = { PROGRESS_WEIGHTS = { PresentationState.INIT: 0, - PresentationState.FILES_UPLOADED: 10, - PresentationState.SUMMARY_GENERATED: 20, - PresentationState.OUTLINE_REQUESTED: 25, + PresentationState.OUTLINE_REQUESTED: 20, PresentationState.OUTLINE_GENERATED: 35, PresentationState.OUTLINE_APPROVED: 40, PresentationState.LAYOUT_REQUESTED: 45, @@ -134,8 +112,6 @@ PROGRESS_WEIGHTS = { ERROR_STATES = { - PresentationState.UPLOAD_FAILED, - PresentationState.SUMMARY_FAILED, PresentationState.OUTLINE_FAILED, PresentationState.GENERATION_FAILED, PresentationState.EXPORT_FAILED, diff --git a/servers/fastapi/app_mcp/services/state_machine/states.py b/servers/fastapi/app_mcp/services/state_machine/states.py index deb9b109..fdffed25 100644 --- a/servers/fastapi/app_mcp/services/state_machine/states.py +++ b/servers/fastapi/app_mcp/services/state_machine/states.py @@ -5,11 +5,8 @@ class PresentationState(Enum): Represents the various states in the presentation workflow. """ INIT = auto() - # Upload and summary phase - FILES_UPLOADED = auto() - SUMMARY_GENERATED = auto() - - # Outline generation phase + + # Outline generation phase (now includes file processing) OUTLINE_REQUESTED = auto() OUTLINE_GENERATED = auto() OUTLINE_APPROVED = auto() @@ -32,8 +29,6 @@ class PresentationState(Enum): TEMPLATE_EDITING = auto() # Error states - UPLOAD_FAILED = auto() - SUMMARY_FAILED = auto() OUTLINE_FAILED = auto() GENERATION_FAILED = auto() EXPORT_FAILED = auto() diff --git a/servers/fastapi/app_mcp/services/workflow_orchestrator.py b/servers/fastapi/app_mcp/services/workflow_orchestrator.py index d52f4fc5..a036c175 100644 --- a/servers/fastapi/app_mcp/services/workflow_orchestrator.py +++ b/servers/fastapi/app_mcp/services/workflow_orchestrator.py @@ -3,7 +3,6 @@ from dataclasses import asdict from app_mcp.services.state_machine.machine import PresentationStateMachine from app_mcp.services.state_machine.states import PresentationState from utils.user_config import update_env_with_user_config -from app_mcp.wrapper.upload_and_generate_summary import upload_and_summarize_files from app_mcp.wrapper.generate_outline import generate_outline from app_mcp.wrapper.presentation_generation import process_post_outline_workflow from app_mcp.wrapper.presentation_export import export_presentation_and_get_path @@ -73,50 +72,6 @@ class WorkflowOrchestrator: """Remove workflow session""" return self._active_sessions.pop(session_id, None) is not None - async def execute_upload_and_summarize(self, session_id: str, files: List[Any]) -> Dict[str, Any]: - """ - Execute file upload and summary generation workflow step. - Args: - session_id (str): Unique identifier for the session. - files (List[Any]): List of files to be uploaded and summarized. - Returns: - Dict[str, Any]: Result containing status, state, progress, next action, and any - - """ - fsm = self.get_session(session_id) - if not fsm: - raise ValueError(f"Session {session_id} not found") - - try: - fsm.transition(PresentationState.FILES_UPLOADED) - - result = await upload_and_summarize_files(files) - - # Update context and transition to summary generated - context_updates = { - "summary": result["summary"], - "file_paths": result["file_paths"] - } - fsm.transition(PresentationState.SUMMARY_GENERATED, context_updates) - - return { - "status": "success", - "state": fsm.state.name, - "progress": fsm.get_workflow_progress(), - "next_action": fsm.get_next_suggested_action(), - "result": result - } - - except Exception as e: - fsm.transition(PresentationState.UPLOAD_FAILED, {"error_message": str(e)}) - print(f"There was an error uploading and summarizing files: {e}") - return { - "status": "error", - "state": fsm.state.name, - "error": str(e), - "next_action": fsm.get_next_suggested_action() - } - async def execute_generate_outline(self, session_id: str, prompt: str, **kwargs) -> Dict[str, Any]: """ Execute outline generation workflow step @@ -136,7 +91,7 @@ class WorkflowOrchestrator: fsm.transition(PresentationState.OUTLINE_REQUESTED) - result = await generate_outline(prompt, summary=fsm.context.summary, **kwargs) + result = await generate_outline(prompt, **kwargs) # Update the Context and transition to outline generated context_updates = { @@ -149,10 +104,9 @@ class WorkflowOrchestrator: "status": "success", "state": fsm.state.name, "progress": fsm.get_workflow_progress(), - "next_action": "Review outline and approve, or request regeneration", + "next_action": "Review outline and approve", "result": result, - "can_approve": True, - "can_regenerate": True + "can_approve": True } except Exception as e: diff --git a/servers/fastapi/app_mcp/tools/__init__.py b/servers/fastapi/app_mcp/tools/__init__.py index b4c502fa..8905d4b9 100644 --- a/servers/fastapi/app_mcp/tools/__init__.py +++ b/servers/fastapi/app_mcp/tools/__init__.py @@ -2,7 +2,6 @@ from app_mcp.tools.choose_layout import register_choose_layout from app_mcp.tools.export_presentation import register_export_presentation -from app_mcp.tools.regenerate_outline import register_regenerate_outline from app_mcp.tools.get_status import register_get_status from app_mcp.tools.show_layouts import register_show_layouts from app_mcp.tools.start_presentation import register_start_presentation @@ -13,7 +12,6 @@ from app_mcp.tools.continue_workflow import register_continue_workflow __all__ = [ 'register_choose_layout', 'register_export_presentation', - 'register_regenerate_outline', 'register_get_status', 'register_show_layouts', 'register_start_presentation', @@ -27,7 +25,6 @@ def register_tools(mcp, orchestrator): tools = [ register_choose_layout, register_export_presentation, - register_regenerate_outline, register_get_status, register_show_layouts, register_start_presentation, diff --git a/servers/fastapi/app_mcp/tools/continue_workflow.py b/servers/fastapi/app_mcp/tools/continue_workflow.py index 2fbc6eb4..0fa60984 100644 --- a/servers/fastapi/app_mcp/tools/continue_workflow.py +++ b/servers/fastapi/app_mcp/tools/continue_workflow.py @@ -44,11 +44,12 @@ def register_continue_workflow(mcp, orchestrator): current_state = fsm.state.name - if current_state in ["FILES_UPLOADED", "SUMMARY_GENERATED", "INIT"]: - # Generate outline + if current_state in ["INIT"]: + # Generate outline (this now handles file processing internally) prompt = fsm.context.metadata.get("original_prompt", "") n_slides = fsm.context.metadata.get("n_slides", 8) language = fsm.context.metadata.get("language", "English") + files = fsm.context.metadata.get("files", None) if not prompt: return { @@ -57,8 +58,13 @@ def register_continue_workflow(mcp, orchestrator): "suggestion": "Call start_presentation with a valid prompt" } + # Pass files to outline generation if they exist + kwargs = {"n_slides": n_slides, "language": language} + if files: + kwargs["files"] = files + result = await orchestrator.execute_generate_outline( - session_id, prompt, n_slides=n_slides, language=language + session_id, prompt, **kwargs ) if result["status"] == "success": @@ -68,8 +74,9 @@ def register_continue_workflow(mcp, orchestrator): "message": "Here's your presentation outline:", "title": result["result"]["title"], "outlines": result["result"]["outlines"], + "files_processed": bool(files), "suggestion": "Take a look at the outline. If it looks good, use 'continue_workflow' again to proceed to layout selection.", - "next_step": "Call continue_workflow again to choose layouts, or use regenerate_outline to try different approach" + "next_step": "Call continue_workflow again to choose layouts" } return result diff --git a/servers/fastapi/app_mcp/tools/export_presentation.py b/servers/fastapi/app_mcp/tools/export_presentation.py index 21960b4c..a1aedb4b 100644 --- a/servers/fastapi/app_mcp/tools/export_presentation.py +++ b/servers/fastapi/app_mcp/tools/export_presentation.py @@ -39,12 +39,7 @@ def register_export_presentation(mcp, orchestrator): "session_id": session_id, "message": f"๐ŸŽ‰ Your presentation has been exported as {format.upper()}!", "path": result["result"]["path"], - "suggestion": "You can download it now, or start creating another presentation.", - "available_actions": { - "download": "Download the presentation", - "new_presentation": "Create a new presentation", - "edit": "Make edits to this presentation" - } + "suggestion": "You can download it now, or start creating another presentation." } return result except Exception as e: diff --git a/servers/fastapi/app_mcp/tools/get_status.py b/servers/fastapi/app_mcp/tools/get_status.py index baae4d0f..6dee2a2f 100644 --- a/servers/fastapi/app_mcp/tools/get_status.py +++ b/servers/fastapi/app_mcp/tools/get_status.py @@ -42,7 +42,7 @@ def register_get_status(mcp, orchestrator): # Provide user-friendly status messages friendly_messages = { "INIT": "Ready to start! Use 'start_presentation' to begin.", - "SUMMARY_GENERATED": "Files processed. Use 'continue_workflow' to generate outline.", + "OUTLINE_REQUESTED": "Generating outline with file analysis if applicable.", "OUTLINE_GENERATED": "Outline created. Use 'continue_workflow' to proceed to layouts.", "OUTLINE_APPROVED": "Outline approved. Use 'choose_layout' to select a theme.", "LAYOUT_SELECTED": "Layout chosen. Use 'continue_workflow' to generate presentation.", @@ -52,8 +52,8 @@ def register_get_status(mcp, orchestrator): next_actions = { "INIT": "start_presentation", - "SUMMARY_GENERATED": "continue_workflow", - "OUTLINE_GENERATED": "continue_workflow (or regenerate_outline)", + "OUTLINE_REQUESTED": "Wait for outline generation to complete", + "OUTLINE_GENERATED": "continue_workflow", "OUTLINE_APPROVED": "choose_layout", "LAYOUT_SELECTED": "continue_workflow", "PRESENTATION_READY": "export_presentation", diff --git a/servers/fastapi/app_mcp/tools/help_me.py b/servers/fastapi/app_mcp/tools/help_me.py index 2fe02867..62c05652 100644 --- a/servers/fastapi/app_mcp/tools/help_me.py +++ b/servers/fastapi/app_mcp/tools/help_me.py @@ -30,7 +30,6 @@ def register_help_me(mcp, orchestrator): "helpful_commands": { "get_status": "๐Ÿ“Š Check your current progress anytime", "show_layouts": "๐Ÿ‘€ Browse available themes and styles", - "regenerate_outline": "๐Ÿ”„ Try a different outline approach", "help": "โ“ Show this helpful guide" }, "quick_start": { diff --git a/servers/fastapi/app_mcp/tools/regenerate_outline.py b/servers/fastapi/app_mcp/tools/regenerate_outline.py deleted file mode 100644 index 2fb9d33e..00000000 --- a/servers/fastapi/app_mcp/tools/regenerate_outline.py +++ /dev/null @@ -1,63 +0,0 @@ -from typing import Dict, Any, Optional, List -from app_mcp.tools.continue_workflow import register_continue_workflow -from app_mcp.services.state_machine.states import PresentationState - - -def register_regenerate_outline(mcp, orchestrator): - """Register all workflow-related tools for chat-based interaction""" - - @mcp.tool("regenerate_outline") - async def regenerate_outline( - session_id: str, - new_prompt: Optional[str] = None, - n_slides: Optional[int] = None, - language: Optional[str] = None - ) -> Dict[str, Any]: - """ - ๐Ÿ”„ Create a new outline with different requirements. - - Not happy with the generated outline? Use this to: - - Try a different angle or focus for your topic - - Change the number of slides - - Adjust the language or tone - - Incorporate new requirements - - Args: - session_id: Your presentation session ID - new_prompt: New description of what you want (optional) - n_slides: Different number of slides (optional) - language: Different language (optional) - """ - try: - fsm = orchestrator.get_session(session_id) - if not fsm: - return {"status": "error", "error": "Session not found"} - - # Update parameters if provided - if new_prompt: - fsm.context.metadata["original_prompt"] = new_prompt - if n_slides: - fsm.context.metadata["n_slides"] = n_slides - if language: - fsm.context.metadata["language"] = language - - # Reset to outline generation - if fsm.can_transition_to(PresentationState.OUTLINE_REQUESTED): - fsm.transition(PresentationState.OUTLINE_REQUESTED) - - # Generate new outline - continue_workflow = register_continue_workflow(mcp, orchestrator) - result = await continue_workflow(session_id=session_id, action="continue") - - if result["status"] == "success": - result["message"] = "I've created a new outline for you:" - - return result - except Exception as e: - return { - "status": "error", - "error": str(e), - "session_id": session_id - } - - return regenerate_outline \ No newline at end of file diff --git a/servers/fastapi/app_mcp/tools/start_presentation.py b/servers/fastapi/app_mcp/tools/start_presentation.py index 159dc5e2..97aac520 100644 --- a/servers/fastapi/app_mcp/tools/start_presentation.py +++ b/servers/fastapi/app_mcp/tools/start_presentation.py @@ -71,20 +71,21 @@ def register_start_presentation(mcp, orchestrator): # Debug log to verify metadata update print("DEBUG: Metadata after update:", fsm.context.metadata) - # Handle files if provided + # Handle files if provided - store them in context for later use if files and len(files) > 0: - result = await orchestrator.execute_upload_and_summarize(session_id, files) - if result["status"] == "error": - return result - + # Store files in context for integrated processing during outline generation + fsm.context.metadata.update({ + "files": files + }) + return { "status": "success", "session_id": session_id, - "message": "Great! I've uploaded and analyzed your files. Here's a summary:", - "summary": result["result"]["summary"], + "message": "Great! I've received your files and will analyze them during presentation creation.", "prompt": prompt, - "suggestion": f"Now I can create a presentation outline based on your prompt '{prompt}' and the file content. Use 'continue_workflow' to proceed.", - "next_step": "Call continue_workflow to generate the outline" + "files_count": len(files), + "suggestion": f"Now I'll create a presentation outline based on your prompt '{prompt}' and analyze the uploaded files. Use 'continue_workflow' to proceed.", + "next_step": "Call continue_workflow to generate the outline with file analysis" } else: # Direct outline generation without files diff --git a/servers/fastapi/app_mcp/wrapper/generate_outline.py b/servers/fastapi/app_mcp/wrapper/generate_outline.py index 5356c728..28b8200f 100644 --- a/servers/fastapi/app_mcp/wrapper/generate_outline.py +++ b/servers/fastapi/app_mcp/wrapper/generate_outline.py @@ -1,35 +1,91 @@ import json -from typing import Dict, Any, Optional +import os +from typing import Dict, Any, Optional, List, Annotated from models.presentation_outline_model import PresentationOutlineModel from utils.llm_calls.generate_presentation_outlines import generate_ppt_outline +from services import TEMP_FILE_SERVICE +from services.documents_loader import DocumentsLoader +from services.score_based_chunker import ScoreBasedChunker +from utils.validators import validate_files +from fastapi import UploadFile, File +from models.sse_response import SSEResponse +from constants.documents import UPLOAD_ACCEPTED_FILE_TYPES +import asyncio + async def generate_outline( prompt: str, n_slides: int = 8, language: str = "English", - summary: Optional[str] = None, + files: Annotated[Optional[List[UploadFile]], File()] = None, ) -> Dict[str, Any]: """ - Generate presentation outlines given a prompt, number of slides, language, and optional summary. + Generate presentation outlines given a prompt, number of slides, language, optional summary, and files. + Files are now processed directly within this function instead of a separate step. Returns the parsed outline data. """ - presentation_content_text = "" - async for chunk in generate_ppt_outline( - prompt, - n_slides, - language, - summary, - ): - presentation_content_text += chunk + validate_files(files, True, True, 50, UPLOAD_ACCEPTED_FILE_TYPES) + + temp_dir = TEMP_FILE_SERVICE.create_temp_dir() + file_paths = [] + if files: + for upload in files: + file_path = os.path.join(temp_dir, upload.filename) + with open(file_path, "wb") as f: + f.write(await upload.read()) + file_paths.append(file_path) + + presentation_outlines = None + additional_context = "" + if file_paths: + documents_loader = DocumentsLoader(file_paths=file_paths) + await documents_loader.load_documents(temp_dir) + documents = documents_loader.documents + if documents: + additional_context = documents[0] + chunker = ScoreBasedChunker() + try: + chunks = await chunker.get_n_chunks(documents[0], n_slides) + presentation_outlines = PresentationOutlineModel( + slides=[chunk.to_slide_outline() for chunk in chunks] + ) + except Exception as e: + print(e) + + if not presentation_outlines: + presentation_outlines_text = "" + async for chunk in generate_ppt_outline( + prompt, + n_slides, + language, + additional_context, + ): + # Give control to the event loop + await asyncio.sleep(0) + presentation_outlines_text += chunk + + presentation_outlines_json = json.loads(presentation_outlines_text) + presentation_outlines = PresentationOutlineModel( + **presentation_outlines_json + ) + + # Truncate slides to n_slides + presentation_outlines.slides = presentation_outlines.slides[:n_slides] + + # Compose title from first slide (if available) + title = "" + if presentation_outlines.slides and hasattr(presentation_outlines.slides[0], '__str__'): + title = str(presentation_outlines.slides[0])[:50] + title = title.replace("#", "").replace("/", "").replace("\\", "").replace("\n", "") + elif presentation_outlines.slides: + title = str(presentation_outlines.slides[0])[:50] + + # Prepare outlines list + outlines = [slide.model_dump() for slide in presentation_outlines.slides] - presentation_content_json = json.loads(presentation_content_text) - presentation_content = PresentationOutlineModel( - **presentation_content_json) - outlines = [slide.model_dump() - for slide in presentation_content.slides[:n_slides]] return { - "title": presentation_content.title, + "title": getattr(presentation_outlines, 'title', title), "outlines": outlines, - "notes": presentation_content.notes + "notes": getattr(presentation_outlines, 'notes', []), } diff --git a/servers/fastapi/app_mcp/wrapper/upload_and_generate_summary.py b/servers/fastapi/app_mcp/wrapper/upload_and_generate_summary.py deleted file mode 100644 index 8107a58b..00000000 --- a/servers/fastapi/app_mcp/wrapper/upload_and_generate_summary.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import List, Dict, Any -from fastapi import UploadFile -from services import TEMP_FILE_SERVICE -from services.documents_loader import DocumentsLoader -from utils.randomizers import get_random_uuid -from utils.llm_calls.generate_document_summary import generate_document_summary - - -# Standalone function for workflow orchestrator -async def upload_and_summarize_files( - files: List[UploadFile] -) -> Dict[str, Any]: - """ - Upload files, generate a document summary, and return both summary and file paths. - """ - if not files: - raise ValueError("No files provided") - temp_dir = TEMP_FILE_SERVICE.create_temp_dir(get_random_uuid()) - file_paths = [] - for upload in files: - temp_path = TEMP_FILE_SERVICE.create_temp_file_path(upload.filename, temp_dir) - with open(temp_path, "wb") as f: - f.write(await upload.read()) - file_paths.append(temp_path) - documents_loader = DocumentsLoader(file_paths=file_paths) - await documents_loader.load_documents(temp_dir) - summary = await generate_document_summary(documents_loader.documents) - return { - "summary": summary, - "file_paths": file_paths, - }