@_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,522 @@
|
|
|
1
|
+
"""Shared utilities for Sora video tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from urllib.parse import urlparse
|
|
13
|
+
|
|
14
|
+
import cv2
|
|
15
|
+
import httpx
|
|
16
|
+
from openai import OpenAI
|
|
17
|
+
from PIL import Image
|
|
18
|
+
from google import genai
|
|
19
|
+
|
|
20
|
+
from agency_swarm import ToolOutputText, ToolOutputImage
|
|
21
|
+
|
|
22
|
+
from .image_utils import (
|
|
23
|
+
load_image_by_name,
|
|
24
|
+
get_images_dir,
|
|
25
|
+
compress_image_for_base64,
|
|
26
|
+
IMAGES_DIR,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
SORA_MODEL = "sora-2"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_veo_model(model: str) -> bool:
|
|
36
|
+
return model.startswith("veo-")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def is_sora_model(model: str) -> bool:
|
|
40
|
+
return model.startswith("sora-")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def is_seedance_model(model: str) -> bool:
|
|
44
|
+
return model.startswith("seedance-")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if os.path.isfile("/.dockerenv"):
|
|
48
|
+
MNT_DIR = Path("/app/mnt")
|
|
49
|
+
else:
|
|
50
|
+
MNT_DIR = Path(__file__).parent.parent.parent.parent / "mnt"
|
|
51
|
+
|
|
52
|
+
VIDEO_DIR = str(MNT_DIR / "generated_videos") # fallback when no product_name is given
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_videos_dir(product_name: str) -> str:
|
|
56
|
+
"""
|
|
57
|
+
Get the videos directory for a specific product.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
product_name: Name of the product (sanitized folder name)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Path to product's generated_videos directory
|
|
64
|
+
"""
|
|
65
|
+
videos_dir = MNT_DIR / product_name / "generated_videos"
|
|
66
|
+
videos_dir.mkdir(parents=True, exist_ok=True)
|
|
67
|
+
return str(videos_dir)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def resolve_ffmpeg_executable() -> str:
|
|
71
|
+
"""Resolve an ffmpeg executable from PATH or imageio-ffmpeg fallback."""
|
|
72
|
+
ffmpeg_path = shutil.which("ffmpeg")
|
|
73
|
+
if ffmpeg_path:
|
|
74
|
+
return ffmpeg_path
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
from imageio_ffmpeg import get_ffmpeg_exe # type: ignore
|
|
78
|
+
|
|
79
|
+
ffmpeg_path = get_ffmpeg_exe()
|
|
80
|
+
if ffmpeg_path:
|
|
81
|
+
return ffmpeg_path
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
raise RuntimeError(
|
|
86
|
+
"ffmpeg executable not found. Install ffmpeg and add it to PATH, "
|
|
87
|
+
"or install the Python package 'imageio-ffmpeg' for bundled ffmpeg fallback."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_openai_client() -> OpenAI:
|
|
92
|
+
"""Instantiate an OpenAI client from the environment."""
|
|
93
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
|
94
|
+
if not api_key:
|
|
95
|
+
raise RuntimeError(
|
|
96
|
+
"OPENAI_API_KEY environment variable is required for video operations"
|
|
97
|
+
)
|
|
98
|
+
return OpenAI(api_key=api_key)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_gemini_client():
|
|
102
|
+
"""Instantiate a Gemini client from the environment."""
|
|
103
|
+
api_key = os.getenv("GOOGLE_API_KEY")
|
|
104
|
+
if not api_key:
|
|
105
|
+
raise ValueError("GOOGLE_API_KEY is not set. Add it to your .env to use video generation.")
|
|
106
|
+
return genai.Client(api_key=api_key)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def parse_video_size(size: str) -> tuple[int, int]:
|
|
110
|
+
"""
|
|
111
|
+
Parse video size string into width and height tuple.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
size: Size string in WIDTHxHEIGHT format (e.g. '1280x720')
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Tuple of (width, height)
|
|
118
|
+
"""
|
|
119
|
+
parts = size.lower().split("x")
|
|
120
|
+
if len(parts) != 2:
|
|
121
|
+
raise ValueError(f"Invalid size format: {size}")
|
|
122
|
+
return int(parts[0]), int(parts[1])
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def resize_image_to_dimensions(image: Image.Image, width: int, height: int) -> Image.Image:
|
|
126
|
+
"""
|
|
127
|
+
Resize an image to match specific dimensions while maintaining aspect ratio.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
image: PIL Image to resize
|
|
131
|
+
width: Target width
|
|
132
|
+
height: Target height
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Resized PIL Image
|
|
136
|
+
"""
|
|
137
|
+
# Calculate aspect ratios
|
|
138
|
+
target_ratio = width / height
|
|
139
|
+
image_ratio = image.width / image.height
|
|
140
|
+
|
|
141
|
+
if abs(target_ratio - image_ratio) < 0.01:
|
|
142
|
+
# Aspect ratios are close enough, just resize directly
|
|
143
|
+
return image.resize((width, height), Image.Resampling.LANCZOS)
|
|
144
|
+
|
|
145
|
+
# Aspect ratios differ, crop to match target ratio first
|
|
146
|
+
if image_ratio > target_ratio:
|
|
147
|
+
# Image is wider than target, crop width
|
|
148
|
+
new_width = int(image.height * target_ratio)
|
|
149
|
+
left = (image.width - new_width) // 2
|
|
150
|
+
image = image.crop((left, 0, left + new_width, image.height))
|
|
151
|
+
else:
|
|
152
|
+
# Image is taller than target, crop height
|
|
153
|
+
new_height = int(image.width / target_ratio)
|
|
154
|
+
top = (image.height - new_height) // 2
|
|
155
|
+
image = image.crop((0, top, image.width, top + new_height))
|
|
156
|
+
|
|
157
|
+
# Now resize to exact dimensions
|
|
158
|
+
return image.resize((width, height), Image.Resampling.LANCZOS)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def resolve_input_reference(reference: Optional[str], target_size: Optional[str] = None, product_name: Optional[str] = None) -> Optional[io.BufferedReader]:
|
|
162
|
+
"""
|
|
163
|
+
Turn an image name, local path, or HTTPS URL into a binary file handle for the API.
|
|
164
|
+
Optionally resizes the image to match target video dimensions.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
reference: Image name (without extension), full path, or URL to the reference image
|
|
168
|
+
target_size: Optional target size in WIDTHxHEIGHT format (e.g. '1280x720')
|
|
169
|
+
product_name: Product name for locating images in product-specific folders
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Binary file handle ready for API upload
|
|
173
|
+
"""
|
|
174
|
+
if reference is None:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
parsed = urlparse(reference)
|
|
178
|
+
|
|
179
|
+
if parsed.scheme in ("http", "https"):
|
|
180
|
+
# Handle URL
|
|
181
|
+
logger.info("Downloading reference image from URL...")
|
|
182
|
+
with httpx.Client(timeout=30.0) as client:
|
|
183
|
+
response = client.get(reference)
|
|
184
|
+
response.raise_for_status()
|
|
185
|
+
image_data = io.BytesIO(response.content)
|
|
186
|
+
filename = Path(parsed.path).name or "reference.png"
|
|
187
|
+
else:
|
|
188
|
+
# Try as full path first
|
|
189
|
+
path = Path(reference).expanduser().resolve()
|
|
190
|
+
|
|
191
|
+
if path.exists():
|
|
192
|
+
# Handle full path
|
|
193
|
+
logger.info(f"Loading reference image from {path}...")
|
|
194
|
+
with open(path, "rb") as f:
|
|
195
|
+
image_data = io.BytesIO(f.read())
|
|
196
|
+
filename = path.name
|
|
197
|
+
else:
|
|
198
|
+
# Try as image name without extension in multiple directories
|
|
199
|
+
pil_image = None
|
|
200
|
+
image_path = None
|
|
201
|
+
|
|
202
|
+
# Get product-specific directories
|
|
203
|
+
if product_name:
|
|
204
|
+
images_dir = get_images_dir(product_name)
|
|
205
|
+
videos_dir = get_videos_dir(product_name)
|
|
206
|
+
else:
|
|
207
|
+
# Fallback to legacy paths if no product_name provided
|
|
208
|
+
images_dir = IMAGES_DIR
|
|
209
|
+
videos_dir = VIDEO_DIR
|
|
210
|
+
|
|
211
|
+
# Try in generated_images directory first
|
|
212
|
+
logger.info(f"Looking for image '{reference}' in {images_dir}...")
|
|
213
|
+
pil_image, image_path, load_error_images = load_image_by_name(
|
|
214
|
+
reference, images_dir, [".png", ".jpg", ".jpeg", ".webp"]
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# If not found, try in generated_videos directory (for thumbnails/spritesheets)
|
|
218
|
+
if load_error_images:
|
|
219
|
+
logger.info(f"Not found in {images_dir}, trying {videos_dir}...")
|
|
220
|
+
pil_image, image_path, load_error_videos = load_image_by_name(
|
|
221
|
+
reference, videos_dir, [".png", ".jpg", ".jpeg", ".webp"]
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if pil_image is None:
|
|
225
|
+
raise FileNotFoundError(f"Reference image '{reference}' not found in {images_dir} or {videos_dir}")
|
|
226
|
+
|
|
227
|
+
logger.info(f"Loaded image: {image_path}")
|
|
228
|
+
|
|
229
|
+
# Convert PIL Image to BytesIO
|
|
230
|
+
image_data = io.BytesIO()
|
|
231
|
+
pil_image.save(image_data, format=pil_image.format or "PNG")
|
|
232
|
+
image_data.seek(0)
|
|
233
|
+
filename = Path(image_path).name
|
|
234
|
+
|
|
235
|
+
if target_size:
|
|
236
|
+
logger.info(f"Resizing reference image to match video dimensions: {target_size}")
|
|
237
|
+
image_data.seek(0)
|
|
238
|
+
image = Image.open(image_data)
|
|
239
|
+
|
|
240
|
+
# Get target dimensions
|
|
241
|
+
width, height = parse_video_size(target_size)
|
|
242
|
+
|
|
243
|
+
# Resize the image
|
|
244
|
+
resized_image = resize_image_to_dimensions(image, width, height)
|
|
245
|
+
|
|
246
|
+
# Save resized image to buffer
|
|
247
|
+
buffer = io.BytesIO()
|
|
248
|
+
# Preserve format or use PNG as default
|
|
249
|
+
image_format = image.format or "PNG"
|
|
250
|
+
resized_image.save(buffer, format=image_format)
|
|
251
|
+
buffer.seek(0)
|
|
252
|
+
buffer.name = filename
|
|
253
|
+
|
|
254
|
+
logger.info(f"Reference image resized from {image.width}x{image.height} to {width}x{height}")
|
|
255
|
+
return buffer # type: ignore[return-value]
|
|
256
|
+
|
|
257
|
+
image_data.seek(0)
|
|
258
|
+
image_data.name = filename
|
|
259
|
+
return image_data # type: ignore[return-value]
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def validate_resolution(value: Optional[str]) -> Optional[str]:
|
|
263
|
+
"""Ensure a resolution string is in WIDTHxHEIGHT format."""
|
|
264
|
+
|
|
265
|
+
if value is None:
|
|
266
|
+
return None
|
|
267
|
+
parts = value.lower().split("x")
|
|
268
|
+
if len(parts) != 2 or not all(part.isdigit() for part in parts):
|
|
269
|
+
raise ValueError("size must be formatted as WIDTHxHEIGHT (e.g. 1280x720)")
|
|
270
|
+
return value
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def ensure_not_blank(value: str, field_name: str) -> str:
|
|
274
|
+
"""Raise if a text field is empty or whitespace only."""
|
|
275
|
+
|
|
276
|
+
if not value.strip():
|
|
277
|
+
raise ValueError(f"{field_name} must not be empty")
|
|
278
|
+
return value
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def download_video_variant(client: OpenAI, video_id: str, variant: str, output_path: str) -> None:
|
|
282
|
+
"""
|
|
283
|
+
Download a specific variant of a video from Sora API.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
client: OpenAI client instance
|
|
287
|
+
video_id: The video ID from Sora API
|
|
288
|
+
variant: Type of content to download (spritesheet, thumbnail, or video)
|
|
289
|
+
output_path: Full path where the file should be saved
|
|
290
|
+
"""
|
|
291
|
+
logger.info(f"Downloading {variant} for video {video_id}...")
|
|
292
|
+
content = client.videos.download_content(video_id, variant=variant)
|
|
293
|
+
content.write_to_file(output_path)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def create_image_output(image_path: str, label: str) -> list:
|
|
297
|
+
"""
|
|
298
|
+
Create tool output objects for an image file.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
image_path: Path to the image file
|
|
302
|
+
label: Label to display for the image (filename)
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
List containing ToolOutputText and ToolOutputImage objects
|
|
306
|
+
"""
|
|
307
|
+
image = Image.open(image_path)
|
|
308
|
+
compressed_b64 = compress_image_for_base64(image)
|
|
309
|
+
|
|
310
|
+
return [
|
|
311
|
+
ToolOutputText(type="text", text=f"{label}\nPath: {image_path}"),
|
|
312
|
+
ToolOutputImage(type="image", image_url=f"data:image/png;base64,{compressed_b64}", detail="auto")
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def extract_last_frame(video_path: str, output_path: str) -> Optional[Image.Image]:
|
|
317
|
+
"""
|
|
318
|
+
Extract the last frame from a video file.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
video_path: Path to the video file
|
|
322
|
+
output_path: Path where the last frame image should be saved
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
PIL Image object of the last frame, or None if extraction failed
|
|
326
|
+
"""
|
|
327
|
+
logger.info("Extracting last frame from video...")
|
|
328
|
+
cap = cv2.VideoCapture(video_path)
|
|
329
|
+
|
|
330
|
+
# Get total frame count and set to last frame
|
|
331
|
+
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
332
|
+
cap.set(cv2.CAP_PROP_POS_FRAMES, total_frames - 1)
|
|
333
|
+
ret, frame = cap.read()
|
|
334
|
+
|
|
335
|
+
cap.release()
|
|
336
|
+
|
|
337
|
+
if not ret:
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
# Convert BGR to RGB (OpenCV uses BGR)
|
|
341
|
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
342
|
+
last_frame_image = Image.fromarray(frame_rgb)
|
|
343
|
+
|
|
344
|
+
# Save last frame
|
|
345
|
+
last_frame_image.save(output_path)
|
|
346
|
+
|
|
347
|
+
return last_frame_image
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def generate_spritesheet(video_path: str, output_path: str, frames_per_row: int = 6, num_rows: int = 1) -> Optional[Image.Image]:
|
|
351
|
+
"""
|
|
352
|
+
Generate a linear spritesheet from a video file.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
video_path: Path to the video file
|
|
356
|
+
output_path: Path where the spritesheet should be saved
|
|
357
|
+
frames_per_row: Number of frames in the spritesheet (default 6, range 4-7)
|
|
358
|
+
num_rows: Number of rows (default 1 for linear layout)
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
PIL Image object of the spritesheet, or None if generation failed
|
|
362
|
+
"""
|
|
363
|
+
logger.info("Generating spritesheet from video...")
|
|
364
|
+
cap = cv2.VideoCapture(video_path)
|
|
365
|
+
|
|
366
|
+
# Get total frame count
|
|
367
|
+
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
368
|
+
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
369
|
+
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
370
|
+
|
|
371
|
+
# Calculate frames to extract (evenly distributed)
|
|
372
|
+
total_frames_needed = frames_per_row * num_rows
|
|
373
|
+
frame_indices = [int(i * total_frames / total_frames_needed) for i in range(total_frames_needed)]
|
|
374
|
+
|
|
375
|
+
# Extract frames
|
|
376
|
+
frames = []
|
|
377
|
+
for frame_idx in frame_indices:
|
|
378
|
+
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
|
|
379
|
+
ret, frame = cap.read()
|
|
380
|
+
if ret:
|
|
381
|
+
# Convert BGR to RGB
|
|
382
|
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
383
|
+
frames.append(Image.fromarray(frame_rgb))
|
|
384
|
+
|
|
385
|
+
cap.release()
|
|
386
|
+
|
|
387
|
+
if not frames:
|
|
388
|
+
return None
|
|
389
|
+
|
|
390
|
+
# Calculate thumbnail size (smaller for spritesheet)
|
|
391
|
+
thumb_width = frame_width // 4
|
|
392
|
+
thumb_height = frame_height // 4
|
|
393
|
+
|
|
394
|
+
# Create spritesheet
|
|
395
|
+
spritesheet_width = thumb_width * frames_per_row
|
|
396
|
+
spritesheet_height = thumb_height * num_rows
|
|
397
|
+
spritesheet = Image.new('RGB', (spritesheet_width, spritesheet_height))
|
|
398
|
+
|
|
399
|
+
# Paste frames into spritesheet
|
|
400
|
+
for idx, frame in enumerate(frames):
|
|
401
|
+
row = idx // frames_per_row
|
|
402
|
+
col = idx % frames_per_row
|
|
403
|
+
x = col * thumb_width
|
|
404
|
+
y = row * thumb_height
|
|
405
|
+
|
|
406
|
+
# Resize frame to thumbnail size
|
|
407
|
+
frame_thumb = frame.resize((thumb_width, thumb_height), Image.Resampling.LANCZOS)
|
|
408
|
+
spritesheet.paste(frame_thumb, (x, y))
|
|
409
|
+
|
|
410
|
+
# Save spritesheet
|
|
411
|
+
spritesheet.save(output_path, quality=85)
|
|
412
|
+
logger.info(f"Spritesheet saved to {output_path}")
|
|
413
|
+
|
|
414
|
+
return spritesheet
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def download_veo_video(gemini_client, video_file, output_path: str) -> None:
|
|
418
|
+
"""
|
|
419
|
+
Download a Veo video from Google Gemini API.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
gemini_client: Google Gemini client instance
|
|
423
|
+
video_file: Video file object from Gemini API
|
|
424
|
+
output_path: Full path where the video should be saved
|
|
425
|
+
"""
|
|
426
|
+
logger.info(f"Downloading Veo video to {output_path}...")
|
|
427
|
+
gemini_client.files.download(file=video_file)
|
|
428
|
+
video_file.save(output_path)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def save_veo_video_with_metadata(gemini_client, video_file, name: str, product_name: str) -> list:
|
|
432
|
+
"""
|
|
433
|
+
Download and save Veo video with metadata (spritesheet, thumbnail, last frame).
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
gemini_client: Google Gemini client instance
|
|
437
|
+
video_file: Video file object from Gemini API
|
|
438
|
+
name: Base name for saved files (without extension)
|
|
439
|
+
product_name: Name of the product (for organizing files in product-specific folders)
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
List of ToolOutput objects showing the saved files
|
|
443
|
+
"""
|
|
444
|
+
output = []
|
|
445
|
+
videos_dir = get_videos_dir(product_name)
|
|
446
|
+
|
|
447
|
+
video_path = os.path.join(videos_dir, f"{name}.mp4")
|
|
448
|
+
download_veo_video(gemini_client, video_file, video_path)
|
|
449
|
+
|
|
450
|
+
spritesheet_path = os.path.join(videos_dir, f"{name}_spritesheet.jpg")
|
|
451
|
+
generate_spritesheet(video_path, spritesheet_path)
|
|
452
|
+
|
|
453
|
+
thumbnail_path = os.path.join(videos_dir, f"{name}_thumbnail.jpg")
|
|
454
|
+
cap = cv2.VideoCapture(video_path)
|
|
455
|
+
ret, frame = cap.read()
|
|
456
|
+
if ret:
|
|
457
|
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
458
|
+
Image.fromarray(frame_rgb).save(thumbnail_path)
|
|
459
|
+
cap.release()
|
|
460
|
+
|
|
461
|
+
last_frame_path = os.path.join(videos_dir, f"{name}_last_frame.jpg")
|
|
462
|
+
extract_last_frame(video_path, last_frame_path)
|
|
463
|
+
|
|
464
|
+
veo_reference = {}
|
|
465
|
+
if getattr(video_file, "name", None):
|
|
466
|
+
veo_reference["veo_video_ref"] = video_file.name
|
|
467
|
+
if getattr(video_file, "uri", None):
|
|
468
|
+
veo_reference["veo_video_uri"] = video_file.uri
|
|
469
|
+
|
|
470
|
+
reference_path = None
|
|
471
|
+
if veo_reference:
|
|
472
|
+
reference_path = os.path.join(videos_dir, f"{name}_veo_reference.json")
|
|
473
|
+
with open(reference_path, "w", encoding="utf-8") as handle:
|
|
474
|
+
json.dump(veo_reference, handle, indent=2, ensure_ascii=True)
|
|
475
|
+
|
|
476
|
+
summary_lines = [
|
|
477
|
+
f"Video saved to `{name}.mp4`",
|
|
478
|
+
f"Path: {video_path}",
|
|
479
|
+
]
|
|
480
|
+
if veo_reference.get("veo_video_ref"):
|
|
481
|
+
summary_lines.append(f"Veo reference: {veo_reference['veo_video_ref']}")
|
|
482
|
+
if veo_reference.get("veo_video_uri"):
|
|
483
|
+
summary_lines.append(f"Veo URI: {veo_reference['veo_video_uri']}")
|
|
484
|
+
if reference_path:
|
|
485
|
+
summary_lines.append(f"Reference file: {reference_path}")
|
|
486
|
+
|
|
487
|
+
output.append(ToolOutputText(type="text", text="\n".join(summary_lines)))
|
|
488
|
+
|
|
489
|
+
return output
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def save_video_with_metadata(client: OpenAI, video_id: str, name: str, product_name: str) -> list:
|
|
493
|
+
"""
|
|
494
|
+
Download and save video with all metadata (spritesheet, thumbnail, last frame).
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
client: OpenAI client instance
|
|
498
|
+
video_id: The video ID from Sora API
|
|
499
|
+
name: Base name for saved files (without extension)
|
|
500
|
+
product_name: Name of the product (for organizing files in product-specific folders)
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
List of ToolOutput objects showing the saved files
|
|
504
|
+
"""
|
|
505
|
+
output = []
|
|
506
|
+
videos_dir = get_videos_dir(product_name)
|
|
507
|
+
|
|
508
|
+
spritesheet_path = os.path.join(videos_dir, f"{name}_spritesheet.jpg")
|
|
509
|
+
download_video_variant(client, video_id, "spritesheet", spritesheet_path)
|
|
510
|
+
|
|
511
|
+
thumbnail_path = os.path.join(videos_dir, f"{name}_thumbnail.jpg")
|
|
512
|
+
download_video_variant(client, video_id, "thumbnail", thumbnail_path)
|
|
513
|
+
|
|
514
|
+
video_path = os.path.join(videos_dir, f"{name}.mp4")
|
|
515
|
+
download_video_variant(client, video_id, "video", video_path)
|
|
516
|
+
|
|
517
|
+
last_frame_path = os.path.join(videos_dir, f"{name}_last_frame.jpg")
|
|
518
|
+
extract_last_frame(video_path, last_frame_path)
|
|
519
|
+
|
|
520
|
+
output.append(ToolOutputText(type="text", text=f"Video saved to `{name}.mp4`\nPath: {video_path}\nVideo ID: {video_id}"))
|
|
521
|
+
|
|
522
|
+
return output
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from agency_swarm import Agent, ModelSettings
|
|
2
|
+
from agency_swarm.tools import LoadFileAttachment
|
|
3
|
+
from openai.types.shared.reasoning import Reasoning
|
|
4
|
+
|
|
5
|
+
from config import get_default_model, is_openai_provider
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def create_video_generation_agent() -> Agent:
|
|
9
|
+
return Agent(
|
|
10
|
+
name="Video Agent",
|
|
11
|
+
description="A general-purpose agent for video generation and editing.",
|
|
12
|
+
instructions="instructions.md",
|
|
13
|
+
tools_folder="./tools",
|
|
14
|
+
tools=[LoadFileAttachment],
|
|
15
|
+
model=get_default_model(),
|
|
16
|
+
model_settings=ModelSettings(
|
|
17
|
+
reasoning=Reasoning(summary="auto", effort="medium") if is_openai_provider() else None,
|
|
18
|
+
truncation="auto",
|
|
19
|
+
),
|
|
20
|
+
conversation_starters=[
|
|
21
|
+
"Generate a short promo video for my product launch.",
|
|
22
|
+
"Create an animated explainer video about how AI works.",
|
|
23
|
+
"Edit this video clip and add captions.",
|
|
24
|
+
"Turn my blog post into a video with voiceover.",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .virtual_assistant import create_virtual_assistant
|
|
Binary file
|
|
Binary file
|