@_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,369 @@
|
|
|
1
|
+
"""Tool for editing video content via fal.ai models."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import cv2
|
|
8
|
+
import httpx
|
|
9
|
+
from typing import Annotated, Literal, Optional, Union
|
|
10
|
+
|
|
11
|
+
import fal_client
|
|
12
|
+
from pydantic import BaseModel, Field, field_validator
|
|
13
|
+
from google.genai import types
|
|
14
|
+
from PIL import Image
|
|
15
|
+
|
|
16
|
+
from agency_swarm import BaseTool, ToolOutputText
|
|
17
|
+
|
|
18
|
+
from .utils.video_utils import (
|
|
19
|
+
create_image_output,
|
|
20
|
+
extract_last_frame,
|
|
21
|
+
generate_spritesheet,
|
|
22
|
+
get_gemini_client,
|
|
23
|
+
get_openai_client,
|
|
24
|
+
save_video_with_metadata,
|
|
25
|
+
save_veo_video_with_metadata,
|
|
26
|
+
get_videos_dir,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
_VEO_MODEL = "veo-3.1-generate-preview"
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _ensure_not_blank(value: str, field_name: str) -> str:
|
|
35
|
+
if not value.strip():
|
|
36
|
+
raise ValueError(f"{field_name} must not be empty")
|
|
37
|
+
return value
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class EditMode(BaseModel):
|
|
41
|
+
action: Literal["edit"]
|
|
42
|
+
video_source: str = Field(
|
|
43
|
+
...,
|
|
44
|
+
description="Source video path or HTTP/HTTPS URL.",
|
|
45
|
+
)
|
|
46
|
+
prompt: str = Field(
|
|
47
|
+
...,
|
|
48
|
+
description="Editing prompt.",
|
|
49
|
+
)
|
|
50
|
+
reference_images: Optional[list[str]] = Field(
|
|
51
|
+
default=None,
|
|
52
|
+
description="Optional reference image paths or URLs.",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@field_validator("video_source")
|
|
56
|
+
@classmethod
|
|
57
|
+
def _video_source_not_blank(cls, value: str) -> str:
|
|
58
|
+
return _ensure_not_blank(value, "video_source")
|
|
59
|
+
|
|
60
|
+
@field_validator("prompt")
|
|
61
|
+
@classmethod
|
|
62
|
+
def _prompt_not_blank(cls, value: str) -> str:
|
|
63
|
+
if re.match(r"^\[.*\]", value) or re.match(r"^\[.*?\]\s+.+", value):
|
|
64
|
+
raise ValueError(
|
|
65
|
+
"PROMPT CANNOT CONTAIN VARIABLES. Rewrite the full prompt from scratch — "
|
|
66
|
+
"this tool is stateless and does not retain prior context."
|
|
67
|
+
)
|
|
68
|
+
return _ensure_not_blank(value, "prompt")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RemixMode(BaseModel):
|
|
72
|
+
action: Literal["remix"]
|
|
73
|
+
video_id: str = Field(
|
|
74
|
+
...,
|
|
75
|
+
description="Sora video ID to remix.",
|
|
76
|
+
)
|
|
77
|
+
prompt: str = Field(
|
|
78
|
+
...,
|
|
79
|
+
description="Remix prompt.",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@field_validator("video_id")
|
|
83
|
+
@classmethod
|
|
84
|
+
def _video_id_not_blank(cls, value: str) -> str:
|
|
85
|
+
return _ensure_not_blank(value, "video_id")
|
|
86
|
+
|
|
87
|
+
@field_validator("prompt")
|
|
88
|
+
@classmethod
|
|
89
|
+
def _prompt_not_blank(cls, value: str) -> str:
|
|
90
|
+
if re.match(r"^\[.*\]", value) or re.match(r"^\[.*?\]\s+.+", value):
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"PROMPT CANNOT CONTAIN VARIABLES. Rewrite the full prompt from scratch — "
|
|
93
|
+
"this tool is stateless and does not retain prior context."
|
|
94
|
+
)
|
|
95
|
+
return _ensure_not_blank(value, "prompt")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ExtendMode(BaseModel):
|
|
99
|
+
action: Literal["extend"]
|
|
100
|
+
veo_video_ref: str = Field(
|
|
101
|
+
...,
|
|
102
|
+
description=(
|
|
103
|
+
"Veo video reference from a previous Veo generation "
|
|
104
|
+
"(e.g., 'files/abc123' or a Veo download URL)."
|
|
105
|
+
),
|
|
106
|
+
)
|
|
107
|
+
prompt: Optional[str] = Field(
|
|
108
|
+
default=None,
|
|
109
|
+
description="Optional extension prompt.",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
@field_validator("veo_video_ref")
|
|
113
|
+
@classmethod
|
|
114
|
+
def _veo_video_ref_not_blank(cls, value: str) -> str:
|
|
115
|
+
return _ensure_not_blank(value, "veo_video_ref")
|
|
116
|
+
|
|
117
|
+
@field_validator("prompt")
|
|
118
|
+
@classmethod
|
|
119
|
+
def _prompt_not_blank(cls, value: Optional[str]) -> Optional[str]:
|
|
120
|
+
if value is None:
|
|
121
|
+
return value
|
|
122
|
+
if re.match(r"^\[.*\]", value) or re.match(r"^\[.*?\]\s+.+", value):
|
|
123
|
+
raise ValueError(
|
|
124
|
+
"PROMPT CANNOT CONTAIN VARIABLES. Rewrite the full prompt from scratch — "
|
|
125
|
+
"this tool is stateless and does not retain prior context."
|
|
126
|
+
)
|
|
127
|
+
return _ensure_not_blank(value, "prompt")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
EditModeUnion = Annotated[
|
|
131
|
+
Union[EditMode, RemixMode, ExtendMode],
|
|
132
|
+
Field(discriminator="action"),
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class EditVideoContent(BaseTool):
|
|
137
|
+
"""
|
|
138
|
+
Edit video content via fal.ai video-to-video models.
|
|
139
|
+
|
|
140
|
+
Videos are saved to: mnt/{product_name}/generated_videos/
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
product_name: str = Field(
|
|
144
|
+
...,
|
|
145
|
+
description="Name of the product this video is for. Used to organize files into product-specific folders.",
|
|
146
|
+
)
|
|
147
|
+
name: str = Field(
|
|
148
|
+
...,
|
|
149
|
+
description="The name for the edited video file (without extension)",
|
|
150
|
+
)
|
|
151
|
+
mode: Union[EditMode, RemixMode, ExtendMode] = Field(
|
|
152
|
+
...,
|
|
153
|
+
description=(
|
|
154
|
+
"Action-specific inputs. Use the 'action' field to select which "
|
|
155
|
+
"mode shape is required."
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@field_validator("name")
|
|
160
|
+
@classmethod
|
|
161
|
+
def _name_not_blank(cls, value: str) -> str:
|
|
162
|
+
return _ensure_not_blank(value, "name")
|
|
163
|
+
|
|
164
|
+
async def run(self) -> list:
|
|
165
|
+
if self.mode.action == "remix":
|
|
166
|
+
return await self._run_remix(self.mode)
|
|
167
|
+
if self.mode.action == "extend":
|
|
168
|
+
return await self._run_extend(self.mode)
|
|
169
|
+
|
|
170
|
+
api_key = os.getenv("FAL_KEY")
|
|
171
|
+
if not api_key:
|
|
172
|
+
raise ValueError("FAL_KEY is not set. Add it to your .env to use video editing.")
|
|
173
|
+
fal = fal_client.SyncClient(key=api_key)
|
|
174
|
+
|
|
175
|
+
endpoint = self._resolve_endpoint(self.mode.action)
|
|
176
|
+
video_url = self._resolve_media_url(self.mode.video_source, fal)
|
|
177
|
+
arguments = {"video_url": video_url}
|
|
178
|
+
|
|
179
|
+
arguments["prompt"] = self.mode.prompt
|
|
180
|
+
if self.mode.reference_images:
|
|
181
|
+
arguments["image_urls"] = [
|
|
182
|
+
self._resolve_media_url(ref, fal) for ref in self.mode.reference_images
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
result = fal.subscribe(endpoint, arguments=arguments, with_logs=True)
|
|
186
|
+
output_url = self._extract_video_url(result)
|
|
187
|
+
|
|
188
|
+
if not output_url:
|
|
189
|
+
raise RuntimeError("fal.ai response did not include a video URL")
|
|
190
|
+
|
|
191
|
+
videos_dir = get_videos_dir(self.product_name)
|
|
192
|
+
output_path = os.path.join(videos_dir, f"{self.name}.mp4")
|
|
193
|
+
self._download_file(output_url, output_path)
|
|
194
|
+
|
|
195
|
+
output = []
|
|
196
|
+
|
|
197
|
+
spritesheet_path = os.path.join(videos_dir, f"{self.name}_spritesheet.jpg")
|
|
198
|
+
spritesheet = generate_spritesheet(output_path, spritesheet_path)
|
|
199
|
+
if spritesheet:
|
|
200
|
+
output.extend(create_image_output(spritesheet_path, f"{self.name}_spritesheet.jpg"))
|
|
201
|
+
|
|
202
|
+
thumbnail_path = os.path.join(videos_dir, f"{self.name}_thumbnail.jpg")
|
|
203
|
+
thumbnail = self._extract_first_frame(output_path, thumbnail_path)
|
|
204
|
+
if thumbnail:
|
|
205
|
+
output.extend(create_image_output(thumbnail_path, f"{self.name}_thumbnail.jpg"))
|
|
206
|
+
|
|
207
|
+
last_frame_path = os.path.join(videos_dir, f"{self.name}_last_frame.jpg")
|
|
208
|
+
last_frame = extract_last_frame(output_path, last_frame_path)
|
|
209
|
+
if last_frame:
|
|
210
|
+
output.extend(create_image_output(last_frame_path, f"{self.name}_last_frame.jpg"))
|
|
211
|
+
|
|
212
|
+
output.append(
|
|
213
|
+
ToolOutputText(
|
|
214
|
+
type="text",
|
|
215
|
+
text=f"Video edit complete!\nSaved to: `{self.name}.mp4`\nPath: {output_path}",
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
return output
|
|
220
|
+
|
|
221
|
+
async def _run_remix(self, payload: RemixMode) -> list:
|
|
222
|
+
client = get_openai_client()
|
|
223
|
+
loop = asyncio.get_event_loop()
|
|
224
|
+
|
|
225
|
+
video = await loop.run_in_executor(
|
|
226
|
+
None,
|
|
227
|
+
lambda: client.videos.remix(video_id=payload.video_id, prompt=payload.prompt),
|
|
228
|
+
)
|
|
229
|
+
video = await loop.run_in_executor(None, lambda: client.videos.poll(video.id))
|
|
230
|
+
return save_video_with_metadata(client, video.id, self.name, self.product_name)
|
|
231
|
+
|
|
232
|
+
async def _run_extend(self, payload: ExtendMode) -> list:
|
|
233
|
+
client = get_gemini_client()
|
|
234
|
+
loop = asyncio.get_event_loop()
|
|
235
|
+
|
|
236
|
+
if payload.veo_video_ref.startswith("http://") or payload.veo_video_ref.startswith(
|
|
237
|
+
"https://"
|
|
238
|
+
):
|
|
239
|
+
video_uri = payload.veo_video_ref
|
|
240
|
+
else:
|
|
241
|
+
video_file = await loop.run_in_executor(
|
|
242
|
+
None,
|
|
243
|
+
lambda: client.files.get(name=payload.veo_video_ref),
|
|
244
|
+
)
|
|
245
|
+
video_uri = getattr(video_file, "uri", None) or getattr(
|
|
246
|
+
video_file, "download_uri", None
|
|
247
|
+
)
|
|
248
|
+
if not video_uri:
|
|
249
|
+
raise ValueError(
|
|
250
|
+
"Veo file reference did not include a usable URI. "
|
|
251
|
+
"Use the veo_video_uri value from the reference JSON."
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
config = types.GenerateVideosConfig(
|
|
255
|
+
number_of_videos=1,
|
|
256
|
+
duration_seconds=8,
|
|
257
|
+
resolution="720p",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
request_kwargs = {
|
|
261
|
+
"model": _VEO_MODEL,
|
|
262
|
+
"video": types.Video(uri=video_uri),
|
|
263
|
+
"config": config,
|
|
264
|
+
}
|
|
265
|
+
if payload.prompt:
|
|
266
|
+
request_kwargs["prompt"] = payload.prompt
|
|
267
|
+
|
|
268
|
+
operation = await loop.run_in_executor(
|
|
269
|
+
None,
|
|
270
|
+
lambda: client.models.generate_videos(**request_kwargs),
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
while not operation.done:
|
|
274
|
+
await asyncio.sleep(10)
|
|
275
|
+
operation = await loop.run_in_executor(
|
|
276
|
+
None,
|
|
277
|
+
lambda: client.operations.get(operation),
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
generated_video = operation.response.generated_videos[0]
|
|
281
|
+
return save_veo_video_with_metadata(
|
|
282
|
+
client,
|
|
283
|
+
generated_video.video,
|
|
284
|
+
self.name,
|
|
285
|
+
self.product_name,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _resolve_endpoint(self, action: str) -> str:
|
|
289
|
+
if action == "edit":
|
|
290
|
+
return "fal-ai/kling-video/o3/standard/video-to-video/edit"
|
|
291
|
+
raise ValueError(f"Unsupported fal.ai action: {action}")
|
|
292
|
+
|
|
293
|
+
def _resolve_media_url(self, value: Optional[str], fal: fal_client.SyncClient) -> str:
|
|
294
|
+
if value is None:
|
|
295
|
+
raise ValueError("Media source is required")
|
|
296
|
+
|
|
297
|
+
if value.startswith("http://") or value.startswith("https://"):
|
|
298
|
+
return value
|
|
299
|
+
|
|
300
|
+
# Try as absolute/relative path first
|
|
301
|
+
path = os.path.expanduser(value)
|
|
302
|
+
if os.path.exists(path):
|
|
303
|
+
return fal.upload_file(path)
|
|
304
|
+
|
|
305
|
+
# Try in product's generated_videos directory
|
|
306
|
+
videos_dir = get_videos_dir(self.product_name)
|
|
307
|
+
|
|
308
|
+
# Try with common video extensions
|
|
309
|
+
for ext in [".mp4", ".mov", ".avi", ".webm"]:
|
|
310
|
+
# Try with extension
|
|
311
|
+
video_path = os.path.join(videos_dir, f"{value}{ext}")
|
|
312
|
+
if os.path.exists(video_path):
|
|
313
|
+
return fal.upload_file(video_path)
|
|
314
|
+
|
|
315
|
+
# Try without adding extension (in case value already has one)
|
|
316
|
+
video_path = os.path.join(videos_dir, value)
|
|
317
|
+
if os.path.exists(video_path):
|
|
318
|
+
return fal.upload_file(video_path)
|
|
319
|
+
|
|
320
|
+
raise FileNotFoundError(
|
|
321
|
+
f"Video file not found: '{value}'\n"
|
|
322
|
+
f" Searched in: {videos_dir}\n"
|
|
323
|
+
f" Also tried as absolute/relative path: {path}"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def _extract_video_url(self, result: dict) -> Optional[str]:
|
|
327
|
+
video_info = result.get("video")
|
|
328
|
+
if isinstance(video_info, dict):
|
|
329
|
+
return video_info.get("url")
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
def _download_file(self, url: str, output_path: str) -> None:
|
|
333
|
+
with httpx.Client(timeout=120.0) as client:
|
|
334
|
+
with client.stream("GET", url) as response:
|
|
335
|
+
response.raise_for_status()
|
|
336
|
+
with open(output_path, "wb") as out_file:
|
|
337
|
+
for chunk in response.iter_bytes():
|
|
338
|
+
if chunk:
|
|
339
|
+
out_file.write(chunk)
|
|
340
|
+
|
|
341
|
+
def _extract_first_frame(self, video_path: str, output_path: str):
|
|
342
|
+
cap = cv2.VideoCapture(video_path)
|
|
343
|
+
ret, frame = cap.read()
|
|
344
|
+
cap.release()
|
|
345
|
+
|
|
346
|
+
if not ret:
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
350
|
+
thumbnail_image = Image.fromarray(frame_rgb)
|
|
351
|
+
thumbnail_image.save(output_path)
|
|
352
|
+
|
|
353
|
+
return thumbnail_image
|
|
354
|
+
|
|
355
|
+
if __name__ == "__main__":
|
|
356
|
+
tool = EditVideoContent(
|
|
357
|
+
product_name="Test_Product",
|
|
358
|
+
name="test_video_edit",
|
|
359
|
+
mode=EditMode(
|
|
360
|
+
action="edit",
|
|
361
|
+
video_source="test_video",
|
|
362
|
+
prompt="Replace the dog with a fox",
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
try:
|
|
366
|
+
result = asyncio.run(tool.run())
|
|
367
|
+
print(result)
|
|
368
|
+
except Exception as exc:
|
|
369
|
+
print(f"Video editing failed: {exc}")
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Tool for generating images using Google's Gemini 2.5 Flash Image model."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from google import genai
|
|
8
|
+
from pydantic import Field, field_validator
|
|
9
|
+
|
|
10
|
+
from agency_swarm import BaseTool
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
from .utils.image_utils import (
|
|
14
|
+
get_images_dir,
|
|
15
|
+
MODEL_NAME,
|
|
16
|
+
extract_image_from_response,
|
|
17
|
+
extract_usage_metadata,
|
|
18
|
+
process_variant_result,
|
|
19
|
+
split_results_and_usage,
|
|
20
|
+
run_parallel_variants,
|
|
21
|
+
compress_image_for_base64,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GenerateImage(BaseTool):
|
|
26
|
+
"""Generate images using Google's Gemini 2.5 Flash Image (Nano Banana) model.
|
|
27
|
+
|
|
28
|
+
Images are saved to: mnt/{product_name}/generated_images/
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
product_name: str = Field(
|
|
32
|
+
...,
|
|
33
|
+
description="Name of the product this image is for (e.g., 'Acme_Widget_Pro', 'Green_Tea_Extract'). Used to organize files into product-specific folders.",
|
|
34
|
+
)
|
|
35
|
+
prompt: str = Field(
|
|
36
|
+
...,
|
|
37
|
+
description=(
|
|
38
|
+
"The text prompt describing the image to generate. Start with 'Generate an image of' "
|
|
39
|
+
"and describe the image in detail."
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
file_name: str = Field(
|
|
43
|
+
...,
|
|
44
|
+
description="The name for the generated image file (without extension)",
|
|
45
|
+
)
|
|
46
|
+
num_variants: int = Field(
|
|
47
|
+
default=1,
|
|
48
|
+
description="Number of image variants to generate (1-4, default is 1)",
|
|
49
|
+
)
|
|
50
|
+
aspect_ratio: Literal["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"] = Field(
|
|
51
|
+
default="1:1",
|
|
52
|
+
description="The aspect ratio of the generated image (default is 1:1)",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@field_validator("prompt")
|
|
56
|
+
@classmethod
|
|
57
|
+
def _prompt_not_blank(cls, value: str) -> str:
|
|
58
|
+
if not value.strip():
|
|
59
|
+
raise ValueError("prompt must not be empty")
|
|
60
|
+
return value
|
|
61
|
+
|
|
62
|
+
@field_validator("file_name")
|
|
63
|
+
@classmethod
|
|
64
|
+
def _filename_not_blank(cls, value: str) -> str:
|
|
65
|
+
if not value.strip():
|
|
66
|
+
raise ValueError("file_name must not be empty")
|
|
67
|
+
return value
|
|
68
|
+
|
|
69
|
+
@field_validator("num_variants")
|
|
70
|
+
@classmethod
|
|
71
|
+
def _validate_num_variants(cls, value: int) -> int:
|
|
72
|
+
if value < 1 or value > 4:
|
|
73
|
+
raise ValueError("num_variants must be between 1 and 4")
|
|
74
|
+
return value
|
|
75
|
+
|
|
76
|
+
async def run(self) -> list:
|
|
77
|
+
"""Generate images using the Gemini API."""
|
|
78
|
+
api_key = os.getenv("GOOGLE_API_KEY")
|
|
79
|
+
if not api_key:
|
|
80
|
+
raise ValueError("GOOGLE_API_KEY is not set. Add it to your .env to use image generation.")
|
|
81
|
+
|
|
82
|
+
client = genai.Client(api_key=api_key)
|
|
83
|
+
images_dir = get_images_dir(self.product_name)
|
|
84
|
+
|
|
85
|
+
def generate_single_variant(variant_num: int):
|
|
86
|
+
try:
|
|
87
|
+
response = client.models.generate_content(
|
|
88
|
+
model=MODEL_NAME,
|
|
89
|
+
contents=[self.prompt],
|
|
90
|
+
config=genai.types.GenerateContentConfig(
|
|
91
|
+
image_config=genai.types.ImageConfig(aspect_ratio=self.aspect_ratio),
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
usage_metadata = extract_usage_metadata(response)
|
|
95
|
+
image, _text = extract_image_from_response(response)
|
|
96
|
+
if image is None:
|
|
97
|
+
return None
|
|
98
|
+
result = process_variant_result(
|
|
99
|
+
variant_num,
|
|
100
|
+
image,
|
|
101
|
+
self.file_name,
|
|
102
|
+
self.num_variants,
|
|
103
|
+
compress_image_for_base64,
|
|
104
|
+
images_dir,
|
|
105
|
+
)
|
|
106
|
+
result["prompt_tokens"] = float(usage_metadata.get("prompt_token_count") or 0)
|
|
107
|
+
result["candidate_tokens"] = float(usage_metadata.get("candidates_token_count") or 0)
|
|
108
|
+
return result
|
|
109
|
+
except Exception:
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
raw_results = await run_parallel_variants(generate_single_variant, self.num_variants)
|
|
113
|
+
if not raw_results:
|
|
114
|
+
raise RuntimeError("No variants were successfully generated")
|
|
115
|
+
|
|
116
|
+
results, _usage = split_results_and_usage(raw_results)
|
|
117
|
+
return results
|
|
118
|
+
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
# Example usage with Google Gemini 2.5 Flash Image
|
|
121
|
+
tool = GenerateImage(
|
|
122
|
+
product_name="Test_Product",
|
|
123
|
+
prompt=(
|
|
124
|
+
"Generate an image of a sparrow flying away from the camera"
|
|
125
|
+
),
|
|
126
|
+
file_name="test_image",
|
|
127
|
+
aspect_ratio="16:9",
|
|
128
|
+
)
|
|
129
|
+
try:
|
|
130
|
+
result = asyncio.run(tool.run())
|
|
131
|
+
print(result)
|
|
132
|
+
except Exception as exc:
|
|
133
|
+
print(f"Image generation failed: {exc}")
|