water3 / agent /tools /slides_tool.py
onewayto's picture
Upload 187 files
070daf8 verified
"""
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