@_vrsen/openswarm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/bin/openswarm.js +38 -0
- package/config.py +34 -0
- package/data_analyst_agent/.cursor/rules/data_analyst.mdc +43 -0
- package/data_analyst_agent/__init__.py +3 -0
- package/data_analyst_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/data_analyst_agent/__pycache__/data_analyst_agent.cpython-312.pyc +0 -0
- package/data_analyst_agent/data_analyst_agent.py +45 -0
- package/data_analyst_agent/instructions.md +173 -0
- package/data_analyst_agent/test_files/test_file.csv +21 -0
- package/data_analyst_agent/tools/__init__.py +6 -0
- package/deep_research/__init__.py +1 -0
- package/deep_research/__pycache__/__init__.cpython-312.pyc +0 -0
- package/deep_research/__pycache__/deep_research.cpython-312.pyc +0 -0
- package/deep_research/deep_research.py +27 -0
- package/deep_research/instructions.md +104 -0
- package/deep_research/tools/__init__.py +1 -0
- package/docs_agent/__init__.py +3 -0
- package/docs_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/__pycache__/docs_agent.cpython-312.pyc +0 -0
- package/docs_agent/docs_agent.py +61 -0
- package/docs_agent/instructions.md +418 -0
- package/docs_agent/tools/ConvertDocument.py +323 -0
- package/docs_agent/tools/CreateDocument.py +287 -0
- package/docs_agent/tools/ListDocuments.py +134 -0
- package/docs_agent/tools/ModifyDocument.py +247 -0
- package/docs_agent/tools/RestoreDocument.py +79 -0
- package/docs_agent/tools/ViewDocument.py +153 -0
- package/docs_agent/tools/__init__.py +1 -0
- package/docs_agent/tools/__pycache__/ConvertDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/CreateDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ListDocuments.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ModifyDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/RestoreDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/ViewDocument.cpython-312.pyc +0 -0
- package/docs_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__init__.py +1 -0
- package/docs_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/doc_file_utils.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_blocks.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_constants.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_core.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_css.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_images.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_page.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_paragraphs.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_playwright.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_selectors.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_docx_shared.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/__pycache__/html_validation.cpython-312.pyc +0 -0
- package/docs_agent/tools/utils/doc_file_utils.py +29 -0
- package/docs_agent/tools/utils/html_docx_blocks.py +262 -0
- package/docs_agent/tools/utils/html_docx_constants.py +78 -0
- package/docs_agent/tools/utils/html_docx_core.py +138 -0
- package/docs_agent/tools/utils/html_docx_css.py +262 -0
- package/docs_agent/tools/utils/html_docx_images.py +293 -0
- package/docs_agent/tools/utils/html_docx_page.py +185 -0
- package/docs_agent/tools/utils/html_docx_paragraphs.py +342 -0
- package/docs_agent/tools/utils/html_docx_playwright.py +184 -0
- package/docs_agent/tools/utils/html_docx_selectors.py +196 -0
- package/docs_agent/tools/utils/html_docx_shared.py +23 -0
- package/docs_agent/tools/utils/html_docx_tables.py +942 -0
- package/docs_agent/tools/utils/html_validation.py +102 -0
- package/helpers.py +59 -0
- package/image_generation_agent/__init__.py +1 -0
- package/image_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/image_generation_agent/__pycache__/image_generation_agent.cpython-312.pyc +0 -0
- package/image_generation_agent/image_generation_agent.py +31 -0
- package/image_generation_agent/instructions.md +80 -0
- package/image_generation_agent/tools/CombineImages.py +211 -0
- package/image_generation_agent/tools/EditImages.py +200 -0
- package/image_generation_agent/tools/GenerateImages.py +184 -0
- package/image_generation_agent/tools/RemoveBackground.py +108 -0
- package/image_generation_agent/tools/__init__.py +2 -0
- package/image_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/EditImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/GenerateImages.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/__pycache__/RemoveBackground.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/__init__.py +2 -0
- package/image_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/__pycache__/image_io.cpython-312.pyc +0 -0
- package/image_generation_agent/tools/utils/image_io.py +308 -0
- package/onboard.py +325 -0
- package/orchestrator/__init__.py +3 -0
- package/orchestrator/__pycache__/__init__.cpython-312.pyc +0 -0
- package/orchestrator/__pycache__/orchestrator.cpython-312.pyc +0 -0
- package/orchestrator/instructions.md +90 -0
- package/orchestrator/orchestrator.py +33 -0
- package/package.json +49 -0
- package/patches/__init__.py +1 -0
- package/patches/__pycache__/__init__.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_agency_swarm_dual_comms.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_file_attachment_refs.cpython-312.pyc +0 -0
- package/patches/__pycache__/patch_ipython_interpreter_composio.cpython-312.pyc +0 -0
- package/patches/dom-to-pptx+1.1.5.patch +133440 -0
- package/patches/patch_agency_swarm_dual_comms.py +199 -0
- package/patches/patch_file_attachment_refs.py +74 -0
- package/patches/patch_ipython_interpreter_composio.py +54 -0
- package/pyproject.toml +67 -0
- package/run.py +343 -0
- package/server.py +26 -0
- package/shared_instructions.md +119 -0
- package/shared_tools/CopyFile.py +68 -0
- package/shared_tools/ExecuteTool.py +184 -0
- package/shared_tools/FindTools.py +101 -0
- package/shared_tools/ManageConnections.py +43 -0
- package/shared_tools/SearchTools.py +44 -0
- package/shared_tools/__init__.py +7 -0
- package/shared_tools/__pycache__/CopyFile.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/ExecuteTool.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/FindTools.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/ManageConnections.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/SearchTools.cpython-312.pyc +0 -0
- package/shared_tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/.cursor/rules/slides-agent-workflow.mdc +9 -0
- package/slides_agent/__init__.py +1 -0
- package/slides_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/__pycache__/slides_agent.cpython-312.pyc +0 -0
- package/slides_agent/instructions.md +298 -0
- package/slides_agent/pptx/SKILL.md +528 -0
- package/slides_agent/pptx/html2pptx.md +625 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/slides_agent/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/slides_agent/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/slides_agent/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/slides_agent/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/slides_agent/pptx/ooxml/scripts/pack.py +169 -0
- package/slides_agent/pptx/ooxml/scripts/unpack.py +29 -0
- package/slides_agent/pptx/ooxml/scripts/validate.py +69 -0
- package/slides_agent/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/slides_agent/pptx/ooxml/scripts/validation/base.py +951 -0
- package/slides_agent/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/slides_agent/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/slides_agent/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/slides_agent/pptx/ooxml.md +427 -0
- package/slides_agent/pptx/scripts/html2pptx.js +1092 -0
- package/slides_agent/pptx/scripts/inventory.py +1020 -0
- package/slides_agent/pptx/scripts/rearrange.py +231 -0
- package/slides_agent/pptx/scripts/replace.py +385 -0
- package/slides_agent/pptx/scripts/thumbnail.py +451 -0
- package/slides_agent/slides_agent.py +109 -0
- package/slides_agent/test_deck/_theme.css +92 -0
- package/slides_agent/test_deck/assets/placeholder.svg +11 -0
- package/slides_agent/test_deck/slide_01_title.html +10 -0
- package/slides_agent/test_deck/slide_02_image_split.html +23 -0
- package/slides_agent/test_deck/slide_03_kpi.html +21 -0
- package/slides_agent/tools/ApplyPptxTextReplacements.py +91 -0
- package/slides_agent/tools/BuildPptxFromHtmlSlides.py +221 -0
- package/slides_agent/tools/CheckSlide.py +218 -0
- package/slides_agent/tools/CheckSlideCanvasOverflow.py +221 -0
- package/slides_agent/tools/CreateImageMontage.py +261 -0
- package/slides_agent/tools/CreatePptxThumbnailGrid.py +168 -0
- package/slides_agent/tools/DeleteSlide.py +78 -0
- package/slides_agent/tools/DownloadImage.py +79 -0
- package/slides_agent/tools/EnsureRasterImage.py +157 -0
- package/slides_agent/tools/ExtractPptxTextInventory.py +104 -0
- package/slides_agent/tools/GenerateImage.py +189 -0
- package/slides_agent/tools/ImageSearch.py +127 -0
- package/slides_agent/tools/InsertNewSlides.py +393 -0
- package/slides_agent/tools/ManageTheme.py +112 -0
- package/slides_agent/tools/ModifySlide.py +563 -0
- package/slides_agent/tools/ReadSlide.py +26 -0
- package/slides_agent/tools/RearrangePptxSlidesFromTemplate.py +114 -0
- package/slides_agent/tools/RestoreSnapshot.py +89 -0
- package/slides_agent/tools/SlideScreenshot.py +66 -0
- package/slides_agent/tools/__init__.py +54 -0
- package/slides_agent/tools/__pycache__/ApplyPptxTextReplacements.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/BuildPptxFromHtmlSlides.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CheckSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CheckSlideCanvasOverflow.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CreateImageMontage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/CreatePptxThumbnailGrid.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/DeleteSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/DownloadImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/EnsureRasterImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ExtractPptxTextInventory.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ImageSearch.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/InsertNewSlides.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ManageTheme.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ModifySlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/ReadSlide.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/RearrangePptxSlidesFromTemplate.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/RestoreSnapshot.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/SlideScreenshot.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/slide_file_utils.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/slide_html_utils.cpython-312.pyc +0 -0
- package/slides_agent/tools/__pycache__/template_registry.cpython-312.pyc +0 -0
- package/slides_agent/tools/deck_utils.py +31 -0
- package/slides_agent/tools/html2pptx_runner.js +1183 -0
- package/slides_agent/tools/html_writer_instructions.md +149 -0
- package/slides_agent/tools/slide_file_utils.py +108 -0
- package/slides_agent/tools/slide_html_utils.py +354 -0
- package/slides_agent/tools/template_registry.py +55 -0
- package/swarm.py +82 -0
- package/video_generation_agent/__init__.py +1 -0
- package/video_generation_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/video_generation_agent/__pycache__/video_generation_agent.cpython-312.pyc +0 -0
- package/video_generation_agent/instructions.md +178 -0
- package/video_generation_agent/tools/AddSubtitles.py +425 -0
- package/video_generation_agent/tools/CombineImages.py +166 -0
- package/video_generation_agent/tools/CombineVideos.py +113 -0
- package/video_generation_agent/tools/EditAudio.py +297 -0
- package/video_generation_agent/tools/EditImage.py +144 -0
- package/video_generation_agent/tools/EditVideoContent.py +369 -0
- package/video_generation_agent/tools/GenerateImage.py +133 -0
- package/video_generation_agent/tools/GenerateVideo.py +556 -0
- package/video_generation_agent/tools/TrimVideo.py +233 -0
- package/video_generation_agent/tools/__init__.py +1 -0
- package/video_generation_agent/tools/__pycache__/AddSubtitles.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/CombineImages.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/CombineVideos.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditAudio.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditImage.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/EditVideoContent.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/GenerateImage.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/GenerateVideo.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/__pycache__/TrimVideo.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__init__.py +1 -0
- package/video_generation_agent/tools/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__pycache__/image_utils.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/__pycache__/video_utils.cpython-312.pyc +0 -0
- package/video_generation_agent/tools/utils/image_utils.py +174 -0
- package/video_generation_agent/tools/utils/video_utils.py +522 -0
- package/video_generation_agent/video_generation_agent.py +26 -0
- package/virtual_assistant/__init__.py +1 -0
- package/virtual_assistant/__pycache__/__init__.cpython-312.pyc +0 -0
- package/virtual_assistant/__pycache__/virtual_assistant.cpython-312.pyc +0 -0
- package/virtual_assistant/instructions.md +206 -0
- package/virtual_assistant/tools/AddLabelToEmail.py +154 -0
- package/virtual_assistant/tools/CheckEventsForDate.py +218 -0
- package/virtual_assistant/tools/CheckUnreadSlackMessages.py +216 -0
- package/virtual_assistant/tools/CreateCalendarEvent.py +261 -0
- package/virtual_assistant/tools/DeleteCalendarEvent.py +137 -0
- package/virtual_assistant/tools/DeleteDraft.py +95 -0
- package/virtual_assistant/tools/DraftEmail.py +239 -0
- package/virtual_assistant/tools/EditFile.py +113 -0
- package/virtual_assistant/tools/FindEmails.py +330 -0
- package/virtual_assistant/tools/GetCurrentTime.py +69 -0
- package/virtual_assistant/tools/GetSlackUserInfo.py +117 -0
- package/virtual_assistant/tools/ListDirectory.py +113 -0
- package/virtual_assistant/tools/ListSkills.py +94 -0
- package/virtual_assistant/tools/ManageLabels.py +295 -0
- package/virtual_assistant/tools/ProductSearch.py +254 -0
- package/virtual_assistant/tools/ReadEmail.py +251 -0
- package/virtual_assistant/tools/ReadFile.py +108 -0
- package/virtual_assistant/tools/ReadSlackMessages.py +191 -0
- package/virtual_assistant/tools/RemoveLabelFromEmail.py +137 -0
- package/virtual_assistant/tools/RescheduleCalendarEvent.py +227 -0
- package/virtual_assistant/tools/ScholarSearch.py +216 -0
- package/virtual_assistant/tools/SendDraft.py +101 -0
- package/virtual_assistant/tools/SendSlackMessage.py +148 -0
- package/virtual_assistant/tools/WriteFile.py +95 -0
- package/virtual_assistant/tools/__init__.py +1 -0
- package/virtual_assistant/tools/__pycache__/AddLabelToEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CheckEventsForDate.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CheckUnreadSlackMessages.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/CreateCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DeleteCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DeleteDraft.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/DraftEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/EditFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/FindEmails.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/GetCurrentTime.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/GetSlackUserInfo.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ListDirectory.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ListSkills.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ManageLabels.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ProductSearch.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ReadSlackMessages.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/RemoveLabelFromEmail.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/RescheduleCalendarEvent.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/ScholarSearch.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/SendDraft.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/SendSlackMessage.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/WriteFile.cpython-312.pyc +0 -0
- package/virtual_assistant/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/virtual_assistant/virtual_assistant.py +52 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<div class="canvas">
|
|
2
|
+
<div class="title-block">
|
|
3
|
+
<p class="kicker">METRICS</p>
|
|
4
|
+
<h2>KPI layout with three cards</h2>
|
|
5
|
+
<p class="small">Use big numbers and short labels for fast scanning.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="kpi-row">
|
|
8
|
+
<div class="kpi-card">
|
|
9
|
+
<p class="kpi-value">3.2x</p>
|
|
10
|
+
<p class="kpi-label">Faster turnaround</p>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="kpi-card">
|
|
13
|
+
<p class="kpi-value">18%</p>
|
|
14
|
+
<p class="kpi-label">Lower review time</p>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="kpi-card">
|
|
17
|
+
<p class="kpi-value">92</p>
|
|
18
|
+
<p class="kpi-label">Quality score</p>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Apply text replacements to a PowerPoint presentation."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from agency_swarm.tools import BaseTool
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
# Add pptx/scripts to path for replace module
|
|
10
|
+
PPTX_SCRIPTS_DIR = Path(__file__).parent.parent / "pptx" / "scripts"
|
|
11
|
+
sys.path.insert(0, str(PPTX_SCRIPTS_DIR))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ApplyPptxTextReplacements(BaseTool):
|
|
15
|
+
"""
|
|
16
|
+
Apply text replacements to shapes in a PowerPoint presentation.
|
|
17
|
+
|
|
18
|
+
Takes a PPTX file and a JSON file containing replacement paragraphs,
|
|
19
|
+
then applies the replacements while preserving formatting. All text
|
|
20
|
+
shapes identified in the inventory will have their text cleared unless
|
|
21
|
+
"paragraphs" is specified for that shape in the replacements JSON.
|
|
22
|
+
|
|
23
|
+
The replacements JSON should follow the structure from ExtractPptxTextInventory:
|
|
24
|
+
{
|
|
25
|
+
"slide-0": {
|
|
26
|
+
"shape-0": {
|
|
27
|
+
"paragraphs": [
|
|
28
|
+
{"text": "New title", "bold": true, "alignment": "CENTER"},
|
|
29
|
+
{"text": "Bullet point", "bullet": true, "level": 0}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
The tool validates that:
|
|
36
|
+
- All referenced shapes exist in the presentation
|
|
37
|
+
- Text overflow does not worsen after replacements
|
|
38
|
+
- No bullet formatting warnings are triggered
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
input_pptx: str = Field(
|
|
42
|
+
...,
|
|
43
|
+
description="Path to the input PowerPoint file (.pptx)",
|
|
44
|
+
)
|
|
45
|
+
replacements_json: str = Field(
|
|
46
|
+
...,
|
|
47
|
+
description="Path to the JSON file containing replacement paragraphs",
|
|
48
|
+
)
|
|
49
|
+
output_pptx: str = Field(
|
|
50
|
+
...,
|
|
51
|
+
description="Path where the updated presentation will be saved",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def run(self) -> str:
|
|
55
|
+
"""Apply replacements and save the updated presentation."""
|
|
56
|
+
import sys
|
|
57
|
+
from pathlib import Path as PathLib
|
|
58
|
+
|
|
59
|
+
# Add pptx/scripts to path for replace import
|
|
60
|
+
scripts_dir = PathLib(__file__).parent.parent / "pptx" / "scripts"
|
|
61
|
+
if str(scripts_dir) not in sys.path:
|
|
62
|
+
sys.path.insert(0, str(scripts_dir))
|
|
63
|
+
|
|
64
|
+
from replace import apply_replacements # type: ignore
|
|
65
|
+
|
|
66
|
+
input_path = Path(self.input_pptx)
|
|
67
|
+
json_path = Path(self.replacements_json)
|
|
68
|
+
output_path = Path(self.output_pptx)
|
|
69
|
+
|
|
70
|
+
# Validate inputs
|
|
71
|
+
if not input_path.exists():
|
|
72
|
+
return f"Error: Input file not found: {self.input_pptx}"
|
|
73
|
+
if input_path.suffix.lower() != ".pptx":
|
|
74
|
+
return f"Error: Input must be a PowerPoint file (.pptx), got: {input_path.suffix}"
|
|
75
|
+
if not json_path.exists():
|
|
76
|
+
return f"Error: Replacements JSON not found: {self.replacements_json}"
|
|
77
|
+
|
|
78
|
+
# Ensure output directory exists
|
|
79
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
|
|
81
|
+
# Apply replacements
|
|
82
|
+
try:
|
|
83
|
+
apply_replacements(str(input_path), str(json_path), str(output_path))
|
|
84
|
+
return f"Presentation saved to: {output_path}\nReplacements applied successfully"
|
|
85
|
+
except ValueError as e:
|
|
86
|
+
return f"Error: {e}"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
print("ApplyPptxTextReplacements tool definition is valid.")
|
|
91
|
+
print("Requires input PPTX and replacements JSON to test.")
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Convert HTML slides to an editable PowerPoint presentation via dom-to-pptx."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from agency_swarm.tools import BaseTool
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
|
|
12
|
+
from .slide_file_utils import get_project_dir, next_pptx_version
|
|
13
|
+
|
|
14
|
+
RUNNER_JS = Path(__file__).parent / "html2pptx_runner.js"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BuildPptxFromHtmlSlides(BaseTool):
|
|
18
|
+
"""
|
|
19
|
+
Convert HTML slides to a fully editable PowerPoint presentation.
|
|
20
|
+
|
|
21
|
+
Uses dom-to-pptx (via Playwright) to measure every element's exact computed
|
|
22
|
+
position and style, then maps them to native PPTX shapes and text boxes.
|
|
23
|
+
CSS gradients are converted to vector SVGs, inline SVGs are kept as editable
|
|
24
|
+
vectors, custom fonts are auto-embedded, and text remains fully editable.
|
|
25
|
+
|
|
26
|
+
The output file is auto-versioned: if my_deck.pptx already exists the tool
|
|
27
|
+
saves my_deck_v2.pptx, then my_deck_v3.pptx, etc. Previous exports and their
|
|
28
|
+
snapshots are never overwritten.
|
|
29
|
+
|
|
30
|
+
Requires: Node.js with local node_modules (dom-to-pptx, playwright)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
project_name: str = Field(
|
|
34
|
+
...,
|
|
35
|
+
description="Presentation project folder name (e.g. 'my_pitch')",
|
|
36
|
+
)
|
|
37
|
+
slide_names: List[str] = Field(
|
|
38
|
+
...,
|
|
39
|
+
description=(
|
|
40
|
+
"Ordered list of slide names to include, e.g. ['slide_01', 'slide_02']. "
|
|
41
|
+
"Use bare names (without .html) or include the extension — both work."
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
output_filename: str = Field(
|
|
45
|
+
...,
|
|
46
|
+
description=(
|
|
47
|
+
"Output filename stem, e.g. 'my_deck' (saved as my_deck.pptx inside "
|
|
48
|
+
"the project folder). Including the .pptx extension is also accepted."
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
layout: str = Field(
|
|
52
|
+
default="LAYOUT_16x9_1280",
|
|
53
|
+
description=(
|
|
54
|
+
"Presentation layout: LAYOUT_16x9_1280 (1280x720 HTML), "
|
|
55
|
+
"LAYOUT_16x9_1920 (1920x1080 HTML), LAYOUT_16x9, LAYOUT_4x3, or LAYOUT_16x10"
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
tmp_dir: Optional[str] = Field(
|
|
59
|
+
default=None,
|
|
60
|
+
description="Optional temporary directory for intermediate files",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def run(self) -> str:
|
|
64
|
+
"""Convert HTML slides to PPTX."""
|
|
65
|
+
project_dir = get_project_dir(self.project_name)
|
|
66
|
+
|
|
67
|
+
html_paths = self._resolve_slide_paths(self.slide_names, project_dir)
|
|
68
|
+
if isinstance(html_paths, str):
|
|
69
|
+
return html_paths # error message
|
|
70
|
+
|
|
71
|
+
if not html_paths:
|
|
72
|
+
return "Error: No slide names provided"
|
|
73
|
+
|
|
74
|
+
valid_layouts = ["LAYOUT_16x9_1280", "LAYOUT_16x9_1920", "LAYOUT_16x9", "LAYOUT_4x3", "LAYOUT_16x10"]
|
|
75
|
+
if self.layout not in valid_layouts:
|
|
76
|
+
return f"Error: Invalid layout '{self.layout}'. Must be one of: {', '.join(valid_layouts)}"
|
|
77
|
+
|
|
78
|
+
output_path = next_pptx_version(self._resolve_output_path(self.output_filename, project_dir))
|
|
79
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
|
|
81
|
+
if not self._check_node():
|
|
82
|
+
return "Error: Node.js not found. Please install Node.js."
|
|
83
|
+
|
|
84
|
+
node_modules = Path(__file__).parent.parent.parent / "node_modules"
|
|
85
|
+
if not node_modules.exists():
|
|
86
|
+
return (
|
|
87
|
+
"Error: node_modules not found. Please run 'npm install' in the project root.\n"
|
|
88
|
+
f"Expected location: {node_modules}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if not RUNNER_JS.exists():
|
|
92
|
+
return f"Error: Runner script not found at {RUNNER_JS}"
|
|
93
|
+
|
|
94
|
+
tmp_dir = self.tmp_dir or tempfile.mkdtemp(prefix="html2pptx_")
|
|
95
|
+
|
|
96
|
+
cmd = [
|
|
97
|
+
"node",
|
|
98
|
+
str(RUNNER_JS),
|
|
99
|
+
"--output", str(output_path),
|
|
100
|
+
"--layout", self.layout,
|
|
101
|
+
"--tmp-dir", tmp_dir,
|
|
102
|
+
"--",
|
|
103
|
+
] + html_paths
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
kwargs = {
|
|
107
|
+
"capture_output": True,
|
|
108
|
+
"text": True,
|
|
109
|
+
"timeout": 300,
|
|
110
|
+
"cwd": str(Path(__file__).parent.parent.parent),
|
|
111
|
+
}
|
|
112
|
+
if os.name == "nt":
|
|
113
|
+
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
114
|
+
|
|
115
|
+
result = subprocess.run(cmd, **kwargs)
|
|
116
|
+
|
|
117
|
+
if result.returncode != 0:
|
|
118
|
+
error_msg = result.stderr.strip() or result.stdout.strip()
|
|
119
|
+
return f"Error converting HTML to PPTX:\n{error_msg}"
|
|
120
|
+
|
|
121
|
+
self._write_snapshots(html_paths, output_path)
|
|
122
|
+
snapshot_dir = output_path.parent / f"{output_path.name}.slides"
|
|
123
|
+
return (
|
|
124
|
+
f"Presentation saved to: {output_path}\n"
|
|
125
|
+
f"Snapshot saved to: {snapshot_dir}\n"
|
|
126
|
+
f"Converted {len(html_paths)} slide(s)"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
except subprocess.TimeoutExpired:
|
|
130
|
+
return "Error: Conversion timed out after 5 minutes"
|
|
131
|
+
except Exception as e:
|
|
132
|
+
return f"Error running html2pptx: {e}"
|
|
133
|
+
|
|
134
|
+
# ------------------------------------------------------------------
|
|
135
|
+
# Internal helpers
|
|
136
|
+
# ------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
def _resolve_slide_paths(self, slide_names: List[str], project_dir: Path) -> list[str] | str:
|
|
139
|
+
"""Resolve slide name strings to absolute .html paths."""
|
|
140
|
+
paths = []
|
|
141
|
+
for name in slide_names:
|
|
142
|
+
path = project_dir / (name if name.endswith(".html") else f"{name}.html")
|
|
143
|
+
if not path.exists():
|
|
144
|
+
return f"Error: Slide not found: {name} (looked for {path})"
|
|
145
|
+
paths.append(str(path.absolute()))
|
|
146
|
+
return paths
|
|
147
|
+
|
|
148
|
+
def _resolve_output_path(self, output_filename: str, project_dir: Path) -> Path:
|
|
149
|
+
"""Return the full output .pptx path from a stem or filename."""
|
|
150
|
+
stem = Path(output_filename).stem
|
|
151
|
+
return project_dir / f"{stem}.pptx"
|
|
152
|
+
|
|
153
|
+
def _write_snapshots(self, html_paths: list[str], output_pptx: Path) -> None:
|
|
154
|
+
"""Write self-contained HTML snapshots to <output>.pptx.slides/1.html, 2.html, …"""
|
|
155
|
+
slides_dir = output_pptx.parent / f"{output_pptx.name}.slides"
|
|
156
|
+
slides_dir.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
|
|
158
|
+
for i, html_path in enumerate(html_paths, start=1):
|
|
159
|
+
src = Path(html_path)
|
|
160
|
+
html = src.read_text(encoding="utf-8")
|
|
161
|
+
html = self._inline_theme_css(html, src.parent)
|
|
162
|
+
(slides_dir / f"{i}.html").write_text(html, encoding="utf-8")
|
|
163
|
+
|
|
164
|
+
def _inline_theme_css(self, html: str, slide_dir: Path) -> str:
|
|
165
|
+
"""Inline local <link rel="stylesheet"> tags into sentinel-marked <style> blocks.
|
|
166
|
+
|
|
167
|
+
External URLs (http/https/protocol-relative) are left as-is.
|
|
168
|
+
Local files are inlined as:
|
|
169
|
+
<!-- css-snapshot:<filename>:start -->
|
|
170
|
+
<style>…</style>
|
|
171
|
+
<!-- css-snapshot:<filename>:end -->
|
|
172
|
+
so that RestoreSnapshot can reverse the operation exactly.
|
|
173
|
+
"""
|
|
174
|
+
import re
|
|
175
|
+
|
|
176
|
+
def replace_link(match: re.Match) -> str:
|
|
177
|
+
href = match.group(1)
|
|
178
|
+
if href.startswith(("http://", "https://", "//")):
|
|
179
|
+
return match.group(0)
|
|
180
|
+
css_path = (slide_dir / href).resolve()
|
|
181
|
+
if not css_path.exists():
|
|
182
|
+
return match.group(0)
|
|
183
|
+
filename = css_path.name
|
|
184
|
+
css = css_path.read_text(encoding="utf-8")
|
|
185
|
+
return (
|
|
186
|
+
f"<!-- css-snapshot:{filename}:start -->\n"
|
|
187
|
+
f"<style>\n{css}\n</style>\n"
|
|
188
|
+
f"<!-- css-snapshot:{filename}:end -->"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return re.sub(
|
|
192
|
+
r'<link\b[^>]*\brel=["\']stylesheet["\'][^>]*\bhref=["\']([^"\']+)["\'][^>]*>',
|
|
193
|
+
replace_link,
|
|
194
|
+
html,
|
|
195
|
+
flags=re.IGNORECASE,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
def _check_node(self) -> bool:
|
|
199
|
+
"""Check if Node.js is available."""
|
|
200
|
+
try:
|
|
201
|
+
kwargs = {
|
|
202
|
+
"capture_output": True,
|
|
203
|
+
"timeout": 5,
|
|
204
|
+
"text": True,
|
|
205
|
+
}
|
|
206
|
+
if os.name == "nt":
|
|
207
|
+
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
208
|
+
subprocess.run(["node", "--version"], **kwargs)
|
|
209
|
+
return True
|
|
210
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
if __name__ == "__main__":
|
|
215
|
+
tool = BuildPptxFromHtmlSlides(
|
|
216
|
+
project_name="dinosaur_presentation_v2",
|
|
217
|
+
slide_names=["slide_01", "slide_02", "slide_03", "slide_04", "slide_05"],
|
|
218
|
+
output_filename="dinosaur_presentation_v2",
|
|
219
|
+
layout="LAYOUT_16x9_1280",
|
|
220
|
+
)
|
|
221
|
+
print(tool.run())
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Render a single slide to image and load it as attachment."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import inspect
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from agency_swarm.tools import BaseTool, LoadFileAttachment
|
|
13
|
+
from pydantic import Field
|
|
14
|
+
|
|
15
|
+
PPTX_SCRIPTS_DIR = Path(__file__).parent.parent / "pptx" / "scripts"
|
|
16
|
+
sys.path.insert(0, str(PPTX_SCRIPTS_DIR))
|
|
17
|
+
|
|
18
|
+
_SLIDE_VIEWPORT = {"width": 1280, "height": 720}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CheckSlide(BaseTool):
|
|
22
|
+
"""
|
|
23
|
+
Render a single slide (HTML or PPTX) to an image and return a file attachment.
|
|
24
|
+
|
|
25
|
+
Allows you to see the slide so you can inspect it for spacing, alignment, and other issues.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
slide_path: str = Field(
|
|
29
|
+
...,
|
|
30
|
+
description="Path to a single HTML slide or a PPTX file",
|
|
31
|
+
)
|
|
32
|
+
output_image_path: Optional[str] = Field(
|
|
33
|
+
default=None,
|
|
34
|
+
description="Optional output image path (.jpg). Defaults next to slide.",
|
|
35
|
+
)
|
|
36
|
+
layout: str = Field(
|
|
37
|
+
default="LAYOUT_16x9_1280",
|
|
38
|
+
description="Layout for HTML slides: LAYOUT_16x9_1280 or LAYOUT_16x9_1920",
|
|
39
|
+
)
|
|
40
|
+
slide_index: int = Field(
|
|
41
|
+
default=1,
|
|
42
|
+
description="1-based slide index when rendering a PPTX with multiple slides",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def run(self):
|
|
46
|
+
input_path = Path(self.slide_path)
|
|
47
|
+
if not input_path.exists():
|
|
48
|
+
return f"Error: Slide not found: {self.slide_path}"
|
|
49
|
+
|
|
50
|
+
if input_path.suffix.lower() == ".pptx":
|
|
51
|
+
if not self._check_soffice():
|
|
52
|
+
return "Error: LibreOffice (soffice) not found. Please install LibreOffice."
|
|
53
|
+
if not self._check_pdftoppm():
|
|
54
|
+
return "Error: pdftoppm not found. Please install Poppler (poppler-utils)."
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
if input_path.suffix.lower() == ".html":
|
|
58
|
+
image_path = self._screenshot_html(input_path)
|
|
59
|
+
elif input_path.suffix.lower() == ".pptx":
|
|
60
|
+
image_path = self._render_pptx_slide(input_path, input_path)
|
|
61
|
+
else:
|
|
62
|
+
return "Error: slide_path must be a .html or .pptx file."
|
|
63
|
+
|
|
64
|
+
attachment = LoadFileAttachment(path=str(image_path))
|
|
65
|
+
return self._run_attachment(attachment)
|
|
66
|
+
except Exception as exc:
|
|
67
|
+
return f"Error checking slide: {exc}"
|
|
68
|
+
|
|
69
|
+
def _screenshot_html(self, html_path: Path) -> Path:
|
|
70
|
+
"""Direct Playwright screenshot — fast, no PPTX/PDF round-trip."""
|
|
71
|
+
from playwright.sync_api import sync_playwright
|
|
72
|
+
from PIL import Image
|
|
73
|
+
|
|
74
|
+
output_path = self._resolve_output_path(html_path)
|
|
75
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
|
|
77
|
+
with sync_playwright() as pw:
|
|
78
|
+
browser = pw.chromium.launch(headless=True)
|
|
79
|
+
page = browser.new_page(viewport=_SLIDE_VIEWPORT)
|
|
80
|
+
page.goto(html_path.resolve().as_uri(), wait_until="load", timeout=20_000)
|
|
81
|
+
page.wait_for_timeout(800)
|
|
82
|
+
tmp = Path(tempfile.mktemp(suffix=".jpg"))
|
|
83
|
+
page.screenshot(
|
|
84
|
+
path=str(tmp),
|
|
85
|
+
clip={"x": 0, "y": 0, **_SLIDE_VIEWPORT},
|
|
86
|
+
type="jpeg",
|
|
87
|
+
quality=80,
|
|
88
|
+
)
|
|
89
|
+
browser.close()
|
|
90
|
+
|
|
91
|
+
img = Image.open(tmp)
|
|
92
|
+
new_size = (int(img.width * 0.75), int(img.height * 0.75))
|
|
93
|
+
img = img.resize(new_size, Image.Resampling.LANCZOS)
|
|
94
|
+
img.save(output_path, "JPEG", quality=75, optimize=True)
|
|
95
|
+
return output_path
|
|
96
|
+
|
|
97
|
+
def _build_temp_pptx(self, html_path: Path) -> Path:
|
|
98
|
+
if __package__ is None:
|
|
99
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
100
|
+
from tools.BuildPptxFromHtmlSlides import BuildPptxFromHtmlSlides
|
|
101
|
+
else:
|
|
102
|
+
from .BuildPptxFromHtmlSlides import BuildPptxFromHtmlSlides
|
|
103
|
+
|
|
104
|
+
temp_dir = Path(tempfile.mkdtemp(prefix="slide_check_"))
|
|
105
|
+
output_pptx = temp_dir / "slide_check.pptx"
|
|
106
|
+
tool = BuildPptxFromHtmlSlides(
|
|
107
|
+
html_files=[str(html_path)],
|
|
108
|
+
output_pptx=str(output_pptx),
|
|
109
|
+
layout=self.layout,
|
|
110
|
+
)
|
|
111
|
+
result = tool.run()
|
|
112
|
+
if isinstance(result, str) and result.lower().startswith("error"):
|
|
113
|
+
raise RuntimeError(result)
|
|
114
|
+
return output_pptx
|
|
115
|
+
|
|
116
|
+
def _render_pptx_slide(self, pptx_path: Path, source_path: Path) -> Path:
|
|
117
|
+
import sys
|
|
118
|
+
from pathlib import Path as PathLib
|
|
119
|
+
|
|
120
|
+
# Add pptx/scripts to path for thumbnail import
|
|
121
|
+
scripts_dir = PathLib(__file__).parent.parent / "pptx" / "scripts"
|
|
122
|
+
if str(scripts_dir) not in sys.path:
|
|
123
|
+
sys.path.insert(0, str(scripts_dir))
|
|
124
|
+
|
|
125
|
+
from thumbnail import convert_to_images # type: ignore
|
|
126
|
+
from PIL import Image
|
|
127
|
+
|
|
128
|
+
output_path = self._resolve_output_path(source_path)
|
|
129
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
130
|
+
slide_images = convert_to_images(pptx_path, Path(temp_dir), 120)
|
|
131
|
+
if not slide_images:
|
|
132
|
+
raise RuntimeError("No slides found in presentation")
|
|
133
|
+
|
|
134
|
+
if self.slide_index < 1 or self.slide_index > len(slide_images):
|
|
135
|
+
raise RuntimeError(
|
|
136
|
+
f"slide_index out of range (1-{len(slide_images)}): {self.slide_index}"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
selected = Path(slide_images[self.slide_index - 1])
|
|
140
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
141
|
+
|
|
142
|
+
# Compress image to reduce token usage
|
|
143
|
+
img = Image.open(selected)
|
|
144
|
+
# Resize to 75% of original (reduces token usage significantly)
|
|
145
|
+
new_size = (int(img.width * 0.75), int(img.height * 0.75))
|
|
146
|
+
img = img.resize(new_size, Image.Resampling.LANCZOS)
|
|
147
|
+
# Save with moderate JPEG quality
|
|
148
|
+
img.save(output_path, 'JPEG', quality=75, optimize=True)
|
|
149
|
+
|
|
150
|
+
return output_path
|
|
151
|
+
|
|
152
|
+
def _resolve_output_path(self, pptx_path: Path) -> Path:
|
|
153
|
+
if self.output_image_path:
|
|
154
|
+
return Path(self.output_image_path)
|
|
155
|
+
|
|
156
|
+
stem = pptx_path.stem
|
|
157
|
+
if pptx_path.suffix.lower() == ".pptx":
|
|
158
|
+
return pptx_path.with_name(f"{stem}_check.jpg")
|
|
159
|
+
return pptx_path.with_name(f"{stem}_check.jpg")
|
|
160
|
+
|
|
161
|
+
def _check_soffice(self) -> bool:
|
|
162
|
+
try:
|
|
163
|
+
kwargs = {
|
|
164
|
+
"capture_output": True,
|
|
165
|
+
"timeout": 15,
|
|
166
|
+
"text": True,
|
|
167
|
+
"input": "\n",
|
|
168
|
+
}
|
|
169
|
+
if os.name == "nt":
|
|
170
|
+
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
171
|
+
|
|
172
|
+
soffice_bin = "soffice.com" if os.name == "nt" else "soffice"
|
|
173
|
+
subprocess.run([soffice_bin, "--version"], **kwargs)
|
|
174
|
+
return True
|
|
175
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
def _check_pdftoppm(self) -> bool:
|
|
179
|
+
try:
|
|
180
|
+
kwargs = {
|
|
181
|
+
"capture_output": True,
|
|
182
|
+
"timeout": 5,
|
|
183
|
+
"stdin": subprocess.DEVNULL,
|
|
184
|
+
}
|
|
185
|
+
if os.name == "nt":
|
|
186
|
+
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
187
|
+
|
|
188
|
+
subprocess.run(["pdftoppm", "-v"], **kwargs)
|
|
189
|
+
return True
|
|
190
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
def _run_attachment(self, attachment):
|
|
194
|
+
result = attachment.run()
|
|
195
|
+
if not inspect.isawaitable(result):
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
asyncio.get_running_loop()
|
|
200
|
+
except RuntimeError:
|
|
201
|
+
return asyncio.run(result)
|
|
202
|
+
|
|
203
|
+
new_loop = asyncio.new_event_loop()
|
|
204
|
+
try:
|
|
205
|
+
return new_loop.run_until_complete(result)
|
|
206
|
+
finally:
|
|
207
|
+
new_loop.close()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
repo_root = Path(__file__).resolve().parents[2]
|
|
212
|
+
test_pptx = repo_root / "mnt/claude_cowork_deck/presentations/claude_cowork_deck_v5_rendered.pptx"
|
|
213
|
+
if test_pptx.exists():
|
|
214
|
+
tool = CheckSlide(slide_path="mnt/claude_cowork_deck/presentations/claude_cowork_deck_v5_rendered.pptx", slide_index=3, output_image_path="mnt/claude_cowork_deck/presentations/_v5_prev3.jpg")
|
|
215
|
+
print(tool.run())
|
|
216
|
+
else:
|
|
217
|
+
print(f"Test file not found: {test_pptx}")
|
|
218
|
+
print("Tool definition is valid.")
|