""" Slides/Presentation Generation Tool - Create PowerPoint presentations """ import logging import os import tempfile import base64 from typing import Any, Dict from datetime import datetime from pptx import Presentation from pptx.util import Inches, Pt from pptx.enum.text import PP_ALIGN from pptx.dml.color import RGBColor logger = logging.getLogger(__name__) SLIDES_TOOL_SPEC = { "name": "create_slides", "description": ( "Create a PowerPoint presentation with multiple slides. " "Use this to generate professional presentations with titles, content, and formatting. " "Supports title slides, content slides with bullet points, and section dividers." ), "parameters": { "type": "object", "properties": { "title": { "type": "string", "description": "The main title of the presentation", }, "slides": { "type": "array", "description": "Array of slide objects, each with 'type' (title, content, section), 'title', and 'content' (array of bullet points)", "items": { "type": "object", "properties": { "type": {"type": "string", "enum": ["title", "content", "section"]}, "title": {"type": "string"}, "content": {"type": "array", "items": {"type": "string"}}, }, "required": ["type", "title"], }, }, "filename": { "type": "string", "description": "Output filename for the presentation (e.g., 'presentation.pptx')", }, }, "required": ["title", "slides", "filename"], }, } async def create_slides_handler(arguments: Dict[str, Any]) -> tuple[str, bool]: """Handler for creating PowerPoint presentations.""" try: title = arguments.get("title", "Presentation") slides_data = arguments.get("slides", []) filename = arguments.get("filename", "presentation.pptx") if not slides_data: return "Error: No slides provided", False # Ensure filename has .pptx extension if not filename.endswith(".pptx"): filename += ".pptx" # Create presentation prs = Presentation() prs.slide_width = Inches(13.333) prs.slide_height = Inches(7.5) # Define color scheme title_color = RGBColor(31, 78, 121) # Dark blue text_color = RGBColor(51, 51, 51) # Dark gray for slide_data in slides_data: slide_type = slide_data.get("type", "content") slide_title = slide_data.get("title", "") slide_content = slide_data.get("content", []) if slide_type == "title": # Title slide layout slide_layout = prs.slide_layouts[6] # Blank layout slide = prs.slides.add_slide(slide_layout) # Add title title_box = slide.shapes.add_textbox(Inches(0.5), Inches(2.5), Inches(12.333), Inches(1.5)) title_frame = title_box.text_frame title_frame.text = slide_title title_para = title_frame.paragraphs[0] title_para.font.size = Pt(44) title_para.font.bold = True title_para.font.color.rgb = title_color title_para.alignment = PP_ALIGN.CENTER # Add subtitle subtitle_box = slide.shapes.add_textbox(Inches(0.5), Inches(4.2), Inches(12.333), Inches(1)) subtitle_frame = subtitle_box.text_frame subtitle_frame.text = title subtitle_para = subtitle_frame.paragraphs[0] subtitle_para.font.size = Pt(24) subtitle_para.font.color.rgb = text_color subtitle_para.alignment = PP_ALIGN.CENTER elif slide_type == "section": # Section divider slide slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(slide_layout) # Add section title title_box = slide.shapes.add_textbox(Inches(0.5), Inches(3), Inches(12.333), Inches(1.5)) title_frame = title_box.text_frame title_frame.text = slide_title title_para = title_frame.paragraphs[0] title_para.font.size = Pt(40) title_para.font.bold = True title_para.font.color.rgb = title_color title_para.alignment = PP_ALIGN.CENTER else: # content slide slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(slide_layout) # Add slide title title_box = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12.333), Inches(1)) title_frame = title_box.text_frame title_frame.text = slide_title title_para = title_frame.paragraphs[0] title_para.font.size = Pt(32) title_para.font.bold = True title_para.font.color.rgb = title_color # Add content if slide_content: content_box = slide.shapes.add_textbox(Inches(0.7), Inches(1.5), Inches(12), Inches(5.5)) content_frame = content_box.text_frame content_frame.word_wrap = True for i, bullet in enumerate(slide_content): if i == 0: p = content_frame.paragraphs[0] else: p = content_frame.add_paragraph() p.text = f"• {bullet}" p.font.size = Pt(18) p.font.color.rgb = text_color p.space_after = Pt(12) # Save presentation output_dir = os.path.join(tempfile.gettempdir(), "kimi_agent_outputs") os.makedirs(output_dir, exist_ok=True) output_path = os.path.join(output_dir, filename) prs.save(output_path) # Also save to current working directory for easy access prs.save(filename) file_size = os.path.getsize(output_path) # Read file content for the event with open(output_path, "rb") as f: file_content = f.read() file_content_b64 = base64.b64encode(file_content).decode() # Create file data for file_generated event file_data = { "id": f"file_{datetime.now().strftime('%Y%m%d%H%M%S')}_{filename}", "name": filename, "type": "document", "content": file_content_b64, "language": None, "originalContent": None, "modifiedContent": file_content_b64, "size": file_size, "created_at": datetime.now().isoformat(), } return ( f"✅ Presentation created successfully!\n" f"📊 Filename: {filename}\n" f"📄 Total slides: {len(slides_data)}\n" f"💾 File size: {file_size / 1024:.2f} KB\n" f"📁 Saved to: {output_path}", True, file_data # Return file data for event ) except Exception as e: logger.error(f"Slides creation error: {e}") return f"❌ Error creating presentation: {str(e)}", False