-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: add PptxNodeToolkit with PptxGenJS support #3712
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
aviralgarg05
wants to merge
14
commits into
camel-ai:master
Choose a base branch
from
aviralgarg05:feat/pptx-node-toolkit
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+40,730
−39,680
Open
Changes from 1 commit
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
3d0228d
feat: add PptxNodeToolkit with PptxGenJS support
aviralgarg05 37de5d2
fix: address PR feedback (JSON output, docstrings, extensions)
aviralgarg05 16326b0
fix: ci failures (add license headers, rename variable, restore file …
aviralgarg05 9d753e8
fix: resolve ruff line length violations (E501)
aviralgarg05 c923f67
fix(toolkits): robust PptxNodeToolkit with checks and package.json
aviralgarg05 922388b
fix(toolkits): improve PptxNodeToolkit docstrings and type hints
aviralgarg05 c26c8f6
Address PR feedback: refactor PptxNodeToolkit, add validation, and fi…
aviralgarg05 02d8336
Merge branch 'master' into feat/pptx-node-toolkit
aviralgarg05 2b0664a
feat: address PR review feedback for PptxNodeToolkit
aviralgarg05 e715f75
Merge remote-tracking branch 'origin/master' into feat/pptx-node-toolkit
aviralgarg05 4d29ba4
fix: resolve merge conflicts and workflow failures
aviralgarg05 b2786e2
feat: resolve PR comments and fix CI workflows
aviralgarg05 4be13c6
Merge remote-tracking branch 'origin/master' into feat/pptx-node-toolkit
aviralgarg05 2a00cc7
style: fix end-of-file issues
aviralgarg05 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
|
|
||
| import json | ||
| import os | ||
| import subprocess | ||
| from pathlib import Path | ||
| from typing import Optional | ||
|
|
||
| from camel.logger import get_logger | ||
| from camel.toolkits.base import BaseToolkit | ||
| from camel.toolkits.function_tool import FunctionTool | ||
| from camel.utils import MCPServer | ||
|
|
||
| logger = get_logger(__name__) | ||
|
|
||
|
|
||
| @MCPServer() | ||
| class PptxNodeToolkit(BaseToolkit): | ||
aviralgarg05 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| r"""A toolkit for creating PowerPoint presentations using PptxGenJS (Node.js). | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| working_directory: Optional[str] = None, | ||
| timeout: Optional[float] = None, | ||
| ) -> None: | ||
| r"""Initialize the PptxNodeToolkit. | ||
|
|
||
| Args: | ||
| working_directory (str, optional): The default directory for | ||
| output files. | ||
| timeout (Optional[float]): The timeout for the toolkit. | ||
| (default: :obj:`None`) | ||
| """ | ||
| super().__init__(timeout=timeout) | ||
|
|
||
| if working_directory: | ||
| self.working_directory = Path(working_directory).resolve() | ||
| else: | ||
| camel_workdir = os.environ.get("CAMEL_WORKDIR") | ||
| if camel_workdir: | ||
| self.working_directory = Path(camel_workdir).resolve() | ||
| else: | ||
| self.working_directory = Path("./camel_working_dir").resolve() | ||
|
|
||
| self.working_directory.mkdir(parents=True, exist_ok=True) | ||
|
|
||
| def _resolve_filepath(self, file_path: str) -> Path: | ||
| path_obj = Path(file_path) | ||
| if not path_obj.is_absolute(): | ||
| path_obj = self.working_directory / path_obj | ||
| return path_obj.resolve() | ||
|
|
||
| def create_presentation( | ||
aviralgarg05 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self, | ||
| content: str, | ||
aviralgarg05 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| filename: str, | ||
| ) -> str: | ||
| r"""Create a PowerPoint presentation (PPTX) file using PptxGenJS. | ||
|
|
||
| Args: | ||
| content (str): The content to write to the PPTX file as a JSON | ||
aviralgarg05 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| string. | ||
| filename (str): The name or path of the file. | ||
|
|
||
| Returns: | ||
| str: A success message indicating the file was created. | ||
| """ | ||
| if not filename.lower().endswith('.pptx'): | ||
| filename += '.pptx' | ||
aviralgarg05 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| file_path = self._resolve_filepath(filename) | ||
| script_path = Path(__file__).parent / "scripts" / "generate_pptx.js" | ||
|
|
||
| try: | ||
| # Ensure content is a valid JSON string | ||
| try: | ||
| if not isinstance(content, str): | ||
| content_str = json.dumps(content) | ||
| else: | ||
| # Validate JSON | ||
| json_obj = json.loads(content) | ||
| content_str = json.dumps(json_obj) | ||
| except json.JSONDecodeError: | ||
| return "Error: Content must be valid JSON string representing slides." | ||
|
|
||
| # Run node script | ||
| result = subprocess.run( | ||
| ["node", str(script_path), str(file_path), content_str], | ||
| capture_output=True, | ||
| text=True, | ||
| check=True | ||
| ) | ||
| res = result.stdout.strip() | ||
| return res | ||
aviralgarg05 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| except subprocess.CalledProcessError as e: | ||
| logger.error(f"Error creating presentation: {e.stderr}") | ||
| return f"Error creating presentation: {e.stderr}" | ||
| except Exception as e: | ||
| logger.error(f"Error creating presentation: {str(e)}") | ||
| return f"Error creating presentation: {str(e)}" | ||
|
|
||
| def get_tools(self) -> list[FunctionTool]: | ||
| r"""Returns a list of FunctionTool objects representing the | ||
| functions in the toolkit. | ||
|
|
||
| Returns: | ||
| List[FunctionTool]: A list of FunctionTool objects | ||
| representing the functions in the toolkit. | ||
| """ | ||
| return [FunctionTool(self.create_presentation)] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
|
|
||
| import PptxGenJS from "pptxgenjs"; | ||
| import fs from "fs"; | ||
|
|
||
| // Get arguments | ||
| const args = process.argv.slice(2); | ||
| if (args.length < 2) { | ||
| console.error("Usage: node generate_pptx.js <filename> <content_json>"); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const filename = args[0]; | ||
| const contentJson = args[1]; | ||
|
|
||
| let slidesData; | ||
| try { | ||
| slidesData = JSON.parse(contentJson); | ||
| } catch (e) { | ||
| console.error("Error parsing JSON content:", e); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // Create Presentation | ||
| const pres = new PptxGenJS(); | ||
|
|
||
| // Process slides | ||
| if (Array.isArray(slidesData)) { | ||
| slidesData.forEach((slideData) => { | ||
| const slide = pres.addSlide(); | ||
|
|
||
| // simple layout heuristics based on keys | ||
| if (slideData.title) { | ||
| slide.addText(slideData.title, { x: 1, y: 1, w: "80%", h: 1, fontSize: 24, bold: true, color: "363636" }); | ||
aviralgarg05 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (slideData.subtitle) { | ||
| slide.addText(slideData.subtitle, { x: 1, y: 2.5, w: "80%", h: 1, fontSize: 18, color: "737373" }); | ||
| } | ||
|
|
||
| if (slideData.heading) { | ||
| slide.addText(slideData.heading, { x: 0.5, y: 0.5, w: "90%", h: 0.5, fontSize: 20, bold: true, color: "000000" }); | ||
| } | ||
|
|
||
| if (slideData.bullet_points && Array.isArray(slideData.bullet_points)) { | ||
| const bullets = slideData.bullet_points.map(bp => ({ text: bp, options: { fontSize: 14, bullet: true, color: "000000", breakLine: true } })); | ||
| slide.addText(bullets, { x: 1, y: 1.5, w: "80%", h: 4 }); | ||
| } | ||
|
|
||
| if (slideData.text) { | ||
| slide.addText(slideData.text, { x: 1, y: 1.5, w: "80%", h: 4, fontSize: 14, color: "000000" }); | ||
| } | ||
|
|
||
| // Table support | ||
| if (slideData.table) { | ||
| const tableData = []; | ||
| // headers | ||
| if (slideData.table.headers) { | ||
| tableData.push(slideData.table.headers.map(h => ({ text: h, options: { bold: true, fill: "F7F7F7" } }))); | ||
| } | ||
| // rows | ||
| if (slideData.table.rows) { | ||
| slideData.table.rows.forEach(row => { | ||
| tableData.push(row); | ||
| }); | ||
| } | ||
| slide.addTable(tableData, { x: 1, y: 2, w: "80%" }); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // Save File | ||
| pres.writeFile({ fileName: filename }) | ||
| .then((fileName) => { | ||
| console.log(`PowerPoint presentation successfully created: ${fileName}`); | ||
| }) | ||
| .catch((err) => { | ||
| console.error("Error saving file:", err); | ||
| process.exit(1); | ||
| }); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.