@_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,323 @@
|
|
|
1
|
+
"""Convert documents to different formats (PDF, Markdown, TXT)."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
import html2text
|
|
7
|
+
from agency_swarm.tools import BaseTool, ToolOutputText, tool_output_file_from_path
|
|
8
|
+
from bs4 import BeautifulSoup
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
from weasyprint import HTML
|
|
11
|
+
|
|
12
|
+
from .CreateDocument import CreateDocument
|
|
13
|
+
from .utils.html_docx_core import html_to_docx
|
|
14
|
+
from .utils.html_docx_images import embed_local_images
|
|
15
|
+
from .utils.html_docx_playwright import auto_page_breaks
|
|
16
|
+
|
|
17
|
+
# Base directory for all document files
|
|
18
|
+
from .utils.doc_file_utils import get_project_dir, next_docx_version
|
|
19
|
+
|
|
20
|
+
# Characters that PDF fonts commonly lack glyphs for.
|
|
21
|
+
# Includes both proper Unicode typographic chars and ASCII control-char
|
|
22
|
+
# corruptions that sometimes appear when the LLM emits smart-quote/dash
|
|
23
|
+
# codepoints that get truncated to their low byte during serialisation.
|
|
24
|
+
_UNICODE_TO_ASCII = str.maketrans({
|
|
25
|
+
"\u2018": "'", # ' left single quotation mark
|
|
26
|
+
"\u2019": "'", # ' right single quotation mark / apostrophe
|
|
27
|
+
"\u201c": '"', # " left double quotation mark
|
|
28
|
+
"\u201d": '"', # " right double quotation mark
|
|
29
|
+
"\u2013": "-", # – en dash
|
|
30
|
+
"\u2014": "--", # — em dash
|
|
31
|
+
"\u2026": "...", # … horizontal ellipsis
|
|
32
|
+
"\u00a0": " ", # non-breaking space
|
|
33
|
+
# Corrupted forms: low-byte of U+2019 / U+2013 stored as control chars
|
|
34
|
+
"\x19": "'", # truncated U+2019 right-single-quote low byte
|
|
35
|
+
"\x11": "-", # truncated U+2011/U+2013 dash low byte
|
|
36
|
+
"\x18": "'", # truncated U+2018 left-single-quote low byte
|
|
37
|
+
"\x1c": '"', # truncated U+201C left-double-quote low byte
|
|
38
|
+
"\x1d": '"', # truncated U+201D right-double-quote low byte
|
|
39
|
+
"\x14": "--", # truncated U+2014 em-dash low byte
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _normalize_unicode(html: str) -> str:
|
|
44
|
+
return html.translate(_UNICODE_TO_ASCII)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ConvertDocument(BaseTool):
|
|
48
|
+
"""
|
|
49
|
+
Convert a document to different formats.
|
|
50
|
+
|
|
51
|
+
Supported conversions:
|
|
52
|
+
- HTML → PDF (high-quality, print-ready)
|
|
53
|
+
- HTML → DOCX (Word document)
|
|
54
|
+
- HTML → Markdown (for documentation)
|
|
55
|
+
- HTML → TXT (plain text)
|
|
56
|
+
|
|
57
|
+
The tool reads the .source.html file and converts it to the requested format.
|
|
58
|
+
The original files are preserved - conversion creates a new file.
|
|
59
|
+
|
|
60
|
+
Use this tool to:
|
|
61
|
+
- Create PDF versions for sharing/printing
|
|
62
|
+
- Export to Markdown for documentation sites
|
|
63
|
+
- Generate plain text versions
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
project_name: str = Field(
|
|
67
|
+
...,
|
|
68
|
+
description="Name of the project folder containing the document"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
document_name: str = Field(
|
|
72
|
+
...,
|
|
73
|
+
description="Name of the document to convert (without extension)"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
output_format: Literal["pdf", "docx", "markdown", "txt"] = Field(
|
|
77
|
+
...,
|
|
78
|
+
description="""Target format for conversion:
|
|
79
|
+
- 'pdf': High-quality PDF (requires weasyprint)
|
|
80
|
+
- 'docx': Word document
|
|
81
|
+
- 'markdown': Markdown format (useful for documentation)
|
|
82
|
+
- 'txt': Plain text (strips all formatting)"""
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
overwrite: bool = Field(
|
|
86
|
+
default=True,
|
|
87
|
+
description="If True (default), overwrites existing converted file. If False, returns error if file exists."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def run(self):
|
|
91
|
+
"""Convert document to specified format."""
|
|
92
|
+
try:
|
|
93
|
+
project_dir = get_project_dir(self.project_name)
|
|
94
|
+
|
|
95
|
+
if not project_dir.exists():
|
|
96
|
+
return f"Error: Project '{self.project_name}' not found."
|
|
97
|
+
|
|
98
|
+
doc_name = (
|
|
99
|
+
self.document_name.replace(".html", "")
|
|
100
|
+
.replace(".docx", "")
|
|
101
|
+
.replace(".md", "")
|
|
102
|
+
)
|
|
103
|
+
source_path = project_dir / f"{doc_name}.source.html"
|
|
104
|
+
|
|
105
|
+
if not source_path.exists():
|
|
106
|
+
return f"Error: Document '{doc_name}' not found in project '{self.project_name}'."
|
|
107
|
+
|
|
108
|
+
# Determine output file extension
|
|
109
|
+
ext_map = {
|
|
110
|
+
"pdf": ".pdf",
|
|
111
|
+
"docx": ".docx",
|
|
112
|
+
"markdown": ".md",
|
|
113
|
+
"txt": ".txt",
|
|
114
|
+
}
|
|
115
|
+
output_path = project_dir / f"{doc_name}{ext_map[self.output_format]}"
|
|
116
|
+
|
|
117
|
+
# DOCX auto-versions; all other formats respect overwrite flag
|
|
118
|
+
if self.output_format == "docx":
|
|
119
|
+
output_path = next_docx_version(output_path)
|
|
120
|
+
elif output_path.exists() and not self.overwrite:
|
|
121
|
+
return (
|
|
122
|
+
f"Error: Output file '{output_path.name}' already exists. "
|
|
123
|
+
"Set overwrite=True to replace it."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
html_content = source_path.read_text(encoding="utf-8")
|
|
127
|
+
html_content = embed_local_images(html_content, project_dir)
|
|
128
|
+
if self.output_format in ("pdf", "docx"):
|
|
129
|
+
html_content = auto_page_breaks(html_content)
|
|
130
|
+
|
|
131
|
+
if self.output_format == "pdf":
|
|
132
|
+
self._convert_to_pdf(html_content, output_path)
|
|
133
|
+
elif self.output_format == "docx":
|
|
134
|
+
self._convert_to_docx(html_content, output_path)
|
|
135
|
+
elif self.output_format == "markdown":
|
|
136
|
+
self._convert_to_markdown(html_content, output_path)
|
|
137
|
+
elif self.output_format == "txt":
|
|
138
|
+
self._convert_to_txt(html_content, output_path)
|
|
139
|
+
|
|
140
|
+
if not output_path.exists():
|
|
141
|
+
return f"Error: Conversion failed to produce '{output_path.name}'."
|
|
142
|
+
|
|
143
|
+
# For DOCX exports, save a snapshot of the HTML source alongside the file.
|
|
144
|
+
# This is the version history — RestoreDocument can roll back to any snapshot.
|
|
145
|
+
if self.output_format == "docx":
|
|
146
|
+
snapshot_path = output_path.parent / f"{output_path.name}.snapshot.html"
|
|
147
|
+
snapshot_path.write_text(source_path.read_text(encoding="utf-8"), encoding="utf-8")
|
|
148
|
+
|
|
149
|
+
output_size = output_path.stat().st_size
|
|
150
|
+
|
|
151
|
+
message = f"""Successfully converted document to {self.output_format.upper()}!
|
|
152
|
+
|
|
153
|
+
Project: {self.project_name}
|
|
154
|
+
Document: {doc_name}
|
|
155
|
+
Source: {source_path.name}
|
|
156
|
+
Output: {output_path.name} ({output_size:,} bytes)
|
|
157
|
+
|
|
158
|
+
Path: {output_path}"""
|
|
159
|
+
|
|
160
|
+
if self.output_format == "pdf":
|
|
161
|
+
return [
|
|
162
|
+
ToolOutputText(text=message),
|
|
163
|
+
tool_output_file_from_path(output_path),
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
return message
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return f"Error converting document: {str(e)}"
|
|
169
|
+
|
|
170
|
+
def _convert_to_pdf(self, html_content: str, output_path: Path):
|
|
171
|
+
"""Convert HTML to PDF using weasyprint."""
|
|
172
|
+
HTML(string=_normalize_unicode(html_content)).write_pdf(output_path)
|
|
173
|
+
|
|
174
|
+
def _convert_to_docx(self, html_content: str, output_path: Path):
|
|
175
|
+
"""Convert HTML to DOCX using the internal converter."""
|
|
176
|
+
html_to_docx(html_content, output_path)
|
|
177
|
+
|
|
178
|
+
def _convert_to_markdown(self, html_content: str, output_path: Path):
|
|
179
|
+
"""Convert HTML to Markdown."""
|
|
180
|
+
converter = html2text.HTML2Text()
|
|
181
|
+
converter.body_width = 0 # Don't wrap text
|
|
182
|
+
markdown = converter.handle(html_content)
|
|
183
|
+
output_path.write_text(markdown, encoding="utf-8")
|
|
184
|
+
|
|
185
|
+
def _convert_to_txt(self, html_content: str, output_path: Path):
|
|
186
|
+
"""Convert HTML to plain text."""
|
|
187
|
+
soup = BeautifulSoup(html_content, "html.parser")
|
|
188
|
+
text = soup.get_text(separator="\n", strip=True)
|
|
189
|
+
output_path.write_text(text, encoding="utf-8")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
print("=" * 70)
|
|
194
|
+
print("TEST: ConvertDocument Tool")
|
|
195
|
+
print("=" * 70)
|
|
196
|
+
print()
|
|
197
|
+
|
|
198
|
+
print("Setup: Creating test document...")
|
|
199
|
+
html_content = """<!DOCTYPE html>
|
|
200
|
+
<html>
|
|
201
|
+
<head>
|
|
202
|
+
<meta charset="UTF-8">
|
|
203
|
+
<title>Sample Report</title>
|
|
204
|
+
<style>
|
|
205
|
+
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
206
|
+
h1 { color: #0066cc; }
|
|
207
|
+
h2 { color: #333; border-bottom: 2px solid #0066cc; padding-bottom: 5px; }
|
|
208
|
+
p { line-height: 1.6; }
|
|
209
|
+
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
|
|
210
|
+
th { background: #0066cc; color: white; padding: 10px; text-align: left; }
|
|
211
|
+
td { border: 1px solid #ddd; padding: 8px; }
|
|
212
|
+
</style>
|
|
213
|
+
</head>
|
|
214
|
+
<body>
|
|
215
|
+
<h1>Annual Report 2026</h1>
|
|
216
|
+
|
|
217
|
+
<h2>Executive Summary</h2>
|
|
218
|
+
<p>
|
|
219
|
+
This report provides an overview of our company's performance in 2026.
|
|
220
|
+
We achieved significant growth across all key metrics.
|
|
221
|
+
</p>
|
|
222
|
+
|
|
223
|
+
<h2>Financial Highlights</h2>
|
|
224
|
+
<table>
|
|
225
|
+
<tr>
|
|
226
|
+
<th>Metric</th>
|
|
227
|
+
<th>2025</th>
|
|
228
|
+
<th>2026</th>
|
|
229
|
+
<th>Growth</th>
|
|
230
|
+
</tr>
|
|
231
|
+
<tr>
|
|
232
|
+
<td>Revenue</td>
|
|
233
|
+
<td>$10M</td>
|
|
234
|
+
<td>$15M</td>
|
|
235
|
+
<td>+50%</td>
|
|
236
|
+
</tr>
|
|
237
|
+
<tr>
|
|
238
|
+
<td>Customers</td>
|
|
239
|
+
<td>500</td>
|
|
240
|
+
<td>800</td>
|
|
241
|
+
<td>+60%</td>
|
|
242
|
+
</tr>
|
|
243
|
+
</table>
|
|
244
|
+
|
|
245
|
+
<h2>Key Achievements</h2>
|
|
246
|
+
<ul>
|
|
247
|
+
<li>Launched new product line</li>
|
|
248
|
+
<li>Expanded to 3 new markets</li>
|
|
249
|
+
<li>Achieved profitability</li>
|
|
250
|
+
</ul>
|
|
251
|
+
</body>
|
|
252
|
+
</html>"""
|
|
253
|
+
|
|
254
|
+
create_tool = CreateDocument(
|
|
255
|
+
project_name="test_convert",
|
|
256
|
+
document_name="annual_report",
|
|
257
|
+
content={"type": "html", "value": html_content},
|
|
258
|
+
overwrite=True,
|
|
259
|
+
)
|
|
260
|
+
print(create_tool.run())
|
|
261
|
+
print()
|
|
262
|
+
|
|
263
|
+
# Test 1: Convert to PDF
|
|
264
|
+
print("Test 1: Converting to PDF...")
|
|
265
|
+
tool = ConvertDocument(
|
|
266
|
+
project_name="test_convert",
|
|
267
|
+
document_name="annual_report",
|
|
268
|
+
output_format="pdf"
|
|
269
|
+
)
|
|
270
|
+
result = tool.run()
|
|
271
|
+
print(result)
|
|
272
|
+
print()
|
|
273
|
+
|
|
274
|
+
# Test 2: Convert to Markdown
|
|
275
|
+
print("Test 2: Converting to Markdown...")
|
|
276
|
+
tool = ConvertDocument(
|
|
277
|
+
project_name="test_convert",
|
|
278
|
+
document_name="annual_report",
|
|
279
|
+
output_format="markdown"
|
|
280
|
+
)
|
|
281
|
+
result = tool.run()
|
|
282
|
+
print(result)
|
|
283
|
+
print()
|
|
284
|
+
|
|
285
|
+
# Test 3: Convert to TXT
|
|
286
|
+
print("Test 3: Converting to plain text...")
|
|
287
|
+
tool = ConvertDocument(
|
|
288
|
+
project_name="test_convert",
|
|
289
|
+
document_name="annual_report",
|
|
290
|
+
output_format="txt"
|
|
291
|
+
)
|
|
292
|
+
result = tool.run()
|
|
293
|
+
print(result)
|
|
294
|
+
print()
|
|
295
|
+
|
|
296
|
+
# Test 4: View markdown output
|
|
297
|
+
if "✅" in result:
|
|
298
|
+
print("Test 4: Viewing converted Markdown content...")
|
|
299
|
+
from pathlib import Path
|
|
300
|
+
md_path = get_project_dir("test_convert") / "annual_report.md"
|
|
301
|
+
if md_path.exists():
|
|
302
|
+
print("Markdown content:")
|
|
303
|
+
print("-" * 70)
|
|
304
|
+
print(md_path.read_text(encoding='utf-8')[:500])
|
|
305
|
+
print("...")
|
|
306
|
+
print("-" * 70)
|
|
307
|
+
print()
|
|
308
|
+
|
|
309
|
+
# Test 5: Convert non-existent document (should fail)
|
|
310
|
+
print("Test 5: Attempting to convert non-existent document...")
|
|
311
|
+
tool = ConvertDocument(
|
|
312
|
+
project_name="test_convert",
|
|
313
|
+
document_name="nonexistent",
|
|
314
|
+
output_format="pdf"
|
|
315
|
+
)
|
|
316
|
+
print(tool.run())
|
|
317
|
+
print()
|
|
318
|
+
|
|
319
|
+
print("=" * 70)
|
|
320
|
+
print("✅ ALL TESTS COMPLETE")
|
|
321
|
+
print("=" * 70)
|
|
322
|
+
print("\nNote: Some conversion tests may show warnings if optional dependencies")
|
|
323
|
+
print("(weasyprint, html2text) are not installed. This is expected.")
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Create a new document from HTML or Markdown content."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tempfile import TemporaryDirectory
|
|
5
|
+
from typing import Literal, Union
|
|
6
|
+
|
|
7
|
+
from agency_swarm.tools import BaseTool, ToolOutputText, tool_output_image_from_path
|
|
8
|
+
from playwright.sync_api import sync_playwright
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
from .utils.html_validation import build_unsupported_error, find_unsupported_html
|
|
11
|
+
from .utils.html_docx_playwright import _launch_chromium_with_install
|
|
12
|
+
from .utils.html_docx_constants import _UA_RESET_STYLE
|
|
13
|
+
from .utils.doc_file_utils import get_project_dir
|
|
14
|
+
from .utils.html_docx_images import embed_local_images
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HtmlContent(BaseModel):
|
|
18
|
+
type: Literal["html"]
|
|
19
|
+
value: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MarkdownContent(BaseModel):
|
|
23
|
+
type: Literal["markdown"]
|
|
24
|
+
value: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ContentInput = Union[HtmlContent, MarkdownContent]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CreateDocument(BaseTool):
|
|
31
|
+
"""
|
|
32
|
+
Create a new document from HTML or Markdown content.
|
|
33
|
+
|
|
34
|
+
HTML workflow creates:
|
|
35
|
+
- .source.html file (the canonical source of truth)
|
|
36
|
+
|
|
37
|
+
Markdown workflow creates:
|
|
38
|
+
- .md file only (no .docx or .pdf generation)
|
|
39
|
+
|
|
40
|
+
HTML is used as the source format because it provides:
|
|
41
|
+
- Full styling control (fonts, colors, spacing, etc.)
|
|
42
|
+
- Standard conversion tools (weasyprint)
|
|
43
|
+
- WYSIWYG editing experience
|
|
44
|
+
- Easy web preview capability
|
|
45
|
+
|
|
46
|
+
Use this tool to create new documents with custom formatting and styling.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
project_name: str = Field(
|
|
50
|
+
...,
|
|
51
|
+
description="Name of the project folder (creates/uses ./mnt/{project_name}/documents/). Use lowercase with underscores (e.g., 'business_proposals', 'client_reports')"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
document_name: str = Field(
|
|
55
|
+
...,
|
|
56
|
+
description="Name of the document file without extension (e.g., 'quarterly_report', 'contract_template'). Extension will be added automatically."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
content: ContentInput = Field(
|
|
60
|
+
...,
|
|
61
|
+
description="""Content object for the document.
|
|
62
|
+
|
|
63
|
+
HTML example:
|
|
64
|
+
{
|
|
65
|
+
"type": "html",
|
|
66
|
+
"value": "<!DOCTYPE html>..."
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Markdown example:
|
|
70
|
+
{
|
|
71
|
+
"type": "markdown",
|
|
72
|
+
"value": "# Title\\n\\n- Item"
|
|
73
|
+
}
|
|
74
|
+
"""
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
overwrite: bool = Field(
|
|
78
|
+
default=False,
|
|
79
|
+
description="If True, overwrites existing document. If False (default), returns an error if document already exists."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def run(self):
|
|
83
|
+
"""Create a new document from HTML or Markdown content."""
|
|
84
|
+
try:
|
|
85
|
+
project_dir = get_project_dir(self.project_name)
|
|
86
|
+
project_dir.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
(project_dir / "assets").mkdir(exist_ok=True)
|
|
88
|
+
|
|
89
|
+
# Strip extension if the caller included one
|
|
90
|
+
doc_name = (
|
|
91
|
+
self.document_name.replace(".html", "")
|
|
92
|
+
.replace(".docx", "")
|
|
93
|
+
.replace(".md", "")
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
content_value = self.content.value
|
|
97
|
+
if not content_value:
|
|
98
|
+
return "Error: content.value is required."
|
|
99
|
+
|
|
100
|
+
if self.content.type == "markdown":
|
|
101
|
+
return self._create_markdown(doc_name, project_dir, content_value)
|
|
102
|
+
|
|
103
|
+
source_path = project_dir / f"{doc_name}.source.html"
|
|
104
|
+
|
|
105
|
+
if source_path.exists() and not self.overwrite:
|
|
106
|
+
return f"Error: Document '{doc_name}' already exists in project '{self.project_name}'. Use overwrite=True to replace it, or choose a different document name."
|
|
107
|
+
|
|
108
|
+
normalized_html = _ensure_ua_reset(content_value)
|
|
109
|
+
issues = find_unsupported_html(normalized_html)
|
|
110
|
+
if issues:
|
|
111
|
+
return build_unsupported_error(issues)
|
|
112
|
+
source_path.write_text(normalized_html, encoding='utf-8')
|
|
113
|
+
|
|
114
|
+
source_size = source_path.stat().st_size
|
|
115
|
+
|
|
116
|
+
operation = "updated" if self.overwrite and source_path.exists() else "created"
|
|
117
|
+
|
|
118
|
+
message = f"""Successfully {operation} document
|
|
119
|
+
|
|
120
|
+
Project: {self.project_name}
|
|
121
|
+
Document: {doc_name}
|
|
122
|
+
|
|
123
|
+
Files created:
|
|
124
|
+
- {source_path.name} ({source_size:,} bytes)
|
|
125
|
+
|
|
126
|
+
Path: {source_path}
|
|
127
|
+
|
|
128
|
+
Note: The .source.html file is the canonical source to be used for document conversion."""
|
|
129
|
+
try:
|
|
130
|
+
preview = _build_html_preview_image(normalized_html, project_dir)
|
|
131
|
+
except Exception:
|
|
132
|
+
return ToolOutputText(text=message)
|
|
133
|
+
|
|
134
|
+
return [
|
|
135
|
+
ToolOutputText(text=message),
|
|
136
|
+
preview,
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
return f"Error creating document: {str(e)}"
|
|
141
|
+
|
|
142
|
+
def _create_markdown(self, doc_name, project_dir, markdown_value):
|
|
143
|
+
md_path = project_dir / f"{doc_name}.md"
|
|
144
|
+
if md_path.exists() and not self.overwrite:
|
|
145
|
+
return f"Error: Document '{doc_name}' already exists in project '{self.project_name}'. Use overwrite=True to replace it, or choose a different document name."
|
|
146
|
+
|
|
147
|
+
md_path.write_text(markdown_value, encoding="utf-8")
|
|
148
|
+
if not md_path.exists():
|
|
149
|
+
return f"Error: Markdown generation failed for document '{doc_name}'."
|
|
150
|
+
md_size = md_path.stat().st_size
|
|
151
|
+
operation = "updated" if self.overwrite and md_path.exists() else "created"
|
|
152
|
+
|
|
153
|
+
return f"""Successfully {operation} document!
|
|
154
|
+
|
|
155
|
+
Project: {self.project_name}
|
|
156
|
+
Document: {doc_name}
|
|
157
|
+
|
|
158
|
+
Files created:
|
|
159
|
+
- {md_path.name} ({md_size:,} bytes) [Markdown source]
|
|
160
|
+
|
|
161
|
+
Path: {md_path}
|
|
162
|
+
|
|
163
|
+
Note: Markdown workflow only creates a .md file and does not generate .docx or .pdf files."""
|
|
164
|
+
|
|
165
|
+
def _build_html_preview_image(html_content: str, base_dir: Path):
|
|
166
|
+
"""Render a preview JPEG of the HTML document.
|
|
167
|
+
|
|
168
|
+
The temp file is written inside base_dir so Playwright resolves
|
|
169
|
+
relative image paths correctly (mirrors the slides-agent pattern).
|
|
170
|
+
Images are also embedded as data URIs for full fidelity.
|
|
171
|
+
"""
|
|
172
|
+
import tempfile
|
|
173
|
+
|
|
174
|
+
from PIL import Image
|
|
175
|
+
|
|
176
|
+
preview_html = embed_local_images(html_content, base_dir)
|
|
177
|
+
|
|
178
|
+
with tempfile.NamedTemporaryFile(
|
|
179
|
+
mode="w",
|
|
180
|
+
suffix=".html",
|
|
181
|
+
delete=False,
|
|
182
|
+
encoding="utf-8",
|
|
183
|
+
dir=base_dir,
|
|
184
|
+
) as tmp_html:
|
|
185
|
+
tmp_html.write(preview_html)
|
|
186
|
+
tmp_html_path = Path(tmp_html.name)
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
with TemporaryDirectory() as tmp_dir:
|
|
190
|
+
tmp_dir_path = Path(tmp_dir)
|
|
191
|
+
raw_jpg = tmp_dir_path / "preview_raw.jpg"
|
|
192
|
+
out_jpg = tmp_dir_path / "preview.jpg"
|
|
193
|
+
|
|
194
|
+
with sync_playwright() as p:
|
|
195
|
+
browser = _launch_chromium_with_install(p)
|
|
196
|
+
page = browser.new_page(viewport={"width": 794, "height": 1123})
|
|
197
|
+
page.goto(tmp_html_path.as_uri())
|
|
198
|
+
page.wait_for_load_state("networkidle")
|
|
199
|
+
page.screenshot(path=str(raw_jpg), full_page=True, type="jpeg", quality=80)
|
|
200
|
+
browser.close()
|
|
201
|
+
|
|
202
|
+
img = Image.open(raw_jpg)
|
|
203
|
+
new_size = (int(img.width * 0.75), int(img.height * 0.75))
|
|
204
|
+
img = img.resize(new_size, Image.Resampling.LANCZOS)
|
|
205
|
+
img.save(out_jpg, "JPEG", quality=75, optimize=True)
|
|
206
|
+
|
|
207
|
+
return tool_output_image_from_path(out_jpg, detail="auto")
|
|
208
|
+
finally:
|
|
209
|
+
tmp_html_path.unlink(missing_ok=True)
|
|
210
|
+
|
|
211
|
+
def _ensure_ua_reset(html_content: str) -> str:
|
|
212
|
+
"""Ensure a UA reset style exists in the HTML head."""
|
|
213
|
+
if "UA reset to neutralize browser defaults" in html_content:
|
|
214
|
+
return html_content
|
|
215
|
+
|
|
216
|
+
lower = html_content.lower()
|
|
217
|
+
head_index = lower.find("<head")
|
|
218
|
+
if head_index != -1:
|
|
219
|
+
head_close = lower.find(">", head_index)
|
|
220
|
+
if head_close != -1:
|
|
221
|
+
return (
|
|
222
|
+
html_content[: head_close + 1]
|
|
223
|
+
+ _UA_RESET_STYLE
|
|
224
|
+
+ html_content[head_close + 1 :]
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if "<html" in lower:
|
|
228
|
+
return html_content.replace("<html>", f"<html><head>{_UA_RESET_STYLE}</head>", 1)
|
|
229
|
+
|
|
230
|
+
return f"<!DOCTYPE html><html><head>{_UA_RESET_STYLE}</head><body>{html_content}</body></html>"
|
|
231
|
+
|
|
232
|
+
if __name__ == "__main__":
|
|
233
|
+
print("=" * 70)
|
|
234
|
+
print("TEST: CreateDocument Tool")
|
|
235
|
+
print("=" * 70)
|
|
236
|
+
print()
|
|
237
|
+
html_simple = """<!DOCTYPE html>
|
|
238
|
+
<html>
|
|
239
|
+
<head>
|
|
240
|
+
<meta charset="UTF-8">
|
|
241
|
+
<title>Business Proposal</title>
|
|
242
|
+
</head>
|
|
243
|
+
<body>
|
|
244
|
+
<h1 style="color: #0066cc; font-family: Arial, sans-serif;">Business Proposal</h1>
|
|
245
|
+
|
|
246
|
+
<h2 style="color: #333; font-family: Arial, sans-serif;">Executive Summary</h2>
|
|
247
|
+
<p style="font-family: Georgia, serif; font-size: 11pt; line-height: 1.5;">
|
|
248
|
+
This proposal outlines our comprehensive approach to solving your business challenges.
|
|
249
|
+
We bring extensive experience and proven methodologies to deliver results.
|
|
250
|
+
</p>
|
|
251
|
+
|
|
252
|
+
<h2 style="color: #333; font-family: Arial, sans-serif;">Our Services</h2>
|
|
253
|
+
<ul style="font-family: Georgia, serif; font-size: 11pt; line-height: 1.5;">
|
|
254
|
+
<li><strong>Consulting:</strong> Strategic business advisory</li>
|
|
255
|
+
<li><strong>Implementation:</strong> End-to-end project execution</li>
|
|
256
|
+
<li><strong>Support:</strong> Ongoing maintenance and optimization</li>
|
|
257
|
+
</ul>
|
|
258
|
+
|
|
259
|
+
<h2 style="color: #333; font-family: Arial, sans-serif;">Pricing</h2>
|
|
260
|
+
<table style="width: 100%; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 10pt;">
|
|
261
|
+
<tr style="background: #0066cc; color: white;">
|
|
262
|
+
<th style="padding: 10px; border: 1px solid #ccc; text-align: left;">Package</th>
|
|
263
|
+
<th style="padding: 10px; border: 1px solid #ccc; text-align: right;">Price</th>
|
|
264
|
+
</tr>
|
|
265
|
+
<tr>
|
|
266
|
+
<td style="padding: 8px; border: 1px solid #ccc;">Basic</td>
|
|
267
|
+
<td style="padding: 8px; border: 1px solid #ccc; text-align: right;">$5,000</td>
|
|
268
|
+
</tr>
|
|
269
|
+
<tr style="background: #f9f9f9;">
|
|
270
|
+
<td style="padding: 8px; border: 1px solid #ccc;">Professional</td>
|
|
271
|
+
<td style="padding: 8px; border: 1px solid #ccc; text-align: right;">$10,000</td>
|
|
272
|
+
</tr>
|
|
273
|
+
<tr>
|
|
274
|
+
<td style="padding: 8px; border: 1px solid #ccc;">Enterprise</td>
|
|
275
|
+
<td style="padding: 8px; border: 1px solid #ccc; text-align: right;">$25,000</td>
|
|
276
|
+
</tr>
|
|
277
|
+
</table>
|
|
278
|
+
</body>
|
|
279
|
+
</html>"""
|
|
280
|
+
|
|
281
|
+
tool = CreateDocument(
|
|
282
|
+
project_name="test_project",
|
|
283
|
+
document_name="business_proposal",
|
|
284
|
+
content={"type": "html", "value": html_simple},
|
|
285
|
+
)
|
|
286
|
+
print(tool.run())
|
|
287
|
+
print()
|