@_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,451 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Create thumbnail grids from PowerPoint presentation slides.
|
|
4
|
+
|
|
5
|
+
Creates a grid layout of slide thumbnails with configurable columns (max 6).
|
|
6
|
+
Each grid contains up to cols×(cols+1) images. For presentations with more
|
|
7
|
+
slides, multiple numbered grid files are created automatically.
|
|
8
|
+
|
|
9
|
+
The program outputs the names of all files created.
|
|
10
|
+
|
|
11
|
+
Output:
|
|
12
|
+
- Single grid: {prefix}.jpg (if slides fit in one grid)
|
|
13
|
+
- Multiple grids: {prefix}-1.jpg, {prefix}-2.jpg, etc.
|
|
14
|
+
|
|
15
|
+
Grid limits by column count:
|
|
16
|
+
- 3 cols: max 12 slides per grid (3×4)
|
|
17
|
+
- 4 cols: max 20 slides per grid (4×5)
|
|
18
|
+
- 5 cols: max 30 slides per grid (5×6) [default]
|
|
19
|
+
- 6 cols: max 42 slides per grid (6×7)
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
python thumbnail.py input.pptx [output_prefix] [--cols N] [--outline-placeholders]
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
python thumbnail.py presentation.pptx
|
|
26
|
+
# Creates: thumbnails.jpg (using default prefix)
|
|
27
|
+
# Outputs:
|
|
28
|
+
# Created 1 grid(s):
|
|
29
|
+
# - thumbnails.jpg
|
|
30
|
+
|
|
31
|
+
python thumbnail.py large-deck.pptx grid --cols 4
|
|
32
|
+
# Creates: grid-1.jpg, grid-2.jpg, grid-3.jpg
|
|
33
|
+
# Outputs:
|
|
34
|
+
# Created 3 grid(s):
|
|
35
|
+
# - grid-1.jpg
|
|
36
|
+
# - grid-2.jpg
|
|
37
|
+
# - grid-3.jpg
|
|
38
|
+
|
|
39
|
+
python thumbnail.py template.pptx analysis --outline-placeholders
|
|
40
|
+
# Creates thumbnail grids with red outlines around text placeholders
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
import argparse
|
|
44
|
+
import os
|
|
45
|
+
import subprocess
|
|
46
|
+
import sys
|
|
47
|
+
import tempfile
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
|
|
50
|
+
from inventory import extract_text_inventory
|
|
51
|
+
from PIL import Image, ImageDraw
|
|
52
|
+
from pptx import Presentation
|
|
53
|
+
|
|
54
|
+
# Constants
|
|
55
|
+
THUMBNAIL_WIDTH = 420 # Fixed thumbnail width in pixels
|
|
56
|
+
CONVERSION_DPI = 100 # DPI for PDF to image conversion
|
|
57
|
+
MAX_COLS = 6 # Maximum number of columns
|
|
58
|
+
DEFAULT_COLS = 5 # Default number of columns
|
|
59
|
+
JPEG_QUALITY = 95 # JPEG compression quality
|
|
60
|
+
SOFFICE_TIMEOUT = 60 # Seconds for LibreOffice conversion
|
|
61
|
+
PDFTOPPM_TIMEOUT = 60 # Seconds for Poppler rasterization
|
|
62
|
+
|
|
63
|
+
# Grid layout constants
|
|
64
|
+
GRID_PADDING = 20 # Padding between thumbnails
|
|
65
|
+
BORDER_WIDTH = 2 # Border width around thumbnails
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main():
|
|
69
|
+
parser = argparse.ArgumentParser(
|
|
70
|
+
description="Create thumbnail grids from PowerPoint slides."
|
|
71
|
+
)
|
|
72
|
+
parser.add_argument("input", help="Input PowerPoint file (.pptx)")
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"output_prefix",
|
|
75
|
+
nargs="?",
|
|
76
|
+
default="thumbnails",
|
|
77
|
+
help="Output prefix for image files (default: thumbnails, will create prefix.jpg or prefix-N.jpg)",
|
|
78
|
+
)
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"--cols",
|
|
81
|
+
type=int,
|
|
82
|
+
default=DEFAULT_COLS,
|
|
83
|
+
help=f"Number of columns (default: {DEFAULT_COLS}, max: {MAX_COLS})",
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"--outline-placeholders",
|
|
87
|
+
action="store_true",
|
|
88
|
+
help="Outline text placeholders with a colored border",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
args = parser.parse_args()
|
|
92
|
+
|
|
93
|
+
# Validate columns
|
|
94
|
+
cols = min(args.cols, MAX_COLS)
|
|
95
|
+
if args.cols > MAX_COLS:
|
|
96
|
+
print(f"Warning: Columns limited to {MAX_COLS} (requested {args.cols})")
|
|
97
|
+
|
|
98
|
+
# Validate input
|
|
99
|
+
input_path = Path(args.input)
|
|
100
|
+
if not input_path.exists() or input_path.suffix.lower() != ".pptx":
|
|
101
|
+
print(f"Error: Invalid PowerPoint file: {args.input}")
|
|
102
|
+
sys.exit(1)
|
|
103
|
+
|
|
104
|
+
# Construct output path (always JPG)
|
|
105
|
+
output_path = Path(f"{args.output_prefix}.jpg")
|
|
106
|
+
|
|
107
|
+
print(f"Processing: {args.input}")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
111
|
+
# Get placeholder regions if outlining is enabled
|
|
112
|
+
placeholder_regions = None
|
|
113
|
+
slide_dimensions = None
|
|
114
|
+
if args.outline_placeholders:
|
|
115
|
+
print("Extracting placeholder regions...")
|
|
116
|
+
placeholder_regions, slide_dimensions = get_placeholder_regions(
|
|
117
|
+
input_path
|
|
118
|
+
)
|
|
119
|
+
if placeholder_regions:
|
|
120
|
+
print(f"Found placeholders on {len(placeholder_regions)} slides")
|
|
121
|
+
|
|
122
|
+
# Convert slides to images
|
|
123
|
+
slide_images = convert_to_images(input_path, Path(temp_dir), CONVERSION_DPI)
|
|
124
|
+
if not slide_images:
|
|
125
|
+
print("Error: No slides found")
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
|
|
128
|
+
print(f"Found {len(slide_images)} slides")
|
|
129
|
+
|
|
130
|
+
# Create grids (max cols×(cols+1) images per grid)
|
|
131
|
+
grid_files = create_grids(
|
|
132
|
+
slide_images,
|
|
133
|
+
cols,
|
|
134
|
+
THUMBNAIL_WIDTH,
|
|
135
|
+
output_path,
|
|
136
|
+
placeholder_regions,
|
|
137
|
+
slide_dimensions,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Print saved files
|
|
141
|
+
print(f"Created {len(grid_files)} grid(s):")
|
|
142
|
+
for grid_file in grid_files:
|
|
143
|
+
print(f" - {grid_file}")
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(f"Error: {e}")
|
|
147
|
+
sys.exit(1)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def create_hidden_slide_placeholder(size):
|
|
151
|
+
"""Create placeholder image for hidden slides."""
|
|
152
|
+
img = Image.new("RGB", size, color="#F0F0F0")
|
|
153
|
+
draw = ImageDraw.Draw(img)
|
|
154
|
+
line_width = max(5, min(size) // 100)
|
|
155
|
+
draw.line([(0, 0), size], fill="#CCCCCC", width=line_width)
|
|
156
|
+
draw.line([(size[0], 0), (0, size[1])], fill="#CCCCCC", width=line_width)
|
|
157
|
+
return img
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_placeholder_regions(pptx_path):
|
|
161
|
+
"""Extract ALL text regions from the presentation.
|
|
162
|
+
|
|
163
|
+
Returns a tuple of (placeholder_regions, slide_dimensions).
|
|
164
|
+
text_regions is a dict mapping slide indices to lists of text regions.
|
|
165
|
+
Each region is a dict with 'left', 'top', 'width', 'height' in inches.
|
|
166
|
+
slide_dimensions is a tuple of (width_inches, height_inches).
|
|
167
|
+
"""
|
|
168
|
+
prs = Presentation(str(pptx_path))
|
|
169
|
+
inventory = extract_text_inventory(pptx_path, prs)
|
|
170
|
+
placeholder_regions = {}
|
|
171
|
+
|
|
172
|
+
# Get actual slide dimensions in inches (EMU to inches conversion)
|
|
173
|
+
slide_width_inches = (prs.slide_width or 9144000) / 914400.0
|
|
174
|
+
slide_height_inches = (prs.slide_height or 5143500) / 914400.0
|
|
175
|
+
|
|
176
|
+
for slide_key, shapes in inventory.items():
|
|
177
|
+
# Extract slide index from "slide-N" format
|
|
178
|
+
slide_idx = int(slide_key.split("-")[1])
|
|
179
|
+
regions = []
|
|
180
|
+
|
|
181
|
+
for shape_key, shape_data in shapes.items():
|
|
182
|
+
# The inventory only contains shapes with text, so all shapes should be highlighted
|
|
183
|
+
regions.append(
|
|
184
|
+
{
|
|
185
|
+
"left": shape_data.left,
|
|
186
|
+
"top": shape_data.top,
|
|
187
|
+
"width": shape_data.width,
|
|
188
|
+
"height": shape_data.height,
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if regions:
|
|
193
|
+
placeholder_regions[slide_idx] = regions
|
|
194
|
+
|
|
195
|
+
return placeholder_regions, (slide_width_inches, slide_height_inches)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def convert_to_images(pptx_path, temp_dir, dpi):
|
|
199
|
+
"""Convert PowerPoint to images via PDF, handling hidden slides."""
|
|
200
|
+
# Detect hidden slides
|
|
201
|
+
print("Analyzing presentation...")
|
|
202
|
+
prs = Presentation(str(pptx_path))
|
|
203
|
+
total_slides = len(prs.slides)
|
|
204
|
+
|
|
205
|
+
# Find hidden slides (1-based indexing for display)
|
|
206
|
+
hidden_slides = {
|
|
207
|
+
idx + 1
|
|
208
|
+
for idx, slide in enumerate(prs.slides)
|
|
209
|
+
if slide.element.get("show") == "0"
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
print(f"Total slides: {total_slides}")
|
|
213
|
+
if hidden_slides:
|
|
214
|
+
print(f"Hidden slides: {sorted(hidden_slides)}")
|
|
215
|
+
|
|
216
|
+
pdf_path = temp_dir / f"{pptx_path.stem}.pdf"
|
|
217
|
+
|
|
218
|
+
# Convert to PDF
|
|
219
|
+
print("Converting to PDF...")
|
|
220
|
+
try:
|
|
221
|
+
kwargs = {
|
|
222
|
+
"capture_output": True,
|
|
223
|
+
"text": True,
|
|
224
|
+
"timeout": SOFFICE_TIMEOUT,
|
|
225
|
+
"input": "\n",
|
|
226
|
+
}
|
|
227
|
+
if os.name == "nt":
|
|
228
|
+
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
229
|
+
|
|
230
|
+
# On Windows, prefer soffice.com for CLI usage as it handles headless mode better than soffice.exe
|
|
231
|
+
soffice_bin = "soffice.com" if os.name == "nt" else "soffice"
|
|
232
|
+
|
|
233
|
+
result = subprocess.run(
|
|
234
|
+
[
|
|
235
|
+
soffice_bin,
|
|
236
|
+
"--headless",
|
|
237
|
+
"--convert-to",
|
|
238
|
+
"pdf",
|
|
239
|
+
"--outdir",
|
|
240
|
+
str(temp_dir),
|
|
241
|
+
str(pptx_path),
|
|
242
|
+
],
|
|
243
|
+
**kwargs
|
|
244
|
+
)
|
|
245
|
+
except subprocess.TimeoutExpired as exc:
|
|
246
|
+
raise RuntimeError(f"PDF conversion timed out after {SOFFICE_TIMEOUT}s") from exc
|
|
247
|
+
if result.returncode != 0 or not pdf_path.exists():
|
|
248
|
+
raise RuntimeError("PDF conversion failed")
|
|
249
|
+
|
|
250
|
+
# Convert PDF to images
|
|
251
|
+
print(f"Converting to images at {dpi} DPI...")
|
|
252
|
+
try:
|
|
253
|
+
kwargs = {
|
|
254
|
+
"capture_output": True,
|
|
255
|
+
"text": True,
|
|
256
|
+
"timeout": PDFTOPPM_TIMEOUT,
|
|
257
|
+
"stdin": subprocess.DEVNULL,
|
|
258
|
+
}
|
|
259
|
+
if os.name == "nt":
|
|
260
|
+
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
261
|
+
|
|
262
|
+
result = subprocess.run(
|
|
263
|
+
["pdftoppm", "-jpeg", "-r", str(dpi), str(pdf_path), str(temp_dir / "slide")],
|
|
264
|
+
**kwargs
|
|
265
|
+
)
|
|
266
|
+
except subprocess.TimeoutExpired as exc:
|
|
267
|
+
raise RuntimeError(
|
|
268
|
+
f"Image conversion timed out after {PDFTOPPM_TIMEOUT}s"
|
|
269
|
+
) from exc
|
|
270
|
+
if result.returncode != 0:
|
|
271
|
+
raise RuntimeError("Image conversion failed")
|
|
272
|
+
|
|
273
|
+
visible_images = sorted(temp_dir.glob("slide-*.jpg"))
|
|
274
|
+
|
|
275
|
+
# Create full list with placeholders for hidden slides
|
|
276
|
+
all_images = []
|
|
277
|
+
visible_idx = 0
|
|
278
|
+
|
|
279
|
+
# Get placeholder dimensions from first visible slide
|
|
280
|
+
if visible_images:
|
|
281
|
+
with Image.open(visible_images[0]) as img:
|
|
282
|
+
placeholder_size = img.size
|
|
283
|
+
else:
|
|
284
|
+
placeholder_size = (1920, 1080)
|
|
285
|
+
|
|
286
|
+
for slide_num in range(1, total_slides + 1):
|
|
287
|
+
if slide_num in hidden_slides:
|
|
288
|
+
# Create placeholder image for hidden slide
|
|
289
|
+
placeholder_path = temp_dir / f"hidden-{slide_num:03d}.jpg"
|
|
290
|
+
placeholder_img = create_hidden_slide_placeholder(placeholder_size)
|
|
291
|
+
placeholder_img.save(placeholder_path, "JPEG")
|
|
292
|
+
all_images.append(placeholder_path)
|
|
293
|
+
else:
|
|
294
|
+
# Use the actual visible slide image
|
|
295
|
+
if visible_idx < len(visible_images):
|
|
296
|
+
all_images.append(visible_images[visible_idx])
|
|
297
|
+
visible_idx += 1
|
|
298
|
+
|
|
299
|
+
return all_images
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def create_grids(
|
|
303
|
+
image_paths,
|
|
304
|
+
cols,
|
|
305
|
+
width,
|
|
306
|
+
output_path,
|
|
307
|
+
placeholder_regions=None,
|
|
308
|
+
slide_dimensions=None,
|
|
309
|
+
):
|
|
310
|
+
"""Create multiple thumbnail grids from slide images, max cols×(cols+1) images per grid."""
|
|
311
|
+
# Maximum images per grid is cols × (cols + 1) for better proportions
|
|
312
|
+
max_images_per_grid = cols * (cols + 1)
|
|
313
|
+
grid_files = []
|
|
314
|
+
|
|
315
|
+
print(
|
|
316
|
+
f"Creating grids with {cols} columns (max {max_images_per_grid} images per grid)"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Split images into chunks
|
|
320
|
+
for chunk_idx, start_idx in enumerate(
|
|
321
|
+
range(0, len(image_paths), max_images_per_grid)
|
|
322
|
+
):
|
|
323
|
+
end_idx = min(start_idx + max_images_per_grid, len(image_paths))
|
|
324
|
+
chunk_images = image_paths[start_idx:end_idx]
|
|
325
|
+
|
|
326
|
+
# Create grid for this chunk
|
|
327
|
+
grid = create_grid(
|
|
328
|
+
chunk_images, cols, width, start_idx, placeholder_regions, slide_dimensions
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Generate output filename
|
|
332
|
+
if len(image_paths) <= max_images_per_grid:
|
|
333
|
+
# Single grid - use base filename without suffix
|
|
334
|
+
grid_filename = output_path
|
|
335
|
+
else:
|
|
336
|
+
# Multiple grids - insert index before extension with dash
|
|
337
|
+
stem = output_path.stem
|
|
338
|
+
suffix = output_path.suffix
|
|
339
|
+
grid_filename = output_path.parent / f"{stem}-{chunk_idx + 1}{suffix}"
|
|
340
|
+
|
|
341
|
+
# Save grid
|
|
342
|
+
grid_filename.parent.mkdir(parents=True, exist_ok=True)
|
|
343
|
+
grid.save(str(grid_filename), quality=JPEG_QUALITY)
|
|
344
|
+
grid_files.append(str(grid_filename))
|
|
345
|
+
|
|
346
|
+
return grid_files
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def create_grid(
|
|
350
|
+
image_paths,
|
|
351
|
+
cols,
|
|
352
|
+
width,
|
|
353
|
+
start_slide_num=0,
|
|
354
|
+
placeholder_regions=None,
|
|
355
|
+
slide_dimensions=None,
|
|
356
|
+
):
|
|
357
|
+
"""Create thumbnail grid from slide images with optional placeholder outlining."""
|
|
358
|
+
# Get dimensions
|
|
359
|
+
with Image.open(image_paths[0]) as img:
|
|
360
|
+
aspect = img.height / img.width
|
|
361
|
+
height = int(width * aspect)
|
|
362
|
+
|
|
363
|
+
# Calculate grid size
|
|
364
|
+
rows = (len(image_paths) + cols - 1) // cols
|
|
365
|
+
grid_w = cols * width + (cols + 1) * GRID_PADDING
|
|
366
|
+
grid_h = rows * height + (rows + 1) * GRID_PADDING
|
|
367
|
+
|
|
368
|
+
# Create grid
|
|
369
|
+
grid = Image.new("RGB", (grid_w, grid_h), "white")
|
|
370
|
+
draw = ImageDraw.Draw(grid)
|
|
371
|
+
|
|
372
|
+
# Place thumbnails
|
|
373
|
+
for i, img_path in enumerate(image_paths):
|
|
374
|
+
row, col = i // cols, i % cols
|
|
375
|
+
x = col * width + (col + 1) * GRID_PADDING
|
|
376
|
+
y_thumbnail = row * height + (row + 1) * GRID_PADDING
|
|
377
|
+
|
|
378
|
+
with Image.open(img_path) as img:
|
|
379
|
+
# Get original dimensions before thumbnail
|
|
380
|
+
orig_w, orig_h = img.size
|
|
381
|
+
|
|
382
|
+
# Apply placeholder outlines if enabled
|
|
383
|
+
if placeholder_regions and (start_slide_num + i) in placeholder_regions:
|
|
384
|
+
# Convert to RGBA for transparency support
|
|
385
|
+
if img.mode != "RGBA":
|
|
386
|
+
img = img.convert("RGBA")
|
|
387
|
+
|
|
388
|
+
# Get the regions for this slide
|
|
389
|
+
regions = placeholder_regions[start_slide_num + i]
|
|
390
|
+
|
|
391
|
+
# Calculate scale factors using actual slide dimensions
|
|
392
|
+
if slide_dimensions:
|
|
393
|
+
slide_width_inches, slide_height_inches = slide_dimensions
|
|
394
|
+
else:
|
|
395
|
+
# Fallback: estimate from image size at CONVERSION_DPI
|
|
396
|
+
slide_width_inches = orig_w / CONVERSION_DPI
|
|
397
|
+
slide_height_inches = orig_h / CONVERSION_DPI
|
|
398
|
+
|
|
399
|
+
x_scale = orig_w / slide_width_inches
|
|
400
|
+
y_scale = orig_h / slide_height_inches
|
|
401
|
+
|
|
402
|
+
# Create a highlight overlay
|
|
403
|
+
overlay = Image.new("RGBA", img.size, (255, 255, 255, 0))
|
|
404
|
+
overlay_draw = ImageDraw.Draw(overlay)
|
|
405
|
+
|
|
406
|
+
# Highlight each placeholder region
|
|
407
|
+
for region in regions:
|
|
408
|
+
# Convert from inches to pixels in the original image
|
|
409
|
+
px_left = int(region["left"] * x_scale)
|
|
410
|
+
px_top = int(region["top"] * y_scale)
|
|
411
|
+
px_width = int(region["width"] * x_scale)
|
|
412
|
+
px_height = int(region["height"] * y_scale)
|
|
413
|
+
|
|
414
|
+
# Draw highlight outline with red color and thick stroke
|
|
415
|
+
# Using a bright red outline instead of fill
|
|
416
|
+
stroke_width = max(
|
|
417
|
+
5, min(orig_w, orig_h) // 150
|
|
418
|
+
) # Thicker proportional stroke width
|
|
419
|
+
overlay_draw.rectangle(
|
|
420
|
+
[(px_left, px_top), (px_left + px_width, px_top + px_height)],
|
|
421
|
+
outline=(255, 0, 0, 255), # Bright red, fully opaque
|
|
422
|
+
width=stroke_width,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# Composite the overlay onto the image using alpha blending
|
|
426
|
+
img = Image.alpha_composite(img, overlay)
|
|
427
|
+
# Convert back to RGB for JPEG saving
|
|
428
|
+
img = img.convert("RGB")
|
|
429
|
+
|
|
430
|
+
img.thumbnail((width, height), Image.Resampling.LANCZOS)
|
|
431
|
+
w, h = img.size
|
|
432
|
+
tx = x + (width - w) // 2
|
|
433
|
+
ty = y_thumbnail + (height - h) // 2
|
|
434
|
+
grid.paste(img, (tx, ty))
|
|
435
|
+
|
|
436
|
+
# Add border
|
|
437
|
+
if BORDER_WIDTH > 0:
|
|
438
|
+
draw.rectangle(
|
|
439
|
+
[
|
|
440
|
+
(tx - BORDER_WIDTH, ty - BORDER_WIDTH),
|
|
441
|
+
(tx + w + BORDER_WIDTH - 1, ty + h + BORDER_WIDTH - 1),
|
|
442
|
+
],
|
|
443
|
+
outline="gray",
|
|
444
|
+
width=BORDER_WIDTH,
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
return grid
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
if __name__ == "__main__":
|
|
451
|
+
main()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from agency_swarm import Agent, ModelSettings
|
|
2
|
+
from agency_swarm.tools import IPythonInterpreter, PersistentShellTool, LoadFileAttachment, WebSearchTool
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from openai.types.shared import Reasoning
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from virtual_assistant.tools.ReadFile import ReadFile
|
|
7
|
+
|
|
8
|
+
from config import get_default_model, is_openai_provider
|
|
9
|
+
|
|
10
|
+
# Import slide tools
|
|
11
|
+
from .tools import (
|
|
12
|
+
InsertNewSlides,
|
|
13
|
+
ModifySlide,
|
|
14
|
+
ManageTheme,
|
|
15
|
+
DeleteSlide,
|
|
16
|
+
SlideScreenshot,
|
|
17
|
+
ReadSlide,
|
|
18
|
+
BuildPptxFromHtmlSlides,
|
|
19
|
+
RestoreSnapshot,
|
|
20
|
+
CreatePptxThumbnailGrid,
|
|
21
|
+
CheckSlideCanvasOverflow,
|
|
22
|
+
CheckSlide,
|
|
23
|
+
DownloadImage,
|
|
24
|
+
EnsureRasterImage,
|
|
25
|
+
# ExtractPptxTextInventory,
|
|
26
|
+
# RearrangePptxSlidesFromTemplate,
|
|
27
|
+
# ApplyPptxTextReplacements,
|
|
28
|
+
ImageSearch,
|
|
29
|
+
GenerateImage,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
_INSTRUCTIONS_PATH = Path(__file__).parent / "instructions.md"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _list_existing_projects() -> str:
|
|
36
|
+
from .tools.slide_file_utils import get_mnt_dir
|
|
37
|
+
base = get_mnt_dir()
|
|
38
|
+
if not base.exists():
|
|
39
|
+
return "(none)"
|
|
40
|
+
dirs = sorted(d.name for d in base.iterdir() if d.is_dir())
|
|
41
|
+
return "\n".join(f" - {d}" for d in dirs) if dirs else "(none)"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _build_instructions() -> str:
|
|
45
|
+
now_utc = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
|
46
|
+
body = _INSTRUCTIONS_PATH.read_text(encoding="utf-8")
|
|
47
|
+
projects_block = _list_existing_projects()
|
|
48
|
+
return (
|
|
49
|
+
f"{body}\n\n"
|
|
50
|
+
f"Current date/time (UTC): {now_utc}\n\n"
|
|
51
|
+
f"Existing project folders (do NOT reuse these names for a new presentation):\n{projects_block}"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_slides_agent() -> Agent:
|
|
56
|
+
return Agent(
|
|
57
|
+
name="Slides Agent",
|
|
58
|
+
description="PowerPoint presentation specialist for creating, editing, and analyzing .pptx files",
|
|
59
|
+
instructions=_build_instructions(),
|
|
60
|
+
# files_folder=os.path.join(current_dir, "files"),
|
|
61
|
+
# tools_folder=os.path.join(current_dir, "tools"),
|
|
62
|
+
tools=[
|
|
63
|
+
# Slide creation and management: InsertNewSlides then ModifySlide
|
|
64
|
+
InsertNewSlides,
|
|
65
|
+
ModifySlide,
|
|
66
|
+
ManageTheme,
|
|
67
|
+
DeleteSlide,
|
|
68
|
+
SlideScreenshot,
|
|
69
|
+
ReadSlide,
|
|
70
|
+
# PPTX building
|
|
71
|
+
BuildPptxFromHtmlSlides,
|
|
72
|
+
RestoreSnapshot,
|
|
73
|
+
CreatePptxThumbnailGrid,
|
|
74
|
+
CheckSlideCanvasOverflow,
|
|
75
|
+
CheckSlide,
|
|
76
|
+
# Image download
|
|
77
|
+
DownloadImage,
|
|
78
|
+
EnsureRasterImage,
|
|
79
|
+
GenerateImage,
|
|
80
|
+
# Template-based editing
|
|
81
|
+
# ExtractPptxTextInventory,
|
|
82
|
+
# RearrangePptxSlidesFromTemplate,
|
|
83
|
+
# ApplyPptxTextReplacements,
|
|
84
|
+
ImageSearch,
|
|
85
|
+
# Utility tools
|
|
86
|
+
IPythonInterpreter,
|
|
87
|
+
PersistentShellTool,
|
|
88
|
+
LoadFileAttachment,
|
|
89
|
+
ReadFile,
|
|
90
|
+
WebSearchTool(search_context_size="high"),
|
|
91
|
+
],
|
|
92
|
+
model=get_default_model(),
|
|
93
|
+
model_settings=ModelSettings(
|
|
94
|
+
reasoning=Reasoning(effort="high", summary="auto") if is_openai_provider() else None,
|
|
95
|
+
verbosity="medium" if is_openai_provider() else None,
|
|
96
|
+
response_include=["web_search_call.action.sources"] if is_openai_provider() else None,
|
|
97
|
+
),
|
|
98
|
+
conversation_starters=[
|
|
99
|
+
"Create a new presentation about the benefits of using AI in the workplace.",
|
|
100
|
+
"Edit my existing presentation and improve the design.",
|
|
101
|
+
"Create a pitch deck for my startup idea.",
|
|
102
|
+
"Turn this document into a professional slide deck.",
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
from agency_swarm import Agency
|
|
109
|
+
Agency(create_slides_agent()).terminal_demo(reload=False)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: #F7F4EF;
|
|
3
|
+
--ink: #141518;
|
|
4
|
+
--muted: #5B606A;
|
|
5
|
+
--accent: #E36A2E;
|
|
6
|
+
--card: #FFFFFF;
|
|
7
|
+
--line: #D7D1C7;
|
|
8
|
+
--dark: #0F0F10;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
* { box-sizing: border-box; }
|
|
12
|
+
body {
|
|
13
|
+
margin: 0;
|
|
14
|
+
background: var(--bg);
|
|
15
|
+
color: var(--ink);
|
|
16
|
+
font-family: "Inter", Helvetica, Arial, sans-serif;
|
|
17
|
+
}
|
|
18
|
+
h1, h2, h3, p, ul, ol { margin: 0; }
|
|
19
|
+
|
|
20
|
+
h1 { font-size: 56px; line-height: 1.05; font-weight: 800; letter-spacing: -0.4px; }
|
|
21
|
+
h2 { font-size: 36px; line-height: 1.12; font-weight: 800; letter-spacing: -0.25px; }
|
|
22
|
+
h3 { font-size: 24px; line-height: 1.18; font-weight: 800; }
|
|
23
|
+
p { font-size: 20px; line-height: 1.35; color: var(--ink); }
|
|
24
|
+
|
|
25
|
+
.small { font-size: 16px; line-height: 1.35; color: var(--muted); }
|
|
26
|
+
.kicker { font-size: 16px; letter-spacing: 2px; font-weight: 800; color: var(--accent); }
|
|
27
|
+
|
|
28
|
+
.canvas {
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
padding: 32px 32px 48px 32px;
|
|
32
|
+
overflow: hidden;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.title-block { display: flex; flex-direction: column; gap: 24px; }
|
|
36
|
+
.subtitle { font-size: 28px; line-height: 1.35; color: var(--muted); }
|
|
37
|
+
.footer { position: absolute; left: 32px; right: 32px; bottom: 48px; }
|
|
38
|
+
|
|
39
|
+
.font-serif { font-family: "Merriweather", serif; }
|
|
40
|
+
.font-sans { font-family: "Inter", sans-serif; }
|
|
41
|
+
.font-mono { font-family: "Roboto Mono", monospace; }
|
|
42
|
+
|
|
43
|
+
.split { display: flex; gap: 32px; height: 100%; }
|
|
44
|
+
.leftPanel {
|
|
45
|
+
width: 420px;
|
|
46
|
+
background: var(--dark);
|
|
47
|
+
color: #FFFFFF;
|
|
48
|
+
border-radius: 20px;
|
|
49
|
+
padding: 32px;
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
justify-content: space-between;
|
|
53
|
+
}
|
|
54
|
+
.leftPanel h1, .leftPanel h2, .leftPanel h3, .leftPanel p { color: #FFFFFF; }
|
|
55
|
+
.rightPanel {
|
|
56
|
+
flex: 1;
|
|
57
|
+
background: var(--card);
|
|
58
|
+
border: 2px solid var(--line);
|
|
59
|
+
border-radius: 20px;
|
|
60
|
+
padding: 32px;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
gap: 20px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.image-frame {
|
|
67
|
+
width: 100%;
|
|
68
|
+
height: 360px;
|
|
69
|
+
border-radius: 16px;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
border: 2px solid var(--line);
|
|
72
|
+
background: #FFFFFF;
|
|
73
|
+
}
|
|
74
|
+
.image-frame img {
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
object-fit: cover;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.kpi-row { display: flex; gap: 20px; margin-top: 24px; }
|
|
81
|
+
.kpi-card {
|
|
82
|
+
flex: 1;
|
|
83
|
+
background: var(--card);
|
|
84
|
+
border: 2px solid var(--line);
|
|
85
|
+
border-radius: 16px;
|
|
86
|
+
padding: 20px;
|
|
87
|
+
}
|
|
88
|
+
.kpi-value { font-size: 48px; font-weight: 800; color: var(--dark); }
|
|
89
|
+
.kpi-label { font-size: 18px; color: var(--muted); }
|
|
90
|
+
|
|
91
|
+
ul { padding-left: 22px; }
|
|
92
|
+
ul li { font-size: 18px; line-height: 1.35; margin: 8px 0; }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="800" viewBox="0 0 1200 800">
|
|
2
|
+
<rect width="1200" height="800" fill="#F2EEE8"/>
|
|
3
|
+
<rect x="40" y="40" width="1120" height="720" fill="#FFFFFF" stroke="#D7D1C7" stroke-width="6" rx="24"/>
|
|
4
|
+
<circle cx="220" cy="240" r="70" fill="#E36A2E" opacity="0.85"/>
|
|
5
|
+
<rect x="340" y="200" width="700" height="22" fill="#141518" opacity="0.85"/>
|
|
6
|
+
<rect x="340" y="250" width="560" height="18" fill="#5B606A" opacity="0.7"/>
|
|
7
|
+
<rect x="340" y="300" width="620" height="18" fill="#5B606A" opacity="0.6"/>
|
|
8
|
+
<rect x="140" y="430" width="920" height="180" fill="#F7F4EF" stroke="#D7D1C7" stroke-width="4" rx="16"/>
|
|
9
|
+
<text x="600" y="520" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="36" fill="#141518">Image Placeholder</text>
|
|
10
|
+
<text x="600" y="570" text-anchor="middle" font-family="Arial, Helvetica, sans-serif" font-size="22" fill="#5B606A">Replace with a real visual</text>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="canvas">
|
|
2
|
+
<div class="title-block">
|
|
3
|
+
<p class="kicker">TEST DECK</p>
|
|
4
|
+
<h1>Slides Agent Layout Smoke Test</h1>
|
|
5
|
+
<p class="subtitle">Full-size 1280x720 canvas with safe margins.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="footer">
|
|
8
|
+
<p class="small">Internal QA deck • Includes image and KPI layouts</p>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="canvas">
|
|
2
|
+
<div class="split">
|
|
3
|
+
<div class="leftPanel">
|
|
4
|
+
<div>
|
|
5
|
+
<p class="kicker">VISUAL</p>
|
|
6
|
+
<h2>Image-led split layout</h2>
|
|
7
|
+
<p class="small">Use real imagery and keep explicit sizing.</p>
|
|
8
|
+
</div>
|
|
9
|
+
<ul>
|
|
10
|
+
<li>Image fills a framed container</li>
|
|
11
|
+
<li>Text stays inside fixed panels</li>
|
|
12
|
+
<li>No overlap or overflow</li>
|
|
13
|
+
</ul>
|
|
14
|
+
<p class="small">Inline SVG or image icons only</p>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="rightPanel">
|
|
17
|
+
<div class="image-frame">
|
|
18
|
+
<img src="./assets/placeholder.svg" alt="Placeholder image" />
|
|
19
|
+
</div>
|
|
20
|
+
<p class="small">Example: product mockup or market photo</p>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|